2017-02-25 52 views
1

嗨,我需要一點幫助/指導,因爲我陷入了我的研究。在libav中讀取dumepd RTP流

問題:

如何使用任何的GStreamer或任一API(通過編程)或控制檯版本avlib(ffmpeg)來轉換成RTP數據。

數據

我有一個來自RTP/RTCP通過TCP這樣我就可以得到準確的開始和停止在文件中的每個RTP包RTP轉儲。這是一個H264視頻流轉儲。 的數據是用這種方式,因爲我需要獲得通過的libcurl的RTCP/RTP交錯流(這我目前正在做的)

狀態

我試着使用的ffmpeg消耗純RTP數據包,但似乎是通過控制檯或編程使用rtp涉及到「啓動」ffmpeg中的整個rtsp/rtp會話業務。我已經停在那裏,暫時我沒有更深入地追求這條道路。我想這是可能的情人級RTP API像ff_rtp_parse_packet()我太新,用這個庫直接做。

然後是gstreamer它有更多的功能可以在沒有編程的情況下完成,但暫時我無法弄清楚如何將它傳遞給我的RTP轉儲。

我也試圖做一個掛羊頭賣狗肉的一點點,並通過socat/NC流轉儲到UDP端口,並通過與SDP文件作爲輸入ffplay它聽,似乎有一些進步rtp至少會被識別出來,但是對於socat,有大量數據包丟失(數據發送速度可能太快?),最後數據不可視化。當我使用nc視頻嚴重畸形,但至少沒有那麼多的接收錯誤。

這種或那種方式的數據沒有正確的可視化。

我知道我可以將數據「手工」拆包,但想法是通過某種類型的庫來實現,因爲最終還會有第二個音頻流,必須與視頻混合在一起。

我將不勝感激關於如何解決這個問題的任何幫助。 謝謝。

回答

2

終於在一段時間後,我有時間再次坐下來解決這個問題,最後我得到了滿足我的解決方案。我繼續使用RTP交織流(RTP通過單個TCP連接與RTCP交織)。
所以我有一個交錯的RTCP/RTP流,需要反彙編到音頻(PCM A-Law)和視頻(h.264約束基線)RTP數據包。
這裏描述了包含RTP數據的RTSP流的分解rfc2326
H264的拆包在此處描述爲rfc6184,對於PCM A-Law,幀在RTP中是原始音頻,因此不需要拆分。

下一步是計算每個流的適當的PTS(或表示時間戳),這有點麻煩,但最後Live555代碼來幫助 (見RTP lipsync synchronization)。
最後一項任務是將它複合到一個支持PCM alaw的容器中,我使用了ffmpeg的avlibraries。在互聯網上有很多例子,但其中很多都是過時的(ffmpeg在API變更區域非常「動態」),所以我發佈了最重要的部分:

的設置部分:

#include <libavcodec/avcodec.h> 
#include <libavformat/avformat.h> 
#include "libavutil/intreadwrite.h" 
#include "libavutil/mathematics.h" 

AVFormatContext *formatContext; 
AVOutputFormat *outputFormat; 
AVStream   *video_st; 
AVStream   *audio_st; 
AVCodec   *av_encode_codec = NULL; 
AVCodec   *av_audio_encode_codec = NULL; 
AVCodecContext *av_video_encode_codec_ctx = NULL; 
AVCodecContext *av_audio_encode_codec_ctx = NULL; 


av_register_all(); 
av_log_set_level(AV_LOG_TRACE); 
outputFormat = av_guess_format(NULL, pu8outFileName, NULL); 
outputFormat->video_codec = AV_CODEC_ID_H264; 

av_encode_codec = avcodec_find_encoder(AV_CODEC_ID_H264); 
av_audio_encode_codec = avcodec_find_encoder(AV_CODEC_ID_PCM_ALAW); 
avformat_alloc_output_context2(&formatContext, NULL, NULL, pu8outFileName); 
formatContext->oformat = outputFormat; 
strcpy(formatContext->filename, pu8outFileName); 
outputFormat->audio_codec = AV_CODEC_ID_PCM_ALAW; 

