在python中使用nvidia的VPF库对RTSP流进行硬解码并使用opencv进行显示

这篇具有很好参考价值的文章主要介绍了在python中使用nvidia的VPF库对RTSP流进行硬解码并使用opencv进行显示。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

解码并处理视频流的多线程应用

随着视频处理技术的不断发展,越来越多的应用需要对视频流进行解码和处理。在本文中,我们将介绍一个基于Python的多线程应用程序,该应用程序可以解码并处理多个RTSP视频流,同时利用GPU加速,以提高处理速度。

这个应用程序使用了一些关键的Python库和工具,包括PyNvCodec、OpenCV、和PyCUDA等。它充分利用了现代GPU的计算能力,实现了高效的视频解码和处理。

多线程解码

在这个应用程序中,我们使用了Python的concurrent.futures库来实现多线程解码。每个视频流都在独立的线程中解码,这样可以同时处理多个视频流,充分利用了多核CPU的性能。

from concurrent.futures import ThreadPoolExecutor

# ...

# 创建线程池
pool = ThreadPoolExecutor(max_workers=len(urls))
futures = []

# 遍历每个视频流并提交解码任务
for url in urls:
    future = pool.submit(decode_rtsp_stream, index, url, gpuID)
    futures.append(future)
    index += 1

# 等待所有任务完成
pool.shutdown()

# 获取每个任务的结果
for future in futures:
    future.result()

视频解码和处理

视频解码是这个应用程序的核心功能。我们使用PyNvCodec库来进行视频解码,同时利用了GPU来加速处理。

def decode_rtsp_stream(thread_index: int, url: str, gpu_id: int):
    # 获取视频流参数
    params = get_stream_params(url)

    # ...

    # 创建NvDecoder实例
    nvdec = nvc.PyNvDecoder(w, h, f, c, g)

    # ...

    while True:
        # 读取视频流数据
        bits = proc.stdout.read(read_size)

        # ...

        # 解码视频帧
        surf = nvdec.DecodeSurfaceFromPacket(enc_packet, pkt_data)

        # ...

        # 执行颜色空间转换和表面下载
        cvtSurface = nv_cvt.Execute(surf, cc_ctx)
        success = nv_down.DownloadSingleSurface(cvtSurface, data)

        # ...

        # 显示解码后的帧
        cv2.imshow(str(thread_index), new_data)
        cv2.waitKey(1)

    # ...

完整代码

这个应用程序可以广泛用于视频监控、实时视频分析、视频编码和解码等领域。通过多线程解码和GPU加速,它可以处理多个高分辨率视频流,并在实时性要求较高的情况下提供流畅的显示和处理效果。

import os
import sys
import subprocess
import json
import PyNvCodec as nvc
import numpy as np
from io import BytesIO
from multiprocessing import Process
import uuid
import time
from concurrent.futures import ThreadPoolExecutor
import cv2
import pycuda.gpuarray as gpuarray
# import PytorchNvCodec as pnvc
import torch
import torchvision.transforms as T



def add_cuda_dll_directories():
    if os.name == "nt":
        cuda_path = os.environ.get("CUDA_PATH")
        if cuda_path:
            os.add_dll_directory(cuda_path)
        else:
            print("CUDA_PATH environment variable is not set.", file=sys.stderr)
            exit(1)

        sys_path = os.environ.get("PATH")
        if sys_path:
            paths = sys_path.split(";")
            for path in paths:
                if os.path.isdir(path) and path != '.':
                    os.add_dll_directory(path)
        else:
            print("PATH environment variable is not set.", file=sys.stderr)
            exit(1)


