我过去一周一直尝试使用x264作为编码器和libavformat打包和发送流来实现H.264 RTP流。问题是,据我所知它似乎没有正常工作。
目前我只是对随机数据(x264_picture_alloc)进行编码并从libx264中提取NAL帧。这相当简单:
这是出错的地方。上面的
现在,当我想要流出NAL时,我使用以下代码:
在这个阶段,我可以看到RTP数据出现在网络上,并且它看起来是我发送的帧,甚至包括来自x264的版权小块。但是,我使用的任何播放器都不能理解这些数据。VLC退出并要求SDP描述,显然不是必需的。
然后我尝试通过
错误:元素/GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0:没有协商RTP格式。附加调试信息:gstbasertpdepayload.c(372):gst_base_rtp_depayload_chain():/GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0:输入缓冲区需要在其上设置RTP caps。这通常是通过设置上游源元素(通常为udpsrc或appsrc)的“caps”属性,或者在解码器之前放置一个capsfilter元素并在其上设置“caps”属性来实现的。另请参见http://cgit.freedesktop.org/gstreamer/gst-plugins-good/tree/gst/rtp/README。
目前我只是对随机数据(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中的帧数据。使用libavformat来流式传输数据,首先需要在一个Streamer类中进行初始化:
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);
}
这是出错的地方。上面的
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的版权小块。但是,我使用的任何播放器都不能理解这些数据。VLC退出并要求SDP描述,显然不是必需的。
然后我尝试通过
gst-launch
播放它:
gst-launch udpsrc port=49990 ! rtph264depay ! decodebin ! xvimagesink
这将等待UDP数据,但是当接收到时,我会得到:错误:元素/GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0:没有协商RTP格式。附加调试信息:gstbasertpdepayload.c(372):gst_base_rtp_depayload_chain():/GstPipeline:pipeline0/GstRtpH264Depay:rtph264depay0:输入缓冲区需要在其上设置RTP caps。这通常是通过设置上游源元素(通常为udpsrc或appsrc)的“caps”属性,或者在解码器之前放置一个capsfilter元素并在其上设置“caps”属性来实现的。另请参见http://cgit.freedesktop.org/gstreamer/gst-plugins-good/tree/gst/rtp/README。
由于我没有使用GStreamer本身进行流媒体传输,我不太确定它所说的RTP caps是什么意思。但是,它让我想知道是否没有发送足够的信息通过RTP来描述流。我对视频还很陌生,感觉自己在这里错过了一些重要的东西。有什么提示吗?
gst-launch udpsrc port=49990 ! application/x-rtp,clock-rate=90000,payload=96 ! rtph264depay ! decodebin ! xvimagesink
。 - Jacob Peddicord