[FFmpeg-devel] [PATCH] Per-frame metadata

Stefano Sabatini stefano.sabatini-lala at poste.it
Thu Jun 16 14:37:15 CEST 2011


On date Friday 2011-06-10 20:47:51 +0200, Nicolas George encoded:
> Hi. After the recent move of the metadata API to libavutil, I rebased and
> reworked my proposal for per-frame metadata decoding:
> 
> 1/5: Introduce av_size_mult and av_realloc_f
> 
> This is very similar to the original proposal. The chunk with av_size_mult
> was ok-ed, but was later changed to return an AVERROR code rather than -1.
> 
> I also learned in the meantime that BSD has a reallocf function, which is
> similar to the proposed av_realloc_f: it frees the memory if it fails, but
> it does not check for integer overflow.
> 
> 2/5: Replace av_realloc by av_realloc_f when relevant
> 
> Unchanged except for merge conflicts.
> 
> 3/5: Introduce a metadata field in AVFrame
> 
> It also introduces CODEC_FLAG2_FRAME_METADATA: per-frame metadata is only
> decoded if this flag is set. This avoids memory leaks if release_buffer does
> not free the metadata.
> 
> 4/5: PNG: decode textual data (tEXt and zTXt chunks)
> 
> Almost identical to the original proposal.
> 
> 5/5: ffmpeg: dump per-frame metadata
> 
> For testing purposes; I am not sure if we want it or not.
> 
> Regards,
> 
> -- 
>   Nicolas George

> From d203796c069d88a8354cdd3513245ec247fc6b4b Mon Sep 17 00:00:00 2001
> From: Nicolas George <nicolas.george at normalesup.org>
> Date: Sun, 20 Mar 2011 19:39:20 +0100
> Subject: [PATCH 1/5] Introduce av_size_mult and av_realloc_f.
> 
> av_size_mult helps checking for overflow when computing the size of a memory
> area.
> 
> av_realloc_f helps avoiding memory-leaks in typical uses of realloc.
> 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  libavutil/mem.c |   15 +++++++++++++++
>  libavutil/mem.h |   26 ++++++++++++++++++++++++++
>  2 files changed, 41 insertions(+), 0 deletions(-)
> 
> diff --git a/libavutil/mem.c b/libavutil/mem.c
> index 87c2008..2e365f6 100644
> --- a/libavutil/mem.c
> +++ b/libavutil/mem.c
> @@ -143,6 +143,21 @@ void *av_realloc(void *ptr, size_t size)
>  #endif
>  }
>  
> +void *av_realloc_f(void *ptr, size_t nelem, size_t elsize)

Nit++: nb_elems and elem_size for readability/consistency.

> +{
> +    size_t size;
> +    void *r;
> +
> +    if (av_size_mult(elsize, nelem, &size)) {
> +        av_free(ptr);
> +        return NULL;
> +    }
> +    r = av_realloc(ptr, size);
> +    if (!r && size)
> +        av_free(ptr);
> +    return r;
> +}
> +
>  void av_free(void *ptr)
>  {
>  #if CONFIG_MEMALIGN_HACK
> diff --git a/libavutil/mem.h b/libavutil/mem.h
> index 7c30e16..8876087 100644
> --- a/libavutil/mem.h
> +++ b/libavutil/mem.h
> @@ -27,6 +27,7 @@
>  #define AVUTIL_MEM_H
>  
>  #include "attributes.h"
> +#include "error.h"
>  #include "avutil.h"
>  
>  #if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1110 || defined(__SUNPRO_C)


> @@ -87,6 +88,16 @@ void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
>  void *av_realloc(void *ptr, size_t size) av_alloc_size(2);
>  

>  /**
> + * Allocate or reallocate a block of memory.
> + * This function does the same thing as av_realloc, except:
> + * - It takes two arguments and checks the result of the multiplication for
> + *   integer overflow.
> + * - It frees the input block in case of failure, thus avoiding the memory
> + *   leak with the classic "buf = realloc(buf); if (!buf) return -1;".
> + */
> +void *av_realloc_f(void *ptr, size_t nelem, size_t elsize);

Nit: I prefer to avoid references to other functions, so the docs will
result auto-contained and won't require updates if the referred
function is changed.

> +
> +/**
>   * Free a memory block which has been allocated with av_malloc(z)() or
>   * av_realloc().
>   * @param ptr Pointer to the memory block which should be freed.
> @@ -132,4 +143,19 @@ void av_freep(void *ptr);
>   */
>  void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem);
>  
> +/**
> + * Multiply two size_t values checking for overflow.
> + * @return  0 if success, AVERROR(EINVAL) if overflow.
> + */
> +static inline int av_size_mult(size_t a, size_t b, size_t *r)

