Part 1flvtag组成

FLV 文件结构由 FLVheader和FLVBody组成。(注意flv文件是大端格式的)
FLV头组成(以c为例子,一字节对齐):
FLVBody是由若干个Tag组成的;
     Tag=Tag头(11字节)+数据

  1. typedef struct _FLV_HEADER
  2. {
  3. char FLV[3];//={0x46,0x4c,0x56};
  4. char Ver;   //版本号
  5. char StreamInfo;// 有视频又有音频就是0x01 | 0x04(0x05)
  6. int HeaderLen; /*****头的长度*****/
  7. } FLV_HEADER;

Part2 h264

H264是一个个NALU单元组成的,每个单元以00 00 01 或者 00 00 00 01分隔开来,每2个00 00 00 01之间就是一个NALU单元。我们实际上就是将一个个NALU单元封装进FLV文件。
每个NALU单元开头第一个byte的低5bits表示着该单元的类型,即NAL nal_unit_type:

  1. #define NALU_TYPE_SLICE 1
  2. #define NALU_TYPE_DPA 2
  3. #define NALU_TYPE_DPB 3
  4. #define NALU_TYPE_DPC 4
  5. #define NALU_TYPE_IDR 5    /**关键帧***/
  6. #define NALU_TYPE_SEI 6    /*****曾强帧******/
  7. #define NALU_TYPE_SPS 7
  8. #define NALU_TYPE_PPS 8
  9. #define NALU_TYPE_AUD 9
  10. #define NALU_TYPE_EOSEQ 10
  11. #define NALU_TYPE_EOSTREAM 11
  12. #define NALU_TYPE_FILL 12

每个NALU第一个byte & 0x1f 就可以得出它的类型,比如上图第一个NALU:67 & 0x1f = 7,则此单元是SPS,第三个:68 & 0x1f = 8,则此单元是PPS。

Part3 h264 封装flv

我们现在开始把H264,AAC封装为FLV文件。
 首先定义一个函数(功能反向拷贝):

  1. void ReverseMemcpy(void* dest,size_t destLen, const void* src, size_t n)
  2. {
  3. char*     d= (char*) dest;
  4. const char*  s= (const char*)src;
  5. s=s+n-1;
  6. while(n--&&destLen--)
  7. {
  8. *d++=*s--;
  9. }
  10. return dest;
  11. }

1.写入FLV头。
              2.写入FLV脚本Tag;
              3.由于分装的是H264,AAC所以所写入一个视频配置信息,和一个音频配置信息
              3.写入视频Tag.由于是H264,Tag的数据就需要按照AVC格式封装。Tag数据区有两种,一种是视频(0x17),一种是音频(0x27)。
                 AVC格式:AVCPacketType(1字节)+CompositionTime(3字节)
                               如果AVCPacketType=0x00,这格式为AVCPacketType(1字节)+CompositionTime(3字节)+AVCDecoderConfigurationRecord。
                               如果AVCPacketType=0x01,这格式为AVCPacketType(1字节)+CompositionTime(3字节)+ 4个bytes的NALU单元长度 + N个bytes的NALU数据。
AVCDecoderConfigurationRecord结构信息

  1. typedef struct _AVC_DEC_CON_REC
  2. {
  3. char cfgVersion;//configurationVersion  //0x01
  4. char avcProfile;//AVCProfileIndication  //sps[1]
  5. char profileCompatibility;//profile_Compatibility //sps[2]
  6. char avcLevel;//AVCLevelIndication //sps[3]
  7. //lengthSizeMinusOne:indicates the length in bytes of the NALUnitLength field in an AVC video
  8. char reserved6_lengthSizeMinusOne2;//
  9. char reserved3_numOfSPS5;//个数
  10. long spsLength;//sequenceParameterSetLength
  11. void *sps;
  12. char numOfPPS;//个数
  13. long ppsLength;
  14. void *pps;
  15. }AVC_DEC_CON_REC;
  16. char *pH264Data=....;//h264数据。
  17. int H264DataLen=....;//h264数据长度
  18. FLV_TAG_HEADER tagHeader;
  19. char AVCPacket[4]={0x00,0x00,0x00,0x00}
  20. memset(tagHeader,0,sizeof(FLV_TAG_HEADER));
  21. int Index=0;//分隔符长度
  22. if(*pH264Data==0x00&&(*pH264Data+1)==0x00&&(*pH264Data+2)==0x01)
  23. {
  24. Index=3;
  25. }else if(*pH264Data==0x00&&(*pH264Data+1)==0x00&&(*pH264Data+2)==0x00&&(*pH264Data+4)==0x01)
  26. {
  27. Index=4;
  28. }else{
  29. Err//错误不是h264数据
  30. }
  31. if(*(pH264Data+Index)&0x1f==0x07)//sps帧,此h264数据还有一帧,pps。
  32. {
  33. int PreTagLen=.....//前一个Tag长度
  34. ReverseMemcpy(&tagHeader.PreTagLen,4,&PreTagLen,4);//大端字节序;
  35. tagHeader.TagType=0x09;//视频类型
  36. //AVCPacket应全为0x00.
  37. }

