自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

在 Windows 下玩轉(zhuǎn)多媒體處理框架 BMF

系統(tǒng) Windows
本文將沿三個(gè)步驟,全面介紹 BMF 框架在 Windows 端的能力建設(shè)與技術(shù)實(shí)踐,首先介紹 BMF 框架在 Windows 環(huán)境下如何配置與編譯,其次介紹如何在 Windows 環(huán)境配置 BMF 開(kāi)發(fā)環(huán)境,并展示一個(gè)簡(jiǎn)單 Python 模塊的運(yùn)行過(guò)程,最后展示一個(gè)基于 DirectX 的全鏈路圖像縮放模塊的開(kāi)發(fā)與部署案例...

一、簡(jiǎn)介

現(xiàn)代科技網(wǎng)絡(luò)日益發(fā)達(dá),視頻已經(jīng)成為人們生活中不可或缺的一部分。隨著互聯(lián)網(wǎng)和移動(dòng)設(shè)備的普及,視頻內(nèi)容在傳播和分享方面發(fā)揮著越來(lái)越重要的作用。從社交媒體到在線教育,從數(shù)字廣告到遠(yuǎn)程工作,視頻已經(jīng)成為人們獲取信息、娛樂(lè)和交流的主要方式之一。在這樣一個(gè)視頻日益普及的超視頻時(shí)代,開(kāi)發(fā)一套跨語(yǔ)言、跨設(shè)備、跨系統(tǒng)的多媒體處理框架顯得尤為重要,這樣的框架可以為開(kāi)發(fā)人員提供統(tǒng)一的解決方案,幫助他們?cè)诓煌钠脚_(tái)上快速、高效地處理多媒體內(nèi)容,從而提供一致的用戶(hù)體驗(yàn)和功能,是迎接未來(lái)的必然趨勢(shì)。

在當(dāng)今數(shù)字化的世界中,Windows 平臺(tái)的重要性和關(guān)鍵性無(wú)可置疑。作為普通用戶(hù)的首要選擇,Windows 提供了廣泛的硬件和軟件支持,為用戶(hù)提供了豐富多彩的體驗(yàn)。特別是在多媒體處理領(lǐng)域,Windows 平臺(tái)憑借其強(qiáng)大的生態(tài)系統(tǒng)和穩(wěn)定的性能,基本是普通用戶(hù)的首選。Windows 平臺(tái)擁有龐大而完善的 DirectX 能力體系,這使得在Windows 環(huán)境下可以很方便地實(shí)現(xiàn)通過(guò) GPU 加速圖像視頻處理的性能,這種強(qiáng)大的圖形處理能力可以更高效地處理和渲染視頻、音頻等多媒體內(nèi)容。特別是對(duì)于游戲主播、視頻編輯等相關(guān)領(lǐng)域的從業(yè)者,Windows 平臺(tái)提供了一個(gè)穩(wěn)定而強(qiáng)大的開(kāi)發(fā)環(huán)境,為他們的創(chuàng)作和工作帶來(lái)了極大的便利和效率。因此,開(kāi)發(fā)一套兼容 Windows 平臺(tái)的多媒體處理框架具有重要的意義。這不僅可以滿足普通用戶(hù)對(duì)于多媒體內(nèi)容的需求,還可以為專(zhuān)業(yè)從業(yè)者提供強(qiáng)大的工具和支持。無(wú)論是游戲行業(yè)、直播行業(yè)還是視頻編輯領(lǐng)域,都可以受益于這樣一套高效、穩(wěn)定的多媒體處理框架,為用戶(hù)帶來(lái)更優(yōu)質(zhì)的體驗(yàn)和服務(wù)。

基于以上兩個(gè)前提,2023 年 8 月 22 日,火山引擎視頻云與NVIDIA正式開(kāi)源多媒體處理框架 Babit Multimedia Framework (以下統(tǒng)稱(chēng) BMF 框架),BMF 在 Windows 側(cè)對(duì)齊 Linux,目前已經(jīng)打通了框架的編譯、構(gòu)建、同時(shí)支持模塊自定義開(kāi)發(fā),在字節(jié)跳動(dòng)內(nèi)部,BMF 在 Win 側(cè)已集成多種使用 CPU/GPU 的圖像處理算法,服務(wù)于抖音直播伴侶業(yè)務(wù),目前已有 5 個(gè)算法已被成功集成,BMF 框架作為搭建算法與業(yè)務(wù)的橋梁,通過(guò)自定義模塊實(shí)現(xiàn)算法邏輯與業(yè)務(wù)的完全解耦,其內(nèi)部可以很方便地在 Win 側(cè)集成不同圖像處理算法。本文將沿三個(gè)步驟,全面介紹 BMF 框架在 Windows 端的能力建設(shè)與技術(shù)實(shí)踐,首先介紹 BMF 框架在 Windows 環(huán)境下如何配置與編譯,其次介紹如何在 Windows 環(huán)境配置 BMF 開(kāi)發(fā)環(huán)境,并展示一個(gè)簡(jiǎn)單 Python 模塊的運(yùn)行過(guò)程,最后展示一個(gè)基于 DirectX 的全鏈路圖像縮放模塊的開(kāi)發(fā)與部署案例,展示 BMF 在 Windows 端友好的兼容性和強(qiáng)大的功能適配能力,助您在 Windows 下玩轉(zhuǎn)BMF!

二、編譯與構(gòu)建

編譯 BMF 框架需要依賴(lài)以下環(huán)境:

  1. MSYS2。提供了一個(gè)基于開(kāi)源軟件的本機(jī)構(gòu)建環(huán)境,可以在 Windows 上用 Linux 的方式使用多種不同的環(huán)境和工具來(lái)執(zhí)行不同的任務(wù)。
  2. CMake。管理框架構(gòu)建過(guò)程,推薦版本 3.27。
  3. Visual Studio 2013 - 2022。BMF 在 Win 端選用兼容性較好的 msvc 編譯工具鏈,目前支持版本 2013 - 2022。

以上三個(gè)依賴(lài)是必須項(xiàng),下面還有 2 個(gè)依賴(lài)可供選擇是否打開(kāi)相關(guān)的框架能力:

  1. Python 3.7 - 3.10。用于編譯 BMF Python SDK,如果不提供,則框架無(wú)法編譯 Python 相關(guān)的調(diào)用能力
  2. FFmpeg 4.4 - 5.1。用于編譯 BMF built-in modules(ffmpeg_decoder、ffmpeg_encoder、ffmpeg_filter),如果不提供,默認(rèn)取消編譯相關(guān)產(chǎn)物

本文在介紹編譯過(guò)程時(shí),默認(rèn)會(huì)打開(kāi)以上兩個(gè)選項(xiàng),實(shí)現(xiàn)一次全鏈路的編譯構(gòu)建,下面用圖文方式介紹 BMF 框架編譯過(guò)程:

  1. 打開(kāi) Visual Studio 的命令提示環(huán)境,建議以管理員方式

