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