def surface_to_tensor(surface: nvc.Surface) -> torch.Tensor:
    """
    Converts planar rgb surface to cuda float tensor.
    """
    if surface.Format() != nvc.PixelFormat.RGB_PLANAR:
        raise RuntimeError("Surface shall be of RGB_PLANAR pixel format")

    surf_plane = surface.PlanePtr()
    img_tensor = pnvc.DptrToTensor(
        surf_plane.GpuMem(),
        surf_plane.Width(),
        surf_plane.Height(),
        surf_plane.Pitch(),
        surf_plane.ElemSize(),
    )
    if img_tensor is None:
        raise RuntimeError("Can not export to tensor.")

    img_tensor.resize_(3, int(surf_plane.Height() / 3), surf_plane.Width())
    img_tensor = img_tensor.type(dtype=torch.cuda.FloatTensor)
    img_tensor = torch.divide(img_tensor, 255.0)
    img_tensor = torch.clamp(img_tensor, 0.0, 1.0)

    return img_tensor


def get_stream_params(url: str):
    cmd = [
        "ffprobe",
        "-v",
        "quiet",
        "-print_format",
        "json",
        "-show_format",
        "-show_streams",
        url,
    ]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    stdout = proc.communicate()[0]

    bio = BytesIO(stdout)
    json_out = json.load(bio)

    params = {}
    if not "streams" in json_out:
        return {}

    for stream in json_out["streams"]:
        if stream["codec_type"] == "video":
            params["width"] = stream["width"]
            params["height"] = stream["height"]
            params["framerate"] = float(eval(stream["avg_frame_rate"]))

            codec_name = stream["codec_name"]
            is_h264 = True if codec_name == "h264" else False
            is_hevc = True if codec_name == "hevc" else False
            if not is_h264 and not is_hevc:
                raise ValueError(
                    "Unsupported codec: "
                    + codec_name
                    + ". Only H.264 and HEVC are supported in this sample."
                )
            else:
                params["codec"] = (
                    nvc.CudaVideoCodec.H264 if is_h264 else nvc.CudaVideoCodec.HEVC
                )

                pix_fmt = stream["pix_fmt"]
                is_yuv420 = pix_fmt == "yuv420p"
                is_yuv444 = pix_fmt == "yuv444p"

                # YUVJ420P and YUVJ444P are deprecated but still wide spread, so handle
                # them as well. They also indicate JPEG color range.
                is_yuvj420 = pix_fmt == "yuvj420p"
                is_yuvj444 = pix_fmt == "yuvj444p"

                if is_yuvj420:
                    is_yuv420 = True
                    params["color_range"] = nvc.ColorRange.JPEG
                if is_yuvj444:
                    is_yuv444 = True
                    params["color_range"] = nvc.ColorRange.JPEG

                if not is_yuv420 and not is_yuv444:
                    raise ValueError(
                        "Unsupported pixel format: "
                        + pix_fmt
                        + ". Only YUV420 and YUV444 are supported in this sample."
                    )
                else:
                    params["format"] = (
                        nvc.PixelFormat.NV12 if is_yuv420 else nvc.PixelFormat.YUV444
                    )

                # Color range default option. We may have set when parsing
                # pixel format, so check first.
                if "color_range" not in params:
                    params["color_range"] = nvc.ColorRange.MPEG
                # Check actual value.
                if "color_range" in stream:
                    color_range = stream["color_range"]
                    if color_range == "pc" or color_range == "jpeg":
                        params["color_range"] = nvc.ColorRange.JPEG

                # Color space default option:
                params["color_space"] = nvc.ColorSpace.BT_601
                # Check actual value.
                if "color_space" in stream:
                    color_space = stream["color_space"]
                    if color_space == "bt709":
                        params["color_space"] = nvc.ColorSpace.BT_709

                return params
    return {}


