Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Zhao Zhili <quinkblack@foxmail.com>
To: ffmpeg-devel@ffmpeg.org
Cc: Zhao Zhili <zhilizhao@tencent.com>
Subject: [FFmpeg-devel] [PATCH v2 2/2] fftools/ffplay: add hwaccel decoding support
Date: Thu, 19 Oct 2023 00:55:16 +0800
Message-ID: <tencent_5B85E615D0BE66C8A7C6AA92530DDC43CB06@qq.com> (raw)
In-Reply-To: <20231018165516.275945-1-quinkblack@foxmail.com>

From: Zhao Zhili <zhilizhao@tencent.com>

---
 fftools/ffplay.c          |  30 ++++++++++
 fftools/ffplay_renderer.c | 117 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 147 insertions(+)

diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index 305d72d8b8..0f9584b57e 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -352,6 +352,7 @@ static int autorotate = 1;
 static int find_stream_info = 1;
 static int filter_nbthreads = 0;
 static int enable_vulkan = 0;
+static const char *hwaccel = NULL;
 
 /* current context */
 static int is_full_screen;
@@ -2557,6 +2558,24 @@ static int audio_open(void *opaque, AVChannelLayout *wanted_channel_layout, int
     return spec.size;
 }
 
+static int create_hwaccel(AVBufferRef **device_ctx)
+{
+    enum AVHWDeviceType type;
+    int ret;
+
+    *device_ctx = NULL;
+
+    if (!hwaccel)
+        return 0;
+
+    type = av_hwdevice_find_type_by_name(hwaccel);
+    if (type == AV_HWDEVICE_TYPE_NONE)
+        return AVERROR(ENOTSUP);
+
+    ret = av_hwdevice_ctx_create(device_ctx, type, NULL, NULL, 0);
+    return ret;
+}
+
 /* open a given stream. Return 0 if OK */
 static int stream_component_open(VideoState *is, int stream_index)
 {
@@ -2624,6 +2643,12 @@ static int stream_component_open(VideoState *is, int stream_index)
 
     av_dict_set(&opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY);
 
+    if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+        ret = create_hwaccel(&avctx->hw_device_ctx);
+        if (ret < 0)
+            goto fail;
+    }
+
     if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {
         goto fail;
     }
@@ -3625,6 +3650,7 @@ static const OptionDef options[] = {
         "read and decode the streams to fill missing information with heuristics" },
     { "filter_threads", HAS_ARG | OPT_INT | OPT_EXPERT, { &filter_nbthreads }, "number of filter threads per graph" },
     { "enable_vulkan", OPT_BOOL, { &enable_vulkan }, "enable vulkan render" },
+    { "hwaccel", HAS_ARG | OPT_STRING | OPT_EXPERT, { &hwaccel }, "use HW accelerated decoding" },
     { NULL, },
 };
 
@@ -3739,6 +3765,10 @@ int main(int argc, char **argv)
 #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
         SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
 #endif
