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