Maybe:
static inline int av_mult_size(size_t *r, size_t a, size_t b);

Mult is the verb and thus should be mentioned before (like in
"multiply size") since we are not adopting hierachi notation here, r
before a and b is consistent with the usual C notation:
foo(r, a, b) <=> r = a + b;

> +{
> +    size_t t = a * b;

> +    /* Hack inspired from glibc: only try the division if nelem and elsize
> +     * are both greater than sqrt(SIZE_MAX). */
> +    if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b)
> +        return AVERROR(EINVAL);

Dumb question... why not a simple:

if (b && a > SIZE_MAX / b)
   return AVERROR(EINVAL);
?

Is avoiding a division worth the added obfuscation?

> +    *r = t;
> +    return 0;
> +}
> +
>  #endif /* AVUTIL_MEM_H */
> -- 
> 1.7.5.3
> 

> From 8970285e2e219dffa865c4d27ec20e4ab1beefc8 Mon Sep 17 00:00:00 2001
> From: Nicolas George <nicolas.george at normalesup.org>
> Date: Tue, 22 Mar 2011 21:39:00 +0100
> Subject: [PATCH 2/5] Replace av_realloc by av_realloc_f when relevant.
> 
> Also mark with a visible comment "FIXME realloc failure" places where
> av_realloc seems to lack a proper test for failure.
> 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  cmdutils.c                      |    1 +
>  ffmpeg.c                        |    3 ++-
>  libavcodec/apedec.c             |    1 +
>  libavcodec/bitstream.c          |    4 ++--
>  libavcodec/eatgv.c              |    2 ++
>  libavcodec/flacdec.c            |    1 +
>  libavcodec/libdiracenc.c        |    1 +
>  libavcodec/libschroedingerenc.c |    1 +
>  libavcodec/libvpxenc.c          |    4 ++--
>  libavcodec/resample.c           |    1 +
>  libavcodec/rv34.c               |    1 +
>  libavcodec/shorten.c            |    1 +
>  libavcodec/truemotion2.c        |    1 +
>  libavcodec/vmnc.c               |    1 +
>  libavcodec/vp56.c               |    1 +
>  libavcodec/zmbv.c               |    1 +
>  libavfilter/avfilter.c          |    1 +
>  libavformat/4xm.c               |    5 +++--
>  libavformat/asfenc.c            |    1 +
>  libavformat/avidec.c            |    2 +-
>  libavformat/avienc.c            |    2 +-
>  libavformat/aviobuf.c           |    4 ++--
>  libavformat/gxfenc.c            |   10 ++++++----
>  libavformat/matroskadec.c       |    1 +
>  libavformat/movenc.c            |    2 +-
>  libavformat/mxfdec.c            |    1 +
>  libavformat/mxfenc.c            |    1 +
>  libavformat/oggdec.c            |    1 +
>  libavformat/oggparsetheora.c    |    1 +
>  libavformat/oggparsevorbis.c    |    1 +
>  libavformat/rtmpproto.c         |    3 +++
>  libavformat/rtpdec_asf.c        |    1 +
>  libavformat/rtpdec_qt.c         |    1 +
>  libavformat/smacker.c           |    1 +
>  libavformat/utils.c             |    2 ++
>  libavutil/mem.c                 |    1 +
>  36 files changed, 51 insertions(+), 16 deletions(-)
[...]

