[FFmpeg-devel] [PATCH v5] Add experimental support for vp9 in iso-bmff

Hendrik Leppkes h.leppkes at gmail.com
Wed Jun 15 01:34:31 CEST 2016


On Wed, Jun 15, 2016 at 12:05 AM, Kongqun Yang <yangkongqun at gmail.com> wrote:
> Implemented according to the draft specification
> "VP Codec ISO Media File Format Binding":
> http://www.webmproject.org/vp9/#draft-vp-codec-iso-media-file-format-binding
>
> '-strict -2' is required to use this feature.
>

Not sure I really like the vpc name, when I read vpc I don't think of
vp9, and the correlation to avc/hevc is also not really there, as
those codecs are actually called that. AVC and HEVC, the C isn't added
randomly.
If anything, it should have been vp9.c then, but oh well.

But anyway, if Ronald is ok with the name I won't complain.

> ---
>  libavformat/Makefile |   2 +-
>  libavformat/isom.c   |   3 +
>  libavformat/movenc.c |  26 +++++++++
>  libavformat/vpc.c    | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/vpc.h    |  44 ++++++++++++++
>  5 files changed, 233 insertions(+), 1 deletion(-)
>  create mode 100644 libavformat/vpc.c
>  create mode 100644 libavformat/vpc.h
>
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 6684ead..be8c261 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -276,7 +276,7 @@ OBJS-$(CONFIG_MM_DEMUXER)                += mm.o
>  OBJS-$(CONFIG_MMF_DEMUXER)               += mmf.o
>  OBJS-$(CONFIG_MMF_MUXER)                 += mmf.o rawenc.o
>  OBJS-$(CONFIG_MOV_DEMUXER)               += mov.o mov_chan.o replaygain.o
> -OBJS-$(CONFIG_MOV_MUXER)                 += movenc.o avc.o hevc.o \
> +OBJS-$(CONFIG_MOV_MUXER)                 += movenc.o avc.o hevc.o vpc.o \
>                                              movenchint.o mov_chan.o rtp.o \
>                                              movenccenc.o rawutils.o
>  OBJS-$(CONFIG_MP2_MUXER)                 += mp3enc.o rawenc.o id3v2enc.o
> diff --git a/libavformat/isom.c b/libavformat/isom.c
> index b1757e2..9a65268 100644
> --- a/libavformat/isom.c
> +++ b/libavformat/isom.c
> @@ -59,6 +59,7 @@ const AVCodecTag ff_mp4_obj_type[] = {
>      { AV_CODEC_ID_AC3         , 0xA5 },
>      { AV_CODEC_ID_EAC3        , 0xA6 },
>      { AV_CODEC_ID_DTS         , 0xA9 }, /* mp4ra.org */
> +    { AV_CODEC_ID_VP9         , 0xC0 }, /* non standard, update when there is a standard value */
>      { AV_CODEC_ID_TSCC2       , 0xD0 }, /* non standard, camtasia uses it */
>      { AV_CODEC_ID_VORBIS      , 0xDD }, /* non standard, gpac uses it */
>      { AV_CODEC_ID_DVD_SUBTITLE, 0xE0 }, /* non standard, see unsupported-embedded-subs-2.mp4 */
> @@ -179,6 +180,8 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
>      { AV_CODEC_ID_H264, MKTAG('a', 'i', 'v', 'x') }, /* XAVC 4:2:2 10bit */
>      { AV_CODEC_ID_H264, MKTAG('r', 'v', '6', '4') }, /* X-Com Radvision */
>
> +    { AV_CODEC_ID_VP9,  MKTAG('v', 'p', '0', '9') }, /* VP9 */
> +
>      { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', ' ') },
>      { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', '1') }, /* Apple MPEG-1 Camcorder */
>      { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'e', 'g') }, /* MPEG */
> diff --git a/libavformat/movenc.c b/libavformat/movenc.c
> index 2f00091..e4c51e8 100644
> --- a/libavformat/movenc.c
> +++ b/libavformat/movenc.c
> @@ -49,6 +49,7 @@
>  #include "hevc.h"
>  #include "rtpenc.h"
>  #include "mov_chan.h"
> +#include "vpc.h"
>
>  static const AVOption options[] = {
>      { "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
> @@ -1039,6 +1040,17 @@ static int mov_write_avcc_tag(AVIOContext *pb, MOVTrack *track)
>      return update_size(pb, pos);
>  }
>
> +static int mov_write_vpcc_tag(AVIOContext *pb, MOVTrack *track)
> +{
> +    int64_t pos = avio_tell(pb);
> +
> +    avio_wb32(pb, 0);
> +    ffio_wfourcc(pb, "vpcC");
> +    avio_wb32(pb, 0); /* version & flags */
> +    ff_isom_write_vpcc(pb, track->par);
> +    return update_size(pb, pos);
> +}
> +
>  static int mov_write_hvcc_tag(AVIOContext *pb, MOVTrack *track)
>  {
>      int64_t pos = avio_tell(pb);
> @@ -1143,6 +1155,7 @@ static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
>
>      if      (track->par->codec_id == AV_CODEC_ID_H264)      tag = MKTAG('a','v','c','1');
>      else if (track->par->codec_id == AV_CODEC_ID_HEVC)      tag = MKTAG('h','e','v','1');
> +    else if (track->par->codec_id == AV_CODEC_ID_VP9)       tag = MKTAG('v','p','0','9');
>      else if (track->par->codec_id == AV_CODEC_ID_AC3)       tag = MKTAG('a','c','-','3');
>      else if (track->par->codec_id == AV_CODEC_ID_EAC3)      tag = MKTAG('e','c','-','3');
>      else if (track->par->codec_id == AV_CODEC_ID_DIRAC)     tag = MKTAG('d','r','a','c');
> @@ -1758,6 +1771,8 @@ static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *tr
>          mov_write_avcc_tag(pb, track);
>          if (track->mode == MODE_IPOD)
>              mov_write_uuid_tag_ipod(pb);
> +    } else if (track->par->codec_id == AV_CODEC_ID_VP9) {
> +        mov_write_vpcc_tag(pb, track);
>      } else if (track->par->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0)
>          mov_write_dvc1_tag(pb, track);
>      else if (track->par->codec_id == AV_CODEC_ID_VP6F ||
> @@ -5369,6 +5384,17 @@ static int mov_write_header(AVFormatContext *s)
>                          pix_fmt == AV_PIX_FMT_MONOWHITE ||
>                          pix_fmt == AV_PIX_FMT_MONOBLACK;
>              }
> +            if (track->mode == MODE_MP4 &&
> +                track->par->codec_id == AV_CODEC_ID_VP9) {
> +                if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
> +                    av_log(s, AV_LOG_ERROR,
> +                           "VP9 in MP4 support is experimental, add "
> +                           "'-strict %d' if you want to use it.\n",
> +                           FF_COMPLIANCE_EXPERIMENTAL);
> +                    ret = AVERROR_EXPERIMENTAL;
> +                    goto error;
> +                }
> +            }
>          } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
>              track->timescale = st->codecpar->sample_rate;
>              if (!st->codecpar->frame_size && !av_get_bits_per_sample(st->codecpar->codec_id)) {
> diff --git a/libavformat/vpc.c b/libavformat/vpc.c
> new file mode 100644
> index 0000000..890c5a6
> --- /dev/null
> +++ b/libavformat/vpc.c
> @@ -0,0 +1,159 @@
> +/*
> + * Copyright (c) 2016 Google Inc.
> + * Copyright (c) 2016 KongQun Yang (kqyang at google.com)
> + *
> + * 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 "libavutil/pixdesc.h"
> +#include "libavutil/pixfmt.h"
> +#include "vpc.h"
> +
> +enum VpxColorSpace
> +{
> +    VPX_COLOR_SPACE_UNSPECIFIED = 0,
> +    VPX_COLOR_SPACE_BT601 = 1,
> +    VPX_COLOR_SPACE_BT709 = 2,
> +    VPX_COLOR_SPACE_SMPTE_170 = 3,
> +    VPX_COLOR_SPACE_SMPTE_240 = 4,
> +    VPX_COLOR_SPACE_BT2020_NCL = 5,
> +    VPX_COLOR_SPACE_BT2020_CL = 6,
> +    VPX_COLOR_SPACE_RGB = 7,
> +};
> +
> +static int get_vpx_color_space(enum AVColorSpace color_space)
> +{
> +    switch (color_space) {
> +    case AVCOL_SPC_RGB:
> +        return VPX_COLOR_SPACE_RGB;
> +    case AVCOL_SPC_BT709:
> +        return VPX_COLOR_SPACE_BT709;
> +    case AVCOL_SPC_UNSPECIFIED:
> +        return VPX_COLOR_SPACE_UNSPECIFIED;
> +    case AVCOL_SPC_BT470BG:
> +        return VPX_COLOR_SPACE_BT601;
> +    case AVCOL_SPC_SMPTE170M:
> +        return VPX_COLOR_SPACE_SMPTE_170;
> +    case AVCOL_SPC_SMPTE240M:
> +        return VPX_COLOR_SPACE_SMPTE_240;
> +    case AVCOL_SPC_BT2020_NCL:
> +        return VPX_COLOR_SPACE_BT2020_NCL;
> +    case AVCOL_SPC_BT2020_CL:
> +        return VPX_COLOR_SPACE_BT2020_CL;
> +    default:
> +        av_log(NULL, AV_LOG_ERROR, "Unsupported color space (%d)\n",
> +               color_space);
> +        return -1;
> +    }
> +}
> +
> +enum VPX_CHROMA_SUBSAMPLING
> +{
> +    VPX_SUBSAMPLING_420_VERTICAL = 0,
> +    VPX_SUBSAMPLING_420_COLLOCATED_WITH_LUMA = 1,
> +    VPX_SUBSAMPLING_422 = 2,
> +    VPX_SUBSAMPLING_444 = 3,
> +};
> +
> +static int get_vpx_chroma_subsampling(enum AVPixelFormat pixel_format,
> +                                      enum AVChromaLocation chroma_location)
> +{
> +    switch (pixel_format) {
> +    case AV_PIX_FMT_YUV420P:
> +    case AV_PIX_FMT_YUV420P10LE:
> +    case AV_PIX_FMT_YUV420P10BE:
> +    case AV_PIX_FMT_YUV420P12LE:
> +    case AV_PIX_FMT_YUV420P12BE:
> +        if (chroma_location == AVCHROMA_LOC_LEFT)
> +            return VPX_SUBSAMPLING_420_VERTICAL;
> +        // Otherwise assume collocated.
> +        return VPX_SUBSAMPLING_420_COLLOCATED_WITH_LUMA;
> +    case AV_PIX_FMT_YUV422P:
> +    case AV_PIX_FMT_YUV422P10LE:
> +    case AV_PIX_FMT_YUV422P10BE:
> +    case AV_PIX_FMT_YUV422P12LE:
> +    case AV_PIX_FMT_YUV422P12BE:
> +        return VPX_SUBSAMPLING_422;
> +    case AV_PIX_FMT_YUV444P:
> +    case AV_PIX_FMT_YUV444P10LE:
> +    case AV_PIX_FMT_YUV444P10BE:
> +    case AV_PIX_FMT_YUV444P12LE:
> +    case AV_PIX_FMT_YUV444P12BE:
> +        return VPX_SUBSAMPLING_444;
> +    default:
> +        av_log(NULL, AV_LOG_ERROR, "Unsupported pixel format (%d)\n",
> +               pixel_format);
> +        return -1;
> +    }
> +}

We have subsampling info in the pixel formt descriptors, it would seem
smarter to use thoe instead of hardcoding a long list of ever-changing
pixel formats.
You already use the descriptor  in get_bit_depth below, so try looking
at log2_chroma_w/log2_chroma_h, or using
av_pix_fmt_get_chroma_sub_sample (which just returns those two values
to you).

> +
> +static int get_bit_depth(enum AVPixelFormat pixel_format)
> +{
> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pixel_format);
> +    if (desc == NULL) {
> +        av_log(NULL, AV_LOG_ERROR, "Unsupported pixel format (%d)\n",
> +               pixel_format);
> +        return -1;
> +    }
> +    return desc->comp[0].depth;
> +}
> +
> +static int get_vpx_transfer_function(
> +    enum AVColorTransferCharacteristic transfer)
> +{
> +    return !!(transfer == AVCOL_TRC_SMPTEST2084);

This looks like an odd way to write it, if its ever extended to make
use of more values, you'll have to change it entirely.
But really just a style nit.

> +}
> +
> +static int get_vpx_video_full_range_flag(enum AVColorRange color_range)
> +{
> +    return !!(color_range == AVCOL_RANGE_JPEG);
> +}
> +
> +int ff_isom_write_vpcc(AVIOContext *pb, AVCodecParameters *par)
> +{
> +    int profile = par->profile;
> +    int level = par->level == FF_LEVEL_UNKNOWN ? 0 : par->level;
> +    int bit_depth = get_bit_depth(par->format);
> +    int vpx_color_space = get_vpx_color_space(par->color_space);
> +    int vpx_chroma_subsampling =
> +        get_vpx_chroma_subsampling(par->format, par->chroma_location);
> +    int vpx_transfer_function = get_vpx_transfer_function(par->color_trc);
> +    int vpx_video_full_range_flag =
> +        get_vpx_video_full_range_flag(par->color_range);
> +
> +    if (bit_depth < 0 || vpx_color_space < 0 || vpx_chroma_subsampling < 0)
> +        return AVERROR_INVALIDDATA;
> +
> +    if (profile == FF_PROFILE_UNKNOWN) {
> +        if (vpx_chroma_subsampling == VPX_SUBSAMPLING_420_VERTICAL ||
> +            vpx_chroma_subsampling == VPX_SUBSAMPLING_420_COLLOCATED_WITH_LUMA) {
> +            profile = (bit_depth == 8) ? FF_PROFILE_VP9_0 : FF_PROFILE_VP9_2;
> +        } else {
> +            profile = (bit_depth == 8) ? FF_PROFILE_VP9_1 : FF_PROFILE_VP9_3;
> +        }
> +    }
> +
> +    avio_w8(pb, profile);
> +    avio_w8(pb, level);
> +    avio_w8(pb, (bit_depth << 4) | vpx_color_space);
> +    avio_w8(pb, (vpx_chroma_subsampling << 4) | (vpx_transfer_function << 1) |
> +                    vpx_video_full_range_flag);
> +
> +    // vp9 does not have codec initialization data.
> +    avio_wb16(pb, 0);
> +    return 0;
> +}
> diff --git a/libavformat/vpc.h b/libavformat/vpc.h
> new file mode 100644
> index 0000000..6d609cc
> --- /dev/null
> +++ b/libavformat/vpc.h
> @@ -0,0 +1,44 @@
> +/*
> + * Copyright (c) 2016 Google Inc.
> + * Copyright (c) 2016 KongQun Yang (kqyang at google.com)
> + *
> + * 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
> + * internal header for VPx codec (de)muxer utilities.
> + */
> +
> +#ifndef AVFORMAT_VPC_H
> +#define AVFORMAT_VPC_H
> +
> +#include <stdint.h>
> +#include "avio.h"
> +#include "libavcodec/avcodec.h"
> +
> +/**
> + * Writes VP codec configuration to the provided AVIOContext.
> + *
> + * @param pb address of the AVIOContext where the vpcC shall be written.
> + * @param par address of the AVCodecParameters which contains codec information.
> + * @return >=0 in case of success, a negative value corresponding to an AVERROR
> + *         code in case of failure
> + */
> +int ff_isom_write_vpcc(AVIOContext *pb, AVCodecParameters *par);
> +
> +#endif /* AVFORMAT_VPC_H */
> --
> 2.8.0.rc3.226.g39d4020
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel


More information about the ffmpeg-devel mailing list