T31开发笔记:MP4录制

若该文为原创文章,转载请注明原文出处

使用MP4V2把H264和AAC文件封装成MP4文件。

一、硬件和开发环境

1、硬件:T31X+SC5235 

2、开发环境: ubuntu16.04-64bit

3、编译器:mips-gcc540-glibc222-32bit-r3.3.0.tar.gz

注:板子和和WIFI模块是某淘上淘的,使用的是RTL8188,使用的是USB接口,uboot和内核是自己裁剪移植的,内核默认自带WIFI驱动,所以不用移植可以直接使用。

二、MP4V2下载及编译

1、MP4V2下载

mp4v2官网连接: https://launchpad.net/ubuntu/artful/+source/mp4v2

 

 2、MP4V2编译

1、解压:
tar xvf mp4v2_2.0.0_dfsg0.orig.tar.bz2
2、进入文件:
cd mp4v2-2.0.0
3、创建install文件
mkdir install
3、配置
./configure --host=mips-linux CC=ips-linux-gnu-gcc CXX=mips-linux-gnu-g++ --prefix=/home/yifeng/mp4v2-2.0.0/install --disable-option-checking --disable-debug --disable-optimize --disable-fvisibility --disable-gch --disable-largefile --disable-util --disable-dependency-tracking --disable-libtool-lock --enable-shared --enable-static
4、编译
make 
5、安装
make install

编译完成后,会在install生成lib文件,扡生成的动态库拷贝到开发板的/usr/lib下。

至此:编译完成。

三、测试

说明:把h264和aac文件封装成MP4文件,不是实时采集h264和aac。

完整代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mp4v2/mp4v2.h>

struct AdtsHeader
{
  unsigned int syncword;  //12 bit 同步字 '1111 1111 1111',说明一个ADTS帧的开始
  unsigned int id;        //1 bit MPEG 标示符, 0 for MPEG-4,1 for MPEG-2
  unsigned int layer;     //2 bit 总是'00'
  unsigned int protectionAbsent;  //1 bit 1表示没有crc,0表示有crc
  unsigned int profile;           //1 bit 表示使用哪个级别的AAC
  unsigned int samplingFreqIndex; //4 bit 表示使用的采样频率
  unsigned int privateBit;        //1 bit
  unsigned int channelCfg; //3 bit 表示声道数
  unsigned int originalCopy;         //1 bit 
  unsigned int home;                  //1 bit 

  /*下面的为改变的参数即每一帧都不同*/
  unsigned int copyrightIdentificationBit;   //1 bit
  unsigned int copyrightIdentificationStart; //1 bit
  unsigned int aacFrameLength;               //13 bit 一个ADTS帧的长度包括ADTS头和AAC原始流
  unsigned int adtsBufferFullness;           //11 bit 0x7FF 说明是码率可变的码流

  /* number_of_raw_data_blocks_in_frame
   * 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧
   * 所以说number_of_raw_data_blocks_in_frame == 0 
   * 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
   */
  unsigned int numberOfRawDataBlockInFrame; //2 bit
};

