从RTP数据包重建图像

时间:2014-05-26 06:38:11

标签: opencv udp webrtc janus-gateway

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

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

在服务器端,我有 janus_incomming_rtp 函数,它给我一个 char * buffer int length 。在检查时,接收到的数据缓冲区大约是MTU的长度:我的视频的每一帧都是在几个RTP数据包上发送的。

我已按照this wikipedia page检查了一下标题,但我不知道如何从该UDP RTP数据包流中重建图像。理想情况下,我想将流传递给openCV进行实时图像处理。

我听说过gstreamer,但我不知道它是什么,也不知道它对我有什么帮助;此外,我不知道openCV是否有任何内置功能来重建"图像 ?而且我不知道视频帧的编码格式: PT (有效载荷类型)似乎是116,定义为"动态"但我不知道这意味着什么。

任何帮助?

3 个答案:

答案 0 :(得分:2)

以下是处理SRTP数据包以解码它们的一些指导步骤。

  1. 确保rtp和RTCP未进行多路复用,您可以remove that option from the SDP
  2. 将SRTP数据包解密为原始RTP,您将需要访问密钥交换(不确定您是否已经这样做但所有媒体都已加密并且使用DTLS进行密钥交换,并且必须在处理之前解密)
  3. 抓取您的媒体有效负载类型并将其与来自SDP的媒体进行匹配(您可以从SDP中的RTPMAP看到哪些媒体是什么有效负载)
  4. 从数据包中删除RTP Payload(Gstreamer具有针对大多数常见有效负载的RtpDepay插件,包括VP8)并对流进行解码。使用vp8
  5. Quick example命令行管道
  6. 现在您有一个可以显示的原始视频/音频数据包。
  7. SDP:

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

    SRTP:

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

    的GStreamer:

    • 你可能想看一下 GstAppSrc 它允许您将数组char到gstreamer管道中 解码,您可以将其推送到另一个udp端口以获取它 OpenCV的。
    • 以下是example code 从我写的websocket服务器,它将抓住原始媒体并推动它 到管道。这个例子并不完全是你想要做的(它 不抓取RTP而是从网页获取原始媒体帧) 但它会告诉你如何使用AppSrc。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

我最终通过遵循包括@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管道(在我的情况下,我需要降低帧速率,因此视频速率/封顶;您可能不需要这些) :

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;
}