[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