[FFmpeg-devel] [PATCH] ogg muxer
Måns Rullgård
mans
Sun Oct 28 20:07:17 CET 2007
Baptiste Coudurier <baptiste.coudurier at smartjog.com> writes:
> Hi
>
> $subject.
Neat, but why? Ogg is IMHO such a nasty format that its use should be
actively discouraged. Yet, there may be some reason to want this.
> Muxer needs crc.patch to use av_crc04C11DB7_update.
>
> It also needs xiph.c from libavcodec, I don't know how to handle
> dependence correctly though.
>
> Supports theora and vorbis.
>
> Works with stream copy and encoding with libtheora/libvorbis and native
> vorbis encoder.
>
> Index: libavformat/oggenc.c
> ===================================================================
> --- libavformat/oggenc.c (revision 0)
> +++ libavformat/oggenc.c (revision 0)
> @@ -0,0 +1,180 @@
> +/*
> + * Ogg muxer.
> + * Copyright (c) 2007 Baptiste Coudurier <baptiste dot coudurier at free dot fr>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "avformat.h"
> +#include "crc.h"
> +#include "xiph.h"
> +
> +typedef struct {
> + int64_t duration;
> + unsigned page_counter;
> + uint8_t *header[3];
> + int header_len[3];
> + /** for theora granule */
> + int kfgshift;
> + int64_t last_kf_pts;
> + int vrev;
> +} OGGStreamContext;
> +
> +static void ogg_update_checksum(AVFormatContext *s, offset_t crc_offset)
> +{
> + offset_t pos = url_ftell(&s->pb);
> + uint32_t checksum = get_checksum(&s->pb);
> + url_fseek(&s->pb, crc_offset, SEEK_SET);
> + put_be32(&s->pb, checksum);
> + url_fseek(&s->pb, pos, SEEK_SET);
> +}
> +
> +static int ogg_write_page(AVFormatContext *s, const uint8_t *data, int size,
> + int64_t granule, int stream_index, int flags)
> +{
> + OGGStreamContext *oggstream = s->streams[stream_index]->priv_data;
> + offset_t crc_offset;
> + int page_segments, i;
> +
> + size = FFMIN(size, 255*255);
> + page_segments = size ? FFMIN((size/255)+1, 255) : 0;
page_segments = (size + 254) / 255;
> + init_checksum(&s->pb, av_crc04C11DB7_update, 0);
> + put_tag(&s->pb, "OggS");
> + put_byte(&s->pb, 0);
> + put_byte(&s->pb, flags);
> + put_le64(&s->pb, granule);
> + put_le32(&s->pb, stream_index);
> + put_le32(&s->pb, oggstream->page_counter++);
> + crc_offset = url_ftell(&s->pb);
> + put_le32(&s->pb, 0); // crc
> + put_byte(&s->pb, page_segments);
> + for (i = 0; i < page_segments-1; i++)
> + put_byte(&s->pb, 255);
> + if (size) {
> + put_byte(&s->pb, size - (page_segments-1)*255);
> + put_buffer(&s->pb, data, size);
> + }
> + ogg_update_checksum(s, crc_offset);
> + put_flush_packet(&s->pb);
> + return size;
> +}
> +
> +static int ogg_write_header(AVFormatContext *s)
> +{
> + int i, j;
> + for (i = 0; i < s->nb_streams; i++) {
> + AVStream *st = s->streams[i];
> + if (st->codec->codec_type == CODEC_TYPE_AUDIO)
> + av_set_pts_info(st, 64, 1, st->codec->sample_rate);
> + else if (st->codec->codec_type == CODEC_TYPE_VIDEO)
> + av_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den);
Please try to keep lines less than 80 characters in length.
> + if (st->codec->codec_id == CODEC_ID_VORBIS || st->codec->codec_id == CODEC_ID_THEORA) {
Consider using a switch statement here to ease addition of more codecs.
> + OGGStreamContext *oggstream;
> + if (!st->codec->extradata || !st->codec->extradata_size) {
> + av_log(s, AV_LOG_ERROR, "No extradata present\n");
> + return -1;
> + }
> + oggstream = av_mallocz(sizeof(OGGStreamContext));
sizeof(*oggstream)
> + st->priv_data = oggstream;
> + if (ff_split_xiph_headers(st->codec->extradata, st->codec->extradata_size,
> + st->codec->codec_id == CODEC_ID_VORBIS ? 30 : 42,
> + oggstream->header, oggstream->header_len) < 0) {
80 columns, please.
> + av_log(s, AV_LOG_ERROR, "Vorbis extradata corrupted\n");
> + av_freep(&st->priv_data);
> + return -1;
> + }
> + if (st->codec->codec_id == CODEC_ID_THEORA) {
> + oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5);
80 columns. A comment explaining what that number is would be useful
too. BTW, can that value be trusted? I guess we have no choice but
to trust the encoder.
> + oggstream->vrev = oggstream->header[0][9];
> + av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n",
> + oggstream->kfgshift, oggstream->vrev);
> + }
> + } else {
> + av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i);
> + return -1;
> + }
> + }
> + for (i = 0; i < 3; i++) {
> + for (j = 0; j < s->nb_streams; j++) {
> + AVStream *st = s->streams[j];
> + if (st->priv_data) {
> + OGGStreamContext *oggstream = st->priv_data;
> + ogg_write_page(s, oggstream->header[i], oggstream->header_len[i],
> + 0, st->index, i ? 0 : 2); // bos
> + }
> + }
> + }
> + return 0;
> +}
> +
> +static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + AVStream *st = s->streams[pkt->stream_index];
> + OGGStreamContext *oggstream = st->priv_data;
> + uint8_t *ptr = pkt->data;
> + int ret, size = pkt->size;
> + int64_t granule;
> +
> + if (st->codec->codec_id == CODEC_ID_THEORA) {
> + int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts + pkt->duration;
> + int pframe_count;
> + if (pkt->flags & PKT_FLAG_KEY)
> + oggstream->last_kf_pts = pts;
> + pframe_count = pts - oggstream->last_kf_pts;
> + // prevent frame count from overflow if key frame flag is not set
Trailing whitespace.
> + if (pframe_count >= (1<<oggstream->kfgshift)) {
> + oggstream->last_kf_pts += pframe_count;
> + pframe_count = 0;
> + }
> + granule = (oggstream->last_kf_pts<<oggstream->kfgshift) | pframe_count;
> + } else
Trailing whitespace.
> + granule = pkt->pts + pkt->duration;
This is wrong. The granule number refers to the last sample of the
page, which is one less than this.
> + oggstream->duration = granule;
> +
> + do {
> + ret = ogg_write_page(s, ptr, size, granule, pkt->stream_index, ptr != pkt->data);
80 columns.
> + ptr += ret; size -= ret;
Split that line.
> + } while (size > 0 || ret == 255*255); // need to output a last nil page
> +
> + return 0;
> +}
> +
> +
> +static int ogg_write_trailer(AVFormatContext *s)
> +{
> + int i;
> + for (i = 0; i < s->nb_streams; i++) {
> + OGGStreamContext *oggstream = s->streams[i]->priv_data;
> + ogg_write_page(s, NULL, 0, oggstream->duration, i, 4); // eos
> + av_freep(&s->streams[i]->priv_data);
> + }
> + return 0;
> +}
> +
> +AVOutputFormat ogg_muxer = {
> + "ogg",
> + "Ogg format",
> + "application/ogg",
> + "ogg",
> + 0,
> + CODEC_ID_VORBIS,
> + CODEC_ID_THEORA,
> + ogg_write_header,
> + ogg_write_packet,
> + ogg_write_trailer,
> +};
> Index: configure
> ===================================================================
> --- configure (revision 10867)
> +++ configure (working copy)
> @@ -91,7 +91,6 @@
> echo " --enable-libmp3lame enable MP3 encoding via libmp3lame [default=no]"
> echo " --enable-libnut enable NUT (de)muxing via libnut,"
> echo " native demuxer exists [default=no]"
> - echo " --enable-libogg enable Ogg muxing via libogg [default=no]"
Removing libogg support should be a separate patch.
> echo " --enable-libtheora enable Theora encoding via libtheora [default=no]"
> echo " --enable-libvorbis enable Vorbis en/decoding via libvorbis,"
> echo " native implementations exist [default=no]"
> @@ -620,7 +619,6 @@
> libgsm
> libmp3lame
> libnut
> - libogg
> libtheora
> libvorbis
> libx264
> @@ -801,7 +799,6 @@
> libnut_demuxer_deps="libnut"
> libnut_muxer_deps="libnut"
> mp3_demuxer_deps="mpegaudio_parser"
> -ogg_muxer_deps="libogg"
> oss_demuxer_deps_any="soundcard_h sys_soundcard_h"
> oss_muxer_deps_any="soundcard_h sys_soundcard_h"
> redir_demuxer_deps="network"
> @@ -1248,11 +1245,6 @@
>
> disabled static && LIBNAME=""
>
> -if ! enabled libogg; then
> - enabled libtheora && die "libogg must be enabled to enable libtheora."
> - enabled libvorbis && die "libogg must be enabled to enable libvorbis."
> -fi
> -
> if enabled_any libfaad libfaadbin ; then
> if check_header faad.h; then
> check_cc << EOF
> @@ -1566,9 +1559,8 @@
> enabled libgsm && require libgsm gsm.h gsm_create -lgsm
> enabled libmp3lame && require LAME lame/lame.h lame_init -lmp3lame -lm
> enabled libnut && require libnut libnut.h nut_demuxer_init -lnut
> -enabled libogg && require libogg ogg/ogg.h ogg_sync_init -logg
> -enabled libtheora && require libtheora theora/theora.h theora_info_init -ltheora -logg
> -enabled libvorbis && require libvorbis vorbis/vorbisenc.h vorbis_info_init -lvorbis -lvorbisenc -logg
> +enabled libtheora && require libtheora theora/theora.h theora_info_init -ltheora
> +enabled libvorbis && require libvorbis vorbis/vorbisenc.h vorbis_info_init -lvorbisenc
libvorbis and (I believe) libtheora require libogg.
--
M?ns Rullg?rd
mans at mansr.com
More information about the ffmpeg-devel
mailing list