海思音视频流合成mp4文件 | 您所在的位置:网站首页 › ffmpeg缓存队列 › 海思音视频流合成mp4文件 |
海思SDK只提供了将视频文件合成MP4的例子,SDK中音频和视频流相关例子是分开的,如果需要将音频和视频同步合成MP4文件需要先将编码的音视频流送人合成缓存队列,然后从调用ffmpeg库接口从队列中读取音视频流进行MP4合成。ps:实现过程中参考了很多雷神的博客,在此感谢雷神。 MP4合成库文件下载路径: https://download.csdn.net/download/luky_zhou123/20084201 下载该库后编译移植到海思,然后用以下方式进行调用。 1、将海思编码后的音频流存入合成队,修改sample_audio.c文件中SAMPLE_COMM_AUDIO_AencProc函数如下: void* SAMPLE_COMM_AUDIO_AencProc(void* parg) { HI_S32 s32Ret; HI_S32 AencFd; SAMPLE_AENC_S* pstAencCtl = (SAMPLE_AENC_S*)parg; AUDIO_STREAM_S stStream; fd_set read_fds; struct timeval TimeoutVal; int ch = 0; FD_ZERO(&read_fds); AencFd = HI_MPI_AENC_GetFd(pstAencCtl->AeChn); FD_SET(AencFd, &read_fds); while (pstAencCtl->bStart) { TimeoutVal.tv_sec = 5; TimeoutVal.tv_usec = 0; FD_ZERO(&read_fds); FD_SET(AencFd, &read_fds); s32Ret = select(AencFd + 1, &read_fds, NULL, NULL, &TimeoutVal); if (s32Ret < 0) { break; } else if (0 == s32Ret) { printf("%s: get aenc stream select time out\n", __FUNCTION__); //break; continue; } if (FD_ISSET(AencFd, &read_fds)) { /* get stream from aenc chn */ s32Ret = HI_MPI_AENC_GetStream(pstAencCtl->AeChn, &stStream, HI_FALSE); if (HI_SUCCESS != s32Ret ) { printf("%s: HI_MPI_AENC_GetStream(%d), failed with %#x!\n", \ __FUNCTION__, pstAencCtl->AeChn, s32Ret); pstAencCtl->bStart = HI_FALSE; return NULL; } /* send stream to decoder and play for testing */ 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; } } /* save audio stream to file */ //printf("AppendAudio2Queue append data>>>>>>>>>>>>>>>>\n"); for(ch = 0; ch < MEDIA_CHAN_CNT; ch++) { if(AppendAudio2Queue(stStream.pStream, stStream.u32Len,ch)) { printf("AppendAudio2Queue error\n"); } } //(HI_VOID)fwrite(stStream.pStream, 1, stStream.u32Len, pstAencCtl->pfd); //fflush(pstAencCtl->pfd); /* finally you must release the stream */ s32Ret = HI_MPI_AENC_ReleaseStream(pstAencCtl->AeChn, &stStream); if (HI_SUCCESS != s32Ret ) { printf("%s: HI_MPI_AENC_ReleaseStream(%d), failed with %#x!\n", \ __FUNCTION__, pstAencCtl->AeChn, s32Ret); pstAencCtl->bStart = HI_FALSE; return NULL; } } } fclose(pstAencCtl->pfd); pstAencCtl->bStart = HI_FALSE; return NULL; }2、将海思编码后的视频流存入队列修改sample_comm_venc.c文件中SAMPLE_COMM_VENC_GetVencStreamProc函数如下: HI_VOID* SAMPLE_COMM_VENC_GetVencStreamProc(HI_VOID* p) { HI_S32 i,j; HI_S32 s32ChnTotal; VENC_CHN_ATTR_S stVencChnAttr; SAMPLE_VENC_GETSTREAM_PARA_S* pstPara; HI_S32 maxfd = 0; struct timeval TimeoutVal; fd_set read_fds; HI_U32 u32PictureCnt[VENC_MAX_CHN_NUM]={0}; HI_S32 VencFd[VENC_MAX_CHN_NUM]; HI_CHAR aszFileName[VENC_MAX_CHN_NUM][64]; FILE* pFile[VENC_MAX_CHN_NUM]; char szFilePostfix[10]; VENC_CHN_STATUS_S stStat; VENC_STREAM_S stStream; HI_S32 s32Ret; VENC_CHN VencChn; PAYLOAD_TYPE_E enPayLoadType[VENC_MAX_CHN_NUM]; VENC_STREAM_BUF_INFO_S stStreamBufInfo[VENC_MAX_CHN_NUM]; unsigned char* pStremData = NULL; int nSize = 0; prctl(PR_SET_NAME, "GetVencStream", 0,0,0); //thread_bind(4); pstPara = (SAMPLE_VENC_GETSTREAM_PARA_S*)p; s32ChnTotal = pstPara->s32Cnt; /****************************************** step 1: check & prepare save-file & venc-fd ******************************************/ if (s32ChnTotal >= VENC_MAX_CHN_NUM) { SAMPLE_PRT("input count invaild\n"); return NULL; } for (i = 0; i < s32ChnTotal; i++) { /* decide the stream file name, and open file to save stream */ VencChn = pstPara->VeChn[i]; s32Ret = HI_MPI_VENC_GetChnAttr(VencChn, &stVencChnAttr); if (s32Ret != HI_SUCCESS) { SAMPLE_PRT("HI_MPI_VENC_GetChnAttr chn[%d] failed with %#x!\n", \ VencChn, s32Ret); return NULL; } enPayLoadType[i] = stVencChnAttr.stVencAttr.enType; s32Ret = SAMPLE_COMM_VENC_GetFilePostfix(enPayLoadType[i], szFilePostfix); if (s32Ret != HI_SUCCESS) { SAMPLE_PRT("SAMPLE_COMM_VENC_GetFilePostfix [%d] failed with %#x!\n", \ stVencChnAttr.stVencAttr.enType, s32Ret); return NULL; } if(PT_JPEG != enPayLoadType[i]) { snprintf(aszFileName[i],32, "stream_chn%d%s", i, szFilePostfix); pFile[i] = fopen(aszFileName[i], "wb"); if (!pFile[i]) { SAMPLE_PRT("open file[%s] failed!\n", aszFileName[i]); return NULL; } } /* Set Venc Fd. */ VencFd[i] = HI_MPI_VENC_GetFd(VencChn); if (VencFd[i] < 0) { SAMPLE_PRT("HI_MPI_VENC_GetFd failed with %#x!\n", VencFd[i]); return NULL; } if (maxfd VeChn[0], pstPara->VeChn[1], VencFd[0], VencFd[1]); /****************************************** step 2: Start to get streams of each channel. ******************************************/ while (HI_TRUE == pstPara->bThreadStart) { FD_ZERO(&read_fds); for (i = 0; i < s32ChnTotal; i++) { FD_SET(VencFd[i], &read_fds); } TimeoutVal.tv_sec = 4; TimeoutVal.tv_usec = 0; s32Ret = select(maxfd + 1, &read_fds, NULL, NULL, &TimeoutVal); if (s32Ret < 0) { SAMPLE_PRT("select failed!\n"); break; } else if (s32Ret == 0) { SAMPLE_PRT("get venc chn %d stream time out, continue\n", VencChn); continue; } else { for (i = 0; i < s32ChnTotal; i++) { if (FD_ISSET(VencFd[i], &read_fds)) { /******************************************************* step 2.1 : query how many packs in one-frame stream. *******************************************************/ memset(&stStream, 0, sizeof(stStream)); //s32Ret = HI_MPI_VENC_QueryStatus(i, &stStat); s32Ret = HI_MPI_VENC_QueryStatus(pstPara->VeChn[i], &stStat); if (HI_SUCCESS != s32Ret) { SAMPLE_PRT("HI_MPI_VENC_QueryStatus chn[%d] failed with %#x!\n", pstPara->VeChn[i], s32Ret); break; } /******************************************************* step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,for example: if(0 == stStat.u32CurPacks || 0 == stStat.u32LeftStreamFrames) { SAMPLE_PRT("NOTE: Current frame is NULL!\n"); continue; } *******************************************************/ if(0 == stStat.u32CurPacks) { SAMPLE_PRT("NOTE: Current frame is NULL!\n"); continue; } /******************************************************* step 2.3 : malloc corresponding number of pack nodes. *******************************************************/ stStream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks); if (NULL == stStream.pstPack) { SAMPLE_PRT("malloc stream pack failed!\n"); break; } /******************************************************* step 2.4 : call mpi to get one-frame stream *******************************************************/ stStream.u32PackCount = stStat.u32CurPacks; s32Ret = HI_MPI_VENC_GetStream(pstPara->VeChn[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; } /******************************************************* step 2.5 : save frame to file *******************************************************/ if(PT_JPEG == enPayLoadType[i]) { snprintf(aszFileName[i],32, "stream_chn%d_%d%s", i, u32PictureCnt[i],szFilePostfix); pFile[i] = fopen(aszFileName[i], "wb"); if (!pFile[i]) { SAMPLE_PRT("open file err!\n"); return NULL; } } #ifndef __HuaweiLite__ //SAMPLE_PRT("SAMPLE_COMM_VENC_SaveStream chan:%d\n",i); //if(0 == i) { //printf("SAMPLE_COMM_VENC_SaveStream2MuxerQueue>>>>>>>>>>>>>>\n"); //s32Ret = SAMPLE_COMM_VENC_SaveStream(pFile[i], &stStream); //add by zhousl 20210205 17:11 s32Ret = SAMPLE_COMM_VENC_SaveStream2MuxerQueue(i,&stStream); } //s32Ret = SAMPLE_COMM_VENC_SaveStream(pFile[i], &stStream); //add by zhousl 20210205 17:11 #else s32Ret = SAMPLE_COMM_VENC_SaveStream_PhyAddr(pFile[i], &stStreamBufInfo[i], &stStream); #endif if (HI_SUCCESS != s32Ret) { free(stStream.pstPack); stStream.pstPack = NULL; SAMPLE_PRT("save stream failed!\n"); break; } /******************************************************* step 2.6 : release stream *******************************************************/ s32Ret = HI_MPI_VENC_ReleaseStream(pstPara->VeChn[i], &stStream); if (HI_SUCCESS != s32Ret) { SAMPLE_PRT("HI_MPI_VENC_ReleaseStream failed!\n"); free(stStream.pstPack); stStream.pstPack = NULL; break; } /******************************************************* step 2.7 : free pack nodes *******************************************************/ free(stStream.pstPack); stStream.pstPack = NULL; u32PictureCnt[i]++; if(PT_JPEG == enPayLoadType[i]) { fclose(pFile[i]); } } } } } /******************************************************* * step 3 : close save-file *******************************************************/ for (i = 0; i < s32ChnTotal; i++) { if(PT_JPEG != enPayLoadType[i]) { fclose(pFile[i]); } } return NULL; }sample_comm_venc.c文件中增加SAMPLE_COMM_VENC_SaveStream2MuxerQueue,该函数将视频流存入视频缓存队列 HI_S32 SAMPLE_COMM_VENC_SaveStream2MuxerQueue(int ch, VENC_STREAM_S* pstStream) { HI_S32 i; HI_S32 s32Ret = 0; unsigned char *pStremData = NULL; int nSize = 0; for (i = 0; i < pstStream->u32PackCount; i++) { pStremData = (unsigned char*)pstStream->pstPack[i].pu8Addr + pstStream->pstPack[i].u32Offset; nSize = pstStream->pstPack[i].u32Len - pstStream->pstPack[i].u32Offset; if(s32Ret = AppendVideo2Queue(pStremData,nSize, ch)) { SAMPLE_PRT("AppendVideo2Queue chan:%d err %d\n", s32Ret,ch); return s32Ret; } } return HI_SUCCESS; }3、在海思主程序中调用库中int StartMuxMp4File(void)接口启动合成 有问题欢迎留言:) 参考资料: https://blog.csdn.net/leixiaohua1020/article/details/39759623 https://blog.csdn.net/qq_29536949/article/details/108563398 https://blog.csdn.net/cfl927096306/article/details/107486805 |
CopyRight 2018-2019 实验室设备网 版权所有 |