| /* |
| // 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) |
| { |
| 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; |
| #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) |
| { |
| } |
| |
| 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 |