Home | History | Annotate | Download | only in libopencorehw
      1 /* ------------------------------------------------------------------
      2  * Copyright (C) 2009 Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
     13  * express or implied.
     14  * See the License for the specific language governing permissions
     15  * and limitations under the License.
     16  * -------------------------------------------------------------------
     17  */
     18 
     19 //#define LOG_NDEBUG 0
     20 #define LOG_TAG "VideoMio72xx"
     21 #include <utils/Log.h>
     22 
     23 #include "android_surface_output_msm72xx.h"
     24 #include <media/PVPlayer.h>
     25 
     26 #define PLATFORM_PRIVATE_PMEM 1
     27 
     28 #if HAVE_ANDROID_OS
     29 #include <linux/android_pmem.h>
     30 #endif
     31 
     32 using namespace android;
     33 
     34 static const char* pmem_adsp = "/dev/pmem_adsp";
     35 static const char* pmem = "/dev/pmem";
     36 
     37 OSCL_EXPORT_REF AndroidSurfaceOutputMsm72xx::AndroidSurfaceOutputMsm72xx() :
     38     AndroidSurfaceOutput()
     39 {
     40     mHardwareCodec = false;
     41 }
     42 
     43 OSCL_EXPORT_REF AndroidSurfaceOutputMsm72xx::~AndroidSurfaceOutputMsm72xx()
     44 {
     45 }
     46 
     47 // create a frame buffer for software codecs
     48 OSCL_EXPORT_REF bool AndroidSurfaceOutputMsm72xx::initCheck()
     49 {
     50 
     51     // initialize only when we have all the required parameters
     52     if (((iVideoParameterFlags & VIDEO_SUBFORMAT_VALID) == 0) || !checkVideoParameterFlags())
     53         return mInitialized;
     54 
     55     // release resources if previously initialized
     56     closeFrameBuf();
     57 
     58     // reset flags in case display format changes in the middle of a stream
     59     resetVideoParameterFlags();
     60 
     61     // copy parameters in case we need to adjust them
     62     int displayWidth = iVideoDisplayWidth;
     63     int displayHeight = iVideoDisplayHeight;
     64     int frameWidth = iVideoWidth;
     65     int frameHeight = iVideoHeight;
     66     int frameSize;
     67 
     68     // MSM72xx hardware codec uses semi-planar format
     69     if (iVideoSubFormat == PVMF_MIME_YUV420_SEMIPLANAR_YVU) {
     70         LOGV("using hardware codec");
     71         mHardwareCodec = true;
     72         // Workaround for tearing from SF
     73         // But please make sure that the number of unique output
     74         // buffers are more than 2; otherwise, the video playback
     75         // will freeze due to starvation.
     76         mNumberOfFramesToHold = 2;
     77     } else {
     78         LOGV("using software codec");
     79 
     80         // YUV420 frames are 1.5 bytes/pixel
     81         frameSize = (frameWidth * frameHeight * 3) / 2;
     82 
     83         // create frame buffer heap
     84         sp<MemoryHeapBase> master = new MemoryHeapBase(pmem_adsp, frameSize * kBufferCount);
     85         if (master->heapID() < 0) {
     86             LOGE("Error creating frame buffer heap");
     87             return false;
     88         }
     89         master->setDevice(pmem);
     90         sp<MemoryHeapPmem> heap = new MemoryHeapPmem(master, 0);
     91         heap->slap();
     92         mBufferHeap = ISurface::BufferHeap(displayWidth, displayHeight,
     93                 frameWidth, frameHeight, HAL_PIXEL_FORMAT_YCrCb_420_SP, heap);
     94         master.clear();
     95         mSurface->registerBuffers(mBufferHeap);
     96 
     97         // create frame buffers
     98         for (int i = 0; i < kBufferCount; i++) {
     99             mFrameBuffers[i] = i * frameSize;
    100         }
    101 
    102         LOGV("video = %d x %d", displayWidth, displayHeight);
    103         LOGV("frame = %d x %d", frameWidth, frameHeight);
    104         LOGV("frame #bytes = %d", frameSize);
    105 
    106         // register frame buffers with SurfaceFlinger
    107         mFrameBufferIndex = 0;
    108     }
    109 
    110     mInitialized = true;
    111     LOGV("sendEvent(MEDIA_SET_VIDEO_SIZE, %d, %d)", iVideoDisplayWidth, iVideoDisplayHeight);
    112     mPvPlayer->sendEvent(MEDIA_SET_VIDEO_SIZE, iVideoDisplayWidth, iVideoDisplayHeight);
    113     return mInitialized;
    114 }
    115 
    116 PVMFStatus AndroidSurfaceOutputMsm72xx::writeFrameBuf(uint8* aData, uint32 aDataLen, const PvmiMediaXferHeader& data_header_info)
    117 {
    118     // OK to drop frames if no surface
    119     if (mSurface == 0) return PVMFSuccess;
    120 
    121     // hardware codec
    122     if (mHardwareCodec) {
    123 
    124         // initialize frame buffer heap
    125         if (mBufferHeap.heap == 0) {
    126             LOGV("initializing for hardware");
    127             LOGV("private data pointer is 0%p\n", data_header_info.private_data_ptr);
    128 
    129             // check for correct video format
    130             if (iVideoSubFormat != PVMF_MIME_YUV420_SEMIPLANAR_YVU) return PVMFFailure;
    131 
    132             uint32 fd;
    133             if (!getPmemFd(data_header_info.private_data_ptr, &fd)) {
    134                 LOGE("Error getting pmem heap from private_data_ptr");
    135                 return PVMFFailure;
    136             }
    137 
    138             // ugly hack to pass an sp<MemoryHeapBase> as an int
    139             sp<MemoryHeapBase> master = (MemoryHeapBase *) fd;
    140             master->setDevice(pmem);
    141 
    142             // create new reference
    143             uint32_t heap_flags = master->getFlags() & MemoryHeapBase::NO_CACHING;
    144             sp<MemoryHeapPmem> heap = new MemoryHeapPmem(master, heap_flags);
    145             heap->slap();
    146 
    147             // register frame buffers with SurfaceFlinger
    148             mBufferHeap = ISurface::BufferHeap(iVideoDisplayWidth, iVideoDisplayHeight,
    149                     iVideoWidth, iVideoHeight, HAL_PIXEL_FORMAT_YCrCb_420_SP, heap);
    150             master.clear();
    151             mSurface->registerBuffers(mBufferHeap);
    152         }
    153 
    154         // get pmem offset and post to SurfaceFlinger
    155         if (!getOffset(data_header_info.private_data_ptr, &mOffset)) {
    156             LOGE("Error getting pmem offset from private_data_ptr");
    157             return PVMFFailure;
    158         }
    159         mSurface->postBuffer(mOffset);
    160     } else {
    161         // software codec
    162         if (++mFrameBufferIndex == kBufferCount) mFrameBufferIndex = 0;
    163         convertFrame(aData, static_cast<uint8*>(mBufferHeap.heap->base()) + mFrameBuffers[mFrameBufferIndex], aDataLen);
    164         // post to SurfaceFlinger
    165         mSurface->postBuffer(mFrameBuffers[mFrameBufferIndex]);
    166     }
    167 
    168     return PVMFSuccess;
    169 }
    170 
    171 // post the last video frame to refresh screen after pause
    172 void AndroidSurfaceOutputMsm72xx::postLastFrame()
    173 {
    174     // ignore if no surface or heap
    175     if ((mSurface == NULL) || (mBufferHeap.heap == NULL)) return;
    176 
    177     if (mHardwareCodec) {
    178         mSurface->postBuffer(mOffset);
    179     } else {
    180         mSurface->postBuffer(mFrameBuffers[mFrameBufferIndex]);
    181     }
    182 }
    183 
    184 bool AndroidSurfaceOutputMsm72xx::getPmemFd(OsclAny *private_data_ptr, uint32 *pmemFD)
    185 {
    186     PLATFORM_PRIVATE_LIST *listPtr = NULL;
    187     PLATFORM_PRIVATE_PMEM_INFO *pmemInfoPtr = NULL;
    188     bool returnType = false;
    189     LOGV("in getPmemfd - privatedataptr=%p\n",private_data_ptr);
    190     listPtr = (PLATFORM_PRIVATE_LIST*) private_data_ptr;
    191 
    192     for (uint32 i=0;i<listPtr->nEntries;i++)
    193     {
    194         if(listPtr->entryList->type == PLATFORM_PRIVATE_PMEM)
    195         {
    196             LOGV("in getPmemfd - entry type = %d\n",listPtr->entryList->type);
    197           pmemInfoPtr = (PLATFORM_PRIVATE_PMEM_INFO*) (listPtr->entryList->entry);
    198           returnType = true;
    199           if(pmemInfoPtr){
    200             *pmemFD = pmemInfoPtr->pmem_fd;
    201             LOGV("in getPmemfd - pmemFD = %d\n",*pmemFD);
    202           }
    203           break;
    204         }
    205     }
    206     return returnType;
    207 }
    208 
    209 bool AndroidSurfaceOutputMsm72xx::getOffset(OsclAny *private_data_ptr, uint32 *offset)
    210 {
    211     PLATFORM_PRIVATE_LIST *listPtr = NULL;
    212     PLATFORM_PRIVATE_PMEM_INFO *pmemInfoPtr = NULL;
    213     bool returnType = false;
    214 
    215     listPtr = (PLATFORM_PRIVATE_LIST*) private_data_ptr;
    216     LOGV("in getOffset: listPtr = %p\n",listPtr);
    217     for (uint32 i=0;i<listPtr->nEntries;i++)
    218     {
    219         if(listPtr->entryList->type == PLATFORM_PRIVATE_PMEM)
    220         {
    221             LOGV(" in getOffset: entrytype = %d\n",listPtr->entryList->type);
    222 
    223           pmemInfoPtr = (PLATFORM_PRIVATE_PMEM_INFO*) (listPtr->entryList->entry);
    224           returnType = true;
    225           if(pmemInfoPtr){
    226             *offset = pmemInfoPtr->offset;
    227             LOGV("in getOffset: offset = %d\n",*offset);
    228           }
    229           break;
    230         }
    231     }
    232     return returnType;
    233 }
    234 
    235 static inline void* byteOffset(void* p, size_t offset) { return (void*)((uint8_t*)p + offset); }
    236 
    237 void AndroidSurfaceOutputMsm72xx::convertFrame(void* src, void* dst, size_t len)
    238 {
    239     // copy the Y plane
    240     size_t y_plane_size = iVideoWidth * iVideoHeight;
    241     //LOGV("len=%u, y_plane_size=%u", len, y_plane_size);
    242     memcpy(dst, src, y_plane_size + iVideoWidth);
    243 
    244     // re-arrange U's and V's
    245     uint16_t* pu = (uint16_t*)byteOffset(src, y_plane_size);
    246     uint16_t* pv = (uint16_t*)byteOffset(pu, y_plane_size / 4);
    247     uint32_t* p = (uint32_t*)byteOffset(dst, y_plane_size);
    248 
    249     int count = y_plane_size / 8;
    250     //LOGV("u = %p, v = %p, p = %p, count = %d", pu, pv, p, count);
    251     do {
    252         uint32_t u = *pu++;
    253         uint32_t v = *pv++;
    254         *p++ = ((u & 0xff) << 8) | ((u & 0xff00) << 16) | (v & 0xff) | ((v & 0xff00) << 8);
    255     } while (--count);
    256 }
    257 
    258 // factory function for playerdriver linkage
    259 extern "C" AndroidSurfaceOutputMsm72xx* createVideoMio()
    260 {
    261     return new AndroidSurfaceOutputMsm72xx();
    262 }
    263 
    264