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

KongQun Yang kqyang at google.com
Mon Jun 13 23:50:10 CEST 2016


Patchset v2 addresses review comments. Thanks everyone for the review.

Here is a summary of the changes in patchset v2:

1.  Add "-strict -2" requirement, since the feature is still being reviewed
and may subject to change.
2. Rename vpx.c/vpx.h to vpc.c/vpc.h to align with avc and hevc.
3. Use av_pix_fmt_desc_get(pixel_format)->comp[0].depth suggested by Ronald
to get bit depth
    As for the comments on color space from James, the structure is
specific to "vp in mp4" specification, so we cannot reuse the code from
libavcodec or libvpx.


-- KongQun Yang (KQ)

On Mon, Jun 13, 2016 at 2:26 PM, 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
>
> Change-Id: Iaa7ddf5524b17e8d79cd1923b26f096deeee6e91
> ---
>  libavformat/Makefile |   2 +-
>  libavformat/isom.c   |   3 +
>  libavformat/movenc.c |  26 +++++++++
>  libavformat/vpc.c    | 151
> +++++++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/vpc.h    |  44 +++++++++++++++
>  5 files changed, 225 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..551f0e6 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..d28c4f5
> --- /dev/null
> +++ b/libavformat/vpc.c
> @@ -0,0 +1,151 @@
> +/*
> + * 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_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:
> +            return VPX_COLOR_SPACE_UNSPECIFIED;
> +    }
> +}
> +
> +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, "Unknown pixel format.");
> +            return -1;
> +    }
> +}
> +
> +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, "Unknown pixel format.");
> +        return -1;
> +    }
> +    return desc->comp[0].depth;
> +}
> +
> +static int get_vpx_transfer_function(
> +    enum AVColorTransferCharacteristic transfer)
> +{
> +  return (transfer == AVCOL_TRC_SMPTEST2084) ? 1 : 0;
> +}
> +
> +static int get_vpx_video_full_range_flag(enum AVColorRange color_range)
> +{
> +    return (color_range == AVCOL_RANGE_JPEG) ? 1 : 0;
> +}
> +
> +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_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
>
>


More information about the ffmpeg-devel mailing list