[Ffmpeg-devel] FFmpeg Theora encoding patch

Paul Richards paul.richards
Sun Jan 7 16:41:46 CET 2007


Hi,
I have rolled in many of these suggestions.  See below for details.. :)

I'll post the revised patch to a new thread once I've rolled in the
suggestions from everyone.


On 06/01/07, Michael Niedermayer <michaelni at gmx.at> wrote:
> Hi
>
> On Sat, Jan 06, 2007 at 03:51:28PM +0000, Paul Richards wrote:
> > Hi,
> > Attached is my patch to add theora encoding to ffmpeg's libavcodec (by
> > using libtheora).  I am requesting help to fix the bug I mention below
> > and am seeking general comments before I submit the patch properly.
> >
> > Files encoded using this encoder have a problem playing in VLC.  The
> > files will not play unless "Drop late frames" has been unticked in the
> > advanced video settings.  Hopefully someone can tell me where this
> > problem comes from.  I am not sure which API (FFmpeg or libtheora) I
> > am misusing.
>
> are these .avi or .ogg?
> and does it work if you create the same theora file with another tool then
> ffmpeg?
> and what does "ffmpeg -v 9 -i yourfile" say?
>

I get lots of debugging info (presumably from vp3 decoder), none of
the output looks like error.  Here are the first few lines:

[theora @ 0x458594]Theora bitstream version 30200
[theora @ 0x458594]344 bits left in packet 81
[theora @ 0x458594]7 bits left in packet 82
Input #0, avi, from 'test.avi':
  Duration: 00:00:07.4, start: 0.000000, bitrate: 190 kb/s
  Stream #0.0, 1/15: Video: theora, yuv420p, 320x240, 1/15, 15.00 fps(r)
Output #0, avi, to 'test3.avi':
  Stream #0.0, 1/90000: Video: mpeg4, yuv420p, 320x240, 1/15, q=2-31,
200 kb/s, 15.00 fps(c)
Stream mapping:
  Stream #0.0 -> #0.0
[theora @ 0x458594]Theora bitstream version 30200
[theora @ 0x458594]344 bits left in packet 81
[theora @ 0x458594]7 bits left in packet 82
Press [q] to stop encoding
1471320 dezicycles in init_frame, 1 runs, 0 skips
1075320 dezicycles in unpack_superblocks, 1 runs, 0 skips
152640 dezicycles in unpack_modes, 1 runs, 0 skips
4440 dezicycles in unpack_vectors, 1 runs, 0 skips
...

>
> [...]
> > Index: libavcodec/theora_enc.c
> > ===================================================================
> > --- libavcodec/theora_enc.c   (revision 0)
> > +++ libavcodec/theora_enc.c   (revision 0)
> [...]
> > +/**
> > + * @file theora_enc.c
> > + * Theora encoder using libtheora.
> > + *
> > + * A lot of this is copy / paste from other output codecs in
> > + * libavcodec or pure guesswork (or both).
> > + *
> > + * I have used t_ prefixes on variables which are libtheora types
> > + * and o_ prefixes on variables which are libogg types.
> > + *
> > + * @author Paul Richards <paul.richards at gmail.com>
> > + */
> > +
> > +/** FFmpeg includes */
>
> this is not the correct doxygen syntax to cover >1 element RTFM of doxygen
>

So apart from changing /** to /*! and swapping @... with \..., I think
my doxygen was correct.  Or was there something else you took issue
with?

>
> > +#include "avcodec.h"
> > +#include "log.h"
>
> [...]
> > +/** TheoraContext */
> > +typedef struct TheoraContext{
>
> this comment is giga useless if thats the only thing to say about it then it
> would be better not to say it ...
>

True.  Comment has been axed.

>
> > +    theora_state t_state;
> > +} TheoraContext;
> > +
> > +/** Forward declares for the internal functions below */
> > +static int encode_init(AVCodecContext* avctx);
> > +static int encode_close(AVCodecContext* avctx);
> > +static int encode_frame(AVCodecContext* avctx, uint8_t *buf, int buf_size, void *data);
>
> always try to order code so as to avoid forward declarations ...
>
>