def decode_rtsp_stream(thread_index: int, url: str, gpu_id: int):
    params = get_stream_params(url)

    if not len(params):
        raise ValueError("Can not get " + url + " streams params")

    w = params["width"]
    h = params["height"]
    f = params["format"]
    c = params["codec"]
    framerate = params["framerate"]
    g = gpu_id

    if nvc.CudaVideoCodec.H264 == c:
        codec_name = "h264"
    elif nvc.CudaVideoCodec.HEVC == c:
        codec_name = "hevc"
    bsf_name = codec_name + "_mp4toannexb,dump_extra=all"

    cmd = [
        "ffmpeg",
        "-hide_banner",
        "-i",
        url,
        "-c:v",
        "copy",
        "-bsf:v",
        bsf_name,
        "-f",
        codec_name,
        "pipe:1",
    ]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)

    nvdec = nvc.PyNvDecoder(w, h, f, c, g)

    read_size = 4096
    rt = 0
    fd = 0

    t0 = time.time()
    print("running stream")

    # nv_cvt = nvc.PySurfaceConverter(
    #     w, h, self.nvYuv.Format(), nvc.PixelFormat.RGB, 0
    # )

    nv_cvt = nvc.PySurfaceConverter(w, h, nvc.PixelFormat.NV12, nvc.PixelFormat.BGR, g)
    cc_ctx = nvc.ColorspaceConversionContext(
        params["color_space"], params["color_range"]
    )
    nv_down = nvc.PySurfaceDownloader(
        w, h, nv_cvt.Format(), g
    )
    data = np.zeros((w * h, 3), np.uint8)
    empty_count = 0
    while True:

        t1=time.time()
        if not read_size:
            read_size = int(rt / fd)
            rt = read_size
            fd = 1

        bits = proc.stdout.read(read_size)
        if not len(bits):
            print("Can't read data from pipe")
            break
        else:
            rt += len(bits)

        enc_packet = np.frombuffer(buffer=bits, dtype=np.uint8)
        pkt_data = nvc.PacketData()
        try:
            surf = nvdec.DecodeSurfaceFromPacket(enc_packet, pkt_data)
            if not surf.Empty():
                fd += 1
                if pkt_data.bsl < read_size:
                    read_size = pkt_data.bsl

                cvtSurface = nv_cvt.Execute(surf, cc_ctx)
                success = nv_down.DownloadSingleSurface(cvtSurface, data)
                if success:
                    new_data = data.reshape((h, w, 3))
                    cv2.imshow(str(thread_index), new_data)
                    cv2.waitKey(1)
            else:
                empty_count += 1
                if empty_count > framerate * 30:
                    print("surf is Empty too many times > "+str(framerate * 30))
                    nvdec = nvc.PyNvDecoder(w, h, f, c, g)
                    empty_count = 0


        except nvc.HwResetException:
            nvdec = nvc.PyNvDecoder(w, h, f, c, g)
            empty_count = 0
            continue

        t2 = time.time()

        # print((t2-t1)*1000)


if __name__ == "__main__":
    add_cuda_dll_directories()

    print("This sample decodes multiple videos in parallel on given GPU.")
    print("It doesn't do anything beside decoding, output isn't saved.")
    print("Usage: SampleDecodeRTSP.py $gpu_id $url1 ... $urlN .")

    if len(sys.argv) < 2:
        print("Provide gpu ID and input URL(s).")
        exit(1)

    gpuID = int(sys.argv[1])
    urls = sys.argv[2:]

    pool = ThreadPoolExecutor(max_workers=len(urls))
    futures = []
    index = 0
    for url in urls:
        future = pool.submit(decode_rtsp_stream, index, url, gpuID)
        futures.append(future)
        index += 1

    pool.shutdown()

    for future in futures:
        future.result()

运行脚本

python rtsp_decoder.py  0 rtsp://admin:a1234567@10.10.16.26:554/Streaming/Channels/101?transportmode=multicast

VPF库安装

windows11编译VideoProcessingFramework库_random_2011的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-635641.html