part 4.音频AAC封装flv。

AAC音频格式有ADIF和ADTS:ADIF:Audio Data Interchange Format 音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。ADTS:Audio Data Transport Stream 音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。简单说,ADTS可以在任意帧解码,也就是说它每一帧都有头信息。ADIF只有一个统一的头,所以必须得到所有的数据后解码。且这两种的header的格式也是不同的,目前一般编码后的和抽取出的都是ADTS格式的音频流。语音系统对实时性要求较高,基本是这样一个流程,采集音频数据,本地编码,数据上传,服务器处理,数据下发,本地解码ADTS是帧序列,本身具备流特征,在音频流的传输与处理方面更加合适。
因此我们在音频编码时常选择ADTS的,以下是我们常用的配置

  1. m_hAacEncoder= faacEncOpen(capability.nSamplesPerSec,capability.nChannels,
  2. &m_nAacInputSamples, &m_nAacMaxOutputBytes);
  3. m_nAacnMaxInputBytes=m_nAacInputSamples*capability.wBitsPerSample/8;
  4. m_pAacConfig = faacEncGetCurrentConfiguration(m_hAacEncoder);//获取配置结构指针
  5. m_pAacConfig->inputFormat = FAAC_INPUT_16BIT;//16精度
  6. m_pAacConfig->outputFormat=1; //   设置为 ADTS
  7. m_pAacConfig->useTns=true;
  8. m_pAacConfig->useLfe=false;
  9. m_pAacConfig->aacObjectType=LOW;
  10. m_pAacConfig->shortctl=SHORTCTL_NORMAL;
  11. m_pAacConfig->quantqual=100;
  12. m_pAacConfig->bandWidth=0;
  13. m_pAacConfig->bitRate=capability.nAvgBytesPerSec;

对于flv的aac音频和视频一样需要在第一帧写入配置信息。

  1. flv_packet flvpacket=GetErrPacket();
  2. int TagDataLen=1000;
  3. char *pTagBuffer=(char *)::malloc(TagDataLen);
  4. memset(pTagBuffer,0,TagDataLen);
  5. KKMEDIA::FLV_TAG_HEADER Tag_Head;
  6. memset(&Tag_Head,0,sizeof(Tag_Head));
  7. FlvMemcpy(&Tag_Head.PreTagLen,4,&m_nPreTagLen,4);
  8. memset(&Tag_Head.Timestamp,0,3);
  9. Tag_Head.TagType=0x08;///音频
  10. int datalen=0;
  11. memcpy(pTagBuffer,&Tag_Head,sizeof(KKMEDIA::FLV_TAG_HEADER));
  12. datalen+=sizeof(KKMEDIA::FLV_TAG_HEADER);
  13. //前4bits表示音频格式(全部格式请看官方文档):
  14. //1 -- ADPCM
  15. //2 -- MP3
  16. //4 -- Nellymoser 16-kHz mono
  17. //5 -- Nellymoser 8-kHz mono
  18. //10 -- AAC
  19. //面两个bits表示samplerate:
  20. //·0 -- 5.5KHz
  21. //·1 -- 11kHz
  22. //·2 -- 22kHz
  23. //·3 -- 44kHz 1100=0x0C
  24. //下面1bit表示采样长度:
  25. //·0 -- snd8Bit
  26. //·1 -- snd16Bit
  27. //下面1bit表示类型:
  28. //·0 -- sndMomo
  29. //·1 -- sndStereo
  30. char TagAudio=0xAF; //1010,11,1,1
  31. //TagAudio &=0x0C;//3
  32. //TagAudio &=0x02;//1
  33. //TagAudio &=0x01;//sndStereo
  34. memcpy(pTagBuffer+datalen,&TagAudio,1);
  35. datalen++;
  36. char AACPacketType=0x00;//012->
  37. memcpy(pTagBuffer+datalen,&AACPacketType,1);
  38. datalen++;
  39. ///两个字节
  40. char AudioSpecificConfig[2]={0x12,0x90};///32000hz
  41. memcpy(pTagBuffer+datalen,&AudioSpecificConfig,2);
  42. datalen+=2;
  43. m_nPreTagLen=datalen-4;///(tag长度值)
  44. TagDataLen=datalen-15;//(11+4(tag长度值+tag的头)
  45. //Tag 数据区长度
  46. FlvMemcpy(pTagBuffer+5,3,&TagDataLen,3);
  47. flvpacket.buf =(unsigned char*)pTagBuffer;
  48. flvpacket.bufLen=datalen;
  49. flvpacket.taglen=m_nPreTagLen;
  50. return flvpacket;