+        if (hwaccel && !enable_vulkan) {
+            av_log(NULL, AV_LOG_INFO, "Enable vulkan renderer to support hwaccel %s\n", hwaccel);
+            enable_vulkan = 1;
+        }
         if (enable_vulkan) {
             vk_renderer = vk_get_renderer();
             if (vk_renderer) {
diff --git a/fftools/ffplay_renderer.c b/fftools/ffplay_renderer.c
index 796a964a7b..bf6ad7fbaf 100644
--- a/fftools/ffplay_renderer.c
+++ b/fftools/ffplay_renderer.c
@@ -50,6 +50,12 @@ typedef struct RendererContext {
     pl_tex tex[4];
 
     pl_log vk_log;
+
+    AVBufferRef *hw_device;
+    AVBufferRef *hw_frame;
+    int hw_failed;
+
+    AVFrame *vk_frame;
 } RendererContext;
 
 static void vk_log_cb(void *log_priv, enum pl_log_level level, const char *msg) {
@@ -133,6 +139,13 @@ static int create(VkRenderer *renderer, SDL_Window *window)
         ret = AVERROR_EXTERNAL;
         goto out;
     }
+
+    ctx->vk_frame = av_frame_alloc();
+    if (!ctx->vk_frame) {
+        ret = AVERROR(ENOMEM);
+        goto out;
+    }
+
     ret = 0;
 
 out:
@@ -140,6 +153,102 @@ out:
     return ret;
 }
 
+static int create_hw(VkRenderer *renderer, AVFrame *frame)
+{
+    RendererContext *ctx = (RendererContext *)renderer;
+    AVHWFramesContext *src_hw_frame = (AVHWFramesContext *)frame->hw_frames_ctx->data;
+    AVBufferRef *src_dev = src_hw_frame->device_ref;
+    int ret;
+
+    if (ctx->hw_failed)
+        return ctx->hw_failed;
+
+    if (!ctx->hw_device) {
+        ret = av_hwdevice_ctx_create_derived(&ctx->hw_device, AV_HWDEVICE_TYPE_VULKAN, src_dev, 0);
+        if (ret < 0) {
+            av_log(renderer, AV_LOG_ERROR, "Derive hwaccel failed, %s\n", av_err2str(ret));
+            ctx->hw_failed = ret;
+            return ret;
+        }
+    }
+
+    if (!ctx->hw_frame) {
+        AVHWFramesContext *hw_frame;
+
+        ctx->hw_frame = av_hwframe_ctx_alloc(ctx->hw_device);
+        if (!ctx->hw_frame) {
+            ctx->hw_failed = AVERROR(ENOMEM);
+            return AVERROR(ENOMEM);
+        }
+
+        hw_frame = (AVHWFramesContext *)ctx->hw_frame->data;
+        hw_frame->format = AV_PIX_FMT_VULKAN;
+        hw_frame->sw_format = src_hw_frame->sw_format;
+        hw_frame->width = frame->width;
+        hw_frame->height = frame->height;
+
+        ret = av_hwframe_ctx_init(ctx->hw_frame);
+        if (ret < 0) {
+            av_log(renderer, AV_LOG_ERROR, "Create hwframe context failed, %s\n", av_err2str(ret));
+            ctx->hw_failed = ret;
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+static int transfer_frame(VkRenderer *renderer, AVFrame *frame)
+{
+    RendererContext *ctx = (RendererContext *)renderer;
+    int ret;
+
+    if (!frame->hw_frames_ctx)
+        return 0;
+
+    if (frame->format == AV_PIX_FMT_VULKAN)
+        return 0;
+
+    ret = create_hw(renderer, frame);
+    if (ret < 0 && ret != AVERROR(ENOSYS))
+        return ret;
+
+    // Try map data first
+    av_frame_unref(ctx->vk_frame);
+    if (ctx->hw_frame) {
+        ctx->vk_frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frame);
+        ctx->vk_frame->format = AV_PIX_FMT_VULKAN;
+    }
+    ret = av_hwframe_map(ctx->vk_frame, frame, AV_HWFRAME_MAP_READ);
+    if (!ret) {
+        goto out;
+    }
+
+    if (ret != AVERROR(ENOSYS)) {
+        av_log(NULL, AV_LOG_FATAL, "Map data to vulkan failed: %s\n", av_err2str(ret));
+        return ret;
+    }
+
+    // Try transfer data
+    av_frame_unref(ctx->vk_frame);
+    if (ctx->hw_frame)
+        av_hwframe_get_buffer(ctx->hw_frame, ctx->vk_frame, 0);
+    ret = av_hwframe_transfer_data(ctx->vk_frame, frame, 0);
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_FATAL, "Transfer data to vulkan failed: %s\n", av_err2str(ret));
+        return ret;
+    }
+
+out:
+    ret = av_frame_copy_props(ctx->vk_frame, frame);
+    if (ret < 0)
+        return ret;
+    av_frame_unref(frame);
+    av_frame_move_ref(frame, ctx->vk_frame);
+
+    return 0;
+}
+
 static int display(VkRenderer *renderer, AVFrame *frame)
 {
     struct pl_swapchain_frame swap_frame = {0};
@@ -148,6 +257,10 @@ static int display(VkRenderer *renderer, AVFrame *frame)
     RendererContext *ctx = (RendererContext *)renderer;
     int ret = 0;
 
+    ret = transfer_frame(renderer, frame);
+    if (ret < 0)
+        return ret;
+
     if (!pl_map_avframe_ex(ctx->pl_vk->gpu, &pl_frame, pl_avframe_params(
             .frame = frame,
             .tex = ctx->tex))) {
@@ -194,6 +307,10 @@ static void destroy(VkRenderer *renderer)
     PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
     RendererContext *ctx = (RendererContext *)renderer;
 
+    av_buffer_unref(&ctx->hw_frame);
+    av_buffer_unref(&ctx->hw_device);
+    av_frame_free(&ctx->vk_frame);
+
     for (int i = 0; i < FF_ARRAY_ELEMS(ctx->tex); i++)
         pl_tex_destroy(ctx->pl_vk->gpu, &ctx->tex[i]);
     pl_renderer_destroy(&ctx->renderer);
-- 
2.34.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

       reply	other threads:[~2023-10-18 16:55 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20231018165516.275945-1-quinkblack@foxmail.com>
2023-10-18 16:55 ` Zhao Zhili [this message]
2023-10-18 17:05   ` Zhao Zhili
     [not found]   ` <tencent_C04DA1CF8A5540A716B8A2162D4D26207507@qq.com-Nh2k2V1----9>
2023-10-19  1:31     ` Lynne
2023-10-19  1:41       ` Zhao Zhili
2023-10-19  3:31   ` Lynne
2023-10-19  3:51     ` Zhao Zhili

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=tencent_5B85E615D0BE66C8A7C6AA92530DDC43CB06@qq.com \
    --to=quinkblack@foxmail.com \
    --cc=ffmpeg-devel@ffmpeg.org \
    --cc=zhilizhao@tencent.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git