3 * Copyright (c) 2010 Anton Khirnov
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "libavutil/mathematics.h"
26 #include "libavutil/dict.h"
28 static int probe(AVProbeData
*p
)
30 if(!memcmp(p
->buf
, ID_STRING
, strlen(ID_STRING
)))
31 return AVPROBE_SCORE_MAX
;
35 static void get_line(AVIOContext
*s
, uint8_t *buf
, int size
)
41 while ((c
= avio_r8(s
))) {
53 } while (!avio_feof(s
) && (buf
[0] == ';' || buf
[0] == '#' || buf
[0] == 0));
56 static AVChapter
*read_chapter(AVFormatContext
*s
)
60 AVRational tb
= {1, 1e9
};
62 get_line(s
->pb
, line
, sizeof(line
));
64 if (sscanf(line
, "TIMEBASE=%d/%d", &tb
.num
, &tb
.den
))
65 get_line(s
->pb
, line
, sizeof(line
));
66 if (!sscanf(line
, "START=%"SCNd64
, &start
)) {
67 av_log(s
, AV_LOG_ERROR
, "Expected chapter start timestamp, found %s.\n", line
);
68 start
= (s
->nb_chapters
&& s
->chapters
[s
->nb_chapters
- 1]->end
!= AV_NOPTS_VALUE
) ?
69 s
->chapters
[s
->nb_chapters
- 1]->end
: 0;
71 get_line(s
->pb
, line
, sizeof(line
));
73 if (!sscanf(line
, "END=%"SCNd64
, &end
)) {
74 av_log(s
, AV_LOG_ERROR
, "Expected chapter end timestamp, found %s.\n", line
);
78 return avpriv_new_chapter(s
, s
->nb_chapters
, tb
, start
, end
, NULL
);
81 static uint8_t *unescape(const uint8_t *buf
, int size
)
83 uint8_t *ret
= av_malloc(size
+ 1);
85 const uint8_t *p2
= buf
;
90 while (p2
< buf
+ size
) {
99 static int read_tag(const uint8_t *line
, AVDictionary
**m
)
101 uint8_t *key
, *value
;
102 const uint8_t *p
= line
;
104 /* find first not escaped '=' */
117 if (!(key
= unescape(line
, p
- line
)))
118 return AVERROR(ENOMEM
);
119 if (!(value
= unescape(p
+ 1, strlen(p
+ 1)))) {
121 return AVERROR(ENOMEM
);
124 av_dict_set(m
, key
, value
, AV_DICT_DONT_STRDUP_KEY
| AV_DICT_DONT_STRDUP_VAL
);
128 static int read_header(AVFormatContext
*s
)
130 AVDictionary
**m
= &s
->metadata
;
133 while(!avio_feof(s
->pb
)) {
134 get_line(s
->pb
, line
, sizeof(line
));
136 if (!memcmp(line
, ID_STREAM
, strlen(ID_STREAM
))) {
137 AVStream
*st
= avformat_new_stream(s
, NULL
);
140 return AVERROR(ENOMEM
);
142 st
->codec
->codec_type
= AVMEDIA_TYPE_DATA
;
143 st
->codec
->codec_id
= AV_CODEC_ID_FFMETADATA
;
146 } else if (!memcmp(line
, ID_CHAPTER
, strlen(ID_CHAPTER
))) {
147 AVChapter
*ch
= read_chapter(s
);
150 return AVERROR(ENOMEM
);
159 s
->duration
= av_rescale_q(s
->chapters
[s
->nb_chapters
- 1]->end
,
160 s
->chapters
[s
->nb_chapters
- 1]->time_base
,
166 static int read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
171 AVInputFormat ff_ffmetadata_demuxer
= {
172 .name
= "ffmetadata",
173 .long_name
= NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"),
175 .read_header
= read_header
,
176 .read_packet
= read_packet
,