到了这里,关于在python中使用nvidia的VPF库对RTSP流进行硬解码并使用opencv进行显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python使用OpenPyXl库对Excel进行操作

    参考:知乎文章 / OpenPyXL教程 / 博客园 目录 python使用OpenPyXl库对Excel进行操作 1. 基本概念 2. 判断文件是否存在 3. 创建和打开Excel文件 3.1. 创建Excel文件 3.2. 打开已有的Excel文件 4. 储存数据 5. sheet操作 6. cell操作 在OpenPyXl中的基本改变跟Excel文件中是完全一致的。 workbook sheet c

    2024年02月01日
    浏览(60)
  • python 基础教程:使用jieba库对文本进行分词

    Python的jieba库是一个中文分词工具,它可以将一段中文文本分割成一个一个的词语,方便后续的自然语言处理任务,如文本分类、情感分析等。jieba库使用了基于前缀词典的分词方法,能够处理中文的各种复杂情况,如歧义词、新词等。它还提供了多种分词模式,如精确模式、

    2024年02月05日
    浏览(42)
  • python 基础知识:使用jieba库对文本进行分词

    前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 一、jieba库是什么? Python的jieba库是一个中文分词工具,它可以将一段中文文本分割成一个一个的词语,方便后续的自然语言处理任务,如文本分类、情感分析等。 jieba库使用了基于前缀词典的分词方法,能够处理中文的各种复

    2024年02月10日
    浏览(47)
  • opencv rtsp 硬件解码

    硬件解码的方案有太多种,如果使用ffmpeg硬件解码是最方便的,不方便的是把解码过后的GPU 拉到 CPU 上,再使用opencv的Mat 从cpu 上上载到gpu上,是不是多了两个过程,应该是直接从GPU mat 直接去处理, 最后一步再从GPU mat 上下载到cpu,render显示。 GPU 硬件解码是nv12 格式,我们

    2024年02月14日
    浏览(27)
  • 使用Python的Pillow库对图片进行格式转换和重命名

    Python 是一门功能强大的编程语言,广泛应用于科学计算、数据分析、机器学习等领域。随着互联网的发展和智能手机的普及,现在人们更加倾向于通过图片来表达自己的想法和情感。因此,在科研、工程和设计等领域,对于图片的处理也变得越来越重要。 Python 提供了多种图

    2024年02月08日
    浏览(82)
  • Python实战 | 使用 Python 的日志库(logging)和 pandas 库对日志数据进行分析

    专栏集锦,大佬们可以收藏以备不时之需 Spring Cloud实战专栏:https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏:https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏:https://blog.csdn.net/superdangbo/category_9271502.html tensorflow专栏:https://blog.csdn.net/superdangbo/category_869

    2024年02月05日
    浏览(50)
  • python rtsp 硬件解码 二

    上次使用了python的opencv模块 述说了使用PyNvCodec 模块,这个模块本身并没有rtsp的读写,那么读写rtsp是可以使用很多方法的,我们为了输出到pytorch直接使用AI程序,简化rtsp 输入,可以直接使用ffmpeg的子进程 使用pyav,这个下次再讲 使用pipe方式,也就是我们使用任何一种方式都

    2024年02月11日
    浏览(44)
  • 通过Python的jieba库对文本进行分词

    大家好,我是空空star,本篇给大家分享一下通过Python的jieba库对文本进行分词。 Python的jieba库是一个中文分词工具,它可以将一段中文文本分割成一个一个的词语,方便后续的自然语言处理任务,如文本分类、情感分析等。jieba库使用了基于前缀词典的分词方法,能够处理中

    2024年02月05日
    浏览(38)
  • python使用opencv低延迟拉取rtsp流

    这种方式比较简单,但是拉取的视频流会有1s左右的延迟 安装依赖 cmake执行完成后需要注意下面  需要注意上面的两个划红线的地方,GStreamer需要为YES,编译时候,才能将gstreamer编译到opencv中去,python3下面的四行内容要完整才能保证opencv编译到python3的opencv中去 如果有问题,

    2024年02月11日
    浏览(33)
  • 【记录】自己动手使用HAL库对MPU6050进行编程

    有关于I2C的知识看这篇文章: 【记录】嵌入式经典通信I2C理解 有关于MPU6050的基础知识和手册去看这篇文章: 【记录】MPU6050原理快速入门(附手册) 此篇记录代码编写过程: 直接看汇总: 代码汇总 一、完成CubeMX的配置。 首先分析自身需求:MPU6050需要用到I2C或者是模拟

    2024年02月16日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包