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 | { | |
2ba45a60 DM |
310 | uint8_t *p; |
311 | ||
f6fa7814 DM |
312 | if (avctx->extradata_size < FLAC_STREAMINFO_SIZE) |
313 | return AVERROR(EINVAL); | |
2ba45a60 DM |
314 | |
315 | // first packet: STREAMINFO | |
316 | oggstream->header_len[0] = 51; | |
317 | oggstream->header[0] = av_mallocz(51); // per ogg flac specs | |
318 | p = oggstream->header[0]; | |
319 | if (!p) | |
320 | return AVERROR(ENOMEM); | |
321 | bytestream_put_byte(&p, 0x7F); | |
322 | bytestream_put_buffer(&p, "FLAC", 4); | |
323 | bytestream_put_byte(&p, 1); // major version | |
324 | bytestream_put_byte(&p, 0); // minor version | |
325 | bytestream_put_be16(&p, 1); // headers packets without this one | |
326 | bytestream_put_buffer(&p, "fLaC", 4); | |
327 | bytestream_put_byte(&p, 0x00); // streaminfo | |
328 | bytestream_put_be24(&p, 34); | |
f6fa7814 | 329 | bytestream_put_buffer(&p, avctx->extradata, FLAC_STREAMINFO_SIZE); |
2ba45a60 DM |
330 | |
331 | // second packet: VorbisComment | |
332 | p = ogg_write_vorbiscomment(4, bitexact, &oggstream->header_len[1], m, 0); | |
333 | if (!p) | |
334 | return AVERROR(ENOMEM); | |
335 | oggstream->header[1] = p; | |
336 | bytestream_put_byte(&p, 0x84); // last metadata block and vorbis comment | |
337 | bytestream_put_be24(&p, oggstream->header_len[1] - 4); | |
338 | ||
339 | return 0; | |
340 | } | |
341 | ||
342 | #define SPEEX_HEADER_SIZE 80 | |
343 | ||
344 | static int ogg_build_speex_headers(AVCodecContext *avctx, | |
345 | OGGStreamContext *oggstream, int bitexact, | |
346 | AVDictionary **m) | |
347 | { | |
348 | uint8_t *p; | |
349 | ||
350 | if (avctx->extradata_size < SPEEX_HEADER_SIZE) | |
f6fa7814 | 351 | return AVERROR_INVALIDDATA; |
2ba45a60 DM |
352 | |
353 | // first packet: Speex header | |
354 | p = av_mallocz(SPEEX_HEADER_SIZE); | |
355 | if (!p) | |
356 | return AVERROR(ENOMEM); | |
357 | oggstream->header[0] = p; | |
358 | oggstream->header_len[0] = SPEEX_HEADER_SIZE; | |
359 | bytestream_put_buffer(&p, avctx->extradata, SPEEX_HEADER_SIZE); | |
360 | AV_WL32(&oggstream->header[0][68], 0); // set extra_headers to 0 | |
361 | ||
362 | // second packet: VorbisComment | |
363 | p = ogg_write_vorbiscomment(0, bitexact, &oggstream->header_len[1], m, 0); | |
364 | if (!p) | |
365 | return AVERROR(ENOMEM); | |
366 | oggstream->header[1] = p; | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
371 | #define OPUS_HEADER_SIZE 19 | |
372 | ||
373 | static int ogg_build_opus_headers(AVCodecContext *avctx, | |
374 | OGGStreamContext *oggstream, int bitexact, | |
375 | AVDictionary **m) | |
376 | { | |
377 | uint8_t *p; | |
378 | ||
379 | if (avctx->extradata_size < OPUS_HEADER_SIZE) | |
f6fa7814 | 380 | return AVERROR_INVALIDDATA; |
2ba45a60 DM |
381 | |
382 | /* first packet: Opus header */ | |
383 | p = av_mallocz(avctx->extradata_size); | |
384 | if (!p) | |
385 | return AVERROR(ENOMEM); | |
386 | oggstream->header[0] = p; | |
387 | oggstream->header_len[0] = avctx->extradata_size; | |
388 | bytestream_put_buffer(&p, avctx->extradata, avctx->extradata_size); | |
389 | ||
390 | /* second packet: VorbisComment */ | |
391 | p = ogg_write_vorbiscomment(8, bitexact, &oggstream->header_len[1], m, 0); | |
392 | if (!p) | |
393 | return AVERROR(ENOMEM); | |
394 | oggstream->header[1] = p; | |
395 | bytestream_put_buffer(&p, "OpusTags", 8); | |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | static void ogg_write_pages(AVFormatContext *s, int flush) | |
401 | { | |
402 | OGGContext *ogg = s->priv_data; | |
403 | OGGPageList *next, *p; | |
404 | ||
405 | if (!ogg->page_list) | |
406 | return; | |
407 | ||
408 | for (p = ogg->page_list; p; ) { | |
409 | OGGStreamContext *oggstream = | |
410 | s->streams[p->page.stream_index]->priv_data; | |
411 | if (oggstream->page_count < 2 && !flush) | |
412 | break; | |
413 | ogg_write_page(s, &p->page, | |
414 | flush == 1 && oggstream->page_count == 1 ? 4 : 0); // eos | |
415 | next = p->next; | |
416 | av_freep(&p); | |
417 | p = next; | |
418 | } | |
419 | ogg->page_list = p; | |
420 | } | |
421 | ||
422 | static int ogg_write_header(AVFormatContext *s) | |
423 | { | |
424 | OGGContext *ogg = s->priv_data; | |
425 | OGGStreamContext *oggstream = NULL; | |
426 | int i, j; | |
427 | ||
428 | if (ogg->pref_size) | |
429 | av_log(s, AV_LOG_WARNING, "The pagesize option is deprecated\n"); | |
430 | ||
431 | for (i = 0; i < s->nb_streams; i++) { | |
432 | AVStream *st = s->streams[i]; | |
433 | unsigned serial_num = i; | |
434 | ||
435 | if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { | |
436 | if (st->codec->codec_id == AV_CODEC_ID_OPUS) | |
437 | /* Opus requires a fixed 48kHz clock */ | |
438 | avpriv_set_pts_info(st, 64, 1, 48000); | |
439 | else | |
440 | avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); | |
441 | } | |
442 | ||
443 | if (st->codec->codec_id != AV_CODEC_ID_VORBIS && | |
444 | st->codec->codec_id != AV_CODEC_ID_THEORA && | |
445 | st->codec->codec_id != AV_CODEC_ID_SPEEX && | |
446 | st->codec->codec_id != AV_CODEC_ID_FLAC && | |
447 | st->codec->codec_id != AV_CODEC_ID_OPUS) { | |
448 | av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i); | |
f6fa7814 | 449 | return AVERROR(EINVAL); |
2ba45a60 DM |
450 | } |
451 | ||
452 | if (!st->codec->extradata || !st->codec->extradata_size) { | |
453 | av_log(s, AV_LOG_ERROR, "No extradata present\n"); | |
f6fa7814 | 454 | return AVERROR_INVALIDDATA; |
2ba45a60 DM |
455 | } |
456 | oggstream = av_mallocz(sizeof(*oggstream)); | |
457 | if (!oggstream) | |
458 | return AVERROR(ENOMEM); | |
459 | ||
460 | oggstream->page.stream_index = i; | |
461 | ||
462 | if (!(s->flags & AVFMT_FLAG_BITEXACT)) | |
463 | do { | |
464 | serial_num = av_get_random_seed(); | |
465 | for (j = 0; j < i; j++) { | |
466 | OGGStreamContext *sc = s->streams[j]->priv_data; | |
467 | if (serial_num == sc->serial_num) | |
468 | break; | |
469 | } | |
470 | } while (j < i); | |
471 | oggstream->serial_num = serial_num; | |
472 | ||
473 | av_dict_copy(&st->metadata, s->metadata, AV_DICT_DONT_OVERWRITE); | |
474 | ||
475 | st->priv_data = oggstream; | |
476 | if (st->codec->codec_id == AV_CODEC_ID_FLAC) { | |
477 | int err = ogg_build_flac_headers(st->codec, oggstream, | |
478 | s->flags & AVFMT_FLAG_BITEXACT, | |
479 | &st->metadata); | |
480 | if (err) { | |
481 | av_log(s, AV_LOG_ERROR, "Error writing FLAC headers\n"); | |
482 | av_freep(&st->priv_data); | |
483 | return err; | |
484 | } | |
485 | } else if (st->codec->codec_id == AV_CODEC_ID_SPEEX) { | |
486 | int err = ogg_build_speex_headers(st->codec, oggstream, | |
487 | s->flags & AVFMT_FLAG_BITEXACT, | |
488 | &st->metadata); | |
489 | if (err) { | |
490 | av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n"); | |
491 | av_freep(&st->priv_data); | |
492 | return err; | |
493 | } | |
494 | } else if (st->codec->codec_id == AV_CODEC_ID_OPUS) { | |
495 | int err = ogg_build_opus_headers(st->codec, oggstream, | |
496 | s->flags & AVFMT_FLAG_BITEXACT, | |
497 | &st->metadata); | |
498 | if (err) { | |
499 | av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n"); | |
500 | av_freep(&st->priv_data); | |
501 | return err; | |
502 | } | |
503 | } else { | |
504 | uint8_t *p; | |
505 | const char *cstr = st->codec->codec_id == AV_CODEC_ID_VORBIS ? "vorbis" : "theora"; | |
506 | int header_type = st->codec->codec_id == AV_CODEC_ID_VORBIS ? 3 : 0x81; | |
507 | int framing_bit = st->codec->codec_id == AV_CODEC_ID_VORBIS ? 1 : 0; | |
508 | ||
509 | if (avpriv_split_xiph_headers(st->codec->extradata, st->codec->extradata_size, | |
510 | st->codec->codec_id == AV_CODEC_ID_VORBIS ? 30 : 42, | |
511 | oggstream->header, oggstream->header_len) < 0) { | |
512 | av_log(s, AV_LOG_ERROR, "Extradata corrupted\n"); | |
513 | av_freep(&st->priv_data); | |
f6fa7814 | 514 | return AVERROR_INVALIDDATA; |
2ba45a60 DM |
515 | } |
516 | ||
517 | p = ogg_write_vorbiscomment(7, s->flags & AVFMT_FLAG_BITEXACT, | |
518 | &oggstream->header_len[1], &st->metadata, | |
519 | framing_bit); | |
520 | oggstream->header[1] = p; | |
521 | if (!p) | |
522 | return AVERROR(ENOMEM); | |
523 | ||
524 | bytestream_put_byte(&p, header_type); | |
525 | bytestream_put_buffer(&p, cstr, 6); | |
526 | ||
527 | if (st->codec->codec_id == AV_CODEC_ID_THEORA) { | |
528 | /** KFGSHIFT is the width of the less significant section of the granule position | |
529 | The less significant section is the frame count since the last keyframe */ | |
530 | oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5); | |
531 | oggstream->vrev = oggstream->header[0][9]; | |
532 | av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n", | |
533 | oggstream->kfgshift, oggstream->vrev); | |
534 | } | |
535 | } | |
536 | } | |
537 | ||
538 | for (j = 0; j < s->nb_streams; j++) { | |
539 | OGGStreamContext *oggstream = s->streams[j]->priv_data; | |
540 | ogg_buffer_data(s, s->streams[j], oggstream->header[0], | |
541 | oggstream->header_len[0], 0, 1); | |
542 | oggstream->page.flags |= 2; // bos | |
543 | ogg_buffer_page(s, oggstream); | |
544 | } | |
545 | for (j = 0; j < s->nb_streams; j++) { | |
546 | AVStream *st = s->streams[j]; | |
547 | OGGStreamContext *oggstream = st->priv_data; | |
548 | for (i = 1; i < 3; i++) { | |
549 | if (oggstream->header_len[i]) | |
550 | ogg_buffer_data(s, st, oggstream->header[i], | |
551 | oggstream->header_len[i], 0, 1); | |
552 | } | |
553 | ogg_buffer_page(s, oggstream); | |
554 | } | |
555 | ||
556 | oggstream->page.start_granule = AV_NOPTS_VALUE; | |
557 | ||
558 | ogg_write_pages(s, 2); | |
559 | ||
560 | return 0; | |
561 | } | |
562 | ||
563 | static int ogg_write_packet_internal(AVFormatContext *s, AVPacket *pkt) | |
564 | { | |
565 | AVStream *st = s->streams[pkt->stream_index]; | |
566 | OGGStreamContext *oggstream = st->priv_data; | |
567 | int ret; | |
568 | int64_t granule; | |
569 | ||
570 | if (st->codec->codec_id == AV_CODEC_ID_THEORA) { | |
571 | int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts + pkt->duration; | |
572 | int pframe_count; | |
573 | if (pkt->flags & AV_PKT_FLAG_KEY) | |
574 | oggstream->last_kf_pts = pts; | |
575 | pframe_count = pts - oggstream->last_kf_pts; | |
576 | // prevent frame count from overflow if key frame flag is not set | |
577 | if (pframe_count >= (1<<oggstream->kfgshift)) { | |
578 | oggstream->last_kf_pts += pframe_count; | |
579 | pframe_count = 0; | |
580 | } | |
581 | granule = (oggstream->last_kf_pts<<oggstream->kfgshift) | pframe_count; | |
582 | } else if (st->codec->codec_id == AV_CODEC_ID_OPUS) | |
f6fa7814 DM |
583 | granule = pkt->pts + pkt->duration + |
584 | av_rescale_q(st->codec->initial_padding, | |
585 | (AVRational){ 1, st->codec->sample_rate }, | |
586 | st->time_base); | |
2ba45a60 DM |
587 | else |
588 | granule = pkt->pts + pkt->duration; | |
589 | ||
590 | if (oggstream->page.start_granule == AV_NOPTS_VALUE) | |
591 | oggstream->page.start_granule = pkt->pts; | |
592 | ||
593 | ret = ogg_buffer_data(s, st, pkt->data, pkt->size, granule, 0); | |
594 | if (ret < 0) | |
595 | return ret; | |
596 | ||
597 | ogg_write_pages(s, 0); | |
598 | ||
599 | oggstream->last_granule = granule; | |
600 | ||
601 | return 0; | |
602 | } | |
603 | ||
604 | static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt) | |
605 | { | |
606 | int i; | |
607 | ||
608 | if (pkt) | |
609 | return ogg_write_packet_internal(s, pkt); | |
610 | ||
611 | for (i = 0; i < s->nb_streams; i++) { | |
612 | OGGStreamContext *oggstream = s->streams[i]->priv_data; | |
613 | if (oggstream->page.segments_count) | |
614 | ogg_buffer_page(s, oggstream); | |
615 | } | |
616 | ||
617 | ogg_write_pages(s, 2); | |
618 | return 0; | |
619 | } | |
620 | ||
621 | static int ogg_write_trailer(AVFormatContext *s) | |
622 | { | |
623 | int i; | |
624 | ||
625 | /* flush current page if needed */ | |
626 | for (i = 0; i < s->nb_streams; i++) { | |
627 | OGGStreamContext *oggstream = s->streams[i]->priv_data; | |
628 | ||
629 | if (oggstream->page.size > 0) | |
630 | ogg_buffer_page(s, oggstream); | |
631 | } | |
632 | ||
633 | ogg_write_pages(s, 1); | |
634 | ||
635 | for (i = 0; i < s->nb_streams; i++) { | |
636 | AVStream *st = s->streams[i]; | |
637 | OGGStreamContext *oggstream = st->priv_data; | |
638 | if (st->codec->codec_id == AV_CODEC_ID_FLAC || | |
639 | st->codec->codec_id == AV_CODEC_ID_SPEEX || | |
640 | st->codec->codec_id == AV_CODEC_ID_OPUS) { | |
641 | av_freep(&oggstream->header[0]); | |
642 | } | |
643 | av_freep(&oggstream->header[1]); | |
644 | av_freep(&st->priv_data); | |
645 | } | |
646 | return 0; | |
647 | } | |
648 | ||
649 | #if CONFIG_OGG_MUXER | |
650 | OGG_CLASS(ogg, Ogg) | |
651 | AVOutputFormat ff_ogg_muxer = { | |
652 | .name = "ogg", | |
653 | .long_name = NULL_IF_CONFIG_SMALL("Ogg"), | |
654 | .mime_type = "application/ogg", | |
655 | .extensions = "ogg,ogv" | |
656 | #if !CONFIG_SPX_MUXER | |
657 | ",spx" | |
658 | #endif | |
659 | #if !CONFIG_OPUS_MUXER | |
660 | ",opus" | |
661 | #endif | |
662 | , | |
663 | .priv_data_size = sizeof(OGGContext), | |
664 | .audio_codec = CONFIG_LIBVORBIS_ENCODER ? | |
665 | AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC, | |
666 | .video_codec = AV_CODEC_ID_THEORA, | |
667 | .write_header = ogg_write_header, | |
668 | .write_packet = ogg_write_packet, | |
669 | .write_trailer = ogg_write_trailer, | |
670 | .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, | |
671 | .priv_class = &ogg_muxer_class, | |
672 | }; | |
673 | #endif | |
674 | ||
675 | #if CONFIG_OGA_MUXER | |
676 | OGG_CLASS(oga, Ogg audio) | |
677 | AVOutputFormat ff_oga_muxer = { | |
678 | .name = "oga", | |
679 | .long_name = NULL_IF_CONFIG_SMALL("Ogg Audio"), | |
680 | .mime_type = "audio/ogg", | |
681 | .extensions = "oga", | |
682 | .priv_data_size = sizeof(OGGContext), | |
683 | .audio_codec = CONFIG_LIBVORBIS_ENCODER ? | |
684 | AV_CODEC_ID_VORBIS : AV_CODEC_ID_FLAC, | |
685 | .write_header = ogg_write_header, | |
686 | .write_packet = ogg_write_packet, | |
687 | .write_trailer = ogg_write_trailer, | |
688 | .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, | |
689 | .priv_class = &oga_muxer_class, | |
690 | }; | |
691 | #endif | |
692 | ||
693 | #if CONFIG_SPX_MUXER | |
694 | OGG_CLASS(spx, Ogg Speex) | |
695 | AVOutputFormat ff_spx_muxer = { | |
696 | .name = "spx", | |
697 | .long_name = NULL_IF_CONFIG_SMALL("Ogg Speex"), | |
698 | .mime_type = "audio/ogg", | |
699 | .extensions = "spx", | |
700 | .priv_data_size = sizeof(OGGContext), | |
701 | .audio_codec = AV_CODEC_ID_SPEEX, | |
702 | .write_header = ogg_write_header, | |
703 | .write_packet = ogg_write_packet, | |
704 | .write_trailer = ogg_write_trailer, | |
705 | .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, | |
706 | .priv_class = &spx_muxer_class, | |
707 | }; | |
708 | #endif | |
709 | ||
710 | #if CONFIG_OPUS_MUXER | |
711 | OGG_CLASS(opus, Ogg Opus) | |
712 | AVOutputFormat ff_opus_muxer = { | |
713 | .name = "opus", | |
714 | .long_name = NULL_IF_CONFIG_SMALL("Ogg Opus"), | |
715 | .mime_type = "audio/ogg", | |
716 | .extensions = "opus", | |
717 | .priv_data_size = sizeof(OGGContext), | |
718 | .audio_codec = AV_CODEC_ID_OPUS, | |
719 | .write_header = ogg_write_header, | |
720 | .write_packet = ogg_write_packet, | |
721 | .write_trailer = ogg_write_trailer, | |
722 | .flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, | |
723 | .priv_class = &opus_muxer_class, | |
724 | }; | |
725 | #endif |