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
;
94 if (!ff_flac_is_extradata_valid(avctx
, &format
, &streaminfo
))
97 // first packet: STREAMINFO
98 oggstream
->header_len
[0] = 51;
99 oggstream
->header
[0] = av_mallocz(51); // per ogg flac specs
100 p
= oggstream
->header
[0];
102 return AVERROR_NOMEM
;
103 bytestream_put_byte(&p
, 0x7F);
104 bytestream_put_buffer(&p
, "FLAC", 4);
105 bytestream_put_byte(&p
, 1); // major version
106 bytestream_put_byte(&p
, 0); // minor version
107 bytestream_put_be16(&p
, 1); // headers packets without this one
108 bytestream_put_buffer(&p
, "fLaC", 4);
109 bytestream_put_byte(&p
, 0x00); // streaminfo
110 bytestream_put_be24(&p
, 34);
111 bytestream_put_buffer(&p
, streaminfo
, FLAC_STREAMINFO_SIZE
);
113 // second packet: VorbisComment
114 oggstream
->header_len
[1] = 1+3+4+strlen(vendor
)+4;
115 oggstream
->header
[1] = av_mallocz(oggstream
->header_len
[1]);
116 p
= oggstream
->header
[1];
118 return AVERROR_NOMEM
;
119 bytestream_put_byte(&p
, 0x84); // last metadata block and vorbis comment
120 bytestream_put_be24(&p
, oggstream
->header_len
[1] - 4);
121 bytestream_put_le32(&p
, strlen(vendor
));
122 bytestream_put_buffer(&p
, vendor
, strlen(vendor
));
123 bytestream_put_le32(&p
, 0); // user comment list length
128 static int ogg_write_header(AVFormatContext
*s
)
130 OGGStreamContext
*oggstream
;
132 for (i
= 0; i
< s
->nb_streams
; i
++) {
133 AVStream
*st
= s
->streams
[i
];
134 if (st
->codec
->codec_type
== CODEC_TYPE_AUDIO
)
135 av_set_pts_info(st
, 64, 1, st
->codec
->sample_rate
);
136 else if (st
->codec
->codec_type
== CODEC_TYPE_VIDEO
)
137 av_set_pts_info(st
, 64, st
->codec
->time_base
.num
, st
->codec
->time_base
.den
);
138 if (st
->codec
->codec_id
!= CODEC_ID_VORBIS
&&
139 st
->codec
->codec_id
!= CODEC_ID_THEORA
&&
140 st
->codec
->codec_id
!= CODEC_ID_FLAC
) {
141 av_log(s
, AV_LOG_ERROR
, "Unsupported codec id in stream %d\n", i
);
145 if (!st
->codec
->extradata
|| !st
->codec
->extradata_size
) {
146 av_log(s
, AV_LOG_ERROR
, "No extradata present\n");
149 oggstream
= av_mallocz(sizeof(*oggstream
));
150 st
->priv_data
= oggstream
;
151 if (st
->codec
->codec_id
== CODEC_ID_FLAC
) {
152 if (ogg_build_flac_headers(st
->codec
,
153 oggstream
, st
->codec
->flags
& CODEC_FLAG_BITEXACT
) < 0) {
154 av_log(s
, AV_LOG_ERROR
, "Extradata corrupted\n");
155 av_freep(&st
->priv_data
);
158 if (ff_split_xiph_headers(st
->codec
->extradata
, st
->codec
->extradata_size
,
159 st
->codec
->codec_id
== CODEC_ID_VORBIS ?
30 : 42,
160 oggstream
->header
, oggstream
->header_len
) < 0) {
161 av_log(s
, AV_LOG_ERROR
, "Extradata corrupted\n");
162 av_freep(&st
->priv_data
);
165 if (st
->codec
->codec_id
== CODEC_ID_THEORA
) {
166 /** KFGSHIFT is the width of the less significant section of the granule position
167 The less significant section is the frame count since the last keyframe */
168 oggstream
->kfgshift
= ((oggstream
->header
[0][40]&3)<<3)|(oggstream
->header
[0][41]>>5);
169 oggstream
->vrev
= oggstream
->header
[0][9];
170 av_log(s
, AV_LOG_DEBUG
, "theora kfgshift %d, vrev %d\n",
171 oggstream
->kfgshift
, oggstream
->vrev
);
175 for (i
= 0; i
< 3; i
++) {
176 for (j
= 0; j
< s
->nb_streams
; j
++) {
177 AVStream
*st
= s
->streams
[j
];
178 OGGStreamContext
*oggstream
= st
->priv_data
;
179 if (oggstream
&& oggstream
->header_len
[i
]) {
180 ogg_write_page(s
, oggstream
->header
[i
], oggstream
->header_len
[i
],
181 0, st
->index
, i ?
0 : 2); // bos
188 static int ogg_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
190 AVStream
*st
= s
->streams
[pkt
->stream_index
];
191 OGGStreamContext
*oggstream
= st
->priv_data
;
192 uint8_t *ptr
= pkt
->data
;
193 int ret
, size
= pkt
->size
;
196 if (st
->codec
->codec_id
== CODEC_ID_THEORA
) {
197 int64_t pts
= oggstream
->vrev
< 1 ? pkt
->pts
: pkt
->pts
+ pkt
->duration
;
199 if (pkt
->flags
& PKT_FLAG_KEY
)
200 oggstream
->last_kf_pts
= pts
;
201 pframe_count
= pts
- oggstream
->last_kf_pts
;
202 // prevent frame count from overflow if key frame flag is not set
203 if (pframe_count
>= (1<<oggstream
->kfgshift
)) {
204 oggstream
->last_kf_pts
+= pframe_count
;
207 granule
= (oggstream
->last_kf_pts
<<oggstream
->kfgshift
) | pframe_count
;
209 granule
= pkt
->pts
+ pkt
->duration
;
210 oggstream
->duration
= granule
;
212 ret
= ogg_write_page(s
, ptr
, size
, granule
, pkt
->stream_index
, ptr
!= pkt
->data
);
213 ptr
+= ret
; size
-= ret
;
214 } while (size
> 0 || ret
== 255*255); // need to output a last nil page
219 static int ogg_compare_granule(AVFormatContext
*s
, AVPacket
*next
, AVPacket
*pkt
)
221 AVStream
*st2
= s
->streams
[next
->stream_index
];
222 AVStream
*st
= s
->streams
[pkt
->stream_index
];
224 int64_t next_granule
= av_rescale_q(next
->pts
+ next
->duration
,
225 st2
->time_base
, AV_TIME_BASE_Q
);
226 int64_t cur_granule
= av_rescale_q(pkt
->pts
+ pkt
->duration
,
227 st
->time_base
, AV_TIME_BASE_Q
);
228 return next_granule
> cur_granule
;
231 static int ogg_interleave_per_granule(AVFormatContext
*s
, AVPacket
*out
, AVPacket
*pkt
, int flush
)
234 int stream_count
= 0;
235 int streams
[MAX_STREAMS
] = {0};
239 ff_interleave_add_packet(s
, pkt
, ogg_compare_granule
);
242 pktl
= s
->packet_buffer
;
244 if (streams
[pktl
->pkt
.stream_index
] == 0)
246 streams
[pktl
->pkt
.stream_index
]++;
247 // need to buffer at least one packet to set eos flag
248 if (streams
[pktl
->pkt
.stream_index
] == 2)
253 if ((s
->nb_streams
== stream_count
&& interleaved
== stream_count
) ||
254 (flush
&& stream_count
)) {
255 pktl
= s
->packet_buffer
;
257 s
->packet_buffer
= pktl
->next
;
258 if (flush
&& streams
[out
->stream_index
] == 1) {
259 OGGStreamContext
*ogg
= s
->streams
[out
->stream_index
]->priv_data
;
270 static int ogg_write_trailer(AVFormatContext
*s
)
273 for (i
= 0; i
< s
->nb_streams
; i
++) {
274 AVStream
*st
= s
->streams
[i
];
275 OGGStreamContext
*oggstream
= st
->priv_data
;
276 if (st
->codec
->codec_id
== CODEC_ID_FLAC
) {
277 av_free(oggstream
->header
[0]);
278 av_free(oggstream
->header
[1]);
280 av_freep(&st
->priv_data
);
285 AVOutputFormat ogg_muxer
= {
287 NULL_IF_CONFIG_SMALL("Ogg"),
296 .interleave_packet
= ogg_interleave_per_granule
,