MP4V2版MP4文件生成 - 内存中的H264帧和AAC帧

ffmpeg保存MP4参见:https://blog.csdn.net/xinxinsky/article/details/88531524,代码:https://gitee.com/careye_open_source_platform_group/MP4MuxerTest/repository/archive/master.zip

从IPC采集H264帧和AAC帧,然后保存为MP4文件,直接上代码:

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#include 
#include 

const unsigned char RL_NALU_TYPE_NONE = -1;
const unsigned char RL_NALU_TYPE_SLICE = 0x01;
const unsigned char RL_NALU_TYPE_IDR = 0x05;
const unsigned char RL_NALU_TYPE_SEI = 0x06;
const unsigned char RL_NALU_TYPE_SPS = 0x07;
const unsigned char RL_NALU_TYPE_PPS = 0x08;
const unsigned char RL_NALU_TYPE_MASK = 0x1f;

//视频或者音画同步的主要参数是duration, 公式可以是 (当前帧录制时间-上一针录制时间)*90000/1000;
const int timeScale = 90000;

inline bool is_frame_delimeter(const uint8_t *data, int& len) {
    len = (0x00 == data[0] && 0x00 == data[1] && 0x01 == data[2]) ? 3 : 
        ((0x00 == data[0] && 0x00 == data[1] && 0x00 == data[2] && 0x01 == data[3]) ? 4 : 0);
    return(0 != len);
}

int CreateMP4File(MP4FileHandle& pHandle, const char *strFileName) {
    pHandle = MP4Create(strFileName, 0);
    if(pHandle == MP4_INVALID_FILE_HANDLE)
    {
		printf("ERROR:Create mp4 handle fialed.\n");
		return -1;
    }

    MP4SetTimeScale(pHandle, timeScale);

    return 0;
}


static int WriteSPS(MP4FileHandle pHandle, uint8_t *pNalu, MP4TrackId& videoId, int nNaluSize,
    int timeScale, int width, int height, int frameRate, int& addStream)
{
    /*printf("------------------------------------\n");
    printf("sps(%d)\n", nNaluSize);*/
    if (addStream)
    {
        videoId = MP4AddH264VideoTrack
                (pHandle, 
                timeScale,              // 一秒钟多少timescale
                timeScale/frameRate,    // 每个帧有多少个timescale
                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;
        }
        
        MP4SetVideoProfileLevel(pHandle, 0x7F);

        addStream = 0;
    }

    MP4AddH264SequenceParameterSet(pHandle, videoId, pNalu, nNaluSize);

    return 0;
}

    int addStream = 1;

int WriteVideoFrame(MP4FileHandle pHandle, MP4TrackId& videoId, int& addStream, uint8_t *pBuf, int nDataSize)
{
	int width = 640;  //这3个参数要从SPS中解析出来
	int height = 480;
	int frameRate = 25;

    int nSCPLen = 0;
    if(!is_frame_delimeter(pBuf, nSCPLen)) {
            printf("NALU error.\n");
            return -1;
    }

    assert(4 == nSCPLen);

    int nNaluSize = nDataSize - nSCPLen;
    unsigned char *pNalu = pBuf + nSCPLen;  //pNalu = pBuf+4;
    unsigned char naluType = pNalu[0] & RL_NALU_TYPE_MASK;

    switch (naluType)
    {
        case 0x07: // SPS
            if(0 != WriteSPS(pHandle, pNalu, videoId, nNaluSize,
                timeScale, width, height, frameRate, addStream)) {
                MP4Close(pHandle, 0);
                return -1;
            }

            break;
        
        case 0x08: // PPS
            //printf("pps(%d)\n", nNaluSize);
            MP4AddH264PictureParameterSet(pHandle, videoId, pNalu, nNaluSize);
            break;

        default:
            //printf("slice(%d)\n", nNaluSize);
            pBuf[0] = (nNaluSize>>24)&0xFF;
            pBuf[1] = (nNaluSize>>16)&0xFF;
            pBuf[2] = (nNaluSize>>8)&0xFF;
            pBuf[3] = (nNaluSize>>0)&0xFF;

            MP4WriteSample(pHandle, videoId, pBuf, nNaluSize + 4, MP4_INVALID_DURATION, 0, 1);

            break;
    }

    return 0;
}