关于上面的代码中AudioSpecificConfig的值是怎样计算来的,可以直接中aac编码库中获取,或者采用公式计算出来,请看一下代码。

  1. ///索引表
  2. static unsigned const samplingFrequencyTable[16] = {
  3. 96000, 88200, 64000, 48000,
  4. 44100, 32000, 24000, 22050,
  5. 16000, 12000, 11025, 8000,
  6. 7350,  0,     0,      0
  7. };
  8. int profile=1;
  9. int samplingFrequencyIndex=0;
  10. for(int i=0;i<16;i++)
  11. {
  12. if(samplingFrequencyTable[i]==32000)
  13. {
  14. samplingFrequencyIndex =i;
  15. break;
  16. }
  17. }
  18. char channelConfiguration =0x02;//(立体声)
  19. UINT8 audioConfig[2] = {0};
  20. UINT8 const audioObjectType = profile + 1;  ///其中profile=1;
  21. audioConfig[0] = (audioObjectType<<3) | (samplingFrequencyIndex>>1);
  22. audioConfig[1] = (samplingFrequencyIndex<<7) | (channelConfiguration<<3);
  23. printf("%02x%02x", audioConfig[0], audioConfig[1]);

最后就写入aac帧数据了,请看以下代码:

  1. flv_packet flvpacket=GetErrPacket();
  2. int TagDataLen=1000+srcLen;
  3. char *pTagBuffer=(char *)::malloc(TagDataLen);
  4. memset(pTagBuffer,0,TagDataLen);
  5. KKMEDIA::FLV_TAG_HEADER Tag_Head;
  6. memset(&Tag_Head,0,sizeof(Tag_Head));
  7. //FlvMemcpy等同于ReverseMemcpy
  8. FlvMemcpy(&Tag_Head.PreTagLen,4,&m_nPreTagLen,4);
  9. FlvMemcpy(&Tag_Head.Timestamp,3,&pts,3);
  10. Tag_Head.TagType=0x08;///音频
  11. int datalen=0;
  12. memcpy(pTagBuffer,&Tag_Head,sizeof(KKMEDIA::FLV_TAG_HEADER));
  13. datalen+=sizeof(KKMEDIA::FLV_TAG_HEADER);
  14. char TagAudio=0xAF;
  15. memcpy(pTagBuffer+datalen,&TagAudio,1);
  16. datalen++;
  17. char AACPacketType=0x01;
  18. memcpy(pTagBuffer+datalen,&AACPacketType,1);
  19. datalen++;
  20. //src aac数据指针(不包含ADTS头长度),srcLenAAC数据长度
  21. memcpy(pTagBuffer+datalen,src,srcLen);
  22. datalen+=srcLen;
  23. m_nPreTagLen=datalen-4;///(tag长度值)
  24. TagDataLen=datalen-15;//(11+4(tag长度值+tag的头)
  25. //Tag 数据区长度
  26. FlvMemcpy(pTagBuffer+5,3,&TagDataLen,3);
  27. flvpacket.buf =(unsigned char*)pTagBuffer;
  28. flvpacket.bufLen=datalen;
  29. flvpacket.taglen=m_nPreTagLen;
  30. return flvpacket;

注意在使用ADTS头输出的AAC编码数据,在打包flv格式时,需要跳过其adts头长度(7字节)。
例如:

    1. AudioPacket((const unsigned  char *)(pDataNALU+7),PktSize-7,Pts);

