解复用FLV文件(不用FFMPEG,C++实现)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hiwubihe/article/details/82346800

技术在于交流、沟通,转载请注明出处并保持作品的完整性。

原文:https://blog.csdn.net/hiwubihe/article/details/82346800

[本系列相关文章]

本篇运用C++实现一个FLV文件的解复用,FLV可以是h264+AAC封装,也可以是h264+mp3封装的。参考ffmpeg源码flvenc.c实现。

探测视频格式是否FLV格式,这部分直接参考协议定义即可。

static bool ProbeFlv(unsigned char*p,long lSize)
{
	unsigned char *d = p;
	
	d+=5;
	unsigned offset = avio_rb32(&d);

	//头FLV 版本<5 头长度>8
	if (p[0] == 'F' &&
		p[1] == 'L' &&
		p[2] == 'V' &&
		p[3] < 5 && p[5] == 0 &&
		offset + 100 < lSize &&
		offset > 8)
	{
		return true;
	}
	return false;
}

读取FLV头

//读取FLV头
static bool flvReadHeader(unsigned char*pData,long lSize,int &iConsumed)
{
	iConsumed = 0;
	unsigned char*p = pData;

	p+=4;
	iConsumed+=4;

	int flags = avio_r8(&p);
	iConsumed+=1;

	g_FlvInfo.iStreamFlags = flags & (FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO);

	int offset = avio_rb32(&p);
	iConsumed+=4;

	if(offset != iConsumed)
	{
		return false;
	}
	//前TAG长度字段读取
	int iPreTagSize = avio_rb32(&p);
	iConsumed+=4;

	if(iPreTagSize != 0)
	{
		return false;
	}

	return true;
}

循环读取数据,并解析一个完整的TAG