Looks OK to me.

> From d64f9e8f097fb3e2240ea63a92eb7215bd608735 Mon Sep 17 00:00:00 2001
> From: Nicolas George <nicolas.george at normalesup.org>
> Date: Fri, 10 Jun 2011 19:53:32 +0200
> Subject: [PATCH 3/5] Introduce a metadata field in AVFrame.
> 
> 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  libavcodec/avcodec.h |    9 +++++++++
>  libavcodec/options.c |    1 +
>  libavcodec/utils.c   |    1 +
>  3 files changed, 11 insertions(+), 0 deletions(-)
> 
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index ef539a2..ca5fdd6 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -30,6 +30,7 @@
>  #include "libavutil/samplefmt.h"
>  #include "libavutil/avutil.h"
>  #include "libavutil/cpu.h"
> +#include "libavutil/dict.h"
>  
>  #include "libavcodec/version.h"
>  
> @@ -619,6 +620,7 @@ typedef struct RcOverride{
>  #define CODEC_FLAG2_PSY           0x00080000 ///< Use psycho visual optimizations.
>  #define CODEC_FLAG2_SSIM          0x00100000 ///< Compute SSIM during encoding, error[] values are undefined.
>  #define CODEC_FLAG2_INTRA_REFRESH 0x00200000 ///< Use periodic insertion of intra blocks instead of keyframes.
> +#define CODEC_FLAG2_FRAME_METADATA 0x00400000 ///< Decode per-frame metadata.
>  
>  /* Unsupported options :
>   *              Syntax Arithmetic coding (SAC)
> @@ -1038,6 +1040,13 @@ typedef struct AVPanScan{
>       * - decoding: Read by user.\
>       */\
>      int format;\
> +\
> +    /**
> +     * per-frame metadata\
> +     * - encoding: currently unused\
> +     * - decoding: set by libavcodec if CODEC_FLAG2_FRAME_METADATA is set\
> +     */\
> +    AVDictionary *metadata;
>  
>  
>  #define FF_QSCALE_TYPE_MPEG1 0
> diff --git a/libavcodec/options.c b/libavcodec/options.c
> index 78a7bc8..2c86dfd 100644
> --- a/libavcodec/options.c
> +++ b/libavcodec/options.c
> @@ -416,6 +416,7 @@ static const AVOption options[]={
>  {"rc_lookahead", "specify number of frames to look ahead for frametype", OFFSET(rc_lookahead), FF_OPT_TYPE_INT, {.dbl = 40 }, 0, INT_MAX, V|E},
>  {"ssim", "ssim will be calculated during encoding", 0, FF_OPT_TYPE_CONST, {.dbl = CODEC_FLAG2_SSIM }, INT_MIN, INT_MAX, V|E, "flags2"},
>  {"intra_refresh", "use periodic insertion of intra blocks instead of keyframes", 0, FF_OPT_TYPE_CONST, {.dbl = CODEC_FLAG2_INTRA_REFRESH }, INT_MIN, INT_MAX, V|E, "flags2"},

> +{"frame_metadata", "decode per-frame metadata", 0, FF_OPT_TYPE_CONST, {.dbl = CODEC_FLAG2_FRAME_METADATA }, INT_MIN, INT_MAX, A|V|D, "flags2"},

decode_frame_metadata is clearer but longer and I have no better
ideas, so thic can stay...

>  {"crf_max", "in crf mode, prevents vbv from lowering quality beyond this point", OFFSET(crf_max), FF_OPT_TYPE_FLOAT, {.dbl = DEFAULT }, 0, 51, V|E},
>  {"log_level_offset", "set the log level offset", OFFSET(log_level_offset), FF_OPT_TYPE_INT, {.dbl = 0 }, INT_MIN, INT_MAX },
>  #if FF_API_FLAC_GLOBAL_OPTS
> diff --git a/libavcodec/utils.c b/libavcodec/utils.c
> index 922decf..5020d2a 100644
> --- a/libavcodec/utils.c
> +++ b/libavcodec/utils.c
> @@ -388,6 +388,7 @@ void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic){
>  //        pic->base[i]=NULL;
>      }
>  //printf("R%X\n", pic->opaque);
> +    av_dict_free(&pic->metadata);
>  
>      if(s->debug&FF_DEBUG_BUFFERS)
>          av_log(s, AV_LOG_DEBUG, "default_release_buffer called on pic %p, %d buffers used\n", pic, s->internal_buffer_count);
> -- 
> 1.7.5.3
> 

> From 875b8f1dba58adc9f6dce3bc05e941a6009f6d1b Mon Sep 17 00:00:00 2001
> From: Nicolas George <nicolas.george at normalesup.org>
> Date: Fri, 10 Jun 2011 20:05:19 +0200
> Subject: [PATCH 4/5] PNG: decode textual data (tEXt and zTXt chunks).
> 
> 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  libavcodec/pngdec.c |  242 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 242 insertions(+), 0 deletions(-)
> 
> diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
> index 05ba027..59a626c 100644
> --- a/libavcodec/pngdec.c
> +++ b/libavcodec/pngdec.c
> @@ -42,6 +42,134 @@ static const uint8_t png_pass_dsp_mask[NB_PASSES] = {
>      0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff
>  };
>  

> +/*
> + * PNG tEXt and zTXt chunks are in ISO-8859-1.
> + */
> +
> +/**
> + * Converts text from ISO-8859-1 to UTF-8.

nit: Convert

> + * @param in        input text in ISO-8859-1.

no need for the final dot (uncomplete sentence)

> + * @param size      size of the input text.

no need for the final dot, size *in bytes* of the input text.
size_in is clearer (since we already have a size_out)

> + * @param size_out  if not NULL, will be set to the size of the output.
> + * @return  newly allocated buffer with the converted text, NUL-terminated;
> +            NULL if the buffer could not be allocated.

Nit++: no need to the final point

> + */
> +static uint8_t *ff_iso88591_to_utf8(const uint8_t *in, size_t size,
> +                                    size_t *size_out)
> +{
> +    size_t extra = 0, i;
> +    uint8_t c;
> +    const uint8_t *p;
> +    uint8_t *q, *out;
> +
> +    if (size == SIZE_MAX) /* no room for terminating NUL anyway */
> +        return NULL;
> +    p = in;
> +    for (i = 0; i < size; i++)
> +        if (*(p++) >= 0x80)
> +            extra++;
> +    if (extra > SIZE_MAX - size - 1)
> +        return NULL;
> +    q = out = av_malloc(size + extra + 1);
> +    for (i = 0; i < size; i++) {
> +        c = *(in++);
> +        if (c >= 0x80) {
> +            *(q++) = 0xC0 | (c >> 6);
> +            *(q++) = 0x80 | (c & 0x3F);
> +        } else {
> +            *(q++) = c;
> +        }
> +    }
> +    *(q++) = 0;
> +    if (size_out)
> +        *size_out = size + extra;
> +    return out;
> +}
> +

> +/**
> + * Grow an automatic buffer to a minimum size.
> + * This function is similar to ff_abuffer_grow except that it always
> + * reallocates the buffer, even if it is already large enough.
> + */
> +static void *ff_abuffer_grow_always(void *buf0, size_t elsize,
> +                                    void *buf, size_t *size, size_t min_size)


> +{
> +    size_t new_size = FFMAX(min_size, *size * 2);
> +    size_t bytes;
> +    void *nbuf;
> +
> +    if (av_size_mult(elsize, new_size, &bytes))
> +        return buf;
> +    nbuf = av_realloc(buf == buf0 ? NULL : buf, bytes);
> +    if (!nbuf)
> +        return buf;
> +    if (buf == buf0)
> +        memcpy(nbuf, buf0, elsize * FFMIN(*size, new_size));
> +    *size = new_size;
> +    return nbuf;
> +}
> +
> +/**
> + * Grow an automatic buffer to a minimum size.
> + * An automatic buffer is a buffer that starts allocated on a stack as an
> + * automatic variable, and is enlarged on the heap if necessary.
> + * The typical declaration looks like that:
> + *     struct foo buf0[128];
> + *     struct foo *buf = buf0;
> + *     size_t buf_size = FF_ARRAY_ELEMS(buf0);

Is this API really needed? Couldn't you always use the heap?

> + * This function ensures that the allocated size is at least min_size,
> + * reallocating the buffer if necessary.
> + * If the reallocation fails, the buffer is unchanged:
> + * the correct way to check for success is to compare size and min_size.
> + * @param buf0      original (on stack) buffer.
> + * @param elsize    size of individual elements.
> + * @param buf       current buffer.
> + * @param size      current allocated size.
> + * @param min_size  requested size.
> + * @return new buffer, or old one if failure.
> + */
> +static inline void *ff_abuffer_grow(void *buf0, size_t elsize,
> +                                    void *buf, size_t *size, size_t min_size)
> +{
> +    return *size >= min_size ? buf :
> +        ff_abuffer_grow_always(buf0, elsize, buf, size, min_size);
> +}
> +
> +/**
> + * Free an automatic buffer.
> + * Calling this function is always safe.
> + */
> +static void ff_abuffer_free(void *buf0, void *buf)
> +{
> +    if (buf != buf0)
> +        av_free(buf);
> +}
> +
> +/**
> + * Finish an automatic buffer.
> + * This function ensures that the buffer is heap-allocated and shrinks it if
> + * necessary. The resulting memory area should be freed using av_free.
> + * @param buf0    original (on stack) buffer.
> + * @param elsize  size of individual elements.
> + * @param buf     current value.
> + * @param size    requested size; must not be greater than the current
> + *                allocated size.
> + * @return  final value, or NULL if not possible.
> + * This function can only fail if the buffer is still on the stack: extra
> + * cleanup measures are not necessary.
> + */
> +static void *ff_abuffer_finish(void *buf0, size_t elsize,
> +                               void *buf, size_t size)
> +{
> +    void *nbuf = av_realloc(buf == buf0 ? NULL : buf, elsize * size);
> +    if (!nbuf)
> +        return buf == buf0 ? NULL : buf;
> +    if (buf == buf0)
> +        memcpy(nbuf, buf0, elsize * size);
> +    return nbuf;
> +}
> +
> +
>  /* NOTE: we try to construct a good looking image at each pass. width
>     is the original image width. We also do pixel format conversion at
>     this stage */
> @@ -368,6 +496,55 @@ static int png_decode_idat(PNGDecContext *s, int length)
>      return 0;
>  }
>  
> +/**
> + * Decode completely a zlib-compressed buffer.
> + * The buffer is additionnally converted from ISO-8859-1 to UTF-8.
> + * The calling function must have checked that the input bytestream is long
> + * enough.
> + */
> +static int png_decode_zbuf(PNGDecContext *s, int length,
> +                           uint8_t **buf_ret, size_t *size_ret)
> +{
> +    int ret;
> +    z_stream zstream;
> +    uint8_t buf0[512], *buf = buf0, *utf8;
> +    size_t buf_size = sizeof(buf0), out_size = 0, utf8_size = 0;
> +
> +    zstream.zalloc = ff_png_zalloc;
> +    zstream.zfree  = ff_png_zfree;
> +    zstream.opaque = NULL;
> +    if (inflateInit(&zstream) != Z_OK)
> +        return -1;
> +    zstream.next_in  = s->bytestream;
> +    zstream.avail_in = length;
> +
> +    while (zstream.avail_in > 0) {
> +        buf = ff_abuffer_grow(buf0, 1, buf, &buf_size, out_size + 256);
> +        zstream.next_out  = buf + out_size;
> +        zstream.avail_out = buf_size - 1 - out_size;
> +        ret = inflate(&zstream, Z_PARTIAL_FLUSH);
> +        if (ret != Z_OK && ret != Z_STREAM_END)
> +            goto png_decode_zbuf_fail;
> +        out_size = zstream.next_out - buf;
> +        if (ret == Z_STREAM_END)
> +            break;
> +    }
> +    inflateEnd(&zstream);
> +    utf8 = ff_iso88591_to_utf8(buf, out_size, &utf8_size);
> +    ff_abuffer_free(buf0, buf);
> +    if (!utf8)
> +        return -1;
> +    *size_ret = utf8_size;
> +    *buf_ret  = utf8;
> +    s->bytestream += length;
> +    return 0;
> +png_decode_zbuf_fail:
> +    inflateEnd(&zstream);
> +    ff_abuffer_free(buf0, buf);
> +    return -1;
> +}
> +
> +
>  static int decode_frame(AVCodecContext *avctx,
>                          void *data, int *data_size,
>                          AVPacket *avpkt)
> @@ -380,6 +557,8 @@ static int decode_frame(AVCodecContext *avctx,
>      uint8_t *crow_buf_base = NULL;
>      uint32_t tag, length;
>      int ret;
> +    AVDictionary *metadata = NULL;
> +

Nit+++: superfluous empty line

>  
>      FFSWAP(AVFrame *, s->current_picture, s->last_picture);
>      avctx->coded_frame= s->current_picture;
> @@ -567,6 +746,67 @@ static int decode_frame(AVCodecContext *avctx,
>                  s->bytestream += 4; /* crc */
>              }
>              break;
> +        case MKTAG('t', 'E', 'X', 't'):
> +            if (!(avctx->flags2 & CODEC_FLAG2_FRAME_METADATA))
> +                goto skip_tag;
> +            {
> +                const uint8_t *keyword, *keyword_end;
> +                uint8_t *data;
> +                size_t data_size;
> +                if (s->bytestream + length >= s->bytestream_end)
> +                    goto skip_tag;
> +                keyword = s->bytestream;
> +                keyword_end = memchr(keyword, 0, length);
> +                if (!keyword_end ||
> +                    s->bytestream + length - keyword_end < 1) {
> +                    av_log(avctx, AV_LOG_ERROR, "Bogus textual data.\n");
> +                    goto skip_tag;
> +                }
> +                data_size = (s->bytestream + length) - (keyword_end + 1);
> +                data = ff_iso88591_to_utf8(keyword_end + 1, data_size,
> +                                           &data_size);
> +                if (!data)
> +                    goto skip_tag;
> +                s->bytestream += length;
> +                s->bytestream += 4; /* crc */
> +                av_dict_set(&metadata, keyword, data, AV_DICT_DONT_STRDUP_VAL);
> +            }
> +            break;
> +        case MKTAG('z', 'T', 'X', 't'):
> +            if (!(avctx->flags2 & CODEC_FLAG2_FRAME_METADATA))
> +                goto skip_tag;
> +            {
> +                const uint8_t *keyword, *keyword_end;
> +                uint8_t *data;
> +                size_t data_size;
> +                int method;
> +                if (s->bytestream + length >= s->bytestream_end)
> +                    goto skip_tag;
> +                keyword = s->bytestream;
> +                keyword_end = memchr(keyword, 0, length);
> +                if (!keyword_end ||
> +                    s->bytestream + length - keyword_end < 3) {
> +                    av_log(avctx, AV_LOG_ERROR, "Bogus textual data.\n");
> +                    goto skip_tag;
> +                }
> +                method = keyword_end[1];
> +                length -= keyword_end + 2 - s->bytestream;
> +                s->bytestream = keyword_end + 2;
> +                if (method) {
> +                    av_log(avctx, AV_LOG_ERROR,
> +                           "Unsupported compression method.\n");
> +                    goto skip_tag;
> +                }
> +                if (png_decode_zbuf(s, length, &data, &data_size) < 0) {
> +                    av_log(avctx, AV_LOG_ERROR,
> +                           "Textual data decompression failed.\n");
> +                    goto skip_tag;
> +                }
> +                s->bytestream += 4; /* crc */
> +                av_dict_set(&metadata, keyword, data, AV_DICT_DONT_STRDUP_VAL);
> +            }
> +            break;
> +
>          case MKTAG('I', 'E', 'N', 'D'):
>              if (!(s->state & PNG_ALLIMAGE))
>                  goto fail;
> @@ -597,6 +837,7 @@ static int decode_frame(AVCodecContext *avctx,
>          }
>      }
>  
> +    s->current_picture->metadata = metadata;
>      *picture= *s->current_picture;
>      *data_size = sizeof(AVFrame);
>  
> @@ -610,6 +851,7 @@ static int decode_frame(AVCodecContext *avctx,
>      return ret;
>   fail:
>      ret = -1;
> +    av_dict_free(&metadata);
>      goto the_end;
>  }
>  
> -- 
> 1.7.5.3
> 

