当前位置: 首页 > news >正文

GPU视频编解码:X86 VideoProcessFrame 视频编解码入门(二)

一.实验设备硬件情况说明

        1.VPF框架目前不支持Jetson系列,实验设备为X86主机,

root@autodl-container-2b344a8a9f-e5fa6b67:~/autodl-tmp/VideoProcessingFramework# cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.3 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.3 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
root@autodl-container-2b344a8a9f-e5fa6b67:~/autodl-tmp/VideoProcessingFramework# 

        2.设备端为auto-dl的远程设备(在wsl上cuda-11.8编译不出来PyNvCodec) 

root@autodl-container-2b344a8a9f-e5fa6b67:~/autodl-tmp/VideoProcessingFramework# nvidia-smi
Sat Mar 15 15:47:49 2025       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14              Driver Version: 550.54.14      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 4090 D      On  |   00000000:C2:00.0 Off |                  Off |
| 32%   33C    P0             44W /  425W |       0MiB /  24564MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
                                                                                         
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+

        3.尽量选择cuda-12.1及其以上版本吧 

root@autodl-container-2b344a8a9f-e5fa6b67:~/autodl-tmp/VideoProcessingFramework# nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2023 NVIDIA Corporation
Built on Mon_Apr__3_17:16:06_PDT_2023
Cuda compilation tools, release 12.1, V12.1.105
Build cuda_12.1.r12.1/compiler.32688072_0

二.PyNvCodec安装

        1.基础配置:cuda12.1+python3.12,cuda不要低于12.1,python3.10以上

        2.git clone https://github.com/NVIDIA/VideoProcessingFramework.git

        3.cd VideoProcessingFramework

        4.中间会出现一个error,安装一下依赖就好了

root@autodl-container-2b344a8a9f-e5fa6b67:~/autodl-tmp/VideoProcessingFramework# apt-get install -y libavfilter-dev

        5.pip3 install . 就会提示安装成功

root@autodl-container-2b344a8a9f-e5fa6b67:~/autodl-tmp/VideoProcessingFramework# nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2023 NVIDIA Corporation
Built on Mon_Apr__3_17:16:06_PDT_2023
Cuda compilation tools, release 12.1, V12.1.105
Build cuda_12.1.r12.1/compiler.32688072_0
root@autodl-container-2b344a8a9f-e5fa6b67:~/autodl-tmp/VideoProcessingFramework# apt-get install -y libavfilter-dev^C
root@autodl-container-2b344a8a9f-e5fa6b67:~/autodl-tmp/VideoProcessingFramework# python3
Python 3.12.3 | packaged by Anaconda, Inc. | (main, May  6 2024, 19:46:43) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import PyNvCodec
>>> 

三.PyNvDecoder + PyNvEncoder 实例

        1.定义超参数

# 设定输入视频的基础信息
gt_width = 848  # 视频宽度
gt_height = 464  # 视频高度
gt_res_change = 47  # 预计第 47 帧发生分辨率变化
gt_is_vfr = False  # 是否是可变帧率(VFR)
gt_pix_fmt = nvc.PixelFormat.NV12  # 设定像素格式为 NV12
gt_framerate = 30  # 设定帧率为 30 FPS
gt_num_frames = 96  # 总帧数 96
gt_timebase = 8.1380e-5  # 设定时间基准
gt_color_space = nvc.ColorSpace.BT_709  # 设定颜色空间为 BT.709
gt_color_range = nvc.ColorRange.MPEG  # 设定颜色范围

        2. 测试 GPU 硬件编码功能:读取输入视频,解码并编码成 H.264,验证编码的帧数与解码的帧数是否一致

def test_encode_all_surfaces(self):
        """
        测试 GPU 硬件编码功能:
        - 读取输入视频,解码并编码成 H.264
        - 验证编码的帧数与解码的帧数是否一致
        """
        gpu_id = 0  # 选择 GPU 设备
        res = str(gt_width) + "x" + str(gt_height)  # 设定编码分辨率
        encFrame = np.ndarray(shape=(0), dtype=np.uint8)  # 初始化编码帧数据

        # 创建解码器
        nvDec = nvc.PyNvDecoder(gt_file, gpu_id)

        # 创建编码器(H.264)
        nvEnc = nvc.PyNvEncoder(
            {
                "preset": "P4",
                "tuning_info": "high_quality",
                "codec": "h264",
                "profile": "high",
                "s": res,
                "bitrate": "1M",
            },
            gpu_id,
        )

        frames_sent = 0  # 记录发送到编码器的帧数
        frames_recv = 0  # 记录从编码器获取的帧数

        while True:
            dec_surf = nvDec.DecodeSingleSurface()  # 读取解码后的单帧数据
            if not dec_surf or dec_surf.Empty():
                break  # 结束解码

            frames_sent += 1  # 统计发送到编码器的帧数
            nvEnc.EncodeSingleSurface(dec_surf, encFrame)  # 编码帧

            if encFrame.size:
                frames_recv += 1  # 统计成功编码的帧数

        # 结束编码,确保所有帧被正确编码
        while True:
            success = nvEnc.FlushSinglePacket(encFrame)
            if success and encFrame.size:
                frames_recv += 1
            else:
                break

        # 验证解码的帧数和编码的帧数一致
        self.assertEqual(frames_sent, frames_recv)

        3.测试在 输入视频分辨率动态变化 时,解码器和编码器的重新配置功能。

