Linux下,把h264和aac封装为MP4,利用了新的mp4v2开源库。
mp4Pack.h
#ifndef _MP4PACK_H_
#define _MP4PACK_H_
#include <stdint.h>
#include <pthread.h>
#include "mp4v2/mp4v2.h"
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct mp4Pack_vp_t
{
uint32_t width; // 宽
uint32_t height; // 高
uint32_t frameRate; // 帧率
} mp4Pack_vp_t;
typedef struct mp4Pack_ap_t
{
uint8_t profile; // AAC profile
uint8_t sampleRateIndex; // 采样率索引
uint8_t channelNumber; // 声道数
} mp4Pack_ap_t;
typedef struct mp4Pack
{
mp4Pack_vp_t vParam;
mp4Pack_ap_t aParam;
MP4FileHandle filehandle;
MP4TrackId trackId_v;
MP4TrackId trackId_a;
bool spsFlag;
bool ppsFlag;
uint8_t *pbuf;
uint32_t bufSize;
pthread_mutex_t mutex;
} mp4Pack_t;
mp4Pack_t *mp4Pack_open(const char *fileName);
int mp4Pack_setParam(mp4Pack_t *handle, mp4Pack_vp_t *vParam, mp4Pack_ap_t *aParam, uint32_t bufSize);
int mp4Pack_writeH264(mp4Pack_t *handle, const uint8_t *pdata, uint32_t len);
int mp4Pack_writeAAC(mp4Pack_t *handle, const uint8_t *pdata, uint32_t len);
void mp4Pack_close(mp4Pack_t *handle);
#ifdef __cplusplus
}
#endif
#endif // _MP4PACK_H_
mp4Pack.cpp
#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_t *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_t *mp4Pack = (mp4Pack_t *)malloc(sizeof(mp4Pack_t));
memset(mp4Pack, 0, sizeof(mp4Pack_t));
mp4Pack->filehandle = filehandle;
pthread_mutex_init(&mp4Pack->mutex, NULL);
return mp4Pack;
}
int mp4Pack_setParam(mp4Pack_t *handle, mp4Pack_vp_t *vParam, mp4Pack_ap_t *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_t *handle, const uint8_t *pnalu, uint32_t len)
{
bool isIDR = false;
// 判断nual type
uint8_t nalu_type = pnalu[0] & 0x1f;
// printf("nalu type: %02X\n", nalu_type);
if (nalu_type == 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;
}
else if (nalu_type == 0x08)
{
// pps
if (!handle->ppsFlag)
{
MP4AddH264PictureParameterSet(handle->filehandle, handle->trackId_v, pnalu, len);
}
handle->ppsFlag = true;
}
else
{
if (nalu_type == 0x0C)
{
// 抛弃掉填充数据
return 0;
}
if (nalu_type == 0x05)
{
// 关键帧
isIDR = true;
// printf("IDR frame\n");
}
// 添加4字节长度
if (handle->bufSize < len + 4)
{
printf("err: buf size is too small\n");
return -1;
}
memset(handle->pbuf, 0, handle->bufSize);
handle->pbuf[0] = len >> 24;
handle->pbuf[1] = len >> 16;
handle->pbuf[2] = len >> 8;
handle->pbuf[3] = len;
memcpy(handle->pbuf + 4, pnalu, len);
// 当固定帧率处理,duration 参数设置为 MP4_INVALID_DURATION
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_t *handle, const uint8_t *pdata, uint32_t len)
{
int startCode_len;
pthread_mutex_lock(&handle->mutex);
startCode_len = check_startCode_len(pdata);
if (startCode_len == -1)
{
pthread_mutex_unlock(&handle->mutex);
return -1;
}
mp4Pack_writeNalu(handle, pdata + startCode_len, len - startCode_len);
pthread_mutex_unlock(&handle->mutex);
return 0;
}
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_t *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, 1) == false)
{
printf("err: write audio sample failed\n");
goto exit;
}
ret = 0;
exit:
pthread_mutex_unlock(&handle->mutex);
return ret;
}
void mp4Pack_close(mp4Pack_t *handle)
{
MP4Close(handle->filehandle, 0);
pthread_mutex_destroy(&handle->mutex);
free(handle->pbuf);
free(handle);
}
测试:
void main(void)
{
mp4Pack_t *handle;
mp4Pack_vp_t param_v;
mp4Pack_ap_t param_a;
H264Parse h264Parse;
AACParse aacParse;
uint8_t buf[1024 * 1024] = {0};
uint32_t naluLen = 0;
h264Parse.open_h264File("res/test.h264");
aacParse.open_aacFile("res/test.aac");
handle = mp4Pack_open("./111.mp4");
param_v.width = 1920;
param_v.height = 1080;
param_v.frameRate = 25;
aacParse.get_configInfo(¶m_a.profile, ¶m_a.sampleRateIndex, ¶m_a.channelNumber);
mp4Pack_setParam(handle, ¶m_v, ¶m_a, 1024 * 1024);
// 写视频
while (1)
{
int ret = h264Parse.get_nalu(buf, sizeof(buf), &naluLen, 1024 * 12);
if (ret != 1)
{
break;
}
mp4Pack_writeH264(handle, buf, naluLen);
}
mp4Pack_writeH264(handle, buf, naluLen);
// 写音频
while (1)
{
int ret = aacParse.get_adts(buf, sizeof(buf), &naluLen);
if (ret != 1)
{
break;
}
mp4Pack_writeAAC(handle, buf, naluLen);
}
mp4Pack_writeAAC(handle, buf, naluLen);
mp4Pack_close(handle);
}
音频封装部分参考了:http://t.csdn.cn/MoWiV