blob: 450a8bc7a17dbe6f720148ff7733592618468cbd [file] [log] [blame]
Michael Sevakisa222f272007-12-29 19:46:35 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
Dave Chapmanc9d66562006-08-07 22:11:07 +00009 *
Michael Sevakisa222f272007-12-29 19:46:35 +000010 * mpegplayer video output routines
Dave Chapmanc9d66562006-08-07 22:11:07 +000011 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Dave Chapmanc9d66562006-08-07 22:11:07 +000016 *
Michael Sevakisa222f272007-12-29 19:46:35 +000017 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
Dave Chapmanc9d66562006-08-07 22:11:07 +000019 *
Michael Sevakisa222f272007-12-29 19:46:35 +000020 ****************************************************************************/
Dave Chapmanc9d66562006-08-07 22:11:07 +000021#include "mpeg2dec_config.h"
22
23#include "plugin.h"
Michael Sevakisa222f272007-12-29 19:46:35 +000024#include "mpegplayer.h"
Dave Chapmanc9d66562006-08-07 22:11:07 +000025
Michael Sevakisa5fc3f42008-01-03 17:14:28 +000026#define VO_NON_NULL_RECT 0x1
27#define VO_VISIBLE 0x2
28
Michael Sevakisa222f272007-12-29 19:46:35 +000029struct vo_data
30{
31 int image_width;
32 int image_height;
33 int image_chroma_x;
34 int image_chroma_y;
35 int display_width;
36 int display_height;
37 int output_x;
38 int output_y;
39 int output_width;
40 int output_height;
Michael Sevakisa5fc3f42008-01-03 17:14:28 +000041 unsigned flags;
42 struct vo_rect rc_vid;
43 struct vo_rect rc_clip;
Michael Sevakisa222f272007-12-29 19:46:35 +000044};
Dave Chapmanc9d66562006-08-07 22:11:07 +000045
Michael Sevakisa222f272007-12-29 19:46:35 +000046#ifdef PROC_NEEDS_CACHEALIGN
47/* Cache aligned and padded to avoid clobbering other processors' cacheable
48 * data */
49static uint8_t __vo_data[CACHEALIGN_UP(sizeof(struct vo_data))]
50 CACHEALIGN_ATTR;
51#define vo (*((struct vo_data *)__vo_data))
52#else
53static struct vo_data vo;
54#endif
Dave Chapmanc9d66562006-08-07 22:11:07 +000055
Michael Sevakisa5fc3f42008-01-03 17:14:28 +000056#if NUM_CORES > 1
Michael Sevakis05099142008-04-06 04:34:57 +000057static struct mutex vo_mtx SHAREDBSS_ATTR;
Michael Sevakisa5fc3f42008-01-03 17:14:28 +000058#endif
59
60static inline void video_lock_init(void)
61{
62#if NUM_CORES > 1
63 rb->mutex_init(&vo_mtx);
64#endif
65}
66
67static inline void video_lock(void)
68{
69#if NUM_CORES > 1
70 rb->mutex_lock(&vo_mtx);
71#endif
72}
73
74static inline void video_unlock(void)
75{
76#if NUM_CORES > 1
77 rb->mutex_unlock(&vo_mtx);
78#endif
79}
80
81
Michael Sevakisa222f272007-12-29 19:46:35 +000082/* Draw a black rectangle if no video frame is available */
83static void vo_draw_black(void)
84{
Michael Sevakisa5fc3f42008-01-03 17:14:28 +000085 int foreground;
86
87 video_lock();
88
89 foreground = lcd_(get_foreground)();
Dave Chapmanc9d66562006-08-07 22:11:07 +000090
Michael Sevakisa222f272007-12-29 19:46:35 +000091 lcd_(set_foreground)(DRAW_BLACK);
92
93 lcd_(fillrect)(vo.output_x, vo.output_y, vo.output_width,
94 vo.output_height);
95 lcd_(update_rect)(vo.output_x, vo.output_y, vo.output_width,
96 vo.output_height);
97
98 lcd_(set_foreground)(foreground);
Michael Sevakisa5fc3f42008-01-03 17:14:28 +000099
100 video_unlock();
Michael Sevakisa222f272007-12-29 19:46:35 +0000101}
102
103static inline void yuv_blit(uint8_t * const * buf, int src_x, int src_y,
104 int stride, int x, int y, int width, int height)
Dave Chapmanc9d66562006-08-07 22:11:07 +0000105{
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000106 video_lock();
107
Dave Chapmana5675712007-03-26 01:32:31 +0000108#ifdef HAVE_LCD_COLOR
Jens Arnold68a21682008-03-24 00:35:53 +0000109 rb->lcd_blit_yuv(buf, src_x, src_y, stride, x, y , width, height);
Dave Chapmana5675712007-03-26 01:32:31 +0000110#else
Jens Arnoldfeb5b152008-01-04 23:42:38 +0000111 grey_ub_gray_bitmap_part(buf[0], src_x, src_y, stride, x, y, width, height);
Dave Chapmana5675712007-03-26 01:32:31 +0000112#endif
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000113
114 video_unlock();
Dave Chapmanc9d66562006-08-07 22:11:07 +0000115}
116
Michael Sevakisa222f272007-12-29 19:46:35 +0000117void vo_draw_frame(uint8_t * const * buf)
118{
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000119 if (vo.flags == 0)
Michael Sevakisa222f272007-12-29 19:46:35 +0000120 {
121 /* Frame is hidden - copout */
122 DEBUGF("vo hidden\n");
123 return;
124 }
125 else if (buf == NULL)
126 {
127 /* No frame exists - draw black */
128 vo_draw_black();
129 DEBUGF("vo no frame\n");
130 return;
131 }
132
133 yuv_blit(buf, 0, 0, vo.image_width,
134 vo.output_x, vo.output_y, vo.output_width,
135 vo.output_height);
136}
137
Michael Sevakisa222f272007-12-29 19:46:35 +0000138static inline void vo_rect_clear_inl(struct vo_rect *rc)
Robert Kuklafd3fe452007-10-09 20:42:20 +0000139{
Michael Sevakisa222f272007-12-29 19:46:35 +0000140 rc->l = rc->t = rc->r = rc->b = 0;
141}
142
143static inline bool vo_rect_empty_inl(const struct vo_rect *rc)
144{
145 return rc == NULL || rc->l >= rc->r || rc->t >= rc->b;
146}
147
148static inline bool vo_rects_intersect_inl(const struct vo_rect *rc1,
149 const struct vo_rect *rc2)
150{
151 return !vo_rect_empty_inl(rc1) &&
152 !vo_rect_empty_inl(rc2) &&
153 rc1->l < rc2->r && rc1->r > rc2->l &&
154 rc1->t < rc2->b && rc1->b > rc2->t;
155}
156
157/* Sets all coordinates of a vo_rect to 0 */
158void vo_rect_clear(struct vo_rect *rc)
159{
160 vo_rect_clear_inl(rc);
161}
162
163/* Returns true if left >= right or top >= bottom */
164bool vo_rect_empty(const struct vo_rect *rc)
165{
166 return vo_rect_empty_inl(rc);
167}
168
169/* Initializes a vo_rect using upper-left corner and extents */
170void vo_rect_set_ext(struct vo_rect *rc, int x, int y,
171 int width, int height)
172{
173 rc->l = x;
174 rc->t = y;
175 rc->r = x + width;
176 rc->b = y + height;
177}
178
179/* Query if two rectangles intersect */
180bool vo_rects_intersect(const struct vo_rect *rc1,
181 const struct vo_rect *rc2)
182{
183 return vo_rects_intersect_inl(rc1, rc2);
184}
185
186/* Intersect two rectangles, placing the result in rc_dst */
187bool vo_rect_intersect(struct vo_rect *rc_dst,
188 const struct vo_rect *rc1,
189 const struct vo_rect *rc2)
190{
191 if (rc_dst != NULL)
192 {
193 if (vo_rects_intersect_inl(rc1, rc2))
194 {
195 rc_dst->l = MAX(rc1->l, rc2->l);
196 rc_dst->r = MIN(rc1->r, rc2->r);
197 rc_dst->t = MAX(rc1->t, rc2->t);
198 rc_dst->b = MIN(rc1->b, rc2->b);
199 return true;
200 }
201
202 vo_rect_clear_inl(rc_dst);
203 }
204
205 return false;
206}
207
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000208bool vo_rect_union(struct vo_rect *rc_dst,
209 const struct vo_rect *rc1,
210 const struct vo_rect *rc2)
211{
212 if (rc_dst != NULL)
213 {
214 if (!vo_rect_empty_inl(rc1))
215 {
216 if (!vo_rect_empty_inl(rc2))
217 {
218 rc_dst->l = MIN(rc1->l, rc2->l);
219 rc_dst->t = MIN(rc1->t, rc2->t);
220 rc_dst->r = MAX(rc1->r, rc2->r);
221 rc_dst->b = MAX(rc1->b, rc2->b);
222 }
223 else
224 {
225 *rc_dst = *rc1;
226 }
227
228 return true;
229 }
Michael Sevakise4bb2bb2008-01-26 15:15:50 +0000230 else if (!vo_rect_empty_inl(rc2))
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000231 {
232 *rc_dst = *rc2;
233 return true;
234 }
235
236 vo_rect_clear_inl(rc_dst);
237 }
238
239 return false;
240}
241
242void vo_rect_offset(struct vo_rect *rc, int dx, int dy)
243{
244 rc->l += dx;
245 rc->t += dy;
246 rc->r += dx;
247 rc->b += dy;
248}
249
Michael Sevakisa222f272007-12-29 19:46:35 +0000250/* Shink or stretch each axis - rotate counter-clockwise to retain upright
251 * orientation on rotated displays (they rotate clockwise) */
252void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride,
253 int src_w, int src_h, int dst_w, int dst_h)
254{
255 uint8_t *dst_end = dst + dst_w*dst_h;
Robert Kuklafd3fe452007-10-09 20:42:20 +0000256
257#if LCD_WIDTH >= LCD_HEIGHT
Michael Sevakisa222f272007-12-29 19:46:35 +0000258 int src_w2 = src_w*2; /* 2x dimensions (for rounding before division) */
259 int dst_w2 = dst_w*2;
260 int src_h2 = src_h*2;
261 int dst_h2 = dst_h*2;
262 int qw = src_w2 / dst_w2; /* src-dst width ratio quotient */
263 int rw = src_w2 - qw*dst_w2; /* src-dst width ratio remainder */
264 int qh = src_h2 / dst_h2; /* src-dst height ratio quotient */
265 int rh = src_h2 - qh*dst_h2; /* src-dst height ratio remainder */
266 int dw = dst_w; /* Width error accumulator */
267 int dh = dst_h; /* Height error accumulator */
Robert Kuklafd3fe452007-10-09 20:42:20 +0000268#else
Michael Sevakisa222f272007-12-29 19:46:35 +0000269 int src_w2 = src_w*2;
270 int dst_w2 = dst_h*2;
271 int src_h2 = src_h*2;
272 int dst_h2 = dst_w*2;
273 int qw = src_h2 / dst_w2;
274 int rw = src_h2 - qw*dst_w2;
275 int qh = src_w2 / dst_h2;
276 int rh = src_w2 - qh*dst_h2;
277 int dw = dst_h;
278 int dh = dst_w;
279
280 src += src_w - 1;
Robert Kuklafd3fe452007-10-09 20:42:20 +0000281#endif
282
Michael Sevakisa222f272007-12-29 19:46:35 +0000283 while (1)
284 {
285 const uint8_t *s = src;
286#if LCD_WIDTH >= LCD_HEIGHT
287 uint8_t * const dst_line_end = dst + dst_w;
288#else
289 uint8_t * const dst_line_end = dst + dst_h;
290#endif
291 while (1)
292 {
293 *dst++ = *s;
294
295 if (dst >= dst_line_end)
296 {
297 dw = dst_w;
298 break;
299 }
300
301#if LCD_WIDTH >= LCD_HEIGHT
302 s += qw;
303#else
304 s += qw*stride;
305#endif
306 dw += rw;
307
308 if (dw >= dst_w2)
309 {
310 dw -= dst_w2;
311#if LCD_WIDTH >= LCD_HEIGHT
312 s++;
313#else
314 s += stride;
315#endif
316 }
317 }
318
319 if (dst >= dst_end)
320 break;
321#if LCD_WIDTH >= LCD_HEIGHT
322 src += qh*stride;
323#else
324 src -= qh;
325#endif
326 dh += rh;
327
328 if (dh >= dst_h2)
329 {
330 dh -= dst_h2;
331#if LCD_WIDTH >= LCD_HEIGHT
332 src += stride;
333#else
334 src--;
335#endif
336 }
337 }
338}
339
340bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc)
341{
342 void *mem;
343 size_t bufsize;
344 uint8_t *yuv[3];
345 struct vo_rect thumb_rc;
346 int thumb_width, thumb_height;
347 int thumb_uv_width, thumb_uv_height;
348
349 if (buf == NULL)
350 return false;
351
352 /* Obtain rectangle as clipped to the screen */
353 vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT);
354 if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc))
355 return true;
356
357 DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t,
358 thumb_rc.r, thumb_rc.b);
359
360 thumb_width = rc->r - rc->l;
361 thumb_height = rc->b - rc->t;
362 thumb_uv_width = thumb_width / 2;
363 thumb_uv_height = thumb_height / 2;
364
365 DEBUGF("thumb: w: %d h: %d uvw: %d uvh: %d\n", thumb_width,
366 thumb_height, thumb_uv_width, thumb_uv_height);
367
368 /* Use remaining mpeg2 buffer as temp space */
369 mem = mpeg2_get_buf(&bufsize);
370
371 if (bufsize < (size_t)(thumb_width*thumb_height)
372#ifdef HAVE_LCD_COLOR
373 + 2u*(thumb_uv_width * thumb_uv_height)
374#endif
375 )
376 {
377 DEBUGF("thumb: insufficient buffer\n");
378 return false;
379 }
380
381 yuv[0] = mem;
382 stretch_image_plane(buf[0], yuv[0], vo.image_width,
383 vo.display_width, vo.display_height,
384 thumb_width, thumb_height);
Robert Kuklafd3fe452007-10-09 20:42:20 +0000385
386#ifdef HAVE_LCD_COLOR
Michael Sevakisa222f272007-12-29 19:46:35 +0000387 yuv[1] = yuv[0] + thumb_width*thumb_height;
388 yuv[2] = yuv[1] + thumb_uv_width*thumb_uv_height;
Robert Kuklafd3fe452007-10-09 20:42:20 +0000389
Michael Sevakisa222f272007-12-29 19:46:35 +0000390 stretch_image_plane(buf[1], yuv[1], vo.image_width / 2,
391 vo.display_width / 2, vo.display_height / 2,
392 thumb_uv_width, thumb_uv_height);
393
394 stretch_image_plane(buf[2], yuv[2], vo.image_width / 2,
395 vo.display_width / 2, vo.display_height / 2,
396 thumb_uv_width, thumb_uv_height);
Robert Kuklafd3fe452007-10-09 20:42:20 +0000397#endif
Michael Sevakisa222f272007-12-29 19:46:35 +0000398
Robert Kuklafd3fe452007-10-09 20:42:20 +0000399#if LCD_WIDTH >= LCD_HEIGHT
Michael Sevakisa222f272007-12-29 19:46:35 +0000400 yuv_blit(yuv, 0, 0, thumb_width,
401 thumb_rc.l, thumb_rc.t,
402 thumb_rc.r - thumb_rc.l,
403 thumb_rc.b - thumb_rc.t);
Robert Kuklafd3fe452007-10-09 20:42:20 +0000404#else
Michael Sevakisa222f272007-12-29 19:46:35 +0000405 yuv_blit(yuv, 0, 0, thumb_height,
406 thumb_rc.t, thumb_rc.l,
407 thumb_rc.b - thumb_rc.t,
408 thumb_rc.r - thumb_rc.l);
409#endif /* LCD_WIDTH >= LCD_HEIGHT */
410
411 return true;
Robert Kuklafd3fe452007-10-09 20:42:20 +0000412}
413
Marcoen Hirschberg4d9c0012007-04-04 22:51:57 +0000414void vo_setup(const mpeg2_sequence_t * sequence)
Dave Chapmanc9d66562006-08-07 22:11:07 +0000415{
Michael Sevakisa222f272007-12-29 19:46:35 +0000416 vo.image_width = sequence->width;
417 vo.image_height = sequence->height;
418 vo.display_width = sequence->display_width;
419 vo.display_height = sequence->display_height;
Robert Kuklafd3fe452007-10-09 20:42:20 +0000420
Michael Sevakisa222f272007-12-29 19:46:35 +0000421 DEBUGF("vo_setup - w:%d h:%d\n", vo.display_width, vo.display_height);
Robert Kuklafd3fe452007-10-09 20:42:20 +0000422
Michael Sevakisa222f272007-12-29 19:46:35 +0000423 vo.image_chroma_x = vo.image_width / sequence->chroma_width;
424 vo.image_chroma_y = vo.image_height / sequence->chroma_height;
Dave Chapmanc9d66562006-08-07 22:11:07 +0000425
Michael Sevakisa222f272007-12-29 19:46:35 +0000426 if (sequence->display_width >= SCREEN_WIDTH)
427 {
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000428 vo.rc_vid.l = 0;
429 vo.rc_vid.r = SCREEN_WIDTH;
Michael Sevakisa222f272007-12-29 19:46:35 +0000430 }
431 else
432 {
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000433 vo.rc_vid.l = (SCREEN_WIDTH - sequence->display_width) / 2;
Michael Sevakise4bb2bb2008-01-26 15:15:50 +0000434#ifdef HAVE_LCD_COLOR
435 vo.rc_vid.l &= ~1;
436#endif
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000437 vo.rc_vid.r = vo.rc_vid.l + sequence->display_width;
Dave Chapmanc9d66562006-08-07 22:11:07 +0000438 }
439
Michael Sevakisa222f272007-12-29 19:46:35 +0000440 if (sequence->display_height >= SCREEN_HEIGHT)
441 {
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000442 vo.rc_vid.t = 0;
443 vo.rc_vid.b = SCREEN_HEIGHT;
Dave Chapmanc9d66562006-08-07 22:11:07 +0000444 }
Michael Sevakisa222f272007-12-29 19:46:35 +0000445 else
446 {
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000447 vo.rc_vid.t = (SCREEN_HEIGHT - sequence->display_height) / 2;
Michael Sevakise4bb2bb2008-01-26 15:15:50 +0000448#ifdef HAVE_LCD_COLOR
449 vo.rc_vid.t &= ~1;
450#endif
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000451 vo.rc_vid.b = vo.rc_vid.t + sequence->display_height;
Michael Sevakisa222f272007-12-29 19:46:35 +0000452 }
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000453
454 vo_set_clip_rect(&vo.rc_clip);
Michael Sevakisa222f272007-12-29 19:46:35 +0000455}
456
457void vo_dimensions(struct vo_ext *sz)
458{
459 sz->w = vo.display_width;
460 sz->h = vo.display_height;
461}
462
463bool vo_init(void)
464{
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000465 vo.flags = 0;
466 vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
467 video_lock_init();
Michael Sevakisa222f272007-12-29 19:46:35 +0000468 return true;
469}
470
471bool vo_show(bool show)
472{
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000473 bool vis = vo.flags & VO_VISIBLE;
474
475 if (show)
476 vo.flags |= VO_VISIBLE;
477 else
478 vo.flags &= ~VO_VISIBLE;
479
Michael Sevakisa222f272007-12-29 19:46:35 +0000480 return vis;
481}
482
483bool vo_is_visible(void)
484{
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000485 return vo.flags & VO_VISIBLE;
Dave Chapmanc9d66562006-08-07 22:11:07 +0000486}
Robert Kuklafd3fe452007-10-09 20:42:20 +0000487
488void vo_cleanup(void)
489{
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000490 vo.flags = 0;
Robert Kuklafd3fe452007-10-09 20:42:20 +0000491}
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000492
493void vo_set_clip_rect(const struct vo_rect *rc)
494{
495 struct vo_rect rc_out;
496
497 if (rc == NULL)
498 vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
499 else
500 vo.rc_clip = *rc;
501
502 if (!vo_rect_intersect(&rc_out, &vo.rc_vid, &vo.rc_clip))
503 vo.flags &= ~VO_NON_NULL_RECT;
504 else
505 vo.flags |= VO_NON_NULL_RECT;
506
507 vo.output_x = rc_out.l;
508 vo.output_y = rc_out.t;
509 vo.output_width = rc_out.r - rc_out.l;
510 vo.output_height = rc_out.b - rc_out.t;
511}
512
Michael Sevakis75380fd2008-01-09 22:19:25 +0000513#if NUM_CORES > 1
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000514void vo_lock(void)
515{
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000516 video_lock();
517}
518
519void vo_unlock(void)
520{
521 video_unlock();
Michael Sevakisa5fc3f42008-01-03 17:14:28 +0000522}
523#endif