live555 關於使用ffmpeg擴展文件格式

live555目前只mpg、mkv、webM等少數幾種音視頻混合的文件,可以使用ffmpeg來擴展live555所支持的文件格式。花了一個多月的時間,
終於可以支持mp4和avi了,媒體格式支持mpeg4、h264、mp3、aac。主要是參考了live555中mpg的實現。

1.擴展mediaServer的主要步驟    
1)定義一個RTSP Server類MyRTSPServer,繼承自DynamicRTSPServer。作用是重新實現lookupServerMediaSession函數,在其中添加對avi、mp4的支持代碼。
2)參考mpg的實現,定義以下幾個類
MyDemux->Mdeium: 
調用ffmpeg中的函數,完成文件的解析,分離出文件中媒體流的數據。每一個客戶端連接將對應一個MyDemux實例。
MyDemuxedElementaryStream->FramedSource:
這個類作爲source,實際上會調用MyDemux實例獲取所需的數據。每一個流將對應一個MyDemuxedElementaryStream實例。
MyServerDemux->Medium:
這個一個服務類,主要是輔助創建MyDemux、MyDemuxedElementaryStream及subsession實例。
3)subsession
對於每一種媒體格式,都需要實現一個subsession,並重新實現虛函數createNewStreamSource以創建自己的source。
    h264,處理h264的subsession從H264VideoFileServerMediaSubsession繼承,將從packet中可以獲取h264的es流,直接交給H264VideoStreamFramer處理即可。
    mpeg4,subsession從MPEG4VideoFileServerMediaSubsession繼承,另外需要注意的是,從packet中獲取到的數據並不是嚴格的ES流。與處理mpeg4 es流相關的類有兩個,MPEG4VideoStreamFramer與MPEG4VideoStreamDiscreteFramer,這裏應選擇MPEG4VideoStreamDiscreteFramer。具體可以參考前面的文章"live555 關於mpeg4的處理"
    mp3, subsession從MP3AudioFileServerMediaSubsession繼承。注意還需要實現seekStreamSource函數,函數體可以爲空。在創建MP3AudioFileServerMediaSubsession時,傳遞的參數中generateADUs應爲False, interleaving爲空。
    aac, subsession從FileServerMediaSubsession繼承。要實現createNewStreamSource及createNewRTPSink。createNewRTPSink的實現,可以參考live555對Matroska中aac的處理。

2.處理avi、mp4文件文件的一些注意事項
    通過ffmpeg獲取的數據一般保存在packet中。 對於avi文件,處理比較簡單,直接將packet中的數據提取出來,進行後續處理即可。對於mp4文件,則稍複雜。

從mp4中提取h264數據時, packet中不會包含SPS及PPS信息,這些信息儲存在AVCodecContext的extradata數據域中,通過流過濾器"h264_mp4toannexb"可以得到標準的sps及pps。另外packet中的數據並不是標準nalu單元,需要將前4個字節替成開始符。詳情見"ffmpeg 從mp4上提取H264的nalu"。

關於mp4中的mpeg4, 獲取的packet中,只包含vop層次(0x000001B5)的數據,vos(0x000001B0)則儲存在在AVCodecContext的extradata數據域中,所以只需要將extradata中的數據添加到流的開始處即可。

對於mp4中的音頻aac來講,音頻配置信息config,也是保存在extradata。

3.aac音頻的配置信息config
mpeg4中的aac是交給MPEG4GenericRTPSink的,需要關注一下傳遞的參數,來看Matroska文件中是怎麼處理的

RTPSink* AACAudioMatroskaFileServerMediaSubsession
::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /inputSource/) {
MatroskaTrack* track = fOurDemux.lookup(fTrackNumber);
return MPEG4GenericRTPSink::createNew(envir(), rtpGroupsock,
rtpPayloadTypeIfDynamic,
track->samplingFrequency, //採樣頻率
“audio”, “AAC-hbr”, fConfigStr,
track->numChannels); //通道數
}

