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