00001 /* 00002 * Ogg muxer 00003 * Copyright (c) 2007 Baptiste Coudurier <baptiste dot coudurier at free dot fr> 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include "libavutil/crc.h" 00023 #include "libavcodec/xiph.h" 00024 #include "libavcodec/bytestream.h" 00025 #include "libavcodec/flac.h" 00026 #include "avformat.h" 00027 #include "internal.h" 00028 00029 typedef struct { 00030 int64_t duration; 00031 unsigned page_counter; 00032 uint8_t *header[3]; 00033 int header_len[3]; 00035 int kfgshift; 00036 int64_t last_kf_pts; 00037 int vrev; 00038 int eos; 00039 } OGGStreamContext; 00040 00041 static void ogg_update_checksum(AVFormatContext *s, int64_t crc_offset) 00042 { 00043 int64_t pos = url_ftell(s->pb); 00044 uint32_t checksum = get_checksum(s->pb); 00045 url_fseek(s->pb, crc_offset, SEEK_SET); 00046 put_be32(s->pb, checksum); 00047 url_fseek(s->pb, pos, SEEK_SET); 00048 } 00049 00050 static int ogg_write_page(AVFormatContext *s, const uint8_t *data, int size, 00051 int64_t granule, int stream_index, int flags) 00052 { 00053 OGGStreamContext *oggstream = s->streams[stream_index]->priv_data; 00054 int64_t crc_offset; 00055 int page_segments, i; 00056 00057 if (size >= 255*255) { 00058 granule = -1; 00059 size = 255*255; 00060 } else if (oggstream->eos) 00061 flags |= 4; 00062 00063 page_segments = FFMIN((size/255)+!!size, 255); 00064 00065 init_checksum(s->pb, ff_crc04C11DB7_update, 0); 00066 put_tag(s->pb, "OggS"); 00067 put_byte(s->pb, 0); 00068 put_byte(s->pb, flags); 00069 put_le64(s->pb, granule); 00070 put_le32(s->pb, stream_index); 00071 put_le32(s->pb, oggstream->page_counter++); 00072 crc_offset = url_ftell(s->pb); 00073 put_le32(s->pb, 0); // crc 00074 put_byte(s->pb, page_segments); 00075 for (i = 0; i < page_segments-1; i++) 00076 put_byte(s->pb, 255); 00077 if (size) { 00078 put_byte(s->pb, size - (page_segments-1)*255); 00079 put_buffer(s->pb, data, size); 00080 } 00081 ogg_update_checksum(s, crc_offset); 00082 put_flush_packet(s->pb); 00083 return size; 00084 } 00085 00086 static int ogg_build_flac_headers(AVCodecContext *avctx, 00087 OGGStreamContext *oggstream, int bitexact) 00088 { 00089 const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; 00090 enum FLACExtradataFormat format; 00091 uint8_t *streaminfo; 00092 uint8_t *p; 00093 if (!ff_flac_is_extradata_valid(avctx, &format, &streaminfo)) 00094 return -1; 00095 oggstream->header_len[0] = 51; 00096 oggstream->header[0] = av_mallocz(51); // per ogg flac specs 00097 p = oggstream->header[0]; 00098 bytestream_put_byte(&p, 0x7F); 00099 bytestream_put_buffer(&p, "FLAC", 4); 00100 bytestream_put_byte(&p, 1); // major version 00101 bytestream_put_byte(&p, 0); // minor version 00102 bytestream_put_be16(&p, 1); // headers packets without this one 00103 bytestream_put_buffer(&p, "fLaC", 4); 00104 bytestream_put_byte(&p, 0x00); // streaminfo 00105 bytestream_put_be24(&p, 34); 00106 bytestream_put_buffer(&p, streaminfo, FLAC_STREAMINFO_SIZE); 00107 oggstream->header_len[1] = 1+3+4+strlen(vendor)+4; 00108 oggstream->header[1] = av_mallocz(oggstream->header_len[1]); 00109 p = oggstream->header[1]; 00110 bytestream_put_byte(&p, 0x84); // last metadata block and vorbis comment 00111 bytestream_put_be24(&p, oggstream->header_len[1] - 4); 00112 bytestream_put_le32(&p, strlen(vendor)); 00113 bytestream_put_buffer(&p, vendor, strlen(vendor)); 00114 bytestream_put_le32(&p, 0); // user comment list length 00115 return 0; 00116 } 00117 00118 static int ogg_write_header(AVFormatContext *s) 00119 { 00120 OGGStreamContext *oggstream; 00121 int i, j; 00122 for (i = 0; i < s->nb_streams; i++) { 00123 AVStream *st = s->streams[i]; 00124 if (st->codec->codec_type == CODEC_TYPE_AUDIO) 00125 av_set_pts_info(st, 64, 1, st->codec->sample_rate); 00126 else if (st->codec->codec_type == CODEC_TYPE_VIDEO) 00127 av_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den); 00128 if (st->codec->codec_id != CODEC_ID_VORBIS && 00129 st->codec->codec_id != CODEC_ID_THEORA && 00130 st->codec->codec_id != CODEC_ID_FLAC) { 00131 av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i); 00132 return -1; 00133 } 00134 00135 if (!st->codec->extradata || !st->codec->extradata_size) { 00136 av_log(s, AV_LOG_ERROR, "No extradata present\n"); 00137 return -1; 00138 } 00139 oggstream = av_mallocz(sizeof(*oggstream)); 00140 st->priv_data = oggstream; 00141 if (st->codec->codec_id == CODEC_ID_FLAC) { 00142 if (ogg_build_flac_headers(st->codec, 00143 oggstream, st->codec->flags & CODEC_FLAG_BITEXACT) < 0) { 00144 av_log(s, AV_LOG_ERROR, "Extradata corrupted\n"); 00145 av_freep(&st->priv_data); 00146 } 00147 } else { 00148 if (ff_split_xiph_headers(st->codec->extradata, st->codec->extradata_size, 00149 st->codec->codec_id == CODEC_ID_VORBIS ? 30 : 42, 00150 oggstream->header, oggstream->header_len) < 0) { 00151 av_log(s, AV_LOG_ERROR, "Extradata corrupted\n"); 00152 av_freep(&st->priv_data); 00153 return -1; 00154 } 00155 if (st->codec->codec_id == CODEC_ID_THEORA) { 00158 oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5); 00159 oggstream->vrev = oggstream->header[0][9]; 00160 av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n", 00161 oggstream->kfgshift, oggstream->vrev); 00162 } 00163 } 00164 } 00165 for (i = 0; i < 3; i++) { 00166 for (j = 0; j < s->nb_streams; j++) { 00167 AVStream *st = s->streams[j]; 00168 OGGStreamContext *oggstream = st->priv_data; 00169 if (oggstream && oggstream->header_len[i]) { 00170 ogg_write_page(s, oggstream->header[i], oggstream->header_len[i], 00171 0, st->index, i ? 0 : 2); // bos 00172 } 00173 } 00174 } 00175 return 0; 00176 } 00177 00178 static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt) 00179 { 00180 AVStream *st = s->streams[pkt->stream_index]; 00181 OGGStreamContext *oggstream = st->priv_data; 00182 uint8_t *ptr = pkt->data; 00183 int ret, size = pkt->size; 00184 int64_t granule; 00185 00186 if (st->codec->codec_id == CODEC_ID_THEORA) { 00187 int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts + pkt->duration; 00188 int pframe_count; 00189 if (pkt->flags & PKT_FLAG_KEY) 00190 oggstream->last_kf_pts = pts; 00191 pframe_count = pts - oggstream->last_kf_pts; 00192 // prevent frame count from overflow if key frame flag is not set 00193 if (pframe_count >= (1<<oggstream->kfgshift)) { 00194 oggstream->last_kf_pts += pframe_count; 00195 pframe_count = 0; 00196 } 00197 granule = (oggstream->last_kf_pts<<oggstream->kfgshift) | pframe_count; 00198 } else 00199 granule = pkt->pts + pkt->duration; 00200 oggstream->duration = granule; 00201 do { 00202 ret = ogg_write_page(s, ptr, size, granule, pkt->stream_index, ptr != pkt->data); 00203 ptr += ret; size -= ret; 00204 } while (size > 0 || ret == 255*255); // need to output a last nil page 00205 00206 return 0; 00207 } 00208 00209 static int ogg_compare_granule(AVFormatContext *s, AVPacket *next, AVPacket *pkt) 00210 { 00211 AVStream *st2 = s->streams[next->stream_index]; 00212 AVStream *st = s->streams[pkt ->stream_index]; 00213 00214 int64_t next_granule = av_rescale_q(next->pts + next->duration, 00215 st2->time_base, AV_TIME_BASE_Q); 00216 int64_t cur_granule = av_rescale_q(pkt ->pts + pkt ->duration, 00217 st ->time_base, AV_TIME_BASE_Q); 00218 return next_granule > cur_granule; 00219 } 00220 00221 static int ogg_interleave_per_granule(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) 00222 { 00223 AVPacketList *pktl; 00224 int stream_count = 0; 00225 int streams[MAX_STREAMS] = {0}; 00226 int interleaved = 0; 00227 00228 if (pkt) { 00229 ff_interleave_add_packet(s, pkt, ogg_compare_granule); 00230 } 00231 00232 pktl = s->packet_buffer; 00233 while (pktl) { 00234 if (streams[pktl->pkt.stream_index] == 0) 00235 stream_count++; 00236 streams[pktl->pkt.stream_index]++; 00237 // need to buffer at least one packet to set eos flag 00238 if (streams[pktl->pkt.stream_index] == 2) 00239 interleaved++; 00240 pktl = pktl->next; 00241 } 00242 00243 if ((s->nb_streams == stream_count && interleaved == stream_count) || 00244 (flush && stream_count)) { 00245 pktl= s->packet_buffer; 00246 *out= pktl->pkt; 00247 s->packet_buffer = pktl->next; 00248 if (flush && streams[out->stream_index] == 1) { 00249 OGGStreamContext *ogg = s->streams[out->stream_index]->priv_data; 00250 ogg->eos = 1; 00251 } 00252 av_freep(&pktl); 00253 return 1; 00254 } else { 00255 av_init_packet(out); 00256 return 0; 00257 } 00258 } 00259 00260 static int ogg_write_trailer(AVFormatContext *s) 00261 { 00262 int i; 00263 for (i = 0; i < s->nb_streams; i++) { 00264 AVStream *st = s->streams[i]; 00265 OGGStreamContext *oggstream = st->priv_data; 00266 if (st->codec->codec_id == CODEC_ID_FLAC) { 00267 av_free(oggstream->header[0]); 00268 av_free(oggstream->header[1]); 00269 } 00270 av_freep(&st->priv_data); 00271 } 00272 return 0; 00273 } 00274 00275 AVOutputFormat ogg_muxer = { 00276 "ogg", 00277 NULL_IF_CONFIG_SMALL("Ogg"), 00278 "application/ogg", 00279 "ogg,ogv", 00280 0, 00281 CODEC_ID_FLAC, 00282 CODEC_ID_THEORA, 00283 ogg_write_header, 00284 ogg_write_packet, 00285 ogg_write_trailer, 00286 .interleave_packet = ogg_interleave_per_granule, 00287 };