在過去的一週裏,我一直在嘗試通過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