* [FFmpeg-devel] [PATCH v3 01/13] avcodec/dovi_rpu: store entire config record
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 02/13] avcodec/dovi_rpu: properly replace context header Niklas Haas
` (12 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
And make it public.
For encoding, users may also be interested in the configured level and
compatibility ID. So generalize the dv_profile field and just expose the
whole configuration record.
This makes the already rather reductive ff_dovi_update_cfg() function
almost wholly redundant, since users can just directly assign
DOVIContext.cfg.
---
libavcodec/av1dec.c | 6 +++---
libavcodec/dovi_rpu.c | 16 ++++------------
libavcodec/dovi_rpu.h | 21 ++++++++++++---------
libavcodec/hevcdec.c | 13 ++++++-------
libavcodec/libdav1d.c | 6 +++---
5 files changed, 28 insertions(+), 34 deletions(-)
diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c
index 824725c031..4c1405df77 100644
--- a/libavcodec/av1dec.c
+++ b/libavcodec/av1dec.c
@@ -888,10 +888,10 @@ static av_cold int av1_decode_init(AVCodecContext *avctx)
}
s->dovi.logctx = avctx;
- s->dovi.dv_profile = 10; // default for AV1
+ s->dovi.cfg.dv_profile = 10; // default for AV1
sd = ff_get_coded_side_data(avctx, AV_PKT_DATA_DOVI_CONF);
- if (sd && sd->size > 0)
- ff_dovi_update_cfg(&s->dovi, (AVDOVIDecoderConfigurationRecord *) sd->data);
+ if (sd && sd->size >= sizeof(s->dovi.cfg))
+ s->dovi.cfg = *(AVDOVIDecoderConfigurationRecord *) sd->data;
return ret;
}
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index 9f7a6b0066..d95c7e03af 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -64,7 +64,7 @@ void ff_dovi_ctx_flush(DOVIContext *s)
*s = (DOVIContext) {
.logctx = s->logctx,
- .dv_profile = s->dv_profile,
+ .cfg = s->cfg,
/* preserve temporary buffer */
.rpu_buf = s->rpu_buf,
.rpu_buf_sz = s->rpu_buf_sz,
@@ -74,22 +74,14 @@ void ff_dovi_ctx_flush(DOVIContext *s)
void ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0)
{
s->logctx = s0->logctx;
+ s->cfg = s0->cfg;
s->mapping = s0->mapping;
s->color = s0->color;
- s->dv_profile = s0->dv_profile;
for (int i = 0; i <= DOVI_MAX_DM_ID; i++)
ff_refstruct_replace(&s->vdr[i], s0->vdr[i]);
ff_refstruct_replace(&s->ext_blocks, s0->ext_blocks);
}
-void ff_dovi_update_cfg(DOVIContext *s, const AVDOVIDecoderConfigurationRecord *cfg)
-{
- if (!cfg)
- return;
-
- s->dv_profile = cfg->dv_profile;
-}
-
int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
{
AVFrameSideData *sd;
@@ -392,7 +384,7 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
goto fail;
/* Container */
- if (s->dv_profile == 10 /* dav1.10 */) {
+ if (s->cfg.dv_profile == 10 /* dav1.10 */) {
/* DV inside AV1 re-uses an EMDF container skeleton, but with fixed
* values - so we can effectively treat this as a magic byte sequence.
*
@@ -517,7 +509,7 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
use_prev_vdr_rpu = get_bits1(gb);
use_nlq = (hdr->rpu_format & 0x700) == 0 && !hdr->disable_residual_flag;
- profile = s->dv_profile ? s->dv_profile : guess_profile(hdr);
+ profile = s->cfg.dv_profile ? s->cfg.dv_profile : guess_profile(hdr);
if (profile == 5 && use_nlq) {
av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
goto fail;
diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
index 9f26f332ce..9a68e45bf1 100644
--- a/libavcodec/dovi_rpu.h
+++ b/libavcodec/dovi_rpu.h
@@ -31,6 +31,16 @@
typedef struct DOVIContext {
void *logctx;
+ /**
+ * Currently active dolby vision configuration, or {0} for none.
+ * Set by the user when decoding.
+ *
+ * Note: sizeof(cfg) is not part of the libavutil ABI, so users should
+ * never pass &cfg to any other library calls. This is included merely as
+ * a way to look up the values of fields known at compile time.
+ */
+ AVDOVIDecoderConfigurationRecord cfg;
+
/**
* Currently active RPU data header, updates on every dovi_rpu_parse().
*/
@@ -56,7 +66,6 @@ typedef struct DOVIContext {
struct DOVIVdr *vdr[DOVI_MAX_DM_ID+1]; ///< RefStruct references
uint8_t *rpu_buf; ///< temporary buffer
unsigned rpu_buf_sz;
- uint8_t dv_profile;
} DOVIContext;
@@ -68,17 +77,11 @@ void ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0);
void ff_dovi_ctx_unref(DOVIContext *s);
/**
- * Partially reset the internal state. Resets per-frame state while preserving
- * fields parsed from the configuration record.
+ * Partially reset the internal state. Resets per-frame state, but preserves
+ * the stream-wide configuration record.
*/
void ff_dovi_ctx_flush(DOVIContext *s);
-/**
- * Read the contents of an AVDOVIDecoderConfigurationRecord (usually provided
- * by stream side data) and update internal state accordingly.
- */
-void ff_dovi_update_cfg(DOVIContext *s, const AVDOVIDecoderConfigurationRecord *cfg);
-
/**
* Parse the contents of a Dovi RPU NAL and update the parsed values in the
* DOVIContext struct.
diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c
index 007eae6a1e..7825efe2e6 100644
--- a/libavcodec/hevcdec.c
+++ b/libavcodec/hevcdec.c
@@ -3365,14 +3365,13 @@ static int hevc_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
}
sd = av_packet_get_side_data(avpkt, AV_PKT_DATA_DOVI_CONF, &sd_size);
- if (sd && sd_size > 0) {
- int old = s->dovi_ctx.dv_profile;
-
- ff_dovi_update_cfg(&s->dovi_ctx, (AVDOVIDecoderConfigurationRecord *) sd);
+ if (sd && sd_size >= sizeof(s->dovi_ctx.cfg)) {
+ int old = s->dovi_ctx.cfg.dv_profile;
+ s->dovi_ctx.cfg = *(AVDOVIDecoderConfigurationRecord *) sd;
if (old)
av_log(avctx, AV_LOG_DEBUG,
"New DOVI configuration record from input packet (profile %d -> %u).\n",
- old, s->dovi_ctx.dv_profile);
+ old, s->dovi_ctx.cfg.dv_profile);
}
s->ref = s->collocated_ref = NULL;
@@ -3666,8 +3665,8 @@ static av_cold int hevc_decode_init(AVCodecContext *avctx)
}
sd = ff_get_coded_side_data(avctx, AV_PKT_DATA_DOVI_CONF);
- if (sd && sd->size > 0)
- ff_dovi_update_cfg(&s->dovi_ctx, (AVDOVIDecoderConfigurationRecord *) sd->data);
+ if (sd && sd->size >= sizeof(s->dovi_ctx.cfg))
+ s->dovi_ctx.cfg = *(AVDOVIDecoderConfigurationRecord *) sd->data;
}
return 0;
diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c
index 93c77ed9c6..09fe767fb8 100644
--- a/libavcodec/libdav1d.c
+++ b/libavcodec/libdav1d.c
@@ -290,10 +290,10 @@ static av_cold int libdav1d_init(AVCodecContext *c)
#endif
dav1d->dovi.logctx = c;
- dav1d->dovi.dv_profile = 10; // default for AV1
+ dav1d->dovi.cfg.dv_profile = 10; // default for AV1
sd = ff_get_coded_side_data(c, AV_PKT_DATA_DOVI_CONF);
- if (sd && sd->size > 0)
- ff_dovi_update_cfg(&dav1d->dovi, (AVDOVIDecoderConfigurationRecord *) sd->data);
+ if (sd && sd->size >= sizeof(dav1d->dovi.cfg))
+ dav1d->dovi.cfg = *(AVDOVIDecoderConfigurationRecord *) sd->data;
return 0;
}
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 02/13] avcodec/dovi_rpu: properly replace context header
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 01/13] avcodec/dovi_rpu: store entire config record Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 03/13] avcodec/dovi_rpu: clarify error on missing RPU VDR Niklas Haas
` (11 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
This was never set in ff_dovi_ctx_replace(), leading to possibly
out-of-date when copying from one thread to another.
---
libavcodec/dovi_rpu.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index d95c7e03af..bfb7b9fe66 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -75,6 +75,7 @@ void ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0)
{
s->logctx = s0->logctx;
s->cfg = s0->cfg;
+ s->header = s0->header;
s->mapping = s0->mapping;
s->color = s0->color;
for (int i = 0; i <= DOVI_MAX_DM_ID; i++)
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 03/13] avcodec/dovi_rpu: clarify error on missing RPU VDR
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 01/13] avcodec/dovi_rpu: store entire config record Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 02/13] avcodec/dovi_rpu: properly replace context header Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 04/13] avcodec/dovi_rpu: expose guess_profile(), clarify semantics Niklas Haas
` (10 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
The code was written under the misguided assumption that these fields
would only be present when the value changes, however this does not
match the actual patent specification, which says that streams are
required to either always signal this metadata, or never signal it.
That said, the specification does not really clarify what the defaults
of these fields should be in the event that this metadata is missing, so
without any sample file or other reference I don't wish to hazard
a guess at how to interpret these fields.
Fix the current behavior by making sure we always throw this error, even
for files that have the vdr sequence info in one frame but are missing
it in the next frame.
---
libavcodec/dovi_rpu.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index bfb7b9fe66..267e52ceb6 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -499,11 +499,11 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
hdr->el_spatial_resampling_filter_flag = get_bits1(gb);
hdr->disable_residual_flag = get_bits1(gb);
}
- }
-
- if (!hdr->bl_bit_depth) {
- av_log(s->logctx, AV_LOG_ERROR, "Missing RPU VDR sequence info?\n");
- goto fail;
+ } else {
+ /* lack of documentation/samples */
+ avpriv_request_sample(s->logctx, "Missing RPU VDR sequence info\n");
+ ff_dovi_ctx_unref(s);
+ return AVERROR_PATCHWELCOME;
}
vdr_dm_metadata_present = get_bits1(gb);
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 04/13] avcodec/dovi_rpu: expose guess_profile(), clarify semantics
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (2 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 03/13] avcodec/dovi_rpu: clarify error on missing RPU VDR Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 05/13] configure: rename dovi_rpu subsystem to dovi_rpudec Niklas Haas
` (9 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
To allow internally re-using it for both the encoder and decoder.
This is based on HEVC only, H.264/AV1 use their own (hopefully correctly
signalled) profiles (and in particular, the AV1 decoders implicitly
default the correct profile in the absence of a configuration record).
---
libavcodec/dovi_rpu.c | 4 ++--
libavcodec/dovi_rpu.h | 11 +++++++++++
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index 267e52ceb6..77fef8c496 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -121,7 +121,7 @@ int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
return 0;
}
-static int guess_profile(const AVDOVIRpuDataHeader *hdr)
+int ff_dovi_guess_profile_hevc(const AVDOVIRpuDataHeader *hdr)
{
switch (hdr->vdr_rpu_profile) {
case 0:
@@ -510,7 +510,7 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
use_prev_vdr_rpu = get_bits1(gb);
use_nlq = (hdr->rpu_format & 0x700) == 0 && !hdr->disable_residual_flag;
- profile = s->cfg.dv_profile ? s->cfg.dv_profile : guess_profile(hdr);
+ profile = s->cfg.dv_profile ? s->cfg.dv_profile : ff_dovi_guess_profile_hevc(hdr);
if (profile == 5 && use_nlq) {
av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
goto fail;
diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
index 9a68e45bf1..a866bbfebe 100644
--- a/libavcodec/dovi_rpu.h
+++ b/libavcodec/dovi_rpu.h
@@ -87,6 +87,10 @@ void ff_dovi_ctx_flush(DOVIContext *s);
* DOVIContext struct.
*
* Returns 0 or an error code.
+ *
+ * Note: `DOVIContext.cfg` should be initialized before calling into this
+ * function. If not done, the profile will be guessed according to HEVC
+ * semantics.
*/
int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
int err_recognition);
@@ -96,4 +100,11 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
*/
int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame);
+/**
+ * Internal helper function to guess the correct DV profile for HEVC.
+ *
+ * Returns the profile number or 0 if unknown.
+ */
+int ff_dovi_guess_profile_hevc(const AVDOVIRpuDataHeader *hdr);
+
#endif /* AVCODEC_DOVI_RPU_H */
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 05/13] configure: rename dovi_rpu subsystem to dovi_rpudec
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (3 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 04/13] avcodec/dovi_rpu: expose guess_profile(), clarify semantics Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 06/13] avcodec/dovi_rpu: split into dovi_rpu.c and dovi_rpudec.c Niklas Haas
` (8 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
To distinguish it from the to-be-added dovi_rpuenc.
---
configure | 10 +++++-----
libavcodec/Makefile | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/configure b/configure
index 55f1fc354d..c1e1ece1e2 100755
--- a/configure
+++ b/configure
@@ -2550,7 +2550,7 @@ CONFIG_EXTRA="
deflate_wrapper
dirac_parse
dnn
- dovi_rpu
+ dovi_rpudec
dvprofile
evcparse
exif
@@ -2841,7 +2841,7 @@ cbs_vp8_select="cbs"
cbs_vp9_select="cbs"
deflate_wrapper_deps="zlib"
dirac_parse_select="golomb"
-dovi_rpu_select="golomb"
+dovi_rpudec_select="golomb"
dnn_suggest="libtensorflow libopenvino libtorch"
dnn_deps="avformat swscale"
error_resilience_select="me_cmp"
@@ -2901,7 +2901,7 @@ asv1_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp"
asv2_decoder_select="blockdsp bswapdsp idctdsp"
asv2_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp"
atrac1_decoder_select="sinewin"
-av1_decoder_select="atsc_a53 cbs_av1 dovi_rpu"
+av1_decoder_select="atsc_a53 cbs_av1 dovi_rpudec"
bink_decoder_select="blockdsp hpeldsp"
binkaudio_dct_decoder_select="wma_freqs"
binkaudio_rdft_decoder_select="wma_freqs"
@@ -2957,7 +2957,7 @@ h264_decoder_suggest="error_resilience"
hap_decoder_select="snappy texturedsp"
hap_encoder_deps="libsnappy"
hap_encoder_select="texturedspenc"
-hevc_decoder_select="bswapdsp cabac dovi_rpu golomb hevcparse hevc_sei videodsp"
+hevc_decoder_select="bswapdsp cabac dovi_rpudec golomb hevcparse hevc_sei videodsp"
huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp"
huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llvidencdsp"
hymt_decoder_select="huffyuv_decoder"
@@ -3490,7 +3490,7 @@ libcelt_decoder_deps="libcelt"
libcodec2_decoder_deps="libcodec2"
libcodec2_encoder_deps="libcodec2"
libdav1d_decoder_deps="libdav1d"
-libdav1d_decoder_select="atsc_a53 dovi_rpu"
+libdav1d_decoder_select="atsc_a53 dovi_rpudec"
libdavs2_decoder_deps="libdavs2"
libdavs2_decoder_select="avs2_parser"
libfdk_aac_decoder_deps="libfdk_aac"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 7f6de4470e..61d261d606 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -85,7 +85,7 @@ OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o
OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o
-OBJS-$(CONFIG_DOVI_RPU) += dovi_rpu.o
+OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o
OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o
OBJS-$(CONFIG_EVCPARSE) += evc_parse.o evc_ps.o
OBJS-$(CONFIG_EXIF) += exif.o tiff_common.o
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 06/13] avcodec/dovi_rpu: split into dovi_rpu.c and dovi_rpudec.c
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (4 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 05/13] configure: rename dovi_rpu subsystem to dovi_rpudec Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 07/13] avcodec/dovi_rpuenc: add ff_dovi_configure() Niklas Haas
` (7 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
To allow compiling the decoding objects without the encoding objects and
vice versa. Common helpers that users of both APIs need are put into the
shared dovi_rpu.c.
---
libavcodec/Makefile | 2 +-
libavcodec/dovi_rpu.c | 624 +-------------------------------------
libavcodec/dovi_rpu.h | 14 +
libavcodec/dovi_rpudec.c | 635 +++++++++++++++++++++++++++++++++++++++
4 files changed, 651 insertions(+), 624 deletions(-)
create mode 100644 libavcodec/dovi_rpudec.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 61d261d606..45058eb8d2 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -85,7 +85,7 @@ OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o
OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o
-OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o
+OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o dovi_rpudec.o
OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o
OBJS-$(CONFIG_EVCPARSE) += evc_parse.o evc_ps.o
OBJS-$(CONFIG_EXIF) += exif.o tiff_common.o
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index 77fef8c496..b26c19dd5e 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -2,7 +2,7 @@
* Dolby Vision RPU decoder
*
* Copyright (C) 2021 Jan Ekström
- * Copyright (C) 2021 Niklas Haas
+ * Copyright (C) 2021-2024 Niklas Haas
*
* This file is part of FFmpeg.
*
@@ -21,29 +21,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "libavutil/buffer.h"
#include "libavutil/mem.h"
-#include "libavutil/crc.h"
-#include "avcodec.h"
#include "dovi_rpu.h"
-#include "golomb.h"
-#include "get_bits.h"
#include "refstruct.h"
-enum {
- RPU_COEFF_FIXED = 0,
- RPU_COEFF_FLOAT = 1,
-};
-
-/**
- * Private contents of vdr.
- */
-typedef struct DOVIVdr {
- AVDOVIDataMapping mapping;
- AVDOVIColorMetadata color;
-} DOVIVdr;
-
void ff_dovi_ctx_unref(DOVIContext *s)
{
for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr); i++)
@@ -83,44 +65,6 @@ void ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0)
ff_refstruct_replace(&s->ext_blocks, s0->ext_blocks);
}
-int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
-{
- AVFrameSideData *sd;
- AVBufferRef *buf;
- AVDOVIMetadata *dovi;
- size_t dovi_size, ext_sz;
-
- if (!s->mapping || !s->color)
- return 0; /* incomplete dovi metadata */
-
- dovi = av_dovi_metadata_alloc(&dovi_size);
- if (!dovi)
- return AVERROR(ENOMEM);
-
- buf = av_buffer_create((uint8_t *) dovi, dovi_size, NULL, NULL, 0);
- if (!buf) {
- av_free(dovi);
- return AVERROR(ENOMEM);
- }
-
- sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_METADATA, buf);
- if (!sd) {
- av_buffer_unref(&buf);
- return AVERROR(ENOMEM);
- }
-
- /* Copy only the parts of these structs known to us at compiler-time. */
-#define COPY(t, a, b, last) memcpy(a, b, offsetof(t, last) + sizeof((b)->last))
- COPY(AVDOVIRpuDataHeader, av_dovi_get_header(dovi), &s->header, disable_residual_flag);
- COPY(AVDOVIDataMapping, av_dovi_get_mapping(dovi), s->mapping, nlq_pivots);
- COPY(AVDOVIColorMetadata, av_dovi_get_color(dovi), s->color, source_diagonal);
- ext_sz = FFMIN(sizeof(AVDOVIDmData), dovi->ext_block_size);
- for (int i = 0; i < s->num_ext_blocks; i++)
- memcpy(av_dovi_get_ext(dovi, i), &s->ext_blocks[i], ext_sz);
- dovi->num_ext_blocks = s->num_ext_blocks;
- return 0;
-}
-
int ff_dovi_guess_profile_hevc(const AVDOVIRpuDataHeader *hdr)
{
switch (hdr->vdr_rpu_profile) {
@@ -142,569 +86,3 @@ int ff_dovi_guess_profile_hevc(const AVDOVIRpuDataHeader *hdr)
return 0; /* unknown */
}
-
-static inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
-{
- uint64_t ipart;
- union { uint32_t u32; float f32; } fpart;
-
- switch (hdr->coef_data_type) {
- case RPU_COEFF_FIXED:
- ipart = get_ue_golomb_long(gb);
- fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
- return (ipart << hdr->coef_log2_denom) | fpart.u32;
-
- case RPU_COEFF_FLOAT:
- fpart.u32 = get_bits_long(gb, 32);
- return fpart.f32 * (1LL << hdr->coef_log2_denom);
- }
-
- return 0; /* unreachable */
-}
-
-static inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
-{
- int64_t ipart;
- union { uint32_t u32; float f32; } fpart;
-
- switch (hdr->coef_data_type) {
- case RPU_COEFF_FIXED:
- ipart = get_se_golomb_long(gb);
- fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
- return ipart * (1LL << hdr->coef_log2_denom) | fpart.u32;
-
- case RPU_COEFF_FLOAT:
- fpart.u32 = get_bits_long(gb, 32);
- return fpart.f32 * (1LL << hdr->coef_log2_denom);
- }
-
- return 0; /* unreachable */
-}
-
-static inline unsigned get_variable_bits(GetBitContext *gb, int n)
-{
- unsigned int value = get_bits(gb, n);
- int read_more = get_bits1(gb);
- while (read_more) {
- value = (value + 1) << n;
- value |= get_bits(gb, n);
- read_more = get_bits1(gb);
- }
- return value;
-}
-
-#define VALIDATE(VAR, MIN, MAX) \
- do { \
- if (VAR < MIN || VAR > MAX) { \
- av_log(s->logctx, AV_LOG_ERROR, "RPU validation failed: " \
- #MIN" <= "#VAR" = %d <= "#MAX"\n", (int) VAR); \
- goto fail; \
- } \
- } while (0)
-
-static void parse_ext_v1(DOVIContext *s, GetBitContext *gb, AVDOVIDmData *dm)
-{
- switch (dm->level) {
- case 1:
- dm->l1.min_pq = get_bits(gb, 12);
- dm->l1.max_pq = get_bits(gb, 12);
- dm->l1.avg_pq = get_bits(gb, 12);
- break;
- case 2:
- dm->l2.target_max_pq = get_bits(gb, 12);
- dm->l2.trim_slope = get_bits(gb, 12);
- dm->l2.trim_offset = get_bits(gb, 12);
- dm->l2.trim_power = get_bits(gb, 12);
- dm->l2.trim_chroma_weight = get_bits(gb, 12);
- dm->l2.trim_saturation_gain = get_bits(gb, 12);
- dm->l2.ms_weight = get_bits(gb, 13) - 8192;
- break;
- case 4:
- dm->l4.anchor_pq = get_bits(gb, 12);
- dm->l4.anchor_power = get_bits(gb, 12);
- break;
- case 5:
- dm->l5.left_offset = get_bits(gb, 13);
- dm->l5.right_offset = get_bits(gb, 13);
- dm->l5.top_offset = get_bits(gb, 13);
- dm->l5.bottom_offset = get_bits(gb, 13);
- break;
- case 6:
- dm->l6.max_luminance = get_bits(gb, 16);
- dm->l6.min_luminance = get_bits(gb, 16);
- dm->l6.max_cll = get_bits(gb, 16);
- dm->l6.max_fall = get_bits(gb, 16);
- break;
- case 255:
- dm->l255.dm_run_mode = get_bits(gb, 8);
- dm->l255.dm_run_version = get_bits(gb, 8);
- for (int i = 0; i < 4; i++)
- dm->l255.dm_debug[i] = get_bits(gb, 8);
- break;
- default:
- av_log(s->logctx, AV_LOG_WARNING,
- "Unknown Dolby Vision DM v1 level: %u\n", dm->level);
- }
-}
-
-static AVCIExy get_cie_xy(GetBitContext *gb)
-{
- AVCIExy xy;
- const int denom = 32767;
- xy.x = av_make_q(get_sbits(gb, 16), denom);
- xy.y = av_make_q(get_sbits(gb, 16), denom);
- return xy;
-}
-
-static void parse_ext_v2(DOVIContext *s, GetBitContext *gb, AVDOVIDmData *dm,
- int ext_block_length)
-{
- switch (dm->level) {
- case 3:
- dm->l3.min_pq_offset = get_bits(gb, 12);
- dm->l3.max_pq_offset = get_bits(gb, 12);
- dm->l3.avg_pq_offset = get_bits(gb, 12);
- break;
- case 8:
- dm->l8.target_display_index = get_bits(gb, 8);
- dm->l8.trim_slope = get_bits(gb, 12);
- dm->l8.trim_offset = get_bits(gb, 12);
- dm->l8.trim_power = get_bits(gb, 12);
- dm->l8.trim_chroma_weight = get_bits(gb, 12);
- dm->l8.trim_saturation_gain = get_bits(gb, 12);
- dm->l8.ms_weight = get_bits(gb, 12) - 8192;
- if (ext_block_length < 12)
- break;
- dm->l8.target_mid_contrast = get_bits(gb, 12);
- if (ext_block_length < 13)
- break;
- dm->l8.clip_trim = get_bits(gb, 12);
- if (ext_block_length < 19)
- break;
- for (int i = 0; i < 6; i++)
- dm->l8.saturation_vector_field[i] = get_bits(gb, 8);
- if (ext_block_length < 25)
- break;
- for (int i = 0; i < 6; i++)
- dm->l8.hue_vector_field[i] = get_bits(gb, 8);
- break;
- case 9:
- dm->l9.source_primary_index = get_bits(gb, 8);
- if (ext_block_length < 17)
- break;
- dm->l9.source_display_primaries.prim.r = get_cie_xy(gb);
- dm->l9.source_display_primaries.prim.g = get_cie_xy(gb);
- dm->l9.source_display_primaries.prim.b = get_cie_xy(gb);
- dm->l9.source_display_primaries.wp = get_cie_xy(gb);
- break;
- case 10:
- dm->l10.target_display_index = get_bits(gb, 8);
- dm->l10.target_max_pq = get_bits(gb, 12);
- dm->l10.target_min_pq = get_bits(gb, 12);
- dm->l10.target_primary_index = get_bits(gb, 8);
- if (ext_block_length < 21)
- break;
- dm->l10.target_display_primaries.prim.r = get_cie_xy(gb);
- dm->l10.target_display_primaries.prim.g = get_cie_xy(gb);
- dm->l10.target_display_primaries.prim.b = get_cie_xy(gb);
- dm->l10.target_display_primaries.wp = get_cie_xy(gb);
- break;
- case 11:
- dm->l11.content_type = get_bits(gb, 8);
- dm->l11.whitepoint = get_bits(gb, 4);
- dm->l11.reference_mode_flag = get_bits1(gb);
- skip_bits(gb, 3); /* reserved */
- dm->l11.sharpness = get_bits(gb, 2);
- dm->l11.noise_reduction = get_bits(gb, 2);
- dm->l11.mpeg_noise_reduction = get_bits(gb, 2);
- dm->l11.frame_rate_conversion = get_bits(gb, 2);
- dm->l11.brightness = get_bits(gb, 2);
- dm->l11.color = get_bits(gb, 2);
- break;
- case 254:
- dm->l254.dm_mode = get_bits(gb, 8);
- dm->l254.dm_version_index = get_bits(gb, 8);
- break;
- default:
- av_log(s->logctx, AV_LOG_WARNING,
- "Unknown Dolby Vision DM v2 level: %u\n", dm->level);
- }
-}
-
-static int parse_ext_blocks(DOVIContext *s, GetBitContext *gb, int ver)
-{
- int num_ext_blocks, ext_block_length, start_pos, parsed_bits;
-
- num_ext_blocks = get_ue_golomb_31(gb);
- align_get_bits(gb);
- if (s->num_ext_blocks + num_ext_blocks > AV_DOVI_MAX_EXT_BLOCKS)
- return AVERROR_INVALIDDATA;
-
- if (!s->ext_blocks) {
- s->ext_blocks = ff_refstruct_allocz(sizeof(AVDOVIDmData) * AV_DOVI_MAX_EXT_BLOCKS);
- if (!s->ext_blocks)
- return AVERROR(ENOMEM);
- }
-
- while (num_ext_blocks--) {
- AVDOVIDmData *dm = &s->ext_blocks[s->num_ext_blocks++];
- ext_block_length = get_ue_golomb_31(gb);
- dm->level = get_bits(gb, 8);
- start_pos = get_bits_count(gb);
-
- switch (ver) {
- case 1: parse_ext_v1(s, gb, dm); break;
- case 2: parse_ext_v2(s, gb, dm, ext_block_length); break;
- }
-
- parsed_bits = get_bits_count(gb) - start_pos;
- if (parsed_bits > ext_block_length * 8)
- return AVERROR_INVALIDDATA;
- skip_bits(gb, ext_block_length * 8 - parsed_bits);
- }
-
- return 0;
-}
-
-int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
- int err_recognition)
-{
- AVDOVIRpuDataHeader *hdr = &s->header;
- GetBitContext *gb = &(GetBitContext){0};
- DOVIVdr *vdr;
- int ret;
-
- uint8_t rpu_type;
- uint8_t vdr_seq_info_present;
- uint8_t vdr_dm_metadata_present;
- uint8_t use_prev_vdr_rpu;
- uint8_t use_nlq;
- uint8_t profile;
-
- if (rpu_size < 5)
- goto fail;
-
- /* Container */
- if (s->cfg.dv_profile == 10 /* dav1.10 */) {
- /* DV inside AV1 re-uses an EMDF container skeleton, but with fixed
- * values - so we can effectively treat this as a magic byte sequence.
- *
- * The exact fields are, as follows:
- * emdf_version : f(2) = 0
- * key_id : f(3) = 6
- * emdf_payload_id : f(5) = 31
- * emdf_payload_id_ext : var(5) = 225
- * smploffste : f(1) = 0
- * duratione : f(1) = 0
- * groupide : f(1) = 0
- * codecdatae : f(1) = 0
- * discard_unknown_payload : f(1) = 1
- */
- const unsigned header_magic = 0x01be6841u;
- unsigned emdf_header, emdf_payload_size, emdf_protection;
- if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0)
- return ret;
- emdf_header = get_bits_long(gb, 27);
- VALIDATE(emdf_header, header_magic, header_magic);
- emdf_payload_size = get_variable_bits(gb, 8);
- VALIDATE(emdf_payload_size, 6, 512);
- if (emdf_payload_size * 8 > get_bits_left(gb))
- return AVERROR_INVALIDDATA;
-
- /* The payload is not byte-aligned (off by *one* bit, curse Dolby),
- * so copy into a fresh buffer to preserve byte alignment of the
- * RPU struct */
- av_fast_padded_malloc(&s->rpu_buf, &s->rpu_buf_sz, emdf_payload_size);
- if (!s->rpu_buf)
- return AVERROR(ENOMEM);
- for (int i = 0; i < emdf_payload_size; i++)
- s->rpu_buf[i] = get_bits(gb, 8);
- rpu = s->rpu_buf;
- rpu_size = emdf_payload_size;
-
- /* Validate EMDF footer */
- emdf_protection = get_bits(gb, 5 + 12);
- VALIDATE(emdf_protection, 0x400, 0x400);
- } else {
- /* NAL RBSP with prefix and trailing zeroes */
- VALIDATE(rpu[0], 25, 25); /* NAL prefix */
- rpu++;
- rpu_size--;
- /* Strip trailing padding bytes */
- while (rpu_size && rpu[rpu_size - 1] == 0)
- rpu_size--;
- }
-
- if (!rpu_size || rpu[rpu_size - 1] != 0x80)
- goto fail;
-
- if (err_recognition & AV_EF_CRCCHECK) {
- uint32_t crc = av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE),
- -1, rpu, rpu_size - 1)); /* exclude 0x80 */
- if (crc) {
- av_log(s->logctx, AV_LOG_ERROR, "RPU CRC mismatch: %X\n", crc);
- if (err_recognition & AV_EF_EXPLODE)
- goto fail;
- }
- }
-
- if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0)
- return ret;
-
- /* RPU header */
- rpu_type = get_bits(gb, 6);
- if (rpu_type != 2) {
- av_log(s->logctx, AV_LOG_WARNING, "Unrecognized RPU type "
- "%"PRIu8", ignoring\n", rpu_type);
- return 0;
- }
-
- hdr->rpu_type = rpu_type;
- hdr->rpu_format = get_bits(gb, 11);
-
- /* Values specific to RPU type 2 */
- hdr->vdr_rpu_profile = get_bits(gb, 4);
- hdr->vdr_rpu_level = get_bits(gb, 4);
-
- vdr_seq_info_present = get_bits1(gb);
- if (vdr_seq_info_present) {
- hdr->chroma_resampling_explicit_filter_flag = get_bits1(gb);
- hdr->coef_data_type = get_bits(gb, 2);
- VALIDATE(hdr->coef_data_type, RPU_COEFF_FIXED, RPU_COEFF_FLOAT);
- switch (hdr->coef_data_type) {
- case RPU_COEFF_FIXED:
- hdr->coef_log2_denom = get_ue_golomb(gb);
- VALIDATE(hdr->coef_log2_denom, 13, 32);
- break;
- case RPU_COEFF_FLOAT:
- hdr->coef_log2_denom = 32; /* arbitrary, choose maximum precision */
- break;
- }
-
- hdr->vdr_rpu_normalized_idc = get_bits(gb, 2);
- hdr->bl_video_full_range_flag = get_bits1(gb);
-
- if ((hdr->rpu_format & 0x700) == 0) {
- int bl_bit_depth_minus8 = get_ue_golomb_31(gb);
- int el_bit_depth_minus8 = get_ue_golomb_31(gb);
- int vdr_bit_depth_minus8 = get_ue_golomb_31(gb);
- VALIDATE(bl_bit_depth_minus8, 0, 8);
- VALIDATE(el_bit_depth_minus8, 0, 8);
- VALIDATE(vdr_bit_depth_minus8, 0, 8);
- hdr->bl_bit_depth = bl_bit_depth_minus8 + 8;
- hdr->el_bit_depth = el_bit_depth_minus8 + 8;
- hdr->vdr_bit_depth = vdr_bit_depth_minus8 + 8;
- hdr->spatial_resampling_filter_flag = get_bits1(gb);
- skip_bits(gb, 3); /* reserved_zero_3bits */
- hdr->el_spatial_resampling_filter_flag = get_bits1(gb);
- hdr->disable_residual_flag = get_bits1(gb);
- }
- } else {
- /* lack of documentation/samples */
- avpriv_request_sample(s->logctx, "Missing RPU VDR sequence info\n");
- ff_dovi_ctx_unref(s);
- return AVERROR_PATCHWELCOME;
- }
-
- vdr_dm_metadata_present = get_bits1(gb);
- use_prev_vdr_rpu = get_bits1(gb);
- use_nlq = (hdr->rpu_format & 0x700) == 0 && !hdr->disable_residual_flag;
-
- profile = s->cfg.dv_profile ? s->cfg.dv_profile : ff_dovi_guess_profile_hevc(hdr);
- if (profile == 5 && use_nlq) {
- av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
- goto fail;
- }
-
- if (use_prev_vdr_rpu) {
- int prev_vdr_rpu_id = get_ue_golomb_31(gb);
- VALIDATE(prev_vdr_rpu_id, 0, DOVI_MAX_DM_ID);
- if (!s->vdr[prev_vdr_rpu_id]) {
- av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU ID: %u\n",
- prev_vdr_rpu_id);
- goto fail;
- }
- vdr = s->vdr[prev_vdr_rpu_id];
- s->mapping = &vdr->mapping;
- } else {
- int vdr_rpu_id = get_ue_golomb_31(gb);
- VALIDATE(vdr_rpu_id, 0, DOVI_MAX_DM_ID);
- if (!s->vdr[vdr_rpu_id]) {
- s->vdr[vdr_rpu_id] = ff_refstruct_allocz(sizeof(DOVIVdr));
- if (!s->vdr[vdr_rpu_id])
- return AVERROR(ENOMEM);
- }
-
- vdr = s->vdr[vdr_rpu_id];
- s->mapping = &vdr->mapping;
-
- vdr->mapping.vdr_rpu_id = vdr_rpu_id;
- vdr->mapping.mapping_color_space = get_ue_golomb_31(gb);
- vdr->mapping.mapping_chroma_format_idc = get_ue_golomb_31(gb);
-
- for (int c = 0; c < 3; c++) {
- AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c];
- int num_pivots_minus_2 = get_ue_golomb_31(gb);
- int pivot = 0;
-
- VALIDATE(num_pivots_minus_2, 0, AV_DOVI_MAX_PIECES - 1);
- curve->num_pivots = num_pivots_minus_2 + 2;
- for (int i = 0; i < curve->num_pivots; i++) {
- pivot += get_bits(gb, hdr->bl_bit_depth);
- curve->pivots[i] = av_clip_uint16(pivot);
- }
- }
-
- if (use_nlq) {
- int nlq_pivot = 0;
- vdr->mapping.nlq_method_idc = get_bits(gb, 3);
-
- for (int i = 0; i < 2; i++) {
- nlq_pivot += get_bits(gb, hdr->bl_bit_depth);
- vdr->mapping.nlq_pivots[i] = av_clip_uint16(nlq_pivot);
- }
-
- /**
- * The patent mentions another legal value, NLQ_MU_LAW, but it's
- * not documented anywhere how to parse or apply that type of NLQ.
- */
- VALIDATE(vdr->mapping.nlq_method_idc, 0, AV_DOVI_NLQ_LINEAR_DZ);
- } else {
- vdr->mapping.nlq_method_idc = AV_DOVI_NLQ_NONE;
- }
-
- vdr->mapping.num_x_partitions = get_ue_golomb_long(gb) + 1;
- vdr->mapping.num_y_partitions = get_ue_golomb_long(gb) + 1;
- /* End of rpu_data_header(), start of vdr_rpu_data_payload() */
-
- for (int c = 0; c < 3; c++) {
- AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c];
- for (int i = 0; i < curve->num_pivots - 1; i++) {
- int mapping_idc = get_ue_golomb_31(gb);
- VALIDATE(mapping_idc, 0, 1);
- curve->mapping_idc[i] = mapping_idc;
- switch (mapping_idc) {
- case AV_DOVI_MAPPING_POLYNOMIAL: {
- int poly_order_minus1 = get_ue_golomb_31(gb);
- VALIDATE(poly_order_minus1, 0, 1);
- curve->poly_order[i] = poly_order_minus1 + 1;
- if (poly_order_minus1 == 0) {
- int linear_interp_flag = get_bits1(gb);
- if (linear_interp_flag) {
- /* lack of documentation/samples */
- avpriv_request_sample(s->logctx, "Dolby Vision "
- "linear interpolation");
- ff_dovi_ctx_unref(s);
- return AVERROR_PATCHWELCOME;
- }
- }
- for (int k = 0; k <= curve->poly_order[i]; k++)
- curve->poly_coef[i][k] = get_se_coef(gb, hdr);
- break;
- }
- case AV_DOVI_MAPPING_MMR: {
- int mmr_order_minus1 = get_bits(gb, 2);
- VALIDATE(mmr_order_minus1, 0, 2);
- curve->mmr_order[i] = mmr_order_minus1 + 1;
- curve->mmr_constant[i] = get_se_coef(gb, hdr);
- for (int j = 0; j < curve->mmr_order[i]; j++) {
- for (int k = 0; k < 7; k++)
- curve->mmr_coef[i][j][k] = get_se_coef(gb, hdr);
- }
- break;
- }
- }
- }
- }
-
- if (use_nlq) {
- for (int c = 0; c < 3; c++) {
- AVDOVINLQParams *nlq = &vdr->mapping.nlq[c];
- nlq->nlq_offset = get_bits(gb, hdr->el_bit_depth);
- nlq->vdr_in_max = get_ue_coef(gb, hdr);
- switch (vdr->mapping.nlq_method_idc) {
- case AV_DOVI_NLQ_LINEAR_DZ:
- nlq->linear_deadzone_slope = get_ue_coef(gb, hdr);
- nlq->linear_deadzone_threshold = get_ue_coef(gb, hdr);
- break;
- }
- }
- }
- }
-
- if (vdr_dm_metadata_present) {
- AVDOVIColorMetadata *color;
- int affected_dm_id = get_ue_golomb_31(gb);
- int current_dm_id = get_ue_golomb_31(gb);
- VALIDATE(affected_dm_id, 0, DOVI_MAX_DM_ID);
- VALIDATE(current_dm_id, 0, DOVI_MAX_DM_ID);
- if (!s->vdr[affected_dm_id]) {
- s->vdr[affected_dm_id] = ff_refstruct_allocz(sizeof(DOVIVdr));
- if (!s->vdr[affected_dm_id])
- return AVERROR(ENOMEM);
- }
-
- if (!s->vdr[current_dm_id]) {
- av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU DM ID: %u\n",
- current_dm_id);
- goto fail;
- }
-
- /* Update current pointer based on current_dm_id */
- vdr = s->vdr[current_dm_id];
- s->color = &vdr->color;
-
- /* Update values of affected_dm_id */
- vdr = s->vdr[affected_dm_id];
- color = &vdr->color;
- color->dm_metadata_id = affected_dm_id;
- color->scene_refresh_flag = get_ue_golomb_31(gb);
- for (int i = 0; i < 9; i++)
- color->ycc_to_rgb_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 13);
- for (int i = 0; i < 3; i++) {
- int denom = profile == 4 ? (1 << 30) : (1 << 28);
- unsigned offset = get_bits_long(gb, 32);
- if (offset > INT_MAX) {
- /* Ensure the result fits inside AVRational */
- offset >>= 1;
- denom >>= 1;
- }
- color->ycc_to_rgb_offset[i] = av_make_q(offset, denom);
- }
- for (int i = 0; i < 9; i++)
- color->rgb_to_lms_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 14);
-
- color->signal_eotf = get_bits(gb, 16);
- color->signal_eotf_param0 = get_bits(gb, 16);
- color->signal_eotf_param1 = get_bits(gb, 16);
- color->signal_eotf_param2 = get_bits_long(gb, 32);
- color->signal_bit_depth = get_bits(gb, 5);
- VALIDATE(color->signal_bit_depth, 8, 16);
- color->signal_color_space = get_bits(gb, 2);
- color->signal_chroma_format = get_bits(gb, 2);
- color->signal_full_range_flag = get_bits(gb, 2);
- color->source_min_pq = get_bits(gb, 12);
- color->source_max_pq = get_bits(gb, 12);
- color->source_diagonal = get_bits(gb, 10);
- }
-
- /* Parse extension blocks */
- s->num_ext_blocks = 0;
- if ((ret = parse_ext_blocks(s, gb, 1)) < 0) {
- ff_dovi_ctx_unref(s);
- return ret;
- }
-
- if (get_bits_left(gb) > 48 /* padding + CRC32 + terminator */) {
- if ((ret = parse_ext_blocks(s, gb, 2)) < 0) {
- ff_dovi_ctx_unref(s);
- return ret;
- }
- }
-
- return 0;
-
-fail:
- ff_dovi_ctx_unref(s); /* don't leak potentially invalid state */
- return AVERROR_INVALIDDATA;
-}
diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
index a866bbfebe..0c12a3fee0 100644
--- a/libavcodec/dovi_rpu.h
+++ b/libavcodec/dovi_rpu.h
@@ -100,6 +100,20 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
*/
int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame);
+/***************************************************
+ * The following section is for internal use only. *
+ ***************************************************/
+
+typedef struct DOVIVdr {
+ AVDOVIDataMapping mapping;
+ AVDOVIColorMetadata color;
+} DOVIVdr;
+
+enum {
+ RPU_COEFF_FIXED = 0,
+ RPU_COEFF_FLOAT = 1,
+};
+
/**
* Internal helper function to guess the correct DV profile for HEVC.
*
diff --git a/libavcodec/dovi_rpudec.c b/libavcodec/dovi_rpudec.c
new file mode 100644
index 0000000000..7c7eda9d09
--- /dev/null
+++ b/libavcodec/dovi_rpudec.c
@@ -0,0 +1,635 @@
+/*
+ * Dolby Vision RPU decoder
+ *
+ * Copyright (C) 2021 Jan Ekström
+ * Copyright (C) 2021-2024 Niklas Haas
+ *
+ * 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/mem.h"
+#include "libavutil/crc.h"
+
+#include "avcodec.h"
+#include "dovi_rpu.h"
+#include "golomb.h"
+#include "get_bits.h"
+#include "refstruct.h"
+
+int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
+{
+ AVFrameSideData *sd;
+ AVBufferRef *buf;
+ AVDOVIMetadata *dovi;
+ size_t dovi_size, ext_sz;
+
+ if (!s->mapping || !s->color)
+ return 0; /* incomplete dovi metadata */
+
+ dovi = av_dovi_metadata_alloc(&dovi_size);
+ if (!dovi)
+ return AVERROR(ENOMEM);
+
+ buf = av_buffer_create((uint8_t *) dovi, dovi_size, NULL, NULL, 0);
+ if (!buf) {
+ av_free(dovi);
+ return AVERROR(ENOMEM);
+ }
+
+ sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_METADATA, buf);
+ if (!sd) {
+ av_buffer_unref(&buf);
+ return AVERROR(ENOMEM);
+ }
+
+ /* Copy only the parts of these structs known to us at compiler-time. */
+#define COPY(t, a, b, last) memcpy(a, b, offsetof(t, last) + sizeof((b)->last))
+ COPY(AVDOVIRpuDataHeader, av_dovi_get_header(dovi), &s->header, disable_residual_flag);
+ COPY(AVDOVIDataMapping, av_dovi_get_mapping(dovi), s->mapping, nlq_pivots);
+ COPY(AVDOVIColorMetadata, av_dovi_get_color(dovi), s->color, source_diagonal);
+ ext_sz = FFMIN(sizeof(AVDOVIDmData), dovi->ext_block_size);
+ for (int i = 0; i < s->num_ext_blocks; i++)
+ memcpy(av_dovi_get_ext(dovi, i), &s->ext_blocks[i], ext_sz);
+ dovi->num_ext_blocks = s->num_ext_blocks;
+ return 0;
+}
+
+static inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
+{
+ uint64_t ipart;
+ union { uint32_t u32; float f32; } fpart;
+
+ switch (hdr->coef_data_type) {
+ case RPU_COEFF_FIXED:
+ ipart = get_ue_golomb_long(gb);
+ fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
+ return (ipart << hdr->coef_log2_denom) | fpart.u32;
+
+ case RPU_COEFF_FLOAT:
+ fpart.u32 = get_bits_long(gb, 32);
+ return fpart.f32 * (1LL << hdr->coef_log2_denom);
+ }
+
+ return 0; /* unreachable */
+}
+
+static inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
+{
+ int64_t ipart;
+ union { uint32_t u32; float f32; } fpart;
+
+ switch (hdr->coef_data_type) {
+ case RPU_COEFF_FIXED:
+ ipart = get_se_golomb_long(gb);
+ fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
+ return ipart * (1LL << hdr->coef_log2_denom) | fpart.u32;
+
+ case RPU_COEFF_FLOAT:
+ fpart.u32 = get_bits_long(gb, 32);
+ return fpart.f32 * (1LL << hdr->coef_log2_denom);
+ }
+
+ return 0; /* unreachable */
+}
+
+static inline unsigned get_variable_bits(GetBitContext *gb, int n)
+{
+ unsigned int value = get_bits(gb, n);
+ int read_more = get_bits1(gb);
+ while (read_more) {
+ value = (value + 1) << n;
+ value |= get_bits(gb, n);
+ read_more = get_bits1(gb);
+ }
+ return value;
+}
+
+#define VALIDATE(VAR, MIN, MAX) \
+ do { \
+ if (VAR < MIN || VAR > MAX) { \
+ av_log(s->logctx, AV_LOG_ERROR, "RPU validation failed: " \
+ #MIN" <= "#VAR" = %d <= "#MAX"\n", (int) VAR); \
+ goto fail; \
+ } \
+ } while (0)
+
+static void parse_ext_v1(DOVIContext *s, GetBitContext *gb, AVDOVIDmData *dm)
+{
+ switch (dm->level) {
+ case 1:
+ dm->l1.min_pq = get_bits(gb, 12);
+ dm->l1.max_pq = get_bits(gb, 12);
+ dm->l1.avg_pq = get_bits(gb, 12);
+ break;
+ case 2:
+ dm->l2.target_max_pq = get_bits(gb, 12);
+ dm->l2.trim_slope = get_bits(gb, 12);
+ dm->l2.trim_offset = get_bits(gb, 12);
+ dm->l2.trim_power = get_bits(gb, 12);
+ dm->l2.trim_chroma_weight = get_bits(gb, 12);
+ dm->l2.trim_saturation_gain = get_bits(gb, 12);
+ dm->l2.ms_weight = get_bits(gb, 13) - 8192;
+ break;
+ case 4:
+ dm->l4.anchor_pq = get_bits(gb, 12);
+ dm->l4.anchor_power = get_bits(gb, 12);
+ break;
+ case 5:
+ dm->l5.left_offset = get_bits(gb, 13);
+ dm->l5.right_offset = get_bits(gb, 13);
+ dm->l5.top_offset = get_bits(gb, 13);
+ dm->l5.bottom_offset = get_bits(gb, 13);
+ break;
+ case 6:
+ dm->l6.max_luminance = get_bits(gb, 16);
+ dm->l6.min_luminance = get_bits(gb, 16);
+ dm->l6.max_cll = get_bits(gb, 16);
+ dm->l6.max_fall = get_bits(gb, 16);
+ break;
+ case 255:
+ dm->l255.dm_run_mode = get_bits(gb, 8);
+ dm->l255.dm_run_version = get_bits(gb, 8);
+ for (int i = 0; i < 4; i++)
+ dm->l255.dm_debug[i] = get_bits(gb, 8);
+ break;
+ default:
+ av_log(s->logctx, AV_LOG_WARNING,
+ "Unknown Dolby Vision DM v1 level: %u\n", dm->level);
+ }
+}
+
+static AVCIExy get_cie_xy(GetBitContext *gb)
+{
+ AVCIExy xy;
+ const int denom = 32767;
+ xy.x = av_make_q(get_sbits(gb, 16), denom);
+ xy.y = av_make_q(get_sbits(gb, 16), denom);
+ return xy;
+}
+
+static void parse_ext_v2(DOVIContext *s, GetBitContext *gb, AVDOVIDmData *dm,
+ int ext_block_length)
+{
+ switch (dm->level) {
+ case 3:
+ dm->l3.min_pq_offset = get_bits(gb, 12);
+ dm->l3.max_pq_offset = get_bits(gb, 12);
+ dm->l3.avg_pq_offset = get_bits(gb, 12);
+ break;
+ case 8:
+ dm->l8.target_display_index = get_bits(gb, 8);
+ dm->l8.trim_slope = get_bits(gb, 12);
+ dm->l8.trim_offset = get_bits(gb, 12);
+ dm->l8.trim_power = get_bits(gb, 12);
+ dm->l8.trim_chroma_weight = get_bits(gb, 12);
+ dm->l8.trim_saturation_gain = get_bits(gb, 12);
+ dm->l8.ms_weight = get_bits(gb, 12) - 8192;
+ if (ext_block_length < 12)
+ break;
+ dm->l8.target_mid_contrast = get_bits(gb, 12);
+ if (ext_block_length < 13)
+ break;
+ dm->l8.clip_trim = get_bits(gb, 12);
+ if (ext_block_length < 19)
+ break;
+ for (int i = 0; i < 6; i++)
+ dm->l8.saturation_vector_field[i] = get_bits(gb, 8);
+ if (ext_block_length < 25)
+ break;
+ for (int i = 0; i < 6; i++)
+ dm->l8.hue_vector_field[i] = get_bits(gb, 8);
+ break;
+ case 9:
+ dm->l9.source_primary_index = get_bits(gb, 8);
+ if (ext_block_length < 17)
+ break;
+ dm->l9.source_display_primaries.prim.r = get_cie_xy(gb);
+ dm->l9.source_display_primaries.prim.g = get_cie_xy(gb);
+ dm->l9.source_display_primaries.prim.b = get_cie_xy(gb);
+ dm->l9.source_display_primaries.wp = get_cie_xy(gb);
+ break;
+ case 10:
+ dm->l10.target_display_index = get_bits(gb, 8);
+ dm->l10.target_max_pq = get_bits(gb, 12);
+ dm->l10.target_min_pq = get_bits(gb, 12);
+ dm->l10.target_primary_index = get_bits(gb, 8);
+ if (ext_block_length < 21)
+ break;
+ dm->l10.target_display_primaries.prim.r = get_cie_xy(gb);
+ dm->l10.target_display_primaries.prim.g = get_cie_xy(gb);
+ dm->l10.target_display_primaries.prim.b = get_cie_xy(gb);
+ dm->l10.target_display_primaries.wp = get_cie_xy(gb);
+ break;
+ case 11:
+ dm->l11.content_type = get_bits(gb, 8);
+ dm->l11.whitepoint = get_bits(gb, 4);
+ dm->l11.reference_mode_flag = get_bits1(gb);
+ skip_bits(gb, 3); /* reserved */
+ dm->l11.sharpness = get_bits(gb, 2);
+ dm->l11.noise_reduction = get_bits(gb, 2);
+ dm->l11.mpeg_noise_reduction = get_bits(gb, 2);
+ dm->l11.frame_rate_conversion = get_bits(gb, 2);
+ dm->l11.brightness = get_bits(gb, 2);
+ dm->l11.color = get_bits(gb, 2);
+ break;
+ case 254:
+ dm->l254.dm_mode = get_bits(gb, 8);
+ dm->l254.dm_version_index = get_bits(gb, 8);
+ break;
+ default:
+ av_log(s->logctx, AV_LOG_WARNING,
+ "Unknown Dolby Vision DM v2 level: %u\n", dm->level);
+ }
+}
+
+static int parse_ext_blocks(DOVIContext *s, GetBitContext *gb, int ver)
+{
+ int num_ext_blocks, ext_block_length, start_pos, parsed_bits;
+
+ num_ext_blocks = get_ue_golomb_31(gb);
+ align_get_bits(gb);
+ if (s->num_ext_blocks + num_ext_blocks > AV_DOVI_MAX_EXT_BLOCKS)
+ return AVERROR_INVALIDDATA;
+
+ if (!s->ext_blocks) {
+ s->ext_blocks = ff_refstruct_allocz(sizeof(AVDOVIDmData) * AV_DOVI_MAX_EXT_BLOCKS);
+ if (!s->ext_blocks)
+ return AVERROR(ENOMEM);
+ }
+
+ while (num_ext_blocks--) {
+ AVDOVIDmData *dm = &s->ext_blocks[s->num_ext_blocks++];
+ ext_block_length = get_ue_golomb_31(gb);
+ dm->level = get_bits(gb, 8);
+ start_pos = get_bits_count(gb);
+
+ switch (ver) {
+ case 1: parse_ext_v1(s, gb, dm); break;
+ case 2: parse_ext_v2(s, gb, dm, ext_block_length); break;
+ }
+
+ parsed_bits = get_bits_count(gb) - start_pos;
+ if (parsed_bits > ext_block_length * 8)
+ return AVERROR_INVALIDDATA;
+ skip_bits(gb, ext_block_length * 8 - parsed_bits);
+ }
+
+ return 0;
+}
+
+int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
+ int err_recognition)
+{
+ AVDOVIRpuDataHeader *hdr = &s->header;
+ GetBitContext *gb = &(GetBitContext){0};
+ DOVIVdr *vdr;
+ int ret;
+
+ uint8_t rpu_type;
+ uint8_t vdr_seq_info_present;
+ uint8_t vdr_dm_metadata_present;
+ uint8_t use_prev_vdr_rpu;
+ uint8_t use_nlq;
+ uint8_t profile;
+
+ if (rpu_size < 5)
+ goto fail;
+
+ /* Container */
+ if (s->cfg.dv_profile == 10 /* dav1.10 */) {
+ /* DV inside AV1 re-uses an EMDF container skeleton, but with fixed
+ * values - so we can effectively treat this as a magic byte sequence.
+ *
+ * The exact fields are, as follows:
+ * emdf_version : f(2) = 0
+ * key_id : f(3) = 6
+ * emdf_payload_id : f(5) = 31
+ * emdf_payload_id_ext : var(5) = 225
+ * smploffste : f(1) = 0
+ * duratione : f(1) = 0
+ * groupide : f(1) = 0
+ * codecdatae : f(1) = 0
+ * discard_unknown_payload : f(1) = 1
+ */
+ const unsigned header_magic = 0x01be6841u;
+ unsigned emdf_header, emdf_payload_size, emdf_protection;
+ if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0)
+ return ret;
+ emdf_header = get_bits_long(gb, 27);
+ VALIDATE(emdf_header, header_magic, header_magic);
+ emdf_payload_size = get_variable_bits(gb, 8);
+ VALIDATE(emdf_payload_size, 6, 512);
+ if (emdf_payload_size * 8 > get_bits_left(gb))
+ return AVERROR_INVALIDDATA;
+
+ /* The payload is not byte-aligned (off by *one* bit, curse Dolby),
+ * so copy into a fresh buffer to preserve byte alignment of the
+ * RPU struct */
+ av_fast_padded_malloc(&s->rpu_buf, &s->rpu_buf_sz, emdf_payload_size);
+ if (!s->rpu_buf)
+ return AVERROR(ENOMEM);
+ for (int i = 0; i < emdf_payload_size; i++)
+ s->rpu_buf[i] = get_bits(gb, 8);
+ rpu = s->rpu_buf;
+ rpu_size = emdf_payload_size;
+
+ /* Validate EMDF footer */
+ emdf_protection = get_bits(gb, 5 + 12);
+ VALIDATE(emdf_protection, 0x400, 0x400);
+ } else {
+ /* NAL RBSP with prefix and trailing zeroes */
+ VALIDATE(rpu[0], 25, 25); /* NAL prefix */
+ rpu++;
+ rpu_size--;
+ /* Strip trailing padding bytes */
+ while (rpu_size && rpu[rpu_size - 1] == 0)
+ rpu_size--;
+ }
+
+ if (!rpu_size || rpu[rpu_size - 1] != 0x80)
+ goto fail;
+
+ if (err_recognition & AV_EF_CRCCHECK) {
+ uint32_t crc = av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE),
+ -1, rpu, rpu_size - 1)); /* exclude 0x80 */
+ if (crc) {
+ av_log(s->logctx, AV_LOG_ERROR, "RPU CRC mismatch: %X\n", crc);
+ if (err_recognition & AV_EF_EXPLODE)
+ goto fail;
+ }
+ }
+
+ if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0)
+ return ret;
+
+ /* RPU header */
+ rpu_type = get_bits(gb, 6);
+ if (rpu_type != 2) {
+ av_log(s->logctx, AV_LOG_WARNING, "Unrecognized RPU type "
+ "%"PRIu8", ignoring\n", rpu_type);
+ return 0;
+ }
+
+ hdr->rpu_type = rpu_type;
+ hdr->rpu_format = get_bits(gb, 11);
+
+ /* Values specific to RPU type 2 */
+ hdr->vdr_rpu_profile = get_bits(gb, 4);
+ hdr->vdr_rpu_level = get_bits(gb, 4);
+
+ vdr_seq_info_present = get_bits1(gb);
+ if (vdr_seq_info_present) {
+ hdr->chroma_resampling_explicit_filter_flag = get_bits1(gb);
+ hdr->coef_data_type = get_bits(gb, 2);
+ VALIDATE(hdr->coef_data_type, RPU_COEFF_FIXED, RPU_COEFF_FLOAT);
+ switch (hdr->coef_data_type) {
+ case RPU_COEFF_FIXED:
+ hdr->coef_log2_denom = get_ue_golomb(gb);
+ VALIDATE(hdr->coef_log2_denom, 13, 32);
+ break;
+ case RPU_COEFF_FLOAT:
+ hdr->coef_log2_denom = 32; /* arbitrary, choose maximum precision */
+ break;
+ }
+
+ hdr->vdr_rpu_normalized_idc = get_bits(gb, 2);
+ hdr->bl_video_full_range_flag = get_bits1(gb);
+
+ if ((hdr->rpu_format & 0x700) == 0) {
+ int bl_bit_depth_minus8 = get_ue_golomb_31(gb);
+ int el_bit_depth_minus8 = get_ue_golomb_31(gb);
+ int vdr_bit_depth_minus8 = get_ue_golomb_31(gb);
+ VALIDATE(bl_bit_depth_minus8, 0, 8);
+ VALIDATE(el_bit_depth_minus8, 0, 8);
+ VALIDATE(vdr_bit_depth_minus8, 0, 8);
+ hdr->bl_bit_depth = bl_bit_depth_minus8 + 8;
+ hdr->el_bit_depth = el_bit_depth_minus8 + 8;
+ hdr->vdr_bit_depth = vdr_bit_depth_minus8 + 8;
+ hdr->spatial_resampling_filter_flag = get_bits1(gb);
+ skip_bits(gb, 3); /* reserved_zero_3bits */
+ hdr->el_spatial_resampling_filter_flag = get_bits1(gb);
+ hdr->disable_residual_flag = get_bits1(gb);
+ }
+ } else {
+ /* lack of documentation/samples */
+ avpriv_request_sample(s->logctx, "Missing RPU VDR sequence info\n");
+ ff_dovi_ctx_unref(s);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ vdr_dm_metadata_present = get_bits1(gb);
+ use_prev_vdr_rpu = get_bits1(gb);
+ use_nlq = (hdr->rpu_format & 0x700) == 0 && !hdr->disable_residual_flag;
+
+ profile = s->cfg.dv_profile ? s->cfg.dv_profile : ff_dovi_guess_profile_hevc(hdr);
+ if (profile == 5 && use_nlq) {
+ av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
+ goto fail;
+ }
+
+ if (use_prev_vdr_rpu) {
+ int prev_vdr_rpu_id = get_ue_golomb_31(gb);
+ VALIDATE(prev_vdr_rpu_id, 0, DOVI_MAX_DM_ID);
+ if (!s->vdr[prev_vdr_rpu_id]) {
+ av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU ID: %u\n",
+ prev_vdr_rpu_id);
+ goto fail;
+ }
+ vdr = s->vdr[prev_vdr_rpu_id];
+ s->mapping = &vdr->mapping;
+ } else {
+ int vdr_rpu_id = get_ue_golomb_31(gb);
+ VALIDATE(vdr_rpu_id, 0, DOVI_MAX_DM_ID);
+ if (!s->vdr[vdr_rpu_id]) {
+ s->vdr[vdr_rpu_id] = ff_refstruct_allocz(sizeof(DOVIVdr));
+ if (!s->vdr[vdr_rpu_id])
+ return AVERROR(ENOMEM);
+ }
+
+ vdr = s->vdr[vdr_rpu_id];
+ s->mapping = &vdr->mapping;
+
+ vdr->mapping.vdr_rpu_id = vdr_rpu_id;
+ vdr->mapping.mapping_color_space = get_ue_golomb_31(gb);
+ vdr->mapping.mapping_chroma_format_idc = get_ue_golomb_31(gb);
+
+ for (int c = 0; c < 3; c++) {
+ AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c];
+ int num_pivots_minus_2 = get_ue_golomb_31(gb);
+ int pivot = 0;
+
+ VALIDATE(num_pivots_minus_2, 0, AV_DOVI_MAX_PIECES - 1);
+ curve->num_pivots = num_pivots_minus_2 + 2;
+ for (int i = 0; i < curve->num_pivots; i++) {
+ pivot += get_bits(gb, hdr->bl_bit_depth);
+ curve->pivots[i] = av_clip_uint16(pivot);
+ }
+ }
+
+ if (use_nlq) {
+ int nlq_pivot = 0;
+ vdr->mapping.nlq_method_idc = get_bits(gb, 3);
+
+ for (int i = 0; i < 2; i++) {
+ nlq_pivot += get_bits(gb, hdr->bl_bit_depth);
+ vdr->mapping.nlq_pivots[i] = av_clip_uint16(nlq_pivot);
+ }
+
+ /**
+ * The patent mentions another legal value, NLQ_MU_LAW, but it's
+ * not documented anywhere how to parse or apply that type of NLQ.
+ */
+ VALIDATE(vdr->mapping.nlq_method_idc, 0, AV_DOVI_NLQ_LINEAR_DZ);
+ } else {
+ vdr->mapping.nlq_method_idc = AV_DOVI_NLQ_NONE;
+ }
+
+ vdr->mapping.num_x_partitions = get_ue_golomb_long(gb) + 1;
+ vdr->mapping.num_y_partitions = get_ue_golomb_long(gb) + 1;
+ /* End of rpu_data_header(), start of vdr_rpu_data_payload() */
+
+ for (int c = 0; c < 3; c++) {
+ AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c];
+ for (int i = 0; i < curve->num_pivots - 1; i++) {
+ int mapping_idc = get_ue_golomb_31(gb);
+ VALIDATE(mapping_idc, 0, 1);
+ curve->mapping_idc[i] = mapping_idc;
+ switch (mapping_idc) {
+ case AV_DOVI_MAPPING_POLYNOMIAL: {
+ int poly_order_minus1 = get_ue_golomb_31(gb);
+ VALIDATE(poly_order_minus1, 0, 1);
+ curve->poly_order[i] = poly_order_minus1 + 1;
+ if (poly_order_minus1 == 0) {
+ int linear_interp_flag = get_bits1(gb);
+ if (linear_interp_flag) {
+ /* lack of documentation/samples */
+ avpriv_request_sample(s->logctx, "Dolby Vision "
+ "linear interpolation");
+ ff_dovi_ctx_unref(s);
+ return AVERROR_PATCHWELCOME;
+ }
+ }
+ for (int k = 0; k <= curve->poly_order[i]; k++)
+ curve->poly_coef[i][k] = get_se_coef(gb, hdr);
+ break;
+ }
+ case AV_DOVI_MAPPING_MMR: {
+ int mmr_order_minus1 = get_bits(gb, 2);
+ VALIDATE(mmr_order_minus1, 0, 2);
+ curve->mmr_order[i] = mmr_order_minus1 + 1;
+ curve->mmr_constant[i] = get_se_coef(gb, hdr);
+ for (int j = 0; j < curve->mmr_order[i]; j++) {
+ for (int k = 0; k < 7; k++)
+ curve->mmr_coef[i][j][k] = get_se_coef(gb, hdr);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (use_nlq) {
+ for (int c = 0; c < 3; c++) {
+ AVDOVINLQParams *nlq = &vdr->mapping.nlq[c];
+ nlq->nlq_offset = get_bits(gb, hdr->el_bit_depth);
+ nlq->vdr_in_max = get_ue_coef(gb, hdr);
+ switch (vdr->mapping.nlq_method_idc) {
+ case AV_DOVI_NLQ_LINEAR_DZ:
+ nlq->linear_deadzone_slope = get_ue_coef(gb, hdr);
+ nlq->linear_deadzone_threshold = get_ue_coef(gb, hdr);
+ break;
+ }
+ }
+ }
+ }
+
+ if (vdr_dm_metadata_present) {
+ AVDOVIColorMetadata *color;
+ int affected_dm_id = get_ue_golomb_31(gb);
+ int current_dm_id = get_ue_golomb_31(gb);
+ VALIDATE(affected_dm_id, 0, DOVI_MAX_DM_ID);
+ VALIDATE(current_dm_id, 0, DOVI_MAX_DM_ID);
+ if (!s->vdr[affected_dm_id]) {
+ s->vdr[affected_dm_id] = ff_refstruct_allocz(sizeof(DOVIVdr));
+ if (!s->vdr[affected_dm_id])
+ return AVERROR(ENOMEM);
+ }
+
+ if (!s->vdr[current_dm_id]) {
+ av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU DM ID: %u\n",
+ current_dm_id);
+ goto fail;
+ }
+
+ /* Update current pointer based on current_dm_id */
+ vdr = s->vdr[current_dm_id];
+ s->color = &vdr->color;
+
+ /* Update values of affected_dm_id */
+ vdr = s->vdr[affected_dm_id];
+ color = &vdr->color;
+ color->dm_metadata_id = affected_dm_id;
+ color->scene_refresh_flag = get_ue_golomb_31(gb);
+ for (int i = 0; i < 9; i++)
+ color->ycc_to_rgb_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 13);
+ for (int i = 0; i < 3; i++) {
+ int denom = profile == 4 ? (1 << 30) : (1 << 28);
+ unsigned offset = get_bits_long(gb, 32);
+ if (offset > INT_MAX) {
+ /* Ensure the result fits inside AVRational */
+ offset >>= 1;
+ denom >>= 1;
+ }
+ color->ycc_to_rgb_offset[i] = av_make_q(offset, denom);
+ }
+ for (int i = 0; i < 9; i++)
+ color->rgb_to_lms_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 14);
+
+ color->signal_eotf = get_bits(gb, 16);
+ color->signal_eotf_param0 = get_bits(gb, 16);
+ color->signal_eotf_param1 = get_bits(gb, 16);
+ color->signal_eotf_param2 = get_bits_long(gb, 32);
+ color->signal_bit_depth = get_bits(gb, 5);
+ VALIDATE(color->signal_bit_depth, 8, 16);
+ color->signal_color_space = get_bits(gb, 2);
+ color->signal_chroma_format = get_bits(gb, 2);
+ color->signal_full_range_flag = get_bits(gb, 2);
+ color->source_min_pq = get_bits(gb, 12);
+ color->source_max_pq = get_bits(gb, 12);
+ color->source_diagonal = get_bits(gb, 10);
+ }
+
+ /* Parse extension blocks */
+ s->num_ext_blocks = 0;
+ if ((ret = parse_ext_blocks(s, gb, 1)) < 0) {
+ ff_dovi_ctx_unref(s);
+ return ret;
+ }
+
+ if (get_bits_left(gb) > 48 /* padding + CRC32 + terminator */) {
+ if ((ret = parse_ext_blocks(s, gb, 2)) < 0) {
+ ff_dovi_ctx_unref(s);
+ return ret;
+ }
+ }
+
+ return 0;
+
+fail:
+ ff_dovi_ctx_unref(s); /* don't leak potentially invalid state */
+ return AVERROR_INVALIDDATA;
+}
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 07/13] avcodec/dovi_rpuenc: add ff_dovi_configure()
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (5 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 06/13] avcodec/dovi_rpu: split into dovi_rpu.c and dovi_rpudec.c Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 08/13] avcodec/dovi_rpudec: make `enable` also affect decoding Niklas Haas
` (6 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
We need to set up the configuration struct appropriately based on the
codec type, colorspace metadata, and presence/absence of an EL (though,
we currently don't support an EL).
When present, we use the signalled RPU data header to help infer (and
validate) the right values.
Behavior can be controlled by a new DOVIContext.enable flag.
---
configure | 2 +
libavcodec/Makefile | 1 +
libavcodec/dovi_rpu.h | 24 ++++-
libavcodec/dovi_rpuenc.c | 203 +++++++++++++++++++++++++++++++++++++++
4 files changed, 229 insertions(+), 1 deletion(-)
create mode 100644 libavcodec/dovi_rpuenc.c
diff --git a/configure b/configure
index c1e1ece1e2..d25d0f6907 100755
--- a/configure
+++ b/configure
@@ -2551,6 +2551,7 @@ CONFIG_EXTRA="
dirac_parse
dnn
dovi_rpudec
+ dovi_rpuenc
dvprofile
evcparse
exif
@@ -2842,6 +2843,7 @@ cbs_vp9_select="cbs"
deflate_wrapper_deps="zlib"
dirac_parse_select="golomb"
dovi_rpudec_select="golomb"
+dovi_rpuenc_select="golomb"
dnn_suggest="libtensorflow libopenvino libtorch"
dnn_deps="avformat swscale"
error_resilience_select="me_cmp"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 45058eb8d2..6bddb6fb5e 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -86,6 +86,7 @@ OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o
OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o
OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o dovi_rpudec.o
+OBJS-$(CONFIG_DOVI_RPUENC) += dovi_rpu.o dovi_rpuenc.o
OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o
OBJS-$(CONFIG_EVCPARSE) += evc_parse.o evc_ps.o
OBJS-$(CONFIG_EXIF) += exif.o tiff_common.o
diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
index 0c12a3fee0..3e80647422 100644
--- a/libavcodec/dovi_rpu.h
+++ b/libavcodec/dovi_rpu.h
@@ -26,14 +26,25 @@
#include "libavutil/dovi_meta.h"
#include "libavutil/frame.h"
+#include "avcodec.h"
#define DOVI_MAX_DM_ID 15
typedef struct DOVIContext {
void *logctx;
+ /**
+ * Enable tri-state.
+ *
+ * For encoding, FF_DOVI_AUTOMATIC enables Dolby Vision only if
+ * avctx->decoded_side_data contains an AVDOVIMetadata.
+ */
+#define FF_DOVI_AUTOMATIC -1
+ int enable;
+
/**
* Currently active dolby vision configuration, or {0} for none.
- * Set by the user when decoding.
+ * Set by the user when decoding. Generated by ff_dovi_configure()
+ * when encoding.
*
* Note: sizeof(cfg) is not part of the libavutil ABI, so users should
* never pass &cfg to any other library calls. This is included merely as
@@ -100,6 +111,17 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
*/
int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame);
+/**
+ * Configure the encoder for Dolby Vision encoding. Generates a configuration
+ * record in s->cfg, and attaches it to avctx->coded_side_data. Sets the correct
+ * profile and compatibility ID based on the tagged AVCodecContext colorspace
+ * metadata, and the correct level based on the resolution and tagged framerate.
+ *
+ * Returns 0 or a negative error code.
+ */
+int ff_dovi_configure(DOVIContext *s, AVCodecContext *avctx);
+
+
/***************************************************
* The following section is for internal use only. *
***************************************************/
diff --git a/libavcodec/dovi_rpuenc.c b/libavcodec/dovi_rpuenc.c
new file mode 100644
index 0000000000..3ab4624a79
--- /dev/null
+++ b/libavcodec/dovi_rpuenc.c
@@ -0,0 +1,203 @@
+/*
+ * Dolby Vision RPU encoder
+ *
+ * Copyright (C) 2024 Niklas Haas
+ *
+ * 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/avassert.h"
+#include "libavutil/mem.h"
+
+#include "avcodec.h"
+#include "dovi_rpu.h"
+
+static struct {
+ uint64_t pps; // maximum pixels per second
+ int width; // maximum width
+ int main; // maximum bitrate in main tier
+ int high; // maximum bitrate in high tier
+} dv_levels[] = {
+ [1] = {1280*720*24, 1280, 20, 50},
+ [2] = {1280*720*30, 1280, 20, 50},
+ [3] = {1920*1080*24, 1920, 20, 70},
+ [4] = {1920*1080*30, 2560, 20, 70},
+ [5] = {1920*1080*60, 3840, 20, 70},
+ [6] = {3840*2160*24, 3840, 25, 130},
+ [7] = {3840*2160*30, 3840, 25, 130},
+ [8] = {3840*2160*48, 3840, 40, 130},
+ [9] = {3840*2160*60, 3840, 40, 130},
+ [10] = {3840*2160*120, 3840, 60, 240},
+ [11] = {3840*2160*120, 7680, 60, 240},
+ [12] = {7680*4320*60, 7680, 120, 450},
+ [13] = {7680*4320*120u, 7680, 240, 800},
+};
+
+int ff_dovi_configure(DOVIContext *s, AVCodecContext *avctx)
+{
+ AVDOVIDecoderConfigurationRecord *cfg;
+ const AVDOVIRpuDataHeader *hdr = NULL;
+ const AVFrameSideData *sd;
+ int dv_profile, dv_level, bl_compat_id;
+ size_t cfg_size;
+ uint64_t pps;
+
+ if (!s->enable)
+ goto skip;
+
+ sd = av_frame_side_data_get(avctx->decoded_side_data,
+ avctx->nb_decoded_side_data, AV_FRAME_DATA_DOVI_METADATA);
+
+ if (sd)
+ hdr = av_dovi_get_header((const AVDOVIMetadata *) sd->data);
+
+ if (s->enable == FF_DOVI_AUTOMATIC && !hdr)
+ goto skip;
+
+ switch (avctx->codec_id) {
+ case AV_CODEC_ID_AV1: dv_profile = 10; break;
+ case AV_CODEC_ID_H264: dv_profile = 9; break;
+ case AV_CODEC_ID_HEVC: dv_profile = hdr ? ff_dovi_guess_profile_hevc(hdr) : 8; break;
+ default:
+ /* No other encoder should be calling this! */
+ av_assert0(0);
+ return AVERROR_BUG;
+ }
+
+ if (avctx->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) {
+ if (dv_profile == 9) {
+ if (avctx->pix_fmt != AV_PIX_FMT_YUV420P)
+ dv_profile = 0;
+ } else {
+ if (avctx->pix_fmt != AV_PIX_FMT_YUV420P10)
+ dv_profile = 0;
+ }
+ }
+
+ switch (dv_profile) {
+ case 0: /* None */
+ bl_compat_id = -1;
+ break;
+ case 4: /* HEVC with enhancement layer */
+ case 7:
+ if (s->enable > 0) {
+ av_log(s->logctx, AV_LOG_ERROR, "Coding of Dolby Vision enhancement "
+ "layers is currently unsupported.");
+ return AVERROR_PATCHWELCOME;
+ } else {
+ goto skip;
+ }
+ case 5: /* HEVC with proprietary IPTPQc2 */
+ bl_compat_id = 0;
+ break;
+ case 10:
+ /* FIXME: check for proper H.273 tags once those are added */
+ if (hdr && hdr->bl_video_full_range_flag) {
+ /* AV1 with proprietary IPTPQc2 */
+ bl_compat_id = 0;
+ break;
+ }
+ /* fall through */
+ case 8: /* HEVC (or AV1) with BL compatibility */
+ if (avctx->colorspace == AVCOL_SPC_BT2020_NCL &&
+ avctx->color_primaries == AVCOL_PRI_BT2020 &&
+ avctx->color_trc == AVCOL_TRC_SMPTE2084) {
+ bl_compat_id = 1;
+ } else if (avctx->colorspace == AVCOL_SPC_BT2020_NCL &&
+ avctx->color_primaries == AVCOL_PRI_BT2020 &&
+ avctx->color_trc == AVCOL_TRC_ARIB_STD_B67) {
+ bl_compat_id = 4;
+ } else if (avctx->colorspace == AVCOL_SPC_BT709 &&
+ avctx->color_primaries == AVCOL_PRI_BT709 &&
+ avctx->color_trc == AVCOL_TRC_BT709) {
+ bl_compat_id = 2;
+ } else {
+ /* Not a valid colorspace combination */
+ bl_compat_id = -1;
+ }
+ }
+
+ if (!dv_profile || bl_compat_id < 0) {
+ if (s->enable > 0) {
+ av_log(s->logctx, AV_LOG_ERROR, "Dolby Vision enabled, but could "
+ "not determine profile and compaatibility mode. Double-check "
+ "colorspace and format settings for compatibility?\n");
+ return AVERROR(EINVAL);
+ }
+ goto skip;
+ }
+
+ pps = avctx->width * avctx->height;
+ if (avctx->framerate.num) {
+ pps = pps * avctx->framerate.num / avctx->framerate.den;
+ } else {
+ pps *= 25; /* sanity fallback */
+ }
+
+ dv_level = 0;
+ for (int i = 1; i < FF_ARRAY_ELEMS(dv_levels); i++) {
+ if (pps > dv_levels[i].pps)
+ continue;
+ if (avctx->width > dv_levels[i].width)
+ continue;
+ /* In theory, we should also test the bitrate when known, and
+ * distinguish between main and high tier. In practice, just ignore
+ * the bitrate constraints and hope they work out. This would ideally
+ * be handled by either the encoder or muxer directly. */
+ dv_level = i;
+ break;
+ }
+
+ if (!dv_level) {
+ if (avctx->strict_std_compliance >= FF_COMPLIANCE_STRICT) {
+ av_log(s->logctx, AV_LOG_ERROR, "Coded PPS (%"PRIu64") and width (%d) "
+ "exceed Dolby Vision limitations\n", pps, avctx->width);
+ return AVERROR(EINVAL);
+ } else {
+ av_log(s->logctx, AV_LOG_WARNING, "Coded PPS (%"PRIu64") and width (%d) "
+ "exceed Dolby Vision limitations. Ignoring, resulting file "
+ "may be non-conforming.\n", pps, avctx->width);
+ dv_level = FF_ARRAY_ELEMS(dv_levels) - 1;
+ }
+ }
+
+ cfg = av_dovi_alloc(&cfg_size);
+ if (!cfg)
+ return AVERROR(ENOMEM);
+
+ if (!av_packet_side_data_add(&avctx->coded_side_data, &avctx->nb_coded_side_data,
+ AV_PKT_DATA_DOVI_CONF, cfg, cfg_size, 0)) {
+ av_free(cfg);
+ return AVERROR(ENOMEM);
+ }
+
+ cfg->dv_version_major = 1;
+ cfg->dv_version_minor = 0;
+ cfg->dv_profile = dv_profile;
+ cfg->dv_level = dv_level;
+ cfg->rpu_present_flag = 1;
+ cfg->el_present_flag = 0;
+ cfg->bl_present_flag = 1;
+ cfg->dv_bl_signal_compatibility_id = bl_compat_id;
+
+ s->cfg = *cfg;
+ return 0;
+
+skip:
+ s->cfg = (AVDOVIDecoderConfigurationRecord) {0};
+ return 0;
+}
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 08/13] avcodec/dovi_rpudec: make `enable` also affect decoding
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (6 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 07/13] avcodec/dovi_rpuenc: add ff_dovi_configure() Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-18 11:30 ` Andreas Rheinhardt
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 09/13] avcodec/dovi_rpuenc: add ff_dovi_rpu_generate() Niklas Haas
` (5 subsequent siblings)
13 siblings, 1 reply; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
This could be used by codecs to selectively disable parsing Dolby Vision
RPUs, and is cheap to support.
---
libavcodec/av1dec.c | 1 +
libavcodec/dovi_rpu.h | 2 ++
libavcodec/dovi_rpudec.c | 6 ++++++
libavcodec/hevcdec.c | 1 +
libavcodec/libdav1d.c | 1 +
5 files changed, 11 insertions(+)
diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c
index 4c1405df77..20865b4f12 100644
--- a/libavcodec/av1dec.c
+++ b/libavcodec/av1dec.c
@@ -1551,6 +1551,7 @@ static void av1_decode_flush(AVCodecContext *avctx)
static const AVOption av1_options[] = {
{ "operating_point", "Select an operating point of the scalable bitstream",
OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, AV1_MAX_OPERATING_POINTS - 1, VD },
+ { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, VD },
{ NULL }
};
diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
index 3e80647422..8f8905b96b 100644
--- a/libavcodec/dovi_rpu.h
+++ b/libavcodec/dovi_rpu.h
@@ -37,6 +37,8 @@ typedef struct DOVIContext {
*
* For encoding, FF_DOVI_AUTOMATIC enables Dolby Vision only if
* avctx->decoded_side_data contains an AVDOVIMetadata.
+ *
+ * For decoding, FF_DOVI_AUTOMATIC has the same meaning as 1.
*/
#define FF_DOVI_AUTOMATIC -1
int enable;
diff --git a/libavcodec/dovi_rpudec.c b/libavcodec/dovi_rpudec.c
index 7c7eda9d09..978d5dfc2b 100644
--- a/libavcodec/dovi_rpudec.c
+++ b/libavcodec/dovi_rpudec.c
@@ -37,6 +37,9 @@ int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
AVDOVIMetadata *dovi;
size_t dovi_size, ext_sz;
+ if (!s->enable)
+ return 0;
+
if (!s->mapping || !s->color)
return 0; /* incomplete dovi metadata */
@@ -306,6 +309,9 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
uint8_t use_nlq;
uint8_t profile;
+ if (!s->enable)
+ return 0;
+
if (rpu_size < 5)
goto fail;
diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c
index 7825efe2e6..c622a48b94 100644
--- a/libavcodec/hevcdec.c
+++ b/libavcodec/hevcdec.c
@@ -3694,6 +3694,7 @@ static const AVOption options[] = {
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR },
{ "strict-displaywin", "stricly apply default display window size", OFFSET(apply_defdispwin),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR },
+ { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi_ctx.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, PAR },
{ NULL },
};
diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c
index 09fe767fb8..f9e1a181fc 100644
--- a/libavcodec/libdav1d.c
+++ b/libavcodec/libdav1d.c
@@ -674,6 +674,7 @@ static const AVOption libdav1d_options[] = {
{ "filmgrain", "Apply Film Grain", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD | AV_OPT_FLAG_DEPRECATED },
{ "oppoint", "Select an operating point of the scalable bitstream", OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 31, VD },
{ "alllayers", "Output all spatial layers", OFFSET(all_layers), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
+ { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, VD },
{ NULL }
};
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH v3 08/13] avcodec/dovi_rpudec: make `enable` also affect decoding
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 08/13] avcodec/dovi_rpudec: make `enable` also affect decoding Niklas Haas
@ 2024-04-18 11:30 ` Andreas Rheinhardt
2024-04-18 12:23 ` Niklas Haas
0 siblings, 1 reply; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-04-18 11:30 UTC (permalink / raw)
To: ffmpeg-devel
Niklas Haas:
> From: Niklas Haas <git@haasn.dev>
>
> This could be used by codecs to selectively disable parsing Dolby Vision
> RPUs, and is cheap to support.
> ---
> libavcodec/av1dec.c | 1 +
> libavcodec/dovi_rpu.h | 2 ++
> libavcodec/dovi_rpudec.c | 6 ++++++
> libavcodec/hevcdec.c | 1 +
> libavcodec/libdav1d.c | 1 +
> 5 files changed, 11 insertions(+)
>
> diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c
> index 4c1405df77..20865b4f12 100644
> --- a/libavcodec/av1dec.c
> +++ b/libavcodec/av1dec.c
> @@ -1551,6 +1551,7 @@ static void av1_decode_flush(AVCodecContext *avctx)
> static const AVOption av1_options[] = {
> { "operating_point", "Select an operating point of the scalable bitstream",
> OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, AV1_MAX_OPERATING_POINTS - 1, VD },
> + { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, VD },
> { NULL }
> };
>
> diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
> index 3e80647422..8f8905b96b 100644
> --- a/libavcodec/dovi_rpu.h
> +++ b/libavcodec/dovi_rpu.h
> @@ -37,6 +37,8 @@ typedef struct DOVIContext {
> *
> * For encoding, FF_DOVI_AUTOMATIC enables Dolby Vision only if
> * avctx->decoded_side_data contains an AVDOVIMetadata.
> + *
> + * For decoding, FF_DOVI_AUTOMATIC has the same meaning as 1.
> */
> #define FF_DOVI_AUTOMATIC -1
> int enable;
> diff --git a/libavcodec/dovi_rpudec.c b/libavcodec/dovi_rpudec.c
> index 7c7eda9d09..978d5dfc2b 100644
> --- a/libavcodec/dovi_rpudec.c
> +++ b/libavcodec/dovi_rpudec.c
> @@ -37,6 +37,9 @@ int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
> AVDOVIMetadata *dovi;
> size_t dovi_size, ext_sz;
>
> + if (!s->enable)
> + return 0;
> +
> if (!s->mapping || !s->color)
> return 0; /* incomplete dovi metadata */
>
> @@ -306,6 +309,9 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
> uint8_t use_nlq;
> uint8_t profile;
>
> + if (!s->enable)
> + return 0;
> +
> if (rpu_size < 5)
> goto fail;
>
> diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c
> index 7825efe2e6..c622a48b94 100644
> --- a/libavcodec/hevcdec.c
> +++ b/libavcodec/hevcdec.c
> @@ -3694,6 +3694,7 @@ static const AVOption options[] = {
> AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR },
> { "strict-displaywin", "stricly apply default display window size", OFFSET(apply_defdispwin),
> AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR },
> + { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi_ctx.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, PAR },
> { NULL },
> };
>
> diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c
> index 09fe767fb8..f9e1a181fc 100644
> --- a/libavcodec/libdav1d.c
> +++ b/libavcodec/libdav1d.c
> @@ -674,6 +674,7 @@ static const AVOption libdav1d_options[] = {
> { "filmgrain", "Apply Film Grain", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD | AV_OPT_FLAG_DEPRECATED },
> { "oppoint", "Select an operating point of the scalable bitstream", OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 31, VD },
> { "alllayers", "Output all spatial layers", OFFSET(all_layers), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
> + { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, VD },
> { NULL }
> };
>
Is parsing this stuff very expensive? If not, I don't really see a
reason to add an option for it (we also do not add options for lots of
other side data types).
- Andreas
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH v3 08/13] avcodec/dovi_rpudec: make `enable` also affect decoding
2024-04-18 11:30 ` Andreas Rheinhardt
@ 2024-04-18 12:23 ` Niklas Haas
0 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-18 12:23 UTC (permalink / raw)
To: ffmpeg-devel
On Thu, 18 Apr 2024 13:30:39 +0200 Andreas Rheinhardt <andreas.rheinhardt@outlook.com> wrote:
> Niklas Haas:
> > From: Niklas Haas <git@haasn.dev>
> >
> > This could be used by codecs to selectively disable parsing Dolby Vision
> > RPUs, and is cheap to support.
> > ---
> > libavcodec/av1dec.c | 1 +
> > libavcodec/dovi_rpu.h | 2 ++
> > libavcodec/dovi_rpudec.c | 6 ++++++
> > libavcodec/hevcdec.c | 1 +
> > libavcodec/libdav1d.c | 1 +
> > 5 files changed, 11 insertions(+)
> >
> > diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c
> > index 4c1405df77..20865b4f12 100644
> > --- a/libavcodec/av1dec.c
> > +++ b/libavcodec/av1dec.c
> > @@ -1551,6 +1551,7 @@ static void av1_decode_flush(AVCodecContext *avctx)
> > static const AVOption av1_options[] = {
> > { "operating_point", "Select an operating point of the scalable bitstream",
> > OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, AV1_MAX_OPERATING_POINTS - 1, VD },
> > + { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, VD },
> > { NULL }
> > };
> >
> > diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
> > index 3e80647422..8f8905b96b 100644
> > --- a/libavcodec/dovi_rpu.h
> > +++ b/libavcodec/dovi_rpu.h
> > @@ -37,6 +37,8 @@ typedef struct DOVIContext {
> > *
> > * For encoding, FF_DOVI_AUTOMATIC enables Dolby Vision only if
> > * avctx->decoded_side_data contains an AVDOVIMetadata.
> > + *
> > + * For decoding, FF_DOVI_AUTOMATIC has the same meaning as 1.
> > */
> > #define FF_DOVI_AUTOMATIC -1
> > int enable;
> > diff --git a/libavcodec/dovi_rpudec.c b/libavcodec/dovi_rpudec.c
> > index 7c7eda9d09..978d5dfc2b 100644
> > --- a/libavcodec/dovi_rpudec.c
> > +++ b/libavcodec/dovi_rpudec.c
> > @@ -37,6 +37,9 @@ int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
> > AVDOVIMetadata *dovi;
> > size_t dovi_size, ext_sz;
> >
> > + if (!s->enable)
> > + return 0;
> > +
> > if (!s->mapping || !s->color)
> > return 0; /* incomplete dovi metadata */
> >
> > @@ -306,6 +309,9 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
> > uint8_t use_nlq;
> > uint8_t profile;
> >
> > + if (!s->enable)
> > + return 0;
> > +
> > if (rpu_size < 5)
> > goto fail;
> >
> > diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c
> > index 7825efe2e6..c622a48b94 100644
> > --- a/libavcodec/hevcdec.c
> > +++ b/libavcodec/hevcdec.c
> > @@ -3694,6 +3694,7 @@ static const AVOption options[] = {
> > AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR },
> > { "strict-displaywin", "stricly apply default display window size", OFFSET(apply_defdispwin),
> > AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR },
> > + { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi_ctx.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, PAR },
> > { NULL },
> > };
> >
> > diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c
> > index 09fe767fb8..f9e1a181fc 100644
> > --- a/libavcodec/libdav1d.c
> > +++ b/libavcodec/libdav1d.c
> > @@ -674,6 +674,7 @@ static const AVOption libdav1d_options[] = {
> > { "filmgrain", "Apply Film Grain", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD | AV_OPT_FLAG_DEPRECATED },
> > { "oppoint", "Select an operating point of the scalable bitstream", OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 31, VD },
> > { "alllayers", "Output all spatial layers", OFFSET(all_layers), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
> > + { "dolbyvision", "Decode Dolby Vision RPUs", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, VD },
> > { NULL }
> > };
> >
>
> Is parsing this stuff very expensive? If not, I don't really see a
> reason to add an option for it (we also do not add options for lots of
> other side data types).
>
> - Andreas
It's not expensive, no. Well, having DV metadata present technically
alters the interpretation of the video quite substantially (e.g. SDR
streams now displaying as HDR), so users may want *some* way of
"globally" disabling it.
That said, it's probably better to relegate that to `vf_sidedata`.
I'll drop this commit from the series, then.
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 09/13] avcodec/dovi_rpuenc: add ff_dovi_rpu_generate()
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (7 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 08/13] avcodec/dovi_rpudec: make `enable` also affect decoding Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 10/13] avformat/movenc: warn if dovi cfg ignored Niklas Haas
` (4 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
This function takes a decoded AVDOVIMetadata struct and turns it back
into a binary RPU. Verified using existing tools, and matches the
bitstream in official reference files.
I decided to just roll the EMDF and NAL encapsulation into this function
because the end user will need to do it otherwise anyways.
---
libavcodec/dovi_rpu.h | 20 +-
libavcodec/dovi_rpuenc.c | 544 +++++++++++++++++++++++++++++++++++++++
2 files changed, 562 insertions(+), 2 deletions(-)
diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
index 8f8905b96b..1b74548e89 100644
--- a/libavcodec/dovi_rpu.h
+++ b/libavcodec/dovi_rpu.h
@@ -55,20 +55,22 @@ typedef struct DOVIContext {
AVDOVIDecoderConfigurationRecord cfg;
/**
- * Currently active RPU data header, updates on every dovi_rpu_parse().
+ * Currently active RPU data header, updates on every ff_dovi_rpu_parse()
+ * or ff_dovi_rpu_generate().
*/
AVDOVIRpuDataHeader header;
/**
* Currently active data mappings, or NULL. Points into memory owned by the
* corresponding rpu/vdr_ref, which becomes invalid on the next call to
- * dovi_rpu_parse.
+ * ff_dovi_rpu_parse() or ff_dovi_rpu_generate().
*/
const AVDOVIDataMapping *mapping;
const AVDOVIColorMetadata *color;
/**
* Currently active extension blocks, updates on every ff_dovi_rpu_parse()
+ * or ff_dovi_rpu_generate().
*/
AVDOVIDmData *ext_blocks;
int num_ext_blocks;
@@ -138,6 +140,20 @@ enum {
RPU_COEFF_FLOAT = 1,
};
+/**
+ * Synthesize a Dolby Vision RPU reflecting the current state. Note that this
+ * assumes all previous calls to `ff_dovi_rpu_generate` have been appropriately
+ * signalled, i.e. it will not re-send already transmitted redundant data.
+ *
+ * Mutates the internal state of DOVIContext to reflect the change.
+ * Returns 0 or a negative error code.
+ *
+ * This generates a fully formed RPU ready for inclusion in the bitstream,
+ * including the EMDF header (profile 10) or NAL encapsulation (otherwise).
+ */
+int ff_dovi_rpu_generate(DOVIContext *s, const AVDOVIMetadata *metadata,
+ uint8_t **out_rpu, int *out_size);
+
/**
* Internal helper function to guess the correct DV profile for HEVC.
*
diff --git a/libavcodec/dovi_rpuenc.c b/libavcodec/dovi_rpuenc.c
index 3ab4624a79..8b99bf12d9 100644
--- a/libavcodec/dovi_rpuenc.c
+++ b/libavcodec/dovi_rpuenc.c
@@ -21,10 +21,15 @@
*/
#include "libavutil/avassert.h"
+#include "libavutil/crc.h"
#include "libavutil/mem.h"
#include "avcodec.h"
#include "dovi_rpu.h"
+#include "itut35.h"
+#include "put_bits.h"
+#include "put_golomb.h"
+#include "refstruct.h"
static struct {
uint64_t pps; // maximum pixels per second
@@ -201,3 +206,542 @@ skip:
s->cfg = (AVDOVIDecoderConfigurationRecord) {0};
return 0;
}
+
+static inline void put_ue_coef(PutBitContext *pb, const AVDOVIRpuDataHeader *hdr,
+ uint64_t coef)
+{
+ union { uint32_t u32; float f32; } fpart;
+
+ switch (hdr->coef_data_type) {
+ case RPU_COEFF_FIXED:
+ set_ue_golomb(pb, coef >> hdr->coef_log2_denom);
+ put_bits64(pb, hdr->coef_log2_denom,
+ coef & ((1LL << hdr->coef_log2_denom) - 1));
+ break;
+ case RPU_COEFF_FLOAT:
+ fpart.f32 = coef / (float) (1LL << hdr->coef_log2_denom);
+ put_bits64(pb, hdr->coef_log2_denom, fpart.u32);
+ break;
+ }
+}
+
+static inline void put_se_coef(PutBitContext *pb, const AVDOVIRpuDataHeader *hdr,
+ uint64_t coef)
+{
+ union { uint32_t u32; float f32; } fpart;
+
+ switch (hdr->coef_data_type) {
+ case RPU_COEFF_FIXED:
+ set_se_golomb(pb, coef >> hdr->coef_log2_denom);
+ put_bits64(pb, hdr->coef_log2_denom,
+ coef & ((1LL << hdr->coef_log2_denom) - 1));
+ break;
+ case RPU_COEFF_FLOAT:
+ fpart.f32 = coef / (float) (1LL << hdr->coef_log2_denom);
+ put_bits64(pb, hdr->coef_log2_denom, fpart.u32);
+ break;
+ }
+}
+
+static int av_q2den(AVRational q, int den)
+{
+ if (q.den == den)
+ return q.num;
+ q = av_mul_q(q, av_make_q(den, 1));
+ return (q.num + (q.den >> 1)) / q.den;
+}
+
+static void generate_ext_v1(PutBitContext *pb, const AVDOVIDmData *dm)
+{
+ int ext_block_length, start_pos, pad_bits;
+
+ switch (dm->level) {
+ case 1: ext_block_length = 5; break;
+ case 2: ext_block_length = 11; break;
+ case 4: ext_block_length = 3; break;
+ case 5: ext_block_length = 7; break;
+ case 6: ext_block_length = 8; break;
+ case 255: ext_block_length = 6; break;
+ default: return;
+ }
+
+ set_ue_golomb(pb, ext_block_length);
+ put_bits(pb, 8, dm->level);
+ start_pos = put_bits_count(pb);
+
+ switch (dm->level) {
+ case 1:
+ put_bits(pb, 12, dm->l1.min_pq);
+ put_bits(pb, 12, dm->l1.max_pq);
+ put_bits(pb, 12, dm->l1.avg_pq);
+ break;
+ case 2:
+ put_bits(pb, 12, dm->l2.target_max_pq);
+ put_bits(pb, 12, dm->l2.trim_slope);
+ put_bits(pb, 12, dm->l2.trim_offset);
+ put_bits(pb, 12, dm->l2.trim_power);
+ put_bits(pb, 12, dm->l2.trim_chroma_weight);
+ put_bits(pb, 12, dm->l2.trim_saturation_gain);
+ put_bits(pb, 13, dm->l2.ms_weight + 8192);
+ break;
+ case 4:
+ put_bits(pb, 12, dm->l4.anchor_pq);
+ put_bits(pb, 12, dm->l4.anchor_power);
+ break;
+ case 5:
+ put_bits(pb, 13, dm->l5.left_offset);
+ put_bits(pb, 13, dm->l5.right_offset);
+ put_bits(pb, 13, dm->l5.top_offset);
+ put_bits(pb, 13, dm->l5.bottom_offset);
+ break;
+ case 6:
+ put_bits(pb, 16, dm->l6.max_luminance);
+ put_bits(pb, 16, dm->l6.min_luminance);
+ put_bits(pb, 16, dm->l6.max_cll);
+ put_bits(pb, 16, dm->l6.max_fall);
+ break;
+ case 255:
+ put_bits(pb, 8, dm->l255.dm_run_mode);
+ put_bits(pb, 8, dm->l255.dm_run_version);
+ for (int i = 0; i < 4; i++)
+ put_bits(pb, 8, dm->l255.dm_debug[i]);
+ break;
+ }
+
+ pad_bits = ext_block_length * 8 - (put_bits_count(pb) - start_pos);
+ av_assert1(pad_bits >= 0);
+ put_bits(pb, pad_bits, 0);
+}
+
+static void put_cie_xy(PutBitContext *pb, AVCIExy xy)
+{
+ const int denom = 32767;
+ put_sbits(pb, 16, av_q2den(xy.x, denom));
+ put_sbits(pb, 16, av_q2den(xy.y, denom));
+}
+
+#define ANY6(arr) (arr[0] || arr[1] || arr[2] || arr[3] || arr[4] || arr[5])
+#define ANY_XY(xy) (xy.x.num || xy.y.num)
+#define ANY_CSP(csp) (ANY_XY(csp.prim.r) || ANY_XY(csp.prim.g) || \
+ ANY_XY(csp.prim.b) || ANY_XY(csp.wp))
+
+static void generate_ext_v2(PutBitContext *pb, const AVDOVIDmData *dm)
+{
+ int ext_block_length, start_pos, pad_bits;
+
+ switch (dm->level) {
+ case 3: ext_block_length = 5; break;
+ case 8:
+ if (ANY6(dm->l8.hue_vector_field)) {
+ ext_block_length = 25;
+ } else if (ANY6(dm->l8.saturation_vector_field)) {
+ ext_block_length = 19;
+ } else if (dm->l8.clip_trim) {
+ ext_block_length = 13;
+ } else if (dm->l8.target_mid_contrast) {
+ ext_block_length = 12;
+ } else {
+ ext_block_length = 10;
+ }
+ break;
+ case 9:
+ if (ANY_CSP(dm->l9.source_display_primaries)) {
+ ext_block_length = 17;
+ } else {
+ ext_block_length = 1;
+ }
+ break;
+ case 10:
+ if (ANY_CSP(dm->l10.target_display_primaries)) {
+ ext_block_length = 21;
+ } else {
+ ext_block_length = 5;
+ }
+ break;
+ case 11: ext_block_length = 4; break;
+ case 254: ext_block_length = 2; break;
+ default: return;
+ }
+
+ set_ue_golomb(pb, ext_block_length);
+ put_bits(pb, 8, dm->level);
+ start_pos = put_bits_count(pb);
+
+ switch (dm->level) {
+ case 3:
+ put_bits(pb, 12, dm->l3.min_pq_offset);
+ put_bits(pb, 12, dm->l3.max_pq_offset);
+ put_bits(pb, 12, dm->l3.avg_pq_offset);
+ break;
+ case 8:
+ put_bits(pb, 8, dm->l8.target_display_index);
+ put_bits(pb, 12, dm->l8.trim_slope);
+ put_bits(pb, 12, dm->l8.trim_offset);
+ put_bits(pb, 12, dm->l8.trim_power);
+ put_bits(pb, 12, dm->l8.trim_chroma_weight);
+ put_bits(pb, 12, dm->l8.trim_saturation_gain);
+ put_bits(pb, 12, dm->l8.ms_weight + 8192);
+ if (ext_block_length < 12)
+ break;
+ put_bits(pb, 12, dm->l8.target_mid_contrast);
+ if (ext_block_length < 13)
+ break;
+ put_bits(pb, 12, dm->l8.clip_trim);
+ if (ext_block_length < 19)
+ break;
+ for (int i = 0; i < 6; i++)
+ put_bits(pb, 8, dm->l8.saturation_vector_field[i]);
+ if (ext_block_length < 25)
+ break;
+ for (int i = 0; i < 6; i++)
+ put_bits(pb, 8, dm->l8.hue_vector_field[i]);
+ break;
+ case 9:
+ put_bits(pb, 8, dm->l9.source_primary_index);
+ if (ext_block_length < 17)
+ break;
+ put_cie_xy(pb, dm->l9.source_display_primaries.prim.r);
+ put_cie_xy(pb, dm->l9.source_display_primaries.prim.g);
+ put_cie_xy(pb, dm->l9.source_display_primaries.prim.b);
+ put_cie_xy(pb, dm->l9.source_display_primaries.wp);
+ break;
+ case 10:
+ put_bits(pb, 8, dm->l10.target_display_index);
+ put_bits(pb, 12, dm->l10.target_max_pq);
+ put_bits(pb, 12, dm->l10.target_min_pq);
+ put_bits(pb, 8, dm->l10.target_primary_index);
+ if (ext_block_length < 21)
+ break;
+ put_cie_xy(pb, dm->l10.target_display_primaries.prim.r);
+ put_cie_xy(pb, dm->l10.target_display_primaries.prim.g);
+ put_cie_xy(pb, dm->l10.target_display_primaries.prim.b);
+ put_cie_xy(pb, dm->l10.target_display_primaries.wp);
+ break;
+ case 11:
+ put_bits(pb, 8, dm->l11.content_type);
+ put_bits(pb, 4, dm->l11.whitepoint);
+ put_bits(pb, 1, dm->l11.reference_mode_flag);
+ put_bits(pb, 3, 0); /* reserved */
+ put_bits(pb, 2, dm->l11.sharpness);
+ put_bits(pb, 2, dm->l11.noise_reduction);
+ put_bits(pb, 2, dm->l11.mpeg_noise_reduction);
+ put_bits(pb, 2, dm->l11.frame_rate_conversion);
+ put_bits(pb, 2, dm->l11.brightness);
+ put_bits(pb, 2, dm->l11.color);
+ break;
+ case 254:
+ put_bits(pb, 8, dm->l254.dm_mode);
+ put_bits(pb, 8, dm->l254.dm_version_index);
+ break;
+ }
+
+ pad_bits = ext_block_length * 8 - (put_bits_count(pb) - start_pos);
+ av_assert1(pad_bits >= 0);
+ put_bits(pb, pad_bits, 0);
+}
+
+int ff_dovi_rpu_generate(DOVIContext *s, const AVDOVIMetadata *metadata,
+ uint8_t **out_rpu, int *out_size)
+{
+ PutBitContext *pb = &(PutBitContext){0};
+ const AVDOVIRpuDataHeader *hdr;
+ const AVDOVIDataMapping *mapping;
+ const AVDOVIColorMetadata *color;
+ int vdr_dm_metadata_changed, vdr_rpu_id, use_prev_vdr_rpu, profile,
+ buffer_size, rpu_size, pad, zero_run;
+ int num_ext_blocks_v1, num_ext_blocks_v2;
+ uint32_t crc;
+ uint8_t *dst;
+ if (!metadata) {
+ *out_rpu = NULL;
+ *out_size = 0;
+ return 0;
+ }
+
+ hdr = av_dovi_get_header(metadata);
+ mapping = av_dovi_get_mapping(metadata);
+ color = av_dovi_get_color(metadata);
+ av_assert0(s->cfg.dv_profile);
+
+ if (hdr->rpu_type != 2) {
+ av_log(s->logctx, AV_LOG_ERROR, "Unhandled RPU type %"PRIu8"\n",
+ hdr->rpu_type);
+ return AVERROR_INVALIDDATA;
+ }
+
+ vdr_rpu_id = -1;
+ for (int i = 0; i <= DOVI_MAX_DM_ID; i++) {
+ if (s->vdr[i] && !memcmp(&s->vdr[i]->mapping, mapping, sizeof(*mapping))) {
+ vdr_rpu_id = i;
+ break;
+ } else if (vdr_rpu_id < 0 && (!s->vdr[i] || i == DOVI_MAX_DM_ID)) {
+ vdr_rpu_id = i;
+ }
+ }
+
+ if (!s->vdr[vdr_rpu_id]) {
+ s->vdr[vdr_rpu_id] = ff_refstruct_allocz(sizeof(DOVIVdr));
+ if (!s->vdr[vdr_rpu_id])
+ return AVERROR(ENOMEM);
+ }
+
+ if (!s->vdr[color->dm_metadata_id]) {
+ s->vdr[color->dm_metadata_id] = ff_refstruct_allocz(sizeof(DOVIVdr));
+ if (!s->vdr[color->dm_metadata_id])
+ return AVERROR(ENOMEM);
+ }
+
+ num_ext_blocks_v1 = num_ext_blocks_v2 = 0;
+ for (int i = 0; i < metadata->num_ext_blocks; i++) {
+ const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i);
+ switch (dm->level) {
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ case 6:
+ case 255:
+ num_ext_blocks_v1++;
+ break;
+ case 3:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 254:
+ num_ext_blocks_v2++;
+ break;
+ default:
+ av_log(s->logctx, AV_LOG_ERROR, "Invalid ext block level %d\n",
+ dm->level);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ vdr_dm_metadata_changed = !s->color || memcmp(s->color, color, sizeof(*color));
+ use_prev_vdr_rpu = !memcmp(&s->vdr[vdr_rpu_id]->mapping, mapping, sizeof(*mapping));
+
+ buffer_size = 12 /* vdr seq info */ + 5 /* CRC32 + terminator */;
+ buffer_size += num_ext_blocks_v1 * 13;
+ buffer_size += num_ext_blocks_v2 * 28;
+ if (!use_prev_vdr_rpu) {
+ buffer_size += 160;
+ for (int c = 0; c < 3; c++) {
+ for (int i = 0; i < mapping->curves[c].num_pivots - 1; i++) {
+ switch (mapping->curves[c].mapping_idc[i]) {
+ case AV_DOVI_MAPPING_POLYNOMIAL: buffer_size += 26; break;
+ case AV_DOVI_MAPPING_MMR: buffer_size += 177; break;
+ }
+ }
+ }
+ }
+ if (vdr_dm_metadata_changed)
+ buffer_size += 67;
+
+ av_fast_padded_malloc(&s->rpu_buf, &s->rpu_buf_sz, buffer_size);
+ if (!s->rpu_buf)
+ return AVERROR(ENOMEM);
+ init_put_bits(pb, s->rpu_buf, s->rpu_buf_sz);
+
+ /* RPU header */
+ put_bits(pb, 6, hdr->rpu_type);
+ put_bits(pb, 11, hdr->rpu_format);
+ put_bits(pb, 4, hdr->vdr_rpu_profile);
+ put_bits(pb, 4, hdr->vdr_rpu_level);
+ put_bits(pb, 1, 1); /* vdr_seq_info_present */
+ put_bits(pb, 1, hdr->chroma_resampling_explicit_filter_flag);
+ put_bits(pb, 2, hdr->coef_data_type);
+ if (hdr->coef_data_type == RPU_COEFF_FIXED)
+ set_ue_golomb(pb, hdr->coef_log2_denom);
+ put_bits(pb, 2, hdr->vdr_rpu_normalized_idc);
+ put_bits(pb, 1, hdr->bl_video_full_range_flag);
+ if ((hdr->rpu_format & 0x700) == 0) {
+ set_ue_golomb(pb, hdr->bl_bit_depth - 8);
+ set_ue_golomb(pb, hdr->el_bit_depth - 8);
+ set_ue_golomb(pb, hdr->vdr_bit_depth - 8);
+ put_bits(pb, 1, hdr->spatial_resampling_filter_flag);
+ put_bits(pb, 3, 0); /* reserved_zero_3bits */
+ put_bits(pb, 1, hdr->el_spatial_resampling_filter_flag);
+ put_bits(pb, 1, hdr->disable_residual_flag);
+ }
+ s->header = *hdr;
+
+ put_bits(pb, 1, vdr_dm_metadata_changed);
+ put_bits(pb, 1, use_prev_vdr_rpu);
+ set_ue_golomb(pb, vdr_rpu_id);
+ s->mapping = &s->vdr[vdr_rpu_id]->mapping;
+
+ if (!use_prev_vdr_rpu) {
+ set_ue_golomb(pb, mapping->mapping_color_space);
+ set_ue_golomb(pb, mapping->mapping_chroma_format_idc);
+ for (int c = 0; c < 3; c++) {
+ const AVDOVIReshapingCurve *curve = &mapping->curves[c];
+ int prev = 0;
+ set_ue_golomb(pb, curve->num_pivots - 2);
+ for (int i = 0; i < curve->num_pivots; i++) {
+ put_bits(pb, hdr->bl_bit_depth, curve->pivots[i] - prev);
+ prev = curve->pivots[i];
+ }
+ }
+
+ if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) {
+ put_bits(pb, 3, mapping->nlq_method_idc);
+ put_bits(pb, hdr->bl_bit_depth, mapping->nlq_pivots[0]);
+ put_bits(pb, hdr->bl_bit_depth, mapping->nlq_pivots[1] - mapping->nlq_pivots[0]);
+ }
+
+ set_ue_golomb(pb, mapping->num_x_partitions - 1);
+ set_ue_golomb(pb, mapping->num_y_partitions - 1);
+
+ for (int c = 0; c < 3; c++) {
+ const AVDOVIReshapingCurve *curve = &mapping->curves[c];
+ for (int i = 0; i < curve->num_pivots - 1; i++) {
+ set_ue_golomb(pb, curve->mapping_idc[i]);
+ switch (curve->mapping_idc[i]) {
+ case AV_DOVI_MAPPING_POLYNOMIAL: {
+ set_ue_golomb(pb, curve->poly_order[i] - 1);
+ if (curve->poly_order[i] == 1)
+ put_bits(pb, 1, 0); /* linear_interp_flag */
+ for (int k = 0; k <= curve->poly_order[i]; k++)
+ put_se_coef(pb, hdr, curve->poly_coef[i][k]);
+ break;
+ }
+ case AV_DOVI_MAPPING_MMR: {
+ put_bits(pb, 2, curve->mmr_order[i] - 1);
+ put_se_coef(pb, hdr, curve->mmr_constant[i]);
+ for (int j = 0; j < curve->mmr_order[i]; j++) {
+ for (int k = 0; k < 7; k++)
+ put_se_coef(pb, hdr, curve->mmr_coef[i][j][k]);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) {
+ for (int c = 0; c < 3; c++) {
+ const AVDOVINLQParams *nlq = &mapping->nlq[c];
+ put_bits(pb, hdr->el_bit_depth, nlq->nlq_offset);
+ put_ue_coef(pb, hdr, nlq->vdr_in_max);
+ switch (mapping->nlq_method_idc) {
+ case AV_DOVI_NLQ_LINEAR_DZ:
+ put_ue_coef(pb, hdr, nlq->linear_deadzone_slope);
+ put_ue_coef(pb, hdr, nlq->linear_deadzone_threshold);
+ break;
+ }
+ }
+ }
+
+ memcpy(&s->vdr[vdr_rpu_id]->mapping, mapping, sizeof(*mapping));
+ }
+
+ if (vdr_dm_metadata_changed) {
+ const int denom = profile == 4 ? (1 << 30) : (1 << 28);
+ set_ue_golomb(pb, color->dm_metadata_id); /* affected_dm_id */
+ set_ue_golomb(pb, color->dm_metadata_id); /* current_dm_id */
+ set_ue_golomb(pb, color->scene_refresh_flag);
+ for (int i = 0; i < 9; i++)
+ put_sbits(pb, 16, av_q2den(color->ycc_to_rgb_matrix[i], 1 << 13));
+ for (int i = 0; i < 3; i++)
+ put_bits32(pb, av_q2den(color->ycc_to_rgb_offset[i], denom));
+ for (int i = 0; i < 9; i++)
+ put_sbits(pb, 16, av_q2den(color->rgb_to_lms_matrix[i], 1 << 14));
+ put_bits(pb, 16, color->signal_eotf);
+ put_bits(pb, 16, color->signal_eotf_param0);
+ put_bits(pb, 16, color->signal_eotf_param1);
+ put_bits32(pb, color->signal_eotf_param2);
+ put_bits(pb, 5, color->signal_bit_depth);
+ put_bits(pb, 2, color->signal_color_space);
+ put_bits(pb, 2, color->signal_chroma_format);
+ put_bits(pb, 2, color->signal_full_range_flag);
+ put_bits(pb, 12, color->source_min_pq);
+ put_bits(pb, 12, color->source_max_pq);
+ put_bits(pb, 10, color->source_diagonal);
+
+ memcpy(&s->vdr[color->dm_metadata_id]->color, color, sizeof(*color));
+ s->color = &s->vdr[color->dm_metadata_id]->color;
+ }
+
+ set_ue_golomb(pb, num_ext_blocks_v1);
+ align_put_bits(pb);
+ for (int i = 0; i < metadata->num_ext_blocks; i++)
+ generate_ext_v1(pb, av_dovi_get_ext(metadata, i));
+
+ if (num_ext_blocks_v2) {
+ set_ue_golomb(pb, num_ext_blocks_v2);
+ align_put_bits(pb);
+ for (int i = 0; i < metadata->num_ext_blocks; i++)
+ generate_ext_v2(pb, av_dovi_get_ext(metadata, i));
+ }
+
+ flush_put_bits(pb);
+ crc = av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1,
+ s->rpu_buf, put_bytes_output(pb)));
+ put_bits32(pb, crc);
+ put_bits(pb, 8, 0x80); /* terminator */
+ flush_put_bits(pb);
+
+ rpu_size = put_bytes_output(pb);
+ switch (s->cfg.dv_profile) {
+ case 10:
+ /* AV1 uses T.35 OBU with EMDF header */
+ *out_rpu = av_malloc(rpu_size + 15);
+ if (!*out_rpu)
+ return AVERROR(ENOMEM);
+ init_put_bits(pb, *out_rpu, rpu_size + 15);
+ put_bits(pb, 8, ITU_T_T35_COUNTRY_CODE_US);
+ put_bits(pb, 16, ITU_T_T35_PROVIDER_CODE_DOLBY);
+ put_bits32(pb, 0x800); /* provider_oriented_code */
+ put_bits(pb, 27, 0x01be6841u); /* fixed EMDF header, see above */
+ if (rpu_size > 0xFF) {
+ av_assert2(rpu_size <= 0x10000);
+ put_bits(pb, 8, (rpu_size >> 8) - 1);
+ put_bits(pb, 1, 1); /* read_more */
+ put_bits(pb, 8, rpu_size & 0xFF);
+ put_bits(pb, 1, 0);
+ } else {
+ put_bits(pb, 8, rpu_size);
+ put_bits(pb, 1, 0);
+ }
+ ff_copy_bits(pb, s->rpu_buf, rpu_size * 8);
+ put_bits(pb, 17, 0x400); /* emdf payload id + emdf_protection */
+
+ pad = pb->bit_left & 7;
+ put_bits(pb, pad, (1 << pad) - 1); /* pad to next byte with 1 bits */
+ flush_put_bits(pb);
+ *out_size = put_bytes_output(pb);
+ return 0;
+
+ case 5:
+ case 8:
+ *out_rpu = dst = av_malloc(1 + rpu_size * 3 / 2); /* worst case */
+ if (!*out_rpu)
+ return AVERROR(ENOMEM);
+ *dst++ = 25; /* NAL prefix */
+ zero_run = 0;
+ for (int i = 0; i < rpu_size; i++) {
+ if (zero_run < 2) {
+ if (s->rpu_buf[i] == 0) {
+ zero_run++;
+ } else {
+ zero_run = 0;
+ }
+ } else {
+ if ((s->rpu_buf[i] & ~3) == 0) {
+ /* emulation prevention */
+ *dst++ = 3;
+ }
+ zero_run = s->rpu_buf[i] == 0;
+ }
+ *dst++ = s->rpu_buf[i];
+ }
+ *out_size = dst - *out_rpu;
+ return 0;
+
+ default:
+ /* Should be unreachable */
+ av_assert0(0);
+ return AVERROR_BUG;
+ }
+}
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 10/13] avformat/movenc: warn if dovi cfg ignored
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (8 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 09/13] avcodec/dovi_rpuenc: add ff_dovi_rpu_generate() Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 11/13] avcodec/libaomenc: implement dolby vision coding Niklas Haas
` (3 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
Since this is guarded behind strict unofficial, we should warn if the
user feeds a dolby vision stream to this muxer, as it will otherwise
result in a broken file.
---
libavformat/movenc.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 6ede5119f0..1a0502bbb1 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -2526,16 +2526,21 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
const AVPacketSideData *spherical_mapping = av_packet_side_data_get(track->st->codecpar->coded_side_data,
track->st->codecpar->nb_coded_side_data,
AV_PKT_DATA_SPHERICAL);
- const AVPacketSideData *dovi = av_packet_side_data_get(track->st->codecpar->coded_side_data,
- track->st->codecpar->nb_coded_side_data,
- AV_PKT_DATA_DOVI_CONF);
-
if (stereo_3d)
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 (track->mode == MODE_MP4) {
+ const AVPacketSideData *dovi = av_packet_side_data_get(track->st->codecpar->coded_side_data,
+ track->st->codecpar->nb_coded_side_data,
+ AV_PKT_DATA_DOVI_CONF);
+ if (dovi && mov->fc->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {
mov_write_dvcc_dvvc_tag(s, pb, (AVDOVIDecoderConfigurationRecord *)dovi->data);
+ } else if (dovi) {
+ av_log(mov->fc, AV_LOG_WARNING, "Not writing 'dvcC'/'dvvC' box. Requires -strict unofficial.\n");
+ }
}
if (track->par->sample_aspect_ratio.den && track->par->sample_aspect_ratio.num) {
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 11/13] avcodec/libaomenc: implement dolby vision coding
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (9 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 10/13] avformat/movenc: warn if dovi cfg ignored Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 12/13] avcodec/libx265: " Niklas Haas
` (2 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
---
configure | 2 +-
libavcodec/libaomenc.c | 28 ++++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/configure b/configure
index d25d0f6907..bf63c831af 100755
--- a/configure
+++ b/configure
@@ -3485,7 +3485,7 @@ prores_videotoolbox_encoder_deps="pthreads"
prores_videotoolbox_encoder_select="videotoolbox_encoder"
libaom_av1_decoder_deps="libaom"
libaom_av1_encoder_deps="libaom"
-libaom_av1_encoder_select="extract_extradata_bsf"
+libaom_av1_encoder_select="extract_extradata_bsf dovi_rpuenc"
libaribb24_decoder_deps="libaribb24"
libaribcaption_decoder_deps="libaribcaption"
libcelt_decoder_deps="libcelt"
diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
index 4a71bba9c9..b43a902a38 100644
--- a/libavcodec/libaomenc.c
+++ b/libavcodec/libaomenc.c
@@ -43,6 +43,7 @@
#include "avcodec.h"
#include "bsf.h"
#include "codec_internal.h"
+#include "dovi_rpu.h"
#include "encode.h"
#include "internal.h"
#include "libaom.h"
@@ -70,6 +71,7 @@ struct FrameListData {
typedef struct AOMEncoderContext {
AVClass *class;
AVBSFContext *bsf;
+ DOVIContext dovi;
struct aom_codec_ctx encoder;
struct aom_image rawimg;
struct aom_fixed_buf twopass_stats;
@@ -421,6 +423,7 @@ static av_cold int aom_free(AVCodecContext *avctx)
av_freep(&avctx->stats_out);
free_frame_list(ctx->coded_frame_list);
av_bsf_free(&ctx->bsf);
+ ff_dovi_ctx_unref(&ctx->dovi);
return 0;
}
@@ -989,6 +992,10 @@ static av_cold int aom_init(AVCodecContext *avctx,
if (!cpb_props)
return AVERROR(ENOMEM);
+ ctx->dovi.logctx = avctx;
+ if ((res = ff_dovi_configure(&ctx->dovi, avctx)) < 0)
+ return res;
+
if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
const AVBitStreamFilter *filter = av_bsf_get_by_name("extract_extradata");
int ret;
@@ -1242,6 +1249,7 @@ static int aom_encode(AVCodecContext *avctx, AVPacket *pkt,
unsigned long duration = 0;
int res, coded_size;
aom_enc_frame_flags_t flags = 0;
+ AVFrameSideData *sd;
if (frame) {
rawimg = &ctx->rawimg;
@@ -1279,6 +1287,24 @@ FF_ENABLE_DEPRECATION_WARNINGS
break;
}
+ sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DOVI_METADATA);
+ if (ctx->dovi.cfg.dv_profile && sd) {
+ const AVDOVIMetadata *metadata = (const AVDOVIMetadata *)sd->data;
+ uint8_t *t35;
+ int size;
+ if ((res = ff_dovi_rpu_generate(&ctx->dovi, metadata, &t35, &size)) < 0)
+ return res;
+ res = aom_img_add_metadata(rawimg, OBU_METADATA_TYPE_ITUT_T35,
+ t35, size, AOM_MIF_ANY_FRAME);
+ av_free(t35);
+ if (res != AOM_CODEC_OK)
+ return AVERROR(ENOMEM);
+ } else if (ctx->dovi.cfg.dv_profile) {
+ av_log(avctx, AV_LOG_ERROR, "Dolby Vision enabled, but received frame "
+ "without AV_FRAME_DATA_DOVI_METADATA\n");
+ return AVERROR_INVALIDDATA;
+ }
+
if (frame->pict_type == AV_PICTURE_TYPE_I)
flags |= AOM_EFLAG_FORCE_KF;
}
@@ -1459,6 +1485,8 @@ static const AVOption options[] = {
{ "ssim", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AOM_TUNE_SSIM}, 0, 0, VE, .unit = "tune"},
FF_AV1_PROFILE_OPTS
{ "still-picture", "Encode in single frame mode (typically used for still AVIF images).", OFFSET(still_picture), AV_OPT_TYPE_BOOL, {.i64 = 0}, -1, 1, VE },
+ { "dolbyvision", "Enable Dolby Vision RPU coding", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = FF_DOVI_AUTOMATIC }, -1, 1, VE, .unit = "dovi" },
+ { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DOVI_AUTOMATIC}, .flags = VE, .unit = "dovi" },
{ "enable-rect-partitions", "Enable rectangular partitions", OFFSET(enable_rect_partitions), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE},
{ "enable-1to4-partitions", "Enable 1:4/4:1 partitions", OFFSET(enable_1to4_partitions), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE},
{ "enable-ab-partitions", "Enable ab shape partitions", OFFSET(enable_ab_partitions), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE},
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 12/13] avcodec/libx265: implement dolby vision coding
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (10 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 11/13] avcodec/libaomenc: implement dolby vision coding Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 13/13] avcodec/libsvtav1: " Niklas Haas
2024-04-17 12:22 ` [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
libx265 supports these natively, we just need to attach the generated
NALs to the x265picture, as well as setting the appropriate DV profile.
---
configure | 2 +-
libavcodec/libx265.c | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/configure b/configure
index bf63c831af..cce37bb32e 100755
--- a/configure
+++ b/configure
@@ -3553,7 +3553,7 @@ libx264_encoder_select="atsc_a53 golomb"
libx264rgb_encoder_deps="libx264"
libx264rgb_encoder_select="libx264_encoder"
libx265_encoder_deps="libx265"
-libx265_encoder_select="atsc_a53"
+libx265_encoder_select="atsc_a53 dovi_rpueenc"
libxavs_encoder_deps="libxavs"
libxavs2_encoder_deps="libxavs2"
libxevd_decoder_deps="libxevd"
diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c
index 0645cd2045..c4ceffff5d 100644
--- a/libavcodec/libx265.c
+++ b/libavcodec/libx265.c
@@ -36,6 +36,7 @@
#include "libavutil/pixdesc.h"
#include "avcodec.h"
#include "codec_internal.h"
+#include "dovi_rpu.h"
#include "encode.h"
#include "packet_internal.h"
#include "atsc_a53.h"
@@ -78,6 +79,8 @@ typedef struct libx265Context {
* encounter a frame with ROI side data.
*/
int roi_warned;
+
+ DOVIContext dovi;
} libx265Context;
static int is_keyframe(NalUnitType naltype)
@@ -143,6 +146,8 @@ static av_cold int libx265_encode_close(AVCodecContext *avctx)
if (ctx->encoder)
ctx->api->encoder_close(ctx->encoder);
+ ff_dovi_ctx_unref(&ctx->dovi);
+
return 0;
}
@@ -526,6 +531,14 @@ FF_ENABLE_DEPRECATION_WARNINGS
}
}
+#if X265_BUILD >= 167
+ ctx->dovi.logctx = avctx;
+ if ((ret = ff_dovi_configure(&ctx->dovi, avctx)) < 0)
+ return ret;
+ ctx->params->dolbyProfile = ctx->dovi.cfg.dv_profile * 10 +
+ ctx->dovi.cfg.dv_bl_signal_compatibility_id;
+#endif
+
ctx->encoder = ctx->api->encoder_open(ctx->params);
if (!ctx->encoder) {
av_log(avctx, AV_LOG_ERROR, "Cannot open libx265 encoder.\n");
@@ -629,6 +642,10 @@ static void free_picture(libx265Context *ctx, x265_picture *pic)
for (int i = 0; i < sei->numPayloads; i++)
av_free(sei->payloads[i].payload);
+#if X265_BUILD >= 167
+ av_free(pic->rpu.payload);
+#endif
+
if (pic->userData) {
int idx = (int)(intptr_t)pic->userData - 1;
rd_release(ctx, idx);
@@ -660,6 +677,7 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
sei->numPayloads = 0;
if (pic) {
+ AVFrameSideData *sd;
ReorderedData *rd;
int rd_idx;
@@ -760,6 +778,24 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
sei->numPayloads++;
}
}
+
+#if X265_BUILD >= 167
+ sd = av_frame_get_side_data(pic, AV_FRAME_DATA_DOVI_METADATA);
+ if (ctx->dovi.cfg.dv_profile && sd) {
+ const AVDOVIMetadata *metadata = (const AVDOVIMetadata *)sd->data;
+ ret = ff_dovi_rpu_generate(&ctx->dovi, metadata, &x265pic.rpu.payload,
+ &x265pic.rpu.payloadSize);
+ if (ret < 0) {
+ free_picture(ctx, &x265pic);
+ return ret;
+ }
+ } else if (ctx->dovi.cfg.dv_profile) {
+ av_log(avctx, AV_LOG_ERROR, "Dolby Vision enabled, but received frame "
+ "without AV_FRAME_DATA_DOVI_METADATA");
+ free_picture(ctx, &x265pic);
+ return AVERROR_INVALIDDATA;
+ }
+#endif
}
ret = ctx->api->encoder_encode(ctx->encoder, &nal, &nnal,
@@ -914,6 +950,10 @@ static const AVOption options[] = {
{ "udu_sei", "Use user data unregistered SEI if available", OFFSET(udu_sei), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
{ "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE },
{ "x265-params", "set the x265 configuration using a :-separated list of key=value parameters", OFFSET(x265_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE },
+#if X265_BUILD >= 167
+ { "dolbyvision", "Enable Dolby Vision RPU coding", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = FF_DOVI_AUTOMATIC }, -1, 1, VE, .unit = "dovi" },
+ { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DOVI_AUTOMATIC}, .flags = VE, .unit = "dovi" },
+#endif
{ NULL }
};
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* [FFmpeg-devel] [PATCH v3 13/13] avcodec/libsvtav1: implement dolby vision coding
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (11 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 12/13] avcodec/libx265: " Niklas Haas
@ 2024-04-12 11:35 ` Niklas Haas
2024-04-17 12:22 ` [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-12 11:35 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
---
configure | 1 +
libavcodec/libsvtav1.c | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/configure b/configure
index cce37bb32e..6fbd0e44ff 100755
--- a/configure
+++ b/configure
@@ -3534,6 +3534,7 @@ libspeex_decoder_deps="libspeex"
libspeex_encoder_deps="libspeex"
libspeex_encoder_select="audio_frame_queue"
libsvtav1_encoder_deps="libsvtav1"
+libsvtav1_encoder_select="dovi_rpueenc"
libtheora_encoder_deps="libtheora"
libtwolame_encoder_deps="libtwolame"
libuavs3d_decoder_deps="libuavs3d"
diff --git a/libavcodec/libsvtav1.c b/libavcodec/libsvtav1.c
index 6ff893cf10..9bc165f0cf 100644
--- a/libavcodec/libsvtav1.c
+++ b/libavcodec/libsvtav1.c
@@ -23,6 +23,7 @@
#include <stdint.h>
#include <EbSvtAv1ErrorCodes.h>
#include <EbSvtAv1Enc.h>
+#include <EbSvtAv1Metadata.h>
#include "libavutil/common.h"
#include "libavutil/frame.h"
@@ -35,6 +36,7 @@
#include "libavutil/avassert.h"
#include "codec_internal.h"
+#include "dovi_rpu.h"
#include "encode.h"
#include "packet_internal.h"
#include "avcodec.h"
@@ -62,6 +64,8 @@ typedef struct SvtContext {
EOS_STATUS eos_flag;
+ DOVIContext dovi;
+
// User options.
AVDictionary *svtav1_opts;
int enc_mode;
@@ -418,6 +422,7 @@ static int read_in_data(EbSvtAv1EncConfiguration *param, const AVFrame *frame,
in_data->cr_stride = AV_CEIL_RSHIFT(frame->linesize[2], bytes_shift);
header_ptr->n_filled_len = frame_size;
+ svt_metadata_array_free(&header_ptr->metadata);
return 0;
}
@@ -451,6 +456,11 @@ static av_cold int eb_enc_init(AVCodecContext *avctx)
return svt_print_error(avctx, svt_ret, "Error initializing encoder");
}
+ svt_enc->dovi.logctx = avctx;
+ ret = ff_dovi_configure(&svt_enc->dovi, avctx);
+ if (ret < 0)
+ return ret;
+
if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
EbBufferHeaderType *headerPtr = NULL;
@@ -486,6 +496,7 @@ static int eb_send_frame(AVCodecContext *avctx, const AVFrame *frame)
{
SvtContext *svt_enc = avctx->priv_data;
EbBufferHeaderType *headerPtr = svt_enc->in_buf;
+ AVFrameSideData *sd;
EbErrorType svt_ret;
int ret;
@@ -525,6 +536,24 @@ static int eb_send_frame(AVCodecContext *avctx, const AVFrame *frame)
if (avctx->gop_size == 1)
headerPtr->pic_type = EB_AV1_KEY_PICTURE;
+ sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DOVI_METADATA);
+ if (svt_enc->dovi.cfg.dv_profile && sd) {
+ const AVDOVIMetadata *metadata = (const AVDOVIMetadata *)sd->data;
+ uint8_t *t35;
+ int size;
+ if ((ret = ff_dovi_rpu_generate(&svt_enc->dovi, metadata, &t35, &size)) < 0)
+ return ret;
+ ret = svt_add_metadata(headerPtr, EB_AV1_METADATA_TYPE_ITUT_T35, t35, size);
+ av_free(t35);
+ if (ret < 0)
+ return AVERROR(ENOMEM);
+ } else if (svt_enc->dovi.cfg.dv_profile) {
+ av_log(avctx, AV_LOG_ERROR, "Dolby Vision enabled, but received frame "
+ "without AV_FRAME_DATA_DOVI_METADATA\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+
svt_ret = svt_av1_enc_send_picture(svt_enc->svt_handle, headerPtr);
if (svt_ret != EB_ErrorNone)
return svt_print_error(avctx, svt_ret, "Error sending a frame to encoder");
@@ -649,11 +678,13 @@ static av_cold int eb_enc_close(AVCodecContext *avctx)
}
if (svt_enc->in_buf) {
av_free(svt_enc->in_buf->p_buffer);
+ svt_metadata_array_free(&svt_enc->in_buf->metadata);
av_freep(&svt_enc->in_buf);
}
av_buffer_pool_uninit(&svt_enc->pool);
av_frame_free(&svt_enc->frame);
+ ff_dovi_ctx_unref(&svt_enc->dovi);
return 0;
}
@@ -700,6 +731,9 @@ static const AVOption options[] = {
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 63, VE },
{ "svtav1-params", "Set the SVT-AV1 configuration using a :-separated list of key=value parameters", OFFSET(svtav1_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE },
+ { "dolbyvision", "Enable Dolby Vision RPU coding", OFFSET(dovi.enable), AV_OPT_TYPE_BOOL, {.i64 = FF_DOVI_AUTOMATIC }, -1, 1, VE, .unit = "dovi" },
+ { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DOVI_AUTOMATIC}, .flags = VE, .unit = "dovi" },
+
{NULL},
};
--
2.44.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding
2024-04-12 11:35 [FFmpeg-devel] [PATCH v3 00/13] avcodec: add Dolby Vision encoding Niklas Haas
` (12 preceding siblings ...)
2024-04-12 11:35 ` [FFmpeg-devel] [PATCH v3 13/13] avcodec/libsvtav1: " Niklas Haas
@ 2024-04-17 12:22 ` Niklas Haas
13 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-17 12:22 UTC (permalink / raw)
To: ffmpeg-devel
On Fri, 12 Apr 2024 13:35:14 +0200 Niklas Haas <ffmpeg@haasn.xyz> wrote:
> Changes since v2:
> - Split up dovi_rpu.c into dovi_rpudec.c and dovi_rpueenc.
> - Added missing dependencies of encoders onto dovi_rpueenc
> - Clarified and documented semantics of guess_profile()
> - Changed misleading commit message
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Ping for review
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 17+ messages in thread