Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avformat: validate dovi config in muxers #484

Merged
merged 6 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions builder/images/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ RUN \
git config --global advice.detachedHead false

ENV CARGO_HOME="/opt/cargo" RUSTUP_HOME="/opt/rustup" PATH="/opt/cargo/bin:${PATH}"
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y --no-modify-path && \
cargo install cargo-c && rm -rf "${CARGO_HOME}"/registry "${CARGO_HOME}"/git
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y --no-modify-path
RUN cargo install --locked cargo-c && rm -rf "${CARGO_HOME}"/registry "${CARGO_HOME}"/git

RUN --mount=src=.,dst=/input \
for s in /input/*.sh; do cp $s /usr/bin/$(echo $s | sed -e 's|.*/||' -e 's/\.sh$//'); done
149 changes: 140 additions & 9 deletions debian/patches/0031-pass-dovi-sidedata-to-hlsenc-and-mpegtsenc.patch
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ Index: FFmpeg/libavformat/hlsenc.c
int remaining_options;
- int i, ret;
+ int i, j, ret;

ret = avformat_alloc_output_context2(&vs->avf, vs->oformat, NULL, NULL);
if (ret < 0)
@@ -896,6 +896,20 @@ static int hls_mux_init(AVFormatContext
st->codecpar->codec_tag = 0;
}