創建MPEG4GenericRTPSink時,fConfigStr就是aac的幀配置信息,它表示AAC幀的配置信息。其實config有兩種傳輸方式,包含在RTP負載稱爲帶內傳輸,否則稱爲帶外傳輸,這時就必需在SDP中傳輸config信息了。顯然使用帶外傳輸可以節約帶寬。 config是一個16進制的8位字節串,可表示ISO/IEC 14496-3 [5] 定義的MPEG-4音頻負載配置數據"StreamMuxConfig"。SDP中還有一個cpresent屬性, 一個布爾值參數,表示音頻負載配置數據是否已經複用到一個RTP負載中。0表示尚未複用,1表示已經複用。該參數的缺省值爲1。即缺省情況下爲帶外傳輸。

在MPEG4GenericRTPSink構造函數中可以看到這個配置信息是如何保存到sdp中的。
MPEG4GenericRTPSink
::MPEG4GenericRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
u_int8_t rtpPayloadFormat,
u_int32_t rtpTimestampFrequency,
char const* sdpMediaTypeString,
char const* mpeg4Mode, char const* configString,
unsigned numChannels)
: MultiFramedRTPSink(env, RTPgs, rtpPayloadFormat,
rtpTimestampFrequency, “MPEG4-GENERIC”, numChannels),
fSDPMediaTypeString(strDup(sdpMediaTypeString)),
fMPEG4Mode(strDup(mpeg4Mode)), fConfigString(strDup(configString)) {
// Check whether “mpeg4Mode” is one that we handle:
if (mpeg4Mode == NULL) {
env << “MPEG4GenericRTPSink error: NULL “mpeg4Mode” parameter\n”;
} else if (strcmp(mpeg4Mode, “AAC-hbr”) != 0) { //說明必需使用"AAC-hbr"模式
env << “MPEG4GenericRTPSink error: Unknown “mpeg4Mode” parameter: “”
<< mpeg4Mode << “”\n”;
}

// Set up the “a=fmtp:” SDP line for this stream:
char const* fmtpFmt =
"a=fmtp:%d "
“streamtype=%d;profile-level-id=1;”
“mode=%s;sizelength=13;indexlength=3;indexdeltalength=3;”
“config=%s\r\n”; //config屬性
unsigned fmtpFmtSize = strlen(fmtpFmt)
+ 3 /* max char len /
+ 3 /
max char len /
+ strlen(fMPEG4Mode)
+ strlen(fConfigString); //注意
char
fmtp = new char[fmtpFmtSize];
sprintf(fmtp, fmtpFmt,
rtpPayloadType(),
strcmp(fSDPMediaTypeString, “video”) == 0 ? 4 : 5,
fMPEG4Mode,
fConfigString); //注意
fFmtpSDPLine = strDup(fmtp);
delete[] fmtp;
}

ffmpeg獲取到的config信息保存在AVCodecContext.extradata中,佔用兩個字節。對於44khz的音頻,這兩個字節順序爲0x12,0x10。不過需要轉換成字符串後再保存到config中,所以最後config=“1816”。

4.mp3、aac幀播放時間

aac每幀均爲1024個採樣,所以可以通過如下公式計算:
frame_duration = 1024 * 1000000 / sample_rate
例如:sample_rate = 44100HZ時, 計算出的時長爲23.219ms

mp3 每幀均爲1152個字節, 則:

frame_duration = 1152 * 1000000 / sample_rate

例如:sample_rate = 44100HZ時, 計算出的時長爲26.122ms,這就是經常聽到的mp3每幀播放時間固定爲26ms的由來

附上一張mpeg音頻每幀採樣數表

一些問題:
這個地方有一個問題,使用vlc播放avi或者mp4中的mpeg4視頻時,非常卡,cpu會達到100%,但是使用ffplay和mplayer時播放正常,

PS:

代碼已上傳至http://download.csdn.net/detail/gavinr/4320175

作者:gavinr
來源:CSDN
原文:https://blog.csdn.net/gavinr/article/details/7186843
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章