h264 aac 封装 flv的更多相关文章

  1. send/receive h264/aac file/data by rtp/rtsp over udp/tcp

    一.安装一些必要的调试工具 1.vlc安装sudo apt-get install vlcsudo apt-get install vlc-nox 2.ffmpeg安装,带ffplay,ffplay依 ...

  2. H264编码 封装成MP4格式 视频流 RTP封包

    H264编码 封装成MP4格式 视频流 RTP封包         分类:             多媒体编程              2013-02-20 21:31     3067人阅读    ...

  3. 公布一个软件,轻新视频录播程序,H264/AAC录制视音频,保存FLV,支持RTMP直播

    已经上传到CSDN,下载地址:http://download.csdn.net/detail/avsuper/7421647,不要钱滴,嘿嘿... 本程序能够把摄像头视频和麦克风音频,录制为FLV文件 ...

  4. 嵌入式 使用mp4v2将H264+AAC合成mp4文件

    录制程序要添加新功能:录制CMMB电视节目,我们的板卡发送出来的是RTP流(H264视频和AAC音频),录制程序要做的工作是: (1)接收并解析RTP包,分离出H264和AAC数据流: (2)将H26 ...

  5. 嵌入式 hi3518x平台h264+g711a封装mp4代码demo

    先看代码吧,有代码有真相,具体代码的demo(下载demo的朋友请勿在网上上传我的demo,谢谢)下载连接为: http://download.csdn.net/detail/skdkjxy/8071 ...

  6. 使用mp4v2将H264+AAC合成mp4文件

    录制程序要添加新功能:录制CMMB电视节目,我们的板卡发送出来的是RTP流(H264视频和AAC音频),录制程序要做的工作是: (1)接收并解析RTP包,分离出H264和AAC数据流: (2)将H26 ...

  7. rtmp 推送h264 + aac 的数据

    相关源码下载: http://download.csdn.net/detail/keepingstudying/8340431 需要libfaac,librtmp 的支持, 1.帧的划分 1.1 H. ...

  8. 开发RTSP 直播软件 H264 AAC 编码

    上一篇对摄像头预览,拍照做了大概的介绍,现在已经可以拿到视频帧了,在加上 RTSP 实现,就是直播的雏形,当然还要加上一些 WEB 管理和手机平台的支援,就是一整套直播软件. 介绍一些基础概念:RTP ...

  9. Directshow 采集音视频数据H264+AAC+rtmp效果还不错

    从usb摄像头或者采集卡中采集效果还是不错的.

随机推荐

  1. Java进阶2 数组内存和对象的内存管理知识

    Java进阶2 数组内存和对象的内存管理知识 20131028 前言: 在面试的时候,如果是Java的编程语言,也许你认为没有什么可以问的,只能够说明你对于Java了解的太浅了,几乎就是两个星期的节奏 ...

  2. 【51nod-1009】数字1的数量

    给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数.   例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5个1. Input 输入N( ...

  3. C# 设计模式巩固 - 工厂方法模式

    前言 实在编不出来了~ 介绍 - 工厂方法模式 官方定义:(下面摘自百度百科)工厂方法模式(FACTORY METHOD)是一种常用的对象创建型设计模式,此模式的核心精神是封装类中不变的部分,提取其中 ...

  4. 在关于原生js获取getAttribute如果是src的一点注意事项

    最近抽空把seajs看完了 他们在fix代码的时候,写明在某浏览器下的情况和官方文档出处,这样有据可查.太赞了 顺便把我想要这段摘出来 可以直接dom.src,但是在ie6-7中是不支持的,并且在ge ...

  5. ansible配置文件 ansible.cfg的一点说明

    ansible配置文件 ansible.cfg的一点说明 > ansible --version ansible 2.1.1.0 config file = /etc/ansible/ansib ...

  6. zabbix安装收获-WARNING: 'aclocal-1.14' is missing on your system

    zabbix server已经安装成功了,在server端也安装了一个agent,一切OK. 在另外一台pg节点上安装zabbix agent时,报错: WARNING: 'aclocal-1.14' ...

  7. http和https区别

    超文本传输协议 HTTP 协议被用于在 Web 浏览器和网站服务器之间传递信息,HTTP 协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了 Web 浏览器和网站服务器之间的传输报文, ...

  8. 《gradle 用户指南中文版》目录

    gradle 用户指南 版权所有©2007-2017 Hans Dockter,Adam Murdoch只要您不对这些副本收取任何费用,并且进一步规定,每个副本都包含本版权声明,无论是以印刷版还是电子 ...

  9. 2017~ROS暑期学校~分享

    http://www.robotics.sei.ecnu.edu.cn/ROS2017/ ---- 往年暑期学校活动:2015年,2016年 报名开始时间7月2日晚10点:暑期学校报名,机器人挑战赛报 ...

  10. 框架重构:测试中的DateTime.Now

    存在的问题 DateTime.Now是C#语言中获取计算机的当前时间的代码: 但是,在对使用了DateTime.Now的方法进行测试时,由于计算机时间的实时性,期望值一直在变化.如:计算年龄. pub ...