圖片

  1. 找到 MSYS2 的安裝目錄,執(zhí)行命令: .\msys2_shell.cmd -use-full-path,攜帶宿主機(jī)環(huán)境進(jìn)入 msys2(注意:這里需要保證 CMake 工具已經(jīng)成功配置進(jìn)系統(tǒng)環(huán)境變量,本文的實(shí)驗(yàn)環(huán)境默認(rèn)配置了 CMake 3.27、FFmpeg 4.4、Python 3.7.9 的環(huán)境變量)。
  2. 克隆 BMF 項(xiàng)目,在根目錄運(yùn)行 build_win_lite 腳本,項(xiàng)目設(shè)有以下編譯控制選項(xiàng):
Build Options:
--msvc 設(shè)定 msvc 版本,包括[2013, 2015, 2017, 2019, 2022]
bmf_ffmpeg 控制是否集成 FFmpeg 相關(guān)能力并編譯 built-in module
--preset 編譯配置,包括[x86-Debug, x86-Release, x64-Debug, x64-Release]

注意:如果您需要編譯 FFmpeg 相關(guān)的能力,且您本地的 CMake 版本高于等于 3.28,你還需要設(shè)置 ffmpeg 的 include 目錄,命令如下:

export INCLUDE="$INCLUDE;C:\path\to\include_for_ffmpeg"

以 msvc 2022 編譯 x64-Debug 版本為例,腳本執(zhí)行命令:

./build_win_lite.sh --msvc=2022 --preset=x64-Debug bmf_ffmpeg

執(zhí)行后會(huì)在項(xiàng)目目錄的 build_win_lite/x64-Debug 目錄生成 BMF.sln 解決方案

圖片

之后,您可以雙擊 sln 文件使用 Visual Studio 友好的界面進(jìn)行項(xiàng)目構(gòu)建和編譯,您也可以使用以下 CMake 命令直接通過(guò)命令行進(jìn)行項(xiàng)目構(gòu)建:

cmake --build build_win_lite/x64-Debug --config Debug --target ALL_BUILD

至此,您便可以在 Windows 環(huán)境中完成對(duì) BMF 框架的編譯與構(gòu)建,在 Visual Studio 的編譯過(guò)程如圖所示

圖片

三、開(kāi)發(fā)環(huán)境配置與模塊運(yùn)行

