hi3518e之mp4v2--第三步使用mp4v2封装h264和aac

第一步

        由于MP4需要的语音为AAC编码格式,HI3518E可以给出PCM,所以需要将PCM转换成AAC编码。使用的工具为FAAC,具体移植和使用步骤见另一篇博客FAAC在HI3518E上移植_changwuyong的博客-CSDN博客

第二步

        本人使用的SDK里的sample是venc,需要将audio代码和faac代码加入到该sample中。

main函数

int main(int argc, char *argv[])

{

    HI_S32 s32Ret;

    global_step_record = 0;

    pthread_t key_ThreadId = 0;

    //最大输入的PCM数据量
    unsigned long inputSample = 0;
    //最大输出AAC的数据量                            
    unsigned long maxOutputBytes = 0;                         


    //由于音频发送采用环形缓存,初始化一段空间
    ringmalloc(8000*10);                                      

    //加入了按键控制,开始录制通过控制台,结束采用按键
    pthread_create(&key_ThreadId, NULL, scan_key_main, NULL);

    signal(SIGINT, SAMPLE_VENC_HandleSig);

    signal(SIGTERM, SAMPLE_VENC_HandleSig);


    //初始化faac,并获取inputSample和maxOutputBytes,我这里获取到的分别是1024和768
    encoder = faacEncOpen(8000 , 1 , &inputSample, &maxOutputBytes);

    config = faacEncGetCurrentConfiguration(encoder);

    config->aacObjectType = MAIN;

    config->mpegVersion = MPEG4;

    //config->useLfe = 1;

    config->useTns = 1;

    config->allowMidside = 1;

    config->outputFormat = 0;  // RAW_STREAM = 0, ADTS_STREAM 1

    config->inputFormat = FAAC_INPUT_16BIT;

    faacEncSetConfiguration(encoder, config);



    printf("nInputSamples = %d nMaxOutputBytes = %d\n",inputSample,maxOutputBytes);

    //音频的初始化部分在该函数中
    s32Ret = SAMPLE_VENC_1080P_CLASSIC();

    

    if (HI_SUCCESS == s32Ret)

        printf("program exit normally!\n");

    else

        printf("program exit abnormally!\n");

    //使用完毕后关闭faac的handle
    faacEncClose(encoder);

    ringfree();

    exit(s32Ret);

}

 在SAMPLE_VENC_1080P_CLASSIC函数中初始化音频

    /******************************************

     step 2: mpp system init. 

    ******************************************/

    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);

    if (HI_SUCCESS != s32Ret)

    {

        SAMPLE_PRT("system init failed with %d!\n", s32Ret);

        goto END_VENC_1080P_CLASSIC_0;

    }


    //语音初始化,mpp初始化完成之后,才能调用该函数,否则会报错。
    SAMPLE_AUDIO_AiAenc();

获取到视频帧之后,进入mp4处理和存储函数

//获取到视频帧
s32Ret = HI_MPI_VENC_GetStream(i, &stStream, HI_TRUE);

if (HI_SUCCESS != s32Ret)

{

    free(stStream.pstPack);

    stStream.pstPack = NULL;

    SAMPLE_PRT("HI_MPI_VENC_GetStream failed with %#x!\n", s32Ret);

    break;

}

                    


//从环形缓冲区拉去音频流
ringbuflen = ringget(&ringinfo);
//进行mp4数据的处理和存储
SAMPLE_COMM_VENC_MP4(&stStream,ringinfo);

SAMPLE_COMM_VENC_MP4函数如下:

HI_S32 SAMPLE_COMM_VENC_MP4(VENC_STREAM_S *stStream, struct ringbuf ringinfo)

