我们使用gstreamer-rtsp-server ,实现了USB相机转RTSP网络流的基本功能,但是效果很一般,接下来我们需要做优化,以提升其整体性能。
我自己的测试硬件环境在文章《【GStreamer 】USB相机 Jeston TX1平台测试环境说明_机器人虎哥的博客-CSDN博客》有说明,请必要的时候参考。
1、test-launch 测试,使用videotestsrc 为视频源
一直以来我们测试的命令为:使用videotestsrc 做为源,可以同时并发链接多路
./test-launch "( videotestsrc ! x264enc ! rtph264pay name=pay0 pt=96 )"
2、test-launch 测试,使用v4l2src device=/dev/video0 为视频源
./test-launch --gst-debug-level=3 "( v4l2src device=/dev/video0 ! videoconvert! videoscale ! video/x-raw, width=640, height=480, framerate=25/1 ! queue ! x264enc bitrate=2048 ! rtph264pay name=pay0 pt=96 )"
发现使用USB相机做为视频源,链接第二路就会出现报错。
错误信息为:
Call to S_FMT failed for YUYV @ 640x480: Device or resource busy)
0:00:45.254816029 21038 0x55b0381590 WARN rtspmedia rtsp-media.c:2573:wait_preroll: failed to preroll pipeline
0:00:45.254838113 21038 0x55b0381590 WARN rtspmedia rtsp-media.c:2877:gst_rtsp_media_prepare: failed to preroll pipeline
0:00:45.259095810 21038 0x55b0381590 ERROR rtspclient rtsp-client.c:767:find_media: client 0x55b0566d60: can't prepare media
0:00:45.259610808 21038 0x55b0381590 ERROR rtspclient rtsp-client.c:2041:handle_setup_request: client 0x55b0566d60: media '/test' not found
所以,我们其实可以理解为,这个测试代码,每次一个链接,都会从视频源开始到最后的封装,全部都重新建立一次,1中我们使用了videotestsrc 做为测试源,支持复用,2中我们的USB设备,就不支持复用了,所以报了Device or resource busy 的信息。
3、修改代码,USB相机源时支持多路复用转发
上面我们测试,发现USB源测试时,不支持并发复用,无法支持多个客户端链接,如何修改,可以实现呢?仔细阅读官方给的示例代码后发现有这样的描述:
默认情况下,工厂将为每个客户机创建一个新管道。如果要在客户端之间共享管道,请使用 `gst_rtsp_media_factory_set_shared()`。
仔细阅读test-readme.c 代码:
/* GStreamer
*/
#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
int
main (int argc, char *argv[])
{
GMainLoop *loop;
GstRTSPServer *server;
GstRTSPMountPoints *mounts;
GstRTSPMediaFactory *factory;
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* create a server instance */
server = gst_rtsp_server_new ();
/* get the mount points for this server, every server has a default object
* that be used to map uri mount points to media factories */
mounts = gst_rtsp_server_get_mount_points (server);
/* make a media factory for a test stream. The default media factory can use
* gst-launch syntax to create pipelines.
* any launch line works as long as it contains elements named pay%d. Each
* element with pay%d names will be a stream */
factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_launch (factory,
"( videotestsrc is-live=1 ! x264enc ! rtph264pay name=pay0 pt=96 )");
gst_rtsp_media_factory_set_shared (factory, TRUE);
/* attach the test factory to the /test url */
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
/* don't need the ref to the mapper anymore */
g_object_unref (mounts);
/* attach the server to the default maincontext */
gst_rtsp_server_attach (server, NULL);
/* start serving */
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
g_main_loop_run (loop);
return 0;
}
然后再阅读test-launch.c 代码:
/* GStreamer
*/
#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
#define DEFAULT_RTSP_PORT "8554"
static char *port = (char *) DEFAULT_RTSP_PORT;
static GOptionEntry entries[] = {
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,
"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
{NULL}
};
int
main (int argc, char *argv[])
{
GMainLoop *loop;
GstRTSPServer *server;
GstRTSPMountPoints *mounts;
GstRTSPMediaFactory *factory;
GOptionContext *optctx;
GError *error = NULL;
optctx = g_option_context_new ("<launch line> - Test RTSP Server, Launch\n\n"
"Example: \"( videotestsrc ! x264enc ! rtph264pay name=pay0 pt=96 )\"");
g_option_context_add_main_entries (optctx, entries, NULL);
g_option_context_add_group (optctx, gst_init_get_option_group ());
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
g_printerr ("Error parsing options: %s\n", error->message);
g_option_context_free (optctx);
g_clear_error (&error);
return -1;
}
g_option_context_free (optctx);
loop = g_main_loop_new (NULL, FALSE);
/* create a server instance */
server = gst_rtsp_server_new ();
g_object_set (server, "service", port, NULL);
/* get the mount points for this server, every server has a default object
* that be used to map uri mount points to media factories */
mounts = gst_rtsp_server_get_mount_points (server);
/* make a media factory for a test stream. The default media factory can use
* gst-launch syntax to create pipelines.
* any launch line works as long as it contains elements named pay%d. Each
* element with pay%d names will be a stream */
factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_launch (factory, argv[1]);
gst_rtsp_media_factory_set_shared (factory, TRUE);
/* attach the test factory to the /test url */
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
/* don't need the ref to the mapper anymore */
g_object_unref (mounts);
/* attach the server to the default maincontext */
gst_rtsp_server_attach (server, NULL);
/* start serving */
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", port);
g_main_loop_run (loop);
return 0;
}
发现差别和描述是一致的:
/* test-readme.c */
factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_launch (factory,
"( videotestsrc is-live=1 ! x264enc ! rtph264pay name=pay0 pt=96 )");
gst_rtsp_media_factory_set_shared (factory, TRUE);
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
/* test-launch.c */
factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_launch (factory, argv[1]);
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
发现这个区别和描述,我们再看看这个描述
默认情况下,工厂将为每个客户机创建一个新管道。如果要在客户端之间共享管道,请使用 `gst_rtsp_media_factory_set_shared()`。
我们修改test-launch.c 代码,重新命名一个新文件:test-launch2.c 修改后的代码:
/* GStreamer
*/
#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
#define DEFAULT_RTSP_PORT "8554"
static char *port = (char *) DEFAULT_RTSP_PORT;
static GOptionEntry entries[] = {
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,
"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
{NULL}
};
int
main (int argc, char *argv[])
{
GMainLoop *loop;
GstRTSPServer *server;
GstRTSPMountPoints *mounts;
GstRTSPMediaFactory *factory;
GOptionContext *optctx;
GError *error = NULL;
optctx = g_option_context_new ("<launch line> - Test RTSP Server, Launch\n\n"
"Example: \"( videotestsrc ! x264enc ! rtph264pay name=pay0 pt=96 )\"");
g_option_context_add_main_entries (optctx, entries, NULL);
g_option_context_add_group (optctx, gst_init_get_option_group ());
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
g_printerr ("Error parsing options: %s\n", error->message);
g_option_context_free (optctx);
g_clear_error (&error);
return -1;
}
g_option_context_free (optctx);
loop = g_main_loop_new (NULL, FALSE);
/* create a server instance */
server = gst_rtsp_server_new ();
g_object_set (server, "service", port, NULL);
/* get the mount points for this server, every server has a default object
* that be used to map uri mount points to media factories */
mounts = gst_rtsp_server_get_mount_points (server);
/* make a media factory for a test stream. The default media factory can use
* gst-launch syntax to create pipelines.
* any launch line works as long as it contains elements named pay%d. Each
* element with pay%d names will be a stream */
factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_launch (factory, argv[1]);
//新增加 客户端之间共享管道 不用重新建立
gst_rtsp_media_factory_set_shared (factory, TRUE);
/* attach the test factory to the /test url */
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
/* don't need the ref to the mapper anymore */
g_object_unref (mounts);
/* attach the server to the default maincontext */
gst_rtsp_server_attach (server, NULL);
/* start serving */
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", port);
g_main_loop_run (loop);
return 0;
}
编译:
gcc test-launch2.c -o test-launch2 $(pkg-config --cflags --libs gstreamer-rtsp-server-1.0 gstreamer-1.0)
测试:
./test-launch2 --gst-debug-level=3 "( v4l2src device=/dev/video0 ! videoconvert! videoscale ! video/x-raw, width=640, height=480, framerate=25/1 ! queue ! x264enc bitrate=2048 ! rtph264pay name=pay0 pt=96 )"
这样修改就实现了一个可以多路复用的USB转RTSP服务器
4、测试硬件加速的效果
./test-launch2 --gst-debug-level=3 "( v4l2src device=/dev/video0 ! video/x-raw,format=YUY2,width=640, height=480, framerate=25/1 ! nvvidconv ! video/x-raw(memory:NVMM), format=(string)I420, width=640, height=480, framerate=25/1 ! queue ! omxh264enc bitrate=2048 ! rtph264pay name=pay0 pt=96 )"
虽然感觉图像质量变差了,但是资源占用真心有很大优化。而且延时也有一定的优化。