2012-04-13 119 views
10

在过去的一周里,我一直在尝试通过RTP实现H.264流媒体,使用x264作为编码器并使用libavformat打包并发送流。问题是,据我所知它不能正常工作。使用libavformat流式传输H.264 over RTP

现在我只是编码随机数据(x264_picture_alloc)并从libx264中提取NAL帧。这是相当简单:

x264_picture_t pic_out; 
x264_nal_t* nals; 
int num_nals; 
int frame_size = x264_encoder_encode(this->encoder, &nals, &num_nals, this->pic_in, &pic_out); 

if (frame_size <= 0) 
{ 
    return frame_size; 
} 

// push NALs into the queue 
for (int i = 0; i < num_nals; i++) 
{ 
    // create a NAL storage unit 
    NAL nal; 
    nal.size = nals[i].i_payload; 
    nal.payload = new uint8_t[nal.size]; 
    memcpy(nal.payload, nals[i].p_payload, nal.size); 

    // push the storage into the NAL queue 
    { 
     // lock and push the NAL to the queue 
     boost::mutex::scoped_lock lock(this->nal_lock); 
     this->nal_queue.push(nal); 
    } 
} 

nal_queue用于安全地传送帧到流光类,那么将发送帧了。现在它不是线程化的,因为我只是试着去尝试这个工作。在编码单个帧之前,我已经确定初始化编码器。

但我不认为x264是问题,因为我可以在返回的NAL中看到帧数据。

Streamer::Streamer(Encoder* encoder, string rtp_address, int rtp_port, int width, int height, int fps, int bitrate) 
{ 
    this->encoder = encoder; 

    // initalize the AV context 
    this->ctx = avformat_alloc_context(); 
    if (!this->ctx) 
    { 
     throw runtime_error("Couldn't initalize AVFormat output context"); 
    } 

    // get the output format 
    this->fmt = av_guess_format("rtp", NULL, NULL); 
    if (!this->fmt) 
    { 
     throw runtime_error("Unsuitable output format"); 
    } 
    this->ctx->oformat = this->fmt; 

    // try to open the RTP stream 
    snprintf(this->ctx->filename, sizeof(this->ctx->filename), "rtp://%s:%d", rtp_address.c_str(), rtp_port); 
    if (url_fopen(&(this->ctx->pb), this->ctx->filename, URL_WRONLY) < 0) 
    { 
     throw runtime_error("Couldn't open RTP output stream"); 
    } 

    // add an H.264 stream 
    this->stream = av_new_stream(this->ctx, 1); 
    if (!this->stream) 
    { 
     throw runtime_error("Couldn't allocate H.264 stream"); 
    } 

    // initalize codec 
    AVCodecContext* c = this->stream->codec; 
    c->codec_id = CODEC_ID_H264; 
    c->codec_type = AVMEDIA_TYPE_VIDEO; 
    c->bit_rate = bitrate; 
    c->width = width; 
    c->height = height; 
    c->time_base.den = fps; 
    c->time_base.num = 1; 

    // write the header 
    av_write_header(this->ctx); 
} 

在这里,事情似乎去错了: 流数据使用libavformat,它首先在流光类初始化完成。上面的av_write_header似乎绝对没有什么;我用wireshark来验证这一点。作为参考,我使用Streamer streamer(&enc, "10.89.6.3", 49990, 800, 600, 30, 40000);初始化Streamer实例,其中enc是以前用于处理x264的Encoder对象的引用。

现在,当我要流出来NAL,我用这个:

// grab a NAL 
NAL nal = this->encoder->nal_pop(); 
cout << "NAL popped with size " << nal.size << endl; 

// initalize a packet 
AVPacket p; 
av_init_packet(&p); 
p.data = nal.payload; 
p.size = nal.size; 
p.stream_index = this->stream->index; 

// send it out 
av_write_frame(this->ctx, &p); 

在这一点上,我可以看到的RTP数据出现在网络上,它看起来像我一直在发送帧甚至包括来自x264的小版权blob。 但是,我用过的任何播放器都无法识别数据。 VLC退出想要SDP描述,其中apparently isn't required

然后我试图通过gst-launch发挥它:

gst-launch udpsrc port=49990 ! rtph264depay ! decodebin ! xvimagesink

这会坐下等待UDP数据,但在收到时,我得到:

ERROR: element /GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0: No RTP format was negotiated. Additional debug info: gstbasertpdepayload.c(372): gst_base_rtp_depayload_chain(): /GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0: Input buffers need to have RTP caps set on them. This is usually achieved by setting the 'caps' property of the upstream source element (often udpsrc or appsrc), or by putting a capsfilter element before the depayloader and setting the 'caps' property on that. Also see http://cgit.freedesktop.org/gstreamer/gst-plugins-good/tree/gst/rtp/README

正如我不使用GStreamer进行流式传输,我不太清楚RTP上限是什么意思。但是,这让我怀疑我是否没有通过RTP发送足够的信息来描述这个流。我对视频非常陌生,我觉得我在这里错过了一些关键的东西。任何提示?

回答

4

h264是一种编码标准。它规定了视频数据如何压缩并以一种可以在稍后点解压缩为视频流的格式进行存储。

RTP是一种传输协议。它规定了可以携带由任意编码器编码的音频 - 视频数据的分组的格式和顺序。

GStreamer预计会收到符合RTP协议的数据。你期望libaformat能够产生GStreamer保证可立即读取的RTP数据包吗?也许GStreamers期望一个额外的流描述,使其能够使用适当的解码器接受和解码流数据包?也许它需要额外的RTSP交换或SDP流描述符文件?

错误消息非常清楚地表明RTP格式尚未协商。 caps是短期的功能。接收机需要知道发射机的功能才能正确设置接收器/解码器。

我强烈建议至少为你的RTP流创建一个SDP文件。 libavformat should be able to do it for you

+0

就是这样 - 我不知道,我无法找到所需的信息。据我所知,libavformat会为你装入一个RTP流(并且不会发送无效数据包 - 我试过了)。它不会进行任何RTSP协商;最终这将指向冯或其他外部应用程序来处理RTSP流式传输给客户端。但是,这并不能解释为什么没有任何东西可以使libavformat生成的RTP流的正面或反面。 – 2012-04-13 18:44:15

+0

你需要以某种方式进行谈判。为什么不尝试为你的流创建一个SDP文件? – 2012-04-13 18:55:03

+0

我给了它一个去,我可以让VLC显示一个绿色的屏幕 - 不管这是否正确我不知道,但这是一个开始。今天将继续努力,所以我们会看看这是否是真正的问题。 – 2012-04-16 13:28:38