海思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 <= VencFd[i])
{
maxfd = VencFd[i];
}
s32Ret = HI_MPI_VENC_GetStreamBufInfo (VencChn, &stStreamBufInfo[i]);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("HI_MPI_VENC_GetStreamBufInfo failed with %#x!\n", s32Ret);
return (void *)HI_FAILURE;
}
}
//printf("qjw venc chn:%d, %d, %d, %d\n", pstPara->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