summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer/video_out_rockbox.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-12-29 19:46:35 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-12-29 19:46:35 +0000
commita222f27c4a17ed8f9809cda7861fe5f23d4cc0c1 (patch)
treed393a23d83549f99772bb156e59ffb88725148b6 /apps/plugins/mpegplayer/video_out_rockbox.c
parent1d0f6b90ff43776e55b4b9f062c9bea3f99aa768 (diff)
mpegplayer: Make playback engine fully seekable and frame-accurate and split into logical parts. Be sure to have all current features work. Actual UI for seeking will be added soon. Recommended GOP size is about 15-30 frames depending on target or seeking can be slow with really long GOPs (nature of MPEG video). More refined encoding recommendations for a particular player should be posted soon.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15977 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/mpegplayer/video_out_rockbox.c')
-rw-r--r--apps/plugins/mpegplayer/video_out_rockbox.c479
1 files changed, 347 insertions, 132 deletions
diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c
index 9dd8d6a467..d5e927e9f1 100644
--- a/apps/plugins/mpegplayer/video_out_rockbox.c
+++ b/apps/plugins/mpegplayer/video_out_rockbox.c
@@ -1,189 +1,404 @@
-/*
- * video_out_null.c
- * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
- * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
*
- * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
- * See http://libmpeg2.sourceforge.net/ for updates.
+ * mpegplayer video output routines
*
- * mpeg2dec is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
*
- * mpeg2dec is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
+ ****************************************************************************/
#include "mpeg2dec_config.h"
#include "plugin.h"
-#include "gray.h"
+#include "mpegplayer.h"
+
+struct vo_data
+{
+ int image_width;
+ int image_height;
+ int image_chroma_x;
+ int image_chroma_y;
+ int display_width;
+ int display_height;
+ int output_x;
+ int output_y;
+ int output_width;
+ int output_height;
+ bool visible;
+ bool thumb_mode;
+ void *last;
+};
+
+#ifdef PROC_NEEDS_CACHEALIGN
+/* Cache aligned and padded to avoid clobbering other processors' cacheable
+ * data */
+static uint8_t __vo_data[CACHEALIGN_UP(sizeof(struct vo_data))]
+ CACHEALIGN_ATTR;
+#define vo (*((struct vo_data *)__vo_data))
+#else
+static struct vo_data vo;
+#endif
+
+/* Draw a black rectangle if no video frame is available */
+static void vo_draw_black(void)
+{
+ int foreground = lcd_(get_foreground)();
-extern struct plugin_api* rb;
+ lcd_(set_foreground)(DRAW_BLACK);
-#include "mpeg2.h"
-#include "video_out.h"
+ lcd_(fillrect)(vo.output_x, vo.output_y, vo.output_width,
+ vo.output_height);
+ lcd_(update_rect)(vo.output_x, vo.output_y, vo.output_width,
+ vo.output_height);
-static int image_width;
-static int image_height;
-static int image_chroma_x;
-static int image_chroma_y;
-static int output_x;
-static int output_y;
-static int output_width;
-static int output_height;
+ lcd_(set_foreground)(foreground);
+}
-void vo_draw_frame (uint8_t * const * buf)
+static inline void yuv_blit(uint8_t * const * buf, int src_x, int src_y,
+ int stride, int x, int y, int width, int height)
{
#ifdef HAVE_LCD_COLOR
- rb->lcd_yuv_blit(buf, 0,0,image_width,
- output_x,output_y,output_width,output_height);
+ rb->lcd_yuv_blit(buf, src_x, src_y, stride, x, y , width, height);
#else
- gray_ub_gray_bitmap_part(buf[0],0,0,image_width,
- output_x,output_y,output_width,output_height);
+ gray_ub_gray_bitmap_part(buf[0], src_x, src_y, stride, x, y, width, height);
#endif
}
+void vo_draw_frame(uint8_t * const * buf)
+{
+ if (!vo.visible)
+ {
+ /* Frame is hidden - copout */
+ DEBUGF("vo hidden\n");
+ return;
+ }
+ else if (buf == NULL)
+ {
+ /* No frame exists - draw black */
+ vo_draw_black();
+ DEBUGF("vo no frame\n");
+ return;
+ }
+
+ yuv_blit(buf, 0, 0, vo.image_width,
+ vo.output_x, vo.output_y, vo.output_width,
+ vo.output_height);
+}
+
#if LCD_WIDTH >= LCD_HEIGHT
#define SCREEN_WIDTH LCD_WIDTH
#define SCREEN_HEIGHT LCD_HEIGHT
-#else /* Assume the screen is rotates on portraid LCDs */
+#else /* Assume the screen is rotated on portrait LCDs */
#define SCREEN_WIDTH LCD_HEIGHT
#define SCREEN_HEIGHT LCD_WIDTH
#endif
-uint8_t* tmpbufa = 0;
-uint8_t* tmpbufb = 0;
-uint8_t* tmpbufc = 0;
-uint8_t* tmpbuf[3];
+static inline void vo_rect_clear_inl(struct vo_rect *rc)
+{
+ rc->l = rc->t = rc->r = rc->b = 0;
+}
-void vo_draw_frame_thumb (uint8_t * const * buf)
+static inline bool vo_rect_empty_inl(const struct vo_rect *rc)
{
- int r,c;
+ return rc == NULL || rc->l >= rc->r || rc->t >= rc->b;
+}
-#if LCD_WIDTH >= LCD_HEIGHT
- for (r=0;r<image_width/2;r++)
- for (c=0;c<image_height/2;c++)
- *(tmpbuf[0]+c*image_width/2+r) =
- *(buf[0]+2*c*image_width+2*r);
-
- for (r=0;r<image_width/4;r++)
- for (c=0;c<image_height/4;c++)
+static inline bool vo_rects_intersect_inl(const struct vo_rect *rc1,
+ const struct vo_rect *rc2)
+{
+ return !vo_rect_empty_inl(rc1) &&
+ !vo_rect_empty_inl(rc2) &&
+ rc1->l < rc2->r && rc1->r > rc2->l &&
+ rc1->t < rc2->b && rc1->b > rc2->t;
+}
+
+/* Sets all coordinates of a vo_rect to 0 */
+void vo_rect_clear(struct vo_rect *rc)
+{
+ vo_rect_clear_inl(rc);
+}
+
+/* Returns true if left >= right or top >= bottom */
+bool vo_rect_empty(const struct vo_rect *rc)
+{
+ return vo_rect_empty_inl(rc);
+}
+
+/* Initializes a vo_rect using upper-left corner and extents */
+void vo_rect_set_ext(struct vo_rect *rc, int x, int y,
+ int width, int height)
+{
+ rc->l = x;
+ rc->t = y;
+ rc->r = x + width;
+ rc->b = y + height;
+}
+
+/* Query if two rectangles intersect */
+bool vo_rects_intersect(const struct vo_rect *rc1,
+ const struct vo_rect *rc2)
+{
+ return vo_rects_intersect_inl(rc1, rc2);
+}
+
+/* Intersect two rectangles, placing the result in rc_dst */
+bool vo_rect_intersect(struct vo_rect *rc_dst,
+ const struct vo_rect *rc1,
+ const struct vo_rect *rc2)
+{
+ if (rc_dst != NULL)
{
- *(tmpbuf[1]+c*image_width/4+r) =
- *(buf[1]+c*image_width+2*r);
- *(tmpbuf[2]+c*image_width/4+r) =
- *(buf[2]+c*image_width+2*r);
+ if (vo_rects_intersect_inl(rc1, rc2))
+ {
+ rc_dst->l = MAX(rc1->l, rc2->l);
+ rc_dst->r = MIN(rc1->r, rc2->r);
+ rc_dst->t = MAX(rc1->t, rc2->t);
+ rc_dst->b = MIN(rc1->b, rc2->b);
+ return true;
+ }
+
+ vo_rect_clear_inl(rc_dst);
}
+
+ return false;
+}
+
+/* Shink or stretch each axis - rotate counter-clockwise to retain upright
+ * orientation on rotated displays (they rotate clockwise) */
+void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride,
+ int src_w, int src_h, int dst_w, int dst_h)
+{
+ uint8_t *dst_end = dst + dst_w*dst_h;
+
+#if LCD_WIDTH >= LCD_HEIGHT
+ int src_w2 = src_w*2; /* 2x dimensions (for rounding before division) */
+ int dst_w2 = dst_w*2;
+ int src_h2 = src_h*2;
+ int dst_h2 = dst_h*2;
+ int qw = src_w2 / dst_w2; /* src-dst width ratio quotient */
+ int rw = src_w2 - qw*dst_w2; /* src-dst width ratio remainder */
+ int qh = src_h2 / dst_h2; /* src-dst height ratio quotient */
+ int rh = src_h2 - qh*dst_h2; /* src-dst height ratio remainder */
+ int dw = dst_w; /* Width error accumulator */
+ int dh = dst_h; /* Height error accumulator */
#else
- for (r=0;r<image_width/2;r++)
- for (c=0;c<image_height/2;c++)
- *(tmpbuf[0]+(image_width/2-1-r)*image_height/2+c) =
- *(buf[0]+2*c*image_width+2*r);
-
- for (r=0;r<image_width/4;r++)
- for (c=0;c<image_height/4;c++)
+ int src_w2 = src_w*2;
+ int dst_w2 = dst_h*2;
+ int src_h2 = src_h*2;
+ int dst_h2 = dst_w*2;
+ int qw = src_h2 / dst_w2;
+ int rw = src_h2 - qw*dst_w2;
+ int qh = src_w2 / dst_h2;
+ int rh = src_w2 - qh*dst_h2;
+ int dw = dst_h;
+ int dh = dst_w;
+
+ src += src_w - 1;
+#endif
+
+ while (1)
{
- *(tmpbuf[1]+(image_width/4-1-r)*image_height/4+c) =
- *(buf[1]+c*image_width+2*r);
- *(tmpbuf[2]+(image_width/4-1-r)*image_height/4+c) =
- *(buf[2]+c*image_width+2*r);
- }
+ const uint8_t *s = src;
+#if LCD_WIDTH >= LCD_HEIGHT
+ uint8_t * const dst_line_end = dst + dst_w;
+#else
+ uint8_t * const dst_line_end = dst + dst_h;
#endif
+ while (1)
+ {
+ *dst++ = *s;
-rb->lcd_clear_display();
-rb->lcd_update();
+ if (dst >= dst_line_end)
+ {
+ dw = dst_w;
+ break;
+ }
-#ifdef HAVE_LCD_COLOR
-#ifdef SIMULATOR
#if LCD_WIDTH >= LCD_HEIGHT
- rb->lcd_yuv_blit(tmpbuf,0,0,image_width/2,
- (LCD_WIDTH-1-image_width/2)/2,
- LCD_HEIGHT-50-(image_height/2),
- output_width/2,output_height/2);
-
+ s += qw;
#else
- rb->lcd_yuv_blit(tmpbuf,0,0,image_height/2,
- LCD_HEIGHT-50-(image_height/2),
- (LCD_WIDTH-1-image_width/2)/2,
- output_height/2,output_width/2);
+ s += qw*stride;
#endif
-#else
+ dw += rw;
+
+ if (dw >= dst_w2)
+ {
+ dw -= dst_w2;
#if LCD_WIDTH >= LCD_HEIGHT
- rb->lcd_yuv_blit(tmpbuf,0,0,image_width/2,
- (LCD_WIDTH-1-image_width/2)/2,
- LCD_HEIGHT-50-(image_height/2),
- output_width/2,output_height/2);
+ s++;
#else
- rb->lcd_yuv_blit(tmpbuf,0,0,image_height/2,
- LCD_HEIGHT-50-(image_height/2),
- (LCD_WIDTH-1-image_width/2)/2,
- output_height/2,output_width/2);
-#endif
+ s += stride;
#endif
+ }
+ }
+
+ if (dst >= dst_end)
+ break;
+#if LCD_WIDTH >= LCD_HEIGHT
+ src += qh*stride;
#else
+ src -= qh;
+#endif
+ dh += rh;
+
+ if (dh >= dst_h2)
+ {
+ dh -= dst_h2;
#if LCD_WIDTH >= LCD_HEIGHT
- gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_width/2,
- (LCD_WIDTH-1-image_width/2)/2,
- LCD_HEIGHT-50-(image_height/2),
- output_width/2,output_height/2);
+ src += stride;
#else
- gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_height/2,
- LCD_HEIGHT-50-(image_height/2),
- (LCD_WIDTH-1-image_width/2)/2,
- output_height/2,output_width/2);
+ src--;
+#endif
+ }
+ }
+}
+
+bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc)
+{
+ void *mem;
+ size_t bufsize;
+ uint8_t *yuv[3];
+ struct vo_rect thumb_rc;
+ int thumb_width, thumb_height;
+ int thumb_uv_width, thumb_uv_height;
+
+ if (buf == NULL)
+ return false;
+
+ /* Obtain rectangle as clipped to the screen */
+ vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT);
+ if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc))
+ return true;
+
+ DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t,
+ thumb_rc.r, thumb_rc.b);
+
+ thumb_width = rc->r - rc->l;
+ thumb_height = rc->b - rc->t;
+ thumb_uv_width = thumb_width / 2;
+ thumb_uv_height = thumb_height / 2;
+
+ DEBUGF("thumb: w: %d h: %d uvw: %d uvh: %d\n", thumb_width,
+ thumb_height, thumb_uv_width, thumb_uv_height);
+
+ /* Use remaining mpeg2 buffer as temp space */
+ mem = mpeg2_get_buf(&bufsize);
+
+ if (bufsize < (size_t)(thumb_width*thumb_height)
+#ifdef HAVE_LCD_COLOR
+ + 2u*(thumb_uv_width * thumb_uv_height)
#endif
+ )
+ {
+ DEBUGF("thumb: insufficient buffer\n");
+ return false;
+ }
+
+ yuv[0] = mem;
+ stretch_image_plane(buf[0], yuv[0], vo.image_width,
+ vo.display_width, vo.display_height,
+ thumb_width, thumb_height);
+
+#ifdef HAVE_LCD_COLOR
+ yuv[1] = yuv[0] + thumb_width*thumb_height;
+ yuv[2] = yuv[1] + thumb_uv_width*thumb_uv_height;
+
+ stretch_image_plane(buf[1], yuv[1], vo.image_width / 2,
+ vo.display_width / 2, vo.display_height / 2,
+ thumb_uv_width, thumb_uv_height);
+
+ stretch_image_plane(buf[2], yuv[2], vo.image_width / 2,
+ vo.display_width / 2, vo.display_height / 2,
+ thumb_uv_width, thumb_uv_height);
#endif
+
+#if LCD_WIDTH >= LCD_HEIGHT
+ yuv_blit(yuv, 0, 0, thumb_width,
+ thumb_rc.l, thumb_rc.t,
+ thumb_rc.r - thumb_rc.l,
+ thumb_rc.b - thumb_rc.t);
+#else
+ yuv_blit(yuv, 0, 0, thumb_height,
+ thumb_rc.t, thumb_rc.l,
+ thumb_rc.b - thumb_rc.t,
+ thumb_rc.r - thumb_rc.l);
+#endif /* LCD_WIDTH >= LCD_HEIGHT */
+
+ return true;
}
void vo_setup(const mpeg2_sequence_t * sequence)
{
- image_width=sequence->width;
- image_height=sequence->height;
-
- tmpbufa = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width*
- image_height/4, -2);
- tmpbufb = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width*
- image_height/16, -2);
- tmpbufc = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width*
- image_height/16, -2);
- tmpbuf[0] = tmpbufa;
- tmpbuf[1] = tmpbufb;
- tmpbuf[2] = tmpbufc;
-
- image_chroma_x=image_width/sequence->chroma_width;
- image_chroma_y=image_height/sequence->chroma_height;
-
- if (sequence->display_width >= SCREEN_WIDTH) {
- output_width = SCREEN_WIDTH;
- output_x = 0;
- } else {
- output_width = sequence->display_width;
- output_x = (SCREEN_WIDTH-sequence->display_width)/2;
+ vo.image_width = sequence->width;
+ vo.image_height = sequence->height;
+ vo.display_width = sequence->display_width;
+ vo.display_height = sequence->display_height;
+
+ DEBUGF("vo_setup - w:%d h:%d\n", vo.display_width, vo.display_height);
+
+ vo.image_chroma_x = vo.image_width / sequence->chroma_width;
+ vo.image_chroma_y = vo.image_height / sequence->chroma_height;
+
+ if (sequence->display_width >= SCREEN_WIDTH)
+ {
+ vo.output_width = SCREEN_WIDTH;
+ vo.output_x = 0;
+ }
+ else
+ {
+ vo.output_width = sequence->display_width;
+ vo.output_x = (SCREEN_WIDTH - sequence->display_width) / 2;
}
- if (sequence->display_height >= SCREEN_HEIGHT) {
- output_height = SCREEN_HEIGHT;
- output_y = 0;
- } else {
- output_height = sequence->display_height;
- output_y = (SCREEN_HEIGHT-sequence->display_height)/2;
+ if (sequence->display_height >= SCREEN_HEIGHT)
+ {
+ vo.output_height = SCREEN_HEIGHT;
+ vo.output_y = 0;
+ }
+ else
+ {
+ vo.output_height = sequence->display_height;
+ vo.output_y = (SCREEN_HEIGHT - sequence->display_height) / 2;
}
}
+void vo_dimensions(struct vo_ext *sz)
+{
+ sz->w = vo.display_width;
+ sz->h = vo.display_height;
+}
+
+bool vo_init(void)
+{
+ vo.visible = false;
+ return true;
+}
+
+bool vo_show(bool show)
+{
+ bool vis = vo.visible;
+ vo.visible = show;
+ return vis;
+}
+
+bool vo_is_visible(void)
+{
+ return vo.visible;
+}
+
void vo_cleanup(void)
{
- if (tmpbufc)
- mpeg2_free(tmpbufc);
- if (tmpbufb)
- mpeg2_free(tmpbufb);
- if (tmpbufa)
- mpeg2_free(tmpbufa);
+ vo.visible = false;
+#ifndef HAVE_LCD_COLOR
+ gray_release();
+#endif
}