ffmpeg笔记——快速获得H.264视频分辨率的方法
作者:Usher 发布于:2012-12-14 15:02 Friday 分类:技术杂记
在使用ffmpeg解码播放TS流的时候(例如之前写过的UDP组播流),在连接时往往需要耗费大量时间。经过debug发现是av_find_stream_info(已抛弃,现在使用的是avformat_find_stream_info)这个方法十分耗时,而且是阻塞的。av_find_stream_info方法主要是获得相应的流信息,其中对我的应用最有用的就是视频的分辨率。在av_find_stream_info中是要不断的读取数据包,解码获得相应的信息,而其中除了分辨率信息以外的东西对我的应用中是无用的。所以,考虑自己手动从H.264码流中解析出视频的分辨率信息。
以下内容主要参考了这篇文章:
H.264码流的流信息都存储在了特殊的结构中,叫做SPS(Sequence Parameter Set)。要解析SPS就需要知道一些H.264码流的格式信息。
在H.264码流中,都是以0x00 0x00 0x01 或者 0x00 0x00 0x00 0x01为开始码的(在我的应用中为后者),之后通过检测开始码后第一个字节的后五位是否为7(00111)来判断其是否为SPS。得到SPS之后,就可以解析出视频的分辨率。SPS中有两个成员,pic_width_in_mbs_minus1和pic_height_in_map_units_minus_1,分别表示图像的宽和高,但是要注意的是它们都是以16为单位(在面积上就是以16*16的块为单位)再减1,所以实际的宽是(pic_width_in_mbs_minus1 + 1)*16,高为(pic_height_in_map_units_minus_1+1)*16。
欢迎转载,转载请注明出处:
以下是解析宽高的代码:
-
bool UDPReceiver::getResolution(int channel, int &Width, int& Height)
-
{
-
BYTE *buf = new BYTE[1024];
-
int nLen;
-
AVPacket packet;
-
BOOL bSpsComplete = FALSE;
-
// Find SPS
-
while(!bSpsComplete)
-
{
-
int ret = av_read_frame(m_pFormatContext[channel], &packet);
-
if(packet.flags & AV_PKT_FLAG_KEY)
-
{
-
BYTE* p = packet.data;
-
BYTE last_nal_type = 0;
-
int last_nal_pos = 0;
-
for(int i=0; i<packet.size-5; i++)
-
{
-
p = packet.data + i;
-
if(p[0]==0x00&&p[1]==0x00&&p[2]==0x00&&p[3]==0x01)
-
{
-
if(last_nal_type == 0x67)
-
{
-
nLen = i-last_nal_pos;
-
memcpy(buf, packet.data+last_nal_pos, nLen);
-
bSpsComplete = TRUE;
-
}
-
last_nal_type = p[4];
-
last_nal_pos = i;
-
if(bSpsComplete)
-
{
-
break;
-
}
-
}
-
}
-
if (last_nal_type == 0x67 && bSpsComplete == FALSE)
-
{
-
nLen = packet.size - last_nal_pos;
-
memcpy(buf, packet.data+last_nal_pos, nLen);
-
bSpsComplete = TRUE;
-
}
-
}
-
}
-
// Analyze SPS to find width and height
-
UINT StartBit=0;
-
buf = buf + 4;
-
int forbidden_zero_bit=u(1,buf,StartBit);
-
int nal_ref_idc=u(2,buf,StartBit);
-
int nal_unit_type=u(5,buf,StartBit);
-
if(nal_unit_type==7)
-
{
-
int profile_idc=u(8,buf,StartBit);
-
int constraint_set0_flag=u(1,buf,StartBit);//(buf[1] & 0x80)>>7;
-
int constraint_set1_flag=u(1,buf,StartBit);//(buf[1] & 0x40)>>6;
-
int constraint_set2_flag=u(1,buf,StartBit);//(buf[1] & 0x20)>>5;
-
int constraint_set3_flag=u(1,buf,StartBit);//(buf[1] & 0x10)>>4;
-
int reserved_zero_4bits=u(4,buf,StartBit);
-
int level_idc=u(8,buf,StartBit);
-
-
int seq_parameter_set_id=Ue(buf,nLen,StartBit);
-
-
if( profile_idc == 100 || profile_idc == 110 ||
-
profile_idc == 122 || profile_idc == 144 )
-
{
-
int chroma_format_idc=Ue(buf,nLen,StartBit);
-
if( chroma_format_idc == 3 )
-
int residual_colour_transform_flag=u(1,buf,StartBit);
-
int bit_depth_luma_minus8=Ue(buf,nLen,StartBit);
-
int bit_depth_chroma_minus8=Ue(buf,nLen,StartBit);
-
int qpprime_y_zero_transform_bypass_flag=u(1,buf,StartBit);
-
int seq_scaling_matrix_present_flag=u(1,buf,StartBit);
-
-
int seq_scaling_list_present_flag[8];
-
if( seq_scaling_matrix_present_flag )
-
{
-
for( int i = 0; i < 8; i++ ) {
-
seq_scaling_list_present_flag[i]=u(1,buf,StartBit);
-
}
-
}
-
}
-
int log2_max_frame_num_minus4=Ue(buf,nLen,StartBit);
-
int pic_order_cnt_type=Ue(buf,nLen,StartBit);
-
if( pic_order_cnt_type == 0 )
-
int log2_max_pic_order_cnt_lsb_minus4=Ue(buf,nLen,StartBit);
-
else if( pic_order_cnt_type == 1 )
-
{
-
int delta_pic_order_always_zero_flag=u(1,buf,StartBit);
-
int offset_for_non_ref_pic=Se(buf,nLen,StartBit);
-
int offset_for_top_to_bottom_field=Se(buf,nLen,StartBit);
-
int num_ref_frames_in_pic_order_cnt_cycle=Ue(buf,nLen,StartBit);
-
-
int *offset_for_ref_frame=new int[num_ref_frames_in_pic_order_cnt_cycle];
-
for( int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
-
offset_for_ref_frame[i]=Se(buf,nLen,StartBit);
-
delete [] offset_for_ref_frame;
-
}
-
int num_ref_frames=Ue(buf,nLen,StartBit);
-
int gaps_in_frame_num_value_allowed_flag=u(1,buf,StartBit);
-
int pic_width_in_mbs_minus1=Ue(buf,nLen,StartBit);
-
int pic_height_in_map_units_minus1=Ue(buf,nLen,StartBit);
-
-
Width=(pic_width_in_mbs_minus1+1)*16;
-
Height=(pic_height_in_map_units_minus1+1)*16;
-
-
return true;
-
}
-
else
-
{
-
return false;
-
}
-
}
-
-
// Ue find the num of zeros and get (num+1) bits from the first 1, and
-
// change it to decimal
-
// e.g. 00110 -> return 6(110)
-
UINT UDPReceiver::Ue(BYTE *pBuff, UINT nLen, UINT &nStartBit)
-
{
-
//计算0bit的个数
-
UINT nZeroNum = 0;
-
while (nStartBit < nLen * 8)
-
{
-
if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8))) //&:按位与,%取余
-
{
-
break;
-
}
-
nZeroNum++;
-
nStartBit++;
-
}
-
nStartBit ++;
-
-
-
//计算结果
-
DWORD dwRet = 0;
-
for (UINT i=0; i<nZeroNum; i++)
-
{
-
dwRet <<= 1;
-
if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
-
{
-
dwRet += 1;
-
}
-
nStartBit++;
-
}
-
return (1 << nZeroNum) - 1 + dwRet;
-
}
-
-
int UDPReceiver::Se(BYTE *pBuff, UINT nLen, UINT &nStartBit)
-
{
-
int UeVal=Ue(pBuff,nLen,nStartBit);
-
double k=UeVal;
-
int nValue=std::ceil(k/2);//ceil函数:ceil函数的作用是求不小于给定实数的最小整数。ceil(2)=ceil(1.2)=cei(1.5)=2.00
-
if (UeVal % 2==0)
-
nValue=-nValue;
-
return nValue;
-
}
-
-
// u Just returns the BitCount bits of buf and change it to decimal.
-
// e.g. BitCount = 4, buf = 01011100, then return 5(0101)
-
DWORD UDPReceiver::u(UINT BitCount,BYTE * buf,UINT &nStartBit)
-
{
-
DWORD dwRet = 0;
-
for (UINT i=0; i<BitCount; i++)
-
{
-
dwRet <<= 1;
-
if (buf[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
-
{
-
dwRet += 1;
-
}
-
nStartBit++;
-
}
-
return dwRet;
-
}
阅读(3430) | 评论(0) | 转发(0) |