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/bprint.h"
23 #include "libavutil/mathematics.h"
24 #include "libavutil/mem.h"
28 #include "libavutil/dict.h"
30 static int probe(const AVProbeData
*p
)
32 if(!memcmp(p
->buf
, ID_STRING
, strlen(ID_STRING
)))
33 return AVPROBE_SCORE_MAX
;
37 static int64_t read_line_to_bprint_escaped(AVIOContext
*s
, AVBPrint
*bp
)
49 end
= prev
!= '\\' && (c
== '\r' || c
== '\n' || c
== '\0');
53 } while (!end
&& len
< sizeof(tmp
));
54 av_bprint_append_data(bp
, tmp
, len
);
58 if (c
== '\r' && avio_r8(s
) != '\n' && !avio_feof(s
))
64 if (!c
&& !read
&& avio_feof(s
))
70 static void get_bprint_line(AVIOContext
*s
, AVBPrint
*bp
)
75 read_line_to_bprint_escaped(s
, bp
);
76 } while (!avio_feof(s
) && (bp
->str
[0] == ';' || bp
->str
[0] == '#' || bp
->str
[0] == 0));
79 static void get_line(AVIOContext
*s
, uint8_t *buf
, int size
)
85 while ((c
= avio_r8(s
))) {
97 } while (!avio_feof(s
) && (buf
[0] == ';' || buf
[0] == '#' || buf
[0] == 0));
100 static AVChapter
*read_chapter(AVFormatContext
*s
)
104 AVRational tb
= {1, 1e9
};
107 get_line(s
->pb
, line
, sizeof(line
));
109 if (sscanf(line
, "TIMEBASE=%d/%d", &tb
.num
, &tb
.den
))
110 get_line(s
->pb
, line
, sizeof(line
));
111 ret
= sscanf(line
, "START=%"SCNd64
, &start
);
113 av_log(s
, AV_LOG_ERROR
, "Expected chapter start timestamp, found %s.\n", line
);
114 start
= (s
->nb_chapters
&& s
->chapters
[s
->nb_chapters
- 1]->end
!= AV_NOPTS_VALUE
) ?
115 s
->chapters
[s
->nb_chapters
- 1]->end
: 0;
117 get_line(s
->pb
, line
, sizeof(line
));
119 ret
= sscanf(line
, "END=%"SCNd64
, &end
);
121 av_log(s
, AV_LOG_ERROR
, "Expected chapter end timestamp, found %s.\n", line
);
122 end
= AV_NOPTS_VALUE
;
125 return avpriv_new_chapter(s
, s
->nb_chapters
, tb
, start
, end
, NULL
);
128 static uint8_t *unescape(const uint8_t *buf
, int size
)
130 uint8_t *ret
= av_malloc(size
+ 1);
132 const uint8_t *p2
= buf
;
137 while (p2
< buf
+ size
) {
146 static int read_tag(const uint8_t *line
, AVDictionary
**m
)
148 uint8_t *key
, *value
;
149 const uint8_t *p
= line
;
151 /* find first not escaped '=' */
164 if (!(key
= unescape(line
, p
- line
)))
165 return AVERROR(ENOMEM
);
166 if (!(value
= unescape(p
+ 1, strlen(p
+ 1)))) {
168 return AVERROR(ENOMEM
);
171 av_dict_set(m
, key
, value
, AV_DICT_DONT_STRDUP_KEY
| AV_DICT_DONT_STRDUP_VAL
);
175 static int read_header(AVFormatContext
*s
)
177 AVDictionary
**m
= &s
->metadata
;
180 av_bprint_init(&bp
, 0, AV_BPRINT_SIZE_UNLIMITED
);
182 while(!avio_feof(s
->pb
)) {
183 get_bprint_line(s
->pb
, &bp
);
185 if (bp
.len
== strlen(ID_STREAM
) && !memcmp(bp
.str
, ID_STREAM
, strlen(ID_STREAM
))) {
186 AVStream
*st
= avformat_new_stream(s
, NULL
);
191 st
->codecpar
->codec_type
= AVMEDIA_TYPE_DATA
;
192 st
->codecpar
->codec_id
= AV_CODEC_ID_FFMETADATA
;
195 } else if (bp
.len
== strlen(ID_CHAPTER
) && !memcmp(bp
.str
, ID_CHAPTER
, strlen(ID_CHAPTER
))) {
196 AVChapter
*ch
= read_chapter(s
);
206 av_bprint_finalize(&bp
, NULL
);
210 s
->duration
= av_rescale_q(s
->chapters
[s
->nb_chapters
- 1]->end
,
211 s
->chapters
[s
->nb_chapters
- 1]->time_base
,
216 av_bprint_finalize(&bp
, NULL
);
218 return AVERROR(ENOMEM
);
221 static int read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
226 const FFInputFormat ff_ffmetadata_demuxer
= {
227 .p
.name
= "ffmetadata",
228 .p
.long_name
= NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"),
230 .read_header
= read_header
,
231 .read_packet
= read_packet
,