h264和aac封装为MP4

该代码示例展示了在Linux系统中如何利用mp4v2开源库将h264和AAC编码的数据封装成MP4文件。mp4Pack.h和mp4Pack.cpp包含了创建、设置参数、写入H264和AAC数据以及关闭文件的相关函数。程序首先创建MP4文件,然后添加视频和音频轨道,处理并写入编码数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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, &param_v, &param_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文件数据-优快云博客

C/C++ H264文件解析-优快云博客

音频封装部分参考了:http://t.csdn.cn/MoWiV  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值