libjxlenc.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. /*
  2. * JPEG XL encoding support via libjxl
  3. * Copyright (c) 2021 Leo Izen <leo.izen@gmail.com>
  4. *
  5. * This file is part of FFmpeg.
  6. *
  7. * FFmpeg is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * FFmpeg is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with FFmpeg; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. /**
  22. * @file
  23. * JPEG XL encoder using libjxl
  24. */
  25. #include <string.h>
  26. #include "libavutil/avutil.h"
  27. #include "libavutil/csp.h"
  28. #include "libavutil/error.h"
  29. #include "libavutil/frame.h"
  30. #include "libavutil/libm.h"
  31. #include "libavutil/mem.h"
  32. #include "libavutil/opt.h"
  33. #include "libavutil/pixdesc.h"
  34. #include "libavutil/pixfmt.h"
  35. #include "libavutil/version.h"
  36. #include "avcodec.h"
  37. #include "encode.h"
  38. #include "codec_internal.h"
  39. #include <jxl/encode.h>
  40. #include <jxl/thread_parallel_runner.h>
  41. #include "libjxl.h"
  42. typedef struct LibJxlEncodeContext {
  43. AVClass *class;
  44. void *runner;
  45. JxlEncoder *encoder;
  46. JxlEncoderFrameSettings *options;
  47. int effort;
  48. float distance;
  49. int modular;
  50. int xyb;
  51. uint8_t *buffer;
  52. size_t buffer_size;
  53. JxlPixelFormat jxl_fmt;
  54. /* animation stuff */
  55. AVFrame *frame;
  56. AVFrame *prev;
  57. int64_t duration;
  58. } LibJxlEncodeContext;
  59. /**
  60. * Map a quality setting for -qscale roughly from libjpeg
  61. * quality numbers to libjxl's butteraugli distance for
  62. * photographic content.
  63. *
  64. * Setting distance explicitly is preferred, but this will
  65. * allow qscale to be used as a fallback.
  66. *
  67. * This function is continuous and injective on [0, 100] which
  68. * makes it monotonic.
  69. *
  70. * @param quality 0.0 to 100.0 quality setting, libjpeg quality
  71. * @return Butteraugli distance between 0.0 and 15.0
  72. */
  73. static float quality_to_distance(float quality)
  74. {
  75. if (quality >= 100.0)
  76. return 0.0;
  77. else if (quality >= 90.0)
  78. return (100.0 - quality) * 0.10;
  79. else if (quality >= 30.0)
  80. return 0.1 + (100.0 - quality) * 0.09;
  81. else if (quality > 0.0)
  82. return 15.0 + (59.0 * quality - 4350.0) * quality / 9000.0;
  83. else
  84. return 15.0;
  85. }
  86. /**
  87. * Initialize the encoder on a per-file basis. All of these need to be set
  88. * once each time the encoder is reset, which is each frame for still
  89. * images, to make the image2 muxer work. For animation this is run once.
  90. *
  91. * @return 0 upon success, negative on failure.
  92. */
  93. static int libjxl_init_jxl_encoder(AVCodecContext *avctx)
  94. {
  95. LibJxlEncodeContext *ctx = avctx->priv_data;
  96. /* reset the encoder every frame for image2 muxer */
  97. JxlEncoderReset(ctx->encoder);
  98. /* This needs to be set each time the encoder is reset */
  99. if (JxlEncoderSetParallelRunner(ctx->encoder, JxlThreadParallelRunner, ctx->runner)
  100. != JXL_ENC_SUCCESS) {
  101. av_log(avctx, AV_LOG_ERROR, "Failed to set JxlThreadParallelRunner\n");
  102. return AVERROR_EXTERNAL;
  103. }
  104. ctx->options = JxlEncoderFrameSettingsCreate(ctx->encoder, NULL);
  105. if (!ctx->options) {
  106. av_log(avctx, AV_LOG_ERROR, "Failed to create JxlEncoderOptions\n");
  107. return AVERROR_EXTERNAL;
  108. }
  109. return 0;
  110. }
  111. /**
  112. * Global encoder initialization. This only needs to be run once,
  113. * not every frame.
  114. */
  115. static av_cold int libjxl_encode_init(AVCodecContext *avctx)
  116. {
  117. LibJxlEncodeContext *ctx = avctx->priv_data;
  118. JxlMemoryManager manager;
  119. ff_libjxl_init_memory_manager(&manager);
  120. ctx->encoder = JxlEncoderCreate(&manager);
  121. if (!ctx->encoder) {
  122. av_log(avctx, AV_LOG_ERROR, "Failed to create JxlEncoder\n");
  123. return AVERROR_EXTERNAL;
  124. }
  125. ctx->runner = JxlThreadParallelRunnerCreate(&manager, ff_libjxl_get_threadcount(avctx->thread_count));
  126. if (!ctx->runner) {
  127. av_log(avctx, AV_LOG_ERROR, "Failed to create JxlThreadParallelRunner\n");
  128. return AVERROR_EXTERNAL;
  129. }
  130. ctx->buffer_size = 4096;
  131. ctx->buffer = av_realloc(NULL, ctx->buffer_size);
  132. if (!ctx->buffer) {
  133. av_log(avctx, AV_LOG_ERROR, "Could not allocate encoding buffer\n");
  134. return AVERROR(ENOMEM);
  135. }
  136. /* check for negative, our default */
  137. if (ctx->distance < 0.0) {
  138. /* use ffmpeg.c -q option if passed */
  139. if (avctx->flags & AV_CODEC_FLAG_QSCALE)
  140. ctx->distance = quality_to_distance((float)avctx->global_quality / FF_QP2LAMBDA);
  141. else
  142. /* default 1.0 matches cjxl */
  143. ctx->distance = 1.0;
  144. }
  145. /*
  146. * 0.01 is the minimum distance accepted for lossy
  147. * interpreting any positive value less than this as minimum
  148. */
  149. if (ctx->distance > 0.0 && ctx->distance < 0.01)
  150. ctx->distance = 0.01;
  151. return 0;
  152. }
  153. /**
  154. * Initializer for the animation encoder. This calls the other initializers
  155. * to prevent code duplication and also allocates the prev-frame used in the
  156. * encoder.
  157. */
  158. static av_cold int libjxl_anim_encode_init(AVCodecContext *avctx)
  159. {
  160. int ret;
  161. LibJxlEncodeContext *ctx = avctx->priv_data;
  162. ret = libjxl_encode_init(avctx);
  163. if (ret < 0)
  164. return ret;
  165. ret = libjxl_init_jxl_encoder(avctx);
  166. if (ret < 0)
  167. return ret;
  168. ctx->frame = av_frame_alloc();
  169. if (!ctx->frame)
  170. return AVERROR(ENOMEM);
  171. return 0;
  172. }
  173. /**
  174. * Populate a JxlColorEncoding with the given enum AVColorPrimaries.
  175. * @return < 0 upon failure, >= 0 upon success
  176. */
  177. static int libjxl_populate_primaries(void *avctx, JxlColorEncoding *jxl_color, enum AVColorPrimaries prm)
  178. {
  179. const AVColorPrimariesDesc *desc;
  180. switch (prm) {
  181. case AVCOL_PRI_BT709:
  182. jxl_color->primaries = JXL_PRIMARIES_SRGB;
  183. jxl_color->white_point = JXL_WHITE_POINT_D65;
  184. return 0;
  185. case AVCOL_PRI_BT2020:
  186. jxl_color->primaries = JXL_PRIMARIES_2100;
  187. jxl_color->white_point = JXL_WHITE_POINT_D65;
  188. return 0;
  189. case AVCOL_PRI_SMPTE431:
  190. jxl_color->primaries = JXL_PRIMARIES_P3;
  191. jxl_color->white_point = JXL_WHITE_POINT_DCI;
  192. return 0;
  193. case AVCOL_PRI_SMPTE432:
  194. jxl_color->primaries = JXL_PRIMARIES_P3;
  195. jxl_color->white_point = JXL_WHITE_POINT_D65;
  196. return 0;
  197. case AVCOL_PRI_UNSPECIFIED:
  198. av_log(avctx, AV_LOG_WARNING, "Unknown primaries, assuming BT.709/sRGB. Colors may be wrong.\n");
  199. jxl_color->primaries = JXL_PRIMARIES_SRGB;
  200. jxl_color->white_point = JXL_WHITE_POINT_D65;
  201. return 0;
  202. }
  203. desc = av_csp_primaries_desc_from_id(prm);
  204. if (!desc)
  205. return AVERROR(EINVAL);
  206. jxl_color->primaries = JXL_PRIMARIES_CUSTOM;
  207. jxl_color->white_point = JXL_WHITE_POINT_CUSTOM;
  208. jxl_color->primaries_red_xy[0] = av_q2d(desc->prim.r.x);
  209. jxl_color->primaries_red_xy[1] = av_q2d(desc->prim.r.y);
  210. jxl_color->primaries_green_xy[0] = av_q2d(desc->prim.g.x);
  211. jxl_color->primaries_green_xy[1] = av_q2d(desc->prim.g.y);
  212. jxl_color->primaries_blue_xy[0] = av_q2d(desc->prim.b.x);
  213. jxl_color->primaries_blue_xy[1] = av_q2d(desc->prim.b.y);
  214. jxl_color->white_point_xy[0] = av_q2d(desc->wp.x);
  215. jxl_color->white_point_xy[1] = av_q2d(desc->wp.y);
  216. return 0;
  217. }
  218. static int libjxl_populate_colorspace(AVCodecContext *avctx, const AVFrame *frame,
  219. const AVPixFmtDescriptor *pix_desc, const JxlBasicInfo *info)
  220. {
  221. JxlColorEncoding jxl_color;
  222. LibJxlEncodeContext *ctx = avctx->priv_data;
  223. int ret;
  224. switch (frame->color_trc && frame->color_trc != AVCOL_TRC_UNSPECIFIED
  225. ? frame->color_trc : avctx->color_trc) {
  226. case AVCOL_TRC_BT709:
  227. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_709;
  228. break;
  229. case AVCOL_TRC_LINEAR:
  230. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
  231. break;
  232. case AVCOL_TRC_IEC61966_2_1:
  233. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
  234. break;
  235. case AVCOL_TRC_SMPTE428:
  236. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_DCI;
  237. break;
  238. case AVCOL_TRC_SMPTE2084:
  239. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_PQ;
  240. break;
  241. case AVCOL_TRC_ARIB_STD_B67:
  242. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_HLG;
  243. break;
  244. case AVCOL_TRC_GAMMA22:
  245. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
  246. jxl_color.gamma = 1/2.2f;
  247. break;
  248. case AVCOL_TRC_GAMMA28:
  249. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
  250. jxl_color.gamma = 1/2.8f;
  251. break;
  252. default:
  253. if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) {
  254. av_log(avctx, AV_LOG_WARNING,
  255. "Unknown transfer function, assuming Linear Light. Colors may be wrong.\n");
  256. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
  257. } else {
  258. av_log(avctx, AV_LOG_WARNING,
  259. "Unknown transfer function, assuming IEC61966-2-1/sRGB. Colors may be wrong.\n");
  260. jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
  261. }
  262. }
  263. jxl_color.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
  264. if (info->num_color_channels == 1)
  265. jxl_color.color_space = JXL_COLOR_SPACE_GRAY;
  266. else
  267. jxl_color.color_space = JXL_COLOR_SPACE_RGB;
  268. ret = libjxl_populate_primaries(avctx, &jxl_color,
  269. frame->color_primaries && frame->color_primaries != AVCOL_PRI_UNSPECIFIED
  270. ? frame->color_primaries : avctx->color_primaries);
  271. if (ret < 0)
  272. return ret;
  273. if (JxlEncoderSetColorEncoding(ctx->encoder, &jxl_color) != JXL_ENC_SUCCESS) {
  274. av_log(avctx, AV_LOG_WARNING, "Failed to set JxlColorEncoding\n");
  275. return AVERROR_EXTERNAL;
  276. }
  277. return 0;
  278. }
  279. /**
  280. * Sends metadata to libjxl based on the first frame of the stream, such as pixel format,
  281. * orientation, bit depth, and that sort of thing.
  282. */
  283. static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, int animated)
  284. {
  285. LibJxlEncodeContext *ctx = avctx->priv_data;
  286. AVFrameSideData *sd;
  287. const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format);
  288. JxlBasicInfo info;
  289. JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt;
  290. int bits_per_sample;
  291. #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0)
  292. JxlBitDepth jxl_bit_depth;
  293. #endif
  294. /* populate the basic info settings */
  295. JxlEncoderInitBasicInfo(&info);
  296. jxl_fmt->num_channels = pix_desc->nb_components;
  297. info.xsize = frame->width;
  298. info.ysize = frame->height;
  299. info.num_extra_channels = (jxl_fmt->num_channels + 1) & 0x1;
  300. info.num_color_channels = jxl_fmt->num_channels - info.num_extra_channels;
  301. bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt->num_channels;
  302. info.bits_per_sample = avctx->bits_per_raw_sample > 0 && !(pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT)
  303. ? avctx->bits_per_raw_sample : bits_per_sample;
  304. info.alpha_bits = (info.num_extra_channels > 0) * info.bits_per_sample;
  305. if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) {
  306. info.exponent_bits_per_sample = info.bits_per_sample > 16 ? 8 : 5;
  307. info.alpha_exponent_bits = info.alpha_bits ? info.exponent_bits_per_sample : 0;
  308. jxl_fmt->data_type = info.bits_per_sample > 16 ? JXL_TYPE_FLOAT : JXL_TYPE_FLOAT16;
  309. } else {
  310. info.exponent_bits_per_sample = 0;
  311. info.alpha_exponent_bits = 0;
  312. jxl_fmt->data_type = info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16;
  313. }
  314. #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0)
  315. jxl_bit_depth.bits_per_sample = bits_per_sample;
  316. jxl_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT;
  317. jxl_bit_depth.exponent_bits_per_sample = pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT ?
  318. info.exponent_bits_per_sample : 0;
  319. #endif
  320. /* JPEG XL format itself does not support limited range */
  321. if (avctx->color_range == AVCOL_RANGE_MPEG ||
  322. avctx->color_range == AVCOL_RANGE_UNSPECIFIED && frame->color_range == AVCOL_RANGE_MPEG)
  323. av_log(avctx, AV_LOG_WARNING, "This encoder does not support limited (tv) range, colors will be wrong!\n");
  324. else if (avctx->color_range != AVCOL_RANGE_JPEG && frame->color_range != AVCOL_RANGE_JPEG)
  325. av_log(avctx, AV_LOG_WARNING, "Unknown color range, assuming full (pc)\n");
  326. /* bitexact lossless requires there to be no XYB transform */
  327. info.uses_original_profile = ctx->distance == 0.0 || !ctx->xyb;
  328. /* libjxl doesn't support negative linesizes so we use orientation to work around this */
  329. info.orientation = frame->linesize[0] >= 0 ? JXL_ORIENT_IDENTITY : JXL_ORIENT_FLIP_VERTICAL;
  330. if (animated) {
  331. info.have_animation = 1;
  332. info.animation.have_timecodes = 0;
  333. info.animation.num_loops = 0;
  334. /* avctx->timebase is in seconds per tick, so we take the reciprocol */
  335. info.animation.tps_numerator = avctx->time_base.den;
  336. info.animation.tps_denominator = avctx->time_base.num;
  337. }
  338. if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) {
  339. av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo\n");
  340. return AVERROR_EXTERNAL;
  341. }
  342. sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE);
  343. if (sd && sd->size && JxlEncoderSetICCProfile(ctx->encoder, sd->data, sd->size) != JXL_ENC_SUCCESS) {
  344. av_log(avctx, AV_LOG_WARNING, "Could not set ICC Profile\n");
  345. sd = NULL;
  346. }
  347. if (!sd || !sd->size)
  348. libjxl_populate_colorspace(avctx, frame, pix_desc, &info);
  349. #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0)
  350. if (JxlEncoderSetFrameBitDepth(ctx->options, &jxl_bit_depth) != JXL_ENC_SUCCESS)
  351. av_log(avctx, AV_LOG_WARNING, "Failed to set JxlBitDepth\n");
  352. #endif
  353. /* depending on basic info, level 10 might
  354. * be required instead of level 5 */
  355. if (JxlEncoderGetRequiredCodestreamLevel(ctx->encoder) > 5) {
  356. if (JxlEncoderSetCodestreamLevel(ctx->encoder, 10) != JXL_ENC_SUCCESS)
  357. av_log(avctx, AV_LOG_WARNING, "Could not increase codestream level\n");
  358. }
  359. return 0;
  360. }
  361. /**
  362. * Sends frame information to libjxl on a per-frame basis. If this is a still image,
  363. * this is evaluated once per output file. If this is an animated JPEG XL encode, it
  364. * is called once per frame.
  365. *
  366. * This returns a buffer to the data that should be passed to libjxl (via the
  367. * argument **data). If the linesize is nonnegative, this will be frame->data[0],
  368. * although if the linesize is negative, it will be the start of the buffer
  369. * instead. *data is just a pointer to a location in frame->data so it should not be
  370. * freed directly.
  371. */
  372. static int libjxl_preprocess_frame(AVCodecContext *avctx, const AVFrame *frame, const uint8_t **data)
  373. {
  374. LibJxlEncodeContext *ctx = avctx->priv_data;
  375. JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt;
  376. /* these shouldn't fail, libjxl bug notwithstanding */
  377. if (JxlEncoderFrameSettingsSetOption(ctx->options, JXL_ENC_FRAME_SETTING_EFFORT, ctx->effort)
  378. != JXL_ENC_SUCCESS) {
  379. av_log(avctx, AV_LOG_ERROR, "Failed to set effort to: %d\n", ctx->effort);
  380. return AVERROR_EXTERNAL;
  381. }
  382. if (JxlEncoderSetFrameDistance(ctx->options, ctx->distance) != JXL_ENC_SUCCESS) {
  383. av_log(avctx, AV_LOG_ERROR, "Failed to set distance: %f\n", ctx->distance);
  384. return AVERROR_EXTERNAL;
  385. }
  386. /*
  387. * In theory the library should automatically enable modular if necessary,
  388. * but it appears it won't at the moment due to a bug. This will still
  389. * work even if that is patched.
  390. */
  391. if (JxlEncoderFrameSettingsSetOption(ctx->options, JXL_ENC_FRAME_SETTING_MODULAR,
  392. ctx->modular || ctx->distance <= 0.0 ? 1 : -1) != JXL_ENC_SUCCESS) {
  393. av_log(avctx, AV_LOG_ERROR, "Failed to set modular\n");
  394. return AVERROR_EXTERNAL;
  395. }
  396. jxl_fmt->endianness = JXL_NATIVE_ENDIAN;
  397. if (frame->linesize[0] >= 0) {
  398. jxl_fmt->align = frame->linesize[0];
  399. *data = frame->data[0];
  400. } else {
  401. jxl_fmt->align = -frame->linesize[0];
  402. *data = frame->data[0] + frame->linesize[0] * (frame->height - 1);
  403. }
  404. return 0;
  405. }
  406. /**
  407. * Run libjxl's output processing loop, reallocating the packet as necessary
  408. * if libjxl needs more space to work with.
  409. */
  410. static int libjxl_process_output(AVCodecContext *avctx, size_t *bytes_written)
  411. {
  412. LibJxlEncodeContext *ctx = avctx->priv_data;
  413. JxlEncoderStatus jret;
  414. size_t available = ctx->buffer_size;
  415. uint8_t *next_out = ctx->buffer;
  416. while (1) {
  417. jret = JxlEncoderProcessOutput(ctx->encoder, &next_out, &available);
  418. if (jret == JXL_ENC_ERROR) {
  419. av_log(avctx, AV_LOG_ERROR, "Unspecified libjxl error occurred\n");
  420. return AVERROR_EXTERNAL;
  421. }
  422. *bytes_written = ctx->buffer_size - available;
  423. /* all data passed has been encoded */
  424. if (jret == JXL_ENC_SUCCESS)
  425. break;
  426. if (jret == JXL_ENC_NEED_MORE_OUTPUT) {
  427. /*
  428. * at the moment, libjxl has no way to
  429. * tell us how much space it actually needs
  430. * so we need to malloc loop
  431. */
  432. uint8_t *temp;
  433. size_t new_size = ctx->buffer_size * 2;
  434. temp = av_realloc(ctx->buffer, new_size);
  435. if (!temp)
  436. return AVERROR(ENOMEM);
  437. ctx->buffer = temp;
  438. ctx->buffer_size = new_size;
  439. next_out = ctx->buffer + *bytes_written;
  440. available = new_size - *bytes_written;
  441. continue;
  442. }
  443. av_log(avctx, AV_LOG_ERROR, "Bad libjxl event: %d\n", jret);
  444. return AVERROR_EXTERNAL;
  445. }
  446. return 0;
  447. }
  448. /**
  449. * Encode an entire frame. This will always reinitialize a new still image
  450. * and encode a one-frame image (for image2 and image2pipe).
  451. */
  452. static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet)
  453. {
  454. LibJxlEncodeContext *ctx = avctx->priv_data;
  455. int ret;
  456. size_t bytes_written = 0;
  457. const uint8_t *data;
  458. ret = libjxl_init_jxl_encoder(avctx);
  459. if (ret < 0) {
  460. av_log(avctx, AV_LOG_ERROR, "Error frame-initializing JxlEncoder\n");
  461. return ret;
  462. }
  463. ret = libjxl_preprocess_stream(avctx, frame, 0);
  464. if (ret < 0)
  465. return ret;
  466. ret = libjxl_preprocess_frame(avctx, frame, &data);
  467. if (ret < 0)
  468. return ret;
  469. if (JxlEncoderAddImageFrame(ctx->options, &ctx->jxl_fmt, data, ctx->jxl_fmt.align * frame->height)
  470. != JXL_ENC_SUCCESS) {
  471. av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame\n");
  472. return AVERROR_EXTERNAL;
  473. }
  474. /*
  475. * Run this after the last frame in the image has been passed.
  476. */
  477. JxlEncoderCloseInput(ctx->encoder);
  478. ret = libjxl_process_output(avctx, &bytes_written);
  479. if (ret < 0)
  480. return ret;
  481. ret = ff_get_encode_buffer(avctx, pkt, bytes_written, 0);
  482. if (ret < 0)
  483. return ret;
  484. memcpy(pkt->data, ctx->buffer, bytes_written);
  485. *got_packet = 1;
  486. return 0;
  487. }
  488. /**
  489. * Encode one frame of the animation. libjxl requires us to set duration of the frame
  490. * but we're only promised the PTS, not the duration. As a result we have to buffer
  491. * a frame and subtract the PTS from the last PTS. The last frame uses the previous
  492. * frame's calculated duration as a fallback if its duration field is unset.
  493. *
  494. * We also need to tell libjxl if our frame is the last one, which we won't know upon
  495. * receiving a single frame, so we have to buffer a frame as well and send the "last frame"
  496. * upon receiving the special EOF frame.
  497. */
  498. static int libjxl_anim_encode_frame(AVCodecContext *avctx, AVPacket *pkt)
  499. {
  500. LibJxlEncodeContext *ctx = avctx->priv_data;
  501. int ret = 0;
  502. JxlFrameHeader frame_header;
  503. size_t bytes_written = 0;
  504. const uint8_t *data;
  505. if (!ctx->prev) {
  506. ctx->prev = av_frame_alloc();
  507. if (!ctx->prev)
  508. return AVERROR(ENOMEM);
  509. ret = ff_encode_get_frame(avctx, ctx->prev);
  510. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
  511. return ret;
  512. ret = libjxl_preprocess_stream(avctx, ctx->prev, 1);
  513. if (ret < 0)
  514. return ret;
  515. }
  516. ret = ff_encode_get_frame(avctx, ctx->frame);
  517. if (ret == AVERROR_EOF) {
  518. av_frame_free(&ctx->frame);
  519. ret = 0;
  520. }
  521. if (ret == AVERROR(EAGAIN))
  522. return ret;
  523. JxlEncoderInitFrameHeader(&frame_header);
  524. ctx->duration = ctx->prev->duration ? ctx->prev->duration :
  525. ctx->frame ? ctx->frame->pts - ctx->prev->pts :
  526. ctx->duration ? ctx->duration :
  527. 1;
  528. frame_header.duration = ctx->duration;
  529. pkt->duration = ctx->duration;
  530. pkt->pts = ctx->prev->pts;
  531. pkt->dts = pkt->pts;
  532. if (JxlEncoderSetFrameHeader(ctx->options, &frame_header) != JXL_ENC_SUCCESS) {
  533. av_log(avctx, AV_LOG_ERROR, "Failed to set JxlFrameHeader\n");
  534. return AVERROR_EXTERNAL;
  535. }
  536. ret = libjxl_preprocess_frame(avctx, ctx->prev, &data);
  537. if (ret < 0)
  538. return ret;
  539. if (JxlEncoderAddImageFrame(ctx->options, &ctx->jxl_fmt, data, ctx->jxl_fmt.align * ctx->prev->height)
  540. != JXL_ENC_SUCCESS) {
  541. av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame\n");
  542. return AVERROR_EXTERNAL;
  543. }
  544. if (!ctx->frame)
  545. JxlEncoderCloseInput(ctx->encoder);
  546. ret = libjxl_process_output(avctx, &bytes_written);
  547. if (ret < 0)
  548. return ret;
  549. ret = ff_get_encode_buffer(avctx, pkt, bytes_written, 0);
  550. if (ret < 0)
  551. return ret;
  552. memcpy(pkt->data, ctx->buffer, bytes_written);
  553. if (ctx->frame) {
  554. av_frame_unref(ctx->prev);
  555. av_frame_move_ref(ctx->prev, ctx->frame);
  556. } else {
  557. av_frame_free(&ctx->prev);
  558. }
  559. return ret;
  560. }
  561. static av_cold int libjxl_encode_close(AVCodecContext *avctx)
  562. {
  563. LibJxlEncodeContext *ctx = avctx->priv_data;
  564. if (ctx->runner)
  565. JxlThreadParallelRunnerDestroy(ctx->runner);
  566. ctx->runner = NULL;
  567. /*
  568. * destroying the encoder also frees
  569. * ctx->options so we don't need to
  570. */
  571. if (ctx->encoder)
  572. JxlEncoderDestroy(ctx->encoder);
  573. ctx->encoder = NULL;
  574. av_freep(&ctx->buffer);
  575. av_frame_free(&ctx->prev);
  576. av_frame_free(&ctx->frame);
  577. return 0;
  578. }
  579. #define OFFSET(x) offsetof(LibJxlEncodeContext, x)
  580. #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
  581. static const AVOption libjxl_encode_options[] = {
  582. { "effort", "Encoding effort", OFFSET(effort), AV_OPT_TYPE_INT, { .i64 = 7 }, 1, 9, VE },
  583. { "distance", "Maximum Butteraugli distance (quality setting, "
  584. "lower = better, zero = lossless, default 1.0)", OFFSET(distance), AV_OPT_TYPE_FLOAT, { .dbl = -1.0 }, -1.0, 15.0, VE },
  585. { "modular", "Force modular mode", OFFSET(modular), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE },
  586. { "xyb", "Use XYB-encoding for lossy images", OFFSET(xyb),
  587. AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE },
  588. { NULL },
  589. };
  590. static const AVClass libjxl_encode_class = {
  591. .class_name = "libjxl",
  592. .item_name = av_default_item_name,
  593. .option = libjxl_encode_options,
  594. .version = LIBAVUTIL_VERSION_INT,
  595. };
  596. static const enum AVPixelFormat libjxl_supported_pixfmts[] = {
  597. AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA,
  598. AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64,
  599. AV_PIX_FMT_RGBF32, AV_PIX_FMT_RGBAF32,
  600. AV_PIX_FMT_GRAY8, AV_PIX_FMT_YA8,
  601. AV_PIX_FMT_GRAY16, AV_PIX_FMT_YA16,
  602. AV_PIX_FMT_GRAYF32,
  603. AV_PIX_FMT_NONE,
  604. };
  605. const FFCodec ff_libjxl_encoder = {
  606. .p.name = "libjxl",
  607. CODEC_LONG_NAME("libjxl JPEG XL"),
  608. .p.type = AVMEDIA_TYPE_VIDEO,
  609. .p.id = AV_CODEC_ID_JPEGXL,
  610. .priv_data_size = sizeof(LibJxlEncodeContext),
  611. .init = libjxl_encode_init,
  612. FF_CODEC_ENCODE_CB(libjxl_encode_frame),
  613. .close = libjxl_encode_close,
  614. .p.capabilities = AV_CODEC_CAP_OTHER_THREADS |
  615. AV_CODEC_CAP_DR1 |
  616. AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
  617. .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE |
  618. FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP |
  619. FF_CODEC_CAP_ICC_PROFILES,
  620. CODEC_PIXFMTS_ARRAY(libjxl_supported_pixfmts),
  621. .p.priv_class = &libjxl_encode_class,
  622. .p.wrapper_name = "libjxl",
  623. };
  624. const FFCodec ff_libjxl_anim_encoder = {
  625. .p.name = "libjxl_anim",
  626. CODEC_LONG_NAME("libjxl JPEG XL animated"),
  627. .p.type = AVMEDIA_TYPE_VIDEO,
  628. .p.id = AV_CODEC_ID_JPEGXL_ANIM,
  629. .priv_data_size = sizeof(LibJxlEncodeContext),
  630. .init = libjxl_anim_encode_init,
  631. FF_CODEC_RECEIVE_PACKET_CB(libjxl_anim_encode_frame),
  632. .close = libjxl_encode_close,
  633. .p.capabilities = AV_CODEC_CAP_OTHER_THREADS |
  634. AV_CODEC_CAP_DR1 |
  635. AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
  636. .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE |
  637. FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP |
  638. FF_CODEC_CAP_ICC_PROFILES,
  639. CODEC_PIXFMTS_ARRAY(libjxl_supported_pixfmts),
  640. .p.priv_class = &libjxl_encode_class,
  641. .p.wrapper_name = "libjxl",
  642. };