Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Ogg muxer | |
3 | * Copyright (c) 2007 Baptiste Coudurier <baptiste dot coudurier at free dot fr> | |
4 | * | |
5 | * This file is part of FFmpeg. | |
6 | * | |
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. | |
11 | * | |
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. | |
16 | * | |
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 | |
20 | */ | |
21 | ||
22 | #include <stdint.h> | |
23 | ||
24 | #include "libavutil/crc.h" | |
25 | #include "libavutil/mathematics.h" | |
26 | #include "libavutil/opt.h" | |
27 | #include "libavutil/random_seed.h" | |
28 | #include "libavcodec/xiph.h" | |
29 | #include "libavcodec/bytestream.h" | |
30 | #include "libavcodec/flac.h" | |
31 | #include "avformat.h" | |
32 | #include "avio_internal.h" | |
33 | #include "internal.h" | |
34 | #include "vorbiscomment.h" | |
35 | ||
36 | #define MAX_PAGE_SIZE 65025 | |
37 | ||
38 | typedef struct { | |
39 | int64_t start_granule; | |
40 | int64_t granule; | |
41 | int stream_index; | |
42 | uint8_t flags; | |
43 | uint8_t segments_count; | |
44 | uint8_t segments[255]; | |
45 | uint8_t data[MAX_PAGE_SIZE]; | |
46 | uint16_t size; | |
47 | } OGGPage; | |
48 | ||
49 | typedef struct { | |
50 | unsigned page_counter; | |
51 | uint8_t *header[3]; | |
52 | int header_len[3]; | |
53 | /** for theora granule */ | |
54 | int kfgshift; | |
55 | int64_t last_kf_pts; | |
56 | int vrev; | |
57 | int eos; | |
58 | unsigned page_count; ///< number of page buffered | |
59 | OGGPage page; ///< current page | |
60 | unsigned serial_num; ///< serial number | |
61 | int64_t last_granule; ///< last packet granule | |
62 | } OGGStreamContext; | |
63 | ||
64 | typedef struct OGGPageList { | |
65 | OGGPage page; | |
66 | struct OGGPageList *next; | |
67 | } OGGPageList; | |
68 | ||
69 | typedef struct { | |
70 | const AVClass *class; | |
71 | OGGPageList *page_list; | |
72 | int pref_size; ///< preferred page size (0 => fill all segments) | |
73 | int64_t pref_duration; ///< preferred page duration (0 => fill all segments) | |
74 | } OGGContext; | |
75 | ||
76 | #define OFFSET(x) offsetof(OGGContext, x) | |
77 | #define PARAM AV_OPT_FLAG_ENCODING_PARAM | |
78 | ||
79 | static const AVOption options[] = { | |
80 | { "oggpagesize", "Set preferred Ogg page size.", | |
81 | offsetof(OGGContext, pref_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, MAX_PAGE_SIZE, AV_OPT_FLAG_ENCODING_PARAM}, | |
82 | { "pagesize", "preferred page size in bytes (deprecated)", | |
83 | OFFSET(pref_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_PAGE_SIZE, PARAM }, | |
84 | { "page_duration", "preferred page duration, in microseconds", | |
85 | OFFSET(pref_duration), AV_OPT_TYPE_INT64, { .i64 = 1000000 }, 0, INT64_MAX, PARAM }, | |
86 | { NULL }, | |
87 | }; | |
88 | ||
89 | #define OGG_CLASS(flavor, name)\ | |
90 | static const AVClass flavor ## _muxer_class = {\ | |
91 | .class_name = #name " muxer",\ | |
92 | .item_name = av_default_item_name,\ | |
93 | .option = options,\ | |
94 | .version = LIBAVUTIL_VERSION_INT,\ | |
95 | }; | |
96 | ||
97 | static void ogg_update_checksum(AVFormatContext *s, AVIOContext *pb, int64_t crc_offset) | |
98 | { | |
99 | int64_t pos = avio_tell(pb); | |
100 | uint32_t checksum = ffio_get_checksum(pb); | |
101 | avio_seek(pb, crc_offset, SEEK_SET); | |
102 | avio_wb32(pb, checksum); | |
103 | avio_seek(pb, pos, SEEK_SET); | |
104 | } | |
105 | ||
106 | static int ogg_write_page(AVFormatContext *s, OGGPage *page, int extra_flags) | |
107 | { | |
108 | OGGStreamContext *oggstream = s->streams[page->stream_index]->priv_data; | |
109 | AVIOContext *pb; | |
110 | int64_t crc_offset; | |
111 | int ret, size; | |
112 | uint8_t *buf; | |
113 | ||
114 | ret = avio_open_dyn_buf(&pb); | |
115 | if (ret < 0) | |
116 | return ret; | |
117 | ffio_init_checksum(pb, ff_crc04C11DB7_update, 0); | |
118 | ffio_wfourcc(pb, "OggS"); | |
119 | avio_w8(pb, 0); | |
120 | avio_w8(pb, page->flags | extra_flags); | |
121 | avio_wl64(pb, page->granule); | |
122 | avio_wl32(pb, oggstream->serial_num); | |
123 | avio_wl32(pb, oggstream->page_counter++); | |
124 | crc_offset = avio_tell(pb); | |
125 | avio_wl32(pb, 0); // crc | |
126 | avio_w8(pb, page->segments_count); | |
127 | avio_write(pb, page->segments, page->segments_count); | |
128 | avio_write(pb, page->data, page->size); | |
129 | ||
130 | ogg_update_checksum(s, pb, crc_offset); | |
131 | avio_flush(pb); | |
132 | ||
133 | size = avio_close_dyn_buf(pb, &buf); | |
134 | if (size < 0) | |
135 | return size; | |
136 | ||
137 | avio_write(s->pb, buf, size); | |
138 | avio_flush(s->pb); | |
139 | av_free(buf); | |
140 | oggstream->page_count--; | |
141 | return 0; | |
142 | } | |
143 | ||
144 | static int ogg_key_granule(OGGStreamContext *oggstream, int64_t granule) | |
145 | { | |
146 | return oggstream->kfgshift && !(granule & ((1<<oggstream->kfgshift)-1)); | |
147 | } | |
148 | ||
149 | static int64_t ogg_granule_to_timestamp(OGGStreamContext *oggstream, int64_t granule) | |
150 | { | |
151 | if (oggstream->kfgshift) | |
152 | return (granule>>oggstream->kfgshift) + | |
153 | (granule & ((1<<oggstream->kfgshift)-1)); | |
154 | else | |
155 | return granule; | |
156 | } | |
157 | ||
158 | static int ogg_compare_granule(AVFormatContext *s, OGGPage *next, OGGPage *page) | |
159 | { | |
160 | AVStream *st2 = s->streams[next->stream_index]; | |
161 | AVStream *st = s->streams[page->stream_index]; | |
162 | int64_t next_granule, cur_granule; | |
163 | ||
164 | if (next->granule == -1 || page->granule == -1) | |
165 | return 0; | |
166 | ||
167 | next_granule = av_rescale_q(ogg_granule_to_timestamp(st2->priv_data, next->granule), | |
168 | st2->time_base, AV_TIME_BASE_Q); | |
169 | cur_granule = av_rescale_q(ogg_granule_to_timestamp(st->priv_data, page->granule), | |
170 | st ->time_base, AV_TIME_BASE_Q); | |
171 | return next_granule > cur_granule; | |
172 | } | |
173 | ||
174 | static int ogg_reset_cur_page(OGGStreamContext *oggstream) | |
175 | { | |
176 | oggstream->page.granule = -1; | |
177 | oggstream->page.flags = 0; | |
178 | oggstream->page.segments_count = 0; | |
179 | oggstream->page.size = 0; | |
180 | return 0; | |
181 | } | |
182 | ||
183 | static int ogg_buffer_page(AVFormatContext *s, OGGStreamContext *oggstream) | |
184 | { | |
185 | OGGContext *ogg = s->priv_data; | |
186 | OGGPageList **p = &ogg->page_list; | |
187 | OGGPageList *l = av_mallocz(sizeof(*l)); | |
188 | ||
189 | if (!l) | |
190 | return AVERROR(ENOMEM); | |
191 | l->page = oggstream->page; | |
192 | ||
193 | oggstream->page.start_granule = oggstream->page.granule; | |
194 | oggstream->page_count++; | |
195 | ogg_reset_cur_page(oggstream); | |
196 | ||
197 | while (*p) { | |
198 | if (ogg_compare_granule(s, &(*p)->page, &l->page)) | |
199 | break; | |
200 | p = &(*p)->next; | |
201 | } | |
202 | l->next = *p; | |
203 | *p = l; | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static int ogg_buffer_data(AVFormatContext *s, AVStream *st, | |
209 | uint8_t *data, unsigned size, int64_t granule, | |
210 | int header) | |
211 | { | |
212 | OGGStreamContext *oggstream = st->priv_data; | |
213 | OGGContext *ogg = s->priv_data; | |
214 | int total_segments = size / 255 + 1; | |
215 | uint8_t *p = data; | |
216 | int i, segments, len, flush = 0; | |
217 | ||
218 | // Handles VFR by flushing page because this frame needs to have a timestamp | |
219 | // For theora, keyframes also need to have a timestamp to correctly mark | |
220 | // them as such, otherwise seeking will not work correctly at the very | |
221 | // least with old libogg versions. | |
222 | // Do not try to flush header packets though, that will create broken files. | |
223 | if (st->codec->codec_id == AV_CODEC_ID_THEORA && !header && | |
224 | (ogg_granule_to_timestamp(oggstream, granule) > | |
225 | ogg_granule_to_timestamp(oggstream, oggstream->last_granule) + 1 || | |
226 | ogg_key_granule(oggstream, granule))) { | |
227 | if (oggstream->page.granule != -1) | |
228 | ogg_buffer_page(s, oggstream); | |
229 | flush = 1; | |
230 | } | |
231 | ||
232 | // avoid a continued page | |
233 | if (!header && oggstream->page.size > 0 && | |
234 | MAX_PAGE_SIZE - oggstream->page.size < size) { | |
235 | ogg_buffer_page(s, oggstream); | |
236 | } | |
237 | ||
238 | for (i = 0; i < total_segments; ) { | |
239 | OGGPage *page = &oggstream->page; | |
240 | ||
241 | segments = FFMIN(total_segments - i, 255 - page->segments_count); | |
242 | ||
243 | if (i && !page->segments_count) | |
244 | page->flags |= 1; // continued packet | |
245 | ||
246 | memset(page->segments+page->segments_count, 255, segments - 1); | |
247 | page->segments_count += segments - 1; | |
248 | ||
249 | len = FFMIN(size, segments*255); | |
250 | page->segments[page->segments_count++] = len - (segments-1)*255; | |
251 | memcpy(page->data+page->size, p, len); | |
252 | p += len; | |
253 | size -= len; | |
254 | i += segments; | |
255 | page->size += len; | |
256 | ||
257 | if (i == total_segments) | |
258 | page->granule = granule; | |
259 | ||
260 | if (!header) { | |
261 | AVStream *st = s->streams[page->stream_index]; | |
262 | ||
263 | int64_t start = av_rescale_q(page->start_granule, st->time_base, | |
264 | AV_TIME_BASE_Q); | |
265 | int64_t next = av_rescale_q(page->granule, st->time_base, | |
266 | AV_TIME_BASE_Q); | |
267 | ||
268 | if (page->segments_count == 255 || | |
269 | (ogg->pref_size > 0 && page->size >= ogg->pref_size) || | |
270 | (ogg->pref_duration > 0 && next - start >= ogg->pref_duration)) { | |
271 | ogg_buffer_page(s, oggstream); | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
276 | if (flush && oggstream->page.granule != -1) | |
277 | ogg_buffer_page(s, oggstream); | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | static uint8_t *ogg_write_vorbiscomment(int offset, int bitexact, | |
283 | int *header_len, AVDictionary **m, int framing_bit) | |
284 | { | |
285 | const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; | |
286 | int size; | |
287 | uint8_t *p, *p0; | |
288 | ||
289 | ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL); | |
290 | ||
291 | size = offset + ff_vorbiscomment_length(*m, vendor) + framing_bit; | |
292 | p = av_mallocz(size); | |
293 | if (!p) | |
294 | return NULL; | |
295 | p0 = p; | |
296 | ||
297 | p += offset; | |
298 | ff_vorbiscomment_write(&p, m, vendor); | |
299 | if (framing_bit) | |
300 | bytestream_put_byte(&p, 1); | |
301 | ||
302 | *header_len = size; | |
303 | return p0; | |
304 | } | |
305 | ||
306 | static int ogg_build_flac_headers(AVCodecContext *avctx, | |
307 | OGGStreamContext *oggstream, int bitexact, | |
308 | AVDictionary **m) | |
309 | { | |
310 | enum FLACExtradataFormat format; | |
311 | uint8_t *streaminfo; | |
312 | uint8_t *p; | |
313 | ||
314 | if (!avpriv_flac_is_extradata_valid(avctx, &format, &streaminfo)) | |
315 | return -1; | |
316 | ||
317 | // first packet: STREAMINFO | |
318 | oggstream->header_len[0] = 51; | |
319 | oggstream->header[0] = av_mallocz(51); // per ogg flac specs | |
320 | p = oggstream->header[0]; | |
321 | if (!p) | |
322 | return AVERROR(ENOMEM); | |
323 | bytestream_put_byte(&p, 0x7F); | |
324 | bytestream_put_buffer(&p, "FLAC", 4); | |
325 | bytestream_put_byte(&p, 1); // major version | |
326 | bytestream_put_byte(&p, 0); // minor version | |
327 | bytestream_put_be16(&p, 1); // headers packets without this one | |
328 | bytestream_put_buffer(&p, "fLaC", 4); | |
329 | bytestream_put_byte(&p, 0x00); // streaminfo | |
330 | bytestream_put_be24(&p, 34); | |
331 | bytestream_put_buffer(&p, streaminfo, FLAC_STREAMINFO_SIZE); | |
332 | ||
333 | // second packet: VorbisComment | |
334 | p = ogg_write_vorbiscomment(4, bitexact, &oggstream->header_len[1], m, 0); | |
335 | if (!p) | |
336 | return AVERROR(ENOMEM); | |
337 | oggstream->header[1] = p; | |
338 | bytestream_put_byte(&p, 0x84); // last metadata block and vorbis comment | |
339 | bytestream_put_be24(&p, oggstream->header_len[1] - 4); | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | #define SPEEX_HEADER_SIZE 80 | |
345 | ||
346 | static int ogg_build_speex_headers(AVCodecContext *avctx, | |
347 | OGGStreamContext *oggstream, int bitexact, | |
348 | AVDictionary **m) | |
349 | { | |
350 | uint8_t *p; | |
351 | ||
352 | if (avctx->extradata_size < SPEEX_HEADER_SIZE) | |
353 | return -1; | |
354 | ||
355 | // first packet: Speex header | |
356 | p = av_mallocz(SPEEX_HEADER_SIZE); | |
357 | if (!p) | |
358 | return AVERROR(ENOMEM); | |
359 | oggstream->header[0] = p; | |
360 | oggstream->header_len[0] = SPEEX_HEADER_SIZE; | |
361 | bytestream_put_buffer(&p, avctx->extradata, SPEEX_HEADER_SIZE); | |
362 | AV_WL32(&oggstream->header[0][68], 0); // set extra_headers to 0 | |
363 | ||
364 | // second packet: VorbisComment | |
365 | p = ogg_write_vorbiscomment(0, bitexact, &oggstream->header_len[1], m, 0); | |
366 | if (!p) | |
367 | return AVERROR(ENOMEM); | |
368 | oggstream->header[1] = p; | |
369 | ||
370 | return 0; | |
371 | } | |
372 | ||
373 | #define OPUS_HEADER_SIZE 19 | |
374 | ||
375 | static int ogg_build_opus_headers(AVCodecContext *avctx, | |
376 | OGGStreamContext *oggstream, int bitexact, | |
377 | AVDictionary **m) | |
378 | { | |
379 | uint8_t *p; | |
380 | ||
381 | if (avctx->extradata_size < OPUS_HEADER_SIZE) | |
382 | return -1; | |
383 | ||
384 | /* first packet: Opus header */ | |
385 | p = av_mallocz(avctx->extradata_size); | |
386 | if (!p) | |
387 | return AVERROR(ENOMEM); | |
388 | oggstream->header[0] = p; | |
389 | oggstream->header_len[0] = avctx->extradata_size; | |
390 | bytestream_put_buffer(&p, avctx->extradata, avctx->extradata_size); | |
391 | ||
392 | /* second packet: VorbisComment */ | |
393 | p = ogg_write_vorbiscomment(8, bitexact, &oggstream->header_len[1], m, 0); | |
394 | if (!p) | |
395 | return AVERROR(ENOMEM); | |
396 | oggstream->header[1] = p; | |
397 | bytestream_put_buffer(&p, "OpusTags", 8); | |
398 | ||
399 | return 0; | |
400 | } | |
401 | ||
402 | static void ogg_write_pages(AVFormatContext *s, int flush) | |
403 | { | |
404 | OGGContext *ogg = s->priv_data; | |
405 | OGGPageList *next, *p; | |
406 | ||
407 | if (!ogg->page_list) | |
408 | return; | |
409 | ||
410 | for (p = ogg->page_list; p; ) { | |
411 | OGGStreamContext *oggstream = | |
412 | s->streams[p->page.stream_index]->priv_data; | |
413 | if (oggstream->page_count < 2 && !flush) | |
414 | break; | |
415 | ogg_write_page(s, &p->page, | |
416 | flush == 1 && oggstream->page_count == 1 ? 4 : 0); // eos | |
417 | next = p->next; | |
418 | av_freep(&p); | |
419 | p = next; | |
420 | } | |
421 | ogg->page_list = p; | |
422 | } | |
423 | ||
424 | static int ogg_write_header(AVFormatContext *s) | |
425 | { | |
426 | OGGContext *ogg = s->priv_data; | |
427 | OGGStreamContext *oggstream = NULL; | |
428 | int i, j; | |
429 | ||
430 | if (ogg->pref_size) | |
431 | av_log(s, AV_LOG_WARNING, "The pagesize option is deprecated\n"); | |
432 | ||
433 | for (i = 0; i < s->nb_streams; i++) { | |
434 | AVStream *st = s->streams[i]; | |
435 | unsigned serial_num = i; | |
436 | ||
437 | if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { | |
438 | if (st->codec->codec_id == AV_CODEC_ID_OPUS) | |
439 | /* Opus requires a fixed 48kHz clock */ | |
440 | avpriv_set_pts_info(st, 64, 1, 48000); | |
441 | else | |
442 | avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); | |
443 | } | |
444 | ||
445 | if (st->codec->codec_id != AV_CODEC_ID_VORBIS && | |
446 | st->codec->codec_id != AV_CODEC_ID_THEORA && | |
447 | st->codec->codec_id != AV_CODEC_ID_SPEEX && | |
448 | st->codec->codec_id != AV_CODEC_ID_FLAC && | |
449 | st->codec->codec_id != AV_CODEC_ID_OPUS) { | |
450 | av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i); | |
451 | return -1; | |
452 | } | |
453 | ||
454 | if (!st->codec->extradata || !st->codec->extradata_size) { | |
455 | av_log(s, AV_LOG_ERROR, "No extradata present\n"); | |
456 | return -1; | |
457 | } | |
458 | oggstream = av_mallocz(sizeof(*oggstream)); | |
459 | if (!oggstream) | |
460 | return AVERROR(ENOMEM); | |
461 | ||
462 | oggstream->page.stream_index = i; | |
463 | ||
464 | if (!(s->flags & AVFMT_FLAG_BITEXACT)) | |
465 | do { | |
466 | serial_num = av_get_random_seed(); | |
467 | for (j = 0; j < i; j++) { | |
468 | OGGStreamContext *sc = s->streams[j]->priv_data; | |
469 | if (serial_num == sc->serial_num) | |
470 | break; | |
471 | } | |
472 | } while (j < i); | |
473 | oggstream->serial_num = serial_num; | |
474 | ||
475 | av_dict_copy(&st->metadata, s->metadata, AV_DICT_DONT_OVERWRITE); | |
476 | ||
477 | st->priv_data = oggstream; | |
478 | if (st->codec->codec_id == AV_CODEC_ID_FLAC) { | |
479 | int err = ogg_build_flac_headers(st->codec, oggstream, | |
480 | s->flags & AVFMT_FLAG_BITEXACT, | |
481 | &st->metadata); | |
482 | if (err) { | |
483 | av_log(s, AV_LOG_ERROR, "Error writing FLAC headers\n"); | |
484 | av_freep(&st->priv_data); | |
485 | return err; | |
486 | } | |
487 | } else if (st->codec->codec_id == AV_CODEC_ID_SPEEX) { | |
488 | int err = ogg_build_speex_headers(st->codec, oggstream, | |
489 | s->flags & AVFMT_FLAG_BITEXACT, | |
490 | &st->metadata); | |
491 | if (err) { | |
492 | av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n"); | |
493 | av_freep(&st->priv_data); | |
494 | return err; | |
495 | } | |
496 | } else if (st->codec->codec_id == AV_CODEC_ID_OPUS) { | |
497 | int err = ogg_build_opus_headers(st->codec, oggstream, | |
498 | s->flags & AVFMT_FLAG_BITEXACT, | |
499 | &st->metadata); | |
500 | if (err) { | |
501 | av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n"); | |
502 | av_freep(&st->priv_data); | |
503 | return err; | |
504 | } | |
505 | } else { | |
506 | uint8_t *p; | |
507 | const char *cstr = st->codec->codec_id == AV_CODEC_ID_VORBIS ? "vorbis" : "theora"; | |
508 | int header_type = st->codec->codec_id == AV_CODEC_ID_VORBIS ? 3 : 0x81; | |
509 | int framing_bit = st->codec->codec_id == AV_CODEC_ID_VORBIS ? 1 : 0; | |
510 | ||
511 | if (avpriv_split_xiph_headers(st->codec->extradata, st->codec->extradata_size, | |
512 | st->codec->codec_id == AV_CODEC_ID_VORBIS ? 30 : 42, | |
513 | oggstream->header, oggstream->header_len) < 0) { | |
514 | av_log(s, AV_LOG_ERROR, "Extradata corrupted\n"); | |
515 | av_freep(&st->priv_data); | |
516 | return -1; | |
517 | } | |
518 | ||
519 | p = ogg_write_vorbiscomment(7, s->flags & AVFMT_FLAG_BITEXACT, | |
520 | &oggstream->header_len[1], &st->metadata, | |
521 | framing_bit); | |
522 | oggstream->header[1] = p; | |
523 | if (!p) | |
524 | return AVERROR(ENOMEM); | |
525 | ||
526 | bytestream_put_byte(&p, header_type); | |
527 | bytestream_put_buffer(&p, cstr, 6); | |
528 | ||
529 | if (st->codec->codec_id == AV_CODEC_ID_THEORA) { | |
530 | /** KFGSHIFT is the width of the less significant section of the granule position | |
531 | The less significant section is the frame count since the last keyframe */ | |
532 | oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5); | |
533 | oggstream->vrev = oggstream->header[0][9]; | |
534 | av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n", | |
535 | oggstream->kfgshift, oggstream->vrev); | |
536 | } | |
537 | } | |
538 | } | |
539 | ||
540 | for (j = 0; j < s->nb_streams; j++) { | |
541 | OGGStreamContext *oggstream = s->streams[j]->priv_data; | |
542 | ogg_buffer_data(s, s->streams[j], oggstream->header[0], | |
543 | oggstream->header_len[0], 0, 1); | |
544 | oggstream->page.flags |= 2; // bos | |
545 | ogg_buffer_page(s, oggstream); | |
546 | } | |
547 | for (j = 0; j < s->nb_streams; j++) { | |
548 | AVStream *st = s->streams[j]; | |
549 | OGGStreamContext *oggstream = st->priv_data; | |
550 | for (i = 1; i < 3; i++) { | |
551 | if (oggstream->header_len[i]) | |
552 | ogg_buffer_data(s, st, oggstream->header[i], | |
553 | oggstream->header_len[i], 0, 1); | |
554 | } | |
555 | ogg_buffer_page(s, oggstream); | |
556 | } | |
557 | ||
558 | oggstream->page.start_granule = AV_NOPTS_VALUE; | |
559 | ||
560 | ogg_write_pages(s, 2); | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | static int ogg_write_packet_internal(AVFormatContext *s, AVPacket *pkt) | |
566 | { | |
567 | AVStream *st = s->streams[pkt->stream_index]; | |
568 | OGGStreamContext *oggstream = st->priv_data; | |
569 | int ret; | |
570 | int64_t granule; | |
571 | ||
572 | if (st->codec->codec_id == AV_CODEC_ID_THEORA) { | |
573 | int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts + pkt->duration; | |
574 | int pframe_count; | |
575 | if (pkt->flags & AV_PKT_FLAG_KEY) | |
576 | oggstream->last_kf_pts = pts; | |
577 | pframe_count = pts - oggstream->last_kf_pts; | |
578 | // prevent frame count from overflow if key frame flag is not set | |
579 | if (pframe_count >= (1<<oggstream->kfgshift)) { | |
580 | oggstream->last_kf_pts += pframe_count; | |
581 | pframe_count = 0; | |
582 | } | |
583 | granule = (oggstream->last_kf_pts<<oggstream->kfgshift) | pframe_count; | |
584 | } else if (st->codec->codec_id == AV_CODEC_ID_OPUS) | |
585 | granule = pkt->pts + pkt->duration + av_rescale_q(st->codec->delay, (AVRational){ 1, st->codec->sample_rate }, st->time_base); | |
586 | else | |
587 | granule = pkt->pts + pkt->duration; | |
588 | ||
589 | if (oggstream->page.start_granule == AV_NOPTS_VALUE) | |
590 | oggstream->page.start_granule = pkt->pts; | |
591 | ||
592 | ret = ogg_buffer_data(s, st, pkt->data, pkt->size, granule, 0); | |
593 | if (ret < 0) | |
594 | return ret; | |
595 | ||
596 | ogg_write_pages(s, 0); | |
597 | ||
598 | oggstream->last_granule = granule; | |
599 | ||
600 | return 0; | |
601 | } | |
602 | ||
603 | static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt) | |
604 | { | |
605 | int i; | |
606 | ||
607 | if (pkt) | |
608 | return ogg_write_packet_internal(s, pkt); | |
609 | ||
610 | for (i = 0; i < s->nb_streams; i++) { | |
611 | OGGStreamContext *oggstream = s->streams[i]->priv_data; | |
612 | if (oggstream->page.segments_count) | |
613 | ogg_buffer_page(s, oggstream); | |
614 | } | |
615 | ||
616 | ogg_write_pages(s, 2); | |
617 | return 0; | |
618 | } | |
619 | ||
620 | static int ogg_write_trailer(AVFormatContext *s) | |
621 | { | |
622 | int i; | |
623 | ||
624 | /* flush current page if needed */ | |
625 | for (i = 0; i < s->nb_streams; i++) { | |
626 | OGGStreamContext *oggstream = s->streams[i]->priv_data; | |
627 | ||
628 | if (oggstream->page.size > 0) | |
629 | ogg_buffer_page(s, oggstream); | |
630 | } | |
631 | ||
632 | ogg_write_pages(s, 1); | |
633 | ||
634 | for (i = 0; i < s->nb_streams; i++) { | |
635 | AVStream *st = s->streams[i]; | |
636 | OGGStreamContext *oggstream = st->priv_data; | |
637 | if (st->codec->codec_id == AV_CODEC_ID_FLAC || | |
638 | st->codec->codec_id == AV_CODEC_ID_SPEEX || | |
639 | st->codec->codec_id == AV_CODEC_ID_OPUS) { | |
640 | av_freep(&oggstream->header[0]); | |
641 | } | |
642 | av_freep(&oggstream->header[1]); | |
643 | av_freep(&st->priv_data); | |
644 | } | |
645 | return 0; | |
646 | } | |
647 | ||
648 | #if CONFIG_OGG_MUXER | |
649 | OGG_CLASS(ogg, Ogg) | |
650 | AVOutputFormat ff_ogg_muxer = { | |
651 | .name = "ogg", | |
652 | .long_name = NULL_IF_CONFIG_SMALL("Ogg"), | |
653 | .mime_type = "application/ogg", | |
654 | .extensions = "ogg,ogv" | |
655 | #if !CONFIG_SPX_MUXER | |
656 | ",spx" | |
657 | #endif | |
658 | #if !CONFIG_OPUS_MUXER | |
659 | ",opus" | |
660 | #endif | |
661 | , | |
662 | .priv_data_size = sizeof(OGGContext), | |
663 | .audio_codec = CONFIG_LIBVORBIS_ENCODER ? | |
664 | AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC, | |
665 | .video_codec = AV_CODEC_ID_THEORA, | |
666 | .write_header = ogg_write_header, | |
667 | .write_packet = ogg_write_packet, | |
668 | .write_trailer = ogg_write_trailer, | |
669 | .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, | |
670 | .priv_class = &ogg_muxer_class, | |
671 | }; | |
672 | #endif | |
673 | ||
674 | #if CONFIG_OGA_MUXER | |
675 | OGG_CLASS(oga, Ogg audio) | |
676 | AVOutputFormat ff_oga_muxer = { | |
677 | .name = "oga", | |
678 | .long_name = NULL_IF_CONFIG_SMALL("Ogg Audio"), | |
679 | .mime_type = "audio/ogg", | |
680 | .extensions = "oga", | |
681 | .priv_data_size = sizeof(OGGContext), | |
682 | .audio_codec = CONFIG_LIBVORBIS_ENCODER ? | |
683 | AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC, | |
684 | .write_header = ogg_write_header, | |
685 | .write_packet = ogg_write_packet, | |
686 | .write_trailer = ogg_write_trailer, | |
687 | .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, | |
688 | .priv_class = &oga_muxer_class, | |
689 | }; | |
690 | #endif | |
691 | ||
692 | #if CONFIG_SPX_MUXER | |
693 | OGG_CLASS(spx, Ogg Speex) | |
694 | AVOutputFormat ff_spx_muxer = { | |
695 | .name = "spx", | |
696 | .long_name = NULL_IF_CONFIG_SMALL("Ogg Speex"), | |
697 | .mime_type = "audio/ogg", | |
698 | .extensions = "spx", | |
699 | .priv_data_size = sizeof(OGGContext), | |
700 | .audio_codec = AV_CODEC_ID_SPEEX, | |
701 | .write_header = ogg_write_header, | |
702 | .write_packet = ogg_write_packet, | |
703 | .write_trailer = ogg_write_trailer, | |
704 | .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, | |
705 | .priv_class = &spx_muxer_class, | |
706 | }; | |
707 | #endif | |
708 | ||
709 | #if CONFIG_OPUS_MUXER | |
710 | OGG_CLASS(opus, Ogg Opus) | |
711 | AVOutputFormat ff_opus_muxer = { | |
712 | .name = "opus", | |
713 | .long_name = NULL_IF_CONFIG_SMALL("Ogg Opus"), | |
714 | .mime_type = "audio/ogg", | |
715 | .extensions = "opus", | |
716 | .priv_data_size = sizeof(OGGContext), | |
717 | .audio_codec = AV_CODEC_ID_OPUS, | |
718 | .write_header = ogg_write_header, | |
719 | .write_packet = ogg_write_packet, | |
720 | .write_trailer = ogg_write_trailer, | |
721 | .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, | |
722 | .priv_class = &opus_muxer_class, | |
723 | }; | |
724 | #endif |