Done.

> [...]
>
> > +/** Internal stuff below */
> > +#define NOT_IMPLEMENTED_YET 0;
>
> ?

I was using "assert(NOT_IMPLEMENTED_YET)" so I'd get "readable"
run-time assertions.  Forgot to remove the define.

>
>
> > +
> > +/**
> > +    Checks that an AVCodexContext has a pixel format we understand.
> > +*/
> > +static int check_constraints(AVCodecContext* avc_context)
> > +{
> > +    /**
> > +        Currently only support the following input formats:
> > +
> > +        PIX_FMT_YUV420P:
> > +        Planar YUV 4:2:0, 12bpp,
> > +        (1 Cr & Cb sample per 2x2 Y samples).
> > +    */
> > +    if (avc_context->pix_fmt != PIX_FMT_YUV420P) {
> > +        av_log(avc_context, AV_LOG_ERROR, "have only implemented PIX_FMT_YUV420P input");
> > +        return -1;
> > +    }
> > +
> > +    return 0;
> > +}
>
> theres a much simpler way to achive this, and that is to set AVCodec.pix_fmts
>

Excellent, done. :)

>
> [...]
> > +    t_info.keyframe_frequency = 64;
> > +    t_info.keyframe_frequency_force = 64;
>
> AVCodecContext.gop_size
>
>
> > +    t_info.keyframe_data_target_bitrate = t_info.target_bitrate * 1.5;
> > +    t_info.keyframe_auto_threshold = 80;
> > +    t_info.keyframe_mindistance = 8;
>
> AVCodecContext.keyint_min
>

Also done.

>
> [...]
> > +static int encode_frame(
> > +    AVCodecContext* avc_context,
> > +    uint8_t *outbuf,
> > +    int buf_size,
> > +    void *data)
> > +{
> > +    yuv_buffer t_yuv_buffer;
> > +    TheoraContext *h = avc_context->priv_data;
> > +    AVFrame *frame = data;
> > +    ogg_packet o_packet;
> > +    int result;
> > +
> > +    /** Check pixel format contraints */
> > +    if (check_constraints(avc_context) != 0) {
> > +        return -1;
> > +    }
> > +
> > +    /** Copy planes to the theora yuv_buffer */
>
> doxygen doesnt support such comments in the middle of the code i _think_
> at least not last time i checked ...
>

Ok, have swapped these for normal C-style comments.

>
> [...]
> > +    /** Now call into theora_encode_YUVin */
> > +    result = theora_encode_YUVin( &(h->t_state), &t_yuv_buffer );
> > +    switch (result) {
> > +        case 0:
> > +            /** Success */
> > +            break;
> > +        case -1:
> > +            av_log(avc_context, AV_LOG_ERROR, "theora_encode_YUVin failed (differing frame sizes)\n");
> > +            return -1;
> > +        case OC_EINVAL:
> > +            av_log(avc_context, AV_LOG_ERROR, "theora_encode_YUVin failed (encoder is not ready or is finished)\n");
> > +            return -1;
> > +        default:
> > +            av_log(avc_context, AV_LOG_ERROR, "theora_encode_YUVin failed [%d]\n", result);
> > +            return -1;
> > +    }
>
> id write
>
> if(result){
>     char *err= "?";
>     if     (result==-1       ) err= "differing frame sizes";
>     else if(result==OC_EINVAL) err= "encoder is not ready or is finished";
>     av_log(avc_context, AV_LOG_ERROR, "theora_encode_YUVin failed [%d](%s)\n", result, err);
>     return -1;
> }
>
>

I'm afraid I've stuck with the switch statement.  It's probably a
matter of taste but I think it more clearly shows my intent.  If you
feel strongly about this then let me know and I'll switch the code to
what you suggest.


-- 
Paul Richards




More information about the ffmpeg-devel mailing list