* [FFmpeg-devel] [PATCH v2 01/11] avcodec/dovi_rpu: store entire config record
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 02/11] avcodec/dovi_rpu: properly replace context header Niklas Haas
` (9 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 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 824725c031f..4c1405df779 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 9f7a6b00664..d95c7e03af9 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 9f26f332ceb..9a68e45bf1b 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 727b02f0f40..4bc9e2afc1d 100644
--- a/libavcodec/hevcdec.c
+++ b/libavcodec/hevcdec.c
@@ -3364,14 +3364,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;
@@ -3661,8 +3660,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 93c77ed9c6c..09fe767fb86 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 v2 02/11] avcodec/dovi_rpu: properly replace context header
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 01/11] avcodec/dovi_rpu: store entire config record Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 15:36 ` Andreas Rheinhardt
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 03/11] avcodec/dovi_rpu: clarify error on missing RPU VDR Niklas Haas
` (8 subsequent siblings)
10 siblings, 1 reply; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 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 a sub-thread to the main thread.
---
libavcodec/dovi_rpu.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index d95c7e03af9..bfb7b9fe661 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
* Re: [FFmpeg-devel] [PATCH v2 02/11] avcodec/dovi_rpu: properly replace context header
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 02/11] avcodec/dovi_rpu: properly replace context header Niklas Haas
@ 2024-04-09 15:36 ` Andreas Rheinhardt
2024-04-09 16:02 ` Niklas Haas
0 siblings, 1 reply; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-04-09 15:36 UTC (permalink / raw)
To: ffmpeg-devel
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 a sub-thread to the main thread.
> ---
Sub-thread to the main thread? update_thread_context is not called with
the main (user-facing) AVCodecContext.
> libavcodec/dovi_rpu.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
> index d95c7e03af9..bfb7b9fe661 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++)
_______________________________________________
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 v2 02/11] avcodec/dovi_rpu: properly replace context header
2024-04-09 15:36 ` Andreas Rheinhardt
@ 2024-04-09 16:02 ` Niklas Haas
0 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 16:02 UTC (permalink / raw)
To: ffmpeg-devel
On Tue, 09 Apr 2024 17:36:30 +0200 Andreas Rheinhardt <andreas.rheinhardt@outlook.com> wrote:
> 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 a sub-thread to the main thread.
> > ---
>
> Sub-thread to the main thread? update_thread_context is not called with
> the main (user-facing) AVCodecContext.
Changed to "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 d95c7e03af9..bfb7b9fe661 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++)
>
> _______________________________________________
> 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 v2 03/11] avcodec/dovi_rpu: clarify error on missing RPU VDR
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 01/11] avcodec/dovi_rpu: store entire config record Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 02/11] avcodec/dovi_rpu: properly replace context header Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 04/11] avcodec/dovi_rpu: clarify semantics of guess_profile() Niklas Haas
` (7 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 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 bfb7b9fe661..267e52ceb66 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 v2 04/11] avcodec/dovi_rpu: clarify semantics of guess_profile()
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
` (2 preceding siblings ...)
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 03/11] avcodec/dovi_rpu: clarify error on missing RPU VDR Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 15:37 ` Andreas Rheinhardt
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 05/11] avcodec/dovi_rpu: add ff_dovi_configure() Niklas Haas
` (6 subsequent siblings)
10 siblings, 1 reply; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
This is based on HEVC only, H.264/AV1 use their own (hopefully correctly
signalled) profiles.
---
libavcodec/dovi_rpu.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index 267e52ceb66..4da711d763e 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -121,7 +121,8 @@ int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
return 0;
}
-static int guess_profile(const AVDOVIRpuDataHeader *hdr)
+/* Note: Only works for HEVC */
+static int guess_hevc_profile(const AVDOVIRpuDataHeader *hdr)
{
switch (hdr->vdr_rpu_profile) {
case 0:
@@ -510,7 +511,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 : guess_hevc_profile(hdr);
if (profile == 5 && use_nlq) {
av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
goto fail;
--
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 v2 04/11] avcodec/dovi_rpu: clarify semantics of guess_profile()
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 04/11] avcodec/dovi_rpu: clarify semantics of guess_profile() Niklas Haas
@ 2024-04-09 15:37 ` Andreas Rheinhardt
2024-04-09 16:01 ` Niklas Haas
0 siblings, 1 reply; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-04-09 15:37 UTC (permalink / raw)
To: ffmpeg-devel
Niklas Haas:
> From: Niklas Haas <git@haasn.dev>
>
> This is based on HEVC only, H.264/AV1 use their own (hopefully correctly
> signalled) profiles.
> ---
> libavcodec/dovi_rpu.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
> index 267e52ceb66..4da711d763e 100644
> --- a/libavcodec/dovi_rpu.c
> +++ b/libavcodec/dovi_rpu.c
> @@ -121,7 +121,8 @@ int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
> return 0;
> }
>
> -static int guess_profile(const AVDOVIRpuDataHeader *hdr)
> +/* Note: Only works for HEVC */
> +static int guess_hevc_profile(const AVDOVIRpuDataHeader *hdr)
> {
> switch (hdr->vdr_rpu_profile) {
> case 0:
> @@ -510,7 +511,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 : guess_hevc_profile(hdr);
> if (profile == 5 && use_nlq) {
> av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
> goto fail;
Is guess_hevc_profile only called for HEVC?
- 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 v2 04/11] avcodec/dovi_rpu: clarify semantics of guess_profile()
2024-04-09 15:37 ` Andreas Rheinhardt
@ 2024-04-09 16:01 ` Niklas Haas
0 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 16:01 UTC (permalink / raw)
To: ffmpeg-devel
On Tue, 09 Apr 2024 17:37:32 +0200 Andreas Rheinhardt <andreas.rheinhardt@outlook.com> wrote:
> Niklas Haas:
> > From: Niklas Haas <git@haasn.dev>
> >
> > This is based on HEVC only, H.264/AV1 use their own (hopefully correctly
> > signalled) profiles.
> > ---
> > libavcodec/dovi_rpu.c | 5 +++--
> > 1 file changed, 3 insertions(+), 2 deletions(-)
> >
> > diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
> > index 267e52ceb66..4da711d763e 100644
> > --- a/libavcodec/dovi_rpu.c
> > +++ b/libavcodec/dovi_rpu.c
> > @@ -121,7 +121,8 @@ int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
> > return 0;
> > }
> >
> > -static int guess_profile(const AVDOVIRpuDataHeader *hdr)
> > +/* Note: Only works for HEVC */
> > +static int guess_hevc_profile(const AVDOVIRpuDataHeader *hdr)
> > {
> > switch (hdr->vdr_rpu_profile) {
> > case 0:
> > @@ -510,7 +511,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 : guess_hevc_profile(hdr);
> > if (profile == 5 && use_nlq) {
> > av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
> > goto fail;
>
> Is guess_hevc_profile only called for HEVC?
Yes. All non-HEVC codecs explicitly override s->cfg.dv_profile before
calling into this function.
But probably we should document that more clearly somewhere.
>
> - 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".
_______________________________________________
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 v2 05/11] avcodec/dovi_rpu: add ff_dovi_configure()
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
` (3 preceding siblings ...)
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 04/11] avcodec/dovi_rpu: clarify semantics of guess_profile() Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 16:00 ` Andreas Rheinhardt
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 06/11] avcodec/dovi_rpu: make `enable` also affect decoding Niklas Haas
` (5 subsequent siblings)
10 siblings, 1 reply; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 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.
---
libavcodec/dovi_rpu.c | 176 ++++++++++++++++++++++++++++++++++++++++++
libavcodec/dovi_rpu.h | 23 +++++-
2 files changed, 198 insertions(+), 1 deletion(-)
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index 4da711d763e..d3a284c150d 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -144,6 +144,182 @@ static int guess_hevc_profile(const AVDOVIRpuDataHeader *hdr)
return 0; /* unknown */
}
+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 ? guess_hevc_profile(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;
+}
+
static inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
{
uint64_t ipart;
diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
index 9a68e45bf1b..56395707369 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
@@ -96,4 +107,14 @@ 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);
+
#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
* Re: [FFmpeg-devel] [PATCH v2 05/11] avcodec/dovi_rpu: add ff_dovi_configure()
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 05/11] avcodec/dovi_rpu: add ff_dovi_configure() Niklas Haas
@ 2024-04-09 16:00 ` Andreas Rheinhardt
0 siblings, 0 replies; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-04-09 16:00 UTC (permalink / raw)
To: ffmpeg-devel
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.
> ---
> libavcodec/dovi_rpu.c | 176 ++++++++++++++++++++++++++++++++++++++++++
> libavcodec/dovi_rpu.h | 23 +++++-
> 2 files changed, 198 insertions(+), 1 deletion(-)
>
> diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
> index 4da711d763e..d3a284c150d 100644
> --- a/libavcodec/dovi_rpu.c
> +++ b/libavcodec/dovi_rpu.c
> @@ -144,6 +144,182 @@ static int guess_hevc_profile(const AVDOVIRpuDataHeader *hdr)
> return 0; /* unknown */
> }
>
> +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 ? guess_hevc_profile(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;
> +}
> +
> static inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
> {
> uint64_t ipart;
> diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
> index 9a68e45bf1b..56395707369 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
> @@ -96,4 +107,14 @@ 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);
> +
> #endif /* AVCODEC_DOVI_RPU_H */
All of these encoder-only functions should be put into a file of their
own so that it is not built when not enabling these non-native encoders.
- 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
* [FFmpeg-devel] [PATCH v2 06/11] avcodec/dovi_rpu: make `enable` also affect decoding
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
` (4 preceding siblings ...)
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 05/11] avcodec/dovi_rpu: add ff_dovi_configure() Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 07/11] avcodec/dovi_rpu: add ff_dovi_rpu_generate() Niklas Haas
` (4 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 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.c | 6 ++++++
libavcodec/dovi_rpu.h | 2 ++
libavcodec/hevcdec.c | 1 +
libavcodec/libdav1d.c | 1 +
5 files changed, 11 insertions(+)
diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c
index 4c1405df779..20865b4f129 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.c b/libavcodec/dovi_rpu.c
index d3a284c150d..54994188a96 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -90,6 +90,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 */
@@ -558,6 +561,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/dovi_rpu.h b/libavcodec/dovi_rpu.h
index 56395707369..8dc69a2733d 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/hevcdec.c b/libavcodec/hevcdec.c
index 4bc9e2afc1d..651773260e3 100644
--- a/libavcodec/hevcdec.c
+++ b/libavcodec/hevcdec.c
@@ -3689,6 +3689,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 09fe767fb86..f9e1a181fc3 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
* [FFmpeg-devel] [PATCH v2 07/11] avcodec/dovi_rpu: add ff_dovi_rpu_generate()
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
` (5 preceding siblings ...)
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 06/11] avcodec/dovi_rpu: make `enable` also affect decoding Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 08/11] avformat/movenc: warn if dovi cfg ignored Niklas Haas
` (3 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 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.c | 542 ++++++++++++++++++++++++++++++++++++++++++
libavcodec/dovi_rpu.h | 20 +-
2 files changed, 560 insertions(+), 2 deletions(-)
diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
index 54994188a96..272a5125b65 100644
--- a/libavcodec/dovi_rpu.c
+++ b/libavcodec/dovi_rpu.c
@@ -29,6 +29,9 @@
#include "dovi_rpu.h"
#include "golomb.h"
#include "get_bits.h"
+#include "itut35.h"
+#include "put_bits.h"
+#include "put_golomb.h"
#include "refstruct.h"
enum {
@@ -361,6 +364,42 @@ static inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *
return 0; /* unreachable */
}
+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 inline unsigned get_variable_bits(GetBitContext *gb, int n)
{
unsigned int value = get_bits(gb, n);
@@ -891,3 +930,506 @@ fail:
ff_dovi_ctx_unref(s); /* don't leak potentially invalid state */
return AVERROR_INVALIDDATA;
}
+
+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;
+ }
+}
diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
index 8dc69a2733d..1287dabd636 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;
@@ -119,4 +121,18 @@ int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame);
*/
int ff_dovi_configure(DOVIContext *s, AVCodecContext *avctx);
+/**
+ * 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);
+
#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 v2 08/11] avformat/movenc: warn if dovi cfg ignored
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
` (6 preceding siblings ...)
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 07/11] avcodec/dovi_rpu: add ff_dovi_rpu_generate() Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 09/11] avcodec/libaomenc: implement dolby vision coding Niklas Haas
` (2 subsequent siblings)
10 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 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 15b65dcf96d..0f819214be9 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -2528,16 +2528,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 v2 09/11] avcodec/libaomenc: implement dolby vision coding
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
` (7 preceding siblings ...)
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 08/11] avformat/movenc: warn if dovi cfg ignored Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 10/11] avcodec/libx265: " Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 11/11] avcodec/libsvtav1: " Niklas Haas
10 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
---
libavcodec/libaomenc.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
index 4a71bba9c9c..b43a902a384 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 v2 10/11] avcodec/libx265: implement dolby vision coding
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
` (8 preceding siblings ...)
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 09/11] avcodec/libaomenc: implement dolby vision coding Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 11/11] avcodec/libsvtav1: " Niklas Haas
10 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 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.
---
libavcodec/libx265.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c
index 0645cd20457..c4ceffff5d3 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 v2 11/11] avcodec/libsvtav1: implement dolby vision coding
2024-04-09 12:57 [FFmpeg-devel] [PATCH v2 00/11] avcodec: add Dolby Vision encoding Niklas Haas
` (9 preceding siblings ...)
2024-04-09 12:57 ` [FFmpeg-devel] [PATCH v2 10/11] avcodec/libx265: " Niklas Haas
@ 2024-04-09 12:57 ` Niklas Haas
10 siblings, 0 replies; 17+ messages in thread
From: Niklas Haas @ 2024-04-09 12:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Niklas Haas
From: Niklas Haas <git@haasn.dev>
---
libavcodec/libsvtav1.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/libavcodec/libsvtav1.c b/libavcodec/libsvtav1.c
index 105c3369c0f..cd62103dba4 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;
int ret;
if (!frame) {
@@ -524,6 +535,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_av1_enc_send_picture(svt_enc->svt_handle, headerPtr);
return 0;
@@ -644,11 +673,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;
}
@@ -695,6 +726,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