最近项目中遇到将流保存在FLV文件,录制成的文件发现如下现象:
- 用
VLC播放时,只能显示首帧,然后马上闪退。 - 用
QQ影音播放时,可播放但是跳的很快,感觉是只能播放I帧。
分析过程
刚开始的怀疑是AVCDecoderConfigurationRecord的各参数的问题,后来发现都正常,也就是说SPS+PPS参数都正常。
用VLC播放时,发现解析出来的编解码参数也正常。

那可能是为什么呢?
后来发现是Tag Header中的时间戳问题导致的。
FLV tag

Tag 分为两部分: Tag Header和Tag Body。
其中Tag Header共11个字节,包括TagType、DataSize、Timestamp、TimestampExtended、StreamID;Tag Body部分即实际的Data部分。
-
TagType(1字节)
Tag的类型,主要分为:- 0x08: 音频。
- 0x09: 视频。
-
0x12: 即十进制的
18, 脚本Tag, 一般指onMetaData。
DataSize(3字节)
数据(Data)的长度。Timestamp(3字节)
时间戳,单位毫秒。
该时间戳是相对于首个Tag时间戳的相对时间戳。
首个Tag的时间戳为0。-
TimestampExtended(1字节)
扩展时间戳。和Timestamp合在一起拼成一个32bit的时间戳。
该时间戳占高8位。
**TimestampExtended**+**Timestamp**=**32bit时间戳** StreamID(3字节)
为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
