http://www.itdadao.com/articles/c15a234502p0.html
我們可以自己把音頻(AAC)或視頻(h264)數據 封裝爲RTP包 然後,通過UDP發送到一個端口 ,通過VLC播放,但VLC播放時不同於播放網絡流方式,而是通過打開一個後綴名爲sdp的文件來播放網絡rtp流。
個人測試了視頻h264 音頻aac 。PCM格式還沒測試
打開一個文件,修改文件名爲video.sdp 一下漢字爲註解 ,不需要在video.sdp文件中。
m=video 6688 RTP/AVP 96 //這裏意思是 VLC要通過6688接收數據 同時發送端也要發送到這個端口。另外要注意端口別和其他程序重複了,例如注意 5678端口同時是迅雷的一個端口 ,不要用,害的我花了半天時間。96的意思是動態的
a=rtpmap:96 H264 //到這裏告訴了VLC 編碼方式爲h264
c=IN IP4 109.112.145.38 //這一行 我由於發送數據程序也是在本機vlc也是在本機 所以測試的時候並沒有,寫到這裏 只是爲了假如你看了博客後 如果沒這條語句,而不能播放的話,請你加上
同理音頻(aac格式)如下:
m=audio 10020 RTP/AVP 96 同理10020爲目標端口和vlc接收端口
a=rtpmap:96 mpeg4-generic/44100 // 44100爲採樣率 假如你是22050 這裏要改正。不要問我 mpeg4-generic啥意思 我是從live555裏跟蹤出來的。
a=fmtp:96 streamtype=5; profile-level-id=15; mode=AAC-hbr; config=1210; SizeLength=13; IndexLength=3; IndexDeltaLength=3;Profile=1//這一行很重要,雖然不知道里邊的內容那個是必須的。profile中指定1表示低複雜度類型,config爲AAC的詳細格式信息
c=IN IP4 172.16.2.155//同理地址。
乾淨代碼如下:
m=video 5678 RTP/AVP 96
96:track->payload_type 視頻:96
音頻:97
a=rtpmap:96 H264
m=audio 10020 RTP/AVP 97
a=rtpmap:97 mpeg4-generic/44100
a=fmtp:97 streamtype=5; profile-level-id=15; mode=AAC-hbr; config=1210; SizeLength=13; IndexLength=3; IndexDeltaLength=3;Profile=1
c=IN IP4 172.16.2.155
streamtype=5; profile-level-id=15; mode=AAC-hbr:寫死
SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1:寫死
我會上傳一個打包h264爲rtp和打包aac爲rtp的工程。
請交流QQ493061330
aac文件 每幀七個字節頭
u_int8_t profile = (fixedHeader[2]&0xC0)>>6;
// 2 bits 得到Profile
if (profile == 3) {
env.setResultMsg("Bad (reserved) 'profile': 3 in first frame of ADTS file");
break;
}
// Get and check the 'sampling_frequency_index': 得到採樣率
u_int8_t sampling_frequency_index = (fixedHeader[2]&0x3C)>>2; // 4 bits
if (samplingFrequencyTable[sampling_frequency_index] == 0) {
env.setResultMsg("Bad 'sampling_frequency_index' in first frame of ADTS file");
break;
}
// Get and check the 'channel_configuration': 得到通道數
u_int8_t channel_configuration
= ((fixedHeader[2]&0x01)<<2)|((fixedHeader[3]&0xC0)>>6); // 3 bits
sdp中的config 計算方法
samplingFrequencyIndex:4
UINT8 const audioObjectType = profile + 1;
audioConfig[0] = (audioObjectType<<3) | (samplingFrequencyIndex>>1);
audioConfig[1] = (samplingFrequencyIndex<<7) | (channelConfiguration<<3);
printf("%02x%02x", audioConfig[0], audioConfig[1]);
0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0
unsigned char audioSpecificConfig[2];
u_int8_t const audioObjectType = profile + 1;
audioSpecificConfig[0] = (audioObjectType<<3) | (samplingFrequencyIndex>>1);
audioSpecificConfig[1] = (samplingFrequencyIndex<<7) | (channelConfiguration<<3);
sprintf(fConfigStr, "%02X%02x", audioSpecificConfig[0], audioSpecificConfig[1]);
a=fmtp:96streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210
streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; 此部分固定 摘自live555 config爲上邊的到的config
雙聲道 PCM 44100HZ 16位 左右聲道交替存放 即2字節左2字節右交替,編aac時 4096個字節 編一幀 即左右各1024個採樣混在一起編一幀
打包RTP時間戳 如下
例如 讀七個字節 分析 出幀大小 再讀取幀大小減去7個字節 則爲純數據
12個字節RTP頭 加上四個字節
sendbuf[12] = 0; sendbuf[13] = 16 /* bits */; // AU-headers-length
sendbuf[14] = (Framelen-7) >> 5; sendbuf[15] = ((Framelen-7)&0x1F)<<3;
RTP時間戳 如下 假如開始取起始時間 timeStart = GetTickCount();
則偏移時間爲int timeDifferent= GetTickCount()-timeStart;
unsigned int sampling_frequency_index = (adts_headerbuf[2]&0x3C)>>2;取採樣率索引
int sampling_frequency=mpeg4audio_sample_rates[sampling_frequency_index];
unsigned int timestampIncrement = (sampling_frequency*((int)timestamp/1000));//採樣率乘以秒數差
timestamp=(int)timestamp%1000;
timestampIncrement += (unsigned int)(sampling_frequency*((double)timestamp/1000.0)+ 0.5); // 再加上毫秒數乘積
rtp_hdr->timestamp = htonl(timestampIncrement);