diff mbox series

[FFmpeg-devel] avformat/vpcc: parse bitstream data to get profile and bitdepth

Message ID 20221111171733.4484-1-jamrial@gmail.com
State New
Headers show
Series [FFmpeg-devel] avformat/vpcc: parse bitstream data to get profile and bitdepth | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

James Almer Nov. 11, 2022, 5:17 p.m. UTC
Profile can be derived from values codecpar pixel format only with software
formats. For hardware formats, we're forced to parse a frame header to get
the required information.

Signed-off-by: James Almer <jamrial@gmail.com>
---
 libavformat/dashenc.c |  2 +-
 libavformat/movenc.c  |  4 +++-
 libavformat/vpcc.c    | 44 +++++++++++++++++++++++++++++++++++++++++--
 libavformat/vpcc.h    |  5 +++++
 4 files changed, 51 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index a0919f6f2d..9c1bcad9e3 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -338,7 +338,7 @@  static int init_segment_types(AVFormatContext *s)
 static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par,
                               AVRational *frame_rate, char *str, int size) {
     VPCC vpcc;
-    int ret = ff_isom_get_vpcc_features(s, par, frame_rate, &vpcc);
+    int ret = ff_isom_get_vpcc_features(s, par, NULL, 0, frame_rate, &vpcc);
     if (ret == 0) {
         av_strlcatf(str, size, "vp09.%02d.%02d.%02d",
                     vpcc.profile, vpcc.level, vpcc.bitdepth);
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 754f95912a..064b541972 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -1375,7 +1375,8 @@  static int mov_write_vpcc_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
     ffio_wfourcc(pb, "vpcC");
     avio_w8(pb, 1); /* version */
     avio_wb24(pb, 0); /* flags */
-    ff_isom_write_vpcc(s, pb, track->par);
+    ff_isom_write_vpcc(s, pb, track->vos_data, track->vos_len, track->par);
+
     return update_size(pb, pos);
 }
 
@@ -6033,6 +6034,7 @@  int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
     if ((par->codec_id == AV_CODEC_ID_DNXHD ||
          par->codec_id == AV_CODEC_ID_H264 ||
          par->codec_id == AV_CODEC_ID_HEVC ||
+         par->codec_id == AV_CODEC_ID_VP9 ||
          par->codec_id == AV_CODEC_ID_TRUEHD) && !trk->vos_len &&
          !TAG_IS_AVCI(trk->tag)) {
         /* copy frame to create needed atoms */
diff --git a/libavformat/vpcc.c b/libavformat/vpcc.c
index 8b4f913b5d..1fbde74fbb 100644
--- a/libavformat/vpcc.c
+++ b/libavformat/vpcc.c
@@ -22,8 +22,11 @@ 
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
 #include "libavcodec/avcodec.h"
+#include "libavcodec/get_bits.h"
 #include "vpcc.h"
 
+#define VP9_SYNCCODE 0x498342
+
 enum VPX_CHROMA_SUBSAMPLING
 {
     VPX_SUBSAMPLING_420_VERTICAL = 0,
@@ -114,7 +117,30 @@  static int get_vp9_level(AVCodecParameters *par, AVRational *frame_rate) {
     }
 }
 
+static void parse_bitstream(GetBitContext *gb, int *profile, int *bit_depth) {
+    int keyframe;
+
+    if (get_bits(gb, 2) != 0x2) // frame marker
+        return;
+    *profile  = get_bits1(gb);
+    *profile |= get_bits1(gb) << 1;
+    if (*profile == 3)
+        *profile += get_bits1(gb);
+
+    if (get_bits1(gb))
+        return;
+
+    keyframe = !get_bits1(gb);
+    get_bits(gb, 2);
+
+    if (!keyframe || get_bits(gb, 24) != VP9_SYNCCODE)
+        return;
+
+    *bit_depth = *profile <= 1 ? 8 : 10 + get_bits1(gb) * 2;
+}
+
 int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par,
+                              const uint8_t *data, int len,
                               AVRational *frame_rate, VPCC *vpcc)
 {
     int profile = par->profile;
@@ -129,7 +155,17 @@  int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par,
     if (bit_depth < 0 || vpx_chroma_subsampling < 0)
         return AVERROR_INVALIDDATA;
 
-    if (profile == FF_PROFILE_UNKNOWN) {
+    if (len && (profile == FF_PROFILE_UNKNOWN || !bit_depth)) {
+        GetBitContext gb;
+
+        int ret = init_get_bits8(&gb, data, len);
+        if (ret < 0)
+            return AVERROR_INVALIDDATA;
+
+        parse_bitstream(&gb, &profile, &bit_depth);
+    }
+
+    if (profile == FF_PROFILE_UNKNOWN && bit_depth) {
         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;
@@ -138,6 +174,9 @@  int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par,
         }
     }
 
+    if (profile == FF_PROFILE_UNKNOWN || !bit_depth)
+        return AVERROR_INVALIDDATA;
+
     vpcc->profile            = profile;
     vpcc->level              = level;
     vpcc->bitdepth           = bit_depth;
@@ -148,12 +187,13 @@  int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par,
 }
 
 int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb,
+                       const uint8_t *data, int len,
                        AVCodecParameters *par)
 {
     VPCC vpcc;
     int ret;
 
-    ret = ff_isom_get_vpcc_features(s, par, NULL, &vpcc);
+    ret = ff_isom_get_vpcc_features(s, par, data, len, NULL, &vpcc);
     if (ret < 0)
         return ret;
 
diff --git a/libavformat/vpcc.h b/libavformat/vpcc.h
index f05b2f58b3..bad38c5615 100644
--- a/libavformat/vpcc.h
+++ b/libavformat/vpcc.h
@@ -45,14 +45,19 @@  typedef struct VPCC {
  *
  * @param s address of the AVFormatContext for the logging context.
  * @param pb address of the AVIOContext where the vpcC shall be written.
+ * @param data address of a data array which contains coded bitstream data from
+ *             which codec information can be extracted. May be NULL.
+ * @param len length of the data array.
  * @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(AVFormatContext *s, AVIOContext *pb,
+                       const uint8_t *data, int len,
                        AVCodecParameters *par);
 
 int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par,
+                              const uint8_t *data, int len,
                               AVRational *frame_rate, VPCC *vpcc);
 
 #endif /* AVFORMAT_VPCC_H */