Linux下,把h264和aac封装为MP4,利用了新的mp4v2开源库。
mp4Pack.h
#ifndef _MP4PACK_H_
#define _MP4PACK_H_
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include <pthread.h>
#include "mp4v2/mp4v2.h"
typedef struct MP4PackVParams
{
uint32_t width; // 宽
uint32_t height; // 高
uint32_t frameRate; // 帧率
} MP4PackVParams;
typedef struct MP4PackAParams
{
uint8_t profile; // AAC profile
uint8_t sampleRateIndex; // 采样率索引
uint8_t channelNumber; // 声道数
} MP4PackAParams;
typedef struct MP4Pack
{
MP4PackVParams vParam;
MP4PackAParams aParam;
MP4FileHandle filehandle;
MP4TrackId trackId_v;
MP4TrackId trackId_a;
bool spsFlag;
bool ppsFlag;
uint8_t *pbuf;
uint32_t bufSize;
pthread_mutex_t mutex;
} MP4Pack;
MP4Pack *MP4Pack_open(const char *fileName);
int MP4Pack_setParam(MP4Pack *handle, MP4PackVParams *vParam, MP4PackAParams *aParam, uint32_t bufSize);
int MP4Pack_writeH264(MP4Pack *handle, const uint8_t *pdata, uint32_t len);
int MP4Pack_writeAAC(MP4Pack *handle, const uint8_t *pdata, uint32_t len);
void MP4Pack_close(MP4Pack *handle);
#ifdef __cplusplus
}
#endif
#endif // _MP4PACK_H_
mp4Pack.c
#include "mp4Pack.h"
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
static const uint32_t sampling_frequency_set[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000, 7350};
MP4Pack *MP4Pack_open(const char *fileName)
{
MP4FileHandle filehandle = MP4Create(fileName, 0);
if (filehandle == MP4_INVALID_FILE_HANDLE)
{
printf("err: create mp4 file failed\n");
return NULL;
}
MP4SetTimeScale(filehandle, 90000);
MP4Pack *mp4PackHandle = (MP4Pack *)malloc(sizeof(MP4Pack));
memset(mp4PackHandle, 0, sizeof(MP4Pack));
mp4PackHandle->filehandle = filehandle;
pthread_mutex_init(&mp4PackHandle->mutex, NULL);
return mp4PackHandle;
}
int MP4Pack_setParam(MP4Pack *handle, MP4PackVParams *vParam, MP4PackAParams *aParam, uint32_t bufSize)
{
handle->vParam = *vParam;
handle->aParam = *aParam;
handle->pbuf = (uint8_t *)malloc(bufSize);
if (handle->pbuf == NULL)
{
printf("err: malloc buf failed\n");
return -1;
}
memset(handle->pbuf, 0, handle->bufSize);
handle->bufSize = bufSize;
return 0;
}
static int check_startCode_len(const uint8_t *pnalu)
{
int startCode_len = -1;
if (pnalu[0] == 0x00 && pnalu[1] == 0x00 && pnalu[2] == 0x00 && pnalu[3] == 0x01)
{
startCode_len = 4;
}
else if (pnalu[0] == 0x00 && pnalu[1] == 0x00 && pnalu[2] == 0x01)
{
startCode_len = 3;
}
return startCode_len;
}
static int MP4Pack_writeNalu(MP4Pack *handle, const uint8_t *pnalu, uint32_t len)
{
// 获取NALU类型
uint8_t nalu_type = pnalu[0] & 0x1f;
// printf("nalu type: %02X\n", nalu_type);
switch (nalu_type)
{
case 0x06: // SEI
case 0x09: // AUD
case 0x0C: // Filler Data
return 0; // 忽略,不写入
case 0x07: // SPS
if (!handle->spsFlag)
{
// 创建视频轨道
handle->trackId_v = MP4AddH264VideoTrack(handle->filehandle, 90000, 90000 / handle->vParam.frameRate,
handle->vParam.width, handle->vParam.height,
pnalu[1], pnalu[2], pnalu[3], 3);
if (handle->trackId_v == MP4_INVALID_TRACK_ID)
{
printf("err: add h264 video track failed\n");
return -1;
}
MP4SetVideoProfileLevel(handle->filehandle, 0x7F);
MP4AddH264SequenceParameterSet(handle->filehandle, handle->trackId_v, pnalu, len);
handle->spsFlag = true;
}
return 0;
case 0x08: // PPS
if (!handle->ppsFlag && handle->trackId_v != MP4_INVALID_TRACK_ID)
{
MP4AddH264PictureParameterSet(handle->filehandle, handle->trackId_v, pnalu, len);
handle->ppsFlag = true;
}
return 0;
default:
// 确保视频轨道和SPS/PPS已经设置
if (handle->trackId_v != MP4_INVALID_TRACK_ID && handle->spsFlag && handle->ppsFlag)
{
// 判断是否为IDR帧
bool isIDR = (nalu_type == 0x05);
// 检查缓冲区大小
if (handle->bufSize < len + 4)
{
printf("err: buf size is too small\n");
return -1;
}
// 写入长度前缀
handle->pbuf[0] = len >> 24;
handle->pbuf[1] = len >> 16;
handle->pbuf[2] = len >> 8;
handle->pbuf[3] = len;
// 复制NALU数据
memcpy(handle->pbuf + 4, pnalu, len);
// 写入视频帧
if (MP4WriteSample(handle->filehandle, handle->trackId_v, handle->pbuf, len + 4,
MP4_INVALID_DURATION, 0, isIDR) == false)
{
printf("err: write video sample failed\n");
return -1;
}
}
return 0;
}
}
int MP4Pack_writeH264(MP4Pack *handle, const uint8_t *pdata, uint32_t len)
{
pthread_mutex_lock(&handle->mutex);
int startCode_len = check_startCode_len(pdata);
if (startCode_len == -1)
{
pthread_mutex_unlock(&handle->mutex);
return -1;
}
int ret = MP4Pack_writeNalu(handle, pdata + startCode_len, len - startCode_len);
pthread_mutex_unlock(&handle->mutex);
return ret;
}
static uint16_t getDecoderSpecificInfo(uint8_t audioObjectType, uint8_t sampleRateIndex, uint8_t channelNumber)
{
uint16_t decoderSpecificInfo = 0;
uint8_t *p = (uint8_t *)&decoderSpecificInfo;
p[0] = ((audioObjectType << 3) & 0xf8) | ((sampleRateIndex >> 1) & 0x07);
p[1] = ((sampleRateIndex << 7) & 0x80) | ((channelNumber << 3) & 0x70);
return decoderSpecificInfo;
}
int MP4Pack_writeAAC(MP4Pack *handle, const uint8_t *pdata, uint32_t len)
{
int ret = -1;
int head_len = 7;
pthread_mutex_lock(&handle->mutex);
// 检查同步字节
if (pdata[0] != 0xFF || (pdata[1] & 0xF0) != 0xF0)
{
printf("err: aac syncword error\n");
goto exit;
}
// 检查 protection_absent
if ((pdata[1] & 0x01) == 0)
{
head_len = 9;
}
if (handle->trackId_a == 0)
{
handle->trackId_a = MP4AddAudioTrack(handle->filehandle, sampling_frequency_set[handle->aParam.sampleRateIndex],
1024, MP4_MPEG4_AUDIO_TYPE);
if (handle->trackId_a == MP4_INVALID_TRACK_ID)
{
printf("err: add audio track failed\n");
goto exit;
}
MP4SetAudioProfileLevel(handle->filehandle, 0x2);
uint16_t info = getDecoderSpecificInfo(handle->aParam.profile + 1, handle->aParam.sampleRateIndex,
handle->aParam.channelNumber);
MP4SetTrackESConfiguration(handle->filehandle, handle->trackId_a, (uint8_t *)&info, 2);
}
if (MP4WriteSample(handle->filehandle, handle->trackId_a, pdata + head_len, len - head_len,
MP4_INVALID_DURATION, 0, true) == false)
{
printf("err: write audio sample failed\n");
goto exit;
}
ret = 0;
exit:
pthread_mutex_unlock(&handle->mutex);
return ret;
}
void MP4Pack_close(MP4Pack *handle)
{
MP4Close(handle->filehandle, 0);
pthread_mutex_destroy(&handle->mutex);
free(handle->pbuf);
free(handle);
}
测试(main.cpp):
#include "aacParse.h"
#include "h264Parse.h"
#include "mp4Pack.h"
int main(void)
{
MP4Pack *handle;
MP4PackVParams param_v;
MP4PackAParams param_a;
H264Parse h264Parse;
AACParse aacParse;
uint8_t buf[1024 * 1024] = {0};
uint32_t len = 0;
h264Parse.open_file("res/output.h264");
aacParse.open_file("res/output.aac");
handle = MP4Pack_open("./111.mp4");
param_v.width = 1280;
param_v.height = 720;
param_v.frameRate = 30;
aacParse.get_configInfo(param_a.profile, param_a.sampleRateIndex, param_a.channelNumber);
MP4Pack_setParam(handle, ¶m_v, ¶m_a, 1024 * 1024);
// 写视频
while (1)
{
int ret = h264Parse.read_nalu(buf, sizeof(buf), len, 1024 * 12);
if (ret != 1)
{
break;
}
MP4Pack_writeH264(handle, buf, len);
}
MP4Pack_writeH264(handle, buf, len);
// 写音频
while (1)
{
int ret = aacParse.get_adts(buf, sizeof(buf), len);
if (ret != 1)
{
break;
}
MP4Pack_writeAAC(handle, buf, len);
}
MP4Pack_writeAAC(handle, buf, len);
MP4Pack_close(handle);
return 0;
}
测试代码部分用到的aac、h264文件解析源码:
C/C++ AAC文件解析_c++ 读取aac文件数据-优快云博客
音频封装部分参考了:http://t.csdn.cn/MoWiV