英伟达 Jetson 程序员必读:摄像头流媒体和多媒体的程序设计

本文档详细介绍了在英伟达Jetson平台上处理摄像头流媒体和多媒体的程序设计,涵盖MIPI CSI摄像头、V4L2摄像机、RTP/RTSP协议、视频文件和图像文件等,提供了多种接口和协议的支持,并给出了相应的命令行参数和源代码示例。

0. 引言

该项目通过各种接口和协议支持流式视频源和图像,包括:

  • MIPI CSI摄像机
  • V4L2摄像机
  • RTP/RTSP协议
  • 视频和图像
  • 图像序列
  • OpenGL窗口

1. 流媒体的 URI 标识

流通过资源URI标识,并通过videoSource和videoOutput api访问。下表显示了每种类型的流所支持的输入/输出协议和示例URI:

1.1 输入流

.Protocol ResourceURINotes
MIPI CSI 摄像头csi://csi://0CSI camera 0 (substitute other camera numbers for 0)
V4L2 摄像头v4l2://v4l2:///dev/video0V4L2 device 0 (substitute other camera numbers for 0)
RTP 流rtp://rtp://@:1234 localhost, port1234 (requires additional configuration)
RTSP 流rtsp://rtsp://<remote-ip>:1234Replace with remote host’s IP or hostname
视频文件file://file://my_video.mp4Supports loading MP4, MKV, AVI, FLV (see codecs below)
图像文件file://file://my_image.jpgSupports loading JPG, PNG, TGA, BMP, GIF, ect.
图像序列file://file://my_directory/Searches for images in alphanumeric order
  • 支持的解码器编解码器:H.264, H.265, VP8, VP9, MPEG-2, MPEG-4, MJPEG
  • file://, v4l2://, and csi:// 协议前缀可以作为缩写从 URI 的地址中省略。

1.2 输出流

.ProtocolResource URINotes
RTP 流rtp://rtp://:1234Replace with remote host’s IP or hostname
视频文件file://file://my_video.mp4Supports saving MP4, MKV, AVI, FLV (see codecs below)
图像文件file://file://my_image.jpgSupports saving JPG, PNG, TGA, BMP
图像序列file://file://image_%i.jpg%i is replaced by the image number in the sequence
OpenGL 窗口display://display://0Creates GUI window on screen 0
  • 支持的编码器: H.264, H.265, VP8, VP9, MJPEG
  • file:// 协议前缀可以从URI中省略
  • 默认情况下,除非指定–headless,否则将创建OpenGL显示窗口

2. 命令行参数

JETSON推理中的每个示例C++和Python程序都接受相同的命令行参数集,用于指定流URI和附加选项。因此,这些选项可以用于任何示例(例如 imagenet/imagenet.py, detectnet/detectnet.py, segnet/segnet.py, video-viewer/video-viewer.py 等)。这些命令行参数通常采用以下形式:

$ imagenet [options] input_URI [output_URI]  # output URI is optional

其中输入和输出 URI 由两个位置参数指定。例如:

$ imagenet input.jpg output.jpg              # classify input.jpg, save as output.jpg

如上所述,这里可以替换来自jetson推断的任何示例,因为它们使用相同的命令行解析。以下是运行每个程序时可以指定的其他流选项:

2.1 Input Options

    input_URI            resource URI of the input stream (see table above)
  --input-width=WIDTH    explicitly request a resolution of the input stream
  --input-height=HEIGHT  (resolution is optional, except required for RTP)
  --input-codec=CODEC    RTP requires the codec to be set, one of these:
                             * h264, h265
                             * vp8, vp9
                             * mpeg2, mpeg4
                             * mjpeg
  --input-flip=FLIP      flip method to apply to input (excludes V4L2):
                             * none (default)
                             * counterclockwise
                             * rotate-180
                             * clockwise
                             * horizontal
                             * vertical
                             * upper-right-diagonal
                             * upper-left-diagonal
  --input-loop=LOOP      for file-based inputs, the number of loops to run:
                             * -1 = loop forever
                             *  0 = don't loop (default)
                             * >0 = set number of loops

2.2 输出选项

    output_URI           resource URI of the output stream (see table above)
  --output-codec=CODEC   desired codec for compressed output streams:
                            * h264 (default), h265
                            * vp8, vp9
                            * mpeg2, mpeg4
                            * mjpeg
  --bitrate=BITRATE      desired target VBR bitrate for compressed streams,
                         in bits per second. The default is 4000000 (4 Mbps)
  --headless             don't create a default OpenGL GUI window

