/* * Copyright(c) 2024 Intel Corporation * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Copyright(c) 2024 Intel Corporation * SPDX - License - Identifier: BSD - 2 - Clause - Patent */ #include #include "libavutil/mem.h" #include "libavutil/common.h" #include "libavutil/cpu.h" #include "libavutil/imgutils.h" #include "libavutil/avassert.h" #include "avcodec.h" #include "codec_internal.h" #include "decode.h" #include "profiles.h" typedef struct SvtJpegXsDecodeContext { svt_jpeg_xs_image_config_t config; svt_jpeg_xs_decoder_api_t decoder; svt_jpeg_xs_frame_t input; svt_jpeg_xs_frame_t output; uint32_t decoder_initialized; int proxy_mode; } SvtJpegXsDecodeContext; static int set_pix_fmt(void *logctx, const svt_jpeg_xs_image_config_t *config) { int ret = AVERROR_BUG; switch (config->format) { case COLOUR_FORMAT_PLANAR_YUV420: if (config->bit_depth == 8) return AV_PIX_FMT_YUV420P; else if (config->bit_depth == 10) return AV_PIX_FMT_YUV420P10LE; else if (config->bit_depth == 12) return AV_PIX_FMT_YUV420P12LE; else return AV_PIX_FMT_YUV420P14LE; break; case COLOUR_FORMAT_PLANAR_YUV422: if (config->bit_depth == 8) return AV_PIX_FMT_YUV422P; else if (config->bit_depth == 10) return AV_PIX_FMT_YUV422P10LE; else if (config->bit_depth == 12) return AV_PIX_FMT_YUV422P12LE; else return AV_PIX_FMT_YUV422P14LE; break; case COLOUR_FORMAT_PLANAR_YUV444_OR_RGB: if (config->bit_depth == 8) return AV_PIX_FMT_YUV444P; else if (config->bit_depth == 10) return AV_PIX_FMT_YUV444P10LE; else if (config->bit_depth == 12) return AV_PIX_FMT_YUV444P12LE; else return AV_PIX_FMT_YUV444P14LE; break; default: av_log(logctx, AV_LOG_ERROR, "Unsupported pixel format.\n"); ret = AVERROR_INVALIDDATA; break; } return ret; } static int svt_jpegxs_dec_decode(AVCodecContext* avctx, AVFrame* picture, int* got_frame, AVPacket* avpkt) { SvtJpegXsDecodeContext* svt_dec = avctx->priv_data; SvtJxsErrorType_t err = SvtJxsErrorNone; uint32_t frame_size; int ret; err = svt_jpeg_xs_decoder_get_single_frame_size_with_proxy( avpkt->data, avpkt->size, &svt_dec->config, &frame_size, 1 /*quick search*/, svt_dec->decoder.proxy_mode); if (err) { av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_get_single_frame_size_with_proxy failed, err=%d\n", err); return AVERROR_EXTERNAL; } if (avpkt->size < frame_size) { av_log(avctx, AV_LOG_ERROR, "Not enough data in a packet.\n"); return AVERROR(EINVAL); } if (avpkt->size > frame_size) { av_log(avctx, AV_LOG_ERROR, "Single packet have data for more than one frame.\n"); return AVERROR(EINVAL); } ret = set_pix_fmt(avctx, &svt_dec->config); if (ret < 0) return ret; if (!svt_dec->decoder_initialized || ret != avctx->pix_fmt || avctx->width != svt_dec->config.width || avctx->height != svt_dec->config.height) { if (svt_dec->decoder_initialized) svt_jpeg_xs_decoder_close(&svt_dec->decoder); err = svt_jpeg_xs_decoder_init(SVT_JPEGXS_API_VER_MAJOR, SVT_JPEGXS_API_VER_MINOR, &svt_dec->decoder, avpkt->data, avpkt->size, &svt_dec->config); if (err) { av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_init failed, err=%d\n", err); return AVERROR_EXTERNAL; } avctx->pix_fmt = ret; ret = ff_set_dimensions(avctx, svt_dec->config.width, svt_dec->config.height); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "ff_set_dimensions failed, err=%d\n", ret); return ret; } svt_dec->decoder_initialized = 1; } if (avctx->skip_frame == AVDISCARD_ALL) return 0; svt_dec->input.bitstream.buffer = avpkt->data; svt_dec->input.bitstream.allocation_size = avpkt->size; svt_dec->input.bitstream.used_size = avpkt->size; svt_dec->input.user_prv_ctx_ptr = avpkt; ret = ff_get_buffer(avctx, picture, 0); if (ret < 0) return ret; unsigned pixel_shift = svt_dec->config.bit_depth <= 8 ? 0 : 1; for (int comp = 0; comp < svt_dec->config.components_num; comp++) { svt_dec->input.image.data_yuv[comp] = picture->data[comp]; svt_dec->input.image.stride[comp] = picture->linesize[comp] >> pixel_shift; svt_dec->input.image.alloc_size[comp] = picture->linesize[comp] * svt_dec->config.components[comp].height; } err = svt_jpeg_xs_decoder_send_frame(&svt_dec->decoder, &svt_dec->input, 1 /*blocking*/); if (err) { av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_send_frame failed, err=%d\n", err); return AVERROR_EXTERNAL; } err = svt_jpeg_xs_decoder_get_frame(&svt_dec->decoder, &svt_dec->output, 1 /*blocking*/); if (err) { av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_get_frame failed, err=%d\n", err); return AVERROR_EXTERNAL; } if (svt_dec->output.user_prv_ctx_ptr != avpkt) { av_log(avctx, AV_LOG_ERROR, "Returned different user_prv_ctx_ptr than expected\n"); return AVERROR_EXTERNAL; } *got_frame = 1; return avpkt->size; } static av_cold int svt_jpegxs_dec_free(AVCodecContext* avctx) { SvtJpegXsDecodeContext* svt_dec = avctx->priv_data; svt_jpeg_xs_decoder_close(&svt_dec->decoder); return 0; } static av_cold int svt_jpegxs_dec_init(AVCodecContext* avctx) { SvtJpegXsDecodeContext* svt_dec = avctx->priv_data; int log_level = av_log_get_level(); svt_dec->decoder.verbose = log_level < AV_LOG_DEBUG ? VERBOSE_ERRORS : log_level == AV_LOG_DEBUG ? VERBOSE_SYSTEM_INFO : VERBOSE_WARNINGS; if (avctx->lowres == 1) svt_dec->decoder.proxy_mode = proxy_mode_half; else if (avctx->lowres == 2) svt_dec->decoder.proxy_mode = proxy_mode_quarter; else svt_dec->decoder.proxy_mode = proxy_mode_full; int thread_count = avctx->thread_count ? avctx->thread_count : av_cpu_count(); svt_dec->decoder.threads_num = FFMIN(thread_count, 64); svt_dec->decoder.use_cpu_flags = CPU_FLAGS_ALL; return 0; } const FFCodec ff_libsvtjpegxs_decoder = { .p.name = "libsvtjpegxs", CODEC_LONG_NAME("SVT JPEG XS(Scalable Video Technology for JPEG XS) decoder"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_JPEGXS, .priv_data_size = sizeof(SvtJpegXsDecodeContext), .init = svt_jpegxs_dec_init, .close = svt_jpegxs_dec_free, FF_CODEC_DECODE_CB(svt_jpegxs_dec_decode), .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_AUTO_THREADS, .p.max_lowres = 2, .p.wrapper_name = "libsvtjpegxs", };