若该文为原创文章,转载请注明原文出处
使用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版权协议,转载请附上原文出处链接和本声明。