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
AndroidSurfaceOutputMsm72xx()37 OSCL_EXPORT_REF AndroidSurfaceOutputMsm72xx::AndroidSurfaceOutputMsm72xx() :
38 AndroidSurfaceOutput()
39 {
40 mHardwareCodec = false;
41 }
42
~AndroidSurfaceOutputMsm72xx()43 OSCL_EXPORT_REF AndroidSurfaceOutputMsm72xx::~AndroidSurfaceOutputMsm72xx()
44 {
45 }
46
47 // create a frame buffer for software codecs
initCheck()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
writeFrameBuf(uint8 * aData,uint32 aDataLen,const PvmiMediaXferHeader & data_header_info)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
postLastFrame()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
getPmemFd(OsclAny * private_data_ptr,uint32 * pmemFD)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
getOffset(OsclAny * private_data_ptr,uint32 * offset)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
byteOffset(void * p,size_t offset)235 static inline void* byteOffset(void* p, size_t offset) { return (void*)((uint8_t*)p + offset); }
236
convertFrame(void * src,void * dst,size_t len)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
createVideoMio()259 extern "C" AndroidSurfaceOutputMsm72xx* createVideoMio()
260 {
261 return new AndroidSurfaceOutputMsm72xx();
262 }
263
264