> From 106cf1e34f6db337fdb8a193f2139667d0cd11a2 Mon Sep 17 00:00:00 2001
> From: Nicolas George <nicolas.george at normalesup.org>
> Date: Fri, 10 Jun 2011 20:15:51 +0200
> Subject: [PATCH 5/5] ffmpeg: dump per-frame metadata.
> 
> 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  ffmpeg.c |   12 ++++++++++++
>  1 files changed, 12 insertions(+), 0 deletions(-)
> 
> diff --git a/ffmpeg.c b/ffmpeg.c
> index 48f979c..a249483 100644
> --- a/ffmpeg.c
> +++ b/ffmpeg.c
> @@ -1509,6 +1509,17 @@ static void generate_silence(uint8_t* buf, enum AVSampleFormat sample_fmt, size_
>      memset(buf, fill_char, size);
>  }
>  

> +static void dump_frame_metadata(AVCodecContext *ctx, AVDictionary *m)
> +{
> +    AVDictionaryEntry *tag=NULL;

Nit+++: tag_=_NULL

"tag" is also a bit confusing ("entry" would be better)

> +
> +    if (!m)
> +        return;
> +    av_log(ctx, AV_LOG_INFO, "Frame metadata:\n");
> +    while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX)))
> +        av_log(ctx, AV_LOG_INFO, "  %-16s: %s\n", tag->key, tag->value);
> +}
> +
>  /* pkt = NULL means EOF (needed to flush decoder buffers) */
>  static int output_packet(AVInputStream *ist, int ist_index,
>                           AVOutputStream **ost_table, int nb_ostreams,
> @@ -1613,6 +1624,7 @@ static int output_packet(AVInputStream *ist, int ist_index,
>                          /* no picture yet */
>                          goto discard_packet;
>                      }
> +                    dump_frame_metadata(ist->st->codec, picture.metadata);
>                      ist->next_pts = ist->pts = picture.best_effort_timestamp;
>                      if (ist->st->codec->time_base.num != 0) {
>                          int ticks= ist->st->parser ? ist->st->parser->repeat_pict+1 : ist->st->codec->ticks_per_frame;
> -- 
> 1.7.5.3

Please one patch per thread, and thanks for the work.
-- 
FFmpeg = Fantastic & Forgiving Majestic Proud Ecumenical God


More information about the ffmpeg-devel mailing list