/*
* Copyright (C) 2012 Intel Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <cutils/properties.h>
#include <system/graphics.h>
#include <nativebase/nativebase.h>
#include "isv_worker.h"
#ifndef TARGET_VPP_USE_GEN
#include <hal_public.h>
#else
#include <ufo/graphics.h>
#endif
//#define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "isv-omxil"
#define CHECK_VASTATUS(str) \
do { \
if (vaStatus != VA_STATUS_SUCCESS) { \
ALOGE("%s failed\n", str); \
return STATUS_ERROR;} \
}while(0);
enum STRENGTH {
STRENGTH_LOW = 0,
STRENGTH_MEDIUM,
STRENGTH_HIGH
};
#define DENOISE_DEBLOCK_STRENGTH STRENGTH_MEDIUM
#define COLOR_STRENGTH STRENGTH_MEDIUM
#ifdef TARGET_VPP_USE_GEN
#define COLOR_NUM 4
#else
#define COLOR_NUM 2
#endif
#define MAX_FRC_OUTPUT 4 /*for frcx4*/
using namespace android;
ISVWorker::ISVWorker()
:mNumForwardReferences(0),
mVAContext(VA_INVALID_ID),
mWidth(0), mHeight(0),
mDisplay(NULL), mVADisplay(NULL),
mVAConfig(VA_INVALID_ID),
mForwardReferences(NULL),
mPrevInput(0), mPrevOutput(0),
mNumFilterBuffers(0),
mFilterFrc(VA_INVALID_ID), mFilters(0),
mInputIndex(0), mOutputIndex(0),
mOutputCount(0) {
memset(&mFilterBuffers, VA_INVALID_ID, VAProcFilterCount * sizeof(VABufferID));
memset(&mFilterParam, 0, sizeof(mFilterParam));
}
bool ISVWorker::isSupport() const {
bool support = false;
int num_entrypoints = vaMaxNumEntrypoints(mVADisplay);
VAEntrypoint * entrypoints = (VAEntrypoint *)malloc(num_entrypoints * sizeof(VAEntrypoint));
if (entrypoints == NULL) {
ALOGE("failed to malloc entrypoints array\n");
return false;
}
// check if it contains VPP entry point VAEntrypointVideoProc
VAStatus vaStatus = vaQueryConfigEntrypoints(mVADisplay, VAProfileNone, entrypoints, &num_entrypoints);
if (vaStatus != VA_STATUS_SUCCESS) {
ALOGE("vaQueryConfigEntrypoints failed");
return false;
}
for (int i = 0; !support && i < num_entrypoints; i++) {
support = entrypoints[i] == VAEntrypointVideoProc;
}
free(entrypoints);
entrypoints = NULL;
return support;
}
uint32_t ISVWorker::getProcBufCount() {
return getOutputBufCount(mInputIndex);
}
uint32_t ISVWorker::getFillBufCount() {
return getOutputBufCount(mOutputIndex);
}
uint32_t ISVWorker::getOutputBufCount(uint32_t index) {
uint32_t bufCount = 1;
if (((mFilters & FilterFrameRateConversion) != 0)
&& index > 0)
bufCount = mFilterParam.frcRate - (((mFilterParam.frcRate == FRC_RATE_2_5X) ? (index & 1): 0));
return bufCount;
}
status_t ISVWorker::init(uint32_t width, uint32_t height) {
ALOGV("init");
if (mDisplay != NULL) {
ALOGE("VA is particially started");
return STATUS_ERROR;
}
mDisplay = new Display;
*mDisplay = ANDROID_DISPLAY_HANDLE;
mVADisplay = vaGetDisplay(mDisplay);
if (mVADisplay == NULL) {
ALOGE("vaGetDisplay failed");
return STATUS_ERROR;
}
int majorVersion, minorVersion;
VAStatus vaStatus = vaInitialize(mVADisplay, &majorVersion, &minorVersion);
CHECK_VASTATUS("vaInitialize");
// Check if VPP entry point is supported
if (!isSupport()) {
ALOGE("VPP is not supported on current platform");
return STATUS_NOT_SUPPORT;
}
// Find out the format for the target
VAConfigAttrib attrib;
attrib.type = VAConfigAttribRTFormat;
vaStatus = vaGetConfigAttributes(mVADisplay, VAProfileNone, VAEntrypointVideoProc, &attrib, 1);
CHECK_VASTATUS("vaGetConfigAttributes");
if ((attrib.value & VA_RT_FORMAT_YUV420) == 0) {
ALOGE("attribute is %x vs wanted %x", attrib.value, VA_RT_FORMAT_YUV420);
return STATUS_NOT_SUPPORT;
}
ALOGV("ready to create config");
// Create the configuration
vaStatus = vaCreateConfig(mVADisplay, VAProfileNone, VAEntrypointVideoProc, &attrib, 1, &mVAConfig);
CHECK_VASTATUS("vaCreateConfig");
// Create Context
ALOGV("ready to create context");
mWidth = width;
mHeight = height;
vaStatus = vaCreateContext(mVADisplay, mVAConfig, mWidth, mHeight, 0, NULL, 0, &mVAContext);
CHECK_VASTATUS("vaCreateContext");
ALOGV("VA has been successfully started");
return STATUS_OK;
}
status_t ISVWorker::deinit() {
{
Mutex::Autolock autoLock(mPipelineBufferLock);
while (!mPipelineBuffers.isEmpty()) {
VABufferID pipelineBuffer = mPipelineBuffers.itemAt(0);
if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, pipelineBuffer))
ALOGW("%s: failed to destroy va buffer id %d", __func__, pipelineBuffer);
mPipelineBuffers.removeAt(0);
}
}
if (mNumFilterBuffers != 0) {
for (uint32_t i = 0; i < mNumFilterBuffers; i++) {
if(VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, mFilterBuffers[i]))
ALOGW("%s: failed to destroy va buffer id %d", __func__, mFilterBuffers[i]);
}
mNumFilterBuffers = 0;
memset(&mFilterBuffers, VA_INVALID_ID, VAProcFilterCount * sizeof(VABufferID));
mFilterFrc = VA_INVALID_ID;
}
if (mForwardReferences != NULL) {
free(mForwardReferences);
mForwardReferences = NULL;
mNumForwardReferences = 0;
}
if (mVAContext != VA_INVALID_ID) {
vaDestroyContext(mVADisplay, mVAContext);
mVAContext = VA_INVALID_ID;
}
if (mVAConfig != VA_INVALID_ID) {
vaDestroyConfig(mVADisplay, mVAConfig);
mVAConfig = VA_INVALID_ID;
}
if (mVADisplay) {
vaTerminate(mVADisplay);
mVADisplay = NULL;
}
if (mDisplay) {
delete mDisplay;
mDisplay = NULL;
}
return STATUS_OK;
}
status_t ISVWorker::allocSurface(uint32_t* width, uint32_t* height,
uint32_t stride, uint32_t format, unsigned long handle, int32_t* surfaceId)
{
if (mWidth == 0 || mHeight == 0) {
ALOGE("%s: isv worker has not been initialized.", __func__);
return STATUS_ERROR;
}
#ifndef TARGET_VPP_USE_GEN
*width = mWidth;
*height = mHeight;
#endif
// Create VASurfaces
VASurfaceAttrib attribs[3];
VASurfaceAttribExternalBuffers vaExtBuf;
memset(&vaExtBuf, 0, sizeof(VASurfaceAttribExternalBuffers));
switch(format) {
case HAL_PIXEL_FORMAT_YV12:
vaExtBuf.pixel_format = VA_FOURCC_YV12;
vaExtBuf.num_planes = 3;
vaExtBuf.pitches[0] = stride;
vaExtBuf.pitches[1] = stride / 2;
vaExtBuf.pitches[2] = stride / 2;
vaExtBuf.pitches[3] = 0;
vaExtBuf.offsets[0] = 0;
vaExtBuf.offsets[1] = stride * *height;
vaExtBuf.offsets[2] = vaExtBuf.offsets[1] + (stride / 2) * (*height / 2);
vaExtBuf.offsets[3] = 0;
break;
case HAL_PIXEL_FORMAT_INTEL_YV12:
vaExtBuf.pixel_format = VA_FOURCC_YV12;
vaExtBuf.num_planes = 3;
vaExtBuf.pitches[0] = stride;
vaExtBuf.pitches[1] = stride / 2;
vaExtBuf.pitches[2] = stride / 2;
vaExtBuf.pitches[3] = 0;
vaExtBuf.offsets[0] = 0;
// The height of HAL_PIXEL_FORMAT_INTEL_YV12 gralloc buffer has been aligned with 32 pixels.
vaExtBuf.offsets[1] = stride * ((*height + 31) & ~31);
vaExtBuf.offsets[2] = vaExtBuf.offsets[1] + (stride / 2) * (((*height + 31) & ~31) / 2);
vaExtBuf.offsets[3] = 0;
break;
#ifdef TARGET_VPP_USE_GEN
case HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL:
case HAL_PIXEL_FORMAT_NV12_X_TILED_INTEL:
//it will be removed in future, it indicate the same format with HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL
case HAL_PIXEL_FORMAT_YUV420PackedSemiPlanar_Tiled_INTEL:
#else
case HAL_PIXEL_FORMAT_NV12_VED:
case HAL_PIXEL_FORMAT_NV12_VEDT:
#endif
vaExtBuf.pixel_format = VA_FOURCC_NV12;
vaExtBuf.num_planes = 2;
vaExtBuf.pitches[0] = stride;
vaExtBuf.pitches[1] = stride;
vaExtBuf.pitches[2] = 0;
vaExtBuf.pitches[3] = 0;
vaExtBuf.offsets[0] = 0;
vaExtBuf.offsets[1] = stride * *height;
vaExtBuf.offsets[2] = 0;
vaExtBuf.offsets[3] = 0;
break;
default:
ALOGE("%s: can't support this format 0x%08x", __func__, format);
return STATUS_ERROR;
}
vaExtBuf.width = *width;
vaExtBuf.height = *height;
vaExtBuf.data_size = stride * *height * 1.5;
vaExtBuf.num_buffers = 1;
#ifndef TARGET_VPP_USE_GEN
if (format == HAL_PIXEL_FORMAT_NV12_VEDT) {
ALOGV("set TILING flag");
vaExtBuf.flags |= VA_SURFACE_EXTBUF_DESC_ENABLE_TILING;
}
#endif
vaExtBuf.flags |= VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;
vaExtBuf.buffers = (long unsigned int*)&handle;
attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType;
attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
attribs[0].value.type = VAGenericValueTypeInteger;
attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;
attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor;
attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
attribs[1].value.type = VAGenericValueTypePointer;
attribs[1].value.value.p = &vaExtBuf;
attribs[2].type = (VASurfaceAttribType)VASurfaceAttribUsageHint;
attribs[2].flags = VA_SURFACE_ATTRIB_SETTABLE;
attribs[2].value.type = VAGenericValueTypeInteger;
attribs[2].value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
ALOGV("%s: Ext buffer: width %d, height %d, data_size %d, pitch %d", __func__,
vaExtBuf.width, vaExtBuf.height, vaExtBuf.data_size, vaExtBuf.pitches[0]);
VAStatus vaStatus = vaCreateSurfaces(mVADisplay, VA_RT_FORMAT_YUV420, vaExtBuf.width,
vaExtBuf.height, (VASurfaceID*)surfaceId, 1, attribs, 3);
CHECK_VASTATUS("vaCreateSurfaces");
return (vaStatus == VA_STATUS_SUCCESS) ? STATUS_OK : STATUS_ERROR;
}
status_t ISVWorker::freeSurface(int32_t* surfaceId)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
vaDestroySurfaces(mVADisplay, (VASurfaceID*)surfaceId, 1);
CHECK_VASTATUS("vaDestroySurfaces");
return (vaStatus == VA_STATUS_SUCCESS) ? STATUS_OK : STATUS_ERROR;
}
status_t ISVWorker::configFilters(uint32_t filters,
const FilterParam* filterParam)
{
status_t ret = STATUS_OK;
if (!filterParam) {
ALOGE("%s: invalid filterParam", __func__);
return STATUS_ERROR;
}
if (filters != 0) {
mFilterParam.srcWidth = filterParam->srcWidth;
mFilterParam.srcHeight = filterParam->srcHeight;
mFilterParam.dstWidth = filterParam->dstWidth;
mFilterParam.dstHeight = filterParam->dstHeight;
mFilterParam.frameRate = filterParam->frameRate;
mFilterParam.frcRate = filterParam->frcRate;
}
if (mFilters != filters) {
mFilters = filters;
ALOGI("%s: mFilters 0x%x, fps %d, frc rate %d", __func__, mFilters, mFilterParam.frameRate, mFilterParam.frcRate);
ret = setupFilters();
}
return ret;
}
bool ISVWorker::isFpsSupport(int32_t fps, int32_t *fpsSet, int32_t fpsSetCnt) {
bool ret = false;
for (int32_t i = 0; i < fpsSetCnt; i++) {
if (fps == fpsSet[i]) {
ret = true;
break;
}
}
return ret;
}
status_t ISVWorker::setupFilters() {
ALOGV("setupFilters");
VAProcFilterParameterBuffer deblock, denoise, sharpen, stde;
VAProcFilterParameterBufferDeinterlacing deint;
VAProcFilterParameterBufferColorBalance color[COLOR_NUM];
VAProcFilterParameterBufferFrameRateConversion frc;
VABufferID deblockId, denoiseId, deintId, sharpenId, colorId, frcId, stdeId;
uint32_t numCaps;
VAProcFilterCap deblockCaps, denoiseCaps, sharpenCaps, frcCaps, stdeCaps;
VAProcFilterCapDeinterlacing deinterlacingCaps[VAProcDeinterlacingCount];
VAProcFilterCapColorBalance colorCaps[COLOR_NUM];
VAStatus vaStatus;
uint32_t numSupportedFilters = VAProcFilterCount;
VAProcFilterType supportedFilters[VAProcFilterCount];
if (mNumFilterBuffers != 0) {
for (uint32_t i = 0; i < mNumFilterBuffers; i++) {
if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, mFilterBuffers[i]))
ALOGW("%s: failed to destroy va buffer %d", __func__, mFilterBuffers[i]);
//return STATUS_ERROR;
}
memset(&mFilterBuffers, VA_INVALID_ID, VAProcFilterCount * sizeof(VABufferID));
mFilterFrc = VA_INVALID_ID;
mNumFilterBuffers = 0;
}
// query supported filters
vaStatus = vaQueryVideoProcFilters(mVADisplay, mVAContext, supportedFilters, &numSupportedFilters);
CHECK_VASTATUS("vaQueryVideoProcFilters");
// create filter buffer for each filter
for (uint32_t i = 0; i < numSupportedFilters; i++) {
switch (supportedFilters[i]) {
case VAProcFilterDeblocking:
if ((mFilters & FilterDeblocking) != 0) {
// check filter caps
numCaps = 1;
vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
VAProcFilterDeblocking,
&deblockCaps,
&numCaps);
CHECK_VASTATUS("vaQueryVideoProcFilterCaps for deblocking");
// create parameter buffer
deblock.type = VAProcFilterDeblocking;
deblock.value = deblockCaps.range.min_value + DENOISE_DEBLOCK_STRENGTH * deblockCaps.range.step;
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAProcFilterParameterBufferType, sizeof(deblock), 1,
&deblock, &deblockId);
CHECK_VASTATUS("vaCreateBuffer for deblocking");
mFilterBuffers[mNumFilterBuffers] = deblockId;
mNumFilterBuffers++;
}
break;
case VAProcFilterNoiseReduction:
if((mFilters & FilterNoiseReduction) != 0) {
// check filter caps
numCaps = 1;
vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
VAProcFilterNoiseReduction,
&denoiseCaps,
&numCaps);
CHECK_VASTATUS("vaQueryVideoProcFilterCaps for denoising");
// create parameter buffer
denoise.type = VAProcFilterNoiseReduction;
#ifdef TARGET_VPP_USE_GEN
char propValueString[PROPERTY_VALUE_MAX];
// placeholder for vpg driver: can't support denoise factor auto adjust, so leave config to user.
property_get("vpp.filter.denoise.factor", propValueString, "64.0");
denoise.value = atof(propValueString);
denoise.value = (denoise.value < 0.0f) ? 0.0f : denoise.value;
denoise.value = (denoise.value > 64.0f) ? 64.0f : denoise.value;
#else
denoise.value = denoiseCaps.range.min_value + DENOISE_DEBLOCK_STRENGTH * denoiseCaps.range.step;
#endif
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAProcFilterParameterBufferType, sizeof(denoise), 1,
&denoise, &denoiseId);
CHECK_VASTATUS("vaCreateBuffer for denoising");
mFilterBuffers[mNumFilterBuffers] = denoiseId;
mNumFilterBuffers++;
}
break;
case VAProcFilterDeinterlacing:
if ((mFilters & FilterDeinterlacing) != 0) {
numCaps = VAProcDeinterlacingCount;
vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
VAProcFilterDeinterlacing,
&deinterlacingCaps[0],
&numCaps);
CHECK_VASTATUS("vaQueryVideoProcFilterCaps for deinterlacing");
for (uint32_t i = 0; i < numCaps; i++)
{
VAProcFilterCapDeinterlacing * const cap = &deinterlacingCaps[i];
if (cap->type != VAProcDeinterlacingBob) // desired Deinterlacing Type
continue;
deint.type = VAProcFilterDeinterlacing;
deint.algorithm = VAProcDeinterlacingBob;
vaStatus = vaCreateBuffer(mVADisplay,
mVAContext,
VAProcFilterParameterBufferType,
sizeof(deint), 1,
&deint, &deintId);
CHECK_VASTATUS("vaCreateBuffer for deinterlacing");
mFilterBuffers[mNumFilterBuffers] = deintId;
mNumFilterBuffers++;
}
}
break;
case VAProcFilterSharpening:
if((mFilters & FilterSharpening) != 0) {
// check filter caps
numCaps = 1;
vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
VAProcFilterSharpening,
&sharpenCaps,
&numCaps);
CHECK_VASTATUS("vaQueryVideoProcFilterCaps for sharpening");
// create parameter buffer
sharpen.type = VAProcFilterSharpening;
#ifdef TARGET_VPP_USE_GEN
char propValueString[PROPERTY_VALUE_MAX];
// placeholder for vpg driver: can't support sharpness factor auto adjust, so leave config to user.
property_get("vpp.filter.sharpen.factor", propValueString, "8.0");
sharpen.value = atof(propValueString);
sharpen.value = (sharpen.value < 0.0f) ? 0.0f : sharpen.value;
sharpen.value = (sharpen.value > 64.0f) ? 64.0f : sharpen.value;
#else
sharpen.value = sharpenCaps.range.default_value;
#endif
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAProcFilterParameterBufferType, sizeof(sharpen), 1,
&sharpen, &sharpenId);
CHECK_VASTATUS("vaCreateBuffer for sharpening");
mFilterBuffers[mNumFilterBuffers] = sharpenId;
mNumFilterBuffers++;
}
break;
case VAProcFilterColorBalance:
if((mFilters & FilterColorBalance) != 0) {
uint32_t featureCount = 0;
// check filter caps
// FIXME: it's not used at all!
numCaps = COLOR_NUM;
vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
VAProcFilterColorBalance,
colorCaps,
&numCaps);
CHECK_VASTATUS("vaQueryVideoProcFilterCaps for color balance");
// create parameter buffer
for (uint32_t i = 0; i < numCaps; i++) {
if (colorCaps[i].type == VAProcColorBalanceAutoSaturation) {
color[i].type = VAProcFilterColorBalance;
color[i].attrib = VAProcColorBalanceAutoSaturation;
color[i].value = colorCaps[i].range.min_value + COLOR_STRENGTH * colorCaps[i].range.step;
featureCount++;
}
else if (colorCaps[i].type == VAProcColorBalanceAutoBrightness) {
color[i].type = VAProcFilterColorBalance;
color[i].attrib = VAProcColorBalanceAutoBrightness;
color[i].value = colorCaps[i].range.min_value + COLOR_STRENGTH * colorCaps[i].range.step;
featureCount++;
}
}
#ifdef TARGET_VPP_USE_GEN
//TODO: VPG need to support check input value by colorCaps.
enum {kHue = 0, kSaturation, kBrightness, kContrast};
char propValueString[PROPERTY_VALUE_MAX];
color[kHue].type = VAProcFilterColorBalance;
color[kHue].attrib = VAProcColorBalanceHue;
// placeholder for vpg driver: can't support auto color balance, so leave config to user.
property_get("vpp.filter.procamp.hue", propValueString, "179.0");
color[kHue].value = atof(propValueString);
color[kHue].value = (color[kHue].value < -180.0f) ? -180.0f : color[kHue].value;
color[kHue].value = (color[kHue].value > 180.0f) ? 180.0f : color[kHue].value;
featureCount++;
color[kSaturation].type = VAProcFilterColorBalance;
color[kSaturation].attrib = VAProcColorBalanceSaturation;
property_get("vpp.filter.procamp.saturation", propValueString, "1.0");
color[kSaturation].value = atof(propValueString);
color[kSaturation].value = (color[kSaturation].value < 0.0f) ? 0.0f : color[kSaturation].value;
color[kSaturation].value = (color[kSaturation].value > 10.0f) ? 10.0f : color[kSaturation].value;
featureCount++;
color[kBrightness].type = VAProcFilterColorBalance;
color[kBrightness].attrib = VAProcColorBalanceBrightness;
property_get("vpp.filter.procamp.brightness", propValueString, "0.0");
color[kBrightness].value = atof(propValueString);
color[kBrightness].value = (color[kBrightness].value < -100.0f) ? -100.0f : color[kBrightness].value;
color[kBrightness].value = (color[kBrightness].value > 100.0f) ? 100.0f : color[kBrightness].value;
featureCount++;
color[kContrast].type = VAProcFilterColorBalance;
color[kContrast].attrib = VAProcColorBalanceContrast;
property_get("vpp.filter.procamp.contrast", propValueString, "1.0");
color[kContrast].value = atof(propValueString);
color[kContrast].value = (color[kContrast].value < 0.0f) ? 0.0f : color[kContrast].value;
color[kContrast].value = (color[kContrast].value > 10.0f) ? 10.0f : color[kContrast].value;
featureCount++;
#endif
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAProcFilterParameterBufferType, sizeof(*color), featureCount,
color, &colorId);
CHECK_VASTATUS("vaCreateBuffer for color balance");
mFilterBuffers[mNumFilterBuffers] = colorId;
mNumFilterBuffers++;
}
break;
case VAProcFilterFrameRateConversion:
if((mFilters & FilterFrameRateConversion) != 0) {
frc.type = VAProcFilterFrameRateConversion;
frc.input_fps = mFilterParam.frameRate;
switch (mFilterParam.frcRate){
case FRC_RATE_1X:
frc.output_fps = frc.input_fps;
break;
case FRC_RATE_2X:
frc.output_fps = frc.input_fps * 2;
break;
case FRC_RATE_2_5X:
frc.output_fps = frc.input_fps * 5/2;
break;
case FRC_RATE_4X:
frc.output_fps = frc.input_fps * 4;
break;
}
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAProcFilterParameterBufferType, sizeof(frc), 1,
&frc, &frcId);
CHECK_VASTATUS("vaCreateBuffer for frc");
mFilterBuffers[mNumFilterBuffers] = frcId;
mNumFilterBuffers++;
mFilterFrc = frcId;
}
break;
case VAProcFilterSkinToneEnhancement:
if((mFilters & FilterSkinToneEnhancement) != 0) {
// check filter caps
numCaps = 1;
vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
VAProcFilterSkinToneEnhancement,
&stdeCaps,
&numCaps);
CHECK_VASTATUS("vaQueryVideoProcFilterCaps for skintone");
// create parameter buffer
stde.type = VAProcFilterSkinToneEnhancement;
#ifdef TARGET_VPP_USE_GEN
char propValueString[PROPERTY_VALUE_MAX];
// placeholder for vpg driver: can't support skintone factor auto adjust, so leave config to user.
property_get("vpp.filter.skintone.factor", propValueString, "8.0");
stde.value = atof(propValueString);
stde.value = (stde.value < 0.0f) ? 0.0f : stde.value;
stde.value = (stde.value > 8.0f) ? 8.0f : stde.value;
#else
stde.value = stdeCaps.range.default_value;
#endif
vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
VAProcFilterParameterBufferType, sizeof(stde), 1,
&stde, &stdeId);
CHECK_VASTATUS("vaCreateBuffer for skintone");
mFilterBuffers[mNumFilterBuffers] = stdeId;
mNumFilterBuffers++;
}
break;
default:
ALOGW("%s: Not supported filter 0x%08x", __func__, supportedFilters[i]);
break;
}
}
return setupPipelineCaps();
}
status_t ISVWorker::setupPipelineCaps() {
ALOGV("setupPipelineCaps");
//TODO color standards
VAProcPipelineCaps pipelineCaps;
VAStatus vaStatus;
pipelineCaps.input_color_standards = in_color_standards;
pipelineCaps.num_input_color_standards = VAProcColorStandardCount;
pipelineCaps.output_color_standards = out_color_standards;
pipelineCaps.num_output_color_standards = VAProcColorStandardCount;
vaStatus = vaQueryVideoProcPipelineCaps(mVADisplay, mVAContext,
mFilterBuffers, mNumFilterBuffers,
&pipelineCaps);
CHECK_VASTATUS("vaQueryVideoProcPipelineCaps");
if (mForwardReferences != NULL) {
free(mForwardReferences);
mForwardReferences = NULL;
mNumForwardReferences = 0;
}
mNumForwardReferences = pipelineCaps.num_forward_references;
if (mNumForwardReferences > 0) {
mForwardReferences = (VASurfaceID*)malloc(mNumForwardReferences * sizeof(VASurfaceID));
if (mForwardReferences == NULL)
return STATUS_ALLOCATION_ERROR;
memset(mForwardReferences, 0, mNumForwardReferences * sizeof(VASurfaceID));
}
return STATUS_OK;
}
status_t ISVWorker::process(ISVBuffer* inputBuffer, Vector<ISVBuffer*> outputBuffer,
uint32_t outputCount, bool isEOS, uint32_t flags) {
ALOGV("process: outputCount=%d, mInputIndex=%d", outputCount, mInputIndex);
VASurfaceID input;
VASurfaceID output[MAX_FRC_OUTPUT];
VABufferID pipelineId;
VAProcPipelineParameterBuffer *pipeline;
VAProcFilterParameterBufferFrameRateConversion *frc;
VAStatus vaStatus = STATUS_OK;
uint32_t i = 0;
if (isEOS) {
if (mInputIndex == 0) {
ALOGV("%s: don't need to flush VSP", __func__);
return STATUS_OK;
}
input = VA_INVALID_SURFACE;
outputCount = 1;
output[0] = mPrevOutput;
} else {
if (!inputBuffer || outputBuffer.size() != outputCount) {
ALOGE("%s: invalid input/output buffer", __func__);
return STATUS_ERROR;
}
if (outputCount < 1 || outputCount > 4) {
ALOGE("%s: invalid outputCount", __func__);
return STATUS_ERROR;
}
input = inputBuffer->getSurface();
for (i = 0; i < outputCount; i++) {
output[i] = outputBuffer[i]->getSurface();
if (output[i] == VA_INVALID_SURFACE) {
ALOGE("invalid output buffer");
return STATUS_ERROR;
}
}
}
// reference frames setting
if (mNumForwardReferences > 0) {
/* add previous frame into reference array*/
for (i = 1; i < mNumForwardReferences; i++) {
mForwardReferences[i - 1] = mForwardReferences[i];
}
//make last reference to input
mForwardReferences[mNumForwardReferences - 1] = mPrevInput;
}
mPrevInput = input;
// create pipeline parameter buffer
vaStatus = vaCreateBuffer(mVADisplay,
mVAContext,
VAProcPipelineParameterBufferType,
sizeof(VAProcPipelineParameterBuffer),
1,
NULL,
&pipelineId);
CHECK_VASTATUS("vaCreateBuffer for VAProcPipelineParameterBufferType");
ALOGV("before vaBeginPicture");
vaStatus = vaBeginPicture(mVADisplay, mVAContext, output[0]);
CHECK_VASTATUS("vaBeginPicture");
// map pipeline paramter buffer
vaStatus = vaMapBuffer(mVADisplay, pipelineId, (void**)&pipeline);
CHECK_VASTATUS("vaMapBuffer for pipeline parameter buffer");
// frc pamameter setting
if ((mFilters & FilterFrameRateConversion) != 0) {
vaStatus = vaMapBuffer(mVADisplay, mFilterFrc, (void **)&frc);
CHECK_VASTATUS("vaMapBuffer for frc parameter buffer");
if (isEOS)
frc->num_output_frames = 0;
else
frc->num_output_frames = outputCount - 1;
frc->output_frames = output + 1;
}
// pipeline parameter setting
VARectangle dst_region;
dst_region.x = 0;
dst_region.y = 0;
dst_region.width = mFilterParam.dstWidth;
dst_region.height = mFilterParam.dstHeight;
VARectangle src_region;
src_region.x = 0;
src_region.y = 0;
src_region.width = mFilterParam.srcWidth;
src_region.height = mFilterParam.srcHeight;
if (isEOS) {
pipeline->surface = 0;
pipeline->pipeline_flags = VA_PIPELINE_FLAG_END;
}
else {
pipeline->surface = input;
pipeline->pipeline_flags = 0;
}
#ifdef TARGET_VPP_USE_GEN
pipeline->surface_region = &src_region;
pipeline->output_region = &dst_region;
pipeline->surface_color_standard = VAProcColorStandardBT601;
pipeline->output_color_standard = VAProcColorStandardBT601;
#else
pipeline->surface_region = NULL;
pipeline->output_region = NULL;//&output_region;
pipeline->surface_color_standard = VAProcColorStandardNone;
pipeline->output_color_standard = VAProcColorStandardNone;
/* real rotate state will be decided in psb video */
pipeline->rotation_state = 0;
#endif
/* FIXME: set more meaningful background color */
pipeline->output_background_color = 0;
pipeline->filters = mFilterBuffers;
pipeline->num_filters = mNumFilterBuffers;
pipeline->forward_references = mForwardReferences;
pipeline->num_forward_references = mNumForwardReferences;
pipeline->backward_references = NULL;
pipeline->num_backward_references = 0;
//currently, we only transfer TOP field to frame, no frame rate change.
if (flags & (OMX_BUFFERFLAG_TFF | OMX_BUFFERFLAG_BFF)) {
pipeline->filter_flags = VA_TOP_FIELD;
} else {
pipeline->filter_flags = VA_FRAME_PICTURE;
}
if ((mFilters & FilterFrameRateConversion) != 0) {
vaStatus = vaUnmapBuffer(mVADisplay, mFilterFrc);
CHECK_VASTATUS("vaUnmapBuffer for frc parameter buffer");
}
vaStatus = vaUnmapBuffer(mVADisplay, pipelineId);
CHECK_VASTATUS("vaUnmapBuffer for pipeline parameter buffer");
ALOGV("before vaRenderPicture");
// Send parameter to driver
vaStatus = vaRenderPicture(mVADisplay, mVAContext, &pipelineId, 1);
CHECK_VASTATUS("vaRenderPicture");
ALOGV("before vaEndPicture");
vaStatus = vaEndPicture(mVADisplay, mVAContext);
CHECK_VASTATUS("vaEndPicture");
if (isEOS) {
vaStatus = vaSyncSurface(mVADisplay, mPrevOutput);
CHECK_VASTATUS("vaSyncSurface");
if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, pipelineId)) {
ALOGE("%s: failed to destroy va buffer %d", __func__, pipelineId);
return STATUS_ERROR;
}
return STATUS_OK;
}
mPrevOutput = output[0];
mInputIndex++;
Mutex::Autolock autoLock(mPipelineBufferLock);
mPipelineBuffers.push_back(pipelineId);
ALOGV("process, exit");
return STATUS_OK;
}
status_t ISVWorker::fill(Vector<ISVBuffer*> outputBuffer, uint32_t outputCount) {
ALOGV("fill, outputCount=%d, mOutputIndex=%d",outputCount, mOutputIndex);
// get output surface
VASurfaceID output[MAX_FRC_OUTPUT];
VAStatus vaStatus;
VASurfaceStatus surStatus;
if (outputCount < 1)
return STATUS_ERROR;
// map GraphicBuffer to VASurface
for (uint32_t i = 0; i < outputCount; i++) {
output[i] = outputBuffer[i]->getSurface();
if (output[i] == VA_INVALID_SURFACE) {
ALOGE("invalid output buffer");
return STATUS_ERROR;
}
//FIXME: only enable sync mode
#if 0
vaStatus = vaQuerySurfaceStatus(mVADisplay, output[i],&surStatus);
CHECK_VASTATUS("vaQuerySurfaceStatus");
if (surStatus == VASurfaceRendering) {
ALOGV("Rendering %d", i);
/* The behavior of driver is: all output of one process task are return in one interruption.
The whole outputs of one FRC task are all ready or none of them is ready.
If the behavior changed, it hurts the performance.
*/
if (0 != i) {
ALOGW("*****Driver behavior changed. The performance is hurt.");
ALOGW("Please check driver behavior: all output of one task return in one interruption.");
}
vaStatus = STATUS_DATA_RENDERING;
break;
}
if ((surStatus != VASurfaceRendering) && (surStatus != VASurfaceReady)) {
ALOGE("surface statu Error %d", surStatus);
vaStatus = STATUS_ERROR;
}
#endif
vaStatus = vaSyncSurface(mVADisplay, output[i]);
CHECK_VASTATUS("vaSyncSurface");
vaStatus = STATUS_OK;
mOutputCount++;
//dumpYUVFrameData(output[i]);
}
{
Mutex::Autolock autoLock(mPipelineBufferLock);
if (vaStatus == STATUS_OK) {
VABufferID pipelineBuffer = mPipelineBuffers.itemAt(0);
if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, pipelineBuffer)) {
ALOGE("%s: failed to destroy va buffer %d", __func__, pipelineBuffer);
return STATUS_ERROR;
}
mPipelineBuffers.removeAt(0);
mOutputIndex++;
}
}
ALOGV("fill, exit");
return vaStatus;
}
// Debug only
#define FRAME_OUTPUT_FILE_NV12 "/storage/sdcard0/vpp_output.nv12"
status_t ISVWorker::dumpYUVFrameData(VASurfaceID surfaceID) {
status_t ret;
if (surfaceID == VA_INVALID_SURFACE)
return STATUS_ERROR;
VAStatus vaStatus;
VAImage image;
unsigned char *data_ptr;
vaStatus = vaDeriveImage(mVADisplay,
surfaceID,
&image);
CHECK_VASTATUS("vaDeriveImage");
vaStatus = vaMapBuffer(mVADisplay, image.buf, (void **)&data_ptr);
CHECK_VASTATUS("vaMapBuffer");
ret = writeNV12(mFilterParam.srcWidth, mFilterParam.srcHeight, data_ptr, image.pitches[0], image.pitches[1]);
if (ret != STATUS_OK) {
ALOGV("writeNV12 error");
return STATUS_ERROR;
}
vaStatus = vaUnmapBuffer(mVADisplay, image.buf);
CHECK_VASTATUS("vaUnMapBuffer");
vaStatus = vaDestroyImage(mVADisplay,image.image_id);
CHECK_VASTATUS("vaDestroyImage");
return STATUS_OK;
}
status_t ISVWorker::reset() {
status_t ret;
ALOGV("reset");
if (mOutputCount > 0) {
ALOGI("======mVPPInputCount=%d, mVPPRenderCount=%d======",
mInputIndex, mOutputCount);
}
mInputIndex = 0;
mOutputIndex = 0;
mOutputCount = 0;
{
Mutex::Autolock autoLock(mPipelineBufferLock);
while (!mPipelineBuffers.isEmpty()) {
VABufferID pipelineBuffer = mPipelineBuffers.itemAt(0);
if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, pipelineBuffer)) {
ALOGE("%s: failed to destroy va buffer %d", __func__, pipelineBuffer);
return STATUS_ERROR;
}
mPipelineBuffers.removeAt(0);
}
}
if (mNumFilterBuffers != 0) {
for (uint32_t i = 0; i < mNumFilterBuffers; i++) {
if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, mFilterBuffers[i]))
ALOGW("%s: failed to destroy va buffer %d", __func__, mFilterBuffers[i]);
//return STATUS_ERROR;
}
mNumFilterBuffers = 0;
memset(&mFilterBuffers, VA_INVALID_ID, VAProcFilterCount * sizeof(VABufferID));
mFilterFrc = VA_INVALID_ID;
}
// we need to clear the cache for reference surfaces
if (mForwardReferences != NULL) {
free(mForwardReferences);
mForwardReferences = NULL;
mNumForwardReferences = 0;
}
if (mVAContext != VA_INVALID_ID) {
vaDestroyContext(mVADisplay, mVAContext);
mVAContext = VA_INVALID_ID;
}
VAStatus vaStatus = vaCreateContext(mVADisplay, mVAConfig, mWidth, mHeight, 0, NULL, 0, &mVAContext);
CHECK_VASTATUS("vaCreateContext");
return setupFilters();
}
uint32_t ISVWorker::getVppOutputFps() {
uint32_t outputFps;
//mFilterParam.frcRate is 1 if FRC is disabled or input FPS is not changed by VPP.
if (FRC_RATE_2_5X == mFilterParam.frcRate) {
outputFps = mFilterParam.frameRate * 5 / 2;
} else {
outputFps = mFilterParam.frameRate * mFilterParam.frcRate;
}
ALOGV("vpp is on in settings %d %d %d", outputFps, mFilterParam.frameRate, mFilterParam.frcRate);
return outputFps;
}
status_t ISVWorker::writeNV12(int width, int height, unsigned char *out_buf, int y_pitch, int uv_pitch) {
size_t result;
int frame_size;
unsigned char *y_start, *uv_start;
int h;
FILE *ofile = fopen(FRAME_OUTPUT_FILE_NV12, "ab");
if(ofile == NULL) {
ALOGE("Open %s failed!", FRAME_OUTPUT_FILE_NV12);
return STATUS_ERROR;
}
if (out_buf == NULL)
{
fclose(ofile);
return STATUS_ERROR;
}
if ((width % 2) || (height % 2))
{
fclose(ofile);
return STATUS_ERROR;
}
// Set frame size
frame_size = height * width * 3/2;
/* write y */
y_start = out_buf;
for (h = 0; h < height; ++h) {
result = fwrite(y_start, sizeof(unsigned char), width, ofile);
y_start += y_pitch;
}
/* write uv */
uv_start = out_buf + uv_pitch * height;
for (h = 0; h < height / 2; ++h) {
result = fwrite(uv_start, sizeof(unsigned char), width, ofile);
uv_start += uv_pitch;
}
// Close file
fclose(ofile);
return STATUS_OK;
}