下面是在各种类型的流上启动视频查看器工具的命令示例。在这些命令中,您可以用其他程序代替video viewer,因为它们解析相同的参数。在本页的源代码部分,您可以浏览视频查看器源代码的内容,以显示如何在自己的应用程序中使用videoSource和videoOutput API。

3. MIPI CSI摄像头

MIPI CSI摄像头是由Jetson的硬件CSI/ISP接口直接获取的紧凑型传感器。支持的CSI摄像机包括:

  • 用于Jetson Nano和Jetson Xavier NX的Raspberry Pi摄像头模块v2(IMX219)
  • 来自Jetson TX1/TX2 devkits的OV5693摄像头模块。
  • 有关生态系统支持的更多传感器,请参阅Jetson Partner Supported Cameras页面。

下面是一些使用MIPI CSI相机启动的示例。如果您连接了多个CSI摄像头,请将摄像头编号替换为0:

$ video-viewer csi://0                        # MIPI CSI camera 0 (substitue other camera numbers)
$ video-viewer csi://0 output.mp4             # save output stream to MP4 file (H.264 by default)
$ video-viewer csi://0 rtp://<remote-ip>:1234 # broadcast output stream over RTP to <remote-ip>

默认情况下,将创建分辨率为1280x720的CSI相机。要指定不同的分辨率,请使用–input-width 和 input-height 选项。请注意,指定的分辨率必须与相机支持的格式之一匹配。

$ video-viewer --input-width=1920 --input-height=1080 csi://0

4. V4L2摄像机

USB网络摄像头通常作为V4L2设备支持,例如Logitech C270或C920。

$ video-viewer v4l2:///dev/video0                 # /dev/video0 can be replaced with /dev/video1, ect.
$ video-viewer /dev/video0                        # dropping the v4l2:// protocol prefix is fine
$ video-viewer /dev/video0 output.mp4             # save output stream to MP4 file (H.264 by default)
$ video-viewer /dev/video0 rtp://<remote-ip>:1234 # broadcast output stream over RTP to <remote-ip>

注意:如果您插入了MIPI CSI摄像机,它也会显示为/dev/video0。然后,如果您插入一个USB网络摄像头,它将显示为/dev/video1,因此您需要在上面的命令中替换/dev/video1。在这个项目中不支持通过V4L2使用CSI摄像机,因为通过V4L2他们使用的是没有ISP的原始Bayer(相反,使用如上所示的CSI摄像机)。

4.1 V4L2格式

默认情况下,V4L2相机将使用与所需分辨率最接近的最高帧速率的相机格式创建(默认情况下,该分辨率为1280x720)。具有最高帧速率的格式可以被编码(例如用H.264或MJPEG),因为USB相机通常以较低的帧速率传输未压缩的YUV/RGB。在这种情况下,将检测到该编解码器,并且将使用Jetson的硬件解码器自动解码相机流以获得最高帧速率。

如果您显式地想要选择V4L2相机使用的格式,那么可以使用–input width、–input height和–input codec选项进行选择。可能的解码器编解码器选项有–input codec=h264、h265、vp8、vp9、mpeg2、mpeg4、mjpeg。

$ video-viewer --input-width=1920 --input-height=1080 --input-codec=h264 /dev/video0

在V4L2源上运行jetson推断程序之一时,V4L2摄像头支持的不同格式将记录到终端。但是,您也可以使用v4l2-ctl命令列出这些受支持的格式:

$ sudo apt-get install v4l-utils
$ v4l2-ctl --device=/dev/video0 --list-formats-ext

5. RTP

RTP网络流通过UDP/IP广播到特定主机或多播组。接收RTP流时,必须指定编解码器(–input codec),因为RTP无法动态查询此编解码器。这将使用RTP作为另一个设备的输入:

$ video-viewer --input-codec=h264 rtp://@:1234         # recieve on localhost port 1234
$ video-viewer --input-codec=h264 rtp://224.0.0.0:1234 # subscribe to multicast group

上面的命令指定RTP作为输入源,其中网络上的另一个远程主机正在流式传输到Jetson。但是,您也可以从Jetson输出RTP流,并将其传输到网络上的另一个远程主机。

