• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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