| 866 | }; |
| 867 | |
| 868 | static FilterGraph BuildVulkanFilterGraph(const RenderLayout& layout, |
| 869 | int overlay_width, |
| 870 | int overlay_height, |
| 871 | AVBufferRef* vulkan_device_context, |
| 872 | double fps, |
| 873 | int input_width, |
| 874 | int input_height) { |
| 875 | FilterGraph filter_graph; |
| 876 | filter_graph.graph = avfilter_graph_alloc(); |
| 877 | if (!filter_graph.graph) |
| 878 | throw std::runtime_error("Unable to allocate filter graph"); |
| 879 | |
| 880 | const AVFilter* buffer_filter = avfilter_get_by_name("buffer"); |
| 881 | const AVFilter* hwupload_filter = avfilter_get_by_name("hwupload"); |
| 882 | const AVFilter* overlay_vulkan_filter = avfilter_get_by_name("overlay_vulkan"); |
| 883 | const AVFilter* sink_filter = avfilter_get_by_name("buffersink"); |
| 884 | if (!buffer_filter || !hwupload_filter || !overlay_vulkan_filter || !sink_filter) { |
| 885 | throw std::runtime_error("Required FFmpeg Vulkan filters are not available"); |
| 886 | } |
| 887 | |
| 888 | auto make_buffer_args = [fps](int width, int height) { |
| 889 | std::ostringstream args; |
| 890 | args << "video_size=" << width << "x" << height |
| 891 | << ":pix_fmt=" << AV_PIX_FMT_RGBA |
| 892 | << ":time_base=1/" << std::max(1, static_cast<int>(std::lround(fps))) |
| 893 | << ":pixel_aspect=1/1"; |
| 894 | return args.str(); |
| 895 | }; |
| 896 | |
| 897 | std::string main_args = make_buffer_args(input_width, input_height); |
| 898 | CheckAv(avfilter_graph_create_filter(&filter_graph.main_source, buffer_filter, "main_in", |
| 899 | main_args.c_str(), nullptr, filter_graph.graph), |
| 900 | "avfilter_graph_create_filter main source"); |
| 901 | std::string overlay_args = make_buffer_args(overlay_width, overlay_height); |
| 902 | CheckAv(avfilter_graph_create_filter(&filter_graph.overlay_source, buffer_filter, "overlay_in", |
| 903 | overlay_args.c_str(), nullptr, filter_graph.graph), |
| 904 | "avfilter_graph_create_filter overlay source"); |
| 905 | CheckAv(avfilter_graph_create_filter(&filter_graph.sink, sink_filter, "sink", |
| 906 | nullptr, nullptr, filter_graph.graph), |
| 907 | "avfilter_graph_create_filter sink"); |
| 908 | AVFilterContext* main_hwupload = nullptr; |
| 909 | AVFilterContext* overlay_hwupload = nullptr; |
| 910 | AVFilterContext* overlay = nullptr; |
| 911 | CheckAv(avfilter_graph_create_filter(&main_hwupload, hwupload_filter, "main_hwupload", |
| 912 | nullptr, nullptr, filter_graph.graph), |
| 913 | "avfilter_graph_create_filter main hwupload"); |
| 914 | CheckAv(avfilter_graph_create_filter(&overlay_hwupload, hwupload_filter, "overlay_hwupload", |
| 915 | nullptr, nullptr, filter_graph.graph), |
| 916 | "avfilter_graph_create_filter overlay hwupload"); |
| 917 | main_hwupload->hw_device_ctx = av_buffer_ref(vulkan_device_context); |
| 918 | overlay_hwupload->hw_device_ctx = av_buffer_ref(vulkan_device_context); |
| 919 | if (!main_hwupload->hw_device_ctx || !overlay_hwupload->hw_device_ctx) |
| 920 | throw std::runtime_error("Unable to retain Vulkan device context for hwupload"); |
| 921 | |
| 922 | std::ostringstream overlay_args_filter; |
| 923 | overlay_args_filter << "x=" << layout.overlay_x |
| 924 | << ":y=" << layout.overlay_y; |
| 925 | CheckAv(avfilter_graph_create_filter(&overlay, overlay_vulkan_filter, "overlay", |