diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-12-29 19:46:35 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-12-29 19:46:35 +0000 |
commit | a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1 (patch) | |
tree | d393a23d83549f99772bb156e59ffb88725148b6 /apps/plugins/mpegplayer/video_out_rockbox.c | |
parent | 1d0f6b90ff43776e55b4b9f062c9bea3f99aa768 (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.c | 479 |
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 } |