+ // copy side data
+ for (j = 0; j < vs->streams[i]->codecpar->nb_coded_side_data; j++) {
+ const AVPacketSideData *sd_src = &vs->streams[i]->codecpar->coded_side_data[j];
Expand All @@ -36,15 +36,26 @@ Index: FFmpeg/libavformat/movenc.c
===================================================================
--- FFmpeg.orig/libavformat/movenc.c
+++ FFmpeg/libavformat/movenc.c
@@ -8124,6 +8124,7 @@ static const AVCodecTag codec_mp4_tags[]
@@ -2538,8 +2538,9 @@ static int mov_write_video_tag(AVFormatC
mov_write_st3d_tag(s, pb, (AVStereo3D*)stereo_3d->data);
if (spherical_mapping)
mov_write_sv3d_tag(mov->fc, pb, (AVSphericalMapping*)spherical_mapping->data);
- if (dovi)
+ if (dovi && !ff_isom_validate_dovi_config((AVDOVIDecoderConfigurationRecord *)dovi->data, track->par, track->tag)) {
mov_write_dvcc_dvvc_tag(s, pb, (AVDOVIDecoderConfigurationRecord *)dovi->data);
+ }
}

if (track->par->sample_aspect_ratio.den && track->par->sample_aspect_ratio.num) {
@@ -8128,6 +8129,7 @@ static const AVCodecTag codec_mp4_tags[]
{ AV_CODEC_ID_HEVC, MKTAG('h', 'e', 'v', '1') },
{ AV_CODEC_ID_HEVC, MKTAG('h', 'v', 'c', '1') },
{ AV_CODEC_ID_HEVC, MKTAG('d', 'v', 'h', '1') },
+ { AV_CODEC_ID_HEVC, MKTAG('d', 'v', 'h', 'e') },
{ AV_CODEC_ID_VVC, MKTAG('v', 'v', 'c', '1') },
{ AV_CODEC_ID_VVC, MKTAG('v', 'v', 'i', '1') },
{ AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') },
@@ -8137,6 +8138,7 @@ static const AVCodecTag codec_mp4_tags[]
@@ -8141,6 +8143,7 @@ static const AVCodecTag codec_mp4_tags[]
{ AV_CODEC_ID_TSCC2, MKTAG('m', 'p', '4', 'v') },
{ AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') },
{ AV_CODEC_ID_AV1, MKTAG('a', 'v', '0', '1') },
Expand All @@ -64,10 +75,18 @@ Index: FFmpeg/libavformat/mpegtsenc.c
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
@@ -350,6 +351,52 @@ static void put_registration_descriptor(
@@ -40,6 +41,7 @@
#include "internal.h"
#include "mpegts.h"
#include "mux.h"
+#include "dovi_isom.h"

#define PCR_TIME_BASE 27000000

@@ -350,6 +352,52 @@ static void put_registration_descriptor(
*q_ptr = q;
}

+static int put_dovi_descriptor(AVFormatContext *s, uint8_t **q_ptr,
+ const AVDOVIDecoderConfigurationRecord *dovi)
+{
Expand Down Expand Up @@ -117,16 +136,18 @@ Index: FFmpeg/libavformat/mpegtsenc.c
static int get_dvb_stream_type(AVFormatContext *s, AVStream *st)
{
MpegTSWrite *ts = s->priv_data;
@@ -803,7 +850,16 @@ static int mpegts_write_pmt(AVFormatCont
@@ -803,7 +851,18 @@ static int mpegts_write_pmt(AVFormatCont
} else if (stream_type == STREAM_TYPE_VIDEO_VC1) {
put_registration_descriptor(&q, MKTAG('V', 'C', '-', '1'));
} else if (stream_type == STREAM_TYPE_VIDEO_HEVC && s->strict_std_compliance <= FF_COMPLIANCE_NORMAL) {
- put_registration_descriptor(&q, MKTAG('H', 'E', 'V', 'C'));
+ const AVPacketSideData *sd = av_packet_side_data_get(st->codecpar->coded_side_data,
+ st->codecpar->nb_coded_side_data, AV_PKT_DATA_DOVI_CONF);
+ const AVDOVIDecoderConfigurationRecord *dovi = sd ? (const AVDOVIDecoderConfigurationRecord *)sd->data : NULL;
+
+ if (dovi && dovi->bl_present_flag && s->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {
+ if (dovi &&
+ dovi->bl_present_flag &&
+ s->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL &&
+ !ff_isom_validate_dovi_config(dovi, st->codecpar, MKTAG('d', 'v', 'h', '1'))) { // always assume tag is valid
+ if (put_dovi_descriptor(s, &q, dovi) < 0)
+ break;
+ } else {
Expand All @@ -135,3 +156,113 @@ Index: FFmpeg/libavformat/mpegtsenc.c
} else if (stream_type == STREAM_TYPE_VIDEO_CAVS || stream_type == STREAM_TYPE_VIDEO_AVS2 ||
stream_type == STREAM_TYPE_VIDEO_AVS3) {
put_registration_descriptor(&q, MKTAG('A', 'V', 'S', 'V'));
Index: FFmpeg/libavformat/dovi_isom.c
===================================================================
--- FFmpeg.orig/libavformat/dovi_isom.c
+++ FFmpeg/libavformat/dovi_isom.c
@@ -116,3 +116,74 @@ void ff_isom_put_dvcc_dvvc(void *logctx,
dovi->bl_present_flag,
dovi->dv_bl_signal_compatibility_id);
}
+
+int ff_isom_validate_dovi_config(const AVDOVIDecoderConfigurationRecord *dovi,
+ const AVCodecParameters *codec_par, int codec_tag)
+{
+ if (!dovi || !codec_par)
+ return AVERROR(ENOMEM);
+
+ switch (dovi->dv_profile) {
+ case 4:
+ case 5:
+ case 7:
+ case 8:
+ case 20:
+ if (codec_par->codec_id != AV_CODEC_ID_HEVC)
+ return AVERROR(EINVAL);
+ break;
+ case 9:
+ if (codec_par->codec_id != AV_CODEC_ID_H264)
+ return AVERROR(EINVAL);
+ break;
+ case 10:
+ if (codec_par->codec_id != AV_CODEC_ID_AV1)
+ return AVERROR(EINVAL);
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+
+ switch (dovi->dv_bl_signal_compatibility_id) {
+ case 0:
+ // Although the IPT-PQ-C2 Dolby Vision uses is always full range, some videos tag that wrong in the container
+ // To allow stream copy for such videos, don't check for the color range
+ if (codec_par->format != AV_PIX_FMT_YUV420P10 ||
+ (codec_tag && !(codec_tag == MKTAG('d', 'v', 'h', '1') ||
+ codec_tag == MKTAG('d', 'v', 'h', 'e') ||
+ codec_tag == MKTAG('d', 'a', 'v', '1')))) {
+ return AVERROR(EINVAL);
+ }
+ break;
+ case 1: // HDR10
+ case 6:
+ if (codec_par->color_trc != AVCOL_TRC_SMPTE2084 ||
+ codec_par->color_primaries != AVCOL_PRI_BT2020 ||
+ codec_par->color_space != AVCOL_SPC_BT2020_NCL ||
+ codec_par->color_range != AVCOL_RANGE_MPEG ||
+ codec_par->format != AV_PIX_FMT_YUV420P10) {
+ return AVERROR(EINVAL);
+ }
+ break;
+ case 2: // SDR
+ // Don't check range or color info for SDR base layer as a lot of them will set to unspecified
+ // And a lot of players assumes unspecified as BT709 in tv range
+ if (codec_par->format != AV_PIX_FMT_YUV420P)
+ return AVERROR(EINVAL);
+ break;
+ case 4: // HLG
+ if (codec_par->color_trc != AVCOL_TRC_ARIB_STD_B67 ||
+ codec_par->color_primaries != AVCOL_PRI_BT2020 ||
+ codec_par->color_space != AVCOL_SPC_BT2020_NCL ||
+ codec_par->color_range != AVCOL_RANGE_MPEG ||
+ codec_par->format != AV_PIX_FMT_YUV420P10) {
+ return AVERROR(EINVAL);
+ }
+ break;
+ default:
+ // others are reserved value, don't check
+ break;
+ }
+
+ return 0;
+}
Index: FFmpeg/libavformat/dovi_isom.h
===================================================================
--- FFmpeg.orig/libavformat/dovi_isom.h
+++ FFmpeg/libavformat/dovi_isom.h
@@ -33,4 +33,12 @@ int ff_isom_parse_dvcc_dvvc(void *logctx
void ff_isom_put_dvcc_dvvc(void *logctx, uint8_t out[ISOM_DVCC_DVVC_SIZE],
const AVDOVIDecoderConfigurationRecord *dovi);

+/*
+ * Check if the AVDOVIDecoderConfigurationRecord is spec-compliant for current codec parameters
+ * Used by muxers to determine if the configuration record should be copied into the container
+ * Returns 0 when the AVDOVIDecoderConfigurationRecord is safe to copy, otherwise return an AVERROR
+ */
+int ff_isom_validate_dovi_config(const AVDOVIDecoderConfigurationRecord *dovi,
+ const AVCodecParameters *codec_par, int codec_tag);
+
#endif /* AVFORMAT_DOVI_ISOM_H */
Index: FFmpeg/libavformat/matroskaenc.c
===================================================================
--- FFmpeg.orig/libavformat/matroskaenc.c
+++ FFmpeg/libavformat/matroskaenc.c
@@ -1718,7 +1718,8 @@ static void mkv_write_blockadditionmappi
return;

dovi = (const AVDOVIDecoderConfigurationRecord *)sd->data;
- if (dovi->dv_profile <= 10) {
+ if (dovi->dv_profile <= 10 &&
+ !ff_isom_validate_dovi_config(dovi, par, (int)par->codec_tag)) {
ebml_master mapping;
uint8_t buf[ISOM_DVCC_DVVC_SIZE];
uint32_t type;
Loading