[FFmpeg-devel] [PATCH] Cineform HD Decoder:
Michael Niedermayer
michael at niedermayer.cc
Mon Jan 4 17:51:50 CET 2016
On Mon, Jan 04, 2016 at 12:04:08AM +0000, Kieran Kunhya wrote:
> Decodes YUV422P10 samples in AVI and MOV (Gopro Studio)
> Older files with more subbands, skips, Bayer not supported
> ---
> libavcodec/Makefile | 1 +
> libavcodec/allcodecs.c | 1 +
> libavcodec/avcodec.h | 1 +
> libavcodec/cfhd.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++++
> libavcodec/cfhd.h | 97 +++++++++
> libavcodec/cfhddata.c | 466 +++++++++++++++++++++++++++++++++++++++
> libavcodec/codec_desc.c | 6 +
> libavformat/riff.c | 1 +
ideally the avformat change should be a seperate commit
> 8 files changed, 1140 insertions(+)
> create mode 100644 libavcodec/cfhd.c
> create mode 100644 libavcodec/cfhd.h
> create mode 100644 libavcodec/cfhddata.c
>
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index a18ca5b..9db88d7 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -212,6 +212,7 @@ OBJS-$(CONFIG_CDGRAPHICS_DECODER) += cdgraphics.o
> OBJS-$(CONFIG_CDXL_DECODER) += cdxl.o
> OBJS-$(CONFIG_CINEPAK_DECODER) += cinepak.o
> OBJS-$(CONFIG_CINEPAK_ENCODER) += cinepakenc.o elbg.o
> +OBJS-$(CONFIG_CFHD_DECODER) += cfhd.o cfhddata.o
> OBJS-$(CONFIG_CLJR_DECODER) += cljrdec.o
> OBJS-$(CONFIG_CLJR_ENCODER) += cljrenc.o
> OBJS-$(CONFIG_CLLC_DECODER) += cllc.o canopus.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 4eeb6f3..ca6e4c2 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -147,6 +147,7 @@ void avcodec_register_all(void)
> REGISTER_DECODER(CAVS, cavs);
> REGISTER_DECODER(CDGRAPHICS, cdgraphics);
> REGISTER_DECODER(CDXL, cdxl);
> + REGISTER_DECODER(CFHD, cfhd);
> REGISTER_ENCDEC (CINEPAK, cinepak);
> REGISTER_ENCDEC (CLJR, cljr);
> REGISTER_DECODER(CLLC, cllc);
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index f365775..b958a6c 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -315,6 +315,7 @@ enum AVCodecID {
> AV_CODEC_ID_SMVJPEG,
> AV_CODEC_ID_APNG,
> AV_CODEC_ID_DAALA,
> + AV_CODEC_ID_CFHD,
>
> /* various PCM "codecs" */
> AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
> diff --git a/libavcodec/cfhd.c b/libavcodec/cfhd.c
> new file mode 100644
> index 0000000..0f1bcd3
> --- /dev/null
> +++ b/libavcodec/cfhd.c
> @@ -0,0 +1,567 @@
> +/*
> + * Copyright (c) 2015 Kieran Kunhya
> + *
> + * 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
> + */
> +
> +/**
> + * @file
> + * CFHD Video Decoder
> + */
> +
> +#include "avcodec.h"
> +#include "bswapdsp.h"
> +#include "internal.h"
> +#include "cfhd.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/buffer.h"
> +#include "libavutil/common.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/opt.h"
> +
> +static av_cold int cfhd_init_decoder(AVCodecContext *avctx)
> +{
> + CFHDContext *s = avctx->priv_data;
> +
> + ff_cfhd_init_vlcs(s);
> +
> + avctx->pix_fmt = AV_PIX_FMT_YUV422P10;
> + avctx->bits_per_raw_sample = 16;
> + s->avctx = avctx;
> +
> + return 0;
> +}
> +
> +static void init_plane_defaults(CFHDContext *s)
> +{
> + s->subband_num = 0;
> + s->level = 0;
> + s->subband_num_actual = 0;
> +}
> +
> +static void init_frame_defaults(CFHDContext *s)
> +{
> + s->bpc = 10;
> + s->channel_cnt = 4;
> + s->subband_cnt = 10;
> + s->channel_num = 0;
> + s->lowpass_precision = 16;
> + s->quantisation = 1;
> + s->wavelet_depth = 3;
> + s->pshift = 1;
> + s->codebook = 0;
> + init_plane_defaults(s);
> +}
> +
> +static inline int dequant_and_decompand(int level, int quantisation)
> +{
> + int abslevel = abs(level);
> + return (abslevel + ((768 * abslevel * abslevel * abslevel) / (255 * 255 * 255))) * FFSIGN(level) * quantisation;
> +}
> +
> +static inline void filter(int16_t *output, int out_stride, int16_t *low, int low_stride,
> + int16_t *high, int high_stride, int len)
> +{
> + int32_t tmp, tmp2;
> + int16_t tmp3, tmp4;
> +
> + /* these refer to the coefficients for the *next* iteration */
> + int16_t l_0, l_1, l_2, l_m1, l_m2;
> + int16_t h_0;
> +
> + int i;
> + for (i = 0; i < len; i++) {
> + if (i == 0) {
> + l_2 = low[3 * low_stride];
> + l_1 = low[2 * low_stride];
> + l_0 = low[1 * low_stride];
> + l_m1 = low[0 * low_stride];
> + l_m2 = 0;
> + h_0 = high[1 * high_stride];
> +
> + tmp = (11 * l_m1 - 4 * l_0 + l_1 + 4) >> 3;
> + tmp2 = (5 * l_m1 + 4 * l_0 - l_1 + 4) >> 3;
> +
> + output[(2 * i + 0) * out_stride] = (tmp + high[0 * high_stride]) >> 1;
> + output[(2 * i + 1) * out_stride] = (tmp2 - high[0 * high_stride]) >> 1;
> + } else if (i == len - 1) {
> + tmp = (5 * l_0 + 4 * l_m1 - l_m2 + 4) >> 3;
> + output[(2 * i + 0) * out_stride] = (tmp + h_0) >> 1;
> + tmp = (11 * l_0 - 4 * l_m1 + l_m2 + 4) >> 3;
> + output[(2 * i + 1) * out_stride] = (tmp - h_0) >> 1;
> + } else {
> + tmp = (l_m1 - l_1 + 4) >> 3;
> + tmp2 = (l_1 - l_m1 + 4) >> 3;
> +
> + tmp3 = (tmp + l_0 + h_0) >> 1;
> + tmp4 = (tmp2 + l_0 - h_0) >> 1;
> +
> + l_m2 = l_m1;
> + l_m1 = l_0;
> + l_0 = l_1;
> + l_1 = l_2;
> + l_2 = low[(i + 3) * low_stride];
> + h_0 = high[(i + 1) * high_stride];
> +
> + output[(2 * i + 0) * out_stride] = tmp3;
> + output[(2 * i + 1) * out_stride] = tmp4;
> + }
> + }
> +}
> +
> +static void horiz_filter(int16_t *output, int16_t *low, int16_t *high, int width)
> +{
> + filter(output, 1, low, 1, high, 1, width);
> +}
> +
> +static void vert_filter(int16_t *output, int out_stride, int16_t *low, int low_stride,
> + int16_t *high, int high_stride, int len)
> +{
> + filter(output, out_stride, low, low_stride, high, high_stride, len);
> +}
> +
> +static int cfhd_decode(AVCodecContext *avctx, void *data, int *got_frame,
> + AVPacket *avpkt)
> +{
> + CFHDContext *s = avctx->priv_data;
> + uint8_t *bs = avpkt->data;
> + int cnt = 0;
> + AVFrame *pic = data;
> + int ret = 0, i, j;
> + int16_t *plane[3];
> + int16_t *tmp[3];
> + int16_t *subband[3][10] = {{0}};
> + int16_t *l_h[3][8];
> +
> + avcodec_get_chroma_sub_sample(avctx->pix_fmt, &s->chroma_x_shift, &s->chroma_y_shift);
> +
> + for (i = 0; i < 3; i++) {
> + int width = i ? avctx->width >> s->chroma_x_shift : avctx->width;
> + int height = i ? avctx->height >> s->chroma_y_shift : avctx->height;
> + height = FFALIGN(height / 8, 2) * 8;
> + int stride = FFALIGN(width / 8, 8) * 8;
> + s->plane[i].width = width;
> + s->plane[i].height = height;
> + s->plane[i].stride = stride;
> +
> + int w8 = FFALIGN(s->plane[i].width / 8, 8);
> + int h8 = FFALIGN(s->plane[i].height / 8, 2);
> + int w4 = w8 * 2;
> + int h4 = h8 * 2;
> + int w2 = w4 * 2;
> + int h2 = h4 * 2;
> +
> + plane[i] = av_malloc(height * stride * sizeof(*plane[i]));
> + tmp[i] = av_malloc(height * stride * sizeof(*plane[i]));
> +
> + subband[i][0] = plane[i];
missing malloc failure checks
> + subband[i][1] = plane[i] + 2 * w8 * h8;
> + subband[i][2] = plane[i] + 1 * w8 * h8;
> + subband[i][3] = plane[i] + 3 * w8 * h8;
> + subband[i][4] = plane[i] + 2 * w4 * h4;
> + subband[i][5] = plane[i] + 1 * w4 * h4;
> + subband[i][6] = plane[i] + 3 * w4 * h4;
> + subband[i][7] = plane[i] + 2 * w2 * h2;
> + subband[i][8] = plane[i] + 1 * w2 * h2;
> + subband[i][9] = plane[i] + 3 * w2 * h2;
> +
> + l_h[i][0] = tmp[i];
> + l_h[i][1] = tmp[i] + 2 * w8 * h8;
> + //l_h[i][2] = ll2;
> + l_h[i][3] = tmp[i];
> + l_h[i][4] = tmp[i] + 2 * w4 * h4;
> + //l_h[i][5] = ll1;
> + l_h[i][6] = tmp[i];
> + l_h[i][7] = tmp[i] + 2 * w2 * h2;
> + }
> +
> + init_frame_defaults(s);
> +
> + if ((ret = ff_get_buffer(avctx, pic, 0)) < 0)
> + return ret;
> +
> + while (cnt < avpkt->size) {
> + int16_t tag = AV_RB16(&bs[cnt]);
> + int8_t tag8 = (int8_t)bs[cnt];
> + uint16_t abstag = abs(tag);
> + int8_t abs_tag8 = abs(tag8);
> + uint16_t data = AV_RB16(&bs[cnt + 2]);
> + if (abs_tag8 >= 0x60 && abs_tag8 <= 0x6f) {
> + av_log(avctx, AV_LOG_DEBUG, "large len %x \n", AV_RB24(&bs[cnt + 1]));
> + } else if (tag == 20) {
> + av_log(avctx, AV_LOG_DEBUG, "Width %u %x \n", data, cnt);
> + avctx->width = data;
> + } else if (tag == 21) {
> + av_log(avctx, AV_LOG_DEBUG, "Height %u %x \n", data, cnt);
> + avctx->height = data;
> + } else if (tag == 101) {
> + av_log(avctx, AV_LOG_DEBUG, "Bits per component: %u \n", data);
> + s->bpc = data;
the debug av_logs could be put under some
if (avctx->debug & FF_DEBUG_*)
that way they can be enabled when needed and dont clutter the output
when not
> + } else if (tag == 12) {
> + av_log(avctx, AV_LOG_DEBUG, "Channel Count: %u \n", data);
> + s->channel_cnt = data;
> + if (data != 3) {
> + av_log(avctx, AV_LOG_ERROR, "Channel Count of %u is unsupported\n", data);
> + ret = AVERROR_PATCHWELCOME;
> + break;
> + }
> + } else if (tag == 14) {
> + av_log(avctx, AV_LOG_DEBUG, "Subband Count: %u \n", data);
> + if (data != 10) {
> + av_log(avctx, AV_LOG_ERROR, "Subband Count of %u is unsupported\n", data);
> + ret = AVERROR_PATCHWELCOME;
> + break;
> + }
> + }
> + else if (tag == 62) {
> + s->channel_num = data;
> + av_log(avctx, AV_LOG_DEBUG, "Channel number %u \n", data);
> + init_plane_defaults(s);
> + } else if (tag == 48) {
> + if (s->subband_num != 0 && data == 1) // hack
> + s->level++;
> + av_log(avctx, AV_LOG_DEBUG, "Subband number %u \n", data);
> + s->subband_num = data;
> + } else if (tag == 51) {
> + av_log(avctx, AV_LOG_DEBUG, "Subband number actual %u \n", data);
> + s->subband_num_actual = data;
> + } else if (tag == 35)
> + av_log(avctx, AV_LOG_DEBUG, "Lowpass precision bits: %u \n", data);
> + else if (tag == 53) {
> + s->quantisation = data;
> + av_log(avctx, AV_LOG_DEBUG, "Quantisation: %u \n", data);
> + } else if (tag == 109) {
> + s->prescale_shift[0] = (data >> 0) & 0x7;
> + s->prescale_shift[1] = (data >> 3) & 0x7;
> + s->prescale_shift[2] = (data >> 6) & 0x7;
> + av_log(avctx, AV_LOG_DEBUG, "Prescale shift (VC-5): %x \n", data);
> + } else if (tag == 27) {
> + s->plane[s->channel_num].band[0][0].width = data;
> + s->plane[s->channel_num].band[0][0].stride = data;
> + av_log(avctx, AV_LOG_DEBUG, "Lowpass width %u \n", data);
> + } else if (tag == 28) {
> + s->plane[s->channel_num].band[0][0].height = data;
> + av_log(avctx, AV_LOG_DEBUG, "Lowpass height %u \n", data);
> + } else if (tag == 1)
> + av_log(avctx, AV_LOG_DEBUG, "Sample type? %u \n", data);
> + else if (tag == 10) {
> + if (data != 0) {
> + av_log(avctx, AV_LOG_ERROR, "Transform type of %u is unsupported\n", data);
> + ret = AVERROR_PATCHWELCOME;
> + break;
> + }
> + av_log(avctx, AV_LOG_DEBUG, "Transform-type? %u \n", data);
> + }
> + else if (abstag >= 0x4000 && abstag <= 0x40ff) {
> + av_log(avctx, AV_LOG_DEBUG, "Small chunk length %u %s \n", data * 4, tag < 0 ? "optional" : "required");
> + cnt += data * 4;
> + } else if (tag == 23) {
> + av_log(avctx, AV_LOG_DEBUG, "Skip frame \n");
> + av_log(avctx, AV_LOG_ERROR, "Skip frame not supported \n");
> + ret = AVERROR_PATCHWELCOME;
> + break;
> + } else if (tag == 2) {
> + av_log(avctx, AV_LOG_DEBUG, "tag=2 header - skipping %i tag/value pairs \n", data);
> + for (i = 0; i < data + 1; i++) {
> + av_log(avctx, AV_LOG_DEBUG, "Tag/Value = %x %x \n", AV_RB16(&bs[cnt]), AV_RB16(&bs[cnt + 2]));
> + cnt += 4;
> + }
> + } else if (tag == 41) {
> + s->plane[s->channel_num].band[s->level][s->subband_num].width = data;
> + s->plane[s->channel_num].band[s->level][s->subband_num].stride = FFALIGN(data, 8);
> + av_log(avctx, AV_LOG_DEBUG, "Highpass width %i channel %i level %i subband %i \n", data, s->channel_num, s->level, s->subband_num);
> + } else if (tag == 42) {
> + s->plane[s->channel_num].band[s->level][s->subband_num].height = data;
> + av_log(avctx, AV_LOG_DEBUG, "Highpass height %i \n", data);
> + } else if (tag == 49) {
> + s->plane[s->channel_num].band[s->level][s->subband_num].width = data;
> + s->plane[s->channel_num].band[s->level][s->subband_num].stride = FFALIGN(data, 8);
> + av_log(avctx, AV_LOG_DEBUG, "Highpass width2 %i \n", data);
> + } else if (tag == 50) {
> + s->plane[s->channel_num].band[s->level][s->subband_num].height = data;
> + av_log(avctx, AV_LOG_DEBUG, "Highpass height2 %i \n", data);
> + } else if (tag == 71) {
> + s->codebook = data;
> + av_log(avctx, AV_LOG_DEBUG, "Codebook %i \n", s->codebook);
> + } else if (tag == 72) {
> + s->codebook = data;
> + av_log(avctx, AV_LOG_DEBUG, "Other codebook? %i \n", s->codebook);
> + } else
> + av_log(avctx, AV_LOG_DEBUG, "Unknown tag %i data %x \n", tag, data);
> + cnt += 4;
> +
> + int16_t *coeff_data = subband[s->channel_num][s->subband_num_actual];
this produces:
"warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]"
> +
> + /* Lowpass coefficients */
> + if (tag == 4 && data == 0xf0f) {
> + int lowpass_height = s->plane[s->channel_num].band[0][0].height;
> + int lowpass_width = s->plane[s->channel_num].band[0][0].width;
> + uint16_t coeffs = 0;
> + av_log(avctx, AV_LOG_DEBUG, "Start of lowpass coeffs component %u \n", s->channel_num);
> + for (i = 0; i < lowpass_height; i++) {
> + for (j = 0; j < lowpass_width; j++) {
> + coeff_data[j] = AV_RB16(&bs[cnt]);
> +
> + coeffs++;
> + cnt += 2;
> + }
> + coeff_data += lowpass_width;
> + }
have the lowpass_height and lowpass_width been checked somewhere
so this wont write ove the array end ?
also the bs array could be too small causin a overread
[...]
> diff --git a/libavcodec/cfhd.h b/libavcodec/cfhd.h
> new file mode 100644
> index 0000000..1a5830e
> --- /dev/null
> +++ b/libavcodec/cfhd.h
> @@ -0,0 +1,97 @@
> +/*
> + * Copyright (c) 2015 Kieran Kunhya
> + *
> + * 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 "get_bits.h"
> +
> +#define VLC_BITS 9
> +#define NB_VLC_TABLE_9 (71+3)
> +#define NB_VLC_TABLE_18 (263+1)
> +
> +typedef struct CFHD_RL_VLC_ELEM {
> + int16_t level;
> + int8_t len;
> + uint16_t run;
> +} CFHD_RL_VLC_ELEM;
> +
> +#define DWT_LEVELS 3
> +
> +#define CALC_PADDING(size, depth) \
> + (((size + (1 << depth) - 1) >> depth) << depth)
size ande depth should be protected by () so that when passing
expressions the evaluation order works out
[...]
> +av_cold int ff_cfhd_init_vlcs(CFHDContext *s)
> +{
> + int i, j;
> + uint32_t new_cfhd_vlc_bits[NB_VLC_TABLE_18 * 2];
> + uint8_t new_cfhd_vlc_len[NB_VLC_TABLE_18 * 2];
> + uint16_t new_cfhd_vlc_run[NB_VLC_TABLE_18 * 2];
> + int16_t new_cfhd_vlc_level[NB_VLC_TABLE_18 * 2];
> +
> + /** Similar to dv.c, generate signed VLC tables **/
> +
> + /* Table 9 */
> + for (i = 0, j = 0; i < NB_VLC_TABLE_9; i++, j++) {
> + new_cfhd_vlc_bits[j] = table_9_vlc_bits[i];
> + new_cfhd_vlc_len[j] = table_9_vlc_len[i];
> + new_cfhd_vlc_run[j] = table_9_vlc_run[i];
> + new_cfhd_vlc_level[j] = table_9_vlc_level[i];
> +
> + /* Don't include the zero level nor escape bits */
> + if (table_9_vlc_level[i] &&
> + new_cfhd_vlc_bits[j] != table_9_vlc_bits[NB_VLC_TABLE_9-1]) {
> + new_cfhd_vlc_bits[j] <<= 1;
> + new_cfhd_vlc_len[j]++;
> + j++;
> + new_cfhd_vlc_bits[j] = (table_9_vlc_bits[i] << 1) | 1;
> + new_cfhd_vlc_len[j] = table_9_vlc_len[i] + 1;
> + new_cfhd_vlc_run[j] = table_9_vlc_run[i];
> + new_cfhd_vlc_level[j] = -table_9_vlc_level[i];
> + }
> + }
> +
> + init_vlc(&s->vlc_9, VLC_BITS, j, new_cfhd_vlc_len,
> + 1, 1, new_cfhd_vlc_bits, 4, 4, 0);
> + for (i = 0; i < s->vlc_9.table_size; i++) {
> + int code = s->vlc_9.table[i][0];
> + int len = s->vlc_9.table[i][1];
> + int level, run;
> +
> + if (len < 0) { // more bits needed
> + run = 0;
> + level = code;
> + } else {
> + run = new_cfhd_vlc_run[code];
> + level = new_cfhd_vlc_level[code];
> + }
> + s->table_9_rl_vlc[i].len = len;
> + s->table_9_rl_vlc[i].level = level;
> + s->table_9_rl_vlc[i].run = run;
> + }
> +
> + /* Table 18 */
> + for (i = 0, j = 0; i < NB_VLC_TABLE_18; i++, j++) {
> + new_cfhd_vlc_bits[j] = table_18_vlc_bits[i];
> + new_cfhd_vlc_len[j] = table_18_vlc_len[i];
> + new_cfhd_vlc_run[j] = table_18_vlc_run[i];
> + new_cfhd_vlc_level[j] = table_18_vlc_level[i];
> +
> + /* Don't include the zero level nor escape bits */
> + if (table_18_vlc_level[i] &&
> + new_cfhd_vlc_bits[j] != table_18_vlc_bits[NB_VLC_TABLE_18-1]) {
> + new_cfhd_vlc_bits[j] <<= 1;
> + new_cfhd_vlc_len[j]++;
> + j++;
> + new_cfhd_vlc_bits[j] = (table_18_vlc_bits[i] << 1) | 1;
> + new_cfhd_vlc_len[j] = table_18_vlc_len[i] + 1;
> + new_cfhd_vlc_run[j] = table_18_vlc_run[i];
> + new_cfhd_vlc_level[j] = -table_18_vlc_level[i];
> + }
> + }
> +
> + init_vlc(&s->vlc_18, VLC_BITS, j, new_cfhd_vlc_len,
> + 1, 1, new_cfhd_vlc_bits, 4, 4, 0);
> + assert(s->vlc_18.table_size == 4572);
should probably be some av_assert
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
Avoid a single point of failure, be that a person or equipment.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20160104/710430b2/attachment.sig>
More information about the ffmpeg-devel
mailing list