{

	static int nRecordFlag = 0x00;

	static int recording = 0x1;

	static int spsflag = 0;

	static int ppsflag = 0;

	static MP4TrackId video = 0;

    static MP4TrackId audio = 0;

	static MP4FileHandle hMP4File = NULL;

    static int m_nTempPos = 0;

    const  int m_nMaxInputBytes = 2048;

    const  int m_nInputSamples = 1024;

    const  int m_nMaxOutputBytes = 768;

	static char recordfish = 0x1;

    static unsigned char m_pbPCMBuffer[4096];

    static unsigned char szTemp[2048];

    static unsigned char m_pTempBuffer[4096];

    static unsigned char m_pOutAACBuffer[768];



    int buflen=0,ringbuflen=0,ringbuftype;

	

	int j = 0;

	int len = 0;

	char *pData = NULL;

	char isSyncSample = 0;



    static long long cur_time = 0;

    static long long pre_time = 0;



//    printf("type = %d size = %d\n",ringinfo.frame_type,ringinfo.size);



    cur_time = getSystemTime();



	if(recordfish == 0x00){

		return 0;

	}



	if(hMP4File == NULL){

		hMP4File = MP4CreateEx("test.mp4",0, 1, 1, 0, 0, 0, 0);			//文件存储路径

		if (hMP4File == MP4_INVALID_FILE_HANDLE)	{

			printf("open file fialed.\n");

			return -1;

		}



		MP4SetTimeScale(hMP4File, 90000);


        //添加一个AudioTrack
        audio = MP4AddAudioTrack(hMP4File, 8000, 1024, MP4_MPEG4_AUDIO_TYPE); //MP4_MPEG2_AAC_LC_AUDIO_TYPE MP4_MPEG4_AUDIO_TYPE MP4_MPEG2_AAC_AUDIO_TYPE,MP4_MPEG4_AAC_MAIN_AUDIO_TYPE

        if ( audio == MP4_INVALID_TRACK_ID)

        {

            printf("audio error \n");

            return;

        }

        MP4SetAudioProfileLevel(hMP4File, 0x2 );   0x2 ?

	}

	

	if(recording && stStream->u32Seq > 30){			//丢弃前30帧,也可以不丢弃

		if(stStream->u32PackCount >= 3){			//从I帧开始编码,保证文件开始就能播放

            if(nRecordFlag == 0)

            {
                //如果开始存储,则清空一下ring缓冲区
                ringreset();

            }

			nRecordFlag = 1;

		}

	

		if(nRecordFlag){

#if 1
            //该段代码是处理音频数据的,ringinfo包含音频PCM数据
            if(ringinfo.size > 0)

            {

//                printf("ringinfo.size = %d\n",ringinfo.size);

                //此段代码是为了将pcm数据拼接成2048个
                memcpy(m_pTempBuffer+m_nTempPos, ringinfo.buffer ,ringinfo.size ) ;
                
                m_nTempPos += ringinfo.size;

                if ( m_nTempPos >= m_nMaxInputBytes )

                {

                    memcpy(m_pbPCMBuffer, m_pTempBuffer ,m_nMaxInputBytes ) ;

                    int nLeft = m_nTempPos-m_nMaxInputBytes;

                    memcpy( szTemp, (m_pTempBuffer+m_nMaxInputBytes), nLeft );

                    memset(m_pTempBuffer, 0, 2048 );

                    memcpy( m_pTempBuffer, szTemp, nLeft );

                    m_nTempPos -= m_nMaxInputBytes ;
                    //faacEncEncode就是将pcm数据转成aac数据
                    int nRet = faacEncEncode(encoder, (int*)m_pbPCMBuffer, m_nInputSamples, m_pOutAACBuffer, m_nMaxOutputBytes );

                    if ( nRet > 0 )

                    {

                        printf("nRet = %d\n",nRet);

                        //if(MP4WriteSample(hMP4File , audio, m_pOutAACBuffer,nRet, MP4_INVALID_DURATION, 0, 1) != true)

                        //printf("(cur_time - pre_time) * 90000 / 1000 = %lld\n",(cur_time - pre_time) * 90000 / 1000);
                        //将aac数据写入mp4文件
                        if(MP4WriteSample(hMP4File , audio, m_pOutAACBuffer,nRet, (cur_time - pre_time) * 90000 / 1000, 0, 1) != true)

                        {

                            printf("MP4WriteSample error \n");

                        }

                    }

                }

            }

#endif
            //以下代码是封装h264到mp4的代码
			for(j = 0;j < stStream->u32PackCount;j++){

				len 	= stStream->pstPack[j].u32Len - stStream->pstPack[j].u32Offset;

				pData	= (stStream->pstPack[j].pu8Addr + stStream->pstPack[j].u32Offset);						

	

				if(stStream->pstPack[j].DataType.enH264EType == H264E_NALU_SPS){

					if(spsflag == 0x00){

						spsflag = 0x1;

						//写sps

						printf("Write sps =================\n");	

	

	

						video = MP4AddH264VideoTrack(hMP4File, 90000, 90000 / 25, 1280, 720,

																pData[4+1], //sps[1] AVCProfileIndication

																pData[4+2], //sps[2] profile_compat

																pData[4+3], //sps[3] AVCLevelIndication

																3); // 4 bytes length before each NAL unit

							MP4SetVideoProfileLevel(hMP4File, 0x7F);

							MP4AddH264SequenceParameterSet(hMP4File, video, pData+4, len-4);										

					}

					

					continue;

				}

				

				if(stStream->pstPack[j].DataType.enH264EType == H264E_NALU_PPS){

					if(ppsflag == 0x00){

						ppsflag = 0x1;

						//写pps									

						printf("Write pps -------------------\n");										

						MP4AddH264PictureParameterSet(hMP4File, video, pData+4, len-4);

					}

					

					continue;

				}

	

				isSyncSample = (stStream->pstPack[j].DataType.enH264EType == H264E_NALU_ISLICE)	?  (1) : (0);

				pData[0] = (len - 4) >> 24;

				pData[1] = (len - 4) >> 16;

				pData[2] = (len - 4) >> 8;

				pData[3] = len - 4; 							

	

//				printf("Write date type = %d  isSyncSample = %d\n",stStream->pstPack[j].DataType.enH264EType,isSyncSample);								

	

				MP4WriteSample(hMP4File, video, pData, len , MP4_INVALID_DURATION, 0, isSyncSample);

	

				

			}					

		}

	}



	pre_time = cur_time;



	if((recording && global_step_record != 0)){		//控制文件时长

		recording = 0x00;

		printf("Close mp4 file\n");						

		MP4Close(hMP4File, 0);

		hMP4File = NULL;

		video = 0;

		recordfish = 0x00;

		global_step_record = 2;

	}



}

以下代码为音频获取到pcm数据后的操作:

            if (HI_TRUE == pstAencCtl->bSendAdChn)

            {

                s32Ret = HI_MPI_ADEC_SendStream(pstAencCtl->AdChn, &stStream, HI_TRUE);

                if (HI_SUCCESS != s32Ret )

                {

                    printf("%s: HI_MPI_ADEC_SendStream(%d), failed with %#x!\n",\

                           __FUNCTION__, pstAencCtl->AdChn, s32Ret);

                    pstAencCtl->bStart = HI_FALSE;

                    return NULL;

                }

            }
            //将pcm数据放入ring缓冲区,0x03只是一个标志,没啥用
            ringput(stStream.pStream,stStream.u32Len,0x03);

第三步,编译后运行。

        运行结果是mp4文件有完整的音频和视频,但是一旦录制时间长了以后,音频和视频产生不同步的现象,试过很多方法,依然不行。曾试着将MP4AddAudioTrack(hMP4File, 8000, 1024, MP4_MPEG4_AUDIO_TYPE)里的1024改成1056,录制半个小时依然同步,但是隔几秒声音会有一些卡顿。

        目前,音视频的同步问题还在寻找,但是不想卡死在这里,先做其他功能,如果有解决方案后,再放出方法。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值