本章介紹如何搭建 BMF 開(kāi)發(fā)環(huán)境,繼上文所述,當(dāng)我們完成 BMF 框架的編譯構(gòu)建后,會(huì)生成 output 文件夾,我們需要將 bin 目錄與 lib 目錄配置進(jìn)系統(tǒng)環(huán)境變量,與此同時(shí),BMF 開(kāi)發(fā)環(huán)境還需依賴(lài)一個(gè) Win 相關(guān)的依賴(lài)集合 win_rootfs(https://github.com/BabitMF/bmf/releases/download/files/win_rootfs.tar.gz),需配置環(huán)境變量,如圖所示:

圖片

配置完 BMF 環(huán)境變量后,我們需要重啟 msys2 環(huán)境,目的是讓 BMF 的環(huán)境變量生效,下面展示如何運(yùn)行一個(gè) Python Module 的測(cè)試程序:test_customize_module。首先,需要將 msys2 當(dāng)前目錄切換至編譯產(chǎn)物的上級(jí)目錄,設(shè)置一些 msys2 環(huán)境的環(huán)境變量,配置 BMF 框架的 Python 運(yùn)行環(huán)境

export PYTHONHOME="$(dirname "$(which python)")"
export PYTHONPATH=$(pwd)/output/bmf/lib:$(pwd)/output

配置完畢后,進(jìn)入 python 環(huán)境應(yīng)該可以正常 import bmf 框架,如圖所示

圖片

我們要運(yùn)行的 customize_module 文件在 output/test/customize_module 目錄下的 my_module.py 文件,模塊的實(shí)現(xiàn)代碼如下:

from bmf import Module, Log, LogLevel, InputType, ProcessResult, Packet, Timestamp, scale_av_pts, av_time_base, \
    BmfCallBackType, VideoFrame, AudioFrame


class my_module(Module):

    def __init__(self, node, optinotallow=None):
        self.node_ = node
        self.option_ = option
        pass

    def process(self, task):
        for (input_id, input_packets) in task.get_inputs().items():

            ## output queue
            output_packets = task.get_outputs()[input_id]

            while not input_packets.empty():
                pkt = input_packets.get()

                ## process EOS
                if pkt.timestamp == Timestamp.EOF:
                    Log.log_node(LogLevel.DEBUG, task.get_node(),
                                 "Receive EOF")
                    output_packets.put(Packet.generate_eof_packet())
                    task.timestamp = Timestamp.DONE
                    return ProcessResult.OK

                ## copy input packet to output
                if pkt.defined() and pkt.timestamp != Timestamp.UNSET:
                    output_packets.put(pkt)

        return ProcessResult.OK

可以看到,模塊僅僅將幀從輸入隊(duì)列取出,不做任何處理,直接傳遞至輸出隊(duì)列,我們將要執(zhí)行的測(cè)試程序 test_customize_module.py 的實(shí)現(xiàn)如下:

import sys
import time
import unittest

sys.path.append("../../..")
sys.path.append("../../c_module_sdk/build/bin/lib")
import bmf
import os
if os.name == 'nt':
    ## We redefine timeout_decorator on windows
    class timeout_decorator:

        @staticmethod
        def timeout(*args, **kwargs):
            return lambda f: f  ## return a no-op decorator
else:
    import timeout_decorator

sys.path.append("../../test/")
from base_test.base_test_case import BaseTestCase
from base_test.media_info import MediaInfo


class TestCustomizeModule(BaseTestCase):

    @timeout_decorator.timeout(secnotallow=120)
    def test_customize_module(self):
        input_video_path = "../../files/big_bunny_10s_30fps.mp4"
        output_path = "./output.mp4"
        expect_result = '|1080|1920|10.0|MOV,MP4,M4A,3GP,3G2,MJ2|1783292|2229115|h264|' \
                        '{"fps": "30.0662251656"}'
        self.remove_result_data(output_path)
        (bmf.graph().decode({'input_path': input_video_path
                             })['video'].module('my_module').encode(
                                 None, {
                                     "output_path": output_path
                                 }).run())
        self.check_video_diff(output_path, expect_result)


if __name__ == '__main__':
    unittest.main()

在測(cè)試程序的 33 - 37 行中,我們構(gòu)建了一個(gè) BMF Graph,首先對(duì)輸入視頻進(jìn)行解碼,隨后調(diào)起事先我們寫(xiě)好的 Python 模塊 my_module 對(duì)解碼后的視頻幀進(jìn)行一次處理,最后調(diào)用 encode 模塊對(duì)視頻幀進(jìn)行編碼,產(chǎn)出輸出文件,剩余部分是框架使用 Google Test 框架所進(jìn)行的一些轉(zhuǎn)碼指標(biāo)的驗(yàn)證,這里不深入追溯,程序使用的輸入視頻是 BMF 框架為集成測(cè)試預(yù)先準(zhǔn)備好的一組測(cè)試資源包,您可以通過(guò)https://github.com/BabitMF/bmf/releases/download/files/files.tar.gz 進(jìn)行下載和使用,本文將使用這個(gè)資源包,下載命令如下

(cd output && wget https://github.com/BabitMF/bmf/releases/download/files/files.tar.gz && tar xvf files.tar.gz && rm -rf files.tar.gz)

至此,所有執(zhí)行該程序的前置依賴(lài)均已準(zhǔn)備完畢,切換到 customize_module 目錄執(zhí)行程序

cd test/customize_module
python test_customize_module.py

程序執(zhí)行結(jié)果如圖所示,可以看到在本地成功產(chǎn)出了 output.mp4 文件

圖片

四、實(shí)踐案例

本章將從 0 到 1 帶你實(shí)現(xiàn)一個(gè) RGBA 圖像的 GPU 縮放功能,基于 DirectX Compute Shader 機(jī)制完成算法能力建設(shè),并使用 BMF 框架的模塊機(jī)制將算法能力封裝進(jìn) BMF 模塊中,同時(shí)實(shí)現(xiàn)一套 Host 端的 BMF 調(diào)用測(cè)試程序,實(shí)現(xiàn)對(duì) GPU 圖像縮放模塊的調(diào)用,并提供構(gòu)建腳本的實(shí)現(xiàn),整體鏈路將算法層與調(diào)用層解耦,充分發(fā)揮并展示 BMF 框架在 Windows 端的良好的兼容性、適配性與易用性,本節(jié)流程主要分為三個(gè)部分:1. GPU 圖像縮放算法模塊的實(shí)現(xiàn) 2. 調(diào)用程序的實(shí)現(xiàn)。3. 構(gòu)建腳本的實(shí)現(xiàn)

圖像縮放模塊

與其他可編程著色器(例如頂點(diǎn)和幾何著色器)一樣,計(jì)算著色器(Compute Shader)是使用 HLSL 設(shè)計(jì)和實(shí)現(xiàn)的,但相似之處僅此而已。計(jì)算著色器提供高速通用計(jì)算,并利用圖形處理單元 (GPU) 上的大量并行處理器。計(jì)算著色器提供內(nèi)存共享和線程同步功能,這些特性讓 Win 端用戶(hù)具備輕易調(diào)用跨平臺(tái)框架 DirectX 調(diào)用 GPU 高效處理音視頻圖像領(lǐng)域的諸多計(jì)算任務(wù)。

一個(gè)完整的 DirectX Compute Shader 調(diào)用過(guò)程分為 Host 端和 Device 端,下面簡(jiǎn)要闡述 Host 端的調(diào)用步驟:

當(dāng)使用 DirectX 11 或更高版本執(zhí)行計(jì)算著色器時(shí),通常需要以下步驟:

  1. 創(chuàng)建設(shè)備和設(shè)備上下文:
    a.  創(chuàng)建 DirectX 設(shè)備對(duì)象,通常通過(guò)調(diào)用 D3D11CreateDevice() 函數(shù)。
    b.  為了執(zhí)行計(jì)算著色器,設(shè)備需要支持 DirectCompute 功能,因此需要檢查設(shè)備是否支持 DirectCompute??梢酝ㄟ^(guò)檢查設(shè)備屬性來(lái)實(shí)現(xiàn)。
  2. 創(chuàng)建計(jì)算著色器:
    a.  創(chuàng)建計(jì)算著色器對(duì)象,通常通過(guò)編譯 HLSL(High-Level Shading Language)代碼而獲得??梢允褂?HLSL 編譯器將計(jì)算著色器代碼編譯為字節(jié)碼形式。
    b.  使用 ID3D11Device::CreateComputeShader() 函數(shù)創(chuàng)建計(jì)算著色器對(duì)象。
  3. 創(chuàng)建常量緩沖區(qū)和資源:
    a.  如果計(jì)算著色器需要常量或者其他資源作為輸入,則需要?jiǎng)?chuàng)建對(duì)應(yīng)的常量緩沖區(qū)或者資源。
    b.  常量緩沖區(qū)通常通過(guò) ID3D11Device::CreateBuffer() 函數(shù)創(chuàng)建,然后通過(guò) ID3D11DeviceContext::CSSetConstantBuffers() 函數(shù)將常量緩沖區(qū)綁定到計(jì)算著色器上下文。
    c.  其他資源,如紋理、UAV、SRV 視圖、常亮緩沖區(qū)等,可以通過(guò)相應(yīng)的創(chuàng)建函數(shù)創(chuàng)建,并通過(guò) ID3D11DeviceContext::CSSetShaderResources() 函數(shù)將其綁定到計(jì)算著色器上下文。
  4. 設(shè)置執(zhí)行參數(shù):
    a.  在執(zhí)行計(jì)算著色器之前,需要設(shè)置執(zhí)行參數(shù),包括計(jì)算著色器的線程組數(shù)等。
    b.  使用 ID3D11DeviceContext::Dispatch() 函數(shù)設(shè)置計(jì)算著色器執(zhí)行的線程組數(shù)。
  5. 執(zhí)行計(jì)算著色器:
    a.  調(diào)用 ID3D11DeviceContext::CSSetShader() 函數(shù)將計(jì)算著色器綁定到設(shè)備上下文。
    b.  調(diào)用 ID3D11DeviceContext::Dispatch() 函數(shù)執(zhí)行計(jì)算著色器。
  6. 等待執(zhí)行完成:可以通過(guò)插入事件或者查詢(xún)?cè)O(shè)備上下文的執(zhí)行狀態(tài)來(lái)等待計(jì)算著色器的執(zhí)行完成。
  7. 清理資源:在完成計(jì)算著色器的使用后,需要釋放相關(guān)資源,包括計(jì)算著色器對(duì)象、常量緩沖區(qū)、資源等。

基于以上流程,本節(jié)將構(gòu)建一個(gè) BMF 模塊,命名為 d3dresizemodule ,d3dresizemodule 擁有兩個(gè)輸入流(InputStream),編號(hào) 0、1,0 號(hào)輸入流負(fù)責(zé)接收 Device、Devicecontext 等基礎(chǔ)資源管理對(duì)象,并控制 DirectX 側(cè)的初始化流程,在初始化流程中需要完成紋理、SRV/UAV 視圖、著色器、采樣器等資源的創(chuàng)建和初始化,因此 0 號(hào)輸入流也被命名為“配置流”(config_stream)。1 號(hào)流負(fù)責(zé)在 DirectX 資源成功被初始化后,接收外界調(diào)用方傳入的輸入紋理數(shù)據(jù),并職責(zé),因此也被命名為“數(shù)據(jù)流”(data_stream),模塊整體架構(gòu)如下圖所示:

