[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