PR #25142 opened by rnissl
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25142
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25142.patch

libgpujpeg encodes CUDA frames to JPEG through CESNET GPUJPEG. On the same 106 
frames it is faster than mjpeg_nvjpeg and, at the highest quality that stays 
under the CPU JPEG size, also closer to that size.

106 frames, 7680x3840, chroma 4:2:0. The CPU reference is mjpeg -qscale:v 2: 
291564015 bytes, wall time 25.638 s.

| encoder | quality | bytes | share of mjpeg | wall time |
| --- | ---: | ---: | ---: | ---: |
| mjpeg | qscale 2 | 291564015 | 100% | 25.638 s |
| mjpeg_nvjpeg | 83 | 245574607 | 84.2% | 24.138 s |
| mjpeg_nvjpeg | 87 | 279686107 | 95.9% | 23.979 s |
| libgpujpeg | 84 | 287545054 | 98.6% | 16.773 s |

87 is the highest nvJPEG quality that stays under the CPU sum. 88 crosses it at 
296610157 bytes, and the wall time stays about 24 s. 84 is the highest GPUJPEG 
quality under that sum. 85 crosses it at 293380648 bytes, and the wall time 
stays about 16.8 s. Quality 84 is therefore both the closer file size and the 
shorter run: 16.773 s against 23.979 s. User CPU time is about 9 s for either 
GPU encoder. The CPU encoder's user time is 111.406 s; its wall time is shorter 
because several cores overlap.

Input is CUDA yuv444p, full-range BT.601. The encoder does not convert. 4:4:4 
passes linesize minus width as width_padding and does not pack the planes. 
4:2:0 uses the library color kernel, which ignores that padding, so linesize 
must equal the width.

Build with --enable-libgpujpeg and --enable-nonfree. ffnvcodec is required. 
COPYING is BSD 2-Clause. The DCT source carries the NVIDIA 1993-2010 EULA, and 
a shared build links the CUDA runtime, so the FFmpeg binary is 
unredistributable. That finding is comment 71750 on #24727. This request does 
not change mjpeg_nvjpeg.

Head a6e4ab15e0 on master 2fd864c2e9. libavcodec minor goes from 20 to 21, so 
it will conflict with #24727 on version.h.

## Test plan

- [ ] CI on this pull request
- [ ] configure with --enable-libgpujpeg and --enable-nonfree
- [ ] encode a CUDA yuv444p frame to 4:4:4 and, when linesize equals the width, 
to 4:2:0



From a6e4ab15e0b2cfff20af741acc24f64bcf31f778 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= <[email protected]>
Date: Sat, 17 Oct 2026 08:31:14 +0200
Subject: [PATCH] avcodec: add a CUDA JPEG encoder through libgpujpeg
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

CESNET GPUJPEG encodes CUDA yuv444p in place. 4:4:4 passes the frame
stride as width_padding. 4:2:0 needs linesize equal to the width,
because that path ignores the padding. The library COPYING file is
BSD 2-Clause, but the DCT source carries an NVIDIA EULA and the build
links the CUDA runtime, so the encoder depends on the nonfree switch.

On 106 frames of 7680x3840 4:2:0, the largest output that stays under
the CPU mjpeg -qscale:v 2 sum is quality 84: 287545054 bytes in 16.773 s.
mjpeg_nvjpeg reaches quality 87 at 279686107 bytes in 23.979 s before
it crosses that sum. The GPUJPEG result is both closer to the CPU size
and faster.

Co-authored-by: Cursor <[email protected]>
Signed-off-by: Reinhard Nißl <[email protected]>
---
 Changelog                  |   1 +
 configure                  |   4 +
 doc/encoders.texi          |  28 +++
 doc/general_contents.texi  |   8 +
 libavcodec/Makefile        |   1 +
 libavcodec/allcodecs.c     |   1 +
 libavcodec/libgpujpegenc.c | 376 +++++++++++++++++++++++++++++++++++++
 libavcodec/version.h       |   2 +-
 8 files changed, 420 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/libgpujpegenc.c

diff --git a/Changelog b/Changelog
index 32e7c8bfe7..29180b6e4c 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest 
within each release,
 releases are sorted from youngest to oldest.
 
 version <next>:
+- JPEG encoding via libgpujpeg
 - ASTC image encoding and decoding via libastcenc, with .astc and KTX 1.0 
