[FFmpeg-devel] [PATCH] EBU Tech 3285 – Supplement 3 - Peak Envelope Chunk encoder

Michael Niedermayer michaelni at gmx.at
Wed May 28 13:04:45 CEST 2014


On Sun, May 11, 2014 at 03:31:09PM +0200, Georg Lippitsch wrote:
> ---
>  libavformat/wavenc.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 246 insertions(+), 13 deletions(-)
> 
> diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c
> index c892c7b..5bf5e96 100644
> --- a/libavformat/wavenc.c
> +++ b/libavformat/wavenc.c
> @@ -8,6 +8,9 @@
>   * WAV muxer RF64 support
>   * Copyright (c) 2013 Daniel Verkamp <daniel at drv.nu>
>   *
> + * EBU Tech 3285 – Supplement 3 - Peak Envelope Chunk encoder
> + * Copyright (c) 2014 Georg Lippitsch <georg.lippitsch at gmx.at>
> + *
>   * This file is part of FFmpeg.
>   *
>   * FFmpeg is free software; you can redistribute it and/or
> @@ -28,10 +31,13 @@
>  #include <stdint.h>
>  #include <string.h>
>  
> +#include "libavutil/avstring.h"
>  #include "libavutil/dict.h"
>  #include "libavutil/common.h"
> +#include "libavutil/intreadwrite.h"
>  #include "libavutil/mathematics.h"
>  #include "libavutil/opt.h"
> +#include "libavutil/time.h"
>  
>  #include "avformat.h"
>  #include "avio.h"

> @@ -43,6 +49,13 @@
>  #define RF64_NEVER  0
>  #define RF64_ALWAYS 1
>  
> +#define PEAK_OFF           0
> +#define PEAK_ON            1
> +#define PEAK_ONLY          2
> +#define PEAK_FORMAT_UINT8  1
> +#define PEAK_FORMAT_UINT16 2

these should be enums


