视频编码
编码的流程
流程如下:
1、一些初始的相关设置
2、进入第一个while循环
(1)获取一个空闲的任务
(2)获取输入缓存区
(3)读取一个帧
(4)如果有需要就调用RunFrameVPPAsync,对让视频预处理器(vpp)对视频进行处理,注意它是一个异步函数
(5)调用EncodeFrameAsync对帧进行编码,注意它也是一个异步函数,这意味着该函数只是把输入帧放进编码器中而已,不等待它处理完成就返回了
3、如果有必要,进入第二个while循环,这个循环目的是得到视频预处理器的输出,因为RunFrameVPPAsync是一个异步函数
4、进入第三个while循环,这个循环的目的是得到编码器的输出,因为EncodeFrameAsync是一个异步函数
编码函数的实现
mfxStatus CEncodingPipeline::Run()
{
m_statOverall.StartTimeMeasurement();
MSDK_CHECK_POINTER(m_pmfxENC, MFX_ERR_NOT_INITIALIZED);
// 起始状态:无错误
mfxStatus sts = MFX_ERR_NONE;
mfxFrameSurface1* pSurf = NULL; // dispatching pointer
sTask *pCurrentTask = NULL; // a pointer to the current task
mfxU16 nEncSurfIdx = 0; // index of free surface for encoder input (vpp output)
mfxU16 nVppSurfIdx = 0; // index of free surface for vpp input
mfxSyncPoint VppSyncPoint = NULL; // a sync point associated with an asynchronous vpp call
bool bVppMultipleOutput = false; // this flag is true if VPP produces more frames at output
// than consumes at input. E.g. framerate conversion 30 fps -> 60 fps
// Since in sample we support just 2 views
// we will change this value between 0 and 1 in case of MVC
mfxU16 currViewNum = 0;
mfxU32 nFramesProcessed = 0;
sts = MFX_ERR_NONE;
#if defined (ENABLE_V4L2_SUPPORT)
if (isV4L2InputEnabled)
{
msdk_printf(MSDK_STRING("Press Ctrl+C to terminate this application\n"));
}
#endif
/*
** 这个循环的目的是从文件中读取图像,把图像传给预处理器(如果有需要)处理,然后把
** 处理过后的帧传给编码器,让编码器进行编码,注意,编码函数是一个异步函数,它不会等待编码的结果
** 把图像传进去之后就立即返回了
*/
// main loop, preprocessing and encoding
while (MFX_ERR_NONE <= sts || MFX_ERR_MORE_DATA == sts)
{
if ((m_nFramesToProcess != 0) && (nFramesProcessed == m_nFramesToProcess))
{
break;
}
#if defined (ENABLE_V4L2_SUPPORT)
if (v4l2Pipeline.GetV4L2TerminationSignal() && isV4L2InputEnabled)
{
break;
}
#endif
// get a pointer to a free task (bit stream and sync point for encoder)
// 获取一个空闲任务
sts = GetFreeTask(&pCurrentTask);
MSDK_BREAK_ON_ERROR(sts);
// find free surface for encoder input
// 内存获取
if (m_nMemBuffer && !m_pmfxVPP)
{
nEncSurfIdx %= m_nMemBuffer;
}
else
{
nEncSurfIdx = GetFreeSurface(m_pEncSurfaces, m_EncResponse.NumFrameActual);
}
MSDK_CHECK_ERROR(nEncSurfIdx, MSDK_INVALID_SURF_IDX, MFX_ERR_MEMORY_ALLOC);
// point pSurf to encoder surface
pSurf = &m_pEncSurfaces[nEncSurfIdx];
if (!bVppMultipleOutput)
{
// if vpp is enabled find free surface for vpp input and point pSurf to vpp surface
if (m_pmfxVPP)
{
#if defined (ENABLE_V4L2_SUPPORT)
if (isV4L2InputEnabled)
{
nVppSurfIdx = v4l2Pipeline.GetOffQ();
}
#else
if (m_nMemBuffer)
{
nVppSurfIdx = nVppSurfIdx % m_nMemBuffer;
}
else
{
nVppSurfIdx = GetFreeSurface(m_pVppSurfaces, m_VppResponse.NumFrameActual);
}
MSDK_CHECK_ERROR(nVppSurfIdx, MSDK_INVALID_SURF_IDX, MFX_ERR_MEMORY_ALLOC);
#endif
pSurf = &m_pVppSurfaces[nVppSurfIdx];
}
pSurf->Info.FrameId.ViewId = currViewNum;
m_statFile.StartTimeMeasurement();
// 读取一个帧
sts = LoadNextFrame(pSurf);
m_statFile.StopTimeMeasurement();
if ( (MFX_ERR_MORE_DATA == sts) && !m_bTimeOutExceed)
continue;
MSDK_BREAK_ON_ERROR(sts);
m_statFile.StopTimeMeasurement();
if (MVC_ENABLED & m_MVCflags) currViewNum ^= 1; // Flip between 0 and 1 for ViewId
}
// perform preprocessing if required
// 如果需要预处理(vpp)
if (m_pmfxVPP)
{
bVppMultipleOutput = false; // reset the flag before a call to VPP
for (;;)
{
// 视频预处理,注意它是一个异步函数,这表示着for循环结束也不一定能获取所有输出
sts = m_pmfxVPP->RunFrameVPPAsync(&m_pVppSurfaces[nVppSurfIdx], &m_pEncSurfaces[nEncSurfIdx],
NULL, &VppSyncPoint);
if (m_nMemBuffer)
{
// increment buffer index
nVppSurfIdx++;
}
if (MFX_ERR_NONE < sts && !VppSyncPoint) // repeat the call if warning and no output
{
if (MFX_WRN_DEVICE_BUSY == sts)
MSDK_SLEEP(1); // wait if device is busy
}
else if (MFX_ERR_NONE < sts && VppSyncPoint)
{
sts = MFX_ERR_NONE; // ignore warnings if output is available
break;
}
else
break; // not a warning
}
// process errors
if (MFX_ERR_MORE_DATA == sts)
{
continue;
}
else if (MFX_ERR_MORE_SURFACE == sts)
{
bVppMultipleOutput = true;
}
else
{
MSDK_BREAK_ON_ERROR(sts);
}
}
// save the id of preceding vpp task which will produce input data for the encode task
if (VppSyncPoint)
{
pCurrentTask->DependentVppTasks.push_back(VppSyncPoint);
VppSyncPoint = NULL;
}
// 循环,把帧都放进编码器中,让编码器去处理
// 注意循环结束之后,不表示可以得到所有输出,因为调用的是一个异步函数
for (;;)
{
// 插入一个IDR帧,IDR帧是关键帧
InsertIDR(m_bInsertIDR);
// at this point surface for encoder contains either a frame from file or a frame processed by vpp
sts = m_pmfxENC->EncodeFrameAsync(&m_encCtrl, &m_pEncSurfaces[nEncSurfIdx], &pCurrentTask->mfxBS, &pCurrentTask->EncSyncP);
m_bInsertIDR = false;
if (m_nMemBuffer)
{
// increment buffer index
nEncSurfIdx++;
}
if (MFX_ERR_NONE < sts && !pCurrentTask->EncSyncP) // repeat the call if warning and no output
{
if (MFX_WRN_DEVICE_BUSY == sts)
MSDK_SLEEP(1); // wait if device is busy
}
else if (MFX_ERR_NONE < sts && pCurrentTask->EncSyncP)
{
sts = MFX_ERR_NONE; // ignore warnings if output is available
break;
}
else if (MFX_ERR_NOT_ENOUGH_BUFFER == sts)
{
sts = AllocateSufficientBuffer(&pCurrentTask->mfxBS);
MSDK_CHECK_STATUS(sts, "AllocateSufficientBuffer failed");
}
else
{
// get next surface and new task for 2nd bitstream in ViewOutput mode
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_BITSTREAM);
break;
}
}
nFramesProcessed++;
}
// means that the input file has ended, need to go to buffering loops
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_DATA);
// exit in case of other errors
MSDK_CHECK_STATUS(sts, "m_pmfxENC->EncodeFrameAsync failed");
if (m_pmfxVPP)
{
// 得到视频预处理器的输出
// loop to get buffered frames from vpp
while (MFX_ERR_NONE <= sts || MFX_ERR_MORE_DATA == sts || MFX_ERR_MORE_SURFACE == sts)
// MFX_ERR_MORE_SURFACE can be returned only by RunFrameVPPAsync
// MFX_ERR_MORE_DATA is accepted only from EncodeFrameAsync
{
// find free surface for encoder input (vpp output)
nEncSurfIdx = GetFreeSurface(m_pEncSurfaces, m_EncResponse.NumFrameActual);
MSDK_CHECK_ERROR(nEncSurfIdx, MSDK_INVALID_SURF_IDX, MFX_ERR_MEMORY_ALLOC);
for (;;)
{
sts = m_pmfxVPP->RunFrameVPPAsync(NULL, &m_pEncSurfaces[nEncSurfIdx], NULL, &VppSyncPoint);
if (MFX_ERR_NONE < sts && !VppSyncPoint) // repeat the call if warning and no output
{
if (MFX_WRN_DEVICE_BUSY == sts)
MSDK_SLEEP(1); // wait if device is busy
}
else if (MFX_ERR_NONE < sts && VppSyncPoint)
{
sts = MFX_ERR_NONE; // ignore warnings if output is available
break;
}
else
break; // not a warning
}
if (MFX_ERR_MORE_SURFACE == sts)
{
continue;
}
else
{
MSDK_BREAK_ON_ERROR(sts);
}
// get a free task (bit stream and sync point for encoder)
sts = GetFreeTask(&pCurrentTask);
MSDK_BREAK_ON_ERROR(sts);
// save the id of preceding vpp task which will produce input data for the encode task
if (VppSyncPoint)
{
pCurrentTask->DependentVppTasks.push_back(VppSyncPoint);
VppSyncPoint = NULL;
}
for (;;)
{
InsertIDR(m_bInsertIDR);
sts = m_pmfxENC->EncodeFrameAsync(&m_encCtrl, &m_pEncSurfaces[nEncSurfIdx], &pCurrentTask->mfxBS, &pCurrentTask->EncSyncP);
m_bInsertIDR = false;
if (MFX_ERR_NONE < sts && !pCurrentTask->EncSyncP) // repeat the call if warning and no output
{
if (MFX_WRN_DEVICE_BUSY == sts)
MSDK_SLEEP(1); // wait if device is busy
}
else if (MFX_ERR_NONE < sts && pCurrentTask->EncSyncP)
{
sts = MFX_ERR_NONE; // ignore warnings if output is available
break;
}
else if (MFX_ERR_NOT_ENOUGH_BUFFER == sts)
{
sts = AllocateSufficientBuffer(&pCurrentTask->mfxBS);
MSDK_CHECK_STATUS(sts, "AllocateSufficientBuffer failed");
}
else
{
// get next surface and new task for 2nd bitstream in ViewOutput mode
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_BITSTREAM);
break;
}
}
}
// MFX_ERR_MORE_DATA is the correct status to exit buffering loop with
// indicates that there are no more buffered frames
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_DATA);
// exit in case of other errors
MSDK_CHECK_STATUS(sts, "m_pmfxENC->EncodeFrameAsync failed");
}
/*
** 这个循环的目的是等待编码器的输出,和上一个循环一样,也是调用EncodeFrameAsync函数,但是参数不一样,请注意
** 这个循环一直执行,直到所有的帧都编码完成
*/
// loop to get buffered frames from encoder
while (MFX_ERR_NONE <= sts)
{
// get a free task (bit stream and sync point for encoder)
sts = GetFreeTask(&pCurrentTask);
MSDK_BREAK_ON_ERROR(sts);
for (;;)
{
InsertIDR(m_bInsertIDR);
sts = m_pmfxENC->EncodeFrameAsync(&m_encCtrl, NULL, &pCurrentTask->mfxBS, &pCurrentTask->EncSyncP);
m_bInsertIDR = false;
if (MFX_ERR_NONE < sts && !pCurrentTask->EncSyncP) // repeat the call if warning and no output
{
if (MFX_WRN_DEVICE_BUSY == sts)
MSDK_SLEEP(1); // wait if device is busy
}
else if (MFX_ERR_NONE < sts && pCurrentTask->EncSyncP)
{
sts = MFX_ERR_NONE; // ignore warnings if output is available
break;
}
else if (MFX_ERR_NOT_ENOUGH_BUFFER == sts)
{
sts = AllocateSufficientBuffer(&pCurrentTask->mfxBS);
MSDK_CHECK_STATUS(sts, "AllocateSufficientBuffer failed");
}
else
{
// get new task for 2nd bitstream in ViewOutput mode
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_BITSTREAM);
break;
}
}
MSDK_BREAK_ON_ERROR(sts);
}
// MFX_ERR_MORE_DATA is the correct status to exit buffering loop with
// indicates that there are no more buffered frames
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_DATA);
// exit in case of other errors
MSDK_CHECK_STATUS(sts, "m_pmfxENC->EncodeFrameAsync failed");
// synchronize all tasks that are left in task pool
while (MFX_ERR_NONE == sts)
{
sts = m_TaskPool.SynchronizeFirstTask();
}
// MFX_ERR_NOT_FOUND is the correct status to exit the loop with
// EncodeFrameAsync and SyncOperation don't return this status
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_NOT_FOUND);
// report any errors that occurred in asynchronous part
MSDK_CHECK_STATUS(sts, "m_TaskPool.SynchronizeFirstTask failed");
m_statOverall.StopTimeMeasurement();
return sts;
}