bool FindFlvTag(unsigned char*pData,long lSize,TagHeadInfo &stTagHeadInfo)
{
	//头长度11
	if(lSize<(11))
	{
		//长度不够
		return false;
	}
	//没解析头 现在解析
	if(stTagHeadInfo.iHeadLen == 0)
	{
		unsigned char*p = pData;
		int iOffset=0;

		//TAG头类型
		stTagHeadInfo.type = (FlvTagType)(avio_r8(&p) & 0x1F);
		iOffset += 1;
		//TAG数据长度
		stTagHeadInfo.iTagSize = avio_rb24(&p);
		iOffset += 3;

		uint64_t dts  = avio_rb24(&p);
		dts |= (unsigned)avio_r8(&p) << 24;
		stTagHeadInfo.dts = dts;
		iOffset += 4;

		//跳过STREAMID
		iOffset += 3;

		stTagHeadInfo.iHeadLen =iOffset;

		stTagHeadInfo.pData = pData;

		//头长度+Tag消息长度+前一个Tag长度
		if(lSize>= stTagHeadInfo.iHeadLen + stTagHeadInfo.iTagSize+4)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	//已经解析过头
	else
	{
		//头长度+Tag消息长度+前一个Tag长度
		if(lSize>= stTagHeadInfo.iHeadLen + stTagHeadInfo.iTagSize+4)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
}

找到TAG 类型FLV_TAG_TYPE_META没有解析,只解析了 FLV_TAG_TYPE_AUDIO和FLV_TAG_TYPE_VIDEO

解析 FLV_TAG_TYPE_AUDIO数据,一般音频如MP3直接保存,AAC需要特殊处理。

bool ParserAudioTag(unsigned char*pData,TagHeadInfo &stTagHeadInfo)
{
	unsigned char*p = pData+stTagHeadInfo.iHeadLen;
	int iUseLen =0;
	//视频flag (帧类型和帧编码格式)
	int flags    = avio_r8(&p);
	iUseLen+=1;

	stTagHeadInfo.iAudioCodeType = flags & FLV_AUDIO_CODECID_MASK;

	stTagHeadInfo.iChans = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
	stTagHeadInfo.iSampleRate = 44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >>
		FLV_AUDIO_SAMPLERATE_OFFSET) >> 3;
	stTagHeadInfo.iSampleBits = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;

	//AAC 特殊处理
	if(stTagHeadInfo.iAudioCodeType == FLV_CODECID_AAC)
	{
		int AACPacketType = avio_r8(&p);	
		iUseLen+=1;
		//The AudioSpecificConfig is explained in ISO 14496-3
		if(AACPacketType == 0)
		{
			memcpy(g_FlvInfo.stAACInfo.pAacSpecificConfig, p , stTagHeadInfo.iTagSize - iUseLen);
			g_FlvInfo.stAACInfo.iAacSpecificConfigLen = stTagHeadInfo.iTagSize - iUseLen;
			//解析AudioSpecificConfig结构
			aac_decode_extradata(&(g_FlvInfo.stAACInfo.stADTSContext), g_FlvInfo.stAACInfo.pAacSpecificConfig, g_FlvInfo.stAACInfo.iAacSpecificConfigLen);
			

		}
		//
		else if(AACPacketType == 1)
		{

			unsigned char szAdtsHead[ADTS_HEADER_SIZE];
			aac_set_adts_head(&(g_FlvInfo.stAACInfo.stADTSContext), szAdtsHead, stTagHeadInfo.iTagSize-iUseLen);
			printf("write aac autio adts:%d\n",ADTS_HEADER_SIZE);
			fwrite(szAdtsHead, 1,ADTS_HEADER_SIZE, gAudioOutputFile);

			printf("write aac autio data:%d\n",stTagHeadInfo.iTagSize-iUseLen);
			fwrite(p, stTagHeadInfo.iTagSize-iUseLen, 1, gAudioOutputFile);
			
		}
		else
		{

		}


	}
	else
	{
		printf("write autio data:%d\n",stTagHeadInfo.iTagSize-iUseLen);
		fwrite(p, stTagHeadInfo.iTagSize-iUseLen, 1, gAudioOutputFile);
	}
	
	return true;
}

 处理FLV_TAG_TYPE_VIDEO,这里主要保存SPS/PPS信息,遇到I帧需要加上SPS/PPS信息。注意一个TAG里面有多个包的情况,一般一个TAG里面是P帧,或者SEI+PPS+SPS+I帧1+I帧2等,注意处理好。I帧可能分成几个包。每个包格式都是"长度+数据"。

//长度上层已经保证
bool ParserVideoTag(unsigned char*pData,TagHeadInfo &stTagHeadInfo)
{
	unsigned char*p = pData+stTagHeadInfo.iHeadLen;
	int iUseLen =0;
	//视频flag (帧类型和帧编码格式)
	int flags    = avio_r8(&p);
	iUseLen+=1;

	int flvCodeId = flags& FLV_VIDEO_CODECID_MASK;
	//只支持H264目前
	switch (flvCodeId)
	{
		case FLV_CODECID_H263:
			
			break;
		case FLV_CODECID_REALH263:
			
			break; // Really mean it this time
		case FLV_CODECID_SCREEN:
			
			break;
		case FLV_CODECID_SCREEN2:
			
			break;
		case FLV_CODECID_VP6:
		case FLV_CODECID_VP6A:
			break;
			//标准里面的结构 AVCVIDEOPACKET
		case FLV_CODECID_H264:
			{
				/*  0: AVC sequence header
					1: AVC NALU
					2: AVC end of sequence (lower level NALU
					sequence ender is not required or supported)
				*/

				int AVCPacketType = avio_r8(&p);	
				iUseLen+=1;

				//CompositionTime
				//CTS 是PTS-DTS 一般没有B帧时cts=0
				int32_t cts = (avio_rb24(&p) + 0xff800000) ^ 0xff800000;
				iUseLen+=3;
				stTagHeadInfo.pts = stTagHeadInfo.dts + cts;
				//AVCDecoderConfigurationRecord
				if(AVCPacketType == 0)
				{
					//生成sps和pps 保存
					h264_AVCDecoderConfigurationRecord_2SpsPpsSerial(p);
				}
				//一个TAG里可能有多个NALU 所以是NALUS,可能是把一帧分成几个包如
				//一个I帧 分成 (包1长度+包1数据+包2长度+包2数据),处理时需要改成
				//00000001+包1数据+000001+包2数据
				else if(AVCPacketType == 1)
				{
					h264_NalusParser(p,stTagHeadInfo.iTagSize - iUseLen  );
				}
				else
				{

				}
				

			}
			break;
		case FLV_CODECID_MPEG4:
			break;
		default:
			break;

	}

	unsigned char*p1 = pData+stTagHeadInfo.iHeadLen+stTagHeadInfo.iTagSize;
    int iPreTagLen = avio_rb32(&p1);
	if(iPreTagLen != stTagHeadInfo.iHeadLen+stTagHeadInfo.iTagSize)
	{
		printf("PreviousTagSize Error\n");
	}



	return true;

}

猜你喜欢

转载自blog.csdn.net/hiwubihe/article/details/82346800