最近項目中遇到將流保存在FLV文件,錄制成的文件發(fā)現(xiàn)如下現(xiàn)象:
- 用
VLC
播放時尿招,只能顯示首幀矾柜,然后馬上閃退。 - 用
QQ影音
播放時就谜,可播放但是跳的很快怪蔑,感覺是只能播放I幀。
分析過程
剛開始的懷疑是AVCDecoderConfigurationRecord的各參數(shù)的問題丧荐,后來發(fā)現(xiàn)都正常缆瓣,也就是說SPS+PPS參數(shù)都正常。
用VLC
播放時虹统,發(fā)現(xiàn)解析出來的編解碼參數(shù)也正常弓坞。
那可能是為什么呢?
后來發(fā)現(xiàn)是Tag Header中的時間戳問題導致的隧甚。
FLV tag
Tag 分為兩部分: Tag Header和Tag Body。
其中Tag Header共11個字節(jié)昼丑,包括TagType呻逆、DataSize、Timestamp菩帝、TimestampExtended、StreamID
茬腿;Tag Body部分即實際的Data
部分呼奢。
-
TagType(1字節(jié))
Tag
的類型,主要分為:- 0x08: 音頻切平。
- 0x09: 視頻握础。
-
0x12: 即十進制的
18
, 腳本Tag
, 一般指onMetaData
。
DataSize(3字節(jié))
數(shù)據(jù)(Data)的長度悴品。Timestamp(3字節(jié))
時間戳禀综,單位毫秒。
該時間戳是相對于首個Tag時間戳的相對時間戳苔严。
首個Tag的時間戳為0定枷。-
TimestampExtended(1字節(jié))
擴展時間戳。和Timestamp
合在一起拼成一個32bit
的時間戳届氢。
該時間戳占高8位欠窒。
StreamID(3字節(jié))
為0。
文檔中還有段話非常重要:
In playback, the time sequencing of FLV tags depends on the FLV timestamps only. Any timing mechanisms built into the payload data format shall be ignored.
FLV文件回放時退子,FLV tags
的時間順序僅依賴于FLV的時間戳岖妄。
忽略任何payload data中的時間機制。
可見時間戳對FLV文件的播放有多重要寂祥。
導致VLC
只能播放首幀的原因:FLV文件中所有的時間戳未進行有效的初始化, Timestamp和TimestampExtended全是0, 只有首幀的時間戳是正確的(就是0, _)荐虐。
示例:
問題解決
在程序中對時間戳進行正常賦值后,用VLC
可正常播放錄制的FLV
視頻丸凭。
部分代碼:
struct SPxFLVRecorderTagHeader
{
unsigned char uchTagType;
unsigned char uchDataSize[3];
unsigned char uchTimestamp[3];
unsigned char uchTimestampExtended;
unsigned char uchStreamID[3];
};
...
SPxFLVRecorderTagHeader m_sFlvFileTagHeader;
...
HRESULT CPxFLVMuxer::WriteVideoSample(unsigned char *in_pBuffer, int in_nBufferLen, int in_nTimeStamp)
{
HRESULT hr = NS_NOERROR;
...
m_sFlvFileTagHeader.uchTagType = 0x09; // 視頻Tag
...
m_sFlvFileTagHeader.uchTimestamp[0] = (BYTE)((in_nTimeStamp >> 16) & 0xff);
m_sFlvFileTagHeader.uchTimestamp[1] = (BYTE)((in_nTimeStamp >> 8) & 0xff);
m_sFlvFileTagHeader.uchTimestamp[2] = (BYTE)((in_nTimeStamp >> 0) & 0xff);
m_sFlvFileTagHeader.uchTimestampExtended = (BYTE)((in_nTimeStamp >> 24) & 0xff);
...
return hr;
}
...
References:
Video File Format Specification Version 10