1 /*
2 * Copyright (C) 2009 The 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 express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "SoftwareRenderer"
18 #include <utils/Log.h>
19
20 #include "../include/SoftwareRenderer.h"
21
22 #include <cutils/properties.h> // for property_get
23 #include <media/stagefright/foundation/ADebug.h>
24 #include <media/stagefright/foundation/AMessage.h>
25 #include <system/window.h>
26 #include <ui/Fence.h>
27 #include <ui/GraphicBufferMapper.h>
28 #include <ui/GraphicBuffer.h>
29 #include <ui/Rect.h>
30
31 namespace android {
32
runningInEmulator()33 static bool runningInEmulator() {
34 char prop[PROPERTY_VALUE_MAX];
35 return (property_get("ro.kernel.qemu", prop, NULL) > 0);
36 }
37
ALIGN(int x,int y)38 static int ALIGN(int x, int y) {
39 // y must be a power of 2.
40 return (x + y - 1) & ~(y - 1);
41 }
42
SoftwareRenderer(const sp<ANativeWindow> & nativeWindow,int32_t rotation)43 SoftwareRenderer::SoftwareRenderer(
44 const sp<ANativeWindow> &nativeWindow, int32_t rotation)
45 : mColorFormat(OMX_COLOR_FormatUnused),
46 mConverter(NULL),
47 mYUVMode(None),
48 mNativeWindow(nativeWindow),
49 mWidth(0),
50 mHeight(0),
51 mCropLeft(0),
52 mCropTop(0),
53 mCropRight(0),
54 mCropBottom(0),
55 mCropWidth(0),
56 mCropHeight(0),
57 mRotationDegrees(rotation) {
58 }
59
~SoftwareRenderer()60 SoftwareRenderer::~SoftwareRenderer() {
61 delete mConverter;
62 mConverter = NULL;
63 }
64
resetFormatIfChanged(const sp<AMessage> & format)65 void SoftwareRenderer::resetFormatIfChanged(const sp<AMessage> &format) {
66 CHECK(format != NULL);
67
68 int32_t colorFormatNew;
69 CHECK(format->findInt32("color-format", &colorFormatNew));
70
71 int32_t widthNew, heightNew;
72 CHECK(format->findInt32("stride", &widthNew));
73 CHECK(format->findInt32("slice-height", &heightNew));
74
75 int32_t cropLeftNew, cropTopNew, cropRightNew, cropBottomNew;
76 if (!format->findRect(
77 "crop", &cropLeftNew, &cropTopNew, &cropRightNew, &cropBottomNew)) {
78 cropLeftNew = cropTopNew = 0;
79 cropRightNew = widthNew - 1;
80 cropBottomNew = heightNew - 1;
81 }
82
83 if (static_cast<int32_t>(mColorFormat) == colorFormatNew &&
84 mWidth == widthNew &&
85 mHeight == heightNew &&
86 mCropLeft == cropLeftNew &&
87 mCropTop == cropTopNew &&
88 mCropRight == cropRightNew &&
89 mCropBottom == cropBottomNew) {
90 // Nothing changed, no need to reset renderer.
91 return;
92 }
93
94 mColorFormat = static_cast<OMX_COLOR_FORMATTYPE>(colorFormatNew);
95 mWidth = widthNew;
96 mHeight = heightNew;
97 mCropLeft = cropLeftNew;
98 mCropTop = cropTopNew;
99 mCropRight = cropRightNew;
100 mCropBottom = cropBottomNew;
101
102 mCropWidth = mCropRight - mCropLeft + 1;
103 mCropHeight = mCropBottom - mCropTop + 1;
104
105 // by default convert everything to RGB565
106 int halFormat = HAL_PIXEL_FORMAT_RGB_565;
107 size_t bufWidth = mCropWidth;
108 size_t bufHeight = mCropHeight;
109
110 // hardware has YUV12 and RGBA8888 support, so convert known formats
111 if (!runningInEmulator()) {
112 switch (mColorFormat) {
113 case OMX_COLOR_FormatYUV420Planar:
114 case OMX_COLOR_FormatYUV420SemiPlanar:
115 case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
116 {
117 halFormat = HAL_PIXEL_FORMAT_YV12;
118 bufWidth = (mCropWidth + 1) & ~1;
119 bufHeight = (mCropHeight + 1) & ~1;
120 break;
121 }
122 case OMX_COLOR_Format24bitRGB888:
123 {
124 halFormat = HAL_PIXEL_FORMAT_RGB_888;
125 bufWidth = (mCropWidth + 1) & ~1;
126 bufHeight = (mCropHeight + 1) & ~1;
127 break;
128 }
129 case OMX_COLOR_Format32bitARGB8888:
130 case OMX_COLOR_Format32BitRGBA8888:
131 {
132 halFormat = HAL_PIXEL_FORMAT_RGBA_8888;
133 bufWidth = (mCropWidth + 1) & ~1;
134 bufHeight = (mCropHeight + 1) & ~1;
135 break;
136 }
137 default:
138 {
139 break;
140 }
141 }
142 }
143
144 if (halFormat == HAL_PIXEL_FORMAT_RGB_565) {
145 mConverter = new ColorConverter(
146 mColorFormat, OMX_COLOR_Format16bitRGB565);
147 CHECK(mConverter->isValid());
148 }
149
150 CHECK(mNativeWindow != NULL);
151 CHECK(mCropWidth > 0);
152 CHECK(mCropHeight > 0);
153 CHECK(mConverter == NULL || mConverter->isValid());
154
155 CHECK_EQ(0,
156 native_window_set_usage(
157 mNativeWindow.get(),
158 GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
159 | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));
160
161 CHECK_EQ(0,
162 native_window_set_scaling_mode(
163 mNativeWindow.get(),
164 NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW));
165
166 // Width must be multiple of 32???
167 CHECK_EQ(0, native_window_set_buffers_dimensions(
168 mNativeWindow.get(),
169 bufWidth,
170 bufHeight));
171 CHECK_EQ(0, native_window_set_buffers_format(
172 mNativeWindow.get(),
173 halFormat));
174
175 // NOTE: native window uses extended right-bottom coordinate
176 android_native_rect_t crop;
177 crop.left = mCropLeft;
178 crop.top = mCropTop;
179 crop.right = mCropRight + 1;
180 crop.bottom = mCropBottom + 1;
181 ALOGV("setting crop: [%d, %d, %d, %d] for size [%zu, %zu]",
182 crop.left, crop.top, crop.right, crop.bottom, bufWidth, bufHeight);
183
184 CHECK_EQ(0, native_window_set_crop(mNativeWindow.get(), &crop));
185
186 int32_t rotationDegrees;
187 if (!format->findInt32("rotation-degrees", &rotationDegrees)) {
188 rotationDegrees = mRotationDegrees;
189 }
190 uint32_t transform;
191 switch (rotationDegrees) {
192 case 0: transform = 0; break;
193 case 90: transform = HAL_TRANSFORM_ROT_90; break;
194 case 180: transform = HAL_TRANSFORM_ROT_180; break;
195 case 270: transform = HAL_TRANSFORM_ROT_270; break;
196 default: transform = 0; break;
197 }
198
199 CHECK_EQ(0, native_window_set_buffers_transform(
200 mNativeWindow.get(), transform));
201 }
202
clearTracker()203 void SoftwareRenderer::clearTracker() {
204 mRenderTracker.clear(-1 /* lastRenderTimeNs */);
205 }
206
render(const void * data,size_t size,int64_t mediaTimeUs,nsecs_t renderTimeNs,void *,const sp<AMessage> & format)207 std::list<FrameRenderTracker::Info> SoftwareRenderer::render(
208 const void *data, size_t size, int64_t mediaTimeUs, nsecs_t renderTimeNs,
209 void* /*platformPrivate*/, const sp<AMessage>& format) {
210 resetFormatIfChanged(format);
211 FrameRenderTracker::Info *info = NULL;
212
213 ANativeWindowBuffer *buf;
214 int fenceFd = -1;
215 int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
216 if (err == 0 && fenceFd >= 0) {
217 info = mRenderTracker.updateInfoForDequeuedBuffer(buf, fenceFd, 0);
218 sp<Fence> fence = new Fence(fenceFd);
219 err = fence->waitForever("SoftwareRenderer::render");
220 }
221 if (err != 0) {
222 ALOGW("Surface::dequeueBuffer returned error %d", err);
223 // complete (drop) dequeued frame if fence wait failed; otherwise,
224 // this returns an empty list as no frames should have rendered and not yet returned.
225 return mRenderTracker.checkFencesAndGetRenderedFrames(info, false /* dropIncomplete */);
226 }
227
228 GraphicBufferMapper &mapper = GraphicBufferMapper::get();
229
230 Rect bounds(mCropWidth, mCropHeight);
231
232 void *dst;
233 CHECK_EQ(0, mapper.lock(
234 buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, &dst));
235
236 // TODO move the other conversions also into ColorConverter, and
237 // fix cropping issues (when mCropLeft/Top != 0 or mWidth != mCropWidth)
238 if (mConverter) {
239 mConverter->convert(
240 data,
241 mWidth, mHeight,
242 mCropLeft, mCropTop, mCropRight, mCropBottom,
243 dst,
244 buf->stride, buf->height,
245 0, 0, mCropWidth - 1, mCropHeight - 1);
246 } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar) {
247 if ((size_t)mWidth * mHeight * 3 / 2 > size) {
248 goto skip_copying;
249 }
250 const uint8_t *src_y = (const uint8_t *)data;
251 const uint8_t *src_u =
252 (const uint8_t *)data + mWidth * mHeight;
253 const uint8_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
254
255 uint8_t *dst_y = (uint8_t *)dst;
256 size_t dst_y_size = buf->stride * buf->height;
257 size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
258 size_t dst_c_size = dst_c_stride * buf->height / 2;
259 uint8_t *dst_v = dst_y + dst_y_size;
260 uint8_t *dst_u = dst_v + dst_c_size;
261
262 for (int y = 0; y < mCropHeight; ++y) {
263 memcpy(dst_y, src_y, mCropWidth);
264
265 src_y += mWidth;
266 dst_y += buf->stride;
267 }
268
269 for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
270 memcpy(dst_u, src_u, (mCropWidth + 1) / 2);
271 memcpy(dst_v, src_v, (mCropWidth + 1) / 2);
272
273 src_u += mWidth / 2;
274 src_v += mWidth / 2;
275 dst_u += dst_c_stride;
276 dst_v += dst_c_stride;
277 }
278 } else if (mColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
279 || mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
280 if ((size_t)mWidth * mHeight * 3 / 2 > size) {
281 goto skip_copying;
282 }
283 const uint8_t *src_y = (const uint8_t *)data;
284 const uint8_t *src_uv = (const uint8_t *)data
285 + mWidth * (mHeight - mCropTop / 2);
286
287 uint8_t *dst_y = (uint8_t *)dst;
288
289 size_t dst_y_size = buf->stride * buf->height;
290 size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
291 size_t dst_c_size = dst_c_stride * buf->height / 2;
292 uint8_t *dst_v = dst_y + dst_y_size;
293 uint8_t *dst_u = dst_v + dst_c_size;
294
295 for (int y = 0; y < mCropHeight; ++y) {
296 memcpy(dst_y, src_y, mCropWidth);
297
298 src_y += mWidth;
299 dst_y += buf->stride;
300 }
301
302 for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
303 size_t tmp = (mCropWidth + 1) / 2;
304 for (size_t x = 0; x < tmp; ++x) {
305 dst_u[x] = src_uv[2 * x];
306 dst_v[x] = src_uv[2 * x + 1];
307 }
308
309 src_uv += mWidth;
310 dst_u += dst_c_stride;
311 dst_v += dst_c_stride;
312 }
313 } else if (mColorFormat == OMX_COLOR_Format24bitRGB888) {
314 if ((size_t)mWidth * mHeight * 3 > size) {
315 goto skip_copying;
316 }
317 uint8_t* srcPtr = (uint8_t*)data;
318 uint8_t* dstPtr = (uint8_t*)dst;
319
320 for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
321 memcpy(dstPtr, srcPtr, mCropWidth * 3);
322 srcPtr += mWidth * 3;
323 dstPtr += buf->stride * 3;
324 }
325 } else if (mColorFormat == OMX_COLOR_Format32bitARGB8888) {
326 if ((size_t)mWidth * mHeight * 4 > size) {
327 goto skip_copying;
328 }
329 uint8_t *srcPtr, *dstPtr;
330
331 for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
332 srcPtr = (uint8_t*)data + mWidth * 4 * y;
333 dstPtr = (uint8_t*)dst + buf->stride * 4 * y;
334 for (size_t x = 0; x < (size_t)mCropWidth; ++x) {
335 uint8_t a = *srcPtr++;
336 for (size_t i = 0; i < 3; ++i) { // copy RGB
337 *dstPtr++ = *srcPtr++;
338 }
339 *dstPtr++ = a; // alpha last (ARGB to RGBA)
340 }
341 }
342 } else if (mColorFormat == OMX_COLOR_Format32BitRGBA8888) {
343 if ((size_t)mWidth * mHeight * 4 > size) {
344 goto skip_copying;
345 }
346 uint8_t* srcPtr = (uint8_t*)data;
347 uint8_t* dstPtr = (uint8_t*)dst;
348
349 for (size_t y = 0; y < (size_t)mCropHeight; ++y) {
350 memcpy(dstPtr, srcPtr, mCropWidth * 4);
351 srcPtr += mWidth * 4;
352 dstPtr += buf->stride * 4;
353 }
354 } else {
355 LOG_ALWAYS_FATAL("bad color format %#x", mColorFormat);
356 }
357
358 skip_copying:
359 CHECK_EQ(0, mapper.unlock(buf->handle));
360
361 if (renderTimeNs >= 0) {
362 if ((err = native_window_set_buffers_timestamp(mNativeWindow.get(),
363 renderTimeNs)) != 0) {
364 ALOGW("Surface::set_buffers_timestamp returned error %d", err);
365 }
366 }
367
368 // TODO: propagate color aspects to software renderer to allow better
369 // color conversion to RGB. For now, just mark dataspace for YUV rendering.
370 android_dataspace dataSpace;
371 if (format->findInt32("android._dataspace", (int32_t *)&dataSpace) && dataSpace != mDataSpace) {
372 ALOGD("setting dataspace on output surface to #%x", dataSpace);
373 if ((err = native_window_set_buffers_data_space(mNativeWindow.get(), dataSpace))) {
374 ALOGW("failed to set dataspace on surface (%d)", err);
375 }
376 mDataSpace = dataSpace;
377 }
378 if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf, -1)) != 0) {
379 ALOGW("Surface::queueBuffer returned error %d", err);
380 } else {
381 mRenderTracker.onFrameQueued(mediaTimeUs, (GraphicBuffer *)buf, Fence::NO_FENCE);
382 }
383
384 buf = NULL;
385 return mRenderTracker.checkFencesAndGetRenderedFrames(info, info != NULL /* dropIncomplete */);
386 }
387
388 } // namespace android
389