5.1 传输RTP

要传输RTP输出流,请将目标IP/端口指定为输出URI。如果需要,可以指定比特率(默认为–bitrate=4000000或4Mbps)和/或输出编解码器(默认为–output codec=h264),可以是h264、h265、vp8、vp9、mjpeg。

$ video-viewer --bitrate=1000000 csi://0 rtp://<remote-ip>:1234         # transmit camera over RTP, encoded as H.264 @ 1Mbps 
$ video-viewer --output-codec=h265 my_video.mp4 rtp://<remote-ip>:1234  # transmit a video file over RTP, encoded as H.265

在输出RTP时,需要显式设置流发送到的远程主机(或多播组)的IP地址或主机名(如上所示为)。有关从PC查看RTP流的一些指针,请参见下文。

5.2 远程查看RTP

如果您的Jetson正在将RTP传输到另一个远程主机(如PC),下面是一些用于查看流的示例命令:

  • 使用GStreamer:
    安装GStreamer并运行此管道(用您正在使用的端口替换port=1234)
 $ gst-launch-1.0 -v udpsrc port=1234 \
 caps = "application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264, payload=(int)96" ! \
 rtph264depay ! decodebin ! videoconvert ! autovideosink
  • 使用VLC播放器:
    创建一个包含以下内容的SDP文件(.SDP)(用您正在使用的端口替换1234)
    通过双击SDP文件在VLC中打开流
    您可能希望减少VLC中的文件缓存和网络缓存设置,如下所示
  c=IN IP4 127.0.0.1
  m=video 1234 RTP/AVP 96
  a=rtpmap:96 H264/90000
  • 如果您的远程主机是另一个Jetson:
    使用与上述相同的视频查看器命令(将1234替换为您正在使用的端口)
 $ video-viewer --input-codec=h264 rtp://@:1234

6. RTSP

RTSP网络流通过UDP/IP从远程主机订阅。与RTP不同,RTSP可以动态查询流属性(如分辨率和编解码器),因此不需要显式提供这些选项。

$ video-viewer rtsp://<remote-ip>:1234 my_video.mp4      # subscribe to RTSP feed from <remote-ip>, port 1234 (and save it to file)
$ video-viewer rtsp://username:password@<remote-ip>:1234 # with authentication (replace username/password with credentials)

注意:RTSP仅支持作为输入。输出RTSP需要GStreamer对RTSP服务器的额外支持。

7. 视频文件

您可以播放和录制MP4、MKV、AVI和FLV格式的视频文件。

# playback
$ video-viewer my_video.mp4                              # display the video file
$ video-viewer my_video.mp4 rtp://<remote-ip>:1234       # transmit the video over RTP

# recording
$ video-viewer csi://0 my_video.mp4                      # record CSI camera to video file
$ video-viewer /dev/video0 my_video.mp4                  # record V4L2 camera to video file

7.1 编解码器

加载视频文件时,会自动检测编解码器和分辨率,因此不需要设置这些。保存视频文件时,默认的编解码器是H.264,但可以使用--output-codec选项进行设置。

$ video-viewer --output-codec=h265 input.mp4 output.mp4  # transcode video to H.265

支持以下编解码器:

  • Decode - H.264, H.265, VP8, VP9, MPEG-2, MPEG-4, MJPEG
  • Encode - H.264, H.265, VP8, VP9, MJPEG

7.2 调整输入大小

加载视频文件时,会自动检测分辨率。但是,如果希望将输入视频重新缩放为其他分辨率,可以指定–input-width和–input-height选项:

$ video-viewer --input-width=640 --input-height=480 my_video.mp4  # resize video to 640x480

7.3 循环输入

默认情况下,一旦到达流结束(EOS),视频将终止。但是,通过指定–loop选项,可以设置希望视频运行的循环数。–loop的可能选项有:

  • 1 = loop forever
  • 0 = don’t loop (default)
  • > 0 = set number of loops
$ video-viewer --loop=10 my_video.mp4    # loop the video 10 times
$ video-viewer --loop=-1 my_video.mp4    # loop the video forever (until user quits)

8. 图像文件

可以按以下格式加载/保存图像文件:

  • 读取: JPG, PNG, TGA, BMP, GIF, PSD, HDR, PIC, and PNM (PPM/PGM binary)
  • 保存: JPG, PNG, TGA, BMP