圖片image.png

d3dresizemodule 模塊的聲明文件實(shí)現(xiàn)如下所示:

#ifndef ROI_Module_H
#define ROI_Module_H
#include <bmf/sdk/module.h>
#include <bmf/sdk/task.h>
#include <d3d11_common.h>
USE_BMF_SDK_NS

class D3DResizeModule : public Module {
public:
  D3DResizeModule(int node_id, JsonParam option);
  int32_t init();
  // DirectX 側(cè)初始化函數(shù),需通過(guò)配置流成功配置 device_、device_context_ 后觸發(fā)
  int32_t unsafe_init();
  int32_t init_d3d11();
  // 模塊處理函數(shù),由 BMF 框架驅(qū)動(dòng)調(diào)用
  int32_t process(Task &task);
  int32_t unsafe_process(Task &task);
  int32_t close();
  // UAV 功能檢測(cè)函數(shù)
  bool checkUAVFeature();
  ~D3DResizeModule();

  JsonParam option_;
  bool inited = false;
  int width_ = 0;
  int height_ = 0;
  int inputWidth_ = 0;
  int inputHeight_ = 0;

  BMFComPtr<ID3D11ComputeShader> processShader = nullptr;
  BMFComPtr<ID3D11Buffer> outputSizebuf = nullptr;
  BMFComPtr<ID3D11SamplerState> sampleState = nullptr;
  ID3D11Device *device_ = nullptr;
  ID3D11DeviceContext *device_context_ = nullptr;
};

#endif

其中,d3d11_common.h 是一組基于 Windows DirectX 的調(diào)用能力,用戶(hù)可以調(diào)用這些接口輕易地實(shí)現(xiàn) DirectX 相關(guān)資源的創(chuàng)建和管理,文件內(nèi)部主要封裝了一些與 DirectX 11 相關(guān)的常用操作和數(shù)據(jù)結(jié)構(gòu)。讓我們逐一解析:

1.包含頭文件:

包含了一些與 DirectX 11 相關(guān)的頭文件,如 <d3d11.h>, <wrl/client.h>, <d3dcompiler.h> 等。這些頭文件包含了 DirectX 11 中定義的接口和數(shù)據(jù)結(jié)構(gòu)。

2.定義了一些結(jié)構(gòu)體:

a.D3D11DeviceWrapper 結(jié)構(gòu)體用于封裝了一個(gè) DirectX 11 設(shè)備對(duì)象的指針。

b.D3D11DeviceContextWrapper 結(jié)構(gòu)體用于封裝了一個(gè) DirectX 11 設(shè)備上下文對(duì)象的指針。

c.D3D11TextureWrapper 結(jié)構(gòu)體用于封裝了一個(gè) DirectX 11 紋理對(duì)象的指針。

d.InputSizeBuffer 結(jié)構(gòu)體用于定義輸入尺寸緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu),主要用于 DirectX 常量緩沖區(qū)。

e.OutputSizeBuffer 結(jié)構(gòu)體用于定義輸出尺寸緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu),主要用于 DirectX 常量緩沖區(qū)。
3.定義了一個(gè)模板別名:

a.BMFComPtr 是一個(gè)模板別名,用于簡(jiǎn)化使用 Microsoft::WRL::ComPtr類(lèi)型的代碼。

4.聲明了一些外部函數(shù):

a.CreateTexture 函數(shù)用于創(chuàng)建一個(gè) DirectX 11 紋理對(duì)象。

b.CreateUAV 函數(shù)用于創(chuàng)建一個(gè) DirectX 11 無(wú)序訪問(wèn)視圖對(duì)象。

c.CreateSRV 函數(shù)用于創(chuàng)建一個(gè) DirectX 11 著色器資源視圖對(duì)象。

d.createComputeShader 函數(shù)用于創(chuàng)建一個(gè) DirectX 11 計(jì)算著色器對(duì)象。

e.CreateStagingTexture 函數(shù)用于創(chuàng)建一個(gè)用于數(shù)據(jù)傳輸?shù)呐R時(shí)紋理對(duì)象。

f.CreateSampleState 函數(shù)用于創(chuàng)建一個(gè) DirectX 11 采樣器狀態(tài)對(duì)象

總的來(lái)說(shuō),這個(gè)頭文件封裝了一些常用的 DirectX 11 操作函數(shù)和數(shù)據(jù)結(jié)構(gòu),提供了一種簡(jiǎn)化 DirectX 11 編程的方式,使得開(kāi)發(fā)者可以更方便地使用 DirectX 11 相關(guān)功能,下面是 d3d11_common.h 文件的實(shí)現(xiàn):

#ifndef D3D11_COMMON__H
#define D3D11_COMMON__H
#include <d3d11.h>
#include <wrl/client.h>
#include <d3dcompiler.h>
#include <string>
#include <vector>
struct D3D11DeviceWrapper {
    ID3D11Device *device;
};

struct D3D11DeviceContextWrapper {
    ID3D11DeviceContext *device_context;
};

struct D3D11TextureWrapper {
    ID3D11Texture2D *texture;
};

struct InputSizeBuffer
{
    uint32_t inWidth;
    uint32_t inHeight;
    float padding[2];
};

struct OutputSizeBuffer
{
    uint32_t outWidth;
    uint32_t outHeight;
    float padding[2];
};

template <class T>
using BMFComPtr = Microsoft::WRL::ComPtr<T>;

extern bool CreateTexture(ID3D11Texture2D** texture, ID3D11Device* d3dDevice, int width, int height, DXGI_FORMAT format, const void* initData, D3D11_BIND_FLAG bindflag, int pixelbit, int bitSize);

extern bool CreateUAV(ID3D11UnorderedAccessView** uav, ID3D11Device* d3dDevice, D3D11_UNORDERED_ACCESS_VIEW_DESC* desc, ID3D11Texture2D* texture);

extern bool CreateSRV(ID3D11ShaderResourceView** srv, ID3D11Device* d3dDevice, D3D11_SHADER_RESOURCE_VIEW_DESC* desc, ID3D11Texture2D* texture);

extern bool createComputeShader(ID3D11ComputeShader** shader_, const std::string& shader, ID3D11Device* device);

extern bool CreateStagingTexture(ID3D11Texture2D** stagingTexture, ID3D11Device* device, int width, int height, DXGI_FORMAT format);

extern bool CreateSampleState(ID3D11SamplerState** state, ID3D11Device* device);

