* [FFmpeg-devel] [PATCH] Add support for Panasonic V-Log transfer function (PR #20561)
@ 2025-09-20 14:50 Lynne via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: Lynne via ffmpeg-devel @ 2025-09-20 14:50 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Lynne
PR #20561 opened by Lynne
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20561
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20561.patch
This PR adds support for the Panasonic V-Log transfer function, used in Panasonic cameras.
To avoid bloating structures unnecessarily, the definition uses a value of 37, within the reserved area of H.273. Currently, the last defined H.273 value is 18.
As V-Log will very likely never be tagged or supported by H.273, our value may be changed in the future if it looks like the ITU will define 37 to be used for something else.
>From ad8f5e99e8451a0c7c58ce14fdfd40cc340b4a9a Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 27 Aug 2025 21:50:32 +0900
Subject: [PATCH 1/4] lavu: add support for Panasonic V-Log transfer function
---
libavcodec/fflcms2.c | 1 +
libavfilter/vf_libplacebo.c | 1 +
libavfilter/vf_setparams.c | 1 +
libavutil/csp.c | 28 ++++++++++++++++++++++++++++
libavutil/pixdesc.c | 1 +
libavutil/pixfmt.h | 7 +++++++
libswscale/format.c | 3 ++-
7 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/libavcodec/fflcms2.c b/libavcodec/fflcms2.c
index 3b67e62d3a..3d3241f5a3 100644
--- a/libavcodec/fflcms2.c
+++ b/libavcodec/fflcms2.c
@@ -128,6 +128,7 @@ static int get_curve(FFIccContext *s, enum AVColorTransferCharacteristic trc,
case AVCOL_TRC_BT1361_ECG:
case AVCOL_TRC_SMPTE2084:
case AVCOL_TRC_ARIB_STD_B67:
+ case AVCOL_TRC_V_LOG:
return AVERROR_PATCHWELCOME;
default:
diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
index 5e0a678ff2..f5a3c55aa8 100644
--- a/libavfilter/vf_libplacebo.c
+++ b/libavfilter/vf_libplacebo.c
@@ -1612,6 +1612,7 @@ static const AVOption libplacebo_options[] = {
{"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"},
{"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"},
{"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"},
+ {"vlog", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_V_LOG}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"},
{"rotate", "rotate the input clockwise", OFFSET(rotation), AV_OPT_TYPE_INT, {.i64=PL_ROTATION_0}, PL_ROTATION_0, PL_ROTATION_360, DYNAMIC, .unit = "rotation"},
{"0", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PL_ROTATION_0}, .flags = STATIC, .unit = "rotation"},
diff --git a/libavfilter/vf_setparams.c b/libavfilter/vf_setparams.c
index f32757f940..be0323fd83 100644
--- a/libavfilter/vf_setparams.c
+++ b/libavfilter/vf_setparams.c
@@ -101,6 +101,7 @@ static const AVOption setparams_options[] = {
{"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, 0, 0, FLAGS, .unit = "color_trc"},
{"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE428}, 0, 0, FLAGS, .unit = "color_trc"},
{"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, 0, 0, FLAGS, .unit = "color_trc"},
+ {"vlog", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_V_LOG}, 0, 0, FLAGS, .unit = "color_trc"},
{"colorspace", "select colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_NB-1, FLAGS, .unit = "colorspace"},
{"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, .unit = "colorspace"},
diff --git a/libavutil/csp.c b/libavutil/csp.c
index 75ac871616..8ea47d6c55 100644
--- a/libavutil/csp.c
+++ b/libavutil/csp.c
@@ -378,6 +378,32 @@ static double trc_arib_std_b67_inv(double E)
(E <= 0.5 ? E * E / 3.0 : (exp((E - c) / a) + b) / 12.0);
}
+#define VLOG_c1 0.01
+#define VLOG_c2 0.181
+#define VLOG_b 0.00873
+#define VLOG_c 0.241514
+#define VLOG_d 0.598206
+
+static double trc_vlog(double E)
+{
+ const double c2 = VLOG_c2;
+ const double b = VLOG_b;
+ const double c = VLOG_c;
+ const double d = VLOG_d;
+ return (E < c2) ? (E - 0.125) / 5.6 :
+ (pow(10.0, ((E - d) / c)) - b);
+}
+
+static double trc_vlog_inv(double E)
+{
+ const double c1 = VLOG_c1;
+ const double b = VLOG_b;
+ const double c = VLOG_c;
+ const double d = VLOG_d;
+ return (E < c1) ? (5.6 * E + 0.125) :
+ (c * log10(E + b) + d);
+}
+
static const av_csp_trc_function trc_funcs[AVCOL_TRC_NB] = {
[AVCOL_TRC_BT709] = trc_bt709,
[AVCOL_TRC_GAMMA22] = trc_gamma22,
@@ -395,6 +421,7 @@ static const av_csp_trc_function trc_funcs[AVCOL_TRC_NB] = {
[AVCOL_TRC_SMPTE2084] = trc_smpte_st2084,
[AVCOL_TRC_SMPTE428] = trc_smpte_st428_1,
[AVCOL_TRC_ARIB_STD_B67] = trc_arib_std_b67,
+ [AVCOL_TRC_V_LOG] = trc_vlog,
};
av_csp_trc_function av_csp_trc_func_from_id(enum AVColorTransferCharacteristic trc)
@@ -421,6 +448,7 @@ static const av_csp_trc_function trc_inv_funcs[AVCOL_TRC_NB] = {
[AVCOL_TRC_SMPTE2084] = trc_smpte_st2084_inv,
[AVCOL_TRC_SMPTE428] = trc_smpte_st428_1_inv,
[AVCOL_TRC_ARIB_STD_B67] = trc_arib_std_b67_inv,
+ [AVCOL_TRC_V_LOG] = trc_vlog_inv,
};
av_csp_trc_function av_csp_trc_func_inv_from_id(enum AVColorTransferCharacteristic trc)
diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c
index 3c31ee2132..0ce12cbc6f 100644
--- a/libavutil/pixdesc.c
+++ b/libavutil/pixdesc.c
@@ -3312,6 +3312,7 @@ static const char * const color_transfer_names[] = {
[AVCOL_TRC_SMPTE2084] = "smpte2084",
[AVCOL_TRC_SMPTE428] = "smpte428",
[AVCOL_TRC_ARIB_STD_B67] = "arib-std-b67",
+ [AVCOL_TRC_V_LOG] = "vlog",
};
static const char * const color_space_names[] = {
diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h
index 6aa1c94cec..8c4b5aab5d 100644
--- a/libavutil/pixfmt.h
+++ b/libavutil/pixfmt.h
@@ -680,6 +680,13 @@ enum AVColorTransferCharacteristic {
AVCOL_TRC_SMPTE428 = 17, ///< SMPTE ST 428-1
AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
AVCOL_TRC_ARIB_STD_B67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma"
+
+ /**
+ * NOTE: The following transfer functions are not a part of H.273 and are
+ * within the "reserved" window.
+ */
+ AVCOL_TRC_V_LOG = 37, ///< Panasonic V-Log
+
AVCOL_TRC_NB ///< Not part of ABI
};
diff --git a/libswscale/format.c b/libswscale/format.c
index 9741688e98..5dae9138ae 100644
--- a/libswscale/format.c
+++ b/libswscale/format.c
@@ -1270,9 +1270,10 @@ static AVRational *generate_bayer_matrix(const int size_log2)
static bool trc_is_hdr(enum AVColorTransferCharacteristic trc)
{
- static_assert(AVCOL_TRC_NB == 19, "Update this list when adding TRCs");
+ static_assert(AVCOL_TRC_NB == 38, "Update this list when adding TRCs");
switch (trc) {
case AVCOL_TRC_LOG:
+ case AVCOL_TRC_V_LOG:
case AVCOL_TRC_LOG_SQRT:
case AVCOL_TRC_SMPTEST2084:
case AVCOL_TRC_ARIB_STD_B67:
--
2.49.1
>From bbb6037fff08a5c283be148ae5eaff2c75409e87 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Thu, 28 Aug 2025 15:10:26 +0900
Subject: [PATCH 2/4] prores_raw: set color_trc based on the vendor tag
This is the only identifiable piece of information about the
colorspace, apart from the XML embedded in the MOV comments.
---
libavcodec/prores_raw.c | 17 ++++++++++++++++-
libavcodec/prores_raw_parser.c | 17 ++++++++++++++++-
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/libavcodec/prores_raw.c b/libavcodec/prores_raw.c
index b2aa97ddda..71056014fc 100644
--- a/libavcodec/prores_raw.c
+++ b/libavcodec/prores_raw.c
@@ -359,7 +359,22 @@ static int decode_frame(AVCodecContext *avctx,
}
/* Vendor header (e.g. "peac" for Panasonic or "atm0" for Atmos) */
- bytestream2_skip(&gb_hdr, 4);
+ uint32_t vendor = bytestream2_get_be32(&gb_hdr);
+ switch (vendor) {
+ case MKBETAG('p','e','a','c'):
+ /* Internal recording from a Panasonic camera, V-Log */
+ avctx->color_trc = AVCOL_TRC_V_LOG;
+ break;
+ case MKBETAG('a','t','m','0'):
+ /* External recording from an Atomos recorder. Cameras universally
+ * record in their own native log curve internally, but linearize it
+ * when outputting RAW externally */
+ avctx->color_trc = AVCOL_TRC_LINEAR;
+ break;
+ default:
+ avctx->color_trc = AVCOL_TRC_UNSPECIFIED;
+ break;
+ }
/* Width and height must always be even */
int w = bytestream2_get_be16(&gb_hdr);
diff --git a/libavcodec/prores_raw_parser.c b/libavcodec/prores_raw_parser.c
index fca3ec37fb..d0dfdd9e86 100644
--- a/libavcodec/prores_raw_parser.c
+++ b/libavcodec/prores_raw_parser.c
@@ -61,7 +61,22 @@ static int prores_raw_parse(AVCodecParserContext *s, AVCodecContext *avctx,
}
/* Vendor header (e.g. "peac" for Panasonic or "atm0" for Atmos) */
- bytestream2_skip(&gb, 4);
+ uint32_t vendor = bytestream2_get_be32(&gb);
+ switch (vendor) {
+ case MKBETAG('p','e','a','c'):
+ /* Internal recording from a Panasonic camera, V-Log */
+ avctx->color_trc = AVCOL_TRC_V_LOG;
+ break;
+ case MKBETAG('a','t','m','0'):
+ /* External recording from an Atomos recorder. Cameras universally
+ * record in their own native log curve internally, but linearize it
+ * when outputting RAW externally */
+ avctx->color_trc = AVCOL_TRC_LINEAR;
+ break;
+ default:
+ avctx->color_trc = AVCOL_TRC_UNSPECIFIED;
+ break;
+ }
s->width = bytestream2_get_be16(&gb);
s->height = bytestream2_get_be16(&gb);
--
2.49.1
>From 0aea270cccbb1dc021ca5014556e44c70896343e Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Thu, 28 Aug 2025 15:12:52 +0900
Subject: [PATCH 3/4] prores_raw: set profile based on the codec tag
This is the same as what the parser does.
---
libavcodec/prores_raw.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/libavcodec/prores_raw.c b/libavcodec/prores_raw.c
index 71056014fc..ad47e144bf 100644
--- a/libavcodec/prores_raw.c
+++ b/libavcodec/prores_raw.c
@@ -334,6 +334,21 @@ static int decode_frame(AVCodecContext *avctx,
DECLARE_ALIGNED(32, uint8_t, qmat)[64];
memset(qmat, 1, 64);
+ switch (avctx->codec_tag) {
+ case 0:
+ break;
+ case MKTAG('a','p','r','n'):
+ avctx->profile = AV_PROFILE_PRORES_RAW;
+ break;
+ case MKTAG('a','p','r','h'):
+ avctx->profile = AV_PROFILE_PRORES_RAW_HQ;
+ break;
+ default:
+ avpriv_request_sample(avctx, "Profile %d", avctx->codec_tag);
+ return AVERROR_PATCHWELCOME;
+ break;
+ }
+
GetByteContext gb;
bytestream2_init(&gb, avpkt->data, avpkt->size);
if (bytestream2_get_be32(&gb) != avpkt->size)
--
2.49.1
>From baa3f17b66f1ea4c7cdd9c056cf5640568fee913 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Thu, 28 Aug 2025 15:14:03 +0900
Subject: [PATCH 4/4] prores_raw: use MKBETAG for the frame header tag
Equivalent, but more explicit. All values in the header are big endian.
---
libavcodec/prores_raw.c | 2 +-
libavcodec/prores_raw_parser.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/libavcodec/prores_raw.c b/libavcodec/prores_raw.c
index ad47e144bf..beb11aa23f 100644
--- a/libavcodec/prores_raw.c
+++ b/libavcodec/prores_raw.c
@@ -355,7 +355,7 @@ static int decode_frame(AVCodecContext *avctx,
return AVERROR_INVALIDDATA;
/* ProRes RAW frame */
- if (bytestream2_get_le32(&gb) != MKTAG('p','r','r','f'))
+ if (bytestream2_get_be32(&gb) != MKBETAG('p','r','r','f'))
return AVERROR_INVALIDDATA;
int header_len = bytestream2_get_be16(&gb);
diff --git a/libavcodec/prores_raw_parser.c b/libavcodec/prores_raw_parser.c
index d0dfdd9e86..fb690f243d 100644
--- a/libavcodec/prores_raw_parser.c
+++ b/libavcodec/prores_raw_parser.c
@@ -46,7 +46,7 @@ static int prores_raw_parse(AVCodecParserContext *s, AVCodecContext *avctx,
if (bytestream2_get_be32(&gb) != buf_size) /* Packet size */
return buf_size;
- if (bytestream2_get_le32(&gb) != MKTAG('p','r','r','f')) /* Frame header */
+ if (bytestream2_get_be32(&gb) != MKBETAG('p','r','r','f')) /* Frame header */
return buf_size;
int header_size = bytestream2_get_be16(&gb);
--
2.49.1
_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-09-20 14:50 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-20 14:50 [FFmpeg-devel] [PATCH] Add support for Panasonic V-Log transfer function (PR #20561) Lynne via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git