$ video-viewer input.jpg output.jpg	# load/save an image

您还可以循环图像和图像序列-请参阅上面的循环输入部分。

8.1 序列

如果路径是目录或包含通配符,则所有图像都将按顺序加载/保存(按字母数字顺序)。

$ video-viewer input_dir/ output_dir/   # load all images from input_dir and save them to output_dir
$ video-viewer "*.jpg" output_%i.jpg    # load all jpg images and save them to output_0.jpg, output_1.jpg, ect

注意:使用通配符时,请始终将其括在引号(“*.jpg”)中。否则,操作系统将自动扩展序列并修改命令行上参数的顺序,这可能会导致其中一个输入图像被输出覆盖。

保存一系列图像时,如果路径仅指向一个目录(output_dir),则图像将自动保存为JPG,格式为output_dir/%i.jpg,使用图像编号作为文件名(output_dir/0.jpg、output_dir/1.jpg 等)。

如果要指定文件名格式,请在路径(output_dir/image_%i.png)中使用printf样式 %i。可以应用其他printf修饰符(如%04i)来创建文件名,如output_dir/image_0001.jpg。

9. 源代码

使用videoSource和videoOutput对象访问流。它们能够通过一组统一的api来处理上述每种类型的流。可以以下列数据格式捕获和输出图像:

Format stringimageFormat enumData TypeBit Depth
rgb8IMAGE_RGB8uchar324
rgba8IMAGE_RGBA8uchar432
rgb32fIMAGE_RGB32Ffloat396
rgba32fIMAGE_RGBA32Ffloat4128
  • 数据类型和 imageFormat 是C++类型
  • 在Python中,格式字符串可以传递给视频源。捕获()请求特定格式(默认为 rgb8)
  • 在C++中,videoSource::Capture() 模板将从输出指针的数据类型推断格式。

要将图像转换为/从不同格式转换,请参阅“使用CUDA进行图像处理”页面以获取更多信息。

下面是video-viewer.py和video-viewer.cpp,略略缩略,以提高可读性:

9.1 Python 源代码

import jetson.utils
import argparse
import sys

# parse command line
parser = argparse.ArgumentParser()
parser.add_argument("input_URI", type=str, help="URI of the input stream")
parser.add_argument("output_URI", type=str, default="", nargs='?', help="URI of the output stream")
opt = parser.parse_known_args()[0]

# create video sources & outputs
input = jetson.utils.videoSource(opt.input_URI, argv=sys.argv)
output = jetson.utils.videoOutput(opt.output_URI, argv=sys.argv)

# capture frames until user exits
while output.IsStreaming():
	image = input.Capture(format='rgb8')  // can also be format='rgba8', 'rgb32f', 'rgba32f'
	output.Render(image)
	output.SetStatus("Video Viewer | {:d}x{:d} | {:.1f} FPS".format(image.width, image.height, output.GetFrameRate()))

9.2 C++ 源代码

#include "videoSource.h"
#include "videoOutput.h"

int main( int argc, char** argv )
{
	// create input/output streams
	videoSource* inputStream = videoSource::Create(argc, argv, ARG_POSITION(0));
	videoOutput* outputStream = videoOutput::Create(argc, argv, ARG_POSITION(1));
	
	if( !inputStream )
		return 0;

	// capture/display loop
	while( true )
	{
		uchar3* nextFrame = NULL;  // can be uchar3, uchar4, float3, float4

		if( !inputStream->Capture(&nextFrame, 1000) )
			continue;

		if( outputStream != NULL )
		{
			outputStream->Render(nextFrame, inputStream->GetWidth(), inputStream->GetHeight());

			// update status bar
			char str[256];
			sprintf(str, "Video Viewer (%ux%u) | %.1f FPS", inputStream->GetWidth(), inputStream->GetHeight(), outputStream->GetFrameRate());
			outputStream->SetStatus(str);	

			// check if the user quit
			if( !outputStream->IsStreaming() )
				break;
		}

		if( !inputStream->IsStreaming() )
			break;
	}

	// destroy resources
	SAFE_DELETE(inputStream);
	SAFE_DELETE(outputStream);
}

注意,C++ 代码编译时,需要自己编写 CMakeLists.txt 文件。该文件编写模板,参见:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许野平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值