3 * Copyright (c) 2007 Baptiste Coudurier <baptiste dot coudurier at free dot fr>
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/crc.h"
23 #include "libavcodec/xiph.h"
24 #include "libavcodec/bytestream.h"
25 #include "libavcodec/flac.h"
31 unsigned page_counter
;
34 /** for theora granule */
41 static void ogg_update_checksum(AVFormatContext
*s
, int64_t crc_offset
)
43 int64_t pos
= url_ftell(s
->pb
);
44 uint32_t checksum
= get_checksum(s
->pb
);
45 url_fseek(s
->pb
, crc_offset
, SEEK_SET
);
46 put_be32(s
->pb
, checksum
);
47 url_fseek(s
->pb
, pos
, SEEK_SET
);
50 static int ogg_write_page(AVFormatContext
*s
, const uint8_t *data
, int size
,
51 int64_t granule
, int stream_index
, int flags
)
53 OGGStreamContext
*oggstream
= s
->streams
[stream_index
]->priv_data
;
57 if (size
>= 255*255) {
60 } else if (oggstream
->eos
)
63 page_segments
= FFMIN((size
/255)+!!size
, 255);
65 init_checksum(s
->pb
, ff_crc04C11DB7_update
, 0);
66 put_tag(s
->pb
, "OggS");
68 put_byte(s
->pb
, flags
);
69 put_le64(s
->pb
, granule
);
70 put_le32(s
->pb
, stream_index
);
71 put_le32(s
->pb
, oggstream
->page_counter
++);
72 crc_offset
= url_ftell(s
->pb
);
73 put_le32(s
->pb
, 0); // crc
74 put_byte(s
->pb
, page_segments
);
75 for (i
= 0; i
< page_segments
-1; i
++)
78 put_byte(s
->pb
, size
- (page_segments
-1)*255);
79 put_buffer(s
->pb
, data
, size
);
81 ogg_update_checksum(s
, crc_offset
);
82 put_flush_packet(s
->pb
);
86 static int ogg_build_flac_headers(AVCodecContext
*avctx
,
87 OGGStreamContext
*oggstream
, int bitexact
)
89 const char *vendor
= bitexact
? "ffmpeg" : LIBAVFORMAT_IDENT
;
90 enum FLACExtradataFormat format
;
93 if (!ff_flac_is_extradata_valid(avctx
, &format
, &streaminfo
))
95 oggstream
->header_len
[0] = 51;
96 oggstream
->header
[0] = av_mallocz(51); // per ogg flac specs
97 p
= oggstream
->header
[0];
98 bytestream_put_byte(&p
, 0x7F);
99 bytestream_put_buffer(&p
, "FLAC", 4);
100 bytestream_put_byte(&p
, 1); // major version
101 bytestream_put_byte(&p
, 0); // minor version
102 bytestream_put_be16(&p
, 1); // headers packets without this one
103 bytestream_put_buffer(&p
, "fLaC", 4);
104 bytestream_put_byte(&p
, 0x00); // streaminfo
105 bytestream_put_be24(&p
, 34);
106 bytestream_put_buffer(&p
, streaminfo
, FLAC_STREAMINFO_SIZE
);
107 oggstream
->header_len
[1] = 1+3+4+strlen(vendor
)+4;
108 oggstream
->header
[1] = av_mallocz(oggstream
->header_len
[1]);
109 p
= oggstream
->header
[1];
110 bytestream_put_byte(&p
, 0x84); // last metadata block and vorbis comment
111 bytestream_put_be24(&p
, oggstream
->header_len
[1] - 4);
112 bytestream_put_le32(&p
, strlen(vendor
));
113 bytestream_put_buffer(&p
, vendor
, strlen(vendor
));
114 bytestream_put_le32(&p
, 0); // user comment list length
118 static int ogg_write_header(AVFormatContext
*s
)
120 OGGStreamContext
*oggstream
;
122 for (i
= 0; i
< s
->nb_streams
; i
++) {
123 AVStream
*st
= s
->streams
[i
];
124 if (st
->codec
->codec_type
== CODEC_TYPE_AUDIO
)
125 av_set_pts_info(st
, 64, 1, st
->codec
->sample_rate
);
126 else if (st
->codec
->codec_type
== CODEC_TYPE_VIDEO
)
127 av_set_pts_info(st
, 64, st
->codec
->time_base
.num
, st
->codec
->time_base
.den
);
128 if (st
->codec
->codec_id
!= CODEC_ID_VORBIS
&&
129 st
->codec
->codec_id
!= CODEC_ID_THEORA
&&
130 st
->codec
->codec_id
!= CODEC_ID_FLAC
) {
131 av_log(s
, AV_LOG_ERROR
, "Unsupported codec id in stream %d\n", i
);
135 if (!st
->codec
->extradata
|| !st
->codec
->extradata_size
) {
136 av_log(s
, AV_LOG_ERROR
, "No extradata present\n");
139 oggstream
= av_mallocz(sizeof(*oggstream
));
140 st
->priv_data
= oggstream
;
141 if (st
->codec
->codec_id
== CODEC_ID_FLAC
) {
142 if (ogg_build_flac_headers(st
->codec
,
143 oggstream
, st
->codec
->flags
& CODEC_FLAG_BITEXACT
) < 0) {
144 av_log(s
, AV_LOG_ERROR
, "Extradata corrupted\n");
145 av_freep(&st
->priv_data
);
148 if (ff_split_xiph_headers(st
->codec
->extradata
, st
->codec
->extradata_size
,
149 st
->codec
->codec_id
== CODEC_ID_VORBIS
? 30 : 42,
150 oggstream
->header
, oggstream
->header_len
) < 0) {
151 av_log(s
, AV_LOG_ERROR
, "Extradata corrupted\n");
152 av_freep(&st
->priv_data
);
155 if (st
->codec
->codec_id
== CODEC_ID_THEORA
) {
156 /** KFGSHIFT is the width of the less significant section of the granule position
157 The less significant section is the frame count since the last keyframe */
158 oggstream
->kfgshift
= ((oggstream
->header
[0][40]&3)<<3)|(oggstream
->header
[0][41]>>5);
159 oggstream
->vrev
= oggstream
->header
[0][9];
160 av_log(s
, AV_LOG_DEBUG
, "theora kfgshift %d, vrev %d\n",
161 oggstream
->kfgshift
, oggstream
->vrev
);
165 for (i
= 0; i
< 3; i
++) {
166 for (j
= 0; j
< s
->nb_streams
; j
++) {
167 AVStream
*st
= s
->streams
[j
];
168 OGGStreamContext
*oggstream
= st
->priv_data
;
169 if (oggstream
&& oggstream
->header_len
[i
]) {
170 ogg_write_page(s
, oggstream
->header
[i
], oggstream
->header_len
[i
],
171 0, st
->index
, i
? 0 : 2); // bos
178 static int ogg_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
180 AVStream
*st
= s
->streams
[pkt
->stream_index
];
181 OGGStreamContext
*oggstream
= st
->priv_data
;
182 uint8_t *ptr
= pkt
->data
;
183 int ret
, size
= pkt
->size
;
186 if (st
->codec
->codec_id
== CODEC_ID_THEORA
) {
187 int64_t pts
= oggstream
->vrev
< 1 ? pkt
->pts
: pkt
->pts
+ pkt
->duration
;
189 if (pkt
->flags
& PKT_FLAG_KEY
)
190 oggstream
->last_kf_pts
= pts
;
191 pframe_count
= pts
- oggstream
->last_kf_pts
;
192 // prevent frame count from overflow if key frame flag is not set
193 if (pframe_count
>= (1<<oggstream
->kfgshift
)) {
194 oggstream
->last_kf_pts
+= pframe_count
;
197 granule
= (oggstream
->last_kf_pts
<<oggstream
->kfgshift
) | pframe_count
;
199 granule
= pkt
->pts
+ pkt
->duration
;
200 oggstream
->duration
= granule
;
202 ret
= ogg_write_page(s
, ptr
, size
, granule
, pkt
->stream_index
, ptr
!= pkt
->data
);
203 ptr
+= ret
; size
-= ret
;
204 } while (size
> 0 || ret
== 255*255); // need to output a last nil page
209 static int ogg_compare_granule(AVFormatContext
*s
, AVPacket
*next
, AVPacket
*pkt
)
211 AVStream
*st2
= s
->streams
[next
->stream_index
];
212 AVStream
*st
= s
->streams
[pkt
->stream_index
];
214 int64_t next_granule
= av_rescale_q(next
->pts
+ next
->duration
,
215 st2
->time_base
, AV_TIME_BASE_Q
);
216 int64_t cur_granule
= av_rescale_q(pkt
->pts
+ pkt
->duration
,
217 st
->time_base
, AV_TIME_BASE_Q
);
218 return next_granule
> cur_granule
;
221 static int ogg_interleave_per_granule(AVFormatContext
*s
, AVPacket
*out
, AVPacket
*pkt
, int flush
)
224 int stream_count
= 0;
225 int streams
[MAX_STREAMS
] = {0};
229 ff_interleave_add_packet(s
, pkt
, ogg_compare_granule
);
232 pktl
= s
->packet_buffer
;
234 if (streams
[pktl
->pkt
.stream_index
] == 0)
236 streams
[pktl
->pkt
.stream_index
]++;
237 // need to buffer at least one packet to set eos flag
238 if (streams
[pktl
->pkt
.stream_index
] == 2)
243 if ((s
->nb_streams
== stream_count
&& interleaved
== stream_count
) ||
244 (flush
&& stream_count
)) {
245 pktl
= s
->packet_buffer
;
247 s
->packet_buffer
= pktl
->next
;
248 if (flush
&& streams
[out
->stream_index
] == 1) {
249 OGGStreamContext
*ogg
= s
->streams
[out
->stream_index
]->priv_data
;
260 static int ogg_write_trailer(AVFormatContext
*s
)
263 for (i
= 0; i
< s
->nb_streams
; i
++) {
264 AVStream
*st
= s
->streams
[i
];
265 OGGStreamContext
*oggstream
= st
->priv_data
;
266 if (st
->codec
->codec_id
== CODEC_ID_FLAC
) {
267 av_free(oggstream
->header
[0]);
268 av_free(oggstream
->header
[1]);
270 av_freep(&st
->priv_data
);
275 AVOutputFormat ogg_muxer
= {
277 NULL_IF_CONFIG_SMALL("Ogg"),
286 .interleave_packet
= ogg_interleave_per_granule
,