summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer/video_out_rockbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/mpegplayer/video_out_rockbox.c')
-rw-r--r--apps/plugins/mpegplayer/video_out_rockbox.c576
1 files changed, 576 insertions, 0 deletions
diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c
new file mode 100644
index 0000000000..331383843b
--- /dev/null
+++ b/apps/plugins/mpegplayer/video_out_rockbox.c
@@ -0,0 +1,576 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * mpegplayer video output routines
11 *
12 * 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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "libmpeg2/mpeg2dec_config.h"
22
23#include "plugin.h"
24#include "mpegplayer.h"
25
26#define VO_NON_NULL_RECT 0x1
27#define VO_VISIBLE 0x2
28
29struct 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;
41 unsigned flags;
42 struct vo_rect rc_vid;
43 struct vo_rect rc_clip;
44 void (*post_draw_callback)(void);
45};
46
47#if NUM_CORES > 1
48/* Cache aligned and padded to avoid clobbering other processors' cacheable
49 * data */
50static union {
51 uint8_t __vo_data[CACHEALIGN_UP(sizeof(struct vo_data))];
52 struct vo_data vo;
53} vo_raw CACHEALIGN_ATTR;
54#define vo vo_raw.vo
55#else
56static struct vo_data vo;
57#endif
58
59#if NUM_CORES > 1
60static struct mutex vo_mtx SHAREDBSS_ATTR;
61#endif
62
63static inline void video_lock_init(void)
64{
65#if NUM_CORES > 1
66 rb->mutex_init(&vo_mtx);
67#endif
68}
69
70static inline void video_lock(void)
71{
72#if NUM_CORES > 1
73 rb->mutex_lock(&vo_mtx);
74#endif
75}
76
77static inline void video_unlock(void)
78{
79#if NUM_CORES > 1
80 rb->mutex_unlock(&vo_mtx);
81#endif
82}
83
84
85/* Draw a black rectangle if no video frame is available */
86static void vo_draw_black(struct vo_rect *rc)
87{
88 int foreground;
89 int x, y, w, h;
90
91 video_lock();
92
93 foreground = mylcd_get_foreground();
94
95 mylcd_set_foreground(MYLCD_BLACK);
96
97 if (rc)
98 {
99 x = rc->l;
100 y = rc->t;
101 w = rc->r - rc->l;
102 h = rc->b - rc->t;
103 }
104 else
105 {
106#if LCD_WIDTH >= LCD_HEIGHT
107 x = vo.output_x;
108 y = vo.output_y;
109 w = vo.output_width;
110 h = vo.output_height;
111#else
112 x = LCD_WIDTH - vo.output_height - vo.output_y;
113 y = vo.output_x;
114 w = vo.output_height;
115 h = vo.output_width;
116#endif
117 }
118
119 mylcd_fillrect(x, y, w, h);
120 mylcd_update_rect(x, y, w, h);
121
122 mylcd_set_foreground(foreground);
123
124 video_unlock();
125}
126
127static inline void yuv_blit(uint8_t * const * buf, int src_x, int src_y,
128 int stride, int x, int y, int width, int height)
129{
130 video_lock();
131
132#ifdef HAVE_LCD_COLOR
133 rb->lcd_blit_yuv(buf, src_x, src_y, stride, x, y , width, height);
134#else
135 grey_ub_gray_bitmap_part(buf[0], src_x, src_y, stride, x, y, width, height);
136#endif
137
138 video_unlock();
139}
140
141void vo_draw_frame(uint8_t * const * buf)
142{
143 if ((vo.flags & (VO_NON_NULL_RECT | VO_VISIBLE)) !=
144 (VO_NON_NULL_RECT | VO_VISIBLE))
145 {
146 /* Frame is hidden - either by being set invisible or is clipped
147 * away - copout */
148 DEBUGF("vo hidden\n");
149 }
150 else if (buf == NULL)
151 {
152 /* No frame exists - draw black */
153 vo_draw_black(NULL);
154 DEBUGF("vo no frame\n");
155 }
156 else
157 {
158 yuv_blit(buf, 0, 0, vo.image_width,
159 vo.output_x, vo.output_y, vo.output_width,
160 vo.output_height);
161 }
162
163 if (vo.post_draw_callback)
164 vo.post_draw_callback();
165}
166
167static inline void vo_rect_clear_inl(struct vo_rect *rc)
168{
169 rc->l = rc->t = rc->r = rc->b = 0;
170}
171
172static inline bool vo_rect_empty_inl(const struct vo_rect *rc)
173{
174 return rc == NULL || rc->l >= rc->r || rc->t >= rc->b;
175}
176
177static inline bool vo_rects_intersect_inl(const struct vo_rect *rc1,
178 const struct vo_rect *rc2)
179{
180 return !vo_rect_empty_inl(rc1) &&
181 !vo_rect_empty_inl(rc2) &&
182 rc1->l < rc2->r && rc1->r > rc2->l &&
183 rc1->t < rc2->b && rc1->b > rc2->t;
184}
185
186/* Sets all coordinates of a vo_rect to 0 */
187void vo_rect_clear(struct vo_rect *rc)
188{
189 vo_rect_clear_inl(rc);
190}
191
192/* Returns true if left >= right or top >= bottom */
193bool vo_rect_empty(const struct vo_rect *rc)
194{
195 return vo_rect_empty_inl(rc);
196}
197
198/* Initializes a vo_rect using upper-left corner and extents */
199void vo_rect_set_ext(struct vo_rect *rc, int x, int y,
200 int width, int height)
201{
202 rc->l = x;
203 rc->t = y;
204 rc->r = x + width;
205 rc->b = y + height;
206}
207
208/* Query if two rectangles intersect */
209bool vo_rects_intersect(const struct vo_rect *rc1,
210 const struct vo_rect *rc2)
211{
212 return vo_rects_intersect_inl(rc1, rc2);
213}
214
215/* Intersect two rectangles, placing the result in rc_dst */
216bool vo_rect_intersect(struct vo_rect *rc_dst,
217 const struct vo_rect *rc1,
218 const struct vo_rect *rc2)
219{
220 if (rc_dst != NULL)
221 {
222 if (vo_rects_intersect_inl(rc1, rc2))
223 {
224 rc_dst->l = MAX(rc1->l, rc2->l);
225 rc_dst->r = MIN(rc1->r, rc2->r);
226 rc_dst->t = MAX(rc1->t, rc2->t);
227 rc_dst->b = MIN(rc1->b, rc2->b);
228 return true;
229 }
230
231 vo_rect_clear_inl(rc_dst);
232 }
233
234 return false;
235}
236
237bool vo_rect_union(struct vo_rect *rc_dst,
238 const struct vo_rect *rc1,
239 const struct vo_rect *rc2)
240{
241 if (rc_dst != NULL)
242 {
243 if (!vo_rect_empty_inl(rc1))
244 {
245 if (!vo_rect_empty_inl(rc2))
246 {
247 rc_dst->l = MIN(rc1->l, rc2->l);
248 rc_dst->t = MIN(rc1->t, rc2->t);
249 rc_dst->r = MAX(rc1->r, rc2->r);
250 rc_dst->b = MAX(rc1->b, rc2->b);
251 }
252 else
253 {
254 *rc_dst = *rc1;
255 }
256
257 return true;
258 }
259 else if (!vo_rect_empty_inl(rc2))
260 {
261 *rc_dst = *rc2;
262 return true;
263 }
264
265 vo_rect_clear_inl(rc_dst);
266 }
267
268 return false;
269}
270
271void vo_rect_offset(struct vo_rect *rc, int dx, int dy)
272{
273 rc->l += dx;
274 rc->t += dy;
275 rc->r += dx;
276 rc->b += dy;
277}
278
279/* Shink or stretch each axis - rotate counter-clockwise to retain upright
280 * orientation on rotated displays (they rotate clockwise) */
281void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride,
282 int src_w, int src_h, int dst_w, int dst_h)
283{
284 uint8_t *dst_end = dst + dst_w*dst_h;
285
286#if LCD_WIDTH >= LCD_HEIGHT
287 int src_w2 = src_w*2; /* 2x dimensions (for rounding before division) */
288 int dst_w2 = dst_w*2;
289 int src_h2 = src_h*2;
290 int dst_h2 = dst_h*2;
291 int qw = src_w2 / dst_w2; /* src-dst width ratio quotient */
292 int rw = src_w2 - qw*dst_w2; /* src-dst width ratio remainder */
293 int qh = src_h2 / dst_h2; /* src-dst height ratio quotient */
294 int rh = src_h2 - qh*dst_h2; /* src-dst height ratio remainder */
295 int dw = dst_w; /* Width error accumulator */
296 int dh = dst_h; /* Height error accumulator */
297#else
298 int src_w2 = src_w*2;
299 int dst_w2 = dst_h*2;
300 int src_h2 = src_h*2;
301 int dst_h2 = dst_w*2;
302 int qw = src_h2 / dst_w2;
303 int rw = src_h2 - qw*dst_w2;
304 int qh = src_w2 / dst_h2;
305 int rh = src_w2 - qh*dst_h2;
306 int dw = dst_h;
307 int dh = dst_w;
308
309 src += src_w - 1;
310#endif
311
312 while (1)
313 {
314 const uint8_t *s = src;
315#if LCD_WIDTH >= LCD_HEIGHT
316 uint8_t * const dst_line_end = dst + dst_w;
317#else
318 uint8_t * const dst_line_end = dst + dst_h;
319#endif
320 while (1)
321 {
322 *dst++ = *s;
323
324 if (dst >= dst_line_end)
325 {
326 dw = dst_w;
327 break;
328 }
329
330#if LCD_WIDTH >= LCD_HEIGHT
331 s += qw;
332#else
333 s += qw*stride;
334#endif
335 dw += rw;
336
337 if (dw >= dst_w2)
338 {
339 dw -= dst_w2;
340#if LCD_WIDTH >= LCD_HEIGHT
341 s++;
342#else
343 s += stride;
344#endif
345 }
346 }
347
348 if (dst >= dst_end)
349 break;
350#if LCD_WIDTH >= LCD_HEIGHT
351 src += qh*stride;
352#else
353 src -= qh;
354#endif
355 dh += rh;
356
357 if (dh >= dst_h2)
358 {
359 dh -= dst_h2;
360#if LCD_WIDTH >= LCD_HEIGHT
361 src += stride;
362#else
363 src--;
364#endif
365 }
366 }
367}
368
369bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc)
370{
371 void *mem;
372 size_t bufsize = 0;
373 uint8_t *yuv[3];
374 struct vo_rect thumb_rc;
375 int thumb_width, thumb_height;
376#ifdef HAVE_LCD_COLOR
377 int thumb_uv_width, thumb_uv_height;
378#endif
379
380 /* Obtain rectangle as clipped to the screen */
381 vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT);
382 if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc))
383 return true;
384
385 if (buf == NULL)
386 goto no_thumb_exit;
387
388 DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t,
389 thumb_rc.r, thumb_rc.b);
390
391 thumb_width = rc->r - rc->l;
392 thumb_height = rc->b - rc->t;
393#ifdef HAVE_LCD_COLOR
394 thumb_uv_width = thumb_width / 2;
395 thumb_uv_height = thumb_height / 2;
396
397 DEBUGF("thumb: w: %d h: %d uvw: %d uvh: %d\n", thumb_width,
398 thumb_height, thumb_uv_width, thumb_uv_height);
399#else
400 DEBUGF("thumb: w: %d h: %d\n", thumb_width, thumb_height);
401#endif
402
403 /* Use remaining mpeg2 buffer as temp space */
404 mem = mpeg2_get_buf(&bufsize);
405
406 if (bufsize < (size_t)(thumb_width*thumb_height)
407#ifdef HAVE_LCD_COLOR
408 + 2u*(thumb_uv_width * thumb_uv_height)
409#endif
410 )
411 {
412 DEBUGF("thumb: insufficient buffer\n");
413 goto no_thumb_exit;
414 }
415
416 yuv[0] = mem;
417 stretch_image_plane(buf[0], yuv[0], vo.image_width,
418 vo.display_width, vo.display_height,
419 thumb_width, thumb_height);
420
421#ifdef HAVE_LCD_COLOR
422 yuv[1] = yuv[0] + thumb_width*thumb_height;
423 yuv[2] = yuv[1] + thumb_uv_width*thumb_uv_height;
424
425 stretch_image_plane(buf[1], yuv[1], vo.image_width / 2,
426 vo.display_width / 2, vo.display_height / 2,
427 thumb_uv_width, thumb_uv_height);
428
429 stretch_image_plane(buf[2], yuv[2], vo.image_width / 2,
430 vo.display_width / 2, vo.display_height / 2,
431 thumb_uv_width, thumb_uv_height);
432#endif
433
434#if LCD_WIDTH >= LCD_HEIGHT
435 yuv_blit(yuv, 0, 0, thumb_width,
436 thumb_rc.l, thumb_rc.t,
437 thumb_rc.r - thumb_rc.l,
438 thumb_rc.b - thumb_rc.t);
439#else
440 yuv_blit(yuv, 0, 0, thumb_height,
441 thumb_rc.t, thumb_rc.l,
442 thumb_rc.b - thumb_rc.t,
443 thumb_rc.r - thumb_rc.l);
444#endif /* LCD_WIDTH >= LCD_HEIGHT */
445
446 return true;
447
448no_thumb_exit:
449 vo_draw_black(&thumb_rc);
450 return false;
451}
452
453void vo_setup(const mpeg2_sequence_t * sequence)
454{
455 vo.image_width = sequence->width;
456 vo.image_height = sequence->height;
457 vo.display_width = sequence->display_width;
458 vo.display_height = sequence->display_height;
459
460 DEBUGF("vo_setup - w:%d h:%d\n", vo.display_width, vo.display_height);
461
462 vo.image_chroma_x = vo.image_width / sequence->chroma_width;
463 vo.image_chroma_y = vo.image_height / sequence->chroma_height;
464
465 if (sequence->display_width >= SCREEN_WIDTH)
466 {
467 vo.rc_vid.l = 0;
468 vo.rc_vid.r = SCREEN_WIDTH;
469 }
470 else
471 {
472 vo.rc_vid.l = (SCREEN_WIDTH - sequence->display_width) / 2;
473#ifdef HAVE_LCD_COLOR
474 vo.rc_vid.l &= ~1;
475#endif
476 vo.rc_vid.r = vo.rc_vid.l + sequence->display_width;
477 }
478
479 if (sequence->display_height >= SCREEN_HEIGHT)
480 {
481 vo.rc_vid.t = 0;
482 vo.rc_vid.b = SCREEN_HEIGHT;
483 }
484 else
485 {
486 vo.rc_vid.t = (SCREEN_HEIGHT - sequence->display_height) / 2;
487#ifdef HAVE_LCD_COLOR
488 vo.rc_vid.t &= ~1;
489#endif
490 vo.rc_vid.b = vo.rc_vid.t + sequence->display_height;
491 }
492
493 vo_set_clip_rect(&vo.rc_clip);
494}
495
496void vo_dimensions(struct vo_ext *sz)
497{
498 sz->w = vo.display_width;
499 sz->h = vo.display_height;
500}
501
502bool vo_init(void)
503{
504 vo.flags = 0;
505 vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
506 video_lock_init();
507 return true;
508}
509
510bool vo_show(bool show)
511{
512 bool vis = vo.flags & VO_VISIBLE;
513
514 if (show)
515 vo.flags |= VO_VISIBLE;
516 else
517 vo.flags &= ~VO_VISIBLE;
518
519 return vis;
520}
521
522bool vo_is_visible(void)
523{
524 return vo.flags & VO_VISIBLE;
525}
526
527void vo_cleanup(void)
528{
529 vo.flags = 0;
530}
531
532void vo_set_clip_rect(const struct vo_rect *rc)
533{
534 struct vo_rect rc_out;
535
536 if (rc == NULL)
537 vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
538 else
539 vo.rc_clip = *rc;
540
541 if (!vo_rect_intersect(&rc_out, &vo.rc_vid, &vo.rc_clip))
542 vo.flags &= ~VO_NON_NULL_RECT;
543 else
544 vo.flags |= VO_NON_NULL_RECT;
545
546 vo.output_x = rc_out.l;
547 vo.output_y = rc_out.t;
548 vo.output_width = rc_out.r - rc_out.l;
549 vo.output_height = rc_out.b - rc_out.t;
550}
551
552bool vo_get_clip_rect(struct vo_rect *rc)
553{
554 rc->l = vo.output_x;
555 rc->t = vo.output_y;
556 rc->r = rc->l + vo.output_width;
557 rc->b = rc->t + vo.output_height;
558 return (vo.flags & VO_NON_NULL_RECT) != 0;
559}
560
561void vo_set_post_draw_callback(void (*cb)(void))
562{
563 vo.post_draw_callback = cb;
564}
565
566#if NUM_CORES > 1
567void vo_lock(void)
568{
569 video_lock();
570}
571
572void vo_unlock(void)
573{
574 video_unlock();
575}
576#endif