/*
// Copyright (c) 2014 Intel Corporation
//
// 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 <HwcTrace.h>
#include <Hwcomposer.h>
#include <DisplayPlaneManager.h>
#include <DisplayQuery.h>
#include <VirtualDevice.h>
#include <SoftVsyncObserver.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <hal_public.h>
#include <libsync/sw_sync.h>
#include <sync/sync.h>
#include <va/va_android.h>
#include <va/va_vpp.h>
#include <va/va_tpi.h>
#include <cutils/properties.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define NUM_CSC_BUFFERS 6
#define NUM_SCALING_BUFFERS 3
#define QCIF_WIDTH 176
#define QCIF_HEIGHT 144
namespace android {
namespace intel {
static inline uint32_t align_width(uint32_t val)
{
return align_to(val, 64);
}
static inline uint32_t align_height(uint32_t val)
{
return align_to(val, 16);
}
static void my_close_fence(const char* func, const char* fenceName, int& fenceFd)
{
if (fenceFd != -1) {
ALOGV("%s: closing fence %s (fd=%d)", func, fenceName, fenceFd);
int err = close(fenceFd);
if (err < 0) {
ALOGE("%s: fence %s close error %d: %s", func, fenceName, err, strerror(errno));
}
fenceFd = -1;
}
}
static void my_sync_wait_and_close(const char* func, const char* fenceName, int& fenceFd)
{
if (fenceFd != -1) {
ALOGV("%s: waiting on fence %s (fd=%d)", func, fenceName, fenceFd);
int err = sync_wait(fenceFd, 300);
if (err < 0) {
ALOGE("%s: fence %s sync_wait error %d: %s", func, fenceName, err, strerror(errno));
}
my_close_fence(func, fenceName, fenceFd);
}
}
static void my_timeline_inc(const char* func, const char* timelineName, int& syncTimelineFd)
{
if (syncTimelineFd != -1) {
ALOGV("%s: incrementing timeline %s (fd=%d)", func, timelineName, syncTimelineFd);
int err = sw_sync_timeline_inc(syncTimelineFd, 1);
if (err < 0)
ALOGE("%s sync timeline %s increment error %d: %s", func, timelineName, errno, strerror(errno));
syncTimelineFd = -1;
}
}
#define CLOSE_FENCE(fenceName) my_close_fence(__func__, #fenceName, fenceName)
#define SYNC_WAIT_AND_CLOSE(fenceName) my_sync_wait_and_close(__func__, #fenceName, fenceName)
#define TIMELINE_INC(timelineName) my_timeline_inc(__func__, #timelineName, timelineName)
class MappedSurface {
public:
MappedSurface(VADisplay dpy, VASurfaceID surf)
: va_dpy(dpy),
ptr(NULL)
{
VAStatus va_status;
va_status = vaDeriveImage(va_dpy, surf, &image);
if (va_status != VA_STATUS_SUCCESS) {
ETRACE("vaDeriveImage returns %08x", va_status);
return;
}
va_status = vaMapBuffer(va_dpy, image.buf, (void**)&ptr);
if (va_status != VA_STATUS_SUCCESS) {
ETRACE("vaMapBuffer returns %08x", va_status);
vaDestroyImage(va_dpy, image.image_id);
return;
}
}
~MappedSurface() {
if (ptr == NULL)
return;
VAStatus va_status;
va_status = vaUnmapBuffer(va_dpy, image.buf);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaUnmapBuffer returns %08x", va_status);
va_status = vaDestroyImage(va_dpy, image.image_id);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroyImage returns %08x", va_status);
}
bool valid() { return ptr != NULL; }
uint8_t* getPtr() { return ptr; }
private:
VADisplay va_dpy;
VAImage image;
uint8_t* ptr;
};
class VirtualDevice::VAMappedHandle {
public:
VAMappedHandle(VADisplay dpy, buffer_handle_t handle, uint32_t stride, uint32_t height, unsigned int pixel_format)
: va_dpy(dpy),
surface(0)
{
VTRACE("Map gralloc %p size=%ux%u", handle, stride, height);
unsigned int format;
unsigned long buffer = reinterpret_cast<unsigned long>(handle);
VASurfaceAttribExternalBuffers buf;
buf.pixel_format = pixel_format;
buf.width = stride;
buf.height = height;
buf.buffers = &buffer;
buf.num_buffers = 1;
buf.flags = 0;
buf.private_data = NULL;
if (pixel_format == VA_FOURCC_RGBA || pixel_format == VA_FOURCC_BGRA) {
format = VA_RT_FORMAT_RGB32;
buf.data_size = stride * height * 4;
buf.num_planes = 3;
buf.pitches[0] = stride;
buf.pitches[1] = stride;
buf.pitches[2] = stride;
buf.pitches[3] = 0;
buf.offsets[0] = 0;
buf.offsets[1] = 0;
buf.offsets[2] = 0;
buf.offsets[3] = 0;
}
else {
format = VA_RT_FORMAT_YUV420;
buf.data_size = stride * height * 3/2;
buf.num_planes = 2;
buf.pitches[0] = stride;
buf.pitches[1] = stride;
buf.pitches[2] = 0;
buf.pitches[3] = 0;
buf.offsets[0] = 0;
buf.offsets[1] = stride * height;
}
VASurfaceAttrib attrib_list[3];
attrib_list[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType;
attrib_list[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
attrib_list[0].value.type = VAGenericValueTypeInteger;
attrib_list[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;
attrib_list[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor;
attrib_list[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
attrib_list[1].value.type = VAGenericValueTypePointer;
attrib_list[1].value.value.p = (void *)&buf;
attrib_list[2].type = (VASurfaceAttribType)VASurfaceAttribPixelFormat;
attrib_list[2].flags = VA_SURFACE_ATTRIB_SETTABLE;
attrib_list[2].value.type = VAGenericValueTypeInteger;
attrib_list[2].value.value.i = pixel_format;
VAStatus va_status;
va_status = vaCreateSurfaces(va_dpy,
format,
stride,
height,
&surface,
1,
attrib_list,
3);
if (va_status != VA_STATUS_SUCCESS) {
ETRACE("vaCreateSurfaces returns %08x, surface = %x", va_status, surface);
surface = 0;
}
}
VAMappedHandle(VADisplay dpy, buffer_handle_t khandle, uint32_t stride, uint32_t height, bool tiled)
: va_dpy(dpy),
surface(0)
{
int format;
VASurfaceAttributeTPI attribTpi;
memset(&attribTpi, 0, sizeof(attribTpi));
VTRACE("Map khandle 0x%x size=%ux%u", khandle, stride, height);
attribTpi.type = VAExternalMemoryKernelDRMBufffer;
attribTpi.width = stride;
attribTpi.height = height;
attribTpi.size = stride*height*3/2;
attribTpi.pixel_format = VA_FOURCC_NV12;
attribTpi.tiling = tiled;
attribTpi.luma_stride = stride;
attribTpi.chroma_u_stride = stride;
attribTpi.chroma_v_stride = stride;
attribTpi.luma_offset = 0;
attribTpi.chroma_u_offset = stride*height;
attribTpi.chroma_v_offset = stride*height+1;
format = VA_RT_FORMAT_YUV420;
attribTpi.count = 1;
attribTpi.buffers = (long unsigned int*) &khandle;
VAStatus va_status;
va_status = vaCreateSurfacesWithAttribute(va_dpy,
stride,
height,
format,
1,
&surface,
&attribTpi);
if (va_status != VA_STATUS_SUCCESS) {
ETRACE("vaCreateSurfacesWithAttribute returns %08x", va_status);
surface = 0;
}
}
~VAMappedHandle()
{
if (surface == 0)
return;
VAStatus va_status;
va_status = vaDestroySurfaces(va_dpy, &surface, 1);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroySurfaces returns %08x", va_status);
}
private:
VADisplay va_dpy;
public:
VASurfaceID surface;
};
// refcounted version of VAMappedHandle, to make caching easier
class VirtualDevice::VAMappedHandleObject : public RefBase, public VAMappedHandle {
public:
VAMappedHandleObject(VADisplay dpy, buffer_handle_t handle, uint32_t stride, uint32_t height, unsigned int pixel_format)
: VAMappedHandle(dpy, handle, stride, height, pixel_format) { }
VAMappedHandleObject(VADisplay dpy, buffer_handle_t khandle, uint32_t stride, uint32_t height, bool tiled)
: VAMappedHandle(dpy, khandle, stride, height, tiled) { }
protected:
~VAMappedHandleObject() {}
};
VirtualDevice::CachedBuffer::CachedBuffer(BufferManager *mgr, buffer_handle_t handle)
: manager(mgr),
mapper(NULL),
vaMappedHandle(NULL),
cachedKhandle(0)
{
DataBuffer *buffer = manager->lockDataBuffer((buffer_handle_t)handle);
mapper = manager->map(*buffer);
manager->unlockDataBuffer(buffer);
}
VirtualDevice::CachedBuffer::~CachedBuffer()
{
if (vaMappedHandle != NULL)
delete vaMappedHandle;
manager->unmap(mapper);
}
VirtualDevice::HeldDecoderBuffer::HeldDecoderBuffer(const sp<VirtualDevice>& vd, const android::sp<CachedBuffer>& cachedBuffer)
: vd(vd),
cachedBuffer(cachedBuffer)
{
if (!vd->mPayloadManager->setRenderStatus(cachedBuffer->mapper, true)) {
ETRACE("Failed to set render status");
}
}
VirtualDevice::HeldDecoderBuffer::~HeldDecoderBuffer()
{
if (!vd->mPayloadManager->setRenderStatus(cachedBuffer->mapper, false)) {
ETRACE("Failed to set render status");
}
}
struct VirtualDevice::Task : public RefBase {
virtual void run(VirtualDevice& vd) = 0;
virtual ~Task() {}
};
struct VirtualDevice::RenderTask : public VirtualDevice::Task {
RenderTask() : successful(false) { }
virtual void run(VirtualDevice& vd) = 0;
bool successful;
};
struct VirtualDevice::ComposeTask : public VirtualDevice::RenderTask {
ComposeTask()
: videoKhandle(0),
rgbHandle(NULL),
mappedRgbIn(NULL),
outputHandle(NULL),
yuvAcquireFenceFd(-1),
rgbAcquireFenceFd(-1),
outbufAcquireFenceFd(-1),
syncTimelineFd(-1) { }
virtual ~ComposeTask() {
// If queueCompose() creates this object and sets up fences,
// but aborts before enqueuing the task, or if the task runs
// but errors out, make sure our acquire fences get closed
// and any release fences get signaled.
CLOSE_FENCE(yuvAcquireFenceFd);
CLOSE_FENCE(rgbAcquireFenceFd);
CLOSE_FENCE(outbufAcquireFenceFd);
TIMELINE_INC(syncTimelineFd);
}
virtual void run(VirtualDevice& vd) {
bool dump = false;
if (vd.mDebugVspDump && ++vd.mDebugCounter > 200) {
dump = true;
vd.mDebugCounter = 0;
}
SYNC_WAIT_AND_CLOSE(yuvAcquireFenceFd);
VASurfaceID videoInSurface;
if (videoKhandle == 0) {
videoInSurface = vd.va_blank_yuv_in;
} else {
if (videoCachedBuffer->cachedKhandle != videoKhandle || videoCachedBuffer->vaMappedHandle == NULL) {
if (videoCachedBuffer->vaMappedHandle != NULL)
delete videoCachedBuffer->vaMappedHandle;
videoCachedBuffer->vaMappedHandle = new VAMappedHandle(vd.va_dpy, videoKhandle, videoStride, videoBufHeight, videoTiled);
videoCachedBuffer->cachedKhandle = videoKhandle;
}
videoInSurface = videoCachedBuffer->vaMappedHandle->surface;
}
if (videoInSurface == 0) {
ETRACE("Couldn't map video");
return;
}
SYNC_WAIT_AND_CLOSE(rgbAcquireFenceFd);
SYNC_WAIT_AND_CLOSE(outbufAcquireFenceFd);
VAMappedHandle mappedVideoOut(vd.va_dpy, outputHandle, align_width(outWidth), align_height(outHeight), (unsigned int)VA_FOURCC_NV12);
if (mappedVideoOut.surface == 0) {
ETRACE("Unable to map outbuf");
return;
}
if (dump)
dumpSurface(vd.va_dpy, "/data/misc/vsp_in.yuv", videoInSurface, videoStride*videoBufHeight*3/2);
if (mappedRgbIn != NULL) {
if (dump)
dumpSurface(vd.va_dpy, "/data/misc/vsp_in.rgb", mappedRgbIn->surface, align_width(outWidth)*align_height(outHeight)*4);
vd.vspCompose(videoInSurface, mappedRgbIn->surface, mappedVideoOut.surface, &surface_region, &output_region);
}
else if (rgbHandle != NULL) {
VAMappedHandle localMappedRgbIn(vd.va_dpy, rgbHandle, align_width(outWidth), align_height(outHeight), (unsigned int)VA_FOURCC_BGRA);
vd.vspCompose(videoInSurface, localMappedRgbIn.surface, mappedVideoOut.surface, &surface_region, &output_region);
}
else {
// No RGBA, so compose with 100% transparent RGBA frame.
if (dump)
dumpSurface(vd.va_dpy, "/data/misc/vsp_in.rgb", vd.va_blank_rgb_in, align_width(outWidth)*align_height(outHeight)*4);
vd.vspCompose(videoInSurface, vd.va_blank_rgb_in, mappedVideoOut.surface, &surface_region, &output_region);
}
if (dump)
dumpSurface(vd.va_dpy, "/data/misc/vsp_out.yuv", mappedVideoOut.surface, align_width(outWidth)*align_height(outHeight)*3/2);
TIMELINE_INC(syncTimelineFd);
successful = true;
}
void dumpSurface(VADisplay va_dpy, const char* filename, VASurfaceID surf, int size) {
MappedSurface dumpSurface(va_dpy, surf);
if (dumpSurface.valid()) {
int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (fd > 0) {
write(fd, dumpSurface.getPtr(), size);
close(fd);
ALOGI("Output dumped");
}
else
ALOGE("Error %d opening output file: %s", errno, strerror(errno));
}
else
ALOGE("Failed to map output for dump");
}
buffer_handle_t videoKhandle;
uint32_t videoStride;
uint32_t videoBufHeight;
bool videoTiled;
buffer_handle_t rgbHandle;
sp<RefBase> heldRgbHandle;
sp<VAMappedHandleObject> mappedRgbIn;
buffer_handle_t outputHandle;
VARectangle surface_region;
VARectangle output_region;
uint32_t outWidth;
uint32_t outHeight;
sp<CachedBuffer> videoCachedBuffer;
sp<RefBase> heldVideoBuffer;
int yuvAcquireFenceFd;
int rgbAcquireFenceFd;
int outbufAcquireFenceFd;
int syncTimelineFd;
};
struct VirtualDevice::EnableVspTask : public VirtualDevice::Task {
virtual void run(VirtualDevice& vd) {
vd.vspEnable(width, height);
}
uint32_t width;
uint32_t height;
};
struct VirtualDevice::DisableVspTask : public VirtualDevice::Task {
virtual void run(VirtualDevice& vd) {
vd.vspDisable();
}
};
struct VirtualDevice::BlitTask : public VirtualDevice::RenderTask {
BlitTask()
: srcAcquireFenceFd(-1),
destAcquireFenceFd(-1),
syncTimelineFd(-1) { }
virtual ~BlitTask()
{
// If queueColorConvert() creates this object and sets up fences,
// but aborts before enqueuing the task, or if the task runs
// but errors out, make sure our acquire fences get closed
// and any release fences get signaled.
CLOSE_FENCE(srcAcquireFenceFd);
CLOSE_FENCE(destAcquireFenceFd);
TIMELINE_INC(syncTimelineFd);
}
virtual void run(VirtualDevice& vd) {
SYNC_WAIT_AND_CLOSE(srcAcquireFenceFd);
SYNC_WAIT_AND_CLOSE(destAcquireFenceFd);
BufferManager* mgr = vd.mHwc.getBufferManager();
if (!(mgr->blit(srcHandle, destHandle, destRect, false, false))) {
ETRACE("color space conversion from RGB to NV12 failed");
}
else
successful = true;
TIMELINE_INC(syncTimelineFd);
}
buffer_handle_t srcHandle;
buffer_handle_t destHandle;
int srcAcquireFenceFd;
int destAcquireFenceFd;
int syncTimelineFd;
crop_t destRect;
};
struct VirtualDevice::FrameTypeChangedTask : public VirtualDevice::Task {
virtual void run(VirtualDevice& vd) {
#ifdef INTEL_WIDI
typeChangeListener->frameTypeChanged(inputFrameInfo);
ITRACE("Notify frameTypeChanged: %dx%d in %dx%d @ %d fps",
inputFrameInfo.contentWidth, inputFrameInfo.contentHeight,
inputFrameInfo.bufferWidth, inputFrameInfo.bufferHeight,
inputFrameInfo.contentFrameRateN);
#endif
}
#ifdef INTEL_WIDI
sp<IFrameTypeChangeListener> typeChangeListener;
FrameInfo inputFrameInfo;
#endif
};
struct VirtualDevice::BufferInfoChangedTask : public VirtualDevice::Task {
virtual void run(VirtualDevice& vd) {
#ifdef INTEL_WIDI
typeChangeListener->bufferInfoChanged(outputFrameInfo);
ITRACE("Notify bufferInfoChanged: %dx%d in %dx%d @ %d fps",
outputFrameInfo.contentWidth, outputFrameInfo.contentHeight,
outputFrameInfo.bufferWidth, outputFrameInfo.bufferHeight,
outputFrameInfo.contentFrameRateN);
#endif
}
#ifdef INTEL_WIDI
sp<IFrameTypeChangeListener> typeChangeListener;
FrameInfo outputFrameInfo;
#endif
};
struct VirtualDevice::OnFrameReadyTask : public VirtualDevice::Task {
virtual void run(VirtualDevice& vd) {
if (renderTask != NULL && !renderTask->successful)
return;
{
Mutex::Autolock _l(vd.mHeldBuffersLock);
//Add the heldbuffer to the vector before calling onFrameReady, so that the buffer will be removed
//from the vector properly even if the notifyBufferReturned call acquires mHeldBuffersLock first.
vd.mHeldBuffers.add(handle, heldBuffer);
}
#ifdef INTEL_WIDI
// FIXME: we could remove this casting once onFrameReady receives
// a buffer_handle_t handle
status_t result = frameListener->onFrameReady((uint32_t)handle, handleType, renderTimestamp, mediaTimestamp);
if (result != OK) {
Mutex::Autolock _l(vd.mHeldBuffersLock);
vd.mHeldBuffers.removeItem(handle);
}
#else
Mutex::Autolock _l(vd.mHeldBuffersLock);
vd.mHeldBuffers.removeItem(handle);
#endif
}
sp<RenderTask> renderTask;
sp<RefBase> heldBuffer;
buffer_handle_t handle;
#ifdef INTEL_WIDI
sp<IFrameListener> frameListener;
HWCBufferHandleType handleType;
#endif
int64_t renderTimestamp;
int64_t mediaTimestamp;
};
struct VirtualDevice::BufferList::HeldBuffer : public RefBase {
HeldBuffer(BufferList& list, buffer_handle_t handle, uint32_t w, uint32_t h)
: mList(list),
mHandle(handle),
mWidth(w),
mHeight(h) { }
virtual ~HeldBuffer()
{
Mutex::Autolock _l(mList.mVd.mTaskLock);
if (mWidth == mList.mWidth && mHeight == mList.mHeight) {
VTRACE("Returning %s buffer %p (%ux%u) to list", mList.mName, mHandle, mWidth, mHeight);
mList.mAvailableBuffers.push_back(mHandle);
} else {
VTRACE("Deleting %s buffer %p (%ux%u)", mList.mName, mHandle, mWidth, mHeight);
BufferManager* mgr = mList.mVd.mHwc.getBufferManager();
mgr->freeGrallocBuffer((mHandle));
if (mList.mBuffersToCreate < mList.mLimit)
mList.mBuffersToCreate++;
}
}
BufferList& mList;
buffer_handle_t mHandle;
uint32_t mWidth;
uint32_t mHeight;
};
VirtualDevice::BufferList::BufferList(VirtualDevice& vd, const char* name,
uint32_t limit, uint32_t format, uint32_t usage)
: mVd(vd),
mName(name),
mLimit(limit),
mFormat(format),
mUsage(usage),
mBuffersToCreate(0),
mWidth(0),
mHeight(0)
{
}
buffer_handle_t VirtualDevice::BufferList::get(uint32_t width, uint32_t height, sp<RefBase>* heldBuffer)
{
width = align_width(width);
height = align_height(height);
if (mWidth != width || mHeight != height) {
ITRACE("%s buffers changing from %dx%d to %dx%d",
mName, mWidth, mHeight, width, height);
clear();
mWidth = width;
mHeight = height;
mBuffersToCreate = mLimit;
}
buffer_handle_t handle;
if (mAvailableBuffers.empty()) {
if (mBuffersToCreate <= 0)
return NULL;
BufferManager* mgr = mVd.mHwc.getBufferManager();
handle = reinterpret_cast<buffer_handle_t>(
mgr->allocGrallocBuffer(width, height, mFormat, mUsage));
if (handle == NULL){
ETRACE("failed to allocate %s buffer", mName);
return NULL;
}
mBuffersToCreate--;
}
else {
handle = *mAvailableBuffers.begin();
mAvailableBuffers.erase(mAvailableBuffers.begin());
}
*heldBuffer = new HeldBuffer(*this, handle, width, height);
return handle;
}
void VirtualDevice::BufferList::clear()
{
if (mWidth != 0 || mHeight != 0)
ITRACE("Releasing %s buffers (%ux%u)", mName, mWidth, mHeight);
if (!mAvailableBuffers.empty()) {
// iterate the list and call freeGraphicBuffer
for (List<buffer_handle_t>::iterator i = mAvailableBuffers.begin(); i != mAvailableBuffers.end(); ++i) {
VTRACE("Deleting the gralloc buffer associated with handle (%p)", (*i));
mVd.mHwc.getBufferManager()->freeGrallocBuffer((*i));
}
mAvailableBuffers.clear();
}
mWidth = 0;
mHeight = 0;
}
VirtualDevice::VirtualDevice(Hwcomposer& hwc)
: mProtectedMode(false),
mCscBuffers(*this, "CSC",
NUM_CSC_BUFFERS, DisplayQuery::queryNV12Format(),
GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PRIVATE_1),
mRgbUpscaleBuffers(*this, "RGB upscale",
NUM_SCALING_BUFFERS, HAL_PIXEL_FORMAT_BGRA_8888,
GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER),
mInitialized(false),
mHwc(hwc),
mPayloadManager(NULL),
mVsyncObserver(NULL),
mOrigContentWidth(0),
mOrigContentHeight(0),
mFirstVideoFrame(true),
mLastConnectionStatus(false),
mCachedBufferCapcity(16),
mDecWidth(0),
mDecHeight(0),
mFpsDivider(1)
{
CTRACE();
#ifdef INTEL_WIDI
mNextConfig.frameServerActive = false;
#endif
}
VirtualDevice::~VirtualDevice()
{
WARN_IF_NOT_DEINIT();
}
sp<VirtualDevice::CachedBuffer> VirtualDevice::getMappedBuffer(buffer_handle_t handle)
{
ssize_t index = mMappedBufferCache.indexOfKey(handle);
sp<CachedBuffer> cachedBuffer;
if (index == NAME_NOT_FOUND) {
if (mMappedBufferCache.size() > mCachedBufferCapcity)
mMappedBufferCache.clear();
cachedBuffer = new CachedBuffer(mHwc.getBufferManager(), handle);
mMappedBufferCache.add(handle, cachedBuffer);
} else {
cachedBuffer = mMappedBufferCache[index];
}
return cachedBuffer;
}
bool VirtualDevice::threadLoop()
{
sp<Task> task;
{
Mutex::Autolock _l(mTaskLock);
while (mTasks.empty()) {
mRequestQueued.wait(mTaskLock);
}
task = *mTasks.begin();
mTasks.erase(mTasks.begin());
}
if (task != NULL) {
task->run(*this);
task = NULL;
}
mRequestDequeued.signal();
return true;
}
#ifdef INTEL_WIDI
status_t VirtualDevice::start(sp<IFrameTypeChangeListener> typeChangeListener)
{
ITRACE();
Mutex::Autolock _l(mConfigLock);
mNextConfig.typeChangeListener = typeChangeListener;
mNextConfig.frameListener = NULL;
mNextConfig.policy.scaledWidth = 0;
mNextConfig.policy.scaledHeight = 0;
mNextConfig.policy.xdpi = 96;
mNextConfig.policy.ydpi = 96;
mNextConfig.policy.refresh = 60;
mNextConfig.extendedModeEnabled =
Hwcomposer::getInstance().getDisplayAnalyzer()->isVideoExtModeEnabled();
mVideoFramerate = 0;
mFirstVideoFrame = true;
mNextConfig.frameServerActive = true;
mNextConfig.forceNotifyFrameType = true;
mNextConfig.forceNotifyBufferInfo = true;
return NO_ERROR;
}
status_t VirtualDevice::stop(bool isConnected)
{
ITRACE();
Mutex::Autolock _l(mConfigLock);
mNextConfig.typeChangeListener = NULL;
mNextConfig.frameListener = NULL;
mNextConfig.policy.scaledWidth = 0;
mNextConfig.policy.scaledHeight = 0;
mNextConfig.policy.xdpi = 96;
mNextConfig.policy.ydpi = 96;
mNextConfig.policy.refresh = 60;
mNextConfig.frameServerActive = false;
mNextConfig.extendedModeEnabled = false;
mNextConfig.forceNotifyFrameType = false;
mNextConfig.forceNotifyBufferInfo = false;
{
Mutex::Autolock _l(mTaskLock);
mCscBuffers.clear();
}
return NO_ERROR;
}
#endif
bool VirtualDevice::isFrameServerActive() const
{
#ifdef INTEL_WIDI
return mCurrentConfig.frameServerActive;
#endif
return false;
}
#ifdef INTEL_WIDI
/* TODO: 64-bit - this handle of size 32-bit is a problem for 64-bit */
status_t VirtualDevice::notifyBufferReturned(int handle)
{
CTRACE();
Mutex::Autolock _l(mHeldBuffersLock);
ssize_t index = mHeldBuffers.indexOfKey((buffer_handle_t)handle);
if (index == NAME_NOT_FOUND) {
ETRACE("Couldn't find returned khandle %p", handle);
} else {
VTRACE("Removing heldBuffer associated with handle (%p)", handle);
mHeldBuffers.removeItemsAt(index, 1);
}
return NO_ERROR;
}
status_t VirtualDevice::setResolution(const FrameProcessingPolicy& policy, sp<IFrameListener> listener)
{
ITRACE();
Mutex::Autolock _l(mConfigLock);
mNextConfig.frameListener = listener;
mNextConfig.policy = policy;
return NO_ERROR;
}
#endif
static bool canUseDirectly(const hwc_display_contents_1_t *display, size_t n)
{
const hwc_layer_1_t& fbTarget = display->hwLayers[display->numHwLayers-1];
const hwc_layer_1_t& layer = display->hwLayers[n];
const IMG_native_handle_t* nativeHandle = reinterpret_cast<const IMG_native_handle_t*>(layer.handle);
return !(layer.flags & HWC_SKIP_LAYER) && layer.transform == 0 &&
layer.blending == HWC_BLENDING_PREMULT &&
layer.sourceCropf.left == 0 && layer.sourceCropf.top == 0 &&
layer.displayFrame.left == 0 && layer.displayFrame.top == 0 &&
layer.sourceCropf.right == fbTarget.sourceCropf.right &&
layer.sourceCropf.bottom == fbTarget.sourceCropf.bottom &&
layer.displayFrame.right == fbTarget.displayFrame.right &&
layer.displayFrame.bottom == fbTarget.displayFrame.bottom &&
layer.planeAlpha == 255 && layer.handle != NULL &&
(nativeHandle->iFormat == HAL_PIXEL_FORMAT_RGBA_8888 ||
nativeHandle->iFormat == HAL_PIXEL_FORMAT_BGRA_8888);
}
bool VirtualDevice::prePrepare(hwc_display_contents_1_t *display)
{
RETURN_FALSE_IF_NOT_INIT();
return true;
}
bool VirtualDevice::prepare(hwc_display_contents_1_t *display)
{
RETURN_FALSE_IF_NOT_INIT();
mRenderTimestamp = systemTime();
mVspInUse = false;
mExpectAcquireFences = false;
mIsForceCloneMode = false;
#ifdef INTEL_WIDI
{
Mutex::Autolock _l(mConfigLock);
mCurrentConfig = mNextConfig;
}
#endif
bool shouldBeConnected = (display != NULL);
if (shouldBeConnected != mLastConnectionStatus) {
// calling this will reload the property 'hwc.video.extmode.enable'
Hwcomposer::getInstance().getDisplayAnalyzer()->isVideoExtModeEnabled();
char propertyVal[PROPERTY_VALUE_MAX];
if (property_get("widi.compose.rgb_upscale", propertyVal, NULL) > 0)
mVspUpscale = atoi(propertyVal);
if (property_get("widi.compose.all_video", propertyVal, NULL) > 0)
mDebugVspClear = atoi(propertyVal);
if (property_get("widi.compose.dump", propertyVal, NULL) > 0)
mDebugVspDump = atoi(propertyVal);
Hwcomposer::getInstance().getMultiDisplayObserver()->notifyWidiConnectionStatus(shouldBeConnected);
mLastConnectionStatus = shouldBeConnected;
}
if (!display) {
// No image. We're done with any mappings and CSC buffers.
mMappedBufferCache.clear();
Mutex::Autolock _l(mTaskLock);
mCscBuffers.clear();
return true;
}
#ifdef INTEL_WIDI
if (!mCurrentConfig.frameServerActive) {
// We're done with CSC buffers, since we blit to outbuf in this mode.
// We want to keep mappings cached, so we don't clear mMappedBufferCache.
Mutex::Autolock _l(mTaskLock);
mCscBuffers.clear();
}
#else
Mutex::Autolock _l(mTaskLock);
mCscBuffers.clear();
#endif
// by default send the FRAMEBUFFER_TARGET layer (composited image)
const ssize_t fbTarget = display->numHwLayers-1;
mRgbLayer = fbTarget;
mYuvLayer = -1;
DisplayAnalyzer *analyzer = mHwc.getDisplayAnalyzer();
mProtectedMode = false;
#ifdef INTEL_WIDI
if (mCurrentConfig.typeChangeListener != NULL &&
!analyzer->isOverlayAllowed() &&
analyzer->getVideoInstances() <= 1) {
if (mCurrentConfig.typeChangeListener->shutdownVideo() != OK) {
ITRACE("Waiting for prior encoder session to shut down...");
}
/* Setting following flag to true will enable us to call bufferInfoChanged() in clone mode. */
mNextConfig.forceNotifyBufferInfo = true;
mYuvLayer = -1;
mRgbLayer = -1;
// Skipping frames.
// Fences aren't set in prepare, and we don't need them here, but they'll
// be set later and we have to close them. Don't log a warning in this case.
mExpectAcquireFences = true;
for (ssize_t i = 0; i < fbTarget; i++)
display->hwLayers[i].compositionType = HWC_OVERLAY;
return true;
}
for (ssize_t i = 0; i < fbTarget; i++) {
hwc_layer_1_t& layer = display->hwLayers[i];
if (analyzer->isVideoLayer(layer) && (mCurrentConfig.extendedModeEnabled || mDebugVspClear || analyzer->isProtectedLayer(layer))) {
if (mCurrentConfig.frameServerActive && mCurrentConfig.extendedModeEnabled) {
// If composed in surface flinger, then stream fbtarget.
if ((layer.flags & HWC_SKIP_LAYER) && !analyzer->ignoreVideoSkipFlag()) {
continue;
}
/* If the resolution of the video layer is less than QCIF, then we are going to play it in clone mode only.*/
uint32_t vidContentWidth = layer.sourceCropf.right - layer.sourceCropf.left;
uint32_t vidContentHeight = layer.sourceCropf.bottom - layer.sourceCropf.top;
if (vidContentWidth < QCIF_WIDTH || vidContentHeight < QCIF_HEIGHT) {
VTRACE("Ingoring layer %d which is too small for extended mode", i);
continue;
}
}
mYuvLayer = i;
mProtectedMode = analyzer->isProtectedLayer(layer);
break;
}
}
#endif
if (mYuvLayer == -1) {
mFirstVideoFrame = true;
mDecWidth = 0;
mDecHeight = 0;
}
#ifdef INTEL_WIDI
if (mCurrentConfig.frameServerActive && mCurrentConfig.extendedModeEnabled && mYuvLayer != -1) {
if (handleExtendedMode(display)) {
mYuvLayer = -1;
mRgbLayer = -1;
// Extended mode is successful.
// Fences aren't set in prepare, and we don't need them here, but they'll
// be set later and we have to close them. Don't log a warning in this case.
mExpectAcquireFences = true;
for (ssize_t i = 0; i < fbTarget; i++)
display->hwLayers[i].compositionType = HWC_OVERLAY;
return true;
}
// if error in playback file , switch to clone mode
WTRACE("Error, falling back to clone mode");
mIsForceCloneMode = true;
mYuvLayer = -1;
}
#endif
if (mYuvLayer == 0 && fbTarget == 1) {
// No RGB layer, so tell queueCompose to use blank RGB in fbtarget.
mRgbLayer = -1;
}
else if (mYuvLayer == 0 && fbTarget == 2) {
if (canUseDirectly(display, 1))
mRgbLayer = 1;
}
else if (mYuvLayer == -1 && fbTarget == 1) {
if (canUseDirectly(display, 0))
mRgbLayer = 0;
}
for (ssize_t i = 0; i < fbTarget; i++) {
hwc_layer_1_t& layer = display->hwLayers[i];
if (i == mYuvLayer || i == mRgbLayer || mRgbLayer != fbTarget)
layer.compositionType = HWC_OVERLAY;
else
layer.compositionType = HWC_FRAMEBUFFER;
}
if (mYuvLayer != -1 && mRgbLayer == fbTarget)
// This tells SurfaceFlinger to render this layer by writing transparent pixels
// to this layer's target region within the framebuffer. This effectively punches
// a hole through any content that is supposed to show below the video, and the
// video can be seen through this hole when we composite the YUV and RGBA layers
// together. Content above will draw on top of this hole and can cover the video.
// This has no effect when the video is the bottommost layer.
display->hwLayers[mYuvLayer].hints |= HWC_HINT_CLEAR_FB;
#ifdef INTEL_WIDI
// we're streaming fbtarget, so send onFramePrepare and wait for composition to happen
if (mCurrentConfig.frameListener != NULL)
mCurrentConfig.frameListener->onFramePrepare(mRenderTimestamp, -1);
#endif
return true;
}
bool VirtualDevice::commit(hwc_display_contents_1_t *display, IDisplayContext *context)
{
RETURN_FALSE_IF_NOT_INIT();
if (display != NULL && (mRgbLayer != -1 || mYuvLayer != -1))
sendToWidi(display);
if (mVspEnabled && !mVspInUse) {
mVaMapCache.clear();
sp<DisableVspTask> disableVsp = new DisableVspTask();
mMappedBufferCache.clear();
Mutex::Autolock _l(mTaskLock);
mRgbUpscaleBuffers.clear();
mTasks.push(disableVsp);
mRequestQueued.signal();
mVspEnabled = false;
}
if (display != NULL) {
// All acquire fences should be copied somewhere else or closed by now
// and set to -1 in these structs except in the case of extended mode.
// Make sure the fences are closed and log a warning if not in extended mode.
if (display->outbufAcquireFenceFd != -1) {
if (!mExpectAcquireFences)
WTRACE("outbuf acquire fence (fd=%d) not yet saved or closed", display->outbufAcquireFenceFd);
CLOSE_FENCE(display->outbufAcquireFenceFd);
}
for (size_t i = 0; i < display->numHwLayers; i++) {
hwc_layer_1_t& layer = display->hwLayers[i];
if (layer.acquireFenceFd != -1) {
if (!mExpectAcquireFences && (i < display->numHwLayers-1 || i == (size_t) mRgbLayer))
WTRACE("layer %zd acquire fence (fd=%zd) not yet saved or closed", i, layer.acquireFenceFd);
CLOSE_FENCE(layer.acquireFenceFd);
}
}
}
return true;
}
bool VirtualDevice::sendToWidi(hwc_display_contents_1_t *display)
{
VTRACE("RGB=%d, YUV=%d", mRgbLayer, mYuvLayer);
if (mYuvLayer == -1 && mRgbLayer == -1)
return true;
if (mYuvLayer != -1) {
mVspInUse = true;
if (queueCompose(display))
return true;
}
return queueColorConvert(display);
}
bool VirtualDevice::queueCompose(hwc_display_contents_1_t *display)
{
hwc_layer_1_t& yuvLayer = display->hwLayers[mYuvLayer];
if (yuvLayer.handle == NULL) {
ETRACE("No video handle");
return false;
}
#ifdef INTEL_WIDI
if (!mCurrentConfig.frameServerActive && display->outbuf == NULL) {
#else
if (display->outbuf == NULL) {
#endif
ETRACE("No outbuf");
return true; // fallback would be pointless
}
sp<ComposeTask> composeTask = new ComposeTask();
sp<RefBase> heldBuffer;
sp<OnFrameReadyTask> frameReadyTask;
Mutex::Autolock _l(mTaskLock);
float upscale_x = 1.0;
float upscale_y = 1.0;
hwc_layer_1_t& fbTarget = display->hwLayers[display->numHwLayers-1];
composeTask->outWidth = fbTarget.sourceCropf.right - fbTarget.sourceCropf.left;
composeTask->outHeight = fbTarget.sourceCropf.bottom - fbTarget.sourceCropf.top;
bool scaleRgb = false;
#ifdef INTEL_WIDI
if (mCurrentConfig.frameServerActive) {
if (mVspUpscale) {
composeTask->outWidth = mCurrentConfig.policy.scaledWidth;
composeTask->outHeight = mCurrentConfig.policy.scaledHeight;
upscale_x = mCurrentConfig.policy.scaledWidth/(fbTarget.sourceCropf.right - fbTarget.sourceCropf.left);
upscale_y = mCurrentConfig.policy.scaledHeight/(fbTarget.sourceCropf.bottom - fbTarget.sourceCropf.top);
scaleRgb = composeTask->outWidth != fbTarget.sourceCropf.right - fbTarget.sourceCropf.left ||
composeTask->outHeight != fbTarget.sourceCropf.bottom - fbTarget.sourceCropf.top;
}
composeTask->outputHandle = mCscBuffers.get(composeTask->outWidth, composeTask->outHeight, &heldBuffer);
if (composeTask->outputHandle == NULL) {
WTRACE("Out of CSC buffers, dropping frame");
return true;
}
} else {
composeTask->outputHandle = display->outbuf;
}
#else
composeTask->outputHandle = display->outbuf;
#endif
vspPrepare(composeTask->outWidth, composeTask->outHeight);
composeTask->videoCachedBuffer = getMappedBuffer(yuvLayer.handle);
if (composeTask->videoCachedBuffer == NULL) {
ETRACE("Couldn't map video handle %p", yuvLayer.handle);
return false;
}
if (composeTask->videoCachedBuffer->mapper == NULL) {
ETRACE("Src mapper gone");
return false;
}
composeTask->heldVideoBuffer = new HeldDecoderBuffer(this, composeTask->videoCachedBuffer);
IVideoPayloadManager::MetaData videoMetadata;
if (!mPayloadManager->getMetaData(composeTask->videoCachedBuffer->mapper, &videoMetadata)) {
ETRACE("Failed to map video payload info");
return false;
}
if (videoMetadata.normalBuffer.width == 0 || videoMetadata.normalBuffer.height == 0) {
ETRACE("Bad video metadata for handle %p", yuvLayer.handle);
return false;
}
if (videoMetadata.normalBuffer.khandle == 0) {
ETRACE("Bad khandle");
return false;
}
VARectangle& output_region = composeTask->output_region;
output_region.x = static_cast<uint32_t>(yuvLayer.displayFrame.left*upscale_x) & ~1;
output_region.y = static_cast<uint32_t>(yuvLayer.displayFrame.top*upscale_y) & ~1;
output_region.width = (static_cast<uint32_t>(yuvLayer.displayFrame.right*upscale_y+1) & ~1) - output_region.x;
output_region.height = (static_cast<uint32_t>(yuvLayer.displayFrame.bottom*upscale_y+1) & ~1) - output_region.y;
uint32_t videoWidth;
uint32_t videoHeight;
if (videoMetadata.transform == 0 || videoMetadata.transform == HAL_TRANSFORM_ROT_180) {
videoWidth = videoMetadata.normalBuffer.width;
videoHeight = videoMetadata.normalBuffer.height;
} else {
videoWidth = videoMetadata.normalBuffer.height;
videoHeight = videoMetadata.normalBuffer.width;
}
// Layer source crop info is based on an unrotated, unscaled buffer.
// Rotate the rectangle to get the source crop we'd use for a rotated, unscaled buffer.
hwc_frect_t rotatedCrop;
switch (videoMetadata.transform) {
default:
rotatedCrop = yuvLayer.sourceCropf;
break;
case HAL_TRANSFORM_ROT_90:
rotatedCrop.left = yuvLayer.sourceCropf.top;
rotatedCrop.top = videoHeight - yuvLayer.sourceCropf.right;
rotatedCrop.right = yuvLayer.sourceCropf.bottom;
rotatedCrop.bottom = videoHeight - yuvLayer.sourceCropf.left;
break;
case HAL_TRANSFORM_ROT_180:
rotatedCrop.left = videoWidth - yuvLayer.sourceCropf.right;
rotatedCrop.top = videoHeight - yuvLayer.sourceCropf.bottom;
rotatedCrop.right = videoWidth - yuvLayer.sourceCropf.left;
rotatedCrop.bottom = videoHeight - yuvLayer.sourceCropf.top;
break;
case HAL_TRANSFORM_ROT_270:
rotatedCrop.left = videoWidth - yuvLayer.sourceCropf.bottom;
rotatedCrop.top = yuvLayer.sourceCropf.left;
rotatedCrop.right = videoWidth - yuvLayer.sourceCropf.top;
rotatedCrop.bottom = yuvLayer.sourceCropf.right;
break;
}
float factor_x = output_region.width / (rotatedCrop.right - rotatedCrop.left);
float factor_y = output_region.height / (rotatedCrop.bottom - rotatedCrop.top);
uint32_t scaleWidth = videoWidth * factor_x;
uint32_t scaleHeight = videoHeight * factor_y;
scaleWidth &= ~1;
scaleHeight &= ~1;
IVideoPayloadManager::Buffer info;
if (!getFrameOfSize(scaleWidth, scaleHeight, videoMetadata, info)) {
//Returning true as else we fall into the queueColorConvert
//resulting into scrambled frames for protected content.
ITRACE("scaled frame not yet available.");
return true;
}
composeTask->videoKhandle = info.khandle;
composeTask->videoStride = info.lumaStride;
composeTask->videoBufHeight = info.bufHeight;
composeTask->videoTiled = info.tiled;
// rotatedCrop accounts for rotation. Now account for any scaling along each dimension.
hwc_frect_t scaledCrop = rotatedCrop;
if (info.width < videoWidth) {
float factor = static_cast<float>(info.width) / videoWidth;
scaledCrop.left *= factor;
scaledCrop.right *= factor;
}
if (info.height < videoHeight) {
float factor = static_cast<float>(info.height) / videoHeight;
scaledCrop.top *= factor;
scaledCrop.bottom *= factor;
}
VARectangle& surface_region = composeTask->surface_region;
surface_region.x = static_cast<int>(scaledCrop.left) + info.offsetX;
surface_region.y = static_cast<int>(scaledCrop.top) + info.offsetY;
surface_region.width = static_cast<int>(scaledCrop.right - scaledCrop.left);
surface_region.height = static_cast<int>(scaledCrop.bottom - scaledCrop.top);
VTRACE("Want to take (%d,%d)-(%d,%d) region from %dx%d video (in %dx%d buffer) and output to (%d,%d)-(%d,%d)",
surface_region.x, surface_region.y,
surface_region.x + surface_region.width, surface_region.y + surface_region.height,
info.width, info.height,
info.bufWidth, info.bufHeight,
output_region.x, output_region.y,
output_region.x + output_region.width, output_region.y + output_region.height);
if (surface_region.x + surface_region.width > static_cast<int>(info.width + info.offsetX) ||
surface_region.y + surface_region.height > static_cast<int>(info.height + info.offsetY))
{
ETRACE("Source crop exceeds video dimensions: (%d,%d)-(%d,%d) > %ux%u",
surface_region.x, surface_region.y,
surface_region.x + surface_region.width, surface_region.y + surface_region.height,
info.width, info.height);
return false;
}
if (surface_region.width > output_region.width || surface_region.height > output_region.height) {
// VSP can upscale but can't downscale video, so use blank video
// until we start getting downscaled frames.
surface_region.x = 0;
surface_region.y = 0;
surface_region.width = composeTask->outWidth;
surface_region.height = composeTask->outHeight;
output_region = surface_region;
composeTask->videoKhandle = 0;
composeTask->videoStride = composeTask->outWidth;
composeTask->videoBufHeight = composeTask->outHeight;
composeTask->videoTiled = false;
}
composeTask->yuvAcquireFenceFd = yuvLayer.acquireFenceFd;
yuvLayer.acquireFenceFd = -1;
composeTask->outbufAcquireFenceFd = display->outbufAcquireFenceFd;
display->outbufAcquireFenceFd = -1;
int retireFd = sw_sync_fence_create(mSyncTimelineFd, "widi_compose_retire", mNextSyncPoint);
yuvLayer.releaseFenceFd = retireFd;
if (mRgbLayer == -1) {
CLOSE_FENCE(fbTarget.acquireFenceFd);
} else {
hwc_layer_1_t& rgbLayer = display->hwLayers[mRgbLayer];
composeTask->rgbAcquireFenceFd = rgbLayer.acquireFenceFd;
rgbLayer.acquireFenceFd = -1;
rgbLayer.releaseFenceFd = dup(retireFd);
}
mNextSyncPoint++;
composeTask->syncTimelineFd = mSyncTimelineFd;
if (mRgbLayer != -1)
{
hwc_layer_1_t& rgbLayer = display->hwLayers[mRgbLayer];
if (rgbLayer.handle == NULL) {
ETRACE("No RGB handle");
return false;
}
if (scaleRgb) {
buffer_handle_t scalingBuffer;
sp<RefBase> heldUpscaleBuffer;
while ((scalingBuffer = mRgbUpscaleBuffers.get(composeTask->outWidth, composeTask->outHeight, &heldUpscaleBuffer)) == NULL &&
!mTasks.empty()) {
VTRACE("Waiting for free RGB upscale buffer...");
mRequestDequeued.wait(mTaskLock);
}
if (scalingBuffer == NULL) {
ETRACE("Couldn't get scaling buffer");
return false;
}
BufferManager* mgr = mHwc.getBufferManager();
crop_t destRect;
destRect.x = 0;
destRect.y = 0;
destRect.w = composeTask->outWidth;
destRect.h = composeTask->outHeight;
if (!mgr->blit(rgbLayer.handle, scalingBuffer, destRect, true, true))
return true;
composeTask->rgbHandle = scalingBuffer;
composeTask->heldRgbHandle = heldUpscaleBuffer;
}
else {
unsigned int pixel_format = VA_FOURCC_BGRA;
const IMG_native_handle_t* nativeHandle = reinterpret_cast<const IMG_native_handle_t*>(rgbLayer.handle);
if (nativeHandle->iFormat == HAL_PIXEL_FORMAT_RGBA_8888)
pixel_format = VA_FOURCC_RGBA;
mRgbUpscaleBuffers.clear();
ssize_t index = mVaMapCache.indexOfKey(rgbLayer.handle);
if (index == NAME_NOT_FOUND) {
composeTask->mappedRgbIn = new VAMappedHandleObject(va_dpy, rgbLayer.handle, composeTask->outWidth, composeTask->outHeight, pixel_format);
mVaMapCache.add(rgbLayer.handle, composeTask->mappedRgbIn);
}
else
composeTask->mappedRgbIn = mVaMapCache[index];
if (composeTask->mappedRgbIn->surface == 0) {
ETRACE("Unable to map RGB surface");
return false;
}
}
}
else
composeTask->mappedRgbIn = NULL;
mTasks.push_back(composeTask);
mRequestQueued.signal();
#ifdef INTEL_WIDI
if (mCurrentConfig.frameServerActive) {
FrameInfo inputFrameInfo;
memset(&inputFrameInfo, 0, sizeof(inputFrameInfo));
inputFrameInfo.isProtected = mProtectedMode;
inputFrameInfo.frameType = HWC_FRAMETYPE_FRAME_BUFFER;
if (mVspUpscale) {
float upscale_x = (rotatedCrop.right - rotatedCrop.left) /
(yuvLayer.displayFrame.right - yuvLayer.displayFrame.left);
float upscale_y = (rotatedCrop.bottom - rotatedCrop.top) /
(yuvLayer.displayFrame.bottom - yuvLayer.displayFrame.top);
float upscale = upscale_x > upscale_y ? upscale_x : upscale_y;
if (upscale <= 1.0)
upscale = 1.0;
inputFrameInfo.contentWidth = (fbTarget.sourceCropf.right - fbTarget.sourceCropf.left)*upscale;
inputFrameInfo.contentHeight = (fbTarget.sourceCropf.bottom - fbTarget.sourceCropf.top)*upscale;
}
else {
inputFrameInfo.contentWidth = composeTask->outWidth;
inputFrameInfo.contentHeight = composeTask->outHeight;
}
inputFrameInfo.contentFrameRateN = 0;
inputFrameInfo.contentFrameRateD = 0;
FrameInfo outputFrameInfo = inputFrameInfo;
BufferManager* mgr = mHwc.getBufferManager();
DataBuffer* dataBuf = mgr->lockDataBuffer(composeTask->outputHandle);
outputFrameInfo.contentWidth = composeTask->outWidth;
outputFrameInfo.contentHeight = composeTask->outHeight;
outputFrameInfo.bufferWidth = dataBuf->getWidth();
outputFrameInfo.bufferHeight = dataBuf->getHeight();
outputFrameInfo.lumaUStride = dataBuf->getWidth();
outputFrameInfo.chromaUStride = dataBuf->getWidth();
outputFrameInfo.chromaVStride = dataBuf->getWidth();
mgr->unlockDataBuffer(dataBuf);
queueFrameTypeInfo(inputFrameInfo);
if (mCurrentConfig.policy.scaledWidth == 0 || mCurrentConfig.policy.scaledHeight == 0)
return true; // This isn't a failure, WiDi just doesn't want frames right now.
queueBufferInfo(outputFrameInfo);
if (mCurrentConfig.frameListener != NULL) {
frameReadyTask = new OnFrameReadyTask();
frameReadyTask->renderTask = composeTask;
frameReadyTask->heldBuffer = heldBuffer;
frameReadyTask->frameListener = mCurrentConfig.frameListener;
frameReadyTask->handle = composeTask->outputHandle;
frameReadyTask->handleType = HWC_HANDLE_TYPE_GRALLOC;
frameReadyTask->renderTimestamp = mRenderTimestamp;
frameReadyTask->mediaTimestamp = -1;
mTasks.push_back(frameReadyTask);
}
}
else {
display->retireFenceFd = dup(retireFd);
}
#else
display->retireFenceFd = dup(retireFd);
#endif
return true;
}
bool VirtualDevice::queueColorConvert(hwc_display_contents_1_t *display)
{
if (mRgbLayer == -1) {
ETRACE("RGB layer not set");
return false;
}
hwc_layer_1_t& layer = display->hwLayers[mRgbLayer];
if (layer.handle == NULL) {
ETRACE("RGB layer has no handle set");
return false;
}
if (display->outbuf == NULL) {
ETRACE("outbuf is not set");
return false;
}
{
const IMG_native_handle_t* nativeSrcHandle = reinterpret_cast<const IMG_native_handle_t*>(layer.handle);
const IMG_native_handle_t* nativeDestHandle = reinterpret_cast<const IMG_native_handle_t*>(display->outbuf);
if ((nativeSrcHandle->iFormat == HAL_PIXEL_FORMAT_RGBA_8888 &&
nativeDestHandle->iFormat == HAL_PIXEL_FORMAT_BGRA_8888) ||
(nativeSrcHandle->iFormat == HAL_PIXEL_FORMAT_BGRA_8888 &&
nativeDestHandle->iFormat == HAL_PIXEL_FORMAT_RGBA_8888))
{
SYNC_WAIT_AND_CLOSE(layer.acquireFenceFd);
SYNC_WAIT_AND_CLOSE(display->outbufAcquireFenceFd);
display->retireFenceFd = -1;
// synchronous in this case
colorSwap(layer.handle, display->outbuf, ((nativeSrcHandle->iWidth+31)&~31)*nativeSrcHandle->iHeight);
// Workaround: Don't keep cached buffers. If the VirtualDisplaySurface gets destroyed,
// these would be unmapped on the next frame, after the buffers are destroyed,
// which is causing heap corruption, probably due to a double-free somewhere.
mMappedBufferCache.clear();
return true;
}
}
sp<BlitTask> blitTask = new BlitTask();
sp<OnFrameReadyTask> frameReadyTask;
blitTask->destRect.x = 0;
blitTask->destRect.y = 0;
blitTask->destRect.w = layer.sourceCropf.right - layer.sourceCropf.left;
blitTask->destRect.h = layer.sourceCropf.bottom - layer.sourceCropf.top;
blitTask->srcHandle = layer.handle;
sp<RefBase> heldBuffer;
Mutex::Autolock _l(mTaskLock);
blitTask->srcAcquireFenceFd = layer.acquireFenceFd;
layer.acquireFenceFd = -1;
blitTask->syncTimelineFd = mSyncTimelineFd;
// Framebuffer after BlitTask::run() calls sw_sync_timeline_inc().
layer.releaseFenceFd = sw_sync_fence_create(mSyncTimelineFd, "widi_blit_retire", mNextSyncPoint);
mNextSyncPoint++;
#ifdef INTEL_WIDI
if (mCurrentConfig.frameServerActive) {
blitTask->destHandle = mCscBuffers.get(blitTask->destRect.w, blitTask->destRect.h, &heldBuffer);
blitTask->destAcquireFenceFd = -1;
// we do not use retire fence in frameServerActive path.
CLOSE_FENCE(display->retireFenceFd);
// we use our own buffer, so just close this fence without a wait
CLOSE_FENCE(display->outbufAcquireFenceFd);
}
else {
blitTask->destHandle = display->outbuf;
blitTask->destAcquireFenceFd = display->outbufAcquireFenceFd;
// don't let TngDisplayContext::commitEnd() close this
display->outbufAcquireFenceFd = -1;
display->retireFenceFd = dup(layer.releaseFenceFd);
}
#else
blitTask->destHandle = display->outbuf;
blitTask->destAcquireFenceFd = display->outbufAcquireFenceFd;
// don't let TngDisplayContext::commitEnd() close this
display->outbufAcquireFenceFd = -1;
display->retireFenceFd = dup(layer.releaseFenceFd);
#endif
if (blitTask->destHandle == NULL) {
WTRACE("Out of CSC buffers, dropping frame");
return false;
}
mTasks.push_back(blitTask);
mRequestQueued.signal();
#ifdef INTEL_WIDI
if (mCurrentConfig.frameServerActive) {
FrameInfo inputFrameInfo;
memset(&inputFrameInfo, 0, sizeof(inputFrameInfo));
inputFrameInfo.isProtected = mProtectedMode;
FrameInfo outputFrameInfo;
inputFrameInfo.frameType = HWC_FRAMETYPE_FRAME_BUFFER;
inputFrameInfo.contentWidth = blitTask->destRect.w;
inputFrameInfo.contentHeight = blitTask->destRect.h;
inputFrameInfo.contentFrameRateN = 0;
inputFrameInfo.contentFrameRateD = 0;
outputFrameInfo = inputFrameInfo;
BufferManager* mgr = mHwc.getBufferManager();
DataBuffer* dataBuf = mgr->lockDataBuffer(blitTask->destHandle);
outputFrameInfo.bufferWidth = dataBuf->getWidth();
outputFrameInfo.bufferHeight = dataBuf->getHeight();
outputFrameInfo.lumaUStride = dataBuf->getWidth();
outputFrameInfo.chromaUStride = dataBuf->getWidth();
outputFrameInfo.chromaVStride = dataBuf->getWidth();
mgr->unlockDataBuffer(dataBuf);
if (!mIsForceCloneMode)
queueFrameTypeInfo(inputFrameInfo);
if (mCurrentConfig.policy.scaledWidth == 0 || mCurrentConfig.policy.scaledHeight == 0)
return true; // This isn't a failure, WiDi just doesn't want frames right now.
queueBufferInfo(outputFrameInfo);
if (mCurrentConfig.frameListener != NULL) {
frameReadyTask = new OnFrameReadyTask();
frameReadyTask->renderTask = blitTask;
frameReadyTask->heldBuffer = heldBuffer;
frameReadyTask->frameListener = mCurrentConfig.frameListener;
frameReadyTask->handle = blitTask->destHandle;
frameReadyTask->handleType = HWC_HANDLE_TYPE_GRALLOC;
frameReadyTask->renderTimestamp = mRenderTimestamp;
frameReadyTask->mediaTimestamp = -1;
mTasks.push_back(frameReadyTask);
}
}
#endif
return true;
}
#ifdef INTEL_WIDI
bool VirtualDevice::handleExtendedMode(hwc_display_contents_1_t *display)
{
FrameInfo inputFrameInfo;
memset(&inputFrameInfo, 0, sizeof(inputFrameInfo));
inputFrameInfo.isProtected = mProtectedMode;
hwc_layer_1_t& layer = display->hwLayers[mYuvLayer];
if (layer.handle == NULL) {
ETRACE("video layer has no handle set");
return false;
}
sp<CachedBuffer> cachedBuffer;
if ((cachedBuffer = getMappedBuffer(layer.handle)) == NULL) {
ETRACE("Failed to map display buffer");
return false;
}
inputFrameInfo.frameType = HWC_FRAMETYPE_VIDEO;
// for video mode let 30 fps be the default value.
inputFrameInfo.contentFrameRateN = 30;
inputFrameInfo.contentFrameRateD = 1;
IVideoPayloadManager::MetaData metadata;
if (!mPayloadManager->getMetaData(cachedBuffer->mapper, &metadata)) {
ETRACE("Failed to get metadata");
return false;
}
if (metadata.transform == 0 || metadata.transform == HAL_TRANSFORM_ROT_180) {
inputFrameInfo.contentWidth = metadata.normalBuffer.width;
inputFrameInfo.contentHeight = metadata.normalBuffer.height;
} else {
inputFrameInfo.contentWidth = metadata.normalBuffer.height;
inputFrameInfo.contentHeight = metadata.normalBuffer.width;
// 90 and 270 have some issues that appear to be decoder bugs
ITRACE("Skipping extended mode due to rotation of 90 or 270");
return false;
}
// Use the crop size if something changed derive it again..
// Only get video source info if frame rate has not been initialized.
// getVideoSourceInfo() is a fairly expensive operation. This optimization
// will save us a few milliseconds per frame
if (mFirstVideoFrame || (mOrigContentWidth != metadata.normalBuffer.width) ||
(mOrigContentHeight != metadata.normalBuffer.height)) {
mVideoFramerate = inputFrameInfo.contentFrameRateN;
VTRACE("VideoWidth = %d, VideoHeight = %d", metadata.normalBuffer.width, metadata.normalBuffer.height);
mOrigContentWidth = metadata.normalBuffer.width;
mOrigContentHeight = metadata.normalBuffer.height;
// For the first video session by default
int sessionID = Hwcomposer::getInstance().getDisplayAnalyzer()->getFirstVideoInstanceSessionID();
if (sessionID >= 0) {
ITRACE("Session id = %d", sessionID);
VideoSourceInfo videoInfo;
memset(&videoInfo, 0, sizeof(videoInfo));
status_t ret = mHwc.getMultiDisplayObserver()->getVideoSourceInfo(sessionID, &videoInfo);
if (ret == NO_ERROR) {
ITRACE("width = %d, height = %d, fps = %d", videoInfo.width, videoInfo.height,
videoInfo.frameRate);
if (videoInfo.frameRate > 0) {
mVideoFramerate = videoInfo.frameRate;
}
}
}
mFirstVideoFrame = false;
}
inputFrameInfo.contentFrameRateN = mVideoFramerate;
inputFrameInfo.contentFrameRateD = 1;
sp<ComposeTask> composeTask;
sp<RefBase> heldBuffer;
Mutex::Autolock _l(mTaskLock);
if (mCurrentConfig.policy.scaledWidth == 0 || mCurrentConfig.policy.scaledHeight == 0) {
queueFrameTypeInfo(inputFrameInfo);
return true; // This isn't a failure, WiDi just doesn't want frames right now.
}
IVideoPayloadManager::Buffer info;
if (!getFrameOfSize(mCurrentConfig.policy.scaledWidth, mCurrentConfig.policy.scaledHeight, metadata, info)) {
ITRACE("Extended mode waiting for scaled frame");
return false;
}
queueFrameTypeInfo(inputFrameInfo);
heldBuffer = new HeldDecoderBuffer(this, cachedBuffer);
int64_t mediaTimestamp = metadata.timestamp;
VARectangle surface_region;
surface_region.x = info.offsetX;
surface_region.y = info.offsetY;
surface_region.width = info.width;
surface_region.height = info.height;
FrameInfo outputFrameInfo = inputFrameInfo;
outputFrameInfo.bufferFormat = metadata.format;
outputFrameInfo.contentWidth = info.width;
outputFrameInfo.contentHeight = info.height;
outputFrameInfo.bufferWidth = info.bufWidth;
outputFrameInfo.bufferHeight = info.bufHeight;
outputFrameInfo.lumaUStride = info.lumaStride;
outputFrameInfo.chromaUStride = info.chromaUStride;
outputFrameInfo.chromaVStride = info.chromaVStride;
if (outputFrameInfo.bufferFormat == 0 ||
outputFrameInfo.bufferWidth < outputFrameInfo.contentWidth ||
outputFrameInfo.bufferHeight < outputFrameInfo.contentHeight ||
outputFrameInfo.contentWidth <= 0 || outputFrameInfo.contentHeight <= 0 ||
outputFrameInfo.lumaUStride <= 0 ||
outputFrameInfo.chromaUStride <= 0 || outputFrameInfo.chromaVStride <= 0) {
ITRACE("Payload cleared or inconsistent info, not sending frame");
ITRACE("outputFrameInfo.bufferFormat = %d ", outputFrameInfo.bufferFormat);
ITRACE("outputFrameInfo.bufferWidth = %d ", outputFrameInfo.bufferWidth);
ITRACE("outputFrameInfo.contentWidth = %d ", outputFrameInfo.contentWidth);
ITRACE("outputFrameInfo.bufferHeight = %d ", outputFrameInfo.bufferHeight);
ITRACE("outputFrameInfo.contentHeight = %d ", outputFrameInfo.contentHeight);
ITRACE("outputFrameInfo.lumaUStride = %d ", outputFrameInfo.lumaUStride);
ITRACE("outputFrameInfo.chromaUStride = %d ", outputFrameInfo.chromaUStride);
ITRACE("outputFrameInfo.chromaVStride = %d ", outputFrameInfo.chromaVStride);
return false;
}
if (mCurrentConfig.policy.scaledWidth == 0 || mCurrentConfig.policy.scaledHeight == 0)
return true; // This isn't a failure, WiDi just doesn't want frames right now.
if (info.khandle == mExtLastKhandle && mediaTimestamp == mExtLastTimestamp) {
// Same frame again. We don't send a frame, but we return true because
// this isn't an error.
if (metadata.transform != 0)
mVspInUse = true; // Don't shut down VSP just to start it again really quick.
return true;
}
mExtLastKhandle = info.khandle;
mExtLastTimestamp = mediaTimestamp;
HWCBufferHandleType handleType = HWC_HANDLE_TYPE_KBUF;
buffer_handle_t handle = info.khandle;
// Ideally we'd check if there's an offset (info.offsetX > 0 || info.offsetY > 0),
// so we use VSP only when cropping is needed. But using the khandle directly when
// both rotation and scaling are involved can encode the frame with the wrong
// tiling status, so use VSP to normalize if any rotation is involved.
if (metadata.transform != 0) {
// Cropping (or above workaround) needed, so use VSP to do it.
mVspInUse = true;
vspPrepare(info.width, info.height);
composeTask = new ComposeTask();
composeTask->heldVideoBuffer = heldBuffer;
heldBuffer = NULL;
composeTask->outWidth = info.width;
composeTask->outHeight = info.height;
composeTask->outputHandle = mCscBuffers.get(composeTask->outWidth, composeTask->outHeight, &heldBuffer);
if (composeTask->outputHandle == NULL) {
ITRACE("Out of CSC buffers, dropping frame");
return true;
}
composeTask->surface_region = surface_region;
composeTask->videoCachedBuffer = cachedBuffer;
VARectangle& output_region = composeTask->output_region;
output_region.x = 0;
output_region.y = 0;
output_region.width = info.width;
output_region.height = info.height;
composeTask->videoKhandle = info.khandle;
composeTask->videoStride = info.lumaStride;
composeTask->videoBufHeight = info.bufHeight;
composeTask->videoTiled = info.tiled;
BufferManager* mgr = mHwc.getBufferManager();
DataBuffer* dataBuf = mgr->lockDataBuffer(composeTask->outputHandle);
outputFrameInfo.contentWidth = composeTask->outWidth;
outputFrameInfo.contentHeight = composeTask->outHeight;
outputFrameInfo.bufferWidth = dataBuf->getWidth();
outputFrameInfo.bufferHeight = dataBuf->getHeight();
outputFrameInfo.lumaUStride = dataBuf->getWidth();
outputFrameInfo.chromaUStride = dataBuf->getWidth();
outputFrameInfo.chromaVStride = dataBuf->getWidth();
mgr->unlockDataBuffer(dataBuf);
handle = composeTask->outputHandle;
handleType = HWC_HANDLE_TYPE_GRALLOC;
mTasks.push_back(composeTask);
mRequestQueued.signal();
}
queueBufferInfo(outputFrameInfo);
if (mCurrentConfig.frameListener != NULL) {
sp<OnFrameReadyTask> frameReadyTask = new OnFrameReadyTask();
frameReadyTask->renderTask = composeTask;
frameReadyTask->heldBuffer = heldBuffer;
frameReadyTask->frameListener = mCurrentConfig.frameListener;
frameReadyTask->handle = handle;
frameReadyTask->handleType = handleType;
frameReadyTask->renderTimestamp = mRenderTimestamp;
frameReadyTask->mediaTimestamp = mediaTimestamp;
mTasks.push_back(frameReadyTask);
mRequestQueued.signal();
}
return true;
}
void VirtualDevice::queueFrameTypeInfo(const FrameInfo& inputFrameInfo)
{
if (mCurrentConfig.forceNotifyFrameType ||
memcmp(&inputFrameInfo, &mLastInputFrameInfo, sizeof(inputFrameInfo)) != 0) {
// something changed, notify type change listener
mNextConfig.forceNotifyFrameType = false;
mLastInputFrameInfo = inputFrameInfo;
sp<FrameTypeChangedTask> notifyTask = new FrameTypeChangedTask;
notifyTask->typeChangeListener = mCurrentConfig.typeChangeListener;
notifyTask->inputFrameInfo = inputFrameInfo;
mTasks.push_back(notifyTask);
}
}
void VirtualDevice::queueBufferInfo(const FrameInfo& outputFrameInfo)
{
if (mCurrentConfig.forceNotifyBufferInfo ||
memcmp(&outputFrameInfo, &mLastOutputFrameInfo, sizeof(outputFrameInfo)) != 0) {
mNextConfig.forceNotifyBufferInfo = false;
mLastOutputFrameInfo = outputFrameInfo;
sp<BufferInfoChangedTask> notifyTask = new BufferInfoChangedTask;
notifyTask->typeChangeListener = mCurrentConfig.typeChangeListener;
notifyTask->outputFrameInfo = outputFrameInfo;
//if (handleType == HWC_HANDLE_TYPE_GRALLOC)
// mMappedBufferCache.clear(); // !
mTasks.push_back(notifyTask);
}
}
#endif
void VirtualDevice::colorSwap(buffer_handle_t src, buffer_handle_t dest, uint32_t pixelCount)
{
sp<CachedBuffer> srcCachedBuffer;
sp<CachedBuffer> destCachedBuffer;
{
srcCachedBuffer = getMappedBuffer(src);
if (srcCachedBuffer == NULL || srcCachedBuffer->mapper == NULL)
return;
destCachedBuffer = getMappedBuffer(dest);
if (destCachedBuffer == NULL || destCachedBuffer->mapper == NULL)
return;
}
uint8_t* srcPtr = static_cast<uint8_t*>(srcCachedBuffer->mapper->getCpuAddress(0));
uint8_t* destPtr = static_cast<uint8_t*>(destCachedBuffer->mapper->getCpuAddress(0));
if (srcPtr == NULL || destPtr == NULL)
return;
while (pixelCount > 0) {
destPtr[0] = srcPtr[2];
destPtr[1] = srcPtr[1];
destPtr[2] = srcPtr[0];
destPtr[3] = srcPtr[3];
srcPtr += 4;
destPtr += 4;
pixelCount--;
}
}
void VirtualDevice::vspPrepare(uint32_t width, uint32_t height)
{
if (mVspEnabled && width == mVspWidth && height == mVspHeight)
return;
if (mVspEnabled)
{
ITRACE("Going to switch VSP from %ux%u to %ux%u", mVspWidth, mVspHeight, width, height);
mMappedBufferCache.clear();
mVaMapCache.clear();
sp<DisableVspTask> disableVsp = new DisableVspTask();
mTasks.push_back(disableVsp);
}
mVspWidth = width;
mVspHeight = height;
sp<EnableVspTask> enableTask = new EnableVspTask();
enableTask->width = width;
enableTask->height = height;
mTasks.push_back(enableTask);
mRequestQueued.signal();
// to map a buffer from this thread, we need this task to complete on the other thread
while (enableTask->getStrongCount() > 1) {
VTRACE("Waiting for WidiBlit thread to enable VSP...");
mRequestDequeued.wait(mTaskLock);
}
mVspEnabled = true;
}
void VirtualDevice::vspEnable(uint32_t width, uint32_t height)
{
width = align_width(width);
height = align_height(height);
ITRACE("Start VSP at %ux%u", width, height);
VAStatus va_status;
int display = 0;
int major_ver, minor_ver;
va_dpy = vaGetDisplay(&display);
va_status = vaInitialize(va_dpy, &major_ver, &minor_ver);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaInitialize returns %08x", va_status);
VAConfigAttrib va_attr;
va_attr.type = VAConfigAttribRTFormat;
va_status = vaGetConfigAttributes(va_dpy,
VAProfileNone,
VAEntrypointVideoProc,
&va_attr,
1);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaGetConfigAttributes returns %08x", va_status);
va_status = vaCreateConfig(
va_dpy,
VAProfileNone,
VAEntrypointVideoProc,
&(va_attr),
1,
&va_config
);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateConfig returns %08x", va_status);
VADisplayAttribute attr;
attr.type = VADisplayAttribRenderMode;
attr.value = VA_RENDER_MODE_LOCAL_OVERLAY;
va_status = vaSetDisplayAttributes(va_dpy, &attr, 1);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaSetDisplayAttributes returns %08x", va_status);
va_status = vaCreateSurfaces(
va_dpy,
VA_RT_FORMAT_YUV420,
width,
height,
&va_blank_yuv_in,
1,
NULL,
0);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateSurfaces (video in) returns %08x", va_status);
unsigned long buffer;
VASurfaceAttribExternalBuffers buf;
int stride = align_width(width);
int bufHeight = align_height(height);
buf.pixel_format = VA_FOURCC_RGBA;
buf.width = width;
buf.height = height;
buf.data_size = stride * bufHeight * 4;
buf.num_planes = 3;
buf.pitches[0] = stride;
buf.pitches[1] = stride;
buf.pitches[2] = stride;
buf.pitches[3] = 0;
buf.offsets[0] = 0;
buf.offsets[1] = stride * bufHeight;
buf.offsets[2] = buf.offsets[1];
buf.offsets[3] = 0;
buf.buffers = &buffer;
buf.num_buffers = 1;
buf.flags = 0;
buf.private_data = NULL;
VASurfaceAttrib attrib_list[2];
attrib_list[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType;
attrib_list[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
attrib_list[0].value.type = VAGenericValueTypeInteger;
attrib_list[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA;
attrib_list[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor;
attrib_list[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
attrib_list[1].value.type = VAGenericValueTypePointer;
attrib_list[1].value.value.p = (void *)&buf;
va_status = vaCreateSurfaces(
va_dpy,
VA_RT_FORMAT_RGB32,
stride,
bufHeight,
&va_blank_rgb_in,
1,
attrib_list,
2);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateSurfaces (blank rgba in) returns %08x", va_status);
va_status = vaCreateContext(
va_dpy,
va_config,
stride,
bufHeight,
0,
&va_blank_yuv_in /* not used by VSP, but libva checks for it */,
1,
&va_context);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateContext returns %08x", va_status);
VASurfaceID tmp_yuv;
va_status = vaCreateSurfaces(
va_dpy,
VA_RT_FORMAT_YUV420,
stride,
bufHeight,
&tmp_yuv,
1,
NULL,
0);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateSurfaces (temp yuv) returns %08x", va_status);
{
MappedSurface mappedVideoIn(va_dpy, tmp_yuv);
if (mappedVideoIn.valid()) {
// Value doesn't matter, as RGBA will be opaque,
// but I don't want random data in here.
memset(mappedVideoIn.getPtr(), 0x0, width*height*3/2);
}
else
ETRACE("Unable to map tmp black surface");
}
{
MappedSurface mappedBlankIn(va_dpy, va_blank_rgb_in);
if (mappedBlankIn.valid()) {
// Fill RGBA with opaque black temporarily, in order to generate an
// encrypted black buffer in va_blank_yuv_in to use in place of the
// real frame data during the short interval where we're waiting for
// downscaling to kick in.
uint32_t* pixels = reinterpret_cast<uint32_t*>(mappedBlankIn.getPtr());
for (size_t i = 0; i < stride*height; i++)
pixels[i] = 0xff000000;
}
else
ETRACE("Unable to map blank rgba in");
}
// Compose opaque black with temp yuv to produce encrypted black yuv.
VARectangle region;
region.x = 0;
region.y = 0;
region.width = width;
region.height = height;
vspCompose(tmp_yuv, va_blank_rgb_in, va_blank_yuv_in, ®ion, ®ion);
va_status = vaDestroySurfaces(va_dpy, &tmp_yuv, 1);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroySurfaces (temp yuv) returns %08x", va_status);
{
// Fill RGBA with transparent black now, to be used when there is no
// UI to compose on top of the video.
MappedSurface mappedBlankIn(va_dpy, va_blank_rgb_in);
if (mappedBlankIn.valid())
memset(mappedBlankIn.getPtr(), 0, stride*height*4);
else
ETRACE("Unable to map blank rgba in");
}
}
void VirtualDevice::vspDisable()
{
ITRACE("Shut down VSP");
if (va_context == 0 && va_blank_yuv_in == 0) {
ITRACE("Already shut down");
return;
}
VABufferID pipeline_param_id;
VAStatus va_status;
va_status = vaCreateBuffer(va_dpy,
va_context,
VAProcPipelineParameterBufferType,
sizeof(VAProcPipelineParameterBuffer),
1,
NULL,
&pipeline_param_id);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateBuffer returns %08x", va_status);
VABlendState blend_state;
VAProcPipelineParameterBuffer *pipeline_param;
va_status = vaMapBuffer(va_dpy,
pipeline_param_id,
(void **)&pipeline_param);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaMapBuffer returns %08x", va_status);
memset(pipeline_param, 0, sizeof(VAProcPipelineParameterBuffer));
pipeline_param->pipeline_flags = VA_PIPELINE_FLAG_END;
pipeline_param->num_filters = 0;
pipeline_param->blend_state = &blend_state;
va_status = vaUnmapBuffer(va_dpy, pipeline_param_id);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaUnmapBuffer returns %08x", va_status);
va_status = vaBeginPicture(va_dpy, va_context, va_blank_yuv_in /* just need some valid surface */);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaBeginPicture returns %08x", va_status);
va_status = vaRenderPicture(va_dpy, va_context, &pipeline_param_id, 1);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaRenderPicture returns %08x", va_status);
va_status = vaEndPicture(va_dpy, va_context);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaEndPicture returns %08x", va_status);
va_status = vaDestroyContext(va_dpy, va_context);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroyContext returns %08x", va_status);
va_context = 0;
va_status = vaDestroySurfaces(va_dpy, &va_blank_yuv_in, 1);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroySurfaces (video in) returns %08x", va_status);
va_blank_yuv_in = 0;
va_status = vaDestroySurfaces(va_dpy, &va_blank_rgb_in, 1);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaDestroySurfaces (blank rgba in) returns %08x", va_status);
if (va_config) {
vaDestroyConfig(va_dpy, va_config);
va_config = 0;
}
if (va_dpy) {
vaTerminate(va_dpy);
va_dpy = NULL;
}
}
void VirtualDevice::vspCompose(VASurfaceID videoIn, VASurfaceID rgbIn, VASurfaceID videoOut,
const VARectangle* surface_region, const VARectangle* output_region)
{
VAStatus va_status;
VABufferID pipeline_param_id;
va_status = vaCreateBuffer(va_dpy,
va_context,
VAProcPipelineParameterBufferType,
sizeof(VAProcPipelineParameterBuffer),
1,
NULL,
&pipeline_param_id);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaCreateBuffer returns %08x", va_status);
VABlendState blend_state;
VAProcPipelineParameterBuffer *pipeline_param;
va_status = vaMapBuffer(va_dpy,
pipeline_param_id,
(void **)&pipeline_param);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaMapBuffer returns %08x", va_status);
memset(pipeline_param, 0, sizeof(VAProcPipelineParameterBuffer));
pipeline_param->surface = videoIn;
pipeline_param->surface_region = surface_region;
pipeline_param->output_region = output_region;
pipeline_param->pipeline_flags = 0;
pipeline_param->num_filters = 0;
pipeline_param->blend_state = &blend_state;
pipeline_param->num_additional_outputs = 1;
pipeline_param->additional_outputs = &rgbIn;
va_status = vaUnmapBuffer(va_dpy, pipeline_param_id);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaUnmapBuffer returns %08x", va_status);
va_status = vaBeginPicture(va_dpy, va_context, videoOut);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaBeginPicture returns %08x", va_status);
va_status = vaRenderPicture(va_dpy, va_context, &pipeline_param_id, 1);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaRenderPicture returns %08x", va_status);
va_status = vaEndPicture(va_dpy, va_context);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaEndPicture returns %08x", va_status);
va_status = vaSyncSurface(va_dpy, videoOut);
if (va_status != VA_STATUS_SUCCESS) ETRACE("vaSyncSurface returns %08x", va_status);
}
static uint32_t min(uint32_t a, uint32_t b)
{
return (a < b) ? a : b;
}
bool VirtualDevice::getFrameOfSize(uint32_t width, uint32_t height, const IVideoPayloadManager::MetaData& metadata, IVideoPayloadManager::Buffer& info)
{
if (metadata.transform == 0 || metadata.transform == HAL_TRANSFORM_ROT_180)
setMaxDecodeResolution(min(width, metadata.normalBuffer.width), min(height, metadata.normalBuffer.height));
else
setMaxDecodeResolution(min(height, metadata.normalBuffer.width), min(width, metadata.normalBuffer.height));
if (metadata.transform == 0) {
if (metadata.normalBuffer.khandle != 0 && metadata.normalBuffer.width <= width && metadata.normalBuffer.height <= height) {
info = metadata.normalBuffer;
return true;
}
if (metadata.scalingBuffer.khandle != 0 && metadata.scalingBuffer.width <= width && metadata.scalingBuffer.height <= height) {
info = metadata.scalingBuffer;
return true;
}
} else {
if (metadata.rotationBuffer.khandle != 0 && metadata.rotationBuffer.width <= width && metadata.rotationBuffer.height <= height) {
info = metadata.rotationBuffer;
return true;
}
}
return false;
}
void VirtualDevice::setMaxDecodeResolution(uint32_t width, uint32_t height)
{
if (mDecWidth == width && mDecHeight == height)
return;
int sessionID = mHwc.getDisplayAnalyzer()->getFirstVideoInstanceSessionID();
if (sessionID < 0) {
ETRACE("Session id is less than 0");
return;
}
MultiDisplayObserver* mds = mHwc.getMultiDisplayObserver();
status_t ret = mds->setDecoderOutputResolution(sessionID, width, height, 0, 0, width, height);
if (ret != NO_ERROR) {
ETRACE("Failed to set scaling to %ux%u: %x", width, height, ret);
return;
}
mDecWidth = width;
mDecHeight = height;
ITRACE("Set scaling to %ux%u",mDecWidth, mDecHeight);
}
bool VirtualDevice::vsyncControl(bool enabled)
{
RETURN_FALSE_IF_NOT_INIT();
return mVsyncObserver->control(enabled);
}
bool VirtualDevice::blank(bool blank)
{
RETURN_FALSE_IF_NOT_INIT();
return true;
}
bool VirtualDevice::getDisplaySize(int *width, int *height)
{
RETURN_FALSE_IF_NOT_INIT();
if (!width || !height) {
ETRACE("invalid parameters");
return false;
}
// TODO: make this platform specifc
*width = 1280;
*height = 720;
return true;
}
bool VirtualDevice::getDisplayConfigs(uint32_t *configs,
size_t *numConfigs)
{
RETURN_FALSE_IF_NOT_INIT();
if (!configs || !numConfigs) {
ETRACE("invalid parameters");
return false;
}
*configs = 0;
*numConfigs = 1;
return true;
}
bool VirtualDevice::getDisplayAttributes(uint32_t configs,
const uint32_t *attributes,
int32_t *values)
{
RETURN_FALSE_IF_NOT_INIT();
if (!attributes || !values) {
ETRACE("invalid parameters");
return false;
}
int i = 0;
while (attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE) {
switch (attributes[i]) {
case HWC_DISPLAY_VSYNC_PERIOD:
values[i] = 1e9 / 60;
break;
case HWC_DISPLAY_WIDTH:
values[i] = 1280;
break;
case HWC_DISPLAY_HEIGHT:
values[i] = 720;
break;
case HWC_DISPLAY_DPI_X:
values[i] = 0;
break;
case HWC_DISPLAY_DPI_Y:
values[i] = 0;
break;
default:
ETRACE("unknown attribute %d", attributes[i]);
break;
}
i++;
}
return true;
}
bool VirtualDevice::compositionComplete()
{
RETURN_FALSE_IF_NOT_INIT();
return true;
}
bool VirtualDevice::initialize()
{
mRgbLayer = -1;
mYuvLayer = -1;
char prop[PROPERTY_VALUE_MAX];
char *retptr;
if (property_get("hwc.fps_divider", prop, "1") > 0) {
uint32_t divider = strtoul(prop, &retptr, 10);
if (*retptr == '\0' && divider > 1 && divider < 60) {
mFpsDivider = divider;
ALOGI("Virtual display, setting HWC FPS divider to %d", mFpsDivider);
}
}
#ifdef INTEL_WIDI
// Add initialization codes here. If init fails, invoke DEINIT_AND_RETURN_FALSE();
mNextConfig.typeChangeListener = NULL;
mNextConfig.policy.scaledWidth = 0;
mNextConfig.policy.scaledHeight = 0;
mNextConfig.policy.xdpi = 96;
mNextConfig.policy.ydpi = 96;
mNextConfig.policy.refresh = 60;
mNextConfig.extendedModeEnabled = false;
mNextConfig.forceNotifyFrameType = false;
mNextConfig.forceNotifyBufferInfo = false;
mCurrentConfig = mNextConfig;
memset(&mLastInputFrameInfo, 0, sizeof(mLastInputFrameInfo));
memset(&mLastOutputFrameInfo, 0, sizeof(mLastOutputFrameInfo));
#endif
mPayloadManager = mHwc.getPlatFactory()->createVideoPayloadManager();
if (!mPayloadManager) {
DEINIT_AND_RETURN_FALSE("Failed to create payload manager");
}
mVsyncObserver = new SoftVsyncObserver(*this);
if (!mVsyncObserver || !mVsyncObserver->initialize()) {
DEINIT_AND_RETURN_FALSE("Failed to create Soft Vsync Observer");
}
mSyncTimelineFd = sw_sync_timeline_create();
mNextSyncPoint = 1;
mExpectAcquireFences = false;
mThread = new WidiBlitThread(this);
mThread->run("WidiBlit", PRIORITY_URGENT_DISPLAY);
#ifdef INTEL_WIDI
// Publish frame server service with service manager
status_t ret = defaultServiceManager()->addService(String16("hwc.widi"), this);
if (ret == NO_ERROR) {
ProcessState::self()->startThreadPool();
mInitialized = true;
} else {
ETRACE("Could not register hwc.widi with service manager, error = %d", ret);
deinitialize();
}
#else
mInitialized = true;
#endif
mVspEnabled = false;
mVspInUse = false;
mVspWidth = 0;
mVspHeight = 0;
va_dpy = NULL;
va_config = 0;
va_context = 0;
va_blank_yuv_in = 0;
va_blank_rgb_in = 0;
mVspUpscale = false;
mDebugVspClear = false;
mDebugVspDump = false;
mDebugCounter = 0;
ITRACE("Init done.");
return mInitialized;
}
bool VirtualDevice::isConnected() const
{
return true;
}
const char* VirtualDevice::getName() const
{
return "Virtual";
}
int VirtualDevice::getType() const
{
return DEVICE_VIRTUAL;
}
void VirtualDevice::onVsync(int64_t timestamp)
{
mHwc.vsync(DEVICE_VIRTUAL, timestamp);
}
void VirtualDevice::dump(Dump& d)
{
}
uint32_t VirtualDevice::getFpsDivider()
{
return mFpsDivider;
}
void VirtualDevice::deinitialize()
{
VAStatus va_status;
if (mPayloadManager) {
delete mPayloadManager;
mPayloadManager = NULL;
}
DEINIT_AND_DELETE_OBJ(mVsyncObserver);
mInitialized = false;
}
bool VirtualDevice::setPowerMode(int /*mode*/)
{
return true;
}
int VirtualDevice::getActiveConfig()
{
return 0;
}
bool VirtualDevice::setActiveConfig(int /*index*/)
{
return false;
}
} // namespace intel
} // namespace android