def test_reconfigure(self):
        """
        测试在 **输入视频分辨率动态变化** 时,解码器和编码器的重新配置功能。

        测试流程:
        1. 读取 `test_res_change.h264` 这个视频(分辨率会在播放过程中变化)。
        2. 逐帧解码,并检查当前帧的分辨率是否发生变化:
        - 若分辨率变化,则 **重新配置** 编码器,使其匹配新分辨率。
        - 重新配置时,编码器会插入 **IDR(关键帧)**,确保编码流连续性。
        3. 每次编码后,将编码帧解码回来,检查是否符合期望的输出分辨率。

        断言:
        - **编码器的分辨率在 reconfigure 之后应与解码的帧一致**。
        - **在分辨率变化前,解码得到的帧分辨率应与原始设定一致**。

        目的:
        - 验证 `PyNvDecoder`(解码器)能够自动检测分辨率变化。
        - 验证 `PyNvEncoder` 能够正确执行 `Reconfigure()` 并维持 H.264/H.265 编码流的稳定性。
        """
        gpu_id = 0  # 选择 GPU 设备
        res = str(gt_width) + "x" + str(gt_height)  # 设定默认编码分辨率
        encFrame = np.ndarray(shape=(0), dtype=np.uint8)  # 初始化存储编码数据的 NumPy 数组

        # 初始化解码器(用于处理分辨率变化的输入视频)
        nvDec = nvc.PyNvDecoder(gt_file_res_change, gpu_id)

        # 另一个解码器,用于解码回编码后的数据
        nvRcn = nvc.PyNvDecoder(gt_width, gt_height, nvc.PixelFormat.NV12, nvc.CudaVideoCodec.H264, gpu_id)

        # 初始化编码器(初始设定为固定分辨率)
        nvEnc = nvc.PyNvEncoder(
            {
                "preset": "P4",  # 设定编码器预设参数
                "tuning_info": "high_quality",  # 设定编码质量
                "codec": "h264",  # 选择 H.264 编码
                "profile": "high",  # 选择 High Profile
                "s": res,  # 设定初始分辨率
                "bitrate": "1M",  # 设定比特率为 1Mbps
            },
            gpu_id,
        )

        frames_recn = 0  # 统计解码并成功编码的帧数

        while True:
            # 从视频文件中读取并解码单帧数据
            dec_surf = nvDec.DecodeSingleSurface()
            if not dec_surf or dec_surf.Empty():
                break  # 读取完毕,结束循环

            # 获取当前解码帧的分辨率
            sw = dec_surf.Width()
            sh = dec_surf.Height()

            # 检查是否发生了分辨率变化
            if sw != gt_width or sh != gt_height:
                # 发生分辨率变化时,需要先将编码器缓冲区内的帧全部输出
                while nvEnc.FlushSinglePacket(encFrame):
                    frames_recn += 1

                # 重新配置编码器,使其匹配新的分辨率
                res = str(sw) + "x" + str(sh)
                self.assertTrue(nvEnc.Reconfigure({"s": res}, force_idr=True, reset_encoder=True))

                # 断言:编码器的分辨率已经正确更新
                self.assertEqual(nvEnc.Width(), sw)
                self.assertEqual(nvEnc.Height(), sh)

            # 使用新的编码器配置对当前帧进行编码
            nvEnc.EncodeSingleSurface(dec_surf, encFrame)

            if encFrame.size:
                # 解码编码后的数据,确保正确性
                dec_surf = nvRcn.DecodeSurfaceFromPacket(encFrame)
                if dec_surf and not dec_surf.Empty():
                    frames_recn += 1

                    if frames_recn < gt_res_change:
                        # 分辨率变化前,解码的帧应该仍然保持原始设定的大小
                        self.assertEqual(dec_surf.Width(), gt_width)
                        self.assertEqual(dec_surf.Height(), gt_height)
                    else:
                        # 发生变化后,解码帧的大小应匹配新的输入分辨率
                        self.assertEqual(dec_surf.Width(), sw)
                        self.assertEqual(dec_surf.Height(), sh)

 

相关文章:

  • Git提交前时间检查
  • Golang | 每日一练 (6)
  • Mysql 安装指南(小白入门)
  • 基于FPGA轨道交通6U机箱CPCI脉冲板板卡
  • vs2017版本与arcgis10.1的ArcObject SDK for .NET兼容配置终结解决方案
  • 【笔记】计算机网络——数据链路层
  • 10.PE导出表
  • 3.8 Spring Boot监控:Actuator+Prometheus+Grafana可视化
  • 【vue2 + Cesium】使用Cesium、添加第三方地图、去掉商标、Cesium基础配置、地图放大缩小事件、获取可视区域、层级、高度
  • Python第六章01:列表(lsit)定义语法
  • ESP32(3)UDP通信
  • 【Linux篇】:进程抢占式调度的量子纠缠--状态,优先级与上下文切换的三角博弈
  • python基础8 单元测试
  • 【算法】一维差分
  • 【Linux】Makefile秘籍
  • 深度解读 | AI驱动下的新型金融对冲策略:稀疏奖励强化学习的应用
  • 1.angular介绍
  • 第九步:web-js
  • Go基础语法阶段核心内容(5天)
  • ESP32(4)TCP通信
  • 复星医药换帅:陈玉卿接棒吴以芳任董事长,吴以芳改任复星国际执行总裁
  • 秦洪看盘|上市公司业绩“排雷”近尾声,A股下行压力趋缓
  • 跟着京剧电影游运河,京杭大运河沿线六城举行京剧电影展映
  • 河北:开展领导干部任性用权等形式主义官僚主义问题专项整治
  • 4月份全国93个国家气象站日最高气温达到或突破极值
  • 学校食堂饭菜有蛆?举报人遭值班人员辱骂?四川苍溪县教育局回应