2012-04-13 136 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