muxers and demuxers
 - extensively improved AAC encoder
 - APV Vulkan encoder
diff --git a/configure b/configure
index c97ddb186f..6a3438c24c 100755
--- a/configure
+++ b/configure
@@ -240,6 +240,7 @@ External library support:
   --enable-libfribidi      enable libfribidi, improves drawtext filter [no]
   --enable-libharfbuzz     enable libharfbuzz, needed for drawtext filter [no]
   --enable-libgme          enable Game Music Emu via libgme [no]
+  --enable-libgpujpeg      enable JPEG encoding via GPUJPEG [no]
   --enable-libgsm          enable GSM de/encoding via libgsm [no]
   --enable-libiec61883     enable iec61883 via libiec61883 [no]
   --enable-libilbc         enable iLBC de/encoding via libilbc [no]
@@ -2033,6 +2034,7 @@ EXTERNAL_LIBRARY_NONFREE_LIST="
     decklink
     libfdk_aac
     libfdk_xhe_aac
+    libgpujpeg
     libmpeghdec
 "
 
@@ -3811,6 +3813,7 @@ libfdk_aac_encoder_select="audio_frame_queue"
 libfdk_xhe_aac_encoder_deps="libfdk_xhe_aac"
 libfdk_xhe_aac_encoder_select="audio_frame_queue"
 libgme_demuxer_deps="libgme"
+libgpujpeg_encoder_deps="libgpujpeg ffnvcodec"
 libgsm_decoder_deps="libgsm"
 libgsm_encoder_deps="libgsm"
 libgsm_ms_decoder_deps="libgsm"
@@ -7254,6 +7257,7 @@ enabled libfribidi        && require_pkg_config 
libfribidi fribidi fribidi.h fri
 enabled libharfbuzz       && require_pkg_config libharfbuzz harfbuzz hb.h 
hb_buffer_create
 enabled libgme            && { check_pkg_config libgme libgme gme/gme.h 
gme_new_emu ||
                                require libgme gme/gme.h gme_new_emu -lgme 
-lstdc++; }
+enabled libgpujpeg        && require_pkg_config libgpujpeg libgpujpeg 
libgpujpeg/gpujpeg_encoder.h gpujpeg_encoder_create
 enabled libgsm            && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do
                                    check_lib libgsm "${gsm_hdr}" gsm_create 
-lgsm && break;
                                done || die "ERROR: libgsm not found"; }
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 2c173ac916..82280546cc 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -1347,6 +1347,34 @@ a value of 3 produces a quantization table of all ones.
 
 @end table
 
+@anchor{libgpujpeg}
+@section libgpujpeg
+
+JPEG encoder through CESNET GPUJPEG. It encodes CUDA frames in place.
+The input software format is @code{yuv444p}, full-range BT.601.
+
+Build it with @code{--enable-libgpujpeg} and @code{--enable-nonfree}.
+GPUJPEG's COPYING file is BSD 2-Clause. The DCT implementation in the
+library carries an NVIDIA EULA notice, and the library links the CUDA
+runtime, so the FFmpeg binary is unredistributable.
+@code{ffnvcodec} is required and is autodetected.
+
+The three planes must follow one another in one allocation.
+Row padding is passed through as @code{width_padding} and needs no extra
+copy for 4:4:4 output. 4:2:0 output uses the library color kernel, which
+ignores that padding, so it requires @code{linesize} equal to the width.
+
+@subsection Options
+
+@table @option
+@item quality
+JPEG quality from 0 to 100. The default is 75.
+
+@item chroma
+Output subsampling. @code{444} is the default. @code{420} requires a frame
+whose line stride equals its width.
+@end table
+
 @anchor{wavpackenc}
 @section wavpack
 
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 7032f140c6..fce6520090 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -365,6 +365,14 @@ constrained baseline profile and CABAC.) Using it is 
mostly useful for
 testing and for taking advantage of Cisco's patent portfolio license
 (@url{http://www.openh264.org/BINARY_LICENSE.txt}).
 
+@section GPUJPEG
+
+FFmpeg can use the CESNET GPUJPEG library to encode CUDA frames to JPEG.
+The library's COPYING file is BSD 2-Clause. Its DCT source also carries an
+NVIDIA EULA notice, and the build links the CUDA runtime, so the resulting
+binary is nonfree. Pass @code{--enable-libgpujpeg} and @code{--enable-nonfree}
+to @file{./configure}.
+
 @section OpenJPEG
 
 FFmpeg can use the OpenJPEG libraries for decoding/encoding J2K videos.  Go to
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 249b0fe0b8..53131910b3 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1195,6 +1195,7 @@ OBJS-$(CONFIG_LIBDAVS2_DECODER)           += libdavs2.o
 OBJS-$(CONFIG_LIBFDK_AAC_DECODER)         += libfdk-aacdec.o
 OBJS-$(CONFIG_LIBFDK_AAC_ENCODER)         += libfdk-aacenc.o
 OBJS-$(CONFIG_LIBFDK_XHE_AAC_ENCODER)     += libfdk-xhe-aacenc.o
+OBJS-$(CONFIG_LIBGPUJPEG_ENCODER)         += libgpujpegenc.o
 OBJS-$(CONFIG_LIBGSM_DECODER)             += libgsmdec.o
 OBJS-$(CONFIG_LIBGSM_ENCODER)             += libgsmenc.o
 OBJS-$(CONFIG_LIBGSM_MS_DECODER)          += libgsmdec.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index c344cb539e..b10ff71a57 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -786,6 +786,7 @@ extern const FFCodec ff_libdavs2_decoder;
 extern const FFCodec ff_libfdk_aac_encoder;
 extern const FFCodec ff_libfdk_aac_decoder;
 extern const FFCodec ff_libfdk_xhe_aac_encoder;
+extern const FFCodec ff_libgpujpeg_encoder;
 extern const FFCodec ff_libgsm_encoder;
 extern const FFCodec ff_libgsm_decoder;
 extern const FFCodec ff_libgsm_ms_encoder;
diff --git a/libavcodec/libgpujpegenc.c b/libavcodec/libgpujpegenc.c
new file mode 100644
index 0000000000..90296455ab
--- /dev/null
+++ b/libavcodec/libgpujpegenc.c
@@ -0,0 +1,376 @@
+/*
+ * JPEG encoder through CESNET GPUJPEG.
+ *
+ * Copyright (c) 2026 Reinhard Nißl
+ *
+ * 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
+ */
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libgpujpeg/gpujpeg_encoder.h>
+
+#include "libavutil/common.h"
+#include "libavutil/cuda_check.h"
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_cuda_internal.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/rational.h"
+
+#include "avcodec.h"
+#include "codec_internal.h"
+#include "encode.h"
+#include "hwconfig.h"
+
+#define CHECK_CU(x) FF_CUDA_CHECK_DL(avctx, s->hwctx->internal->cuda_dl, x)
+
+enum {
+    GPUJPEG_CHROMA_444 = 0,
+    GPUJPEG_CHROMA_420 = 1,
+};
+
+typedef struct GPUJPEGEncContext {
+    const AVClass *class;
+
+    int quality;
+    int chroma;
+
+    struct gpujpeg_encoder *encoder;
+    AVCUDADeviceContext *hwctx;
+    enum AVPixelFormat sw_format;
+    int csp_warned;
+} GPUJPEGEncContext;
+
+static const AVCodecHWConfigInternal *const gpujpeg_hw_configs[] = {
+    HW_CONFIG_ENCODER_FRAMES(CUDA, CUDA),
+    HW_CONFIG_ENCODER_DEVICE(NONE, CUDA),
+    NULL
+};
+
+/**
+ * GPUJPEG writes square pixels. Store a non-square SAR in the JFIF APP0
+ * density fields with units 0, which means an aspect ratio.
+ */
+static void gpujpeg_write_sar(AVCodecContext *avctx, uint8_t *data, int size)
+{
+    AVRational sar = avctx->sample_aspect_ratio;
+    int i;
+
+    if (sar.num <= 0 || sar.den <= 0)
+        return;
+    if (sar.num > 65535 || sar.den > 65535) {
+        if (!av_reduce(&sar.num, &sar.den, avctx->sample_aspect_ratio.num,
+                       avctx->sample_aspect_ratio.den, 65535))
+            av_log(avctx, AV_LOG_WARNING,
+                   "Cannot store exact sample aspect ratio %d:%d\n",
+                   avctx->sample_aspect_ratio.num,
+                   avctx->sample_aspect_ratio.den);
+        if (!sar.num)
+            sar = (AVRational) { 1, 65535 };
+        else if (!sar.den)
+            sar = (AVRational) { 65535, 1 };
+    }
+    if (size < 20 || data[0] != 0xff || data[1] != 0xd8)
+        goto missing;
+    i = 2;
+    while (i + 18 < size && data[i] == 0xff) {
+        int marker = data[i + 1];
+        int len;
+
+        if (marker == 0xd8 || marker == 0xd9 || (marker >= 0xd0 && marker <= 
0xd7)) {
+            i += 2;
+            continue;
+        }
+        if (i + 4 > size)
+            break;
+        len = (data[i + 2] << 8) | data[i + 3];
+        if (len < 2 || i + 2 + len > size)
+            break;
+        if (marker == 0xe0 && len >= 16 && !memcmp(data + i + 4, "JFIF\0", 5)) 
{
+            data[i + 11] = 0;
+            AV_WB16(data + i + 12, sar.num);
+            AV_WB16(data + i + 14, sar.den);
+            return;
+        }
+        i += 2 + len;
+    }
+missing:
+    av_log(avctx, AV_LOG_WARNING,
+           "Sample aspect ratio %d:%d was not written; no JFIF APP0\n",
+           sar.num, sar.den);
+}
+
+static void gpujpeg_warn_colorspace(AVCodecContext *avctx, const AVFrame 
*frame,
+                                    int *warned)
+{
+    if (*warned)
+        return;
+    if (frame->color_range && frame->color_range != AVCOL_RANGE_JPEG) {
+        av_log(avctx, AV_LOG_WARNING,
+               "JPEG stores full range; frame range is %s\n",
+               av_color_range_name(frame->color_range));
+        *warned = 1;
+    }
+    if (frame->colorspace && frame->colorspace != AVCOL_SPC_BT470BG &&
+        frame->colorspace != AVCOL_SPC_SMPTE170M) {
+        av_log(avctx, AV_LOG_WARNING,
+               "JPEG JFIF assumes BT.601; frame matrix is %s\n",
+               av_color_space_name(frame->colorspace));
+        *warned = 1;
+    }
+}
+
+static av_cold int gpujpeg_encode_init(AVCodecContext *avctx)
+{
+    GPUJPEGEncContext *s = avctx->priv_data;
+    AVHWFramesContext *frames_ctx;
+    CUcontext dummy;
+    int ret, pushed = 0;
+
+    if (avctx->hw_frames_ctx) {
+        frames_ctx = (AVHWFramesContext *)avctx->hw_frames_ctx->data;
+        if (frames_ctx->format != AV_PIX_FMT_CUDA) {
+            av_log(avctx, AV_LOG_ERROR, "hw_frames_ctx must be CUDA\n");
+            return AVERROR(EINVAL);
+        }
+        s->hwctx = frames_ctx->device_ctx->hwctx;
+        s->sw_format = frames_ctx->sw_format;
+    } else if (avctx->hw_device_ctx) {
+        AVHWDeviceContext *dev = (AVHWDeviceContext 
*)avctx->hw_device_ctx->data;
+
+        if (dev->type != AV_HWDEVICE_TYPE_CUDA) {
+            av_log(avctx, AV_LOG_ERROR, "hw_device_ctx must be CUDA\n");
+            return AVERROR(EINVAL);
+        }
+        s->hwctx = dev->hwctx;
+        s->sw_format = AV_PIX_FMT_NONE;
+    } else {
+        av_log(avctx, AV_LOG_ERROR, "CUDA frames or device context 
required\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (s->sw_format != AV_PIX_FMT_NONE &&
+        s->sw_format != AV_PIX_FMT_YUV444P) {
+        av_log(avctx, AV_LOG_ERROR, "GPUJPEG needs CUDA yuv444p (got %s)\n",
+               av_get_pix_fmt_name(s->sw_format));
+        return AVERROR(ENOSYS);
+    }
+
+    ret = 
CHECK_CU(s->hwctx->internal->cuda_dl->cuCtxPushCurrent(s->hwctx->cuda_ctx));
+    if (ret < 0)
+        return ret;
+    pushed = 1;
+
+    s->encoder = gpujpeg_encoder_create((cudaStream_t)s->hwctx->stream);
+    if (!s->encoder) {
+        av_log(avctx, AV_LOG_ERROR, "gpujpeg_encoder_create failed\n");
+        ret = AVERROR_EXTERNAL;
+        goto fail;
+    }
+
+    ret = CHECK_CU(s->hwctx->internal->cuda_dl->cuCtxPopCurrent(&dummy));
+    if (ret < 0)
+        goto fail;
+    return 0;
+
+fail:
+    if (pushed)
+        s->hwctx->internal->cuda_dl->cuCtxPopCurrent(&dummy);
+    return ret < 0 ? ret : AVERROR_EXTERNAL;
+}
+
+static av_cold int gpujpeg_encode_close(AVCodecContext *avctx)
+{
+    GPUJPEGEncContext *s = avctx->priv_data;
+    CUcontext dummy;
+
+    if (!s->encoder)
+        return 0;
+
+    if (!s->hwctx || !s->hwctx->internal || !s->hwctx->internal->cuda_dl ||
+        !s->hwctx->cuda_ctx ||
+        s->hwctx->internal->cuda_dl->cuCtxPushCurrent(s->hwctx->cuda_ctx) != 
CUDA_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR,
+               "Cannot make the GPUJPEG CUDA context current; encoder not 
destroyed\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    gpujpeg_encoder_destroy(s->encoder);
+    s->encoder = NULL;
+    s->hwctx->internal->cuda_dl->cuCtxPopCurrent(&dummy);
+    return 0;
+}
+
+static int gpujpeg_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
+                                const AVFrame *frame, int *got_packet)
+{
+    GPUJPEGEncContext *s = avctx->priv_data;
+    struct gpujpeg_parameters param;
+    struct gpujpeg_image_parameters ip;
+    struct gpujpeg_encoder_input input;
+    CUcontext dummy;
+    uint8_t *jpg = NULL;
+    size_t len = 0;
+    ptrdiff_t pitch;
+    int ret, pushed = 0;
+    enum AVPixelFormat sw;
+
+    if (!frame)
+        return 0;
+    if (!frame->hw_frames_ctx) {
+        av_log(avctx, AV_LOG_ERROR, "CUDA frame required\n");
+        return AVERROR(EINVAL);
+    }
+
+    {
+        AVHWFramesContext *fc = (AVHWFramesContext 
*)frame->hw_frames_ctx->data;
+
+        sw = fc->sw_format;
+        if (!s->hwctx)
+            s->hwctx = fc->device_ctx->hwctx;
+        if (s->sw_format == AV_PIX_FMT_NONE)
+            s->sw_format = sw;
+    }
+    if (sw != AV_PIX_FMT_YUV444P) {
+        av_log(avctx, AV_LOG_ERROR, "Unsupported CUDA sw_format %s\n",
+               av_get_pix_fmt_name(sw));
+        return AVERROR(ENOSYS);
+    }
+    if (!frame->data[0] || !frame->data[1] || !frame->data[2] ||
+        frame->width <= 0 || frame->height <= 0) {
+        av_log(avctx, AV_LOG_ERROR, "Incomplete CUDA frame\n");
+        return AVERROR(EINVAL);
+    }
+
+    pitch = frame->linesize[0];
+    if (pitch < frame->width ||
+        frame->linesize[1] != pitch || frame->linesize[2] != pitch) {
+        av_log(avctx, AV_LOG_ERROR,
+               "GPUJPEG needs equal plane strides of at least the width\n");
+        return AVERROR(EINVAL);
+    }
+    if ((uintptr_t)frame->data[1] != (uintptr_t)frame->data[0] + 
(uintptr_t)pitch * frame->height ||
+        (uintptr_t)frame->data[2] != (uintptr_t)frame->data[1] + 
(uintptr_t)pitch * frame->height) {
+        av_log(avctx, AV_LOG_ERROR,
+               "GPUJPEG needs Y, Cb and Cr packed one after another\n");
+        return AVERROR(EINVAL);
+    }
+    if (s->chroma != GPUJPEG_CHROMA_444 && pitch != frame->width) {
+        av_log(avctx, AV_LOG_ERROR,
+               "4:2:0 output needs linesize equal to the width; "
+               "this frame has linesize %d\n", frame->linesize[0]);
+        return AVERROR(EINVAL);
+    }
+
+    gpujpeg_warn_colorspace(avctx, frame, &s->csp_warned);
+
+    ret = 
CHECK_CU(s->hwctx->internal->cuda_dl->cuCtxPushCurrent(s->hwctx->cuda_ctx));
+    if (ret < 0)
+        return ret;
+    pushed = 1;
+
+    param = gpujpeg_default_parameters();
+    param.quality = s->quality;
+    param.verbose = GPUJPEG_LL_QUIET;
+    if (s->chroma == GPUJPEG_CHROMA_420)
+        gpujpeg_parameters_chroma_subsampling(&param, GPUJPEG_SUBSAMPLING_420);
+
+    ip = gpujpeg_default_image_parameters();
+    ip.width = frame->width;
+    ip.height = frame->height;
+    ip.color_space = GPUJPEG_YCBCR_JPEG;
+    ip.pixel_format = GPUJPEG_444_U8_P0P1P2;
+    ip.width_padding = (int)(pitch - frame->width);
+
+    gpujpeg_encoder_input_set_gpu_image(&input, frame->data[0]);
+    if (gpujpeg_encoder_encode(s->encoder, &param, &ip, &input, &jpg, &len)) {
+        av_log(avctx, AV_LOG_ERROR, "gpujpeg_encoder_encode failed\n");
+        ret = AVERROR_EXTERNAL;
+        goto fail;
+    }
+    if (!jpg || !len || len > INT_MAX) {
+        av_log(avctx, AV_LOG_ERROR, "GPUJPEG returned no packet\n");
+        ret = AVERROR_EXTERNAL;
+        goto fail;
+    }
+
+    ret = ff_get_encode_buffer(avctx, pkt, (int)len, 0);
+    if (ret < 0)
+        goto fail;
+    memcpy(pkt->data, jpg, len);
+
+    ret = CHECK_CU(s->hwctx->internal->cuda_dl->cuCtxPopCurrent(&dummy));
+    if (ret < 0)
+        goto fail;
+    pushed = 0;
+
+    gpujpeg_write_sar(avctx, pkt->data, pkt->size);
+    pkt->flags |= AV_PKT_FLAG_KEY;
+    *got_packet = 1;
+    return 0;
+
+fail:
+    if (pushed)
+        s->hwctx->internal->cuda_dl->cuCtxPopCurrent(&dummy);
+    return ret < 0 ? ret : AVERROR_EXTERNAL;
+}
+
+#define OFFSET(x) offsetof(GPUJPEGEncContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+    { "quality", "JPEG quality 0-100", OFFSET(quality), AV_OPT_TYPE_INT,
+      { .i64 = 75 }, 0, 100, VE },
+    { "chroma", "output chroma subsampling", OFFSET(chroma), AV_OPT_TYPE_INT,
+      { .i64 = GPUJPEG_CHROMA_444 }, 0, 1, VE, .unit = "chroma" },
+        { "444", "4:4:4", 0, AV_OPT_TYPE_CONST, { .i64 = GPUJPEG_CHROMA_444 }, 
0, 0, VE, .unit = "chroma" },
+        { "420", "4:2:0, requires linesize equal to the width", 0, 
AV_OPT_TYPE_CONST,
+          { .i64 = GPUJPEG_CHROMA_420 }, 0, 0, VE, .unit = "chroma" },
+    { NULL },
+};
+
+static const AVClass gpujpeg_class = {
+    .class_name = "libgpujpeg",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+const FFCodec ff_libgpujpeg_encoder = {
+    .p.name         = "libgpujpeg",
+    CODEC_LONG_NAME("GPUJPEG encoder"),
+    .p.type         = AVMEDIA_TYPE_VIDEO,
+    .p.id           = AV_CODEC_ID_MJPEG,
+    .priv_data_size = sizeof(GPUJPEGEncContext),
+    .init           = gpujpeg_encode_init,
+    FF_CODEC_ENCODE_CB(gpujpeg_encode_frame),
+    .close          = gpujpeg_encode_close,
+    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_HARDWARE |
+                      AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
+    .caps_internal  = FF_CODEC_CAP_NOT_INIT_THREADSAFE | 
FF_CODEC_CAP_INIT_CLEANUP,
+    CODEC_PIXFMTS(AV_PIX_FMT_CUDA),
+    .p.priv_class   = &gpujpeg_class,
+    .p.wrapper_name = "gpujpeg",
+    .hw_configs     = gpujpeg_hw_configs,
+    .color_ranges   = AVCOL_RANGE_JPEG,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index d0980e28de..da6f3a84ac 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
 
 #include "version_major.h"
 
-#define LIBAVCODEC_VERSION_MINOR  20
+#define LIBAVCODEC_VERSION_MINOR  21
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to