static int parseAdtsHeader(uint8_t* in, struct AdtsHeader* res)
{
  static int frame_number = 0;
  memset(res,0,sizeof(*res));

  if ((in[0] == 0xFF)&&((in[1] & 0xF0) == 0xF0))
  {
    res->id = ((unsigned int) in[1] & 0x08) >> 3;
    res->layer = ((unsigned int) in[1] & 0x06) >> 1;
    res->protectionAbsent = (unsigned int) in[1] & 0x01;
    res->profile = ((unsigned int) in[2] & 0xc0) >> 6;
    res->samplingFreqIndex = ((unsigned int) in[2] & 0x3c) >> 2;
    res->privateBit = ((unsigned int) in[2] & 0x02) >> 1;
    res->channelCfg = ((((unsigned int) in[2] & 0x01) << 2) | (((unsigned int) in[3] & 0xc0) >> 6));
    res->originalCopy = ((unsigned int) in[3] & 0x20) >> 5;
    res->home = ((unsigned int) in[3] & 0x10) >> 4;
    res->copyrightIdentificationBit = ((unsigned int) in[3] & 0x08) >> 3;
    res->copyrightIdentificationStart = (unsigned int) in[3] & 0x04 >> 2;
    res->aacFrameLength = (((((unsigned int) in[3]) & 0x03) << 11) |
                            (((unsigned int)in[4] & 0xFF) << 3) |
                                ((unsigned int)in[5] & 0xE0) >> 5) ;
    res->adtsBufferFullness = (((unsigned int) in[5] & 0x1f) << 6 |
                                    ((unsigned int) in[6] & 0xfc) >> 2);
    res->numberOfRawDataBlockInFrame = ((unsigned int) in[6] & 0x03);

    return 0;
  }
  else
  {
    printf("failed to parse adts header\n");
    return -1;
  }
}


int getNalu(FILE *pFile, unsigned char *pNalu)
{
  unsigned char c;
  int pos = 0;
  int len;

  if(!pFile)
    return -1;

  if((len = fread(pNalu, 1, 4, pFile)) <= 0)
    return -1;

  if(pNalu[0] != 0 || pNalu[1] != 0 || pNalu[2] != 0 || pNalu[3] != 1)
    return -1;

  pos = 4;

  while(1)
  {
    if(feof(pFile))
        break;

    pNalu[pos] = fgetc(pFile);
    
    if(pNalu[pos-3] == 0 && pNalu[pos-2] == 0 && pNalu[pos-1] == 0 && pNalu[pos] == 1)
    {
      fseek(pFile, -4, SEEK_CUR);
      pos -= 4;
      break;
    }

    pos++;
  }

  len = pos+1;

  return len;
}



static int dump_frame(uint8_t* p_frame, uint32_t size)
{
    printf("*********************************************************:%u\n", size);
    if(p_frame != NULL && size >0)
    {
        uint32_t i=0;
        for(; i<100; i++)
        {
            printf("%x ", p_frame[i]);

            if((i+1)%16 == 0)
            {
                printf("\n");
            }
        }
    }
    printf("\n");
}