> +#define PEAK_BUFFER_SIZE   1024
> +
>  typedef struct WAVMuxContext {
>      const AVClass *class;
>      int64_t data;
> @@ -50,9 +63,22 @@ typedef struct WAVMuxContext {
>      int64_t ds64;
>      int64_t minpts;
>      int64_t maxpts;
> +    int16_t *peak_maxpos, *peak_maxneg;
> +    uint32_t peak_num_frames;
> +    uint32_t peak_outbuf_size;
> +    uint32_t peak_outbuf_bytes;
> +    uint32_t peak_pos_pop;
> +    uint16_t peak_pop;
> +    uint8_t *peak_output;
>      int last_duration;
>      int write_bext;
> +    int write_peak;
>      int rf64;
> +    int peak_block_size;

> +    int peak_format;

should be a enum


> +    int peak_block_pos;
> +    int peak_ppv;
> +    int peak_bps;
>  } WAVMuxContext;
>  
>  #if CONFIG_WAV_MUXER
> @@ -110,6 +136,159 @@ static void bwf_write_bext_chunk(AVFormatContext *s)
>      ff_end_tag(s->pb, bext);
>  }
>  
> +static av_cold void peak_free_buffers(AVFormatContext *s)
> +{
> +    WAVMuxContext *wav = s->priv_data;
> +

> +    av_free(wav->peak_maxpos);
> +    av_free(wav->peak_maxneg);
> +    av_free(wav->peak_output);

av_freep() would ensure no stale pointers remain


> +}
> +
> +static av_cold int peak_init_writer(AVFormatContext *s)
> +{
> +    WAVMuxContext *wav = s->priv_data;
> +    AVCodecContext *enc = s->streams[0]->codec;
> +
> +    if (enc->codec_id != AV_CODEC_ID_PCM_S8 &&
> +        enc->codec_id != AV_CODEC_ID_PCM_S16LE &&
> +        enc->codec_id != AV_CODEC_ID_PCM_U8 &&
> +        enc->codec_id != AV_CODEC_ID_PCM_U16LE) {
> +        av_log(s, AV_LOG_ERROR, "%s codec not supported for Peak Chunk\n",
> +               s->streams[0]->codec->codec ? s->streams[0]->codec->codec->name : "NONE");
> +        return -1;
> +    }
> +
> +    wav->peak_bps = av_get_bits_per_sample(enc->codec_id) / 8;
> +
> +    if (wav->peak_bps == 1 && wav->peak_format == 2) {
> +        av_log(s, AV_LOG_ERROR,
> +               "Writing 16 bit peak for 8 bit audio does not make sense\n");
> +        return -1;
> +    }
> +
> +    wav->peak_maxpos = av_mallocz(enc->channels * sizeof(*wav->peak_maxpos));
> +    if (!wav->peak_maxpos)
> +        goto nomem;
> +    wav->peak_maxneg = av_mallocz(enc->channels * sizeof(*wav->peak_maxneg));
> +    if (!wav->peak_maxneg)
> +        goto nomem;
> +
> +    wav->peak_output = av_malloc(PEAK_BUFFER_SIZE);
> +    if (!wav->peak_output)
> +        goto nomem;
> +
> +    wav->peak_outbuf_size = PEAK_BUFFER_SIZE;
> +
> +    return 0;
> +
> +nomem:
> +    av_log(s, AV_LOG_ERROR, "Out of memory\n");
> +    peak_free_buffers(s);
> +    return AVERROR(ENOMEM);
> +}
> +
> +static void peak_write_frame(AVFormatContext *s)
> +{
> +    WAVMuxContext *wav = s->priv_data;
> +    AVCodecContext *enc = s->streams[0]->codec;
> +    int peak_of_peaks;
> +    int c;
> +
> +    if (!wav->peak_output)
> +        return;
> +
> +    for (c = 0; c < enc->channels; c++) {
> +        wav->peak_maxneg[c] = -wav->peak_maxneg[c];
> +
> +        if (wav->peak_bps == 2 && wav->peak_format == 1) {
> +            wav->peak_maxpos[c] = wav->peak_maxpos[c] / 256;
> +            wav->peak_maxneg[c] = wav->peak_maxneg[c] / 256;
> +        }
> +
> +        if (wav->peak_ppv == 1)
> +            wav->peak_maxpos[c] =
> +                FFMAX(wav->peak_maxpos[c], wav->peak_maxneg[c]);
> +
> +        peak_of_peaks = FFMAX3(wav->peak_maxpos[c], wav->peak_maxneg[c],
> +                               wav->peak_pop);
> +        if (peak_of_peaks > wav->peak_pop)
> +            wav->peak_pos_pop = wav->peak_num_frames;
> +        wav->peak_pop = peak_of_peaks;
> +
> +        if (wav->peak_outbuf_size - wav->peak_outbuf_bytes <
> +            wav->peak_format * wav->peak_ppv) {
> +            wav->peak_outbuf_size += PEAK_BUFFER_SIZE;
> +            wav->peak_output = av_realloc(wav->peak_output,
> +                                          wav->peak_outbuf_size);
> +            if (!wav->peak_output) {
> +                av_log(s, AV_LOG_ERROR, "No memory for peak data\n");
> +                return;
> +            }
> +        }
> +

> +        if (wav->peak_format == 1) {

PEAK_FORMAT_UINT8 would be more readable than 1


    > +            wav->peak_output[wav->peak_outbuf_bytes++] =
> +                wav->peak_maxpos[c];
> +            if (wav->peak_ppv == 2) {
> +                wav->peak_output[wav->peak_outbuf_bytes++] =
> +                    wav->peak_maxneg[c];
> +            }
> +        } else {
> +            AV_WL16(wav->peak_output + wav->peak_outbuf_bytes,
> +                    wav->peak_maxpos[c]);
> +            wav->peak_outbuf_bytes += 2;
> +            if (wav->peak_ppv == 2) {
> +                AV_WL16(wav->peak_output + wav->peak_outbuf_bytes,
> +                        wav->peak_maxneg[c]);
> +                wav->peak_outbuf_bytes += 2;
> +            }
> +        }
> +        wav->peak_maxpos[c] = 0;
> +        wav->peak_maxneg[c] = 0;
> +    }
> +    wav->peak_num_frames++;
> +}
> +
> +static void peak_write_chunk(AVFormatContext *s)
> +{
> +    WAVMuxContext *wav = s->priv_data;
> +    AVIOContext *pb = s->pb;
> +    AVCodecContext *enc = s->streams[0]->codec;
> +    int64_t peak = ff_start_tag(s->pb, "levl");
> +    int64_t now0;
> +    time_t now_secs;
> +    char timestamp[28];
> +
> +    /* Peak frame of incomplete block at end */
> +    if (wav->peak_block_pos)
> +        peak_write_frame(s);
> +
> +    memset(timestamp, 0, 28);

> +    now0 = av_gettime();
> +    now_secs = now0 / 1000000;
> +    strftime(timestamp, 28, "%Y:%m:%d:%H:%M:%S:", localtime(&now_secs));
> +    av_strlcatf(timestamp, 28, "%03d", (int)((now0 / 1000) % 1000));

this cant be done when AVFMT_FLAG_BITEXACT is set.
Also what purpose does it serve to store the muxing time ?
This poses a security issue as it leaks information about the time
as well as the environment
I was not able to find any hint in the specification about the use
of this field


[...]

> @@ -177,7 +366,31 @@ static int wav_write_packet(AVFormatContext *s, AVPacket *pkt)
>  {
>      AVIOContext *pb  = s->pb;
>      WAVMuxContext    *wav = s->priv_data;
> -    avio_write(pb, pkt->data, pkt->size);
> +
> +    if (wav->write_peak != 2)
> +        avio_write(pb, pkt->data, pkt->size);
> +
> +    if (wav->write_peak) {
> +        int c = 0;
> +        int i;
> +        for (i = 0; i < pkt->size; i += wav->peak_bps) {

> +            if (wav->peak_bps == 1) {
> +                wav->peak_maxpos[c] = FFMAX(wav->peak_maxpos[c], *(int8_t*)(pkt->data + i));
> +                wav->peak_maxneg[c] = FFMIN(wav->peak_maxneg[c], *(int8_t*)(pkt->data + i));
> +            } else {
> +                wav->peak_maxpos[c] = FFMAX(wav->peak_maxpos[c], *(int16_t*)(pkt->data + i));
> +                wav->peak_maxneg[c] = FFMIN(wav->peak_maxneg[c], *(int16_t*)(pkt->data + i));

AV_RL16()

also how can the code be tested ?
which software reads/uses these peaks?

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

I have often repented speaking, but never of holding my tongue.
-- Xenocrates
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20140528/47616a40/attachment.asc>


More information about the ffmpeg-devel mailing list