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 /*
20 * This is a sample video sink using frame buffer push model. The
21 * video output is NV21 (YUV 420 semi-planar with VUVU ordering).
22 * This requires re-ordering the YUV 420 planar output from the
23 * software codec to match the hardware color converter. The
24 * encoder outputs its frames in NV21, so no re-order is necessary.
25 *
26 * The hardware decoder and hardware video unit share pmem buffers
27 * (tunneling mode). For software codecs, we allocate a pmem buffer
28 * and convert the decoded YUV frames while copying to them to the
29 * pmem frame buffers used by the hardware output.
30 *
31 * This code should be compiled into a libopencorehw.so module.
32 * Here is a sample makefile to build the library:
33 *
34 * LOCAL_PATH := $(call my-dir)
35 * include $(CLEAR_VARS)
36 *
37 * # Set up the OpenCore variables.
38 * include external/opencore/Config.mk
39 * LOCAL_C_INCLUDES := $(PV_INCLUDES)
40 *
41 * LOCAL_SRC_FILES := android_surface_output_fb.cpp
42 *
43 * LOCAL_CFLAGS := $(PV_CFLAGS)
44 *
45 * LOCAL_SHARED_LIBRARIES := \
46 * libutils \
47 * libcutils \
48 * libui \
49 * libhardware\
50 * libandroid_runtime \
51 * libmedia \
52 * libsgl \
53 * libopencorecommon \
54 * libicuuc \
55 * libopencoreplayer
56 *
57 * LOCAL_MODULE := libopencorehw
58 *
59 * LOCAL_LDLIBS +=
60 *
61 * include $(BUILD_SHARED_LIBRARY)
62 *
63 */
64
65 //#define LOG_NDEBUG 0
66 #define LOG_TAG "VideoMioFB"
67 #include <utils/Log.h>
68
69 #include "android_surface_output_fb.h"
70 #include <media/PVPlayer.h>
71
72 #define PLATFORM_PRIVATE_PMEM 1
73
74 #if HAVE_ANDROID_OS
75 #include <linux/android_pmem.h>
76 #endif
77
78 using namespace android;
79
80 static const char* pmem_adsp = "/dev/pmem_adsp";
81 static const char* pmem = "/dev/pmem";
82
AndroidSurfaceOutputFB()83 OSCL_EXPORT_REF AndroidSurfaceOutputFB::AndroidSurfaceOutputFB() :
84 AndroidSurfaceOutput()
85 {
86 mHardwareCodec = false;
87 }
88
~AndroidSurfaceOutputFB()89 OSCL_EXPORT_REF AndroidSurfaceOutputFB::~AndroidSurfaceOutputFB()
90 {
91 }
92
93 // create a frame buffer for software codecs
initCheck()94 OSCL_EXPORT_REF bool AndroidSurfaceOutputFB::initCheck()
95 {
96
97 // initialize only when we have all the required parameters
98 if (((iVideoParameterFlags & VIDEO_SUBFORMAT_VALID) == 0) || !checkVideoParameterFlags())
99 return mInitialized;
100
101 // release resources if previously initialized
102 closeFrameBuf();
103
104 // reset flags in case display format changes in the middle of a stream
105 resetVideoParameterFlags();
106
107 // copy parameters in case we need to adjust them
108 int displayWidth = iVideoDisplayWidth;
109 int displayHeight = iVideoDisplayHeight;
110 int frameWidth = iVideoWidth;
111 int frameHeight = iVideoHeight;
112 int frameSize;
113
114 // MSM72xx hardware codec uses semi-planar format
115 if (iVideoSubFormat == PVMF_YUV420_SEMIPLANAR_YVU) {
116 LOGV("using hardware codec");
117 mHardwareCodec = true;
118 } else {
119 LOGV("using software codec");
120
121 // YUV420 frames are 1.5 bytes/pixel
122 frameSize = (frameWidth * frameHeight * 3) / 2;
123
124 // create frame buffer heap
125 sp<MemoryHeapBase> master = new MemoryHeapBase(pmem_adsp, frameSize * kBufferCount);
126 if (master->heapID() < 0) {
127 LOGE("Error creating frame buffer heap");
128 return false;
129 }
130 master->setDevice(pmem);
131 mHeapPmem = new MemoryHeapPmem(master, 0);
132 mHeapPmem->slap();
133 master.clear();
134 ISurface::BufferHeap buffers(displayWidth, displayHeight,
135 frameWidth, frameHeight, PIXEL_FORMAT_YCbCr_420_SP, mHeapPmem);
136 mSurface->registerBuffers(buffers);
137
138 // create frame buffers
139 for (int i = 0; i < kBufferCount; i++) {
140 mFrameBuffers[i] = i * frameSize;
141 }
142
143 LOGV("video = %d x %d", displayWidth, displayHeight);
144 LOGV("frame = %d x %d", frameWidth, frameHeight);
145 LOGV("frame #bytes = %d", frameSize);
146
147 // register frame buffers with SurfaceFlinger
148 mFrameBufferIndex = 0;
149 }
150
151 mInitialized = true;
152 LOGV("sendEvent(MEDIA_SET_VIDEO_SIZE, %d, %d)", iVideoDisplayWidth, iVideoDisplayHeight);
153 mPvPlayer->sendEvent(MEDIA_SET_VIDEO_SIZE, iVideoDisplayWidth, iVideoDisplayHeight);
154 return mInitialized;
155 }
156
writeFrameBuf(uint8 * aData,uint32 aDataLen,const PvmiMediaXferHeader & data_header_info)157 PVMFStatus AndroidSurfaceOutputFB::writeFrameBuf(uint8* aData, uint32 aDataLen, const PvmiMediaXferHeader& data_header_info)
158 {
159 if (mSurface == 0) return PVMFFailure;
160
161 // hardware codec
162 if (mHardwareCodec) {
163
164 // initialize frame buffer heap
165 if (mHeapPmem == 0) {
166 LOGV("initializing for hardware");
167 LOGV("private data pointer is 0%p\n", data_header_info.private_data_ptr);
168
169 // check for correct video format
170 if (iVideoSubFormat != PVMF_YUV420_SEMIPLANAR_YVU) return PVMFFailure;
171
172 uint32 fd;
173 if (!getPmemFd(data_header_info.private_data_ptr, &fd)) {
174 LOGE("Error getting pmem heap from private_data_ptr");
175 return PVMFFailure;
176 }
177 sp<MemoryHeapBase> master = (MemoryHeapBase *) fd;
178 master->setDevice(pmem);
179 mHeapPmem = new MemoryHeapPmem(master, 0);
180 mHeapPmem->slap();
181 master.clear();
182
183 // register frame buffers with SurfaceFlinger
184 ISurface::BufferHeap buffers(iVideoDisplayWidth, iVideoDisplayHeight,
185 iVideoWidth, iVideoHeight,
186 PIXEL_FORMAT_YCbCr_420_SP, mHeapPmem);
187 mSurface->registerBuffers(buffers);
188 }
189
190 // get pmem offset and post to SurfaceFlinger
191 if (!getOffset(data_header_info.private_data_ptr, &mOffset)) {
192 LOGE("Error getting pmem offset from private_data_ptr");
193 return PVMFFailure;
194 }
195 mSurface->postBuffer(mOffset);
196 } else {
197
198 // software codec
199 convertFrame(aData, static_cast<uint8*>(mHeapPmem->base()) + mFrameBuffers[mFrameBufferIndex], aDataLen);
200 // post to SurfaceFlinger
201 if (++mFrameBufferIndex == kBufferCount) mFrameBufferIndex = 0;
202 mSurface->postBuffer(mFrameBuffers[mFrameBufferIndex]);
203 }
204
205 return PVMFSuccess;
206 }
207
208 // post the last video frame to refresh screen after pause
postLastFrame()209 void AndroidSurfaceOutputFB::postLastFrame()
210 {
211 mSurface->postBuffer(mOffset);
212 }
213
closeFrameBuf()214 void AndroidSurfaceOutputFB::closeFrameBuf()
215 {
216 LOGV("CloseFrameBuf");
217 if (!mInitialized) return;
218
219 mInitialized = false;
220 if (mSurface.get()) {
221 LOGV("unregisterBuffers");
222 mSurface->unregisterBuffers();
223 mSurface.clear();
224 }
225
226 // free frame buffers
227 LOGV("free frame buffers");
228 for (int i = 0; i < kBufferCount; i++) {
229 mFrameBuffers[i] = 0;
230 }
231
232 // free heaps
233 LOGV("free mFrameHeap");
234 mFrameHeap.clear();
235 LOGV("free mHeapPmem");
236 mHeapPmem.clear();
237 }
238
getPmemFd(OsclAny * private_data_ptr,uint32 * pmemFD)239 bool AndroidSurfaceOutputFB::getPmemFd(OsclAny *private_data_ptr, uint32 *pmemFD)
240 {
241 PLATFORM_PRIVATE_LIST *listPtr = NULL;
242 PLATFORM_PRIVATE_PMEM_INFO *pmemInfoPtr = NULL;
243 bool returnType = false;
244 LOGV("in getPmemfd - privatedataptr=%p\n",private_data_ptr);
245 listPtr = (PLATFORM_PRIVATE_LIST*) private_data_ptr;
246
247 for (uint32 i=0;i<listPtr->nEntries;i++)
248 {
249 if(listPtr->entryList->type == PLATFORM_PRIVATE_PMEM)
250 {
251 LOGV("in getPmemfd - entry type = %d\n",listPtr->entryList->type);
252 pmemInfoPtr = (PLATFORM_PRIVATE_PMEM_INFO*) (listPtr->entryList->entry);
253 returnType = true;
254 if(pmemInfoPtr){
255 *pmemFD = pmemInfoPtr->pmem_fd;
256 LOGV("in getPmemfd - pmemFD = %d\n",*pmemFD);
257 }
258 break;
259 }
260 }
261 return returnType;
262 }
263
getOffset(OsclAny * private_data_ptr,uint32 * offset)264 bool AndroidSurfaceOutputFB::getOffset(OsclAny *private_data_ptr, uint32 *offset)
265 {
266 PLATFORM_PRIVATE_LIST *listPtr = NULL;
267 PLATFORM_PRIVATE_PMEM_INFO *pmemInfoPtr = NULL;
268 bool returnType = false;
269
270 listPtr = (PLATFORM_PRIVATE_LIST*) private_data_ptr;
271 LOGV("in getOffset: listPtr = %p\n",listPtr);
272 for (uint32 i=0;i<listPtr->nEntries;i++)
273 {
274 if(listPtr->entryList->type == PLATFORM_PRIVATE_PMEM)
275 {
276 LOGV(" in getOffset: entrytype = %d\n",listPtr->entryList->type);
277
278 pmemInfoPtr = (PLATFORM_PRIVATE_PMEM_INFO*) (listPtr->entryList->entry);
279 returnType = true;
280 if(pmemInfoPtr){
281 *offset = pmemInfoPtr->offset;
282 LOGV("in getOffset: offset = %d\n",*offset);
283 }
284 break;
285 }
286 }
287 return returnType;
288 }
289
byteOffset(void * p,size_t offset)290 static inline void* byteOffset(void* p, size_t offset) { return (void*)((uint8_t*)p + offset); }
291
convertFrame(void * src,void * dst,size_t len)292 void AndroidSurfaceOutputFB::convertFrame(void* src, void* dst, size_t len)
293 {
294 // copy the Y plane
295 size_t y_plane_size = iVideoWidth * iVideoHeight;
296 //LOGV("len=%u, y_plane_size=%u", len, y_plane_size);
297 memcpy(dst, src, y_plane_size + iVideoWidth);
298
299 // re-arrange U's and V's
300 uint16_t* pu = (uint16_t*)byteOffset(src, y_plane_size);
301 uint16_t* pv = (uint16_t*)byteOffset(pu, y_plane_size / 4);
302 uint32_t* p = (uint32_t*)byteOffset(dst, y_plane_size);
303
304 int count = y_plane_size / 8;
305 //LOGV("u = %p, v = %p, p = %p, count = %d", pu, pv, p, count);
306 do {
307 uint32_t u = *pu++;
308 uint32_t v = *pv++;
309 *p++ = ((u & 0xff) << 8) | ((u & 0xff00) << 16) | (v & 0xff) | ((v & 0xff00) << 8);
310 } while (--count);
311 }
312
313 // factory function for playerdriver linkage
createVideoMio()314 extern "C" AndroidSurfaceOutput* createVideoMio()
315 {
316 return new AndroidSurfaceOutputFB();
317 }
318
319