int packet2Mp4(const char *inputh264File, const char *inputaacFile, const char *outputFiles)
  {
  FILE *pInh264 = NULL;
  FILE *pInAac = NULL;
  unsigned char *pBuf = malloc(1024*1024);
  unsigned char *frame = malloc(5000);
  unsigned char *pNalu = NULL;
  unsigned char naluType;
  int len;
  int num = 0;
  MP4FileHandle pHandle = NULL;
  MP4TrackId videoId;
  MP4TrackId audioId;
  int width = 480;
  int height = 288;
  int frameRate = 25;
  int timeScale = 90000;
  int addStream = 1;
  int ret = 0;
  struct AdtsHeader adtsHeader;
  int videoCnt = 0;
  int audioCnt = 0;
  
  pInh264 = fopen(inputh264File, "rb");
  if(!pInh264)
    return -1;

    
  pInAac = fopen(inputaacFile, "rb");
  if(!pInAac)
    return -1;
  
  pHandle = MP4Create(outputFiles, 0);
  if(pHandle == MP4_INVALID_FILE_HANDLE)
  {
    printf("ERROR:Create mp4 handle fialed.\n");
    return -1;
  }
  
  MP4SetTimeScale(pHandle, timeScale);


  // MP4_MPEG4_AAC_MAIN_AUDIO_TYPE
  audioId = MP4AddAudioTrack(pHandle, 48000, 1024, MP4_MPEG4_AUDIO_TYPE);
  if (audioId == MP4_INVALID_TRACK_ID)
  {
      printf("add audio track fialed.\n");
      return -1;
  } 
  MP4SetAudioProfileLevel(pHandle, 0x2);
  
  
  while(1)
  {
    videoCnt++;
    audioCnt++;

    if(videoCnt >= 40)
    {
      videoCnt = 0;
      /* 视频处理 */
      len = getNalu(pInh264, pBuf);
      if (len <= 0)
      break;
      
      if (pBuf[0] != 0 || pBuf[1] != 0 || pBuf[2] != 0 || pBuf[3] != 1)
      continue;
  
      
      len -= 4;
      pNalu = pBuf+4;
      naluType = pNalu[0]&0x1F;
      
      switch (naluType)
      {
        case 0x07: // SPS
          printf("------------------------------------\n");
          printf("sps(%d)\n", len);
          if (addStream)
          {
            videoId = MP4AddH264VideoTrack
                    (pHandle, 
                    timeScale,              // 涓€绉掗挓澶氬皯timescale
                    timeScale/frameRate,    // 姣忎釜甯ф湁澶氬皯涓猼imescale
                    width,                  // width
                    height,                 // height
                    pNalu[1],               // sps[1] AVCProfileIndication
                    pNalu[2],               // sps[2] profile_compat
                    pNalu[3],               // sps[3] AVCLevelIndication
                    3);                     // 4 bytes length before each NAL unit
            if (videoId == MP4_INVALID_TRACK_ID)
            {
              printf("Error:Can't add track.\n");
              return -1;
            }
            printf("pNalu[1] = %x, pNalu[2] = %x, pNalu[3] = %x\n", pNalu[1], pNalu[2], pNalu[3]);
  
            MP4SetVideoProfileLevel(pHandle, 0x7F);
        
            addStream = 0;
          }
          dump_frame(pNalu, len);
          MP4AddH264SequenceParameterSet(pHandle, videoId, pNalu, len);
          
          break;
        
        case 0x08: // PPS
          printf("pps(%d)\n", len);
          dump_frame(pNalu, len);
          MP4AddH264PictureParameterSet(pHandle, videoId, pNalu, len);
          break;
        
        default:
          printf("slice(%d)\n", len);
          pBuf[0] = (len>>24)&0xFF;
          pBuf[1] = (len>>16)&0xFF;
          pBuf[2] = (len>>8)&0xFF;
          pBuf[3] = (len>>0)&0xFF;
          dump_frame(pBuf, len);
          MP4WriteSample(pHandle, videoId, pBuf, len+4, MP4_INVALID_DURATION, 0, 1);
        
          break;
      }
    }

    if(audioCnt >= 21)
    {
      audioCnt = 0;
      /* 音频处理 */
      ret = fread(frame, 1, 7, pInAac);
      if(ret <= 0)
      {
        break;
      }
      if(parseAdtsHeader(frame, &adtsHeader) < 0)
      {
        printf("parse err\n");
        break;
      }
      ret = fread(frame, 1, adtsHeader.aacFrameLength-7, pInAac);
      if(ret < 0)
      {
        printf("read err\n");
        break;
      }
      printf("audio frame:\n");
      dump_frame(frame, len);
      MP4WriteSample(pHandle, audioId, frame, adtsHeader.aacFrameLength-7 , MP4_INVALID_DURATION, 0, 1);
    }
  }
  
  free(pBuf);
  free(frame);
  fclose(pInh264);
  fclose(pInAac);
  MP4Close(pHandle, 0);
  
  return 0;
}

int main(int argc, char *argv[])
{
    if (packet2Mp4("output.h264", "output.aac", "test.mp4"))
    {
        printf("Error:Packet to Mp4 fail.\n");
        return -1;
    }

    return 0;
}

编译:

mips-linux-gnu-gcc h264_aac_to_mp4.c -o h264_aac_to_mp4 -lpmv4p2

把可执行文件和h264,aac文件拷贝到开发板下运行,生成的mp4可以在vlc上播放。

 

四、总结

1、mpv2库交叉编译。

2、使用mpv2静态库,执行文件太大,建议使用动态库。

3、MP4格式学习。

如有侵权,请及时联系博主删除,VX:18750903063


版权声明:本文为weixin_38807927原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。