int WriteAudioFrame(MP4FileHandle pHandle, MP4TrackId& audioId, uint8_t *buf, int nFrameSize ) {
    //去掉前面元数据头:7个字节或者9个字节
	const unsigned int nMetaLen = ((buf[1] & 0x01) == 1 ? 7 : 9);  //元数据长度

    const int H2 = buf[3] & 0x03;
	const int M8 = buf[4];
	const int L3 = (int(buf[5] & 0xE0)) >> 5;

    const int nTotalLen = (H2 << 9) | (M8 << 3) | L3;   //音频帧长度,包括ADTS头
    if(nTotalLen != int(nFrameSize)) {
		printf("Len error.\n");
	}

    if(MP4_INVALID_TRACK_ID == audioId) {//添加aac音频 
        const int nProfileOffset = 2;
        const uint8_t nProfileMask = 0xC0;
        const uint8_t nSampleRateMask = 0x3C;
        const uint32_t aSmapleRates[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000};
        printf("ADTS: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", 
    		buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], 
    		buf[6], buf[7], buf[8], buf[9]);

        //第2个字节的最高2位,profile, the MPEG-4 Audio Object Type minus 1
	    uint8_t nProfile = ((buf[nProfileOffset] & nProfileMask) >> 6) + 1;
    
        //MPEG-4 Sampling Frequency Index (15 is forbidden)
	    uint8_t nSampleRate = (buf[nProfileOffset] & nSampleRateMask) >> 2;

        //第3字节的最低位和第4字节的最高2位, MPEG-4 Channel Configuration (in the case of 0, 
        //the channel configuration is sent via an inband PCE)
	    uint8_t nChannel = ((buf[nProfileOffset] & 0x1) << 2) | ((buf[nProfileOffset + 1] & 0xc0) >> 6); 

	    printf("profile: %u, sample-rate: %u - %u, channels: %u\n", nProfile, nSampleRate, aSmapleRates[nSampleRate], nChannel);

	    /*
	    timeScale 采样率 16000 32000 44100
        sampleDuration  这个参数填写的是每一帧的字节数: sampleDuration * 1000 / timeScale = Duration Time 
        一帧的持续时间。
	    */
        audioId = MP4AddAudioTrack(pHandle, aSmapleRates[nSampleRate], 
            2048,    //MP4_INVALID_DURATION
            MP4_MPEG2_AAC_LC_AUDIO_TYPE);  //MP4_MPEG4_AUDIO_TYPE
        if (audioId == MP4_INVALID_TRACK_ID) 
        { 
            printf("add audio track failed.\n"); 
            return -2; 
        }

        MP4SetAudioProfileLevel(pHandle, nProfile); //0x2); 

        /*
        aacObjectType(5bits) -- 就是nProfile.
        sampleRateIdx(4bits),
        numChannels(4bits)
        */
        uint8_t aEsConfig[2] = {0};
        aEsConfig[0] = ((nProfile << 3) | (nSampleRate >> 1));
        aEsConfig[1] = ((nSampleRate & 0x01) << 7) | (nChannel << 3);
        printf("ES config: 0x%02x 0x%02x\n", aEsConfig[0], aEsConfig[1]);
        if(!MP4SetTrackESConfiguration(pHandle, audioId, aEsConfig, sizeof(aEsConfig))) {
            printf("Fail to ES configure.\n");
            return -4;
        }
    }

    bool bRet = MP4WriteSample(pHandle, audioId, buf + nMetaLen, 
        nFrameSize - nMetaLen , MP4_INVALID_DURATION, 0, 1);
    if(!bRet) {
        printf("Fail to write audio frame.\n"); 
        return -3;
    }

    return nFrameSize;
}

int main() {
	MP4FileHandle pHandle = NULL;
    int ret = CreateMP4File(pHandle, argv[3]);
    if(0 != ret) {
        printf("Fail to create MP4 file.\n");
        return 1;
    }

    //...
    while(1) {
   			nVideoRet = WriteVideoFrame(...);
            nAudioRet = WriteAudioFrame(...);
    }

	MP4Close(pHandle, 0);

    return 0;
}

你可能感兴趣的:(音视频)