00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "libavutil/mathematics.h"
00023 #include "avformat.h"
00024 #include "ffmeta.h"
00025 #include "internal.h"
00026 #include "libavutil/dict.h"
00027
00028 static int probe(AVProbeData *p)
00029 {
00030 if(!memcmp(p->buf, ID_STRING, strlen(ID_STRING)))
00031 return AVPROBE_SCORE_MAX;
00032 return 0;
00033 }
00034
00035 static void get_line(AVIOContext *s, uint8_t *buf, int size)
00036 {
00037 do {
00038 uint8_t c;
00039 int i = 0;
00040
00041 while ((c = avio_r8(s))) {
00042 if (c == '\\') {
00043 if (i < size - 1)
00044 buf[i++] = c;
00045 c = avio_r8(s);
00046 } else if (c == '\n')
00047 break;
00048
00049 if (i < size - 1)
00050 buf[i++] = c;
00051 }
00052 buf[i] = 0;
00053 } while (!url_feof(s) && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0));
00054 }
00055
00056 static AVChapter *read_chapter(AVFormatContext *s)
00057 {
00058 uint8_t line[256];
00059 int64_t start, end;
00060 AVRational tb = {1, 1e9};
00061
00062 get_line(s->pb, line, sizeof(line));
00063
00064 if (sscanf(line, "TIMEBASE=%d/%d", &tb.num, &tb.den))
00065 get_line(s->pb, line, sizeof(line));
00066 if (!sscanf(line, "START=%"SCNd64, &start)) {
00067 av_log(s, AV_LOG_ERROR, "Expected chapter start timestamp, found %s.\n", line);
00068 start = (s->nb_chapters && s->chapters[s->nb_chapters - 1]->end != AV_NOPTS_VALUE) ?
00069 s->chapters[s->nb_chapters - 1]->end : 0;
00070 } else
00071 get_line(s->pb, line, sizeof(line));
00072
00073 if (!sscanf(line, "END=%"SCNd64, &end)) {
00074 av_log(s, AV_LOG_ERROR, "Expected chapter end timestamp, found %s.\n", line);
00075 end = AV_NOPTS_VALUE;
00076 }
00077
00078 return avpriv_new_chapter(s, s->nb_chapters, tb, start, end, NULL);
00079 }
00080
00081 static uint8_t *unescape(uint8_t *buf, int size)
00082 {
00083 uint8_t *ret = av_malloc(size + 1);
00084 uint8_t *p1 = ret, *p2 = buf;
00085
00086 if (!ret)
00087 return NULL;
00088
00089 while (p2 < buf + size) {
00090 if (*p2 == '\\')
00091 p2++;
00092 *p1++ = *p2++;
00093 }
00094 *p1 = 0;
00095 return ret;
00096 }
00097
00098 static int read_tag(uint8_t *line, AVDictionary **m)
00099 {
00100 uint8_t *key, *value, *p = line;
00101
00102
00103 while (1) {
00104 if (*p == '=')
00105 break;
00106 else if (*p == '\\')
00107 p++;
00108
00109 if (*p++)
00110 continue;
00111
00112 return 0;
00113 }
00114
00115 if (!(key = unescape(line, p - line)))
00116 return AVERROR(ENOMEM);
00117 if (!(value = unescape(p + 1, strlen(p + 1)))) {
00118 av_free(key);
00119 return AVERROR(ENOMEM);
00120 }
00121
00122 av_dict_set(m, key, value, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
00123 return 0;
00124 }
00125
00126 static int read_header(AVFormatContext *s)
00127 {
00128 AVDictionary **m = &s->metadata;
00129 uint8_t line[1024];
00130
00131 while(!url_feof(s->pb)) {
00132 get_line(s->pb, line, sizeof(line));
00133
00134 if (!memcmp(line, ID_STREAM, strlen(ID_STREAM))) {
00135 AVStream *st = avformat_new_stream(s, NULL);
00136
00137 if (!st)
00138 return -1;
00139
00140 st->codec->codec_type = AVMEDIA_TYPE_DATA;
00141 st->codec->codec_id = CODEC_ID_FFMETADATA;
00142
00143 m = &st->metadata;
00144 } else if (!memcmp(line, ID_CHAPTER, strlen(ID_CHAPTER))) {
00145 AVChapter *ch = read_chapter(s);
00146
00147 if (!ch)
00148 return -1;
00149
00150 m = &ch->metadata;
00151 } else
00152 read_tag(line, m);
00153 }
00154
00155 s->start_time = 0;
00156 if (s->nb_chapters)
00157 s->duration = av_rescale_q(s->chapters[s->nb_chapters - 1]->end,
00158 s->chapters[s->nb_chapters - 1]->time_base,
00159 AV_TIME_BASE_Q);
00160
00161 return 0;
00162 }
00163
00164 static int read_packet(AVFormatContext *s, AVPacket *pkt)
00165 {
00166 return AVERROR_EOF;
00167 }
00168
00169 AVInputFormat ff_ffmetadata_demuxer = {
00170 .name = "ffmetadata",
00171 .long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text format"),
00172 .read_probe = probe,
00173 .read_header = read_header,
00174 .read_packet = read_packet,
00175 };