在过去的一周里,我一直在尝试通过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发送足够的信息来描述这个流。我对视频非常陌生,我觉得我在这里错过了一些关键的东西。任何提示?
就是这样 - 我不知道,我无法找到所需的信息。据我所知,libavformat会为你装入一个RTP流(并且不会发送无效数据包 - 我试过了)。它不会进行任何RTSP协商;最终这将指向冯或其他外部应用程序来处理RTSP流式传输给客户端。但是,这并不能解释为什么没有任何东西可以使libavformat生成的RTP流的正面或反面。 – 2012-04-13 18:44:15
你需要以某种方式进行谈判。为什么不尝试为你的流创建一个SDP文件? – 2012-04-13 18:55:03
我给了它一个去,我可以让VLC显示一个绿色的屏幕 - 不管这是否正确我不知道,但这是一个开始。今天将继续努力,所以我们会看看这是否是真正的问题。 – 2012-04-16 13:28:38