av_video_encode_codec_ctx = avcodec_alloc_context3(av_encode_codec); 
av_audio_encode_codec_ctx = avcodec_alloc_context3(av_audio_encode_codec); 

av_video_encode_codec_ctx->codec_id = outputFormat->video_codec; 
av_video_encode_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; 
av_video_encode_codec_ctx->bit_rate = 4000; 
av_video_encode_codec_ctx->width = u32width; 
av_video_encode_codec_ctx->height = u32height; 
av_video_encode_codec_ctx->time_base = (AVRational){ 1, u8fps }; 
av_video_encode_codec_ctx->max_b_frames = 0; 
av_video_encode_codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; 

av_audio_encode_codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16; 
av_audio_encode_codec_ctx->codec_id = AV_CODEC_ID_PCM_ALAW; 
av_audio_encode_codec_ctx->codec_type = AVMEDIA_TYPE_AUDIO; 
av_audio_encode_codec_ctx->sample_rate = 8000; 
av_audio_encode_codec_ctx->channels = 1; 
av_audio_encode_codec_ctx->time_base = (AVRational){ 1, u8fps }; 
av_audio_encode_codec_ctx->channel_layout = AV_CH_LAYOUT_MONO; 

video_st = avformat_new_stream(formatContext, av_encode_codec); 
audio_st = avformat_new_stream(formatContext, av_audio_encode_codec); 
audio_st->index = 1; 
video_st->avg_frame_rate = (AVRational){ 90000, 90000/u8fps }; 
av_stream_set_r_frame_rate(video_st, (AVRational){ 90000, 90000/u8fps }); 

視頻數據包被寫成這樣:

uint8_t *pu8framePtr = video_frame; 
AVPacket pkt = { 0 }; 
av_init_packet(&pkt); 
if (0x65 == pu8framePtr[4] || 0x67 == pu8framePtr[4] || 0x68 == pu8framePtr[4]) 
{ 
    pkt.flags = AV_PKT_FLAG_KEY; 
} 

pkt.data = (uint8_t *)pu8framePtr; 
pkt.size = u32LastFrameSize; 

pkt.pts = av_rescale_q(s_video_sync.fSyncTime.tv_sec * 1000000 + s_video_sync.fSyncTime.tv_usec, (AVRational){ 1, 1000000 }, video_st->time_base); 
pkt.dts = pkt.pts; 
pkt.stream_index = video_st->index; 
av_interleaved_write_frame(formatContext, &pkt); 
av_packet_unref(&pkt); 

和音頻像這樣:

AVPacket pkt = { 0 }; 
av_init_packet(&pkt); 
pkt.flags = AV_PKT_FLAG_KEY; 
pkt.data = (uint8_t *)pu8framePtr; 
pkt.size = u32AudioDataLen; 

pkt.pts = av_rescale_q(s_audio_sync.fSyncTime.tv_sec * 1000000 + s_audio_sync.fSyncTime.tv_usec, (AVRational){ 1, 1000000 }, audio_st->time_base); 
pkt.dts = pkt.pts; 
pkt.stream_index = audio_st->index; 
if (u8FirstIFrameFound) {av_interleaved_write_frame(formatContext, &pkt);} 
av_packet_unref(&pkt) 

,並在年底一些deinits:

av_write_trailer(formatContext); 
av_dump_format(formatContext, 0, pu8outFileName, 1); 
avcodec_free_context(&av_video_encode_codec_ctx); 
avcodec_free_context(&av_audio_encode_codec_ctx); 
avio_closep(&formatContext->pb); 
avformat_free_context(formatContext); 
+0

不知道如果我理解,但你從文件RTP分組閱讀,然後你會FEAD ffmpeg用它來爲你解碼一個幀?我有非常類似的問題,我需要捕獲RTP流並讓ffmpeg解碼(編程),但也找不到如何使用ffmpeg。你可以分享更多的解決方案嗎?謝謝你的時間。 – stviper

+0

嗯,我確實從文件讀取RTP數據(因爲它是交錯的tcp數據,我知道每個RTP數據包的開始和長度),並將h.264拆分爲單個h.264幀,具體取決於它需要的比特率和分辨率RTP數據包。我不會解碼抱歉,我只將已編碼的數據複製到一個容器中。 –