Intel MSDK 硬编码

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 NameDescription
Transcodingsample_multi_transcode
Transforms an elementary video stream from one compressed format to another.
Encodingsample_encode
Converts raw video frames into an elementary compressed stream.
Decodingsample_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 Processingsample_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™ Interoperabilityocl_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 APIssample_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 硬解码

请参考我的另一篇 文章

Blueware
EOF

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值