@@ -117,7 +117,7 @@ static int libjxl_init_jxl_encoder(AVCodecContext *avctx)
return AVERROR_EXTERNAL;
}
- /* check for negative zero, our default */
+ /* check for negative, our default */
if (ctx->distance < 0.0) {
/* use ffmpeg.c -q option if passed */
if (avctx->flags & AV_CODEC_FLAG_QSCALE)
@@ -133,7 +133,8 @@ static int libjxl_init_jxl_encoder(AVCodecContext *avctx)
*/
if (ctx->distance > 0.0 && ctx->distance < 0.01)
ctx->distance = 0.01;
- if (JxlEncoderOptionsSetDistance(ctx->options, ctx->distance) != JXL_ENC_SUCCESS) {
+
+ if (JxlEncoderSetFrameDistance(ctx->options, ctx->distance) != JXL_ENC_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to set distance: %f\n", ctx->distance);
return AVERROR_EXTERNAL;
}
@@ -219,57 +220,118 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra
info.num_color_channels = jxl_fmt.num_channels - info.num_extra_channels;
info.bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt.num_channels;
info.alpha_bits = (info.num_extra_channels > 0) * info.bits_per_sample;
+
if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) {
info.exponent_bits_per_sample = info.bits_per_sample > 16 ? 8 : 5;
info.alpha_exponent_bits = info.alpha_bits ? info.exponent_bits_per_sample : 0;
jxl_fmt.data_type = info.bits_per_sample > 16 ? JXL_TYPE_FLOAT : JXL_TYPE_FLOAT16;
- JxlColorEncodingSetToLinearSRGB(&jxl_color, info.num_color_channels == 1);
} else {
info.exponent_bits_per_sample = 0;
info.alpha_exponent_bits = 0;
jxl_fmt.data_type = info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16;
- JxlColorEncodingSetToSRGB(&jxl_color, info.num_color_channels == 1);
}
- if (info.bits_per_sample > 16
- || info.xsize > (1 << 18) || info.ysize > (1 << 18)
- || (info.xsize << 4) * (info.ysize << 4) > (1 << 20)) {
- /*
- * must upgrade codestream to level 10, from level 5
- * the encoder will not do this automatically
- */
- if (JxlEncoderSetCodestreamLevel(ctx->encoder, 10) != JXL_ENC_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "Could not upgrade JXL Codestream level.\n");
- return AVERROR_EXTERNAL;
- }
+ /* JPEG XL format itself does not support partial range */
+ if (avctx->color_range == AVCOL_RANGE_MPEG) {
+ av_log(avctx, AV_LOG_ERROR, "This encoder does not support partial(tv) range, colors will be wrong!\n");
+ } else if (avctx->color_range != AVCOL_RANGE_JPEG) {
+ av_log(avctx, AV_LOG_WARNING, "Unknown color range, assuming full\n");
}
- /* bitexact lossless requires there to be no XYB transform */
+ /* bitexact lossless requires there to be no XYB transform */
info.uses_original_profile = ctx->distance == 0.0;
- sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE);
- if (sd && sd->size && JxlEncoderSetICCProfile(ctx->encoder, sd->data, sd->size) != JXL_ENC_SUCCESS) {
- av_log(avctx, AV_LOG_WARNING, "Could not set ICC Profile\n");
- } else if (info.uses_original_profile) {
- /*
- * the color encoding is not used if uses_original_profile is false
- * this just works around a bug in libjxl 0.7.0 and lower
- */
- if (JxlEncoderSetColorEncoding(ctx->encoder, &jxl_color) != JXL_ENC_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "Failed to set JxlColorEncoding\n");
- return AVERROR_EXTERNAL;
- }
- }
-
if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo\n");
return AVERROR_EXTERNAL;
}
+ /* rendering intent doesn't matter here
+ * but libjxl will whine if we don't set it */
+ jxl_color.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
+
+ switch (avctx->color_trc) {
+ case AVCOL_TRC_BT709:
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_709;
+ break;
+ case AVCOL_TRC_LINEAR:
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
+ break;
+ case AVCOL_TRC_IEC61966_2_1:
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
+ break;
+ case AVCOL_TRC_SMPTE428:
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_DCI;
+ break;
+ case AVCOL_TRC_ARIB_STD_B67:
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_HLG;
+ break;
+ case AVCOL_TRC_GAMMA22:
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
+ jxl_color.gamma = 2.2;
+ break;
+ case AVCOL_TRC_GAMMA28:
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
+ jxl_color.gamma = 2.8;
+ break;
+ default:
+ if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) {
+ av_log(avctx, AV_LOG_WARNING, "Unknown transfer function, assuming Linear Light\n");
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
+ } else {
+ av_log(avctx, AV_LOG_WARNING, "Unknown transfer function, assuming IEC61966-2-1/sRGB\n");
+ jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
+ }
+ }
+
+ /* This should be implied to be honest
+ * but a libjxl but makes it fail otherwise */
+ if (info.num_color_channels == 1)
+ jxl_color.color_space = JXL_COLOR_SPACE_GRAY;
+ else
+ jxl_color.color_space = JXL_COLOR_SPACE_RGB;
+
+ switch (avctx->color_primaries) {
+ case AVCOL_PRI_BT709:
+ jxl_color.primaries = JXL_PRIMARIES_SRGB;
+ jxl_color.white_point = JXL_WHITE_POINT_D65;
+ break;
+ case AVCOL_PRI_BT2020:
+ jxl_color.primaries = JXL_PRIMARIES_2100;
+ jxl_color.white_point = JXL_WHITE_POINT_D65;
+ break;
+ case AVCOL_PRI_SMPTE431:
+ jxl_color.primaries = JXL_PRIMARIES_P3;
+ jxl_color.primaries = JXL_WHITE_POINT_DCI;
+ break;
+ case AVCOL_PRI_SMPTE432:
+ jxl_color.primaries = JXL_PRIMARIES_P3;
+ jxl_color.primaries = JXL_WHITE_POINT_D65;
+ break;
+ default:
+ av_log(avctx, AV_LOG_WARNING, "Unknown color primaries, assuming BT.709/sRGB+D65\n");
+ jxl_color.primaries = JXL_PRIMARIES_SRGB;
+ jxl_color.white_point = JXL_WHITE_POINT_D65;
+ }
+
+ sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE);
+ if (sd && sd->size && JxlEncoderSetICCProfile(ctx->encoder, sd->data, sd->size) != JXL_ENC_SUCCESS)
+ av_log(avctx, AV_LOG_WARNING, "Could not set ICC Profile\n");
+ if (JxlEncoderSetColorEncoding(ctx->encoder, &jxl_color) != JXL_ENC_SUCCESS)
+ av_log(avctx, AV_LOG_WARNING, "Failed to set JxlColorEncoding\n");
+
+ /* depending on basic info, level 10 might
+ * be required instead of level 5 */
+ if (JxlEncoderGetRequiredCodestreamLevel(ctx->encoder) > 5) {
+ if (JxlEncoderSetCodestreamLevel(ctx->encoder, 10) != JXL_ENC_SUCCESS)
+ av_log(avctx, AV_LOG_WARNING, "Could not increase codestream level\n");
+ }
+
jxl_fmt.endianness = JXL_NATIVE_ENDIAN;
jxl_fmt.align = frame->linesize[0];
- if (JxlEncoderAddImageFrame(ctx->options, &jxl_fmt, frame->data[0], jxl_fmt.align * info.ysize) != JXL_ENC_SUCCESS) {
+ if (JxlEncoderAddImageFrame(ctx->options, &jxl_fmt, frame->data[0],
+ jxl_fmt.align * info.ysize) != JXL_ENC_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame\n");
return AVERROR_EXTERNAL;
}