ffmpeg / libavformat / oggenc.c @ fbe8c56d
History | View | Annotate | Download (11.8 KB)
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 "libavutil/crc.h" |
23 |
#include "libavcodec/xiph.h" |
24 |
#include "libavcodec/bytestream.h" |
25 |
#include "libavcodec/flac.h" |
26 |
#include "avformat.h" |
27 |
#include "internal.h" |
28 |
|
29 |
typedef struct { |
30 |
int64_t duration; |
31 |
unsigned page_counter;
|
32 |
uint8_t *header[3];
|
33 |
int header_len[3]; |
34 |
/** for theora granule */
|
35 |
int kfgshift;
|
36 |
int64_t last_kf_pts; |
37 |
int vrev;
|
38 |
int eos;
|
39 |
unsigned packet_count; ///< number of packet buffered |
40 |
} OGGStreamContext; |
41 |
|
42 |
static void ogg_update_checksum(AVFormatContext *s, int64_t crc_offset) |
43 |
{ |
44 |
int64_t pos = url_ftell(s->pb); |
45 |
uint32_t checksum = get_checksum(s->pb); |
46 |
url_fseek(s->pb, crc_offset, SEEK_SET); |
47 |
put_be32(s->pb, checksum); |
48 |
url_fseek(s->pb, pos, SEEK_SET); |
49 |
} |
50 |
|
51 |
static int ogg_write_page(AVFormatContext *s, const uint8_t *data, int size, |
52 |
int64_t granule, int stream_index, int flags) |
53 |
{ |
54 |
OGGStreamContext *oggstream = s->streams[stream_index]->priv_data; |
55 |
int64_t crc_offset; |
56 |
int page_segments, i;
|
57 |
|
58 |
if (size >= 255*255) { |
59 |
granule = -1;
|
60 |
size = 255*255; |
61 |
} else if (oggstream->eos) |
62 |
flags |= 4;
|
63 |
|
64 |
page_segments = FFMIN(size/255 + 1, 255); |
65 |
|
66 |
init_checksum(s->pb, ff_crc04C11DB7_update, 0);
|
67 |
put_tag(s->pb, "OggS");
|
68 |
put_byte(s->pb, 0);
|
69 |
put_byte(s->pb, flags); |
70 |
put_le64(s->pb, granule); |
71 |
put_le32(s->pb, stream_index); |
72 |
put_le32(s->pb, oggstream->page_counter++); |
73 |
crc_offset = url_ftell(s->pb); |
74 |
put_le32(s->pb, 0); // crc |
75 |
put_byte(s->pb, page_segments); |
76 |
for (i = 0; i < page_segments-1; i++) |
77 |
put_byte(s->pb, 255);
|
78 |
put_byte(s->pb, size - (page_segments-1)*255); |
79 |
put_buffer(s->pb, data, size); |
80 |
|
81 |
ogg_update_checksum(s, crc_offset); |
82 |
put_flush_packet(s->pb); |
83 |
return size;
|
84 |
} |
85 |
|
86 |
static uint8_t *ogg_write_vorbiscomment(int offset, int bitexact, |
87 |
int *header_len)
|
88 |
{ |
89 |
const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; |
90 |
int size;
|
91 |
uint8_t *p, *p0; |
92 |
|
93 |
size = offset + 4 + strlen(vendor) + 4; |
94 |
p = av_mallocz(size); |
95 |
if (!p)
|
96 |
return NULL; |
97 |
p0 = p; |
98 |
|
99 |
p += offset; |
100 |
bytestream_put_le32(&p, strlen(vendor)); |
101 |
bytestream_put_buffer(&p, vendor, strlen(vendor)); |
102 |
bytestream_put_le32(&p, 0); // user comment list length |
103 |
|
104 |
*header_len = size; |
105 |
return p0;
|
106 |
} |
107 |
|
108 |
static int ogg_build_flac_headers(AVCodecContext *avctx, |
109 |
OGGStreamContext *oggstream, int bitexact)
|
110 |
{ |
111 |
enum FLACExtradataFormat format;
|
112 |
uint8_t *streaminfo; |
113 |
uint8_t *p; |
114 |
|
115 |
if (!ff_flac_is_extradata_valid(avctx, &format, &streaminfo))
|
116 |
return -1; |
117 |
|
118 |
// first packet: STREAMINFO
|
119 |
oggstream->header_len[0] = 51; |
120 |
oggstream->header[0] = av_mallocz(51); // per ogg flac specs |
121 |
p = oggstream->header[0];
|
122 |
if (!p)
|
123 |
return AVERROR_NOMEM;
|
124 |
bytestream_put_byte(&p, 0x7F);
|
125 |
bytestream_put_buffer(&p, "FLAC", 4); |
126 |
bytestream_put_byte(&p, 1); // major version |
127 |
bytestream_put_byte(&p, 0); // minor version |
128 |
bytestream_put_be16(&p, 1); // headers packets without this one |
129 |
bytestream_put_buffer(&p, "fLaC", 4); |
130 |
bytestream_put_byte(&p, 0x00); // streaminfo |
131 |
bytestream_put_be24(&p, 34);
|
132 |
bytestream_put_buffer(&p, streaminfo, FLAC_STREAMINFO_SIZE); |
133 |
|
134 |
// second packet: VorbisComment
|
135 |
p = ogg_write_vorbiscomment(4, bitexact, &oggstream->header_len[1]); |
136 |
if (!p)
|
137 |
return AVERROR_NOMEM;
|
138 |
oggstream->header[1] = p;
|
139 |
bytestream_put_byte(&p, 0x84); // last metadata block and vorbis comment |
140 |
bytestream_put_be24(&p, oggstream->header_len[1] - 4); |
141 |
|
142 |
return 0; |
143 |
} |
144 |
|
145 |
#define SPEEX_HEADER_SIZE 80 |
146 |
|
147 |
static int ogg_build_speex_headers(AVCodecContext *avctx, |
148 |
OGGStreamContext *oggstream, int bitexact)
|
149 |
{ |
150 |
uint8_t *p; |
151 |
|
152 |
if (avctx->extradata_size < SPEEX_HEADER_SIZE)
|
153 |
return -1; |
154 |
|
155 |
// first packet: Speex header
|
156 |
p = av_mallocz(SPEEX_HEADER_SIZE); |
157 |
if (!p)
|
158 |
return AVERROR_NOMEM;
|
159 |
oggstream->header[0] = p;
|
160 |
oggstream->header_len[0] = SPEEX_HEADER_SIZE;
|
161 |
bytestream_put_buffer(&p, avctx->extradata, SPEEX_HEADER_SIZE); |
162 |
AV_WL32(&oggstream->header[0][68], 0); // set extra_headers to 0 |
163 |
|
164 |
// second packet: VorbisComment
|
165 |
p = ogg_write_vorbiscomment(0, bitexact, &oggstream->header_len[1]); |
166 |
if (!p)
|
167 |
return AVERROR_NOMEM;
|
168 |
oggstream->header[1] = p;
|
169 |
|
170 |
return 0; |
171 |
} |
172 |
|
173 |
static int ogg_write_header(AVFormatContext *s) |
174 |
{ |
175 |
OGGStreamContext *oggstream; |
176 |
int i, j;
|
177 |
for (i = 0; i < s->nb_streams; i++) { |
178 |
AVStream *st = s->streams[i]; |
179 |
if (st->codec->codec_type == CODEC_TYPE_AUDIO)
|
180 |
av_set_pts_info(st, 64, 1, st->codec->sample_rate); |
181 |
else if (st->codec->codec_type == CODEC_TYPE_VIDEO) |
182 |
av_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den);
|
183 |
if (st->codec->codec_id != CODEC_ID_VORBIS &&
|
184 |
st->codec->codec_id != CODEC_ID_THEORA && |
185 |
st->codec->codec_id != CODEC_ID_SPEEX && |
186 |
st->codec->codec_id != CODEC_ID_FLAC) { |
187 |
av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i);
|
188 |
return -1; |
189 |
} |
190 |
|
191 |
if (!st->codec->extradata || !st->codec->extradata_size) {
|
192 |
av_log(s, AV_LOG_ERROR, "No extradata present\n");
|
193 |
return -1; |
194 |
} |
195 |
oggstream = av_mallocz(sizeof(*oggstream));
|
196 |
st->priv_data = oggstream; |
197 |
if (st->codec->codec_id == CODEC_ID_FLAC) {
|
198 |
int err = ogg_build_flac_headers(st->codec, oggstream,
|
199 |
st->codec->flags & CODEC_FLAG_BITEXACT); |
200 |
if (err) {
|
201 |
av_log(s, AV_LOG_ERROR, "Error writing FLAC headers\n");
|
202 |
av_freep(&st->priv_data); |
203 |
return err;
|
204 |
} |
205 |
} else if (st->codec->codec_id == CODEC_ID_SPEEX) { |
206 |
int err = ogg_build_speex_headers(st->codec, oggstream,
|
207 |
st->codec->flags & CODEC_FLAG_BITEXACT); |
208 |
if (err) {
|
209 |
av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n");
|
210 |
av_freep(&st->priv_data); |
211 |
return err;
|
212 |
} |
213 |
} else {
|
214 |
if (ff_split_xiph_headers(st->codec->extradata, st->codec->extradata_size,
|
215 |
st->codec->codec_id == CODEC_ID_VORBIS ? 30 : 42, |
216 |
oggstream->header, oggstream->header_len) < 0) {
|
217 |
av_log(s, AV_LOG_ERROR, "Extradata corrupted\n");
|
218 |
av_freep(&st->priv_data); |
219 |
return -1; |
220 |
} |
221 |
if (st->codec->codec_id == CODEC_ID_THEORA) {
|
222 |
/** KFGSHIFT is the width of the less significant section of the granule position
|
223 |
The less significant section is the frame count since the last keyframe */
|
224 |
oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5); |
225 |
oggstream->vrev = oggstream->header[0][9]; |
226 |
av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n",
|
227 |
oggstream->kfgshift, oggstream->vrev); |
228 |
} |
229 |
} |
230 |
} |
231 |
for (i = 0; i < 3; i++) { |
232 |
for (j = 0; j < s->nb_streams; j++) { |
233 |
AVStream *st = s->streams[j]; |
234 |
OGGStreamContext *oggstream = st->priv_data; |
235 |
if (oggstream && oggstream->header_len[i]) {
|
236 |
ogg_write_page(s, oggstream->header[i], oggstream->header_len[i], |
237 |
0, st->index, i ? 0 : 2); // bos |
238 |
} |
239 |
} |
240 |
} |
241 |
return 0; |
242 |
} |
243 |
|
244 |
static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt) |
245 |
{ |
246 |
AVStream *st = s->streams[pkt->stream_index]; |
247 |
OGGStreamContext *oggstream = st->priv_data; |
248 |
uint8_t *ptr = pkt->data; |
249 |
int ret, size = pkt->size;
|
250 |
int64_t granule; |
251 |
|
252 |
if (st->codec->codec_id == CODEC_ID_THEORA) {
|
253 |
int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts + pkt->duration;
|
254 |
int pframe_count;
|
255 |
if (pkt->flags & PKT_FLAG_KEY)
|
256 |
oggstream->last_kf_pts = pts; |
257 |
pframe_count = pts - oggstream->last_kf_pts; |
258 |
// prevent frame count from overflow if key frame flag is not set
|
259 |
if (pframe_count >= (1<<oggstream->kfgshift)) { |
260 |
oggstream->last_kf_pts += pframe_count; |
261 |
pframe_count = 0;
|
262 |
} |
263 |
granule = (oggstream->last_kf_pts<<oggstream->kfgshift) | pframe_count; |
264 |
} else
|
265 |
granule = pkt->pts + pkt->duration; |
266 |
oggstream->duration = granule; |
267 |
do {
|
268 |
ret = ogg_write_page(s, ptr, size, granule, pkt->stream_index, ptr != pkt->data); |
269 |
ptr += ret; size -= ret; |
270 |
} while (size > 0 || ret == 255*255); // need to output a last nil page |
271 |
|
272 |
return 0; |
273 |
} |
274 |
|
275 |
static int ogg_compare_granule(AVFormatContext *s, AVPacket *next, AVPacket *pkt) |
276 |
{ |
277 |
AVStream *st2 = s->streams[next->stream_index]; |
278 |
AVStream *st = s->streams[pkt ->stream_index]; |
279 |
|
280 |
int64_t next_granule = av_rescale_q(next->pts + next->duration, |
281 |
st2->time_base, AV_TIME_BASE_Q); |
282 |
int64_t cur_granule = av_rescale_q(pkt ->pts + pkt ->duration, |
283 |
st ->time_base, AV_TIME_BASE_Q); |
284 |
return next_granule > cur_granule;
|
285 |
} |
286 |
|
287 |
static int ogg_interleave_per_granule(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) |
288 |
{ |
289 |
OGGStreamContext *ogg; |
290 |
int i, stream_count = 0; |
291 |
int interleaved = 0; |
292 |
|
293 |
if (pkt) {
|
294 |
ff_interleave_add_packet(s, pkt, ogg_compare_granule); |
295 |
ogg = s->streams[pkt->stream_index]->priv_data; |
296 |
ogg->packet_count++; |
297 |
} |
298 |
|
299 |
for (i = 0; i < s->nb_streams; i++) { |
300 |
ogg = s->streams[i]->priv_data; |
301 |
stream_count += !!ogg->packet_count; |
302 |
interleaved += ogg->packet_count > 1;
|
303 |
} |
304 |
|
305 |
if ((s->nb_streams == stream_count && interleaved == stream_count) ||
|
306 |
(flush && stream_count)) { |
307 |
AVPacketList *pktl= s->packet_buffer; |
308 |
*out= pktl->pkt; |
309 |
s->packet_buffer = pktl->next; |
310 |
|
311 |
ogg = s->streams[out->stream_index]->priv_data; |
312 |
if (flush && ogg->packet_count == 1) |
313 |
ogg->eos = 1;
|
314 |
ogg->packet_count--; |
315 |
|
316 |
if(!s->packet_buffer)
|
317 |
s->packet_buffer_end= NULL;
|
318 |
|
319 |
if(s->streams[out->stream_index]->last_in_packet_buffer == pktl)
|
320 |
s->streams[out->stream_index]->last_in_packet_buffer= NULL;
|
321 |
|
322 |
av_freep(&pktl); |
323 |
return 1; |
324 |
} else {
|
325 |
av_init_packet(out); |
326 |
return 0; |
327 |
} |
328 |
} |
329 |
|
330 |
static int ogg_write_trailer(AVFormatContext *s) |
331 |
{ |
332 |
int i;
|
333 |
for (i = 0; i < s->nb_streams; i++) { |
334 |
AVStream *st = s->streams[i]; |
335 |
OGGStreamContext *oggstream = st->priv_data; |
336 |
if (st->codec->codec_id == CODEC_ID_FLAC ||
|
337 |
st->codec->codec_id == CODEC_ID_SPEEX) { |
338 |
av_free(oggstream->header[0]);
|
339 |
av_free(oggstream->header[1]);
|
340 |
} |
341 |
av_freep(&st->priv_data); |
342 |
} |
343 |
return 0; |
344 |
} |
345 |
|
346 |
AVOutputFormat ogg_muxer = { |
347 |
"ogg",
|
348 |
NULL_IF_CONFIG_SMALL("Ogg"),
|
349 |
"application/ogg",
|
350 |
"ogg,ogv,spx",
|
351 |
0,
|
352 |
CODEC_ID_FLAC, |
353 |
CODEC_ID_THEORA, |
354 |
ogg_write_header, |
355 |
ogg_write_packet, |
356 |
ogg_write_trailer, |
357 |
.interleave_packet = ogg_interleave_per_granule, |
358 |
}; |