// ...

#endif

關(guān)于 Shader 的編譯,默認(rèn)的方式是將 shader 代碼寫(xiě)在 HLSL 文件中,在程序初始化時(shí)調(diào)用編譯程序讀取文件進(jìn)行編譯,這種方式會(huì)要求強(qiáng)制暴露 HLSL 代碼實(shí)現(xiàn),外界調(diào)用方才可以通過(guò) BMF 框架正確加載模塊,不利于 Shader 代碼的封裝,Demo 中使用 map 封裝每個(gè) Shader 的字符二進(jìn)制,并分類(lèi)管理,這樣的好處是在模塊編譯出 dll 時(shí),相關(guān) hlsl 代碼已經(jīng)成功被封裝進(jìn)模塊內(nèi)部,無(wú)需額外附上 hlsl 代碼文件,下面關(guān)于是 gpuresize Shader 的實(shí)現(xiàn):

static std::map<std::string, std::string> hlslMap = {  
    { "gpuresize", R"(
        // Define a linear sampler state
        SamplerState LinearSampler : register(s0);
        Texture2D RGBATexture : register(t0);
        cbuffer OutputSize : register(b0)
        {
            uint outWidth;
            uint outHeight;
        };
        RWTexture2D<float4> RGBOutput : register(u0);

        // bgra -> scale -> rgba
        [numthreads(16, 16, 1)]
        void CSMain(uint3 dtid : SV_DispatchThreadID)
        {
            if (dtid.x >= outWidth || dtid.y >= outHeight)
                return;
            
            float2 samplepoint = (float2(dtid.xy) + float2(0.5, 0.5)) / float2(outWidth, outHeight);
            float4 rgba = RGBATexture.SampleLevel(LinearSampler, samplepoint, 0);
            RGBOutput[dtid.xy] = rgba;
        }
    )" }
};

首先,Shader 定義了采樣器、輸入輸出紋理、常量緩沖區(qū)等資源,在計(jì)算主邏輯中,首先判斷當(dāng)前線程是否處于輸出圖像范圍內(nèi),若超出范圍則直接返回。然后,根據(jù)當(dāng)前線程的索引計(jì)算對(duì)應(yīng)的采樣點(diǎn)坐標(biāo),并使用 SampleLevel 方法從輸入紋理中進(jìn)行線性采樣,獲取采樣到的 RGBA 像素值,并將其寫(xiě)入輸出紋理中。

關(guān)于模塊的具體實(shí)現(xiàn),這里重點(diǎn)分析三個(gè)函數(shù):init_d3d11、process、unsafe_process,首先來(lái)看 process 函數(shù):

int32_t D3DResizeModule::process(Task &task) {
  try {
    int32_t res = unsafe_process(task);
    return res;
  } catch (std::exception &e) {
    BMFLOG(BMF_INFO) << "ROI module process throws std::exception: "
                     << e.what();
    throw e;
    return -1;
  } 
}

模塊的 process 函數(shù)由 BMF 框架層調(diào)用,本模塊將函數(shù)執(zhí)行邏輯主體封裝在 unsafe_process 函數(shù)中,process 函數(shù)使用 try catch 捕獲異常,起到兜底作用,unsafe_process 的實(shí)現(xiàn)如下:

int32_t D3DResizeModule::unsafe_process(Task& task) {
    if (!inited) {
        bmf_sdk::Packet d3d11_packet;
        while (task.pop_packet_from_input_queue(0, d3d11_packet)) {
            if (d3d11_packet.timestamp() == bmf_sdk::BMF_EOF) {
                task.set_timestamp(bmf_sdk::DONE);
                task.fill_output_packet(0, bmf_sdk::Packet::generate_eof_packet());
                break;
            }

            if (d3d11_packet.is<D3D11DeviceWrapper>()) {
                device_ = d3d11_packet.get<D3D11DeviceWrapper>().device;
            }
            else if (d3d11_packet.is<D3D11DeviceContextWrapper>()) {
                device_context_ = d3d11_packet.get<D3D11DeviceContextWrapper>().device_context;
            }
            else {
                BMFLOG_NODE(BMF_WARNING, node_id_) << "get unexpected data:" << d3d11_packet.type_info().name << std::endl;
            }
        }
        if (device_ && device_context_) {
            init_d3d11();
        }
    }

    bmf_sdk::Packet frame_packet;
    int textureStyle = -1; 
    while (task.pop_packet_from_input_queue(1, frame_packet)) {
        if (frame_packet.timestamp() == bmf_sdk::BMF_EOF) {
            task.set_timestamp(bmf_sdk::DONE);
            task.fill_output_packet(0, bmf_sdk::Packet::generate_eof_packet());
            break;
        }
        if (!frame_packet.is<D3D11TextureWrapper>()) {
            BMFLOG_NODE(BMF_ERROR, node_id_) << "get unexpected data:" << frame_packet.type_info().name << std::endl;
        }
        D3D11TextureWrapper inputPkt = frame_packet.get<D3D11TextureWrapper>();
        ID3D11Texture2D* input_texture = inputPkt.texture;
        if (!input_texture) {
            throw std::exception("null texture input!");
        }
        
        ID3D11Texture2D *outputTexture = nullptr;
        CreateTexture(&outputTexture, device_, width_, height_, DXGI_FORMAT_R8G8B8A8_UNORM, nullptr, (D3D11_BIND_FLAG)(D3D11_BIND_UNORDERED_ACCESS), 4, sizeof(uint8_t));
        BMFComPtr<ID3D11ShaderResourceView> input_srv;
        BMFComPtr<ID3D11UnorderedAccessView> output_texture_uav;

        D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
        ZeroMemory(&srvDesc, sizeof(srvDesc));
        srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
        srvDesc.Texture2D.MostDetailedMip = 0;
        srvDesc.Texture2D.MipLevels = 1;

        if (!CreateSRV(input_srv.GetAddressOf(), device_, &srvDesc, input_texture)) {
            throw std::exception("input_srv create failed");
        }
        if (!CreateUAV(output_texture_uav.GetAddressOf(), device_, nullptr, outputTexture)) {
            throw std::exception("output_texture_uav create failed!");
        }

        // flush the DirectX resource
        ID3D11ShaderResourceView* null_srv = nullptr;
        ID3D11UnorderedAccessView* null_uav = nullptr;
        ID3D11Buffer* null_buf = nullptr;
        ID3D11SamplerState* null_sample = nullptr;
        ID3D11ComputeShader* null_shader = nullptr;
        device_context_->CSSetShaderResources(0, 1, &null_srv);
        device_context_->CSSetUnorderedAccessViews(0, 1, &null_uav, nullptr);
        device_context_->CSSetConstantBuffers(0, 1, &null_buf);
        device_context_->CSSetSamplers(0, 1, &null_sample); 
        device_context_->CSSetShader(null_shader, nullptr, 0);
    
        // execute the resize shader
        device_context_->CSSetShaderResources(0, 1, input_srv.GetAddressOf()); 
        device_context_->CSSetUnorderedAccessViews(0, 1, output_texture_uav.GetAddressOf(), nullptr);
        device_context_->CSSetShader(processShader.Get(), nullptr, 0);
        device_context_->CSSetConstantBuffers(0, 1, outputSizebuf.GetAddressOf());
        device_context_->CSSetSamplers(0, 1, sampleState.GetAddressOf());
        device_context_->Dispatch((width_ - 1 + 16) / 16, (height_ - 1 + 16) / 16, 1); 
        
        D3D11TextureWrapper outputTextureWrapper;
        outputTextureWrapper.texture = outputTexture;
        bmf_sdk::Packet output_packet(outputTextureWrapper);
        task.fill_output_packet(0, output_packet);
    }
    return 0;
}

