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 <binder/MemoryHeapBase.h>
21 #include <media/stagefright/MediaDebug.h>
22 #include <media/stagefright/SoftwareRenderer.h>
23 #include <ui/ISurface.h>
24
25 namespace android {
26
27 #define QCOM_YUV 0
28
SoftwareRenderer(OMX_COLOR_FORMATTYPE colorFormat,const sp<ISurface> & surface,size_t displayWidth,size_t displayHeight,size_t decodedWidth,size_t decodedHeight)29 SoftwareRenderer::SoftwareRenderer(
30 OMX_COLOR_FORMATTYPE colorFormat,
31 const sp<ISurface> &surface,
32 size_t displayWidth, size_t displayHeight,
33 size_t decodedWidth, size_t decodedHeight)
34 : mColorFormat(colorFormat),
35 mISurface(surface),
36 mDisplayWidth(displayWidth),
37 mDisplayHeight(displayHeight),
38 mDecodedWidth(decodedWidth),
39 mDecodedHeight(decodedHeight),
40 mFrameSize(mDecodedWidth * mDecodedHeight * 2), // RGB565
41 mMemoryHeap(new MemoryHeapBase(2 * mFrameSize)),
42 mIndex(0),
43 mClip(NULL) {
44 CHECK(mISurface.get() != NULL);
45 CHECK(mDecodedWidth > 0);
46 CHECK(mDecodedHeight > 0);
47 CHECK(mMemoryHeap->heapID() >= 0);
48
49 ISurface::BufferHeap bufferHeap(
50 mDisplayWidth, mDisplayHeight,
51 mDecodedWidth, mDecodedHeight,
52 PIXEL_FORMAT_RGB_565,
53 mMemoryHeap);
54
55 status_t err = mISurface->registerBuffers(bufferHeap);
56 CHECK_EQ(err, OK);
57 }
58
~SoftwareRenderer()59 SoftwareRenderer::~SoftwareRenderer() {
60 mISurface->unregisterBuffers();
61
62 delete[] mClip;
63 mClip = NULL;
64 }
65
render(const void * data,size_t size,void * platformPrivate)66 void SoftwareRenderer::render(
67 const void *data, size_t size, void *platformPrivate) {
68 static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
69
70 switch (mColorFormat) {
71 case OMX_COLOR_FormatYUV420Planar:
72 return renderYUV420Planar(data, size);
73
74 case OMX_COLOR_FormatCbYCrY:
75 return renderCbYCrY(data, size);
76
77 case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
78 return renderQCOMYUV420SemiPlanar(data, size);
79
80 default:
81 {
82 LOGW("Cannot render color format %d", mColorFormat);
83 break;
84 }
85 }
86 }
87
renderYUV420Planar(const void * data,size_t size)88 void SoftwareRenderer::renderYUV420Planar(
89 const void *data, size_t size) {
90 if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) {
91 LOGE("size is %d, expected %d",
92 size, (mDecodedHeight * mDecodedWidth * 3) / 2);
93 }
94 CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
95
96 uint8_t *kAdjustedClip = initClip();
97
98 size_t offset = mIndex * mFrameSize;
99
100 void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
101
102 uint32_t *dst_ptr = (uint32_t *)dst;
103
104 const uint8_t *src_y = (const uint8_t *)data;
105
106 const uint8_t *src_u =
107 (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
108
109 #if !QCOM_YUV
110 const uint8_t *src_v =
111 (const uint8_t *)src_u + (mDecodedWidth / 2) * (mDecodedHeight / 2);
112 #endif
113
114 for (size_t y = 0; y < mDecodedHeight; ++y) {
115 for (size_t x = 0; x < mDecodedWidth; x += 2) {
116 // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
117 // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
118 // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
119
120 // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
121 // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
122 // R = .................. + 409/256 * (V - 128)
123
124 // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
125 // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
126 // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
127
128 // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
129 // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
130 // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
131
132 // clip range -278 .. 535
133
134 signed y1 = (signed)src_y[x] - 16;
135 signed y2 = (signed)src_y[x + 1] - 16;
136
137 #if QCOM_YUV
138 signed u = (signed)src_u[x & ~1] - 128;
139 signed v = (signed)src_u[(x & ~1) + 1] - 128;
140 #else
141 signed u = (signed)src_u[x / 2] - 128;
142 signed v = (signed)src_v[x / 2] - 128;
143 #endif
144
145 signed u_b = u * 517;
146 signed u_g = -u * 100;
147 signed v_g = -v * 208;
148 signed v_r = v * 409;
149
150 signed tmp1 = y1 * 298;
151 signed b1 = (tmp1 + u_b) / 256;
152 signed g1 = (tmp1 + v_g + u_g) / 256;
153 signed r1 = (tmp1 + v_r) / 256;
154
155 signed tmp2 = y2 * 298;
156 signed b2 = (tmp2 + u_b) / 256;
157 signed g2 = (tmp2 + v_g + u_g) / 256;
158 signed r2 = (tmp2 + v_r) / 256;
159
160 uint32_t rgb1 =
161 ((kAdjustedClip[r1] >> 3) << 11)
162 | ((kAdjustedClip[g1] >> 2) << 5)
163 | (kAdjustedClip[b1] >> 3);
164
165 uint32_t rgb2 =
166 ((kAdjustedClip[r2] >> 3) << 11)
167 | ((kAdjustedClip[g2] >> 2) << 5)
168 | (kAdjustedClip[b2] >> 3);
169
170 dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
171 }
172
173 src_y += mDecodedWidth;
174
175 if (y & 1) {
176 #if QCOM_YUV
177 src_u += mDecodedWidth;
178 #else
179 src_u += mDecodedWidth / 2;
180 src_v += mDecodedWidth / 2;
181 #endif
182 }
183
184 dst_ptr += mDecodedWidth / 2;
185 }
186
187 mISurface->postBuffer(offset);
188 mIndex = 1 - mIndex;
189 }
190
renderCbYCrY(const void * data,size_t size)191 void SoftwareRenderer::renderCbYCrY(
192 const void *data, size_t size) {
193 if (size != (mDecodedHeight * mDecodedWidth * 2)) {
194 LOGE("size is %d, expected %d",
195 size, (mDecodedHeight * mDecodedWidth * 2));
196 }
197 CHECK(size >= (mDecodedWidth * mDecodedHeight * 2));
198
199 uint8_t *kAdjustedClip = initClip();
200
201 size_t offset = mIndex * mFrameSize;
202 void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
203 uint32_t *dst_ptr = (uint32_t *)dst;
204
205 const uint8_t *src = (const uint8_t *)data;
206
207 for (size_t y = 0; y < mDecodedHeight; ++y) {
208 for (size_t x = 0; x < mDecodedWidth; x += 2) {
209 signed y1 = (signed)src[2 * x + 1] - 16;
210 signed y2 = (signed)src[2 * x + 3] - 16;
211 signed u = (signed)src[2 * x] - 128;
212 signed v = (signed)src[2 * x + 2] - 128;
213
214 signed u_b = u * 517;
215 signed u_g = -u * 100;
216 signed v_g = -v * 208;
217 signed v_r = v * 409;
218
219 signed tmp1 = y1 * 298;
220 signed b1 = (tmp1 + u_b) / 256;
221 signed g1 = (tmp1 + v_g + u_g) / 256;
222 signed r1 = (tmp1 + v_r) / 256;
223
224 signed tmp2 = y2 * 298;
225 signed b2 = (tmp2 + u_b) / 256;
226 signed g2 = (tmp2 + v_g + u_g) / 256;
227 signed r2 = (tmp2 + v_r) / 256;
228
229 uint32_t rgb1 =
230 ((kAdjustedClip[r1] >> 3) << 11)
231 | ((kAdjustedClip[g1] >> 2) << 5)
232 | (kAdjustedClip[b1] >> 3);
233
234 uint32_t rgb2 =
235 ((kAdjustedClip[r2] >> 3) << 11)
236 | ((kAdjustedClip[g2] >> 2) << 5)
237 | (kAdjustedClip[b2] >> 3);
238
239 dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
240 }
241
242 src += mDecodedWidth * 2;
243 dst_ptr += mDecodedWidth / 2;
244 }
245
246 mISurface->postBuffer(offset);
247 mIndex = 1 - mIndex;
248 }
249
renderQCOMYUV420SemiPlanar(const void * data,size_t size)250 void SoftwareRenderer::renderQCOMYUV420SemiPlanar(
251 const void *data, size_t size) {
252 if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) {
253 LOGE("size is %d, expected %d",
254 size, (mDecodedHeight * mDecodedWidth * 3) / 2);
255 }
256 CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
257
258 uint8_t *kAdjustedClip = initClip();
259
260 size_t offset = mIndex * mFrameSize;
261
262 void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
263
264 uint32_t *dst_ptr = (uint32_t *)dst;
265
266 const uint8_t *src_y = (const uint8_t *)data;
267
268 const uint8_t *src_u =
269 (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
270
271 for (size_t y = 0; y < mDecodedHeight; ++y) {
272 for (size_t x = 0; x < mDecodedWidth; x += 2) {
273 signed y1 = (signed)src_y[x] - 16;
274 signed y2 = (signed)src_y[x + 1] - 16;
275
276 signed u = (signed)src_u[x & ~1] - 128;
277 signed v = (signed)src_u[(x & ~1) + 1] - 128;
278
279 signed u_b = u * 517;
280 signed u_g = -u * 100;
281 signed v_g = -v * 208;
282 signed v_r = v * 409;
283
284 signed tmp1 = y1 * 298;
285 signed b1 = (tmp1 + u_b) / 256;
286 signed g1 = (tmp1 + v_g + u_g) / 256;
287 signed r1 = (tmp1 + v_r) / 256;
288
289 signed tmp2 = y2 * 298;
290 signed b2 = (tmp2 + u_b) / 256;
291 signed g2 = (tmp2 + v_g + u_g) / 256;
292 signed r2 = (tmp2 + v_r) / 256;
293
294 uint32_t rgb1 =
295 ((kAdjustedClip[b1] >> 3) << 11)
296 | ((kAdjustedClip[g1] >> 2) << 5)
297 | (kAdjustedClip[r1] >> 3);
298
299 uint32_t rgb2 =
300 ((kAdjustedClip[b2] >> 3) << 11)
301 | ((kAdjustedClip[g2] >> 2) << 5)
302 | (kAdjustedClip[r2] >> 3);
303
304 dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
305 }
306
307 src_y += mDecodedWidth;
308
309 if (y & 1) {
310 src_u += mDecodedWidth;
311 }
312
313 dst_ptr += mDecodedWidth / 2;
314 }
315
316 mISurface->postBuffer(offset);
317 mIndex = 1 - mIndex;
318 }
319
initClip()320 uint8_t *SoftwareRenderer::initClip() {
321 static const signed kClipMin = -278;
322 static const signed kClipMax = 535;
323
324 if (mClip == NULL) {
325 mClip = new uint8_t[kClipMax - kClipMin + 1];
326
327 for (signed i = kClipMin; i <= kClipMax; ++i) {
328 mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
329 }
330 }
331
332 return &mClip[-kClipMin];
333 }
334
335 } // namespace android
336