3

我正在尝试通过网络将用户的网络摄像头流式传输到基于 C 的服务器。我用过Janus 网关

我创建了一个很大程度上基于 echotest 演示示例的小插件:我的浏览器通过 WebRTC 技术连接到我的 janus 服务器,并让它流式传输用户的网络摄像头。

在服务器端,我有janus_incomming_rtp函数,它给了我一个char * bufferint length。经过检查,传入的数据缓冲区大约是 MTU 的长度:我的视频的每一帧都是通过几个 RTP 数据包发送的。

我已经按照这个维基百科页面检查了一些标题,但我不知道如何从 UDP RTP 数据包流中重建图像。理想情况下,我想将流传递给 openCV 以进行实时图像处理。

我听说过 gstreamer,但我不明白它是什么,也不知道它对我有什么帮助;除了我不知道openCV是否有任何内置函数来“重建”图像?而且我不知道视频帧以哪种格式编码:PT(有效负载类型)似乎是 116,它被定义为“动态”,但我不知道它是什么意思。

有什么帮助吗?

4

3 回答 3

4

以下是处理 SRTP 数据包以对其进行解码的一些指导步骤。

  1. 确保 rtp 和 RTCP 没有复用,您可以从 SDP 中删除该选项
  2. 将 SRTP 数据包解密为原始 RTP,您将需要访问密钥交换(不确定您是否已经这样做,但所有媒体均已加密,并且使用 DTLS 交换密钥,并且必须在处理前解密)
  3. 获取您的媒体有效负载类型并将其与来自 SDP 的媒体进行匹配(您可以从 SDP 中的 RTPMAP 看到什么媒体是什么有效负载)
  4. 从数据包中删除 RTP 有效负载(Gstreamer 具有适用于大多数常见有效负载的 RtpDepay 插件,包括 VP8)并解码流。使用 vp8 的命令行管道的快速示例
  5. 现在您有了一个可以显示的原始视频/音频数据包。

发展规划署:

  • 如果 RTCP 和 RTP 被多路复用,您将看到该行 a=rtcp-mux,您将看到端口输入a=rtcp:50111 IN IP4 <address>和候选媒体端口将相同。
  • 如果媒体本身正在被多路复用,您将看到a=group:BUNDLE audio video

SRTP:

  • Janus 已经处理了 DTLS 交换,它似乎在发送 rtp 之前已经对其进行了解密,但它看起来不像是多路复用的 rtp/rtcp 和媒体。
  • 是一个快速而肮脏的 SRTP 解密器,当您将在 DTLS 中交换的 MasterKey 传递给它时,它就可以工作。

GStreamer:

  • 您可能想查看 GstAppSrc ,它允许您将数组字符化到 gstreamer 管道中进行解码,您可以将其推送到另一个 udp 端口​​以使用 OpenCV 抓取它。
  • 这是我编写的 websocket 服务器的一些示例代码 ,它将获取原始媒体并将其推送到管道。这个例子并不完全是你想要做的(它不抓取 RTP,而是从网页中抓取原始媒体帧),但它会向你展示如何使用 AppSrc。
于 2014-05-26T14:52:33.687 回答
1

通过遵循此线程中其他人的建议,包括@nschoe(OP)和@Benjamin Trent,我最终使用Janus和GStreamer(1.9)完成了这项工作。我想我会包含我的代码,以便让下一个出现的人的生活更轻松,因为我涉及到如此多的反复试验:

首先构建/安装 GStreamer 及其所有需要的插件(对于我的设置,我需要确保两个插件目录位于 GST_PLUGIN_SYSTEM_PATH 环境变量中)。现在在 Janus 插件初始化(init()回调)时初始化 GStreamer:

gst_init(NULL, NULL);

对于每个 WebRTC 会话,您需要保留一些 GStreamer 句柄,因此将以下内容添加到您的 Janus 插件会话结构中:

GstElement *pipeline, *appsrc, *multifilesink;

创建 Janus 插件会话(create_session()回调)时,为该会话设置 GStreamer 管道(在我的情况下,我需要降低帧速率,因此需要降低 videorate/capsrate;您可能不需要这些):

GstElement *conv, *vp8depay, *vp8dec, *videorate, *capsrate, *pngenc;

session->pipeline = gst_pipeline_new("pipeline");

session->appsrc         = gst_element_factory_make("appsrc", "source");
vp8depay                = gst_element_factory_make("rtpvp8depay", NULL);
vp8dec                  = gst_element_factory_make("vp8dec", NULL);
videorate               = gst_element_factory_make("videorate", NULL);
capsrate                = gst_element_factory_make("capsfilter", NULL);
conv                    = gst_element_factory_make("videoconvert", "conv");
pngenc                  = gst_element_factory_make("pngenc", NULL);
session->multifilesink  = gst_element_factory_make("multifilesink", NULL);

GstCaps* capsRate = gst_caps_new_simple("video/x-raw", "framerate", GST_TYPE_FRACTION, 15, 1, NULL);
g_object_set(capsrate, "caps", capsRate, NULL);
gst_caps_unref(capsRate);

GstCaps* caps = gst_caps_new_simple ("application/x-rtp",
                 "media", G_TYPE_STRING, "video",
                 "encoding-name", G_TYPE_STRING, "VP8-DRAFT-IETF-01",
                 "payload", G_TYPE_INT, 96,
                 "clock-rate", G_TYPE_INT, 90000,
                 NULL);
g_object_set(G_OBJECT (session->appsrc), "caps", caps, NULL);
gst_caps_unref(caps);

gst_bin_add_many(GST_BIN(session->pipeline), session->appsrc, vp8depay, vp8dec, conv, videorate, capsrate, pngenc, session->multifilesink, NULL);
gst_element_link_many(session->appsrc, vp8depay, vp8dec, conv, videorate, capsrate, pngenc, session->multifilesink, NULL);

// Setup appsrc
g_object_set(G_OBJECT (session->appsrc), "stream-type", 0, NULL);
g_object_set(G_OBJECT (session->appsrc), "format", GST_FORMAT_TIME, NULL);
g_object_set(G_OBJECT (session->appsrc), "is-live", TRUE, NULL);
g_object_set(G_OBJECT (session->appsrc), "do-timestamp", TRUE, NULL);

g_object_set(session->multifilesink, "location", "/blah/some/dir/output-%d.png", NULL);
gst_element_set_state(session->pipeline, GST_STATE_PLAYING);

当传入的 RTP 数据包被 Janus 解复用并准备好读取时,(incoming_rtp()回调),将其输入 GStreamer 管道:

if(video && session->video_active) {
    // Send to GStreamer
    guchar* temp = NULL;
    temp = (guchar*)malloc(len);
    memcpy(temp, buf, len);

    GstBuffer*  buffer = gst_buffer_new_wrapped_full(0, temp, len, 0, len, temp, g_free);
    gst_app_src_push_buffer(GST_APP_SRC(session->appsrc), buffer);
}

最后,当 Janus 插件会话结束时(destroy_session()回调),一定要释放 GStreamer 资源:

if(session->pipeline) {
    gst_element_set_state(session->pipeline, GST_STATE_NULL);
    gst_object_unref(session->pipeline);
    session->pipeline = NULL;
}
于 2016-07-29T14:24:13.063 回答
0

我们对 WebRTC 的流式传输也有同样的担忧。我所做的是将视频帧发送到 WebSocket 服务器,从那里我使用 imdecode() 解码图像缓冲区。

我在这里有一个现场演示twistedcv并且还在 github 中托管了源代码twistedcv。但是流媒体不是实时的。

于 2014-05-29T09:27:48.890 回答