這段代碼實(shí)現(xiàn)的主要功能如下:

1.初始化階段(未初始化時(shí)):

通過(guò)從模塊的配置流中獲取數(shù)據(jù)包,初始化 D3D11 設(shè)備和設(shè)備上下文對(duì)象。

如果獲取到了設(shè)備和設(shè)備上下文對(duì)象,則調(diào)用 init_d3d11 函數(shù)進(jìn)行 DirectX 11 的初始化。

2.處理階段:

  • 清空之前的著色器資源視圖和無(wú)序訪問(wèn)視圖。
  • 設(shè)置輸入紋理的著色器資源視圖和輸出紋理的無(wú)序訪問(wèn)視圖。
  • 設(shè)置計(jì)算著色器,并執(zhí)行計(jì)算著色器的調(diào)度,實(shí)現(xiàn)了一個(gè) D3D11 RGBA 紋理的 resize 功能

  a .通過(guò)循環(huán)從模塊的數(shù)據(jù)流獲取數(shù)據(jù)包,直到獲取到結(jié)束標(biāo)志(BMF_EOF)為止。

  b.如果獲取到的數(shù)據(jù)包類(lèi)型為 D3D11TextureWrapper,則表示獲取到了 D3D11 紋理對(duì)象。

  c.根據(jù)獲取到的輸入紋理,創(chuàng)建一個(gè)新的輸出紋理對(duì)象。

  d.調(diào)用上文描述的接口,創(chuàng)建輸入紋理的著色器資源視圖(SRV)和輸出紋理的無(wú)序訪問(wèn)視圖(UAV)。

  e.執(zhí)行一系列的 DirectX 11 操作:

  f.封裝輸出紋理對(duì)象為數(shù)據(jù)包,并 push 到模塊的輸出隊(duì)列中,供外界調(diào)用代碼獲取


init_d3d11 函數(shù)的實(shí)現(xiàn)如下:

int32_t D3DResizeModule::init_d3d11() {
    if (!inited) {
        if (!device_ || !device_context_) {
            throw std::exception("d3d11 device or context is not inited!");
        }
        D3D_FEATURE_LEVEL featureLevel = device_->GetFeatureLevel();

        if (featureLevel < D3D_FEATURE_LEVEL_11_0) {
            throw std::exception("local d3d11 feature level < 11! computeshader cannot work!");
        }
        if (!checkUAVFeature()) {
            throw std::exception("UAV load is not supported in this hardware");
        }
        if (!createComputeShader(processShader.GetAddressOf(), "gpuresize", device_)) {
            throw std::exception("shader compile failed!");
        }

        if (!CreateSampleState(sampleState.GetAddressOf(), device_)) {
            throw std::exception("sample state create failed!");
        }

        OutputSizeBuffer sizeBuffer;
        sizeBuffer.outWidth = width_;
        sizeBuffer.outHeight = height_;

        D3D11_BUFFER_DESC desc;
        ZeroMemory(&desc, sizeof(desc));
        desc.Usage = D3D11_USAGE_DYNAMIC; 
        desc.ByteWidth = sizeof(OutputSizeBuffer);
        desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
        desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 
        desc.MiscFlags = 0;
        desc.StructureByteStride = 0;

        D3D11_SUBRESOURCE_DATA initData;
        initData.pSysMem = &sizeBuffer;
        initData.SysMemPitch = 0;
        initData.SysMemSlicePitch = 0;
        HRESULT hr = device_->CreateBuffer(&desc, &initData, outputSizebuf.GetAddressOf());
        if (FAILED(hr)) {
            throw std::exception("constant buffer create failed!");
        }
        inited = true;
    }
}

代碼主要實(shí)現(xiàn)的邏輯如下:

  • 檢查設(shè)備和設(shè)備上下文對(duì)象是否已經(jīng)初始化,如果沒(méi)有則拋出異常。
  • 獲取當(dāng)前設(shè)備的特性級(jí)別(Feature Level),并檢查是否支持 D3D11 特性級(jí)別 11.0 以上,如果不支持則拋出異常。
  • 檢查硬件是否支持 UAV(Unordered Access View)加載特性,如果不支持則拋出異常。
  • 編譯創(chuàng)建計(jì)算著色器,用于圖像的 resize 操作。
  • 創(chuàng)建采樣器狀態(tài)(Sampler State),用于計(jì)算著色器中的紋理采樣。
  • 創(chuàng)建輸出大小的常量緩沖區(qū)(Constant Buffer),用于傳遞輸出圖像的寬度和高度信息給計(jì)算著色器。
  • 將輸出大小的緩沖區(qū)數(shù)據(jù)初始化,并創(chuàng)建 D3D11 緩沖區(qū)對(duì)象。
  • 設(shè)置標(biāo)志位 inited 為 true,表示 D3D11 初始化完成。

從上文 unsafe_process 函數(shù)的邏輯可以獲知:init_d3d11 函數(shù)的調(diào)用時(shí)機(jī)是當(dāng)模塊成功接收了外界傳入的 Device 和 DeviceContext 之后。

以上便是基于 DirectX 的圖像縮放模塊 D3DResizeModule 的設(shè)計(jì)與實(shí)現(xiàn),通過(guò)集成 BMF 開(kāi)發(fā)環(huán)境,可以編譯出對(duì)應(yīng)的 BMF 模塊

調(diào)用程序

本節(jié)將實(shí)現(xiàn)一個(gè)測(cè)試 demo 程序,用于測(cè)試和調(diào)用上文中所構(gòu)建的圖像縮放模塊,測(cè)試程序?qū)崿F(xiàn)如下:

#include <bmf/sdk/log.h>
#include <bmf/sdk/video_frame.h>
#include <nlohmann/json.hpp>
#include <builder.hpp>
#include <chrono>
#include <d3d11_common.h>
#include <fstream>
using json = nlohmann::json;
using namespace std::chrono;
namespace fs = std::filesystem;

static const int TESTINPUTWIDTH = 720;
static const int TESTINPUTHEIGHT = 1280;
static const int TESTOUTPUTWIDTH = 1080;
static const int TESTOUTPUTHEIGHT = 1920;

static void readRGBAFile(std::vector<uint8_t>& data, const std::string& filename) {
    std::ifstream file(filename, std::ios::binary);

    if (!file) {
        throw std::runtime_error("cannot open yuv file!");
    }

    file.seekg(0, std::ios::end);
    std::streampos fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    if (fileSize != (TESTINPUTWIDTH * TESTINPUTHEIGHT * 4)) {
        throw std::runtime_error("file size not compared with TESTINPUTWIDTH and TESTINPUTHEIGHT! it should be a RGBA data");
    }

    file.read(reinterpret_cast<char*>(data.data()), fileSize);

    file.close();
}

int main(int argc, char const *argv[])
{
    static const int profile_time = 1;
    HRESULT hr = S_OK;
    BMFComPtr<ID3D11Device> device = nullptr;
    BMFComPtr<ID3D11DeviceContext> context = nullptr;
    BMFComPtr<ID3D11ComputeShader> computeShader = nullptr;
    BMFComPtr<ID3D11Texture2D> input_rgba = nullptr;

    // Initialize device and context
    hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0,
        D3D11_SDK_VERSION, &device, nullptr, &context);
    if (FAILED(hr)) {
        std::cerr << "Failed to create D3D11 device" << std::endl;
        exit(EXIT_FAILURE);
    }
    std::vector<uint8_t> filedata(TESTINPUTWIDTH * TESTINPUTHEIGHT * 4, 0);
    readRGBAFile(filedata, "../../../files/test_opencv_lenna_720x1280.rgb");
    ID3DBlob* pBlob = nullptr;

    D3D11_SUBRESOURCE_DATA sd;
    sd.pSysMem = filedata.data();
    sd.SysMemPitch = TESTINPUTWIDTH * sizeof(uint8_t) * 4;
    sd.SysMemSlicePitch = TESTINPUTWIDTH * TESTINPUTHEIGHT * sizeof(uint8_t) * 4;
    CD3D11_TEXTURE2D_DESC texDesC(DXGI_FORMAT_R8G8B8A8_UNORM, TESTINPUTWIDTH, TESTINPUTHEIGHT, 1, 1, (D3D11_BIND_FLAG)(D3D11_BIND_SHADER_RESOURCE), D3D11_USAGE_DEFAULT, 0, 1, 0, D3D11_RESOURCE_MISC_SHARED);

    texDesC.CPUAccessFlags = 0;
    texDesC.MipLevels = 1;
    texDesC.ArraySize = 1;
    hr = device->CreateTexture2D(&texDesC, &sd, &input_rgba);
    if (FAILED(hr)) {
        std::cerr << "Failed to CreateTexture2D" << std::endl;
        exit(EXIT_FAILURE);
    }
    {

        try {
            bmf::builder::Graph graph = bmf::builder::Graph(bmf::builder::GeneratorMode);
            auto input_stream0 = graph.InputStream(
                "config_stream", "stream0", "");
            auto input_stream1 = graph.InputStream(
                "data_stream", "stream1", "");

            json roi_option = {
                {"width", TESTOUTPUTWIDTH},
                {"height", TESTOUTPUTHEIGHT}
            };
            std::string moduleName;
#ifdef _DEBUG
            moduleName = "d3dresizemoduled.dll";
#else
            moduleName = "d3dresizemodule.dll";
#endif
            auto resize_module = graph.CppModule({ input_stream0, input_stream1 }, "d3dresizemodule", bmf_sdk::JsonParam(roi_option),
                "", moduleName, "d3dresizemodule:D3DResizeModule");
            auto resize_output_stream = resize_module.Stream(0);

            std::vector<bmf::builder::Stream> enhanceGenerateStreams;
            enhanceGenerateStreams.emplace_back(resize_output_stream);
            graph.Start(enhanceGenerateStreams);

            D3D11DeviceWrapper device_wrapper;
            D3D11DeviceContextWrapper device_context_wrapper;
            device_wrapper.device = device.Get();
            device_context_wrapper.device_context = context.Get();
            graph.FillPacket(input_stream0.GetName(), bmf_sdk::Packet(device_wrapper));
            graph.FillPacket(input_stream0.GetName(), bmf_sdk::Packet(device_context_wrapper));

            D3D11TextureWrapper input;
            input.texture = input_rgba.Get();
            graph.FillPacket(input_stream1.GetName(), bmf_sdk::Packet(input));

            Packet output_pkt = graph.Generate(resize_output_stream.GetName());
            ID3D11Texture2D* out_texture = nullptr;
            D3D11TextureWrapper output_ = output_pkt.get<D3D11TextureWrapper>();
            out_texture = output_.texture;
            // Read Output and write file local
            std::vector<uint8_t> outdata(TESTOUTPUTWIDTH * TESTOUTPUTHEIGHT * 4, 1);
            ReadDataFromTexture(device.Get(), context.Get(), out_texture, TESTOUTPUTWIDTH, TESTOUTPUTHEIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, outdata, 4);

            std::ofstream outputFile("output.rgb", std::ios::binary);
            if (!outputFile.is_open()) {
                printf("open outputfile error!\n");
            }
            outputFile.write((const char*)outdata.data(), sizeof(uint8_t) * TESTOUTPUTWIDTH * TESTOUTPUTHEIGHT * 4);
            outputFile.close();
            out_texture->Release();
            out_texture = nullptr;
            context->Flush();
            
        }
        catch (const fs::filesystem_error& e) {
            std::cerr << "FileSystem error: " << e.what() << std::endl;
        }
        catch (const std::exception& e) {
            std::cerr << "General error: " << e.what() << std::endl;
        }

    }
    return 0;
}

以下是程序的主要步驟和功能:

1.初始化 DirectX 11 設(shè)備和上下文

使用 D3D11CreateDevice 函數(shù)創(chuàng)建 D3D11 設(shè)備和上下文對(duì)象。

2.準(zhǔn)備輸入圖像數(shù)據(jù)

從文件中讀取 RGBA 格式的測(cè)試圖像數(shù)據(jù)。

3.創(chuàng)建輸入圖像的 D3D11 紋理對(duì)象

使用 CreateTexture2D 函數(shù)創(chuàng)建輸入圖像的 D3D11 紋理對(duì)象。

4.構(gòu)建 BMF Graph

使用生成器模式創(chuàng)建并初始化 bmf::builder::Graph

添加 2 個(gè)輸入流(配置流和數(shù)據(jù)流)和輸出流,以及需要的參數(shù),通過(guò) json 數(shù)據(jù)創(chuàng)建要縮放的圖像寬高。

導(dǎo)入名為 d3dresizemodule 的多媒體處理模塊,并連接輸入流和輸出流,調(diào)用 Start 函數(shù)啟動(dòng) Graph

5.填充輸入數(shù)據(jù)

將創(chuàng)建的 D3D11 設(shè)備和上下文對(duì)象填充到配置流中,驅(qū)動(dòng)模塊內(nèi)部完成 DirectX 側(cè)的初始化

將創(chuàng)建的輸入圖像紋理送入到數(shù)據(jù)流中。

6.執(zhí)行 BMF Graph

通過(guò)調(diào)用 graph.Generate 方法驅(qū)動(dòng)模塊執(zhí)行處理流程,獲取數(shù)據(jù)幀

7.讀取輸出數(shù)據(jù)并寫(xiě)入文件

從生成的數(shù)據(jù)中獲取輸出圖像的 D3D11 紋理對(duì)象。

使用自定義函數(shù) ReadDataFromTexture 讀取紋理數(shù)據(jù)。

將輸出圖像數(shù)據(jù)寫(xiě)入文件 output.rgb 中。

8.調(diào)用 Flush 方法清空任務(wù)隊(duì)列,同步釋放 DirectX 相關(guān)資源

9.異常處理

在程序執(zhí)行過(guò)程中捕獲可能出現(xiàn)的異常,并輸出錯(cuò)誤信息。

通過(guò)以上步驟,測(cè)試程序?qū)崿F(xiàn)了對(duì) Direct3D 11 實(shí)現(xiàn)的圖像 resize 模塊的測(cè)試,并將處理結(jié)果保存到文件中,用于后續(xù)的分析和驗(yàn)證。

構(gòu)建腳本與運(yùn)行

本節(jié)主要介紹構(gòu)建并運(yùn)行圖像縮放處理程序 Demo 的流程與步驟,同樣需要進(jìn)入 msys2 環(huán)境,本文實(shí)現(xiàn)的 Demo 可以通過(guò)構(gòu)建腳本實(shí)現(xiàn)多種不同 Module 的選擇性構(gòu)建,Demo 項(xiàng)目的目錄結(jié)構(gòu)如下所示:

bmf_demo/
│
├── cmake/
│   ├── win-toolchain.cmake  
├── modules/
│   ├── common/
│   │   ├── d3d11/
│   │   │   ├── include/
│   │   │   ├   ├── d3d11_common.h
│   │   │   ├── src/
│   │   │   ├   ├── d3d11_common.cpp
│   ├── d3d11resizemodule/
│   │   ├── include/
│   │   ├   ├── d3dresizemodule.h
│   │   ├── src/
│   │   ├   ├── d3dresizemodule.cpp
│   │   ├── test/
│   │   ├   ├── test_d3dresizemodule.cpp
│   │   ├── CMakeLists.txt
│   └── ...
│
├── build.sh
├── CMakeLists.txt
└── CMakePresets.json

其中 cmake/win-toolchain.cmake 是一個(gè) cmake 配置文件,其中包括 BMF 適配 Windows msvc 環(huán)境的編譯選項(xiàng)配置,modules 文件夾下包含了諸多用戶(hù)實(shí)現(xiàn)的 modules,其中 common 文件夾為公共文件目錄,其中實(shí)現(xiàn)了上文所述的 DirectX API,如果用戶(hù)想添加新實(shí)現(xiàn)的 module,可以在 modules 文件夾下再新建一個(gè)文件夾,命名為 module 名稱(chēng),內(nèi)部文件布局保持與 d3d11resizemodule 一致即可。

build.sh 腳本用于構(gòu)建項(xiàng)目,并支持一些參數(shù)來(lái)配置構(gòu)建過(guò)程。這個(gè)解釋使用方法和每個(gè)參數(shù)的意義和作用

--module

指定要編譯構(gòu)建的 module 名稱(chēng),需要與 modules 文件夾下的某個(gè)目錄對(duì)齊命名

--msvc

指定 msvc 版本,支持[2013, 2015, 2017, 2019, 2022]

--bmf_lite

指定 lite 控制標(biāo)識(shí)位,用于控制內(nèi)部的一些 Win 的特化配置項(xiàng)

--test

控制是否編譯對(duì)應(yīng)測(cè)試 demo 程序,內(nèi)部通過(guò) cmake 控制

對(duì)于本例,本文使用的編譯構(gòu)建命令如下:

./build.sh --msvc=2022 --module=d3dresizemodule --bmf-lite --test

在構(gòu)建項(xiàng)目前,需要設(shè)置 BMF 庫(kù)的 INCLUDE 和 LIB 環(huán)境變量

export BMF_INCLUDE_DIR=/path/to/bmf/include
export BMF_LIBRARY_DIR=/path/to/bmf/lib

在 msys 執(zhí)行腳本后,會(huì)在本地生成 build_windows_lite_{preset} 文件夾,與 BMF 框架相同,可以使用 Visual Studio 界面交互進(jìn)行項(xiàng)目構(gòu)建,或使用命令行直接構(gòu)建

圖片圖片

構(gòu)建完成后,在項(xiàng)目目錄本地會(huì)生成 output 文件夾,切換至 output/bin/{preset}/Release/,執(zhí)行 ./test_d3dresizemodule.exe 程序,程序輸出如下

圖片

發(fā)現(xiàn)在本地生成了 output.rgb 文件

圖片

使用 ffplay 查看生成的圖像

ffplay -pix_fmt rgba -s 1080x1920 output.rgb

結(jié)果如下

圖片


責(zé)任編輯:龐桂玉 來(lái)源: 字節(jié)跳動(dòng)技術(shù)團(tuán)隊(duì)
相關(guān)推薦

2023-08-15 13:57:08

開(kāi)發(fā)者

2013-08-28 16:08:19

多媒體Windows8.1

2011-06-09 10:07:28

Qt phonon

2010-01-27 13:52:15

Android多媒體框

2014-07-16 16:17:00

2012-05-02 13:22:46

JavaJava設(shè)計(jì)

2011-06-24 10:21:11

Qt phonon 多媒體

2010-08-01 15:34:27

Android

2010-06-30 10:38:05

2015-09-20 20:13:55

2009-02-09 23:29:40

兼容硬件驅(qū)動(dòng)

2013-12-17 13:29:04

iOS開(kāi)發(fā)多媒體

2009-12-25 17:02:33

WPF多媒體

2010-10-27 11:27:50

MAS視頻監(jiān)控H3C

2013-12-17 11:18:53

iOS開(kāi)發(fā)多媒體API

2010-05-05 21:56:37

融合通信

2023-05-15 07:28:48

2020-12-20 09:05:30

騰訊多媒體5G

2011-08-02 10:36:02

iOS開(kāi)發(fā) SDK 多媒體
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)