Intel MSDK 是 Intel 公司提供的基于硬件加速功能的多媒体开发框架,通过 Intel 显卡的硬件加速功能(Intel® Quick Sync Video),可实现快速视频转码和图像处理。
- 硬编码格式:HEVC(h.265),AVC(h.264),MPEG-2,JPEG
- 硬解码格式:HEVC,AVC,VP8,MPEG-2,VC1,JPEG,MJPEG
- Filter:颜色空间转换,去隔行,降噪,缩放,旋转
Key Specifications (支持的硬件平台,操作系统,编译器等) 请参考 Intel 官网。
Intel MSDK samples
下载链接:https://software.intel.com/en-us/media-sdk/documentation/code-samples
Sample Name | Description |
---|---|
Transcoding | sample_multi_transcode Transforms an elementary video stream from one compressed format to another. |
Encoding | sample_encode Converts raw video frames into an elementary compressed stream. |
Decoding | sample_decode Transforms a compressed video stream into raw frames using HEVC decode and VP8 decode. The plug-in and the included sample_decvpp demonstrate decode functions with color conversion of raw video sequences. |
Video Processing | sample_vpp Demonstrates how to use algorithms that process raw frames using denoising, deinterlacing, inverse telecine, and color conversion techniques. |
OpenCL™ Video Motion Estimation (VME) | ocl_motion_estimation Provides step-by-step guidelines on using Intel’s motion estimation extension for the OpenCL standard. The motion estimation extension includes a set of host-callable functions for frame-based VME. |
OpenCL™ Interoperability | ocl_media_sdk_interop Shows how to use Intel Media SDK and Intel? SDK for Open CL? applications together for efficient video decoding and fast post-processing. |
HEVC GPU Assist APIs | sample_h265_gaa Supplies examples of the typical data and control flow for using HEVC GPU Assist APIs effectively. |
注:本文的代码基于 Intel_Media_SDK_2017_R1。
Intel MSDK encoding sample
以下是我改编过的 encoding sample 用法,利用 FFmpeg 进行 mux:
>intel_msdk_enc.exe
Intel(R) Media SDK Encoding Sample Version 3.5.915.45327
Usage: intel_msdk_enc.exe h264|mpeg2 [Options] -i input_yuv_file -o output_encoded_file -w width -h height
Options:
[-f frameRate] - video frame rate (frames per second)
[-b bitRate] - encoded bit rate (Kbits per second)
[-u speed|quality|balanced] - target usage
[-hw] - use platform specific SDK implementation, if not specified software implementation is used
[-d3d] - work with d3d surfaces
[-d3d11] - work with d3d11 surfaces
Intel MSDK encoding 代码
以下是整个编码及写入文件过程的概要代码,略去各个函数的具体实现和资源释放:
CSmplYUVReader g_file_reader;
ffmpeg_writer g_file_writer;
CEncodingPipeline enc_pipeline;
sInputParams Params = {}; // input parameters from command line
mfxStatus sts = ParseInputString( argv, (mfxU8)argc, &Params );
std::vector<msdk_char*> srcFileBuff;
sts = g_file_reader.Init( Params.strSrcFile, Params.ColorFormat, 1, srcFileBuff );
sts = enc_pipeline.Init( &Params, feed_surface, on_encoded_pkt );
mfxExtCodingOptionSPSPPS sps_pps = enc_pipeline.GetSPSPPS();
sts = g_file_writer.Init( &Params, sps_pps.SPSBuffer, sps_pps.SPSBufSize,
sps_pps.PPSBuffer, sps_pps.PPSBufSize );
while (true) {
sts = enc_pipeline.Run();
if (MFX_ERR_DEVICE_LOST == sts || MFX_ERR_DEVICE_FAILED == sts) {
sts = enc_pipeline.ResetDevice();
sts = enc_pipeline.ResetMFXComponents(&Params);
continue;
}
else
break;
}
enc_pipeline.Close();
g_file_writer.Close();
CEncodingPipeline::Init 函数
初始化 MSDK 编码用的 pipeline。
mfxStatus CEncodingPipeline::Init(sInputParams *pParams, PF_FEED_SURFACE pfFeedSurface, PF_ON_ENCODED_PKT pfEncodePktCb)
{
mfxStatus sts = MFX_ERR_NONE;
m_feedSurfaceCb = pfFeedSurface;
m_onEncPktCb = pfEncodePktCb;
mfxVersion version;
version.Major = 1;
version.Minor = 0;
if (pParams->bUseHWLib) {
// try searching on all display adapters
mfxIMPL impl = MFX_IMPL_HARDWARE_ANY;
if (D3D11_MEMORY == pParams->memType)
impl |= MFX_IMPL_VIA_D3D11;
sts = m_mfxSession.Init(impl, &version);
// MSDK API version may not support multiple adapters - then try initialize on the default
if (MFX_ERR_NONE != sts)
sts = m_mfxSession.Init(impl & !MFX_IMPL_HARDWARE_ANY | MFX_IMPL_HARDWARE, &version);
}
else
sts = m_mfxSession.Init(MFX_IMPL_SOFTWARE, &version);
m_memType = pParams->memType;
sts = CreateAllocator();
sts = InitMfxEncParams(pParams);
m_pmfxENC = new MFXVideoENCODE(m_mfxSession);
m_nAsyncDepth = 4; // this number can be tuned for better performance
sts = ResetMFXComponents(pParams);
MSDK_ZERO_MEMORY( m_extSPSPPS );
m_extSPSPPS.Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS;
m_extSPSPPS.Header.BufferSz = sizeof(mfxExtCodingOptionSPSPPS);
m_extSPSPPS.PPSBuffer = PPSBuffer;
m_extSPSPPS.SPSBuffer = SPSBuffer;
m_extSPSPPS.PPSBufSize = MAXSPSPPSBUFFERSIZE;
m_extSPSPPS.SPSBufSize = MAXSPSPPSBUFFERSIZE;
m_EncExtParams.push_back( (mfxExtBuffer*)&m_extSPSPPS );
m_mfxEncParams.ExtParam = &m_EncExtParams[0];
m_mfxEncParams.NumExtParam = (mfxU16)m_EncExtParams.size();
// Retrieve encoder parameters incl. sequence header
sts = m_pmfxENC->GetVideoParam( &m_mfxEncParams );
return MFX_ERR_NONE;
}
ffmpeg_writer::Init 函数
打开输出文件,初始化编码器(包含 SPS 和 PPS),然后写输出文件头到磁盘上。
其中 open_output_file 函数的介绍可以参考 这里,init_video_encoder 函数的介绍可以参考 这里。
mfxStatus ffmpeg_writer::Init(
sInputParams* in_params,
unsigned char* sps_buf,
int sps_buf_size,
unsigned char* pps_buf,
int pps_buf_size )
{
HRESULT hr = -1;
hr = open_output_file(in_params->strDstFile, AVMEDIA_TYPE_VIDEO, &m_out_fmt_ctx, &m_codec_ctx);
m_video_stream = m_out_fmt_ctx->streams[0];
video_base_info video_info;
video_info.bit_rate = in_params->nBitRate * 1000;
video_info.frame_rate = MAKE_AVRATIONL(in_params->dFrameRate, 1);
video_info.width = in_params->nWidth;
video_info.height = in_params->nHeight;
hr = init_video_encoder(video_info, m_out_fmt_ctx, 0, m_codec_ctx);
uint8_t* ext_data_buf = (uint8_t*)av_malloc(sps_buf_size + pps_buf_size);
memcpy(ext_data_buf, sps_buf, sps_buf_size);
memcpy(ext_data_buf + sps_buf_size, pps_buf, pps_buf_size);
// Codec "extradata" conveys the H.264 stream SPS and PPS info
// (MPEG2: sequence header is housed in SPS buffer, PPS buffer is empty)
m_codec_ctx->extradata = ext_data_buf;
m_codec_ctx->extradata_size = sps_buf_size + pps_buf_size;
hr = avformat_write_header(m_out_fmt_ctx, NULL);
m_initialized = true;
return MFX_ERR_NONE;
}
CEncodingPipeline::Run 函数
获取空闲的 surface → 填充 YUV 数据到 surface → 编码。
mfxStatus CEncodingPipeline::Run()
{
mfxStatus sts = MFX_ERR_NONE;
while (MFX_ERR_NONE <= sts || MFX_ERR_MORE_DATA == sts) {
mfxU16 nEncSurfIdx = GetFreeSurface( m_pEncSurfaces, m_EncResponse.NumFrameActual );
mfxFrameSurface1* pSurf = &m_pEncSurfaces[nEncSurfIdx];
if (m_bExternalAlloc) // get YUV pointers
sts = m_pMFXAllocator->Lock( m_pMFXAllocator->pthis, pSurf->Data.MemId, &(pSurf->Data) );
pSurf->Info.FrameId.ViewId = 0; // single view in current sample
sts = m_feedSurfaceCb( pSurf );
if (m_bExternalAlloc)
sts = m_pMFXAllocator->Unlock( m_pMFXAllocator->pthis, pSurf->Data.MemId, &(pSurf->Data) );
sts = EncodeFrame( &m_pEncSurfaces[nEncSurfIdx] );
}
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_DATA); // means that the input file has ended, need to go to buffering loops
while (MFX_ERR_NONE <= sts)
sts = EncodeFrame(NULL);
// MFX_ERR_MORE_DATA is the correct status to exit buffering loop with
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_DATA);
// 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
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_NOT_FOUND);
return sts;
}
CEncodingPipeline::EncodeFrame 函数
MSDK 的编码是异步进行的。
mfxStatus CEncodingPipeline::EncodeFrame( mfxFrameSurface1* pSurface )
{
mfxStatus sts = MFX_ERR_NONE;
// get a pointer to a free task (bit stream and sync point for encoder)
sTask* pCurrentTask = NULL;
sts = GetFreeTask( &pCurrentTask );
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
while (true) {
sts = m_pmfxENC->EncodeFrameAsync( NULL, pSurface, &pCurrentTask->mfxBS, &pCurrentTask->EncSyncP );
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);
}
else {
// get next surface and new task for 2nd bitstream
MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_BITSTREAM);
break;
}
}
return sts;
}
CEncodingPipeline::GetFreeTask 函数
如果没有空闲的 task,则同步等待。
mfxStatus CEncodingPipeline::GetFreeTask(sTask **ppTask)
{
mfxStatus sts = m_TaskPool.GetFreeTask(ppTask);
if (MFX_ERR_NOT_FOUND == sts) {
sts = m_TaskPool.SynchronizeFirstTask();
sts = m_TaskPool.GetFreeTask(ppTask);
}
return sts;
}
CEncTaskPool::SynchronizeFirstTask 函数
等待第一个 task 编码结束,写入到文件中,然后重置。最后标记下一个工作中的 task 的位置。
mfxStatus CEncTaskPool::SynchronizeFirstTask()
{
if (NULL != m_pTasks[m_nTaskBufStart].EncSyncP) {
mfxStatus sts = m_pmfxSession->SyncOperation(m_pTasks[m_nTaskBufStart].EncSyncP, MSDK_WAIT_INTERVAL);
if (MFX_ERR_NONE == sts) {
sts = m_pTasks[m_nTaskBufStart].WriteBitstream();
sts = m_pTasks[m_nTaskBufStart].Reset();
for (mfxU32 i = 0; i < m_nPoolSize; i++) {
m_nTaskBufStart = (m_nTaskBufStart + 1) % m_nPoolSize;
if (NULL != m_pTasks[m_nTaskBufetart].EncSyncP)
break;
}
}
return sts;
}
else
return MFX_ERR_NOT_FOUND; // no tasks left in task buffer
}
ffmpeg_writer::WriteNextFrame 函数
把编码后的数据写到文件中。
mfxStatus ffmpeg_writer::WriteNextFrame( mfxBitstream *pMfxBitstream )
{
MSDK_CHECK_ERROR(m_initialized, false, MFX_ERR_NOT_INITIALIZED);
MSDK_CHECK_POINTER(pMfxBitstream, MFX_ERR_NULL_PTR);
HRESULT hr = -1;
++m_nProcessedFramesNum;
AVPacket pkt;
av_init_packet(&pkt);
pkt.pts = av_rescale_q(m_nProcessedFramesNum, m_codec_ctx->time_base, m_video_stream->time_base);
pkt.dts = pkt.pts;
pkt.duration = av_rescale_q(1, m_codec_ctx->time_base, m_video_stream->time_base);
pkt.stream_index = m_video_stream->index;
pkt.data = pMfxBitstream->Data;
pkt.size = pMfxBitstream->DataLength;
if (pMfxBitstream->FrameType == (MFX_FRAMETYPE_I | MFX_FRAMETYPE_REF | MFX_FRAMETYPE_IDR))
pkt.flags |= AV_PKT_FLAG_KEY;
hr = av_interleaved_write_frame(m_out_fmt_ctx, &pkt);
RETURN_IF_FAILED_EX(hr, MFX_ERR_UNKNOWN);
return MFX_ERR_NONE;
}
ffmpeg_writer::Close 函数
编码结束,擦屁股。
virtual void ffmpeg_writer::Close()
{
if (m_initialized) {
int hr = av_write_trailer(m_out_fmt_ctx);
PRINT_ERROR_LOG_IF_FAILED(hr);
msdk_printf(MSDK_STRING("Frame number: %u\r"), m_nProcessedFramesNum);
}
if (NULL != m_codec_ctx)
avcodec_free_context(&m_codec_ctx);
if (NULL != m_out_fmt_ctx) {
avio_close(m_out_fmt_ctx->pb);
avformat_free_context(m_out_fmt_ctx);
m_out_fmt_ctx = NULL;
}
m_initialized = false;
m_nProcessedFramesNum = 0;
}
Intel MSDK 硬解码
请参考我的另一篇 文章。
– EOF –