]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Update VAAPI transcoding as recommended by ffmpeg 6.1.1/doc/examples/… (#1792)
authorUkn Unknown <4031821+uknunknown@users.noreply.github.com>
Mon, 19 May 2025 13:21:52 +0000 (06:21 -0700)
committerGitHub <noreply@github.com>
Mon, 19 May 2025 13:21:52 +0000 (17:21 +0400)
Update VAAPI transcoding as recommended by ffmpeg 6.1.1/doc/examples/vaapi_*.c

src/libav.h
src/transcoding/transcode/hwaccels/hwaccels.c
src/transcoding/transcode/hwaccels/hwaccels.h
src/transcoding/transcode/hwaccels/vaapi.c
src/transcoding/transcode/hwaccels/vaapi.h
src/transcoding/transcode/video.c

index c73b61089e6368020378c37e05440038a669dcb0..902b305d852cd58ec28a584a9d513d76ba31e3fc 100644 (file)
@@ -54,6 +54,13 @@ This list must be updated every time we use a new AV_CODEC_ID
 #define AV_CODEC_ID_DVB_TELETEXT CODEC_ID_DVB_TELETEXT
 #endif
 
+// Enable new transcoding starting ffmpeg 4
+#if LIBAVCODEC_VERSION_MAJOR < 58
+#define ENABLE_FFMPEG4_TRANSCODING 0
+#else
+#define ENABLE_FFMPEG4_TRANSCODING 1
+#endif
+
 enum AVCodecID streaming_component_type2codec_id(streaming_component_type_t type);
 streaming_component_type_t codec_id2streaming_component_type(enum AVCodecID id);
 void libav_set_loglevel(void);
index 892bbbc2f6b1f547446a253bac1e536cd86dc373..57524c44e249fd3316b5e936f6bb4ea32b796eda 100644 (file)
@@ -213,13 +213,43 @@ hwaccels_get_sharpness_filter(AVCodecContext *avctx, int value, char *filter, si
 
 /* encoding ================================================================= */
 
+#if ENABLE_FFMPEG4_TRANSCODING
 int
+hwaccels_initialize_encoder_from_decoder(const AVCodecContext *iavctx, AVCodecContext *oavctx)
+{
+    switch (iavctx->pix_fmt) {
+        case AV_PIX_FMT_VAAPI:
+            /* we need to ref hw_frames_ctx of decoder to initialize encoder's codec.
+            Only after we get a decoded frame, can we obtain its hw_frames_ctx */
+            oavctx->hw_frames_ctx = av_buffer_ref(iavctx->hw_frames_ctx);
+            if (!oavctx->hw_frames_ctx) {
+                return AVERROR(ENOMEM);
+            }
+            return 0;
+        case AV_PIX_FMT_YUV420P:
+            break;
+        default:
+            break;
+    }
+    return 0;
+}
+#endif
+
+int
+#if ENABLE_FFMPEG4_TRANSCODING
+hwaccels_encode_setup_context(AVCodecContext *avctx)
+#else
 hwaccels_encode_setup_context(AVCodecContext *avctx, int low_power)
+#endif
 {
     switch (avctx->pix_fmt) {
 #if ENABLE_VAAPI
         case AV_PIX_FMT_VAAPI:
+#if ENABLE_FFMPEG4_TRANSCODING
+            return vaapi_encode_setup_context(avctx);
+#else
             return vaapi_encode_setup_context(avctx, low_power);
+#endif
 #endif
         default:
             break;
index ca129ef883cc5cc40997a907b6a4341c76ee9cc1..ed04f67485fd8f4540585bf0675e1c2e00c6e9fc 100644 (file)
@@ -23,7 +23,7 @@
 
 
 #include "tvheadend.h"
-
+#include "../internals.h"
 #include <libavcodec/avcodec.h>
 
 
@@ -51,9 +51,17 @@ hwaccels_get_sharpness_filter(AVCodecContext *avctx, int value, char *filter, si
 
 
 /* encoding ================================================================= */
+#if ENABLE_FFMPEG4_TRANSCODING
+int
+hwaccels_initialize_encoder_from_decoder(const AVCodecContext *iavctx, AVCodecContext *oavctx);
+#endif
 
 int
+#if ENABLE_FFMPEG4_TRANSCODING
+hwaccels_encode_setup_context(AVCodecContext *avctx);
+#else
 hwaccels_encode_setup_context(AVCodecContext *avctx, int low_power);
+#endif
 
 void
 hwaccels_encode_close_context(AVCodecContext *avctx);
index 3e8dab19a1d6e09d5dfeebc0859bd05191172d1d..711f1156023f6b73d0d9e367c3dac61df7bc79e0 100644 (file)
 
 #include <va/va.h>
 
+#if ENABLE_FFMPEG4_TRANSCODING
+typedef struct tvh_vaapi_context_t {
+    int width;
+    int height;
+    AVBufferRef *hw_device_ref;
+    AVBufferRef *hw_frame_ref;
+} TVHVAContext;
+
+
+static void
+tvhva_done()
+{
+    /* nothing to do */
+}
+
 
+/* TVHVAContext ============================================================= */
+
+static void
+tvhva_context_destroy(TVHVAContext *self)
+{
+    if (self) {
+        if (self->hw_device_ref) {
+            av_buffer_unref(&self->hw_device_ref);
+            self->hw_device_ref = NULL;
+        }
+        if (self->hw_frame_ref) {
+            av_buffer_unref(&self->hw_frame_ref);
+            self->hw_frame_ref = NULL;
+        }
+        free(self);
+        self = NULL;
+    }
+}
+#else
 typedef struct tvh_vaapi_device {
     char *hw_device_name;
     AVBufferRef *hw_device_ref;
@@ -569,6 +603,7 @@ tvhva_context_create(const char *logpref,
     }
     return self;
 }
+#endif
 
 
 /* decoding ================================================================= */
@@ -588,6 +623,34 @@ vaapi_decode_setup_context(AVCodecContext *avctx)
 {
     TVHContext *ctx = avctx->opaque;
 
+#if ENABLE_FFMPEG4_TRANSCODING
+    TVHVAContext *self = NULL;
+    int ret = -1;
+
+    if (!(self = calloc(1, sizeof(TVHVAContext)))) {
+        tvherror(LS_VAAPI, "Decode: Failed to allocate VAAPI context (TVHVAContext)");
+        return AVERROR(ENOMEM);
+    }
+
+    /* Open VAAPI device and create an AVHWDeviceContext for it*/
+    if ((ret = av_hwdevice_ctx_create(&self->hw_device_ref, AV_HWDEVICE_TYPE_VAAPI, ctx->hw_accel_device, NULL, 0)) < 0) {
+        tvherror(LS_VAAPI, "Decode: Failed to Open VAAPI device and create an AVHWDeviceContext for device: "
+                            "%s with error code: %s", 
+                            ctx->hw_accel_device, av_err2str(ret));
+        return ret;
+    }
+
+    /* set hw_frames_ctx for decoder's AVCodecContext */
+    avctx->hw_device_ctx = av_buffer_ref(self->hw_device_ref);
+    if (!avctx->hw_device_ctx) {
+        tvherror(LS_VAAPI, "Decode: Failed to create a hardware device reference for device: %s.", 
+                        ctx->hw_accel_device);
+        return AVERROR(ENOMEM);
+    }
+    ctx->hw_accel_ictx = self;
+    avctx->get_buffer2 = vaapi_get_buffer2;
+    avctx->pix_fmt = AV_PIX_FMT_VAAPI;
+#else
     if (!(ctx->hw_accel_ictx =
           tvhva_context_create("decode", avctx, VAEntrypointVLD))) {
         return -1;
@@ -597,6 +660,7 @@ vaapi_decode_setup_context(AVCodecContext *avctx)
 #if LIBAVCODEC_VERSION_MAJOR < 60
     avctx->thread_safe_callbacks = 0;
 #endif
+#endif 
 
     return 0;
 }
@@ -607,6 +671,12 @@ vaapi_decode_close_context(AVCodecContext *avctx)
 {
     TVHContext *ctx = avctx->opaque;
 
+#if ENABLE_FFMPEG4_TRANSCODING
+    if (avctx->hw_device_ctx) {
+        av_buffer_unref(&avctx->hw_device_ctx);
+        avctx->hw_device_ctx = NULL;
+    }
+#endif
     tvhva_context_destroy(ctx->hw_accel_ictx);
 }
 
@@ -645,7 +715,69 @@ vaapi_get_sharpness_filter(AVCodecContext *avctx, int value, char *filter, size_
 
 /* encoding ================================================================= */
 
+#if ENABLE_FFMPEG4_TRANSCODING
+// lifted from ffmpeg-6.1.1/doc/examples/vaapi_encode.c line 41
+static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx)
+{
+    AVBufferRef *hw_frames_ref;
+    AVHWFramesContext *frames_ctx = NULL;
+    int err = 0;
+
+    if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) {
+        tvherror(LS_VAAPI, "Encode: Failed to create VAAPI frame context.");
+        return AVERROR(ENOMEM);
+    }
+    frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
+    frames_ctx->format    = AV_PIX_FMT_VAAPI;
+    frames_ctx->sw_format = AV_PIX_FMT_NV12;
+    frames_ctx->width     = ctx->width;
+    frames_ctx->height    = ctx->height;
+    frames_ctx->initial_pool_size = 20;
+    if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
+        tvherror(LS_VAAPI, "Encode: Failed to initialize VAAPI frame context."
+                "Error code: %s",av_err2str(err));
+        av_buffer_unref(&hw_frames_ref);
+        return err;
+    }
+    ctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
+    if (!ctx->hw_frames_ctx) {
+        err = AVERROR(ENOMEM);
+        tvherror(LS_VAAPI, "Encode: Failed to create a hardware device reference."
+                "Error code: %s",av_err2str(err));
+    }
+    av_buffer_unref(&hw_frames_ref);
+    return err;
+}
+#endif
+
 int
+#if ENABLE_FFMPEG4_TRANSCODING
+vaapi_encode_setup_context(AVCodecContext *avctx)
+{
+    TVHContext *ctx = avctx->opaque;
+    TVHVAContext *self = NULL;
+    int ret = 0;
+
+    if (!(self = calloc(1, sizeof(TVHVAContext)))) {
+        tvherror(LS_VAAPI, "Encode: Failed to allocate VAAPI context (TVHVAContext)");
+        return AVERROR(ENOMEM);
+    }
+
+    /* Open VAAPI device and create an AVHWDeviceContext for it*/
+    if ((ret = av_hwdevice_ctx_create(&self->hw_frame_ref, AV_HWDEVICE_TYPE_VAAPI, NULL, NULL, 0)) < 0) {
+        tvherror(LS_VAAPI, "Encode: Failed to open VAAPI device and create an AVHWDeviceContext for it."
+                "Error code: %s",av_err2str(ret));
+        return ret;
+    }
+
+    /* set hw_frames_ctx for encoder's AVCodecContext */
+    if ((ret = set_hwframe_ctx(avctx, self->hw_frame_ref)) < 0) {
+        tvherror(LS_VAAPI, "Encode: Failed to set hwframe context."
+                "Error code: %s",av_err2str(ret));
+        return ret;
+    }
+    ctx->hw_device_octx = av_buffer_ref(self->hw_frame_ref);
+#else
 vaapi_encode_setup_context(AVCodecContext *avctx, int low_power)
 {
     TVHContext *ctx = avctx->opaque;
@@ -668,6 +800,7 @@ vaapi_encode_setup_context(AVCodecContext *avctx, int low_power)
         return AVERROR(ENOMEM);
     }
     tvhva_context_destroy(hwaccel_context);
+#endif
     return 0;
 }
 
@@ -678,6 +811,16 @@ vaapi_encode_close_context(AVCodecContext *avctx)
     TVHContext *ctx = avctx->opaque;
     av_buffer_unref(&ctx->hw_device_octx);
     ctx->hw_device_octx = NULL;
+#if ENABLE_FFMPEG4_TRANSCODING
+    if (avctx->hw_device_ctx) {
+        av_buffer_unref(&avctx->hw_device_ctx);
+        avctx->hw_device_ctx = NULL;
+    }
+    if (avctx->hw_frames_ctx) {
+        av_buffer_unref(&avctx->hw_frames_ctx);
+        avctx->hw_frames_ctx = NULL;
+    }
+#endif
 }
 
 
index 72a2c4babe6ef0ccbeaaa9539b8ae210e617ada4..57417b052701b833d369c0fb9d4f4d16779424e7 100644 (file)
@@ -51,7 +51,11 @@ vaapi_get_sharpness_filter(AVCodecContext *avctx, int value, char *filter, size_
 /* encoding ================================================================= */
 
 int
+#if ENABLE_FFMPEG4_TRANSCODING
+vaapi_encode_setup_context(AVCodecContext *avctx);
+#else
 vaapi_encode_setup_context(AVCodecContext *avctx, int low_power);
+#endif
 
 void
 vaapi_encode_close_context(AVCodecContext *avctx);
index c838fd734a9d0a2deadd89e6d3ee40483fc8e50d..a4b6090c40cf16e1f39c5cbb88b50e430a90f304 100644 (file)
@@ -226,10 +226,37 @@ tvh_video_context_open_encoder(TVHContext *self, AVDictionary **opts)
 #if ENABLE_HWACCELS
     self->oavctx->coded_width = self->oavctx->width;
     self->oavctx->coded_height = self->oavctx->height;
+#if ENABLE_FFMPEG4_TRANSCODING
+    // hwaccel is the user input for Hardware acceleration from Codec parameteres
+    int hwaccel = -1;
+    if ((hwaccel = tvh_codec_profile_video_get_hwaccel(self->profile)) < 0) {
+        return -1;
+    }
+    if (_video_filters_hw_pix_fmt(self->oavctx->pix_fmt)){
+        // encoder is hw accelerated
+        if (hwaccel) {
+            // decoder is hw accelerated 
+            // --> we initialize encoder from decoder as recomanded in: 
+            // ffmpeg-6.1.1/doc/examples/vaapi_transcode.c line 169
+            if (hwaccels_initialize_encoder_from_decoder(self->iavctx, self->oavctx)) {
+                return -1;
+            }
+        }
+        else {
+            // decoder is sw
+            // --> we initialize as recommended in:
+            // ffmpeg-6.1.1/doc/examples/vaapi_encode.c line 145
+            if (hwaccels_encode_setup_context(self->oavctx)) {
+                return -1;
+            }
+        }
+    }
+#else
     if (hwaccels_encode_setup_context(self->oavctx, self->profile->low_power)) {
         return -1;
     }
-#endif
+#endif // from ENABLE_FFMPEG4_TRANSCODING
+#endif // from ENABLE_HWACCELS
 
     // XXX: is this a safe assumption?
     if (!self->iavctx->framerate.num) {