h264和aac封装为MP4

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

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(&param_a.profile, &param_a.sampleRateIndex, &param_a.channelNumber);
    mp4Pack_setParam(handle, &param_v, &param_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 

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值