blob: d8cf758ef92e1beef13b5590923ef38326b013f0 [file] [log] [blame]
/*
* * Copyright (C) 2018 Intel Corporation. All Rights Reserved.
* *
** Permission is hereby granted, free of charge, to any person obtaining a
* * copy of this software and associated documentation files (the
* * "Software"), to deal in the Software without restriction, including
* * without limitation the rights to use, copy, modify, merge, publish,
* * distribute, sub license, and/or sell copies of the Software, and to
* * permit persons to whom the Software is furnished to do so, subject to
* * the following conditions:
* *
* * The above copyright notice and this permission notice (including the
* * next paragraph) shall be included in all copies or substantial portions
* * of the Software.
* *
* * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
* * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* */
/**
* @file VDecAccelVA.cpp
* @brief LibVA decode accelerator implementation.
*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <assert.h>
#include <algorithm>
#include <string.h>
#include "VDecAccelVA.h"
#include <va/va.h>
#include <va/va_drm.h>
#define VASUCCEEDED(err) (err == VA_STATUS_SUCCESS)
#define VAFAILED(err) (err != VA_STATUS_SUCCESS)
mvaccel::VDecAccelVAImpl::VDecAccelVAImpl(void* device)
: m_vaDisplay(0)
, m_vaProfile(VAProfileNone)
, m_vaEntrypoint(VAEntrypointVLD)
, m_vaConfigID(0)
, m_vaContextID(0)
, m_surfaceType(VA_RT_FORMAT_YUV420)
{
if (device)
m_vaDisplay = *(reinterpret_cast<VADisplay*>(device));
if (!m_vaDisplay)
{
printf("Invalid VADisplay\n");
delete this;
return;
}
}
mvaccel::VDecAccelVAImpl::VDecAccelVAImpl()
: m_vaDisplay(0)
, m_vaProfile(VAProfileNone)
, m_vaEntrypoint(VAEntrypointVLD)
, m_vaConfigID(0)
, m_vaContextID(0)
, m_surfaceType(VA_RT_FORMAT_YUV420)
{
}
mvaccel::VDecAccelVAImpl::~VDecAccelVAImpl()
{
if (drm_fd != -1) {
close(drm_fd);
}
}
int mvaccel::VDecAccelVAImpl::Open()
{
VAStatus vaStatus = VA_STATUS_ERROR_UNKNOWN;
//get display device
int MajorVer, MinorVer;
if (vaStatus != VA_STATUS_SUCCESS) {
drm_fd = open("/dev/dri/renderD128", O_RDWR);
if (drm_fd >= 0) {
m_vaDisplay = vaGetDisplayDRM(drm_fd);
vaStatus = vaInitialize(m_vaDisplay, &MajorVer, &MinorVer);
}
}
//initialize decode description
create_decode_desc();
m_vaProfile = VAProfileH264Main;
// We only support VLD currently
m_vaEntrypoint = VAEntrypointVLD;
int count = 0;
count = vaMaxNumEntrypoints(m_vaDisplay);
assert(count);
std::vector<VAEntrypoint> vaEntrypoints(count);
vaStatus = vaQueryConfigEntrypoints(
m_vaDisplay,
m_vaProfile,
&vaEntrypoints[0],
&count);
if(VAFAILED(vaStatus))
printf("vaQueryConfigEntrypoints fail\n");
std::vector<VAEntrypoint>::iterator it = std::find(vaEntrypoints.begin(), vaEntrypoints.end(), m_vaEntrypoint);
if (it == vaEntrypoints.end())
{
if(VAFAILED(vaStatus))
printf("VAEntrypoint is not found\n");
return 1;
}
if (!is_config_compatible(m_DecodeDesc))
{
if(VAFAILED(vaStatus))
printf("Decode configuration is not compatible\n");
return 1;
}
// Setup config attributes
std::vector<VAConfigAttrib> vaAttribs;
prepare_config_attribs(m_DecodeDesc, vaAttribs);
// Create config
vaStatus = vaCreateConfig(
m_vaDisplay,
m_vaProfile,
m_vaEntrypoint,
&vaAttribs.at(0),
vaAttribs.size(),
&m_vaConfigID
);
if(VAFAILED(vaStatus))
printf("vaCreateConfig fail\n");
if (!is_rt_foramt_supported(m_DecodeDesc))
{
if(VAFAILED(vaStatus))
printf("Render target is not supported\n");
return 1;
}
// Calculate aligned width/height for gfx surface
uint32_t aligned_width = m_DecodeDesc.width;
uint32_t aligned_height = m_DecodeDesc.height;
// Setup surface attributes
prepare_surface_attribs(m_DecodeDesc, m_vaSurfAttribs, false);
// Create surfaces
for (uint32_t i=0; i<m_DecodeDesc.surfaces_num; i++)
{
VASurfaceID vaID = VA_INVALID_SURFACE;
vaStatus = vaCreateSurfaces(
m_vaDisplay,
m_surfaceType,
aligned_width,
aligned_height,
&vaID,
1,
&(m_vaSurfAttribs.at(0)),
m_vaSurfAttribs.size()
);
if (VASUCCEEDED(vaStatus))
m_vaIDs.push_back(vaID);
}
// Check if surfaces created is equal to requested.
if (m_vaIDs.size() != m_DecodeDesc.surfaces_num)
{
if(VAFAILED(vaStatus))
printf("Create surface fail\n");
return 1;
}
// Create context
vaStatus = vaCreateContext(
m_vaDisplay,
m_vaConfigID,
aligned_width,
aligned_height,
VA_PROGRESSIVE,
&(m_vaIDs.at(0)),
m_vaIDs.size(),
&m_vaContextID
);
if(VAFAILED(vaStatus))
printf("vaCreateContext fail\n");
check_process_pipeline_caps(m_DecodeDesc);
return vaStatus;
}
void mvaccel::VDecAccelVAImpl::Close()
{
std::vector<VASurfaceID>::iterator it;
for (it = m_vaIDs.begin(); it != m_vaIDs.end(); ++it)
delete_surface(*it);
vaDestroyConfig(m_vaDisplay, m_vaConfigID);
vaDestroyContext(m_vaDisplay, m_vaContextID);
m_images.clear();
m_vaIDs.clear();
m_vaSurfAttribs.clear();
m_vaConfigID = 0;
m_vaContextID = 0;
}
uint32_t mvaccel::VDecAccelVAImpl::GetSurfaceID(uint32_t index)
{
assert(index < m_vaIDs.size());
return m_vaIDs[index];
}
/**
* @brief Check if video decode acceleration description is supported.
* @param cc Video decode acceleration description.
* @return true if supported, false if not.
*/
bool mvaccel::VDecAccelVAImpl::is_config_compatible(DecodeDesc& desc)
{
if (!is_slice_mode_supported(desc))
return false;
if (!is_encryption_supported(desc))
return false;
if(!is_sfc_config_supported(desc))
return false;
return true;
}
/**
* @brief Check if long or short format is supported not not.
* @param cc Video decode acceleration description.
* @return true if supported, false if not.
*/
bool mvaccel::VDecAccelVAImpl::is_slice_mode_supported(DecodeDesc& desc)
{
VAConfigAttrib vaAttrib;
memset(&vaAttrib, 0, sizeof(vaAttrib));
vaAttrib.type = VAConfigAttribDecSliceMode;
vaAttrib.value = 0;
vaGetConfigAttributes(
m_vaDisplay,
m_vaProfile,
m_vaEntrypoint,
&vaAttrib,
1);
if(vaAttrib.value & VA_DEC_SLICE_MODE_BASE || vaAttrib.value & VA_DEC_SLICE_MODE_NORMAL)
return true;
else
return false;
}
/**
* @brief Check if encryption is supported or not.
* @param cc Video decode acceleration description.
* @return true if supported, false if not.
*/
bool mvaccel::VDecAccelVAImpl::is_encryption_supported(DecodeDesc& desc)
{
VAConfigAttrib vaAttrib;
memset(&vaAttrib, 0, sizeof(vaAttrib));
vaAttrib.type = VAConfigAttribEncryption;
vaAttrib.value = 0;
vaGetConfigAttributes(
m_vaDisplay,
m_vaProfile,
m_vaEntrypoint,
&vaAttrib,
1
);
if(vaAttrib.value & VA_ATTRIB_NOT_SUPPORTED)
return true;
else
return false;
}
/**
* @brief Check if SFC attribute is supported or not.
* @param cc Video decode acceleration description.
* @return true if supported, false if not.
*/
bool mvaccel::VDecAccelVAImpl::is_sfc_config_supported(DecodeDesc& desc)
{
// SFC attribute check
VAConfigAttrib vaAttrib;
memset(&vaAttrib, 0, sizeof(vaAttrib));
vaAttrib.type = VAConfigAttribDecProcessing;
vaAttrib.value = 0;
vaGetConfigAttributes(
m_vaDisplay,
m_vaProfile,
m_vaEntrypoint,
&vaAttrib,
1);
if (vaAttrib.value != VA_DEC_PROCESSING)
return false;
return true;
}
/**
* @brief Check if render target format is supported or not.
* @param cc Video decode acceleration description.
* @return true if supported, false if not.
*/
bool mvaccel::VDecAccelVAImpl::is_rt_foramt_supported(DecodeDesc& desc)
{
uint32_t count = VASurfaceAttribCount + vaMaxNumImageFormats(m_vaDisplay);
std::vector<VASurfaceAttrib> attribs(count);
VAStatus vaStatus = VA_STATUS_SUCCESS;
vaStatus = vaQuerySurfaceAttributes(
m_vaDisplay,
m_vaConfigID,
&attribs.at(0),
&count
);
if (VAFAILED(vaStatus))
{
printf("vaQuerySurfaceAttributes failed\n");
return false;
}
return true;
}
/**
* @brief Prepare config attribs VAContext creation.
* @param desc Video decode acceleration description.
* @param vaAttribs Array of VASurfaceAttrib which will contains the attrib.
*/
void mvaccel::VDecAccelVAImpl::prepare_config_attribs(
DecodeDesc& desc,
VAConfigAttribArray& attribs)
{
VAConfigAttrib attrib;
memset(&attrib, 0, sizeof(attrib));
// RT formats
attrib.type = VAConfigAttribRTFormat;
attrib.value = VA_RT_FORMAT_YUV420;
attribs.push_back(attrib);
// Slice Mode
attrib.type = VAConfigAttribDecSliceMode;
attrib.value = VA_DEC_SLICE_MODE_NORMAL;
attribs.push_back(attrib);
//dec processing attribs
attrib.type = VAConfigAttribDecProcessing;
attrib.value = VA_DEC_PROCESSING;
attribs.push_back(attrib);
}
/**
* @brief Prepare the VA surface attribs for creation.
* @param desc Video decode acceleration description.
* @param vaSurfAttribs Array of VASurfaceAttrib which will contains attrib.
*/
void mvaccel::VDecAccelVAImpl::prepare_surface_attribs(
DecodeDesc& desc,
VASurfaceAttribArray& attribs,
bool bDecodeDownsamplingHinted)
{
VASurfaceAttrib attrib;
memset(&attrib, 0, sizeof(attrib));
attrib.type = VASurfaceAttribPixelFormat;
attrib.flags = VA_SURFACE_ATTRIB_SETTABLE;
attrib.value.type = VAGenericValueTypeInteger;
// VA_FOURCC and MVFOURCC are interchangeable
if(bDecodeDownsamplingHinted)
attrib.value.value.i = VA_FOURCC_ARGB;
else
attrib.value.value.i = VA_FOURCC_NV12;
attribs.push_back(attrib);
}
/**
* @brief Delete allocated surface
* @param surfaceID Index of allocated surface. After delete, the surface
* values will be set to invalid value.
*/
void mvaccel::VDecAccelVAImpl::delete_surface(VASurfaceID& vaID)
{
// Make sure no others is using this surface
if (m_images.count(vaID))
m_images.erase(m_images.find(vaID));
vaDestroySurfaces(m_vaDisplay, &vaID, 1);
vaID = VA_INVALID_SURFACE;
}
uint8_t* mvaccel::VDecAccelVAImpl::lock_surface(VASurfaceID id, bool write)
{
// Check if decode is completed
VAStatus status = vaSyncSurface(m_vaDisplay, id);
if (VAFAILED(status))
{
printf("vaSyncSurface Error.\n");
return NULL;
}
//sync decode surface
VASurfaceStatus surf_status = VASurfaceSkipped;
for(;;)
{
vaQuerySurfaceStatus(m_vaDisplay, id, &surf_status);
if (surf_status != VASurfaceRendering &&
surf_status != VASurfaceDisplaying)
break;
}
if (surf_status != VASurfaceReady)
{
printf("Surface is not ready by vaQueryStatusSurface");
return NULL;
}
uint8_t* buffer = NULL;
//map the decoded buffer
for(;;)
{
status = vaDeriveImage(m_vaDisplay, id, &m_images[id]);
if (VAFAILED(status))
printf("vaDeriveImage fail. \n");
status = vaMapBuffer(m_vaDisplay, m_images[id].buf, (void**)&buffer);
if (VAFAILED(status))
printf("vaDeriveImage fail. \n");
break;
}
if (VAFAILED(status))
{
status = vaUnmapBuffer(m_vaDisplay, m_images[id].buf);
status = vaDestroyImage(m_vaDisplay, m_images[id].image_id);
}
return buffer;
}
void mvaccel::VDecAccelVAImpl::unlock_surface(VASurfaceID id)
{
VAStatus status = vaUnmapBuffer(m_vaDisplay, m_images[id].buf);
assert(VASUCCEEDED(status));
status = vaDestroyImage(m_vaDisplay, m_images[id].image_id);
assert(VASUCCEEDED(status));
}
//prepare basic format/resolution parameter
void mvaccel::VDecAccelVAImpl::create_decode_desc()
{
m_DecodeDesc.format = VA_FOURCC_NV12;
m_DecodeDesc.sfcformat = VA_FOURCC_ARGB;
m_DecodeDesc.width = 352;
m_DecodeDesc.height = 288;
m_DecodeDesc.sfc_widht = 176;
m_DecodeDesc.sfc_height = 144;
m_DecodeDesc.surfaces_num = 2;
}
bool mvaccel::VDecAccelVAImpl::DecodePicture()
{
// Create addition surfaces for scaled video output
if (m_sfcIDs.empty())
{
if (create_resources())
return 1;
}
VAStatus vaStatus = VA_STATUS_SUCCESS;
VAContextID vaContextID = 0;
VASurfaceID vaID = 0;
//va begin picture
vaStatus = vaBeginPicture(m_vaDisplay, m_vaContextID, vaID);
if (VAFAILED(vaStatus))
printf("vaBeginPicture fail.");
// Set Context ID for End Picture
vaContextID = m_vaContextID;
std::vector<VABufferID> vaBufferIDs;
// Pic parameters buffers
VABufferID vaBufferID;
vaStatus = vaCreateBuffer( m_vaDisplay,
m_vaContextID,
VAPictureParameterBufferType,
sizeof(g_PicParams_AVC),
1,
(uint8_t*)g_PicParams_AVC,
&vaBufferID );
assert(VASUCCEEDED(vaStatus));
if (VASUCCEEDED(vaStatus))
vaBufferIDs.push_back(vaBufferID);
// IQ matrics
vaStatus = vaCreateBuffer( m_vaDisplay,
m_vaContextID,
VAIQMatrixBufferType,
sizeof(g_Qmatrix_AVC),
1,
(uint8_t*)g_Qmatrix_AVC,
&vaBufferID );
assert(VASUCCEEDED(vaStatus));
if (VASUCCEEDED(vaStatus))
vaBufferIDs.push_back(vaBufferID);
//slice parameter buffers
vaStatus = vaCreateBuffer(m_vaDisplay,
m_vaContextID,
VASliceParameterBufferType,
sizeof(g_SlcParams_AVC),
1,
(uint8_t*)g_SlcParams_AVC,
&vaBufferID );
assert(VASUCCEEDED(vaStatus));
if (VASUCCEEDED(vaStatus))
vaBufferIDs.push_back(vaBufferID);
//BITSTREAM buffers
vaStatus = vaCreateBuffer(m_vaDisplay,
m_vaContextID,
VASliceDataBufferType,
sizeof(g_Bitstream_AVC),
1,
(uint8_t*)g_Bitstream_AVC,
&vaBufferID );
assert(VASUCCEEDED(vaStatus));
if (VASUCCEEDED(vaStatus))
vaBufferIDs.push_back(vaBufferID);
//PROC_PIPELINE buffers
vaStatus = vaCreateBuffer(m_vaDisplay,
m_vaContextID,
VAProcPipelineParameterBufferType,
sizeof(m_vaProcBuffer),
1,
(uint8_t*)&m_vaProcBuffer,
&vaBufferID );
assert(VASUCCEEDED(vaStatus));
if (VASUCCEEDED(vaStatus))
vaBufferIDs.push_back(vaBufferID);
if (vaBufferIDs.size())
{
vaStatus = vaRenderPicture(
m_vaDisplay,
m_vaContextID,
&(vaBufferIDs.at(0)),
vaBufferIDs.size());
}
//va end picture
vaStatus = vaEndPicture(m_vaDisplay, vaContextID);
if (VAFAILED(vaStatus))
printf("vaEndPicture fail.");
//lock surface
uint8_t* gfx_surface_buf = lock_surface(m_sfcIDs[0], false);
if (gfx_surface_buf == NULL)
{
printf("Fail to lock gfx surface\n");
return 1;
}
//write to yuv file
FILE* sfc_stream = fopen("sfc_sample_176_144_argb.yuv", "wb");
uint32_t file_size = m_DecodeDesc.sfc_widht * m_DecodeDesc.sfc_height * 4; //ARGB format
if (sfc_stream) {
fwrite(gfx_surface_buf, file_size, 1, sfc_stream);
fclose(sfc_stream);
}
//unlock surface and clear
unlock_surface(m_sfcIDs[0]);
Close();
return (VASUCCEEDED(vaStatus) ? 0 : 1);
}
// check vaQueryVideoProcPipelineCaps
int mvaccel::VDecAccelVAImpl::check_process_pipeline_caps(DecodeDesc& desc)
{
VAProcPipelineCaps caps;
VAProcColorStandardType inputCST[VAProcColorStandardCount];
VAProcColorStandardType outputCST[VAProcColorStandardCount];
caps.input_color_standards = inputCST;
caps.output_color_standards = outputCST;
m_in4CC.resize(vaMaxNumImageFormats(m_vaDisplay));
m_out4CC.resize(vaMaxNumImageFormats(m_vaDisplay));
caps.input_pixel_format = &m_in4CC.at(0);
caps.output_pixel_format = &m_out4CC.at(0);
VABufferID filterIDs[VAProcFilterCount];
uint32_t filterCount = 0;
VAStatus vaStatus = VA_STATUS_SUCCESS;
vaStatus = vaQueryVideoProcPipelineCaps(
m_vaDisplay,
m_vaContextID,
filterIDs,
filterCount,
&caps
);
if (VAFAILED(vaStatus))
{
printf("vaQueryVideoProcPipelineCaps fail\n");
return 1;
}
m_in4CC.resize(caps.num_input_pixel_formats);
m_out4CC.resize(caps.num_output_pixel_formats);
return 0;
}
int mvaccel::VDecAccelVAImpl::create_resources()
{
if (m_sfcIDs.empty())
m_sfcIDs.resize(1);
// Create addition surfaces for scaled video output
uint16_t width = m_DecodeDesc.sfc_widht;
uint16_t height = m_DecodeDesc.sfc_height;
//prepare sfc surface attribs
DecodeDesc SfcDesc;
SfcDesc.sfcformat = VA_FOURCC('A','R','G','B');
VASurfaceAttribArray Sfc_vaSurfAttribs;
prepare_surface_attribs(SfcDesc, Sfc_vaSurfAttribs, true);
uint32_t surfaceType = m_surfaceType;
m_surfaceType = (uint32_t)SfcDesc.sfcformat;
for(uint32_t i = 0; i < m_sfcIDs.size(); i++)
{
vaCreateSurfaces(
m_vaDisplay,
m_surfaceType,
width,
height,
&m_sfcIDs[i],
1,
&(Sfc_vaSurfAttribs.at(0)),
Sfc_vaSurfAttribs.size()
);
}
m_surfaceType = surfaceType;
m_rectSFC.x = m_rectSFC.y = 0;
m_rectSFC.width = width;
m_rectSFC.height = height;
// Prepare VAProcPipelineParameterBuffer for decode
VAProcPipelineParameterBuffer buffer;
memset(&buffer, 0, sizeof(buffer));
m_rectSrc.x = m_rectSrc.y = 0;
m_rectSrc.width = (uint16_t)m_DecodeDesc.width;
m_rectSrc.height = (uint16_t)m_DecodeDesc.height;
buffer.surface_region = &m_rectSrc;
buffer.output_region = &m_rectSFC;
buffer.additional_outputs = (VASurfaceID*)&(m_sfcIDs[0]);
buffer.num_additional_outputs = 1;
m_vaProcBuffer = buffer;
return 0;
}