summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2022-10-13 11:04:12 -0400
committerSolomon Peachy <pizza@shaftnet.org>2022-10-13 11:08:11 -0400
commit9d3d925295112a0080bc1d70fad170db9e1af2a9 (patch)
treef6fb6244519a9430aa97b303e417fba53799b2f0
parent418169aff8faf2cf90124cd95dba0af821cea73d (diff)
downloadrockbox-9d3d925295.tar.gz
rockbox-9d3d925295.zip
Revert "RFC: Get rid of mpegplayer plugin"
This reverts commit d25d24812e8120c0eb133a412287ac030eb185c9. Change-Id: I1563223e343fb1e2eda72a45823b38350025ff93
-rw-r--r--apps/lang/english.lang291
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SUBDIRS4
-rw-r--r--apps/plugins/bitmaps/mono/SOURCES5
-rw-r--r--apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmpbin0 -> 158 bytes
-rw-r--r--apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmpbin0 -> 254 bytes
-rw-r--r--apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmpbin0 -> 126 bytes
-rw-r--r--apps/plugins/mpegplayer/SOURCES35
-rw-r--r--apps/plugins/mpegplayer/alloc.c233
-rw-r--r--apps/plugins/mpegplayer/audio_thread.c721
-rw-r--r--apps/plugins/mpegplayer/disk_buf.c989
-rw-r--r--apps/plugins/mpegplayer/disk_buf.h152
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/AUTHORS33
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/README204
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/README.rockbox44
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/attributes.h42
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/decode.c527
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/header.c1287
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/idct.c274
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/idct_arm.S443
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/idct_armv6.S297
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/idct_coldfire.S575
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/motion_comp.c66
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/motion_comp.h86
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/motion_comp_arm_c.c39
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/motion_comp_arm_s.S342
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/motion_comp_c.c40
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/motion_comp_coldfire_c.c38
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/motion_comp_coldfire_s.S436
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/mpeg2.h223
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/mpeg2_internal.h274
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/mpeg2dec_config.h15
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/slice.c2898
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/vlc.h433
-rw-r--r--apps/plugins/mpegplayer/mpeg_alloc.h32
-rw-r--r--apps/plugins/mpegplayer/mpeg_misc.c227
-rw-r--r--apps/plugins/mpegplayer/mpeg_misc.h258
-rw-r--r--apps/plugins/mpegplayer/mpeg_parser.c1203
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.c1454
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.h110
-rw-r--r--apps/plugins/mpegplayer/mpeg_stream.h122
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c2638
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.h95
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.make32
-rw-r--r--apps/plugins/mpegplayer/parser.h103
-rw-r--r--apps/plugins/mpegplayer/pcm_output.c396
-rw-r--r--apps/plugins/mpegplayer/pcm_output.h48
-rw-r--r--apps/plugins/mpegplayer/stream_mgr.c1163
-rw-r--r--apps/plugins/mpegplayer/stream_mgr.h168
-rw-r--r--apps/plugins/mpegplayer/stream_thread.h201
-rw-r--r--apps/plugins/mpegplayer/video_out.h102
-rw-r--r--apps/plugins/mpegplayer/video_out_rockbox.c576
-rw-r--r--apps/plugins/mpegplayer/video_thread.c1059
-rw-r--r--apps/plugins/viewers.config4
-rw-r--r--docs/MAINTAINERS1
-rw-r--r--lib/rbcodec/codecs/libmad/libmad.make25
-rw-r--r--lib/rbcodec/codecs/libmad/mad_iram.h7
-rw-r--r--lib/rbcodec/codecs/mpa.c2
-rw-r--r--manual/plugins/main.tex2
-rw-r--r--manual/plugins/mpegplayer.tex119
60 files changed, 21189 insertions, 5 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 990bc9fbcd..e8d646b258 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -14264,8 +14264,127 @@
</voice>
</phrase>
<phrase>
+ id: LANG_MENU_AUDIO_OPTIONS
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Audio Options"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Audio Options"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Audio Options"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MENU_RESUME_OPTIONS
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Resume Options"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Resume Options"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Resume Options"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MENU_PLAY_MODE
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Play Mode"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Play Mode"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Play Mode"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SINGLE
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Single"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Single"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Single"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_USE_SOUND_SETTING
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Use sound setting"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Use sound setting"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Use sound setting"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RESTART_PLAYBACK
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Play from beginning"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Play from beginning"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Play from beginning"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SET_RESUME_TIME
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Set resume time (min)"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Set resume time (min)"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Set resume time"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
id: LANG_DISPLAY_FPS
- desc: in the pictureflow settings menus
+ desc: in the mpegplayer and pictureflow settings menus
user: core
<source>
*: "Display FPS"
@@ -14278,6 +14397,176 @@
</voice>
</phrase>
<phrase>
+ id: LANG_LIMIT_FPS
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Limit FPS"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Limit FPS"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Limit FPS"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SKIP_FRAMES
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Skip frames"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Skip frames"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Skip frames"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_BACKLIGHT_BRIGHTNESS
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Backlight brightness"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Backlight brightness"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Backlight brightness"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_USE_COMMON_SETTING
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Use common setting"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Use common setting"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Use common setting"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TONE_CONTROLS
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Tone controls"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Tone controls"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Tone controls"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FORCE_START_MENU
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Start menu"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Start menu"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Start menu"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CONDITIONAL_START_MENU
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Start menu if not completed"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Start menu if not completed"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Start menu if not completed"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_AUTO_RESUME
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Resume automatically"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Resume automatically"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Resume automatically"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_ALL_RESUMES
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Clear all resumes"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Clear all resumes"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Clear all resumes"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_UNAVAILABLE
+ desc: in mpegplayer settings
+ user: core
+ <source>
+ *: "Unavailable"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Unavailable"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Unavailable"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
id: LANG_TOGGLE_ITEM
desc: in main_menu_config
user: core
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 13db81f004..f2ab4843c2 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -68,6 +68,7 @@ mikmod,viewers
minesweeper,games
mosaique,demos
mp3_encoder,apps
+mpegplayer,viewers
multiboot_select,apps
nim,games
open_plugins,viewers
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 23cd029ccc..4cb57edb1b 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -78,6 +78,10 @@ mikmod
pdbox
#endif
+#if !defined(RB_PROFILE) && MEMORYSIZE > 2 /* mpegplayer allocates at least 2MB of RAM */
+mpegplayer
+#endif
+
/* Lua needs at least 160 KB to work in */
#if PLUGIN_BUFFER_SIZE >= 0x80000
lua
diff --git a/apps/plugins/bitmaps/mono/SOURCES b/apps/plugins/bitmaps/mono/SOURCES
index df8521dd0a..eb00bd9e8a 100644
--- a/apps/plugins/bitmaps/mono/SOURCES
+++ b/apps/plugins/bitmaps/mono/SOURCES
@@ -49,6 +49,11 @@ invadrox_fire.6x6x1.bmp
#endif
#endif
+/* MPEGplayer */
+mpegplayer_status_icons_8x8x1.bmp
+mpegplayer_status_icons_12x12x1.bmp
+mpegplayer_status_icons_16x16x1.bmp
+
#if LCD_WIDTH == 160 && LCD_HEIGHT == 128 && LCD_DEPTH < 16
superdom_boarditems.160x128x1.bmp
#endif
diff --git a/apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmp b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmp
new file mode 100644
index 0000000000..61f6a52fbc
--- /dev/null
+++ b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_12x12x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmp b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmp
new file mode 100644
index 0000000000..944bd5132e
--- /dev/null
+++ b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_16x16x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp
new file mode 100644
index 0000000000..d4b71fe1e0
--- /dev/null
+++ b/apps/plugins/bitmaps/mono/mpegplayer_status_icons_8x8x1.bmp
Binary files differ
diff --git a/apps/plugins/mpegplayer/SOURCES b/apps/plugins/mpegplayer/SOURCES
new file mode 100644
index 0000000000..3fc079dfbd
--- /dev/null
+++ b/apps/plugins/mpegplayer/SOURCES
@@ -0,0 +1,35 @@
+libmpeg2/decode.c
+libmpeg2/header.c
+libmpeg2/idct.c
+libmpeg2/motion_comp.c
+libmpeg2/slice.c
+
+#ifdef CPU_COLDFIRE
+libmpeg2/idct_coldfire.S
+libmpeg2/motion_comp_coldfire_c.c
+libmpeg2/motion_comp_coldfire_s.S
+#elif defined CPU_ARM
+#if ARM_ARCH >= 6
+libmpeg2/idct_armv6.S
+#else
+libmpeg2/idct_arm.S
+#endif
+libmpeg2/motion_comp_arm_c.c
+libmpeg2/motion_comp_arm_s.S
+#else /* other CPU or SIM */
+libmpeg2/motion_comp_c.c
+#endif /* CPU_* */
+
+
+
+alloc.c
+video_out_rockbox.c
+video_thread.c
+pcm_output.c
+audio_thread.c
+disk_buf.c
+mpeg_settings.c
+stream_mgr.c
+mpegplayer.c
+mpeg_parser.c
+mpeg_misc.c
diff --git a/apps/plugins/mpegplayer/alloc.c b/apps/plugins/mpegplayer/alloc.c
new file mode 100644
index 0000000000..cbf930a7eb
--- /dev/null
+++ b/apps/plugins/mpegplayer/alloc.c
@@ -0,0 +1,233 @@
+/*
+ * alloc.c
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.13
+ */
+
+#include "plugin.h"
+#include "mpegplayer.h"
+#include <system.h>
+
+/* Main allocator */
+static off_t mem_ptr;
+static size_t bufsize;
+static unsigned char* mallocbuf;
+
+/* libmpeg2 allocator */
+static off_t mpeg2_mem_ptr SHAREDBSS_ATTR;
+static size_t mpeg2_bufsize SHAREDBSS_ATTR;
+static unsigned char *mpeg2_mallocbuf SHAREDBSS_ATTR;
+static unsigned char *mpeg2_bufallocbuf SHAREDBSS_ATTR;
+
+#if defined(DEBUG) || defined(SIMULATOR)
+const char * mpeg_get_reason_str(int reason)
+{
+ const char *str;
+
+ switch (reason)
+ {
+ case MPEG2_ALLOC_MPEG2DEC:
+ str = "MPEG2_ALLOC_MPEG2DEC";
+ break;
+ case MPEG2_ALLOC_CHUNK:
+ str = "MPEG2_ALLOC_CHUNK";
+ break;
+ case MPEG2_ALLOC_YUV:
+ str = "MPEG2_ALLOC_YUV";
+ break;
+ case MPEG2_ALLOC_CONVERT_ID:
+ str = "MPEG2_ALLOC_CONVERT_ID";
+ break;
+ case MPEG2_ALLOC_CONVERTED:
+ str = "MPEG2_ALLOC_CONVERTED";
+ break;
+ case MPEG_ALLOC_MPEG2_BUFFER:
+ str = "MPEG_ALLOC_MPEG2_BUFFER";
+ break;
+ case MPEG_ALLOC_AUDIOBUF:
+ str = "MPEG_ALLOC_AUDIOBUF";
+ break;
+ case MPEG_ALLOC_PCMOUT:
+ str = "MPEG_ALLOC_PCMOUT";
+ break;
+ case MPEG_ALLOC_DISKBUF:
+ str = "MPEG_ALLOC_DISKBUF";
+ break;
+ case MPEG_ALLOC_CODEC_MALLOC:
+ str = "MPEG_ALLOC_CODEC_MALLOC";
+ break;
+ case MPEG_ALLOC_CODEC_CALLOC:
+ str = "MPEG_ALLOC_CODEC_CALLOC";
+ break;
+ default:
+ str = "Unknown";
+ }
+
+ return str;
+}
+#endif
+
+static void * mpeg_malloc_internal (unsigned char *mallocbuf,
+ off_t *mem_ptr,
+ size_t bufsize,
+ unsigned size,
+ int reason)
+{
+ void *x;
+
+ DEBUGF("mpeg_alloc_internal: bs:%lu s:%u reason:%s (%d)\n",
+ (unsigned long)bufsize, size, mpeg_get_reason_str(reason), reason);
+
+ if ((size_t) (*mem_ptr + size) > bufsize)
+ {
+ DEBUGF("OUT OF MEMORY\n");
+ return NULL;
+ }
+
+ x = &mallocbuf[*mem_ptr];
+ *mem_ptr += (size + 3) & ~3; /* Keep memory 32-bit aligned */
+
+ return x;
+ (void)reason;
+}
+
+void *mpeg_malloc(size_t size, mpeg2_alloc_t reason)
+{
+ return mpeg_malloc_internal(mallocbuf, &mem_ptr, bufsize, size,
+ reason);
+}
+
+void *mpeg_malloc_all(size_t *size_out, mpeg2_alloc_t reason)
+{
+ /* Can steal all but MIN_MEMMARGIN */
+ if (bufsize - mem_ptr < MIN_MEMMARGIN)
+ return NULL;
+
+ *size_out = bufsize - mem_ptr - MIN_MEMMARGIN;
+ return mpeg_malloc(*size_out, reason);
+}
+
+bool mpeg_alloc_init(unsigned char *buf, size_t mallocsize)
+{
+ mem_ptr = 0;
+ /* Cache-align buffer or 4-byte align */
+ mallocbuf = buf;
+ bufsize = mallocsize;
+ ALIGN_BUFFER(mallocbuf, bufsize, CACHEALIGN_UP(4));
+
+ /* Separate allocator for video */
+ mpeg2_mem_ptr = 0;
+ mpeg2_mallocbuf = mallocbuf;
+ mpeg2_bufallocbuf = mallocbuf;
+ mpeg2_bufsize = CACHEALIGN_UP(LIBMPEG2_ALLOC_SIZE);
+
+ if (mpeg_malloc_internal(mallocbuf, &mem_ptr,
+ bufsize, mpeg2_bufsize,
+ MPEG_ALLOC_MPEG2_BUFFER) == NULL)
+ {
+ return false;
+ }
+
+ IF_COP(rb->commit_discard_dcache());
+ return true;
+}
+
+/* allocate non-dedicated buffer space which mpeg2_mem_reset will free */
+void * mpeg2_malloc(unsigned size, mpeg2_alloc_t reason)
+{
+ void *ptr = mpeg_malloc_internal(mpeg2_mallocbuf, &mpeg2_mem_ptr,
+ mpeg2_bufsize, size, reason);
+ /* libmpeg2 expects zero-initialized allocations */
+ if (ptr)
+ rb->memset(ptr, 0, size);
+
+ return ptr;
+}
+
+/* allocate dedicated buffer - memory behind buffer pointer becomes dedicated
+ so order is important */
+void * mpeg2_bufalloc(unsigned size, mpeg2_alloc_t reason)
+{
+ void *buf = mpeg2_malloc(size, reason);
+
+ if (buf == NULL)
+ return NULL;
+
+ mpeg2_bufallocbuf = &mpeg2_mallocbuf[mpeg2_mem_ptr];
+ return buf;
+}
+
+/* return unused buffer portion and size */
+void * mpeg2_get_buf(size_t *size)
+{
+ if ((size_t)mpeg2_mem_ptr + 32 >= mpeg2_bufsize)
+ return NULL;
+
+ *size = mpeg2_bufsize - mpeg2_mem_ptr;
+ return &mpeg2_mallocbuf[mpeg2_mem_ptr];
+}
+
+/* de-allocate all non-dedicated buffer space */
+void mpeg2_mem_reset(void)
+{
+ DEBUGF("mpeg2_mem_reset\n");
+ mpeg2_mem_ptr = mpeg2_bufallocbuf - mpeg2_mallocbuf;
+}
+
+/* The following are expected by libmad */
+void * codec_malloc(size_t size)
+{
+ void* ptr;
+
+ ptr = mpeg_malloc_internal(mallocbuf, &mem_ptr,
+ bufsize, size, MPEG_ALLOC_CODEC_MALLOC);
+
+ if (ptr)
+ rb->memset(ptr,0,size);
+
+ return ptr;
+}
+
+void * codec_calloc(size_t nmemb, size_t size)
+{
+ void* ptr;
+
+ ptr = mpeg_malloc_internal(mallocbuf, &mem_ptr,
+ bufsize, nmemb*size,
+ MPEG_ALLOC_CODEC_CALLOC);
+
+ if (ptr)
+ rb->memset(ptr,0,size);
+
+ return ptr;
+}
+
+void codec_free(void* ptr)
+{
+ DEBUGF("codec_free - %p\n", ptr);
+#if 0
+ mem_ptr = (void *)ptr - (void *)mallocbuf;
+#endif
+ (void)ptr;
+}
diff --git a/apps/plugins/mpegplayer/audio_thread.c b/apps/plugins/mpegplayer/audio_thread.c
new file mode 100644
index 0000000000..764ad111f2
--- /dev/null
+++ b/apps/plugins/mpegplayer/audio_thread.c
@@ -0,0 +1,721 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * mpegplayer audio thread implementation
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "mpegplayer.h"
+#include "codecs/libmad/bit.h"
+#include "codecs/libmad/mad.h"
+
+/** Audio stream and thread **/
+struct pts_queue_slot;
+struct audio_thread_data
+{
+ struct queue_event ev; /* Our event queue to receive commands */
+ int state; /* Thread state */
+ int status; /* Media status (STREAM_PLAYING, etc.) */
+ int mad_errors; /* A count of the errors in each frame */
+ unsigned samplerate; /* Current stream sample rate */
+ int nchannels; /* Number of audio channels */
+ struct dsp_config *dsp; /* The DSP we're using */
+ struct dsp_buffer src; /* Current audio data for DSP processing */
+};
+
+/* The audio thread is stolen from the core codec thread */
+static struct event_queue audio_str_queue SHAREDBSS_ATTR;
+static struct queue_sender_list audio_str_queue_send SHAREDBSS_ATTR;
+struct stream audio_str IBSS_ATTR;
+
+/* libmad related definitions */
+static struct mad_stream stream IBSS_ATTR;
+static struct mad_frame frame IBSS_ATTR;
+static struct mad_synth synth IBSS_ATTR;
+
+/*sbsample buffer for mad_frame*/
+mad_fixed_t sbsample[2][36][32];
+
+/* 2567 bytes */
+static unsigned char mad_main_data[MAD_BUFFER_MDLEN];
+
+/* There isn't enough room for this in IRAM on PortalPlayer, but there
+ is for Coldfire. */
+
+/* 4608 bytes */
+#if defined(CPU_COLDFIRE) || defined(CPU_S5L870X)
+static mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR;
+#else
+static mad_fixed_t mad_frame_overlap[2][32][18];
+#endif
+
+/** A queue for saving needed information about MPEG audio packets **/
+#define AUDIODESC_QUEUE_LEN (1 << 5) /* 32 should be way more than sufficient -
+ if not, the case is handled */
+#define AUDIODESC_QUEUE_MASK (AUDIODESC_QUEUE_LEN-1)
+struct audio_frame_desc
+{
+ uint32_t time; /* Time stamp for packet in audio ticks */
+ ssize_t size; /* Number of unprocessed bytes left in packet */
+};
+
+ /* This starts out wr == rd but will never be emptied to zero during
+ streaming again in order to support initializing the first packet's
+ timestamp without a special case */
+struct
+{
+ /* Compressed audio data */
+ uint8_t *start; /* Start of encoded audio buffer */
+ uint8_t *ptr; /* Pointer to next encoded audio data */
+ ssize_t used; /* Number of bytes in MPEG audio buffer */
+ /* Compressed audio data descriptors */
+ unsigned read, write;
+ struct audio_frame_desc *curr; /* Current slot */
+ struct audio_frame_desc descs[AUDIODESC_QUEUE_LEN];
+} audio_queue;
+
+static inline int audiodesc_queue_count(void)
+{
+ return audio_queue.write - audio_queue.read;
+}
+
+static inline bool audiodesc_queue_full(void)
+{
+ return audio_queue.used >= MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD ||
+ audiodesc_queue_count() >= AUDIODESC_QUEUE_LEN;
+}
+
+/* Increments the queue tail postion - should be used to preincrement */
+static inline void audiodesc_queue_add_tail(void)
+{
+ if (audiodesc_queue_full())
+ {
+ DEBUGF("audiodesc_queue_add_tail: audiodesc queue full!\n");
+ return;
+ }
+
+ audio_queue.write++;
+}
+
+/* Increments the queue head position - leaves one slot as current */
+static inline bool audiodesc_queue_remove_head(void)
+{
+ if (audio_queue.write == audio_queue.read)
+ return false;
+
+ audio_queue.read++;
+ return true;
+}
+
+/* Returns the "tail" at the index just behind the write index */
+static inline struct audio_frame_desc * audiodesc_queue_tail(void)
+{
+ return &audio_queue.descs[(audio_queue.write - 1) & AUDIODESC_QUEUE_MASK];
+}
+
+/* Returns a pointer to the current head */
+static inline struct audio_frame_desc * audiodesc_queue_head(void)
+{
+ return &audio_queue.descs[audio_queue.read & AUDIODESC_QUEUE_MASK];
+}
+
+/* Resets the pts queue - call when starting and seeking */
+static void audio_queue_reset(void)
+{
+ audio_queue.ptr = audio_queue.start;
+ audio_queue.used = 0;
+ audio_queue.read = 0;
+ audio_queue.write = 0;
+ rb->memset(audio_queue.descs, 0, sizeof (audio_queue.descs));
+ audio_queue.curr = audiodesc_queue_head();
+}
+
+static void audio_queue_advance_pos(ssize_t len)
+{
+ audio_queue.ptr += len;
+ audio_queue.used -= len;
+ audio_queue.curr->size -= len;
+}
+
+static int audio_buffer(struct stream *str, enum stream_parse_mode type)
+{
+ int ret = STREAM_OK;
+
+ /* Carry any overshoot to the next size since we're technically
+ -size bytes into it already. If size is negative an audio
+ frame was split across packets. Old has to be saved before
+ moving the head. */
+ if (audio_queue.curr->size <= 0 && audiodesc_queue_remove_head())
+ {
+ struct audio_frame_desc *old = audio_queue.curr;
+ audio_queue.curr = audiodesc_queue_head();
+ audio_queue.curr->size += old->size;
+ old->size = 0;
+ }
+
+ /* Add packets to compressed audio buffer until it's full or the
+ * timestamp queue is full - whichever happens first */
+ while (!audiodesc_queue_full())
+ {
+ ret = parser_get_next_data(str, type);
+ struct audio_frame_desc *curr;
+ ssize_t len;
+
+ if (ret != STREAM_OK)
+ break;
+
+ /* Get data from next audio packet */
+ len = str->curr_packet_end - str->curr_packet;
+
+ if (str->pkt_flags & PKT_HAS_TS)
+ {
+ audiodesc_queue_add_tail();
+ curr = audiodesc_queue_tail();
+ curr->time = TS_TO_TICKS(str->pts);
+ /* pts->size should have been zeroed when slot was
+ freed */
+ }
+ else
+ {
+ /* Add to the one just behind the tail - this may be
+ * the head or the previouly added tail - whether or
+ * not we'll ever reach this is quite in question
+ * since audio always seems to have every packet
+ * timestamped */
+ curr = audiodesc_queue_tail();
+ }
+
+ curr->size += len;
+
+ /* Slide any remainder over to beginning */
+ if (audio_queue.ptr > audio_queue.start && audio_queue.used > 0)
+ {
+ rb->memmove(audio_queue.start, audio_queue.ptr,
+ audio_queue.used);
+ }
+
+ /* Splice this packet onto any remainder */
+ rb->memcpy(audio_queue.start + audio_queue.used,
+ str->curr_packet, len);
+
+ audio_queue.used += len;
+ audio_queue.ptr = audio_queue.start;
+
+ rb->yield();
+ }
+
+ return ret;
+}
+
+/* Initialise libmad */
+static void init_mad(void)
+{
+ /* init the sbsample buffer */
+ frame.sbsample_prev = &sbsample;
+ frame.sbsample = &sbsample;
+
+ /* We do this so libmad doesn't try to call codec_calloc(). This needs to
+ * be called before mad_stream_init(), mad_frame_inti() and
+ * mad_synth_init(). */
+ frame.overlap = &mad_frame_overlap;
+ stream.main_data = &mad_main_data;
+
+ /* Call mad initialization. Those will zero the arrays frame.overlap,
+ * frame.sbsample and frame.sbsample_prev. Therefore there is no need to
+ * zero them here. */
+ mad_stream_init(&stream);
+ mad_frame_init(&frame);
+ mad_synth_init(&synth);
+}
+
+/* Sync audio stream to a particular frame - see main decoder loop for
+ * detailed remarks */
+static int audio_sync(struct audio_thread_data *td,
+ struct str_sync_data *sd)
+{
+ int retval = STREAM_MATCH;
+ uint32_t sdtime = TS_TO_TICKS(clip_time(&audio_str, sd->time));
+ uint32_t time;
+ uint32_t duration = 0;
+ struct stream *str;
+ struct stream tmp_str;
+ struct mad_header header;
+ struct mad_stream stream;
+
+ if (td->ev.id == STREAM_SYNC)
+ {
+ /* Actually syncing for playback - use real stream */
+ time = 0;
+ str = &audio_str;
+ }
+ else
+ {
+ /* Probing - use temp stream */
+ time = INVALID_TIMESTAMP;
+ str = &tmp_str;
+ str->id = audio_str.id;
+ }
+
+ str->hdr.pos = sd->sk.pos;
+ str->hdr.limit = sd->sk.pos + sd->sk.len;
+
+ mad_stream_init(&stream);
+ mad_header_init(&header);
+
+ while (1)
+ {
+ if (audio_buffer(str, STREAM_PM_RANDOM_ACCESS) == STREAM_DATA_END)
+ {
+ DEBUGF("audio_sync:STR_DATA_END\n aqu:%ld swl:%ld swr:%ld\n",
+ (long)audio_queue.used, str->hdr.win_left, str->hdr.win_right);
+ if (audio_queue.used <= MAD_BUFFER_GUARD)
+ goto sync_data_end;
+ }
+
+ stream.error = 0;
+ mad_stream_buffer(&stream, audio_queue.ptr, audio_queue.used);
+
+ if (stream.sync && mad_stream_sync(&stream) < 0)
+ {
+ DEBUGF(" audio: mad_stream_sync failed\n");
+ audio_queue_advance_pos(MAX(audio_queue.curr->size - 1, 1));
+ continue;
+ }
+
+ stream.sync = 0;
+
+ if (mad_header_decode(&header, &stream) < 0)
+ {
+ DEBUGF(" audio: mad_header_decode failed:%s\n",
+ mad_stream_errorstr(&stream));
+ audio_queue_advance_pos(1);
+ continue;
+ }
+
+ duration = 32*MAD_NSBSAMPLES(&header);
+ time = audio_queue.curr->time;
+
+ DEBUGF(" audio: ft:%u t:%u fe:%u nsamp:%u sampr:%u\n",
+ (unsigned)TICKS_TO_TS(time), (unsigned)sd->time,
+ (unsigned)TICKS_TO_TS(time + duration),
+ (unsigned)duration, header.samplerate);
+
+ audio_queue_advance_pos(stream.this_frame - audio_queue.ptr);
+
+ if (time <= sdtime && sdtime < time + duration)
+ {
+ DEBUGF(" audio: ft<=t<fe\n");
+ retval = STREAM_PERFECT_MATCH;
+ break;
+ }
+ else if (time > sdtime)
+ {
+ DEBUGF(" audio: ft>t\n");
+ break;
+ }
+
+ audio_queue_advance_pos(stream.next_frame - audio_queue.ptr);
+ audio_queue.curr->time += duration;
+
+ rb->yield();
+ }
+
+sync_data_end:
+ if (td->ev.id == STREAM_FIND_END_TIME)
+ {
+ if (time != INVALID_TIMESTAMP)
+ {
+ time = TICKS_TO_TS(time);
+ duration = TICKS_TO_TS(duration);
+ sd->time = time + duration;
+ retval = STREAM_PERFECT_MATCH;
+ }
+ else
+ {
+ retval = STREAM_NOT_FOUND;
+ }
+ }
+
+ DEBUGF(" audio header: 0x%02X%02X%02X%02X\n",
+ (unsigned)audio_queue.ptr[0], (unsigned)audio_queue.ptr[1],
+ (unsigned)audio_queue.ptr[2], (unsigned)audio_queue.ptr[3]);
+
+ return retval;
+ (void)td;
+}
+
+static void audio_thread_msg(struct audio_thread_data *td)
+{
+ while (1)
+ {
+ intptr_t reply = 0;
+
+ switch (td->ev.id)
+ {
+ case STREAM_PLAY:
+ td->status = STREAM_PLAYING;
+
+ switch (td->state)
+ {
+ case TSTATE_INIT:
+ td->state = TSTATE_DECODE;
+ case TSTATE_DECODE:
+ case TSTATE_RENDER_WAIT:
+ break;
+
+ case TSTATE_EOS:
+ /* At end of stream - no playback possible so fire the
+ * completion event */
+ stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
+ break;
+ }
+
+ break;
+
+ case STREAM_PAUSE:
+ td->status = STREAM_PAUSED;
+ reply = td->state != TSTATE_EOS;
+ break;
+
+ case STREAM_STOP:
+ if (td->state == TSTATE_DATA)
+ stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY);
+
+ td->status = STREAM_STOPPED;
+ td->state = TSTATE_EOS;
+
+ reply = true;
+ break;
+
+ case STREAM_RESET:
+ if (td->state == TSTATE_DATA)
+ stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY);
+
+ td->status = STREAM_STOPPED;
+ td->state = TSTATE_INIT;
+ td->samplerate = 0;
+ td->nchannels = 0;
+
+ init_mad();
+ td->mad_errors = 0;
+
+ audio_queue_reset();
+
+ reply = true;
+ break;
+
+ case STREAM_NEEDS_SYNC:
+ reply = true; /* Audio always needs to */
+ break;
+
+ case STREAM_SYNC:
+ case STREAM_FIND_END_TIME:
+ if (td->state != TSTATE_INIT)
+ break;
+
+ reply = audio_sync(td, (struct str_sync_data *)td->ev.data);
+ break;
+
+ case DISK_BUF_DATA_NOTIFY:
+ /* Our bun is done */
+ if (td->state != TSTATE_DATA)
+ break;
+
+ td->state = TSTATE_DECODE;
+ str_data_notify_received(&audio_str);
+ break;
+
+ case STREAM_QUIT:
+ /* Time to go - make thread exit */
+ td->state = TSTATE_EOS;
+ return;
+ }
+
+ str_reply_msg(&audio_str, reply);
+
+ if (td->status == STREAM_PLAYING)
+ {
+ switch (td->state)
+ {
+ case TSTATE_DECODE:
+ case TSTATE_RENDER_WAIT:
+ /* These return when in playing state */
+ return;
+ }
+ }
+
+ str_get_msg(&audio_str, &td->ev);
+ }
+}
+
+static void audio_thread(void)
+{
+ struct audio_thread_data td;
+#ifdef HAVE_PRIORITY_SCHEDULING
+ /* Up the priority since the core DSP over-yields internally */
+ int old_priority = rb->thread_set_priority(rb->thread_self(),
+ PRIORITY_PLAYBACK-4);
+#endif
+
+ rb->memset(&td, 0, sizeof (td));
+ td.status = STREAM_STOPPED;
+ td.state = TSTATE_EOS;
+
+ /* We need this here to init the EMAC for Coldfire targets */
+ init_mad();
+
+ td.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO);
+ rb->dsp_configure(td.dsp, DSP_SET_OUT_FREQUENCY, CLOCK_RATE);
+#ifdef HAVE_PITCHCONTROL
+ rb->sound_set_pitch(PITCH_SPEED_100);
+ rb->dsp_set_timestretch(PITCH_SPEED_100);
+#endif
+ rb->dsp_configure(td.dsp, DSP_RESET, 0);
+ rb->dsp_configure(td.dsp, DSP_FLUSH, 0);
+ rb->dsp_configure(td.dsp, DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS);
+
+ goto message_wait;
+
+ /* This is the decoding loop. */
+ while (1)
+ {
+ td.state = TSTATE_DECODE;
+
+ /* Check for any pending messages and process them */
+ if (str_have_msg(&audio_str))
+ {
+ message_wait:
+ /* Wait for a message to be queued */
+ str_get_msg(&audio_str, &td.ev);
+
+ message_process:
+ /* Process a message already dequeued */
+ audio_thread_msg(&td);
+
+ switch (td.state)
+ {
+ /* These states are the only ones that should return */
+ case TSTATE_DECODE: goto audio_decode;
+ case TSTATE_RENDER_WAIT: goto render_wait;
+ /* Anything else is interpreted as an exit */
+ default:
+ {
+#ifdef HAVE_PRIORITY_SCHEDULING
+ rb->thread_set_priority(rb->thread_self(), old_priority);
+#endif
+ return;
+ }
+ }
+ }
+
+ audio_decode:
+
+ /** Buffering **/
+ switch (audio_buffer(&audio_str, STREAM_PM_STREAMING))
+ {
+ case STREAM_DATA_NOT_READY:
+ {
+ td.state = TSTATE_DATA;
+ goto message_wait;
+ } /* STREAM_DATA_NOT_READY: */
+
+ case STREAM_DATA_END:
+ {
+ if (audio_queue.used > MAD_BUFFER_GUARD)
+ break; /* Still have frames to decode */
+
+ /* Used up remainder of compressed audio buffer. Wait for
+ * samples on PCM buffer to finish playing. */
+ audio_queue_reset();
+
+ while (1)
+ {
+ if (pcm_output_empty())
+ {
+ td.state = TSTATE_EOS;
+ stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
+ break;
+ }
+
+ pcm_output_drain();
+ str_get_msg_w_tmo(&audio_str, &td.ev, 1);
+
+ if (td.ev.id != SYS_TIMEOUT)
+ break;
+ }
+
+ goto message_wait;
+ } /* STREAM_DATA_END: */
+ }
+
+ /** Decoding **/
+ mad_stream_buffer(&stream, audio_queue.ptr, audio_queue.used);
+
+ int mad_stat = mad_frame_decode(&frame, &stream);
+
+ ssize_t len = stream.next_frame - audio_queue.ptr;
+
+ if (mad_stat != 0)
+ {
+ DEBUGF("audio: Stream error: %s\n",
+ mad_stream_errorstr(&stream));
+
+ /* If something's goofed - try to perform resync by moving
+ * at least one byte at a time */
+ audio_queue_advance_pos(MAX(len, 1));
+
+ if (stream.error == MAD_ERROR_BUFLEN)
+ {
+ /* This makes the codec support partially corrupted files */
+ if (++td.mad_errors <= MPA_MAX_FRAME_SIZE)
+ {
+ stream.error = 0;
+ rb->yield();
+ continue;
+ }
+ DEBUGF("audio: Too many errors\n");
+ }
+ else if (MAD_RECOVERABLE(stream.error))
+ {
+ /* libmad says it can recover - just keep on decoding */
+ rb->yield();
+ continue;
+ }
+ else
+ {
+ /* Some other unrecoverable error */
+ DEBUGF("audio: Unrecoverable error\n");
+ }
+
+ /* This is too hard - bail out */
+ td.state = TSTATE_EOS;
+ td.status = STREAM_ERROR;
+ stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
+
+ goto message_wait;
+ }
+
+ /* Adjust sizes by the frame size */
+ audio_queue_advance_pos(len);
+ td.mad_errors = 0; /* Clear errors */
+
+ /* Generate the pcm samples */
+ mad_synth_frame(&synth, &frame);
+
+ /** Output **/
+ if (frame.header.samplerate != td.samplerate)
+ {
+ td.samplerate = frame.header.samplerate;
+ rb->dsp_configure(td.dsp, DSP_SET_FREQUENCY,
+ td.samplerate);
+ }
+
+ if (MAD_NCHANNELS(&frame.header) != td.nchannels)
+ {
+ td.nchannels = MAD_NCHANNELS(&frame.header);
+ rb->dsp_configure(td.dsp, DSP_SET_STEREO_MODE,
+ td.nchannels == 1 ?
+ STEREO_MONO : STEREO_NONINTERLEAVED);
+ }
+
+ td.src.remcount = synth.pcm.length;
+ td.src.pin[0] = synth.pcm.samples[0];
+ td.src.pin[1] = synth.pcm.samples[1];
+ td.src.proc_mask = 0;
+
+ td.state = TSTATE_RENDER_WAIT;
+
+ /* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */
+ render_wait:
+ rb->yield();
+
+ while (1)
+ {
+ struct dsp_buffer dst;
+ dst.remcount = 0;
+ dst.bufcount = MAX(td.src.remcount, 1024);
+
+ ssize_t size = dst.bufcount * 2 * sizeof(int16_t);
+
+ /* Wait for required amount of free buffer space */
+ while ((dst.p16out = pcm_output_get_buffer(&size)) == NULL)
+ {
+ /* Wait one frame */
+ int timeout = dst.bufcount*HZ / td.samplerate;
+ str_get_msg_w_tmo(&audio_str, &td.ev, MAX(timeout, 1));
+ if (td.ev.id != SYS_TIMEOUT)
+ goto message_process;
+ }
+
+ dst.bufcount = size / (2 * sizeof (int16_t));
+ rb->dsp_process(td.dsp, &td.src, &dst);
+
+ if (dst.remcount > 0)
+ {
+ /* Make this data available to DMA */
+ pcm_output_commit_data(dst.remcount * 2 * sizeof(int16_t),
+ audio_queue.curr->time);
+
+ /* As long as we're on this timestamp, the time is just
+ incremented by the number of samples */
+ audio_queue.curr->time += dst.remcount;
+ }
+ else if (td.src.remcount <= 0)
+ {
+ break;
+ }
+ }
+ } /* end decoding loop */
+}
+
+/* Initializes the audio thread resources and starts the thread */
+bool audio_thread_init(void)
+{
+ /* Initialise the encoded audio buffer and its descriptors */
+ audio_queue.start = mpeg_malloc(AUDIOBUF_ALLOC_SIZE,
+ MPEG_ALLOC_AUDIOBUF);
+ if (audio_queue.start == NULL)
+ return false;
+
+ /* Start the audio thread */
+ audio_str.hdr.q = &audio_str_queue;
+ rb->queue_init(audio_str.hdr.q, false);
+
+ /* We steal the codec thread for audio */
+ rb->codec_thread_do_callback(audio_thread, &audio_str.thread);
+
+ rb->queue_enable_queue_send(audio_str.hdr.q, &audio_str_queue_send,
+ audio_str.thread);
+
+ /* Wait for thread to initialize */
+ str_send_msg(&audio_str, STREAM_NULL, 0);
+
+ return true;
+}
+
+/* Stops the audio thread */
+void audio_thread_exit(void)
+{
+ if (audio_str.thread != 0)
+ {
+ str_post_msg(&audio_str, STREAM_QUIT, 0);
+ rb->codec_thread_do_callback(NULL, NULL);
+ audio_str.thread = 0;
+ }
+}
diff --git a/apps/plugins/mpegplayer/disk_buf.c b/apps/plugins/mpegplayer/disk_buf.c
new file mode 100644
index 0000000000..50c4222192
--- /dev/null
+++ b/apps/plugins/mpegplayer/disk_buf.c
@@ -0,0 +1,989 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * mpegplayer buffering routines
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "mpegplayer.h"
+#include <system.h>
+
+static struct mutex disk_buf_mtx SHAREDBSS_ATTR;
+static struct event_queue disk_buf_queue SHAREDBSS_ATTR;
+static struct queue_sender_list disk_buf_queue_send SHAREDBSS_ATTR;
+static uint32_t disk_buf_stack[DEFAULT_STACK_SIZE*2/sizeof(uint32_t)];
+
+struct disk_buf disk_buf SHAREDBSS_ATTR;
+static void *nf_list[MPEGPLAYER_MAX_STREAMS+1];
+
+static inline void disk_buf_lock(void)
+{
+ rb->mutex_lock(&disk_buf_mtx);
+}
+
+static inline void disk_buf_unlock(void)
+{
+ rb->mutex_unlock(&disk_buf_mtx);
+}
+
+static inline void disk_buf_on_clear_data_notify(struct stream_hdr *sh)
+{
+ DEBUGF("DISK_BUF_CLEAR_DATA_NOTIFY: 0x%02X (cleared)\n",
+ STR_FROM_HDR(sh)->id);
+ list_remove_item(nf_list, sh);
+}
+
+inline bool disk_buf_is_data_ready(struct stream_hdr *sh,
+ ssize_t margin)
+{
+ /* Data window available? */
+ off_t right = sh->win_right;
+
+ /* Margins past end-of-file can still return true */
+ if (right > disk_buf.filesize - margin)
+ right = disk_buf.filesize - margin;
+
+ return sh->win_left >= disk_buf.win_left &&
+ right + margin <= disk_buf.win_right;
+}
+
+void dbuf_l2_init(struct dbuf_l2_cache *l2_p)
+{
+ l2_p->addr = OFF_T_MAX; /* Mark as invalid */
+}
+
+static int disk_buf_on_data_notify(struct stream_hdr *sh)
+{
+ DEBUGF("DISK_BUF_DATA_NOTIFY: 0x%02X ", STR_FROM_HDR(sh)->id);
+
+ if (sh->win_left <= sh->win_right)
+ {
+ /* Check if the data is already ready already */
+ if (disk_buf_is_data_ready(sh, 0))
+ {
+ /* It was - don't register */
+ DEBUGF("(was ready)\n"
+ " swl:%lu swr:%lu\n"
+ " dwl:%lu dwr:%lu\n",
+ sh->win_left, sh->win_right,
+ disk_buf.win_left, disk_buf.win_right);
+ /* Be sure it's not listed though if multiple requests were made */
+ list_remove_item(nf_list, sh);
+ return DISK_BUF_NOTIFY_OK;
+ }
+
+ switch (disk_buf.state)
+ {
+ case TSTATE_DATA:
+ case TSTATE_BUFFERING:
+ case TSTATE_INIT:
+ disk_buf.state = TSTATE_BUFFERING;
+ list_add_item(nf_list, sh);
+ DEBUGF("(registered)\n"
+ " swl:%lu swr:%lu\n"
+ " dwl:%lu dwr:%lu\n",
+ sh->win_left, sh->win_right,
+ disk_buf.win_left, disk_buf.win_right);
+ return DISK_BUF_NOTIFY_REGISTERED;
+ }
+ }
+
+ DEBUGF("(error)\n");
+ return DISK_BUF_NOTIFY_ERROR;
+}
+
+static bool check_data_notifies_callback(struct stream_hdr *sh, void *data)
+{
+ if (disk_buf_is_data_ready(sh, 0))
+ {
+ /* Remove from list then post notification - post because send
+ * could result in a wait for each thread to finish resulting
+ * in deadlock */
+ list_remove_item(nf_list, sh);
+ str_post_msg(STR_FROM_HDR(sh), DISK_BUF_DATA_NOTIFY, 0);
+ DEBUGF("DISK_BUF_DATA_NOTIFY: 0x%02X (notified)\n",
+ STR_FROM_HDR(sh)->id);
+ }
+
+ return true;
+ (void)data;
+}
+
+/* Check registered streams and notify them if their data is available */
+static inline void check_data_notifies(void)
+{
+ list_enum_items(nf_list,
+ (list_enum_callback_t)check_data_notifies_callback,
+ NULL);
+}
+
+/* Clear all registered notifications - do not post them */
+static inline void clear_data_notifies(void)
+{
+ list_clear_all(nf_list);
+}
+
+/* Background buffering when streaming */
+static inline void disk_buf_buffer(void)
+{
+ struct stream_window sw;
+
+ switch (disk_buf.state)
+ {
+ default:
+ {
+ size_t wm;
+ uint32_t time;
+
+ /* Get remaining minimum data based upon the stream closest to the
+ * right edge of the window */
+ if (!stream_get_window(&sw))
+ break;
+
+ time = stream_get_ticks(NULL);
+ wm = muldiv_uint32(5*CLOCK_RATE, sw.right - disk_buf.pos_last,
+ time - disk_buf.time_last);
+ wm = MIN(wm, (size_t)disk_buf.size);
+ wm = MAX(wm, DISK_BUF_LOW_WATERMARK);
+
+ disk_buf.time_last = time;
+ disk_buf.pos_last = sw.right;
+
+ /* Fast attack, slow decay */
+ disk_buf.low_wm = (wm > (size_t)disk_buf.low_wm) ?
+ wm : AVERAGE(disk_buf.low_wm, wm, 16);
+
+#if 0
+ rb->splashf(0, "*%10ld %10ld", disk_buf.low_wm,
+ disk_buf.win_right - sw.right);
+#endif
+
+ if (disk_buf.win_right - sw.right > disk_buf.low_wm)
+ break;
+
+ disk_buf.state = TSTATE_BUFFERING;
+ } /* default: */
+
+ /* Fall-through */
+ case TSTATE_BUFFERING:
+ {
+ ssize_t len, n;
+ uint32_t tag, *tag_p;
+
+ /* Limit buffering up to the stream with the least progress */
+ if (!stream_get_window(&sw))
+ {
+ disk_buf.state = TSTATE_DATA;
+ rb->storage_sleep();
+ break;
+ }
+
+ /* Wrap pointer */
+ if (disk_buf.tail >= disk_buf.end)
+ disk_buf.tail = disk_buf.start;
+
+ len = disk_buf.size - disk_buf.win_right + sw.left;
+
+ if (len < DISK_BUF_PAGE_SIZE)
+ {
+ /* Free space is less than one page */
+ disk_buf.state = TSTATE_DATA;
+ disk_buf.low_wm = DISK_BUF_LOW_WATERMARK;
+ rb->storage_sleep();
+ break;
+ }
+
+ len = disk_buf.tail - disk_buf.start;
+ tag = MAP_OFFSET_TO_TAG(disk_buf.win_right);
+ tag_p = &disk_buf.cache[len >> DISK_BUF_PAGE_SHIFT];
+
+ if (*tag_p != tag)
+ {
+ if (disk_buf.need_seek)
+ {
+ rb->lseek(disk_buf.in_file, disk_buf.win_right, SEEK_SET);
+ disk_buf.need_seek = false;
+ }
+
+ n = rb->read(disk_buf.in_file, disk_buf.tail, DISK_BUF_PAGE_SIZE);
+
+ if (n <= 0)
+ {
+ /* Error or end of stream */
+ disk_buf.state = TSTATE_EOS;
+ rb->storage_sleep();
+ break;
+ }
+
+ if (len < DISK_GUARDBUF_SIZE)
+ {
+ /* Autoguard guard-o-rama - maintain guardbuffer coherency */
+ rb->memcpy(disk_buf.end + len, disk_buf.tail,
+ MIN(DISK_GUARDBUF_SIZE - len, n));
+ }
+
+ /* Update the cache entry for this page */
+ *tag_p = tag;
+ }
+ else
+ {
+ /* Skipping a read */
+ n = MIN(DISK_BUF_PAGE_SIZE,
+ disk_buf.filesize - disk_buf.win_right);
+ disk_buf.need_seek = true;
+ }
+
+ disk_buf.tail += DISK_BUF_PAGE_SIZE;
+
+ /* Keep left edge moving forward */
+
+ /* Advance right edge in temp variable first, then move
+ * left edge if overflow would occur. This avoids a stream
+ * thinking its data might be available when it actually
+ * may not end up that way after a slide of the window. */
+ len = disk_buf.win_right + n;
+
+ if (len - disk_buf.win_left > disk_buf.size)
+ disk_buf.win_left += n;
+
+ disk_buf.win_right = len;
+
+ /* Continue buffering until filled or file end */
+ rb->yield();
+ } /* TSTATE_BUFFERING: */
+
+ case TSTATE_EOS:
+ break;
+ } /* end switch */
+}
+
+static void disk_buf_on_reset(ssize_t pos)
+{
+ int page;
+ uint32_t tag;
+ off_t anchor;
+
+ disk_buf.state = TSTATE_INIT;
+ disk_buf.status = STREAM_STOPPED;
+ clear_data_notifies();
+
+ if (pos >= disk_buf.filesize)
+ {
+ /* Anchor on page immediately following the one containing final
+ * data */
+ anchor = disk_buf.file_pages * DISK_BUF_PAGE_SIZE;
+ disk_buf.win_left = disk_buf.filesize;
+ }
+ else
+ {
+ anchor = pos & ~DISK_BUF_PAGE_MASK;
+ disk_buf.win_left = anchor;
+ }
+
+ /* Collect all valid data already buffered that is contiguous with the
+ * current position - probe to left, then to right */
+ if (anchor > 0)
+ {
+ page = MAP_OFFSET_TO_PAGE(anchor);
+ tag = MAP_OFFSET_TO_TAG(anchor);
+
+ do
+ {
+ if (--tag, --page < 0)
+ page = disk_buf.pgcount - 1;
+
+ if (disk_buf.cache[page] != tag)
+ break;
+
+ disk_buf.win_left = tag << DISK_BUF_PAGE_SHIFT;
+ }
+ while (tag > 0);
+ }
+
+ if (anchor < disk_buf.filesize)
+ {
+ page = MAP_OFFSET_TO_PAGE(anchor);
+ tag = MAP_OFFSET_TO_TAG(anchor);
+
+ do
+ {
+ if (disk_buf.cache[page] != tag)
+ break;
+
+ if (++tag, ++page >= disk_buf.pgcount)
+ page = 0;
+
+ anchor += DISK_BUF_PAGE_SIZE;
+ }
+ while (anchor < disk_buf.filesize);
+ }
+
+ if (anchor >= disk_buf.filesize)
+ {
+ disk_buf.win_right = disk_buf.filesize;
+ disk_buf.state = TSTATE_EOS;
+ }
+ else
+ {
+ disk_buf.win_right = anchor;
+ }
+
+ disk_buf.tail = disk_buf.start + MAP_OFFSET_TO_BUFFER(anchor);
+
+ DEBUGF("disk buf reset\n"
+ " dwl:%ld dwr:%ld\n",
+ disk_buf.win_left, disk_buf.win_right);
+
+ /* Next read position is at right edge */
+ rb->lseek(disk_buf.in_file, disk_buf.win_right, SEEK_SET);
+ disk_buf.need_seek = false;
+
+ disk_buf_reply_msg(disk_buf.win_right - disk_buf.win_left);
+}
+
+static void disk_buf_on_stop(void)
+{
+ bool was_buffering = disk_buf.state == TSTATE_BUFFERING;
+
+ disk_buf.state = TSTATE_EOS;
+ disk_buf.status = STREAM_STOPPED;
+ clear_data_notifies();
+
+ disk_buf_reply_msg(was_buffering);
+}
+
+static void disk_buf_on_play_pause(bool play, bool forcefill)
+{
+ struct stream_window sw;
+
+ if (disk_buf.state != TSTATE_EOS)
+ {
+ if (forcefill)
+ {
+ /* Force buffer filling to top */
+ disk_buf.state = TSTATE_BUFFERING;
+ }
+ else if (disk_buf.state != TSTATE_BUFFERING)
+ {
+ /* If not filling already, simply monitor */
+ disk_buf.state = TSTATE_DATA;
+ }
+ }
+ /* else end of stream - no buffering to do */
+
+ disk_buf.pos_last = stream_get_window(&sw) ? sw.right : 0;
+ disk_buf.time_last = stream_get_ticks(NULL);
+
+ disk_buf.status = play ? STREAM_PLAYING : STREAM_PAUSED;
+}
+
+static int disk_buf_on_load_range(struct dbuf_range *rng)
+{
+ uint32_t tag = rng->tag_start;
+ uint32_t tag_end = rng->tag_end;
+ int page = rng->pg_start;
+
+ /* Check if a seek is required */
+ bool need_seek = rb->lseek(disk_buf.in_file, 0, SEEK_CUR)
+ != (off_t)(tag << DISK_BUF_PAGE_SHIFT);
+
+ do
+ {
+ uint32_t *tag_p = &disk_buf.cache[page];
+
+ if (*tag_p != tag)
+ {
+ /* Page not cached - load it */
+ ssize_t o, n;
+
+ if (need_seek)
+ {
+ rb->lseek(disk_buf.in_file, tag << DISK_BUF_PAGE_SHIFT,
+ SEEK_SET);
+ need_seek = false;
+ }
+
+ o = page << DISK_BUF_PAGE_SHIFT;
+ n = rb->read(disk_buf.in_file, disk_buf.start + o,
+ DISK_BUF_PAGE_SIZE);
+
+ if (n < 0)
+ {
+ /* Read error */
+ return DISK_BUF_NOTIFY_ERROR;
+ }
+
+ if (n == 0)
+ {
+ /* End of file */
+ break;
+ }
+
+ if (o < DISK_GUARDBUF_SIZE)
+ {
+ /* Autoguard guard-o-rama - maintain guardbuffer coherency */
+ rb->memcpy(disk_buf.end + o, disk_buf.start + o,
+ MIN(DISK_GUARDBUF_SIZE - o, n));
+ }
+
+ /* Update the cache entry */
+ *tag_p = tag;
+ }
+ else
+ {
+ /* Skipping a disk read - must seek on next one */
+ need_seek = true;
+ }
+
+ if (++page >= disk_buf.pgcount)
+ page = 0;
+ }
+ while (++tag <= tag_end);
+
+ return DISK_BUF_NOTIFY_OK;
+}
+
+static void disk_buf_thread(void)
+{
+ struct queue_event ev;
+
+ disk_buf.state = TSTATE_EOS;
+ disk_buf.status = STREAM_STOPPED;
+
+ while (1)
+ {
+ if (disk_buf.state != TSTATE_EOS)
+ {
+ /* Poll buffer status and messages */
+ rb->queue_wait_w_tmo(disk_buf.q, &ev,
+ disk_buf.state == TSTATE_BUFFERING ?
+ 0 : HZ/5);
+ }
+ else
+ {
+ /* Sit idle and wait for commands */
+ rb->queue_wait(disk_buf.q, &ev);
+ }
+
+ switch (ev.id)
+ {
+ case SYS_TIMEOUT:
+ if (disk_buf.state == TSTATE_EOS)
+ break;
+
+ disk_buf_buffer();
+
+ /* Check for any due notifications if any are pending */
+ if (*nf_list != NULL)
+ check_data_notifies();
+
+ /* Still more data left? */
+ if (disk_buf.state != TSTATE_EOS)
+ continue;
+
+ /* Nope - end of stream */
+ break;
+
+ case DISK_BUF_CACHE_RANGE:
+ disk_buf_reply_msg(disk_buf_on_load_range(
+ (struct dbuf_range *)ev.data));
+ break;
+
+ case STREAM_RESET:
+ disk_buf_on_reset(ev.data);
+ break;
+
+ case STREAM_STOP:
+ disk_buf_on_stop();
+ break;
+
+ case STREAM_PAUSE:
+ case STREAM_PLAY:
+ disk_buf_on_play_pause(ev.id == STREAM_PLAY, ev.data != 0);
+ disk_buf_reply_msg(1);
+ break;
+
+ case STREAM_QUIT:
+ disk_buf.state = TSTATE_EOS;
+ return;
+
+ case DISK_BUF_DATA_NOTIFY:
+ disk_buf_reply_msg(disk_buf_on_data_notify(
+ (struct stream_hdr *)ev.data));
+ break;
+
+ case DISK_BUF_CLEAR_DATA_NOTIFY:
+ disk_buf_on_clear_data_notify((struct stream_hdr *)ev.data);
+ disk_buf_reply_msg(1);
+ break;
+ }
+ }
+}
+
+/* Caches some data from the current file */
+static ssize_t disk_buf_probe(off_t start, size_t length, void **p)
+{
+ off_t end;
+ uint32_t tag, tag_end;
+ int page;
+
+ /* Can't read past end of file */
+ if (length > (size_t)(disk_buf.filesize - start))
+ {
+ length = disk_buf.filesize - start;
+ }
+
+ /* Can't cache more than the whole buffer size */
+ if (length > (size_t)disk_buf.size)
+ {
+ length = disk_buf.size;
+ }
+ /* Zero-length probes permitted */
+
+ end = start + length;
+
+ /* Prepare the range probe */
+ tag = MAP_OFFSET_TO_TAG(start);
+ tag_end = MAP_OFFSET_TO_TAG(end);
+ page = MAP_OFFSET_TO_PAGE(start);
+
+ /* If the end is on a page boundary, check one less or an extra
+ * one will be probed */
+ if (tag_end > tag && (end & DISK_BUF_PAGE_MASK) == 0)
+ {
+ tag_end--;
+ }
+
+ if (p != NULL)
+ {
+ *p = disk_buf.start + (page << DISK_BUF_PAGE_SHIFT)
+ + (start & DISK_BUF_PAGE_MASK);
+ }
+
+ /* Obtain initial load point. If all data was cached, no message is sent
+ * otherwise begin on the first page that is not cached. Since we have to
+ * send the message anyway, the buffering thread will determine what else
+ * requires loading on its end in order to cache the specified range. */
+ do
+ {
+ if (disk_buf.cache[page] != tag)
+ {
+ static struct dbuf_range rng IBSS_ATTR;
+ intptr_t result;
+
+ DEBUGF("disk_buf: cache miss\n");
+ rng.tag_start = tag;
+ rng.tag_end = tag_end;
+ rng.pg_start = page;
+
+ result = rb->queue_send(disk_buf.q, DISK_BUF_CACHE_RANGE,
+ (intptr_t)&rng);
+
+ return result == DISK_BUF_NOTIFY_OK ? (ssize_t)length : -1;
+ }
+
+ if (++page >= disk_buf.pgcount)
+ page = 0;
+ }
+ while (++tag <= tag_end);
+
+ return length;
+}
+
+/* Attempt to get a pointer to size bytes on the buffer. Returns real amount of
+ * data available as well as the size of non-wrapped data after *p. */
+ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap,
+ size_t *sizewrap)
+{
+ disk_buf_lock();
+
+ size = disk_buf_probe(disk_buf.offset, size, pp);
+
+ if (size != (size_t)-1 && pwrap && sizewrap)
+ {
+ uint8_t *p = (uint8_t *)*pp;
+
+ if (p + size > disk_buf.end + DISK_GUARDBUF_SIZE)
+ {
+ /* Return pointer to wraparound and the size of same */
+ size_t nowrap = (disk_buf.end + DISK_GUARDBUF_SIZE) - p;
+ *pwrap = disk_buf.start + DISK_GUARDBUF_SIZE;
+ *sizewrap = size - nowrap;
+ }
+ else
+ {
+ *pwrap = NULL;
+ *sizewrap = 0;
+ }
+ }
+
+ disk_buf_unlock();
+
+ return size;
+}
+
+ssize_t _disk_buf_getbuffer_l2(struct dbuf_l2_cache *l2,
+ size_t size, void **pp)
+{
+ off_t offs;
+ off_t l2_addr;
+ size_t l2_size;
+ void *l2_p;
+
+ if (l2 == NULL)
+ {
+ /* Shouldn't have to check this normally */
+ DEBUGF("disk_buf_getbuffer_l2: l2 = NULL!\n");
+ }
+
+ if (size > DISK_BUF_L2_CACHE_SIZE)
+ {
+ /* Asking for too much; just go through L1 */
+ return disk_buf_getbuffer(size, pp, NULL, NULL);
+ }
+
+ offs = disk_buf.offset; /* Other calls keep this within bounds */
+ l2_addr = l2->addr;
+
+ if (offs >= l2_addr && offs < l2_addr + DISK_BUF_L2_CACHE_SIZE)
+ {
+ /* Data is in the local buffer */
+ offs &= DISK_BUF_L2_CACHE_MASK;
+
+ *pp = l2->data + offs;
+ if (offs + size > l2->size)
+ size = l2->size - offs; /* Keep size within file limits */
+
+ return size;
+ }
+
+ /* Have to probe main buffer */
+ l2_addr = offs & ~DISK_BUF_L2_CACHE_MASK;
+ l2_size = DISK_BUF_L2_CACHE_SIZE*2; /* 2nd half is a guard buffer */
+
+ disk_buf_lock();
+
+ l2_size = disk_buf_probe(l2_addr, l2_size, &l2_p);
+
+ if (l2_size != (size_t)-1)
+ {
+ rb->memcpy(l2->data, l2_p, l2_size);
+
+ l2->addr = l2_addr;
+ l2->size = l2_size;
+ offs -= l2_addr;
+
+ *pp = l2->data + offs;
+ if (offs + size > l2->size)
+ size = l2->size - offs; /* Keep size within file limits */
+ }
+ else
+ {
+ size = -1;
+ }
+
+ disk_buf_unlock();
+
+ return size;
+}
+
+
+/* Read size bytes of data into a buffer - advances the buffer pointer
+ * and returns the real size read. */
+ssize_t disk_buf_read(void *buffer, size_t size)
+{
+ uint8_t *p;
+
+ disk_buf_lock();
+
+ size = disk_buf_probe(disk_buf.offset, size, PUN_PTR(void **, &p));
+
+ if (size != (size_t)-1)
+ {
+ if (p + size > disk_buf.end + DISK_GUARDBUF_SIZE)
+ {
+ /* Read wraps */
+ size_t nowrap = (disk_buf.end + DISK_GUARDBUF_SIZE) - p;
+ rb->memcpy(buffer, p, nowrap);
+ rb->memcpy(buffer + nowrap, disk_buf.start + DISK_GUARDBUF_SIZE,
+ size - nowrap);
+ }
+ else
+ {
+ /* Read wasn't wrapped or guardbuffer holds it */
+ rb->memcpy(buffer, p, size);
+ }
+
+ disk_buf.offset += size;
+ }
+
+ disk_buf_unlock();
+
+ return size;
+}
+
+ssize_t disk_buf_lseek(off_t offset, int whence)
+{
+ disk_buf_lock();
+
+ /* The offset returned is the result of the current thread's action and
+ * may be invalidated so a local result is returned and not the value
+ * of disk_buf.offset directly */
+ switch (whence)
+ {
+ case SEEK_SET:
+ /* offset is just the offset */
+ break;
+ case SEEK_CUR:
+ offset += disk_buf.offset;
+ break;
+ case SEEK_END:
+ offset = disk_buf.filesize + offset;
+ break;
+ default:
+ disk_buf_unlock();
+ return -2; /* Invalid request */
+ }
+
+ if (offset < 0 || offset > disk_buf.filesize)
+ {
+ offset = -3;
+ }
+ else
+ {
+ disk_buf.offset = offset;
+ }
+
+ disk_buf_unlock();
+
+ return offset;
+}
+
+/* Prepare the buffer to enter the streaming state. Evaluates the available
+ * streaming window. */
+ssize_t disk_buf_prepare_streaming(off_t pos, size_t len)
+{
+ disk_buf_lock();
+
+ if (pos < 0)
+ pos = 0;
+ else if (pos > disk_buf.filesize)
+ pos = disk_buf.filesize;
+
+ DEBUGF("prepare streaming:\n pos:%ld len:%lu\n", pos, (unsigned long)len);
+
+ pos = disk_buf_lseek(pos, SEEK_SET);
+ len = disk_buf_probe(pos, len, NULL);
+
+ DEBUGF(" probe done: pos:%ld len:%lu\n", pos, (unsigned long)len);
+
+ len = disk_buf_send_msg(STREAM_RESET, pos);
+
+ disk_buf_unlock();
+
+ return len;
+}
+
+/* Set the streaming window to an arbitrary position within the file. Makes no
+ * probes to validate data. Use after calling another function to cause data
+ * to be cached and correct values are known. */
+ssize_t disk_buf_set_streaming_window(off_t left, off_t right)
+{
+ ssize_t len;
+
+ disk_buf_lock();
+
+ if (left < 0)
+ left = 0;
+ else if (left > disk_buf.filesize)
+ left = disk_buf.filesize;
+
+ if (left > right)
+ right = left;
+
+ if (right > disk_buf.filesize)
+ right = disk_buf.filesize;
+
+ disk_buf.win_left = left;
+ disk_buf.win_right = right;
+ disk_buf.tail = disk_buf.start + ((right + DISK_BUF_PAGE_SIZE-1) &
+ ~DISK_BUF_PAGE_MASK) % disk_buf.size;
+
+ len = disk_buf.win_right - disk_buf.win_left;
+
+ disk_buf_unlock();
+
+ return len;
+}
+
+void * disk_buf_offset2ptr(off_t offset)
+{
+ if (offset < 0)
+ offset = 0;
+ else if (offset > disk_buf.filesize)
+ offset = disk_buf.filesize;
+
+ return disk_buf.start + (offset % disk_buf.size);
+}
+
+void disk_buf_close(void)
+{
+ disk_buf_lock();
+
+ if (disk_buf.in_file >= 0)
+ {
+ rb->close(disk_buf.in_file);
+ disk_buf.in_file = -1;
+
+ /* Invalidate entire cache */
+ rb->memset(disk_buf.cache, 0xff,
+ disk_buf.pgcount*sizeof (*disk_buf.cache));
+ disk_buf.file_pages = 0;
+ disk_buf.filesize = 0;
+ disk_buf.offset = 0;
+ }
+
+ disk_buf_unlock();
+}
+
+int disk_buf_open(const char *filename)
+{
+ int fd;
+
+ disk_buf_lock();
+
+ disk_buf_close();
+
+ fd = rb->open(filename, O_RDONLY);
+
+ if (fd >= 0)
+ {
+ ssize_t filesize = rb->filesize(fd);
+
+ if (filesize <= 0)
+ {
+ rb->close(disk_buf.in_file);
+ }
+ else
+ {
+ disk_buf.filesize = filesize;
+ /* Number of file pages rounded up toward +inf */
+ disk_buf.file_pages = ((size_t)filesize + DISK_BUF_PAGE_SIZE-1)
+ / DISK_BUF_PAGE_SIZE;
+ disk_buf.in_file = fd;
+ }
+ }
+
+ disk_buf_unlock();
+
+ return fd;
+}
+
+intptr_t disk_buf_send_msg(long id, intptr_t data)
+{
+ return rb->queue_send(disk_buf.q, id, data);
+}
+
+void disk_buf_post_msg(long id, intptr_t data)
+{
+ rb->queue_post(disk_buf.q, id, data);
+}
+
+void disk_buf_reply_msg(intptr_t retval)
+{
+ rb->queue_reply(disk_buf.q, retval);
+}
+
+bool disk_buf_init(void)
+{
+ disk_buf.thread = 0;
+
+ rb->mutex_init(&disk_buf_mtx);
+
+ disk_buf.q = &disk_buf_queue;
+ rb->queue_init(disk_buf.q, false);
+
+ disk_buf.state = TSTATE_EOS;
+ disk_buf.status = STREAM_STOPPED;
+
+ disk_buf.in_file = -1;
+ disk_buf.filesize = 0;
+ disk_buf.win_left = 0;
+ disk_buf.win_right = 0;
+ disk_buf.time_last = 0;
+ disk_buf.pos_last = 0;
+ disk_buf.low_wm = DISK_BUF_LOW_WATERMARK;
+
+ disk_buf.start = mpeg_malloc_all((size_t*)&disk_buf.size, MPEG_ALLOC_DISKBUF);
+ if (disk_buf.start == NULL)
+ return false;
+
+#if NUM_CORES > 1
+ CACHEALIGN_BUFFER(disk_buf.start, disk_buf.size);
+ disk_buf.start = UNCACHED_ADDR(disk_buf.start);
+#endif
+ disk_buf.size -= DISK_GUARDBUF_SIZE;
+ disk_buf.pgcount = disk_buf.size / DISK_BUF_PAGE_SIZE;
+
+ /* Fit it as tightly as possible */
+ while (disk_buf.pgcount*(sizeof (*disk_buf.cache) + DISK_BUF_PAGE_SIZE)
+ > (size_t)disk_buf.size)
+ {
+ disk_buf.pgcount--;
+ }
+
+ disk_buf.cache = (typeof (disk_buf.cache))disk_buf.start;
+ disk_buf.start += sizeof (*disk_buf.cache)*disk_buf.pgcount;
+ disk_buf.size = disk_buf.pgcount*DISK_BUF_PAGE_SIZE;
+ disk_buf.end = disk_buf.start + disk_buf.size;
+ disk_buf.tail = disk_buf.start;
+
+ DEBUGF("disk_buf info:\n"
+ " page count: %d\n"
+ " size: %ld\n",
+ disk_buf.pgcount, (long)disk_buf.size);
+
+ rb->memset(disk_buf.cache, 0xff,
+ disk_buf.pgcount*sizeof (*disk_buf.cache));
+
+ disk_buf.thread = rb->create_thread(
+ disk_buf_thread, disk_buf_stack, sizeof(disk_buf_stack), 0,
+ "mpgbuffer" IF_PRIO(, PRIORITY_BUFFERING) IF_COP(, CPU));
+
+ rb->queue_enable_queue_send(disk_buf.q, &disk_buf_queue_send,
+ disk_buf.thread);
+
+ if (disk_buf.thread == 0)
+ return false;
+
+ /* Wait for thread to initialize */
+ disk_buf_send_msg(STREAM_NULL, 0);
+
+ return true;
+}
+
+void disk_buf_exit(void)
+{
+ if (disk_buf.thread != 0)
+ {
+ rb->queue_post(disk_buf.q, STREAM_QUIT, 0);
+ rb->thread_wait(disk_buf.thread);
+ disk_buf.thread = 0;
+ }
+}
diff --git a/apps/plugins/mpegplayer/disk_buf.h b/apps/plugins/mpegplayer/disk_buf.h
new file mode 100644
index 0000000000..bc76ab6dc3
--- /dev/null
+++ b/apps/plugins/mpegplayer/disk_buf.h
@@ -0,0 +1,152 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * AV disk buffer declarations
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef DISK_BUF_H
+#define DISK_BUF_H
+
+#ifndef OFF_T_MAX
+#define OFF_T_MAX (~((off_t)1 << (sizeof (off_t)*8 - 1)))
+#endif
+
+#ifndef OFF_T_MIN
+#define OFF_T_MIN ((off_t)1 << (sizeof (off_t)*8 - 1))
+#endif
+
+#define DISK_BUF_PAGE_SHIFT 15 /* 32KB cache lines */
+#define DISK_BUF_PAGE_SIZE (1 << DISK_BUF_PAGE_SHIFT)
+#define DISK_BUF_PAGE_MASK (DISK_BUF_PAGE_SIZE-1)
+
+enum
+{
+ DISK_BUF_NOTIFY_ERROR = -1,
+ DISK_BUF_NOTIFY_NULL = 0,
+ DISK_BUF_NOTIFY_OK,
+ DISK_BUF_NOTIFY_TIMEDOUT,
+ DISK_BUF_NOTIFY_PROCESS_EVENT,
+ DISK_BUF_NOTIFY_REGISTERED,
+};
+
+/** Macros to map file offsets to cached data **/
+
+/* Returns a cache tag given a file offset */
+#define MAP_OFFSET_TO_TAG(o) \
+ ((o) >> DISK_BUF_PAGE_SHIFT)
+
+/* Returns the cache page number given a file offset */
+#define MAP_OFFSET_TO_PAGE(o) \
+ (MAP_OFFSET_TO_TAG(o) % disk_buf.pgcount)
+
+/* Returns the buffer offset given a file offset */
+#define MAP_OFFSET_TO_BUFFER(o) \
+ (MAP_OFFSET_TO_PAGE(o) * DISK_BUF_PAGE_SIZE)
+
+struct dbuf_range
+{
+ uint32_t tag_start;
+ uint32_t tag_end;
+ int pg_start;
+};
+
+#define DISK_BUF_L2_CACHE_SHIFT 6
+#define DISK_BUF_L2_CACHE_SIZE (1 << DISK_BUF_L2_CACHE_SHIFT)
+#define DISK_BUF_L2_CACHE_MASK (DISK_BUF_L2_CACHE_SIZE-1)
+
+struct dbuf_l2_cache
+{
+ off_t addr; /* L2 file offset */
+ size_t size; /* Real size */
+ uint8_t data[DISK_BUF_L2_CACHE_SIZE*2]; /* Local data and guard */
+};
+
+void dbuf_l2_init(struct dbuf_l2_cache *l2_p);
+
+/* This object is an extension of the stream manager and handles some
+ * playback events as well as buffering */
+struct disk_buf
+{
+ unsigned int thread;
+ struct event_queue *q;
+ uint8_t *start; /* Start pointer */
+ uint8_t *end; /* End of buffer pointer less MPEG_GUARDBUF_SIZE. The
+ guard space is used to wrap data at the buffer start to
+ pass continuous data packets */
+ uint8_t *tail; /* Location of last data + 1 filled into the buffer */
+ ssize_t size; /* The buffer length _not_ including the guard space (end-start) */
+ int pgcount; /* Total number of available cached pages */
+ uint32_t *cache; /* Pointer to cache structure - allocated on buffer */
+ int in_file; /* File being read */
+ ssize_t filesize; /* Size of file in_file in bytes */
+ int file_pages; /* Number of pages in file (rounded up) */
+ off_t offset; /* Current position (random access) */
+ off_t win_left; /* Left edge of buffer window (streaming) */
+ off_t win_right; /* Right edge of buffer window (streaming) */
+ uint32_t time_last; /* Last time watermark was checked */
+ off_t pos_last; /* Last position at watermark check time */
+ ssize_t low_wm; /* The low watermark for automatic rebuffering */
+ int status; /* Status as stream */
+ int state; /* Current thread state */
+ bool need_seek; /* Need to seek because a read was not contiguous */
+};
+
+extern struct disk_buf disk_buf SHAREDBSS_ATTR;
+
+struct stream_hdr;
+bool disk_buf_is_data_ready(struct stream_hdr *sh, ssize_t margin);
+
+bool disk_buf_init(void);
+void disk_buf_exit(void);
+
+static inline int disk_buf_status(void)
+ { return disk_buf.status; }
+
+int disk_buf_open(const char *filename);
+void disk_buf_close(void);
+ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap,
+ size_t *sizewrap);
+#define disk_buf_getbuffer(size, pp, pwrap, sizewrap) \
+ _disk_buf_getbuffer((size), PUN_PTR(void **, (pp)), \
+ PUN_PTR(void **, (pwrap)), (sizewrap))
+
+ssize_t _disk_buf_getbuffer_l2(struct dbuf_l2_cache *l2,
+ size_t size, void **pp);
+#define disk_buf_getbuffer_l2(l2, size, pp) \
+ _disk_buf_getbuffer_l2((l2), (size), PUN_PTR(void **, (pp)))
+
+ssize_t disk_buf_read(void *buffer, size_t size);
+ssize_t disk_buf_lseek(off_t offset, int whence);
+
+static inline off_t disk_buf_ftell(void)
+ { return disk_buf.offset; }
+
+static inline ssize_t disk_buf_filesize(void)
+ { return disk_buf.filesize; }
+
+ssize_t disk_buf_prepare_streaming(off_t pos, size_t len);
+ssize_t disk_buf_set_streaming_window(off_t left, off_t right);
+void * disk_buf_offset2ptr(off_t offset);
+int disk_buf_check_streaming_window(off_t left, off_t right);
+
+intptr_t disk_buf_send_msg(long id, intptr_t data);
+void disk_buf_post_msg(long id, intptr_t data);
+void disk_buf_reply_msg(intptr_t retval);
+
+#endif /* DISK_BUF_H */
diff --git a/apps/plugins/mpegplayer/libmpeg2/AUTHORS b/apps/plugins/mpegplayer/libmpeg2/AUTHORS
new file mode 100644
index 0000000000..4722897c97
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/AUTHORS
@@ -0,0 +1,33 @@
+Aaron Holtzman <aholtzma@ess.engr.uvic.ca> started the project and
+made the initial working implementation.
+
+Michel Lespinasse <walken@zoy.org> did major changes for speed and
+mpeg conformance and is the current maintainer. Most of the current
+code was (re)written by him.
+
+Other contributors include:
+ Bruno Barreyra <barreyra@ufl.edu> - build fixes
+ Gildas Bazin <gbazin@netcourrier.com> - mingw32 port
+ Alexander W. Chin <alexc@newt.phys.unsw.edu.au> - progressive_seq fix
+ Stephen Crowley <stephenc@dns2.digitalpassage.com> - build fixes
+ Didier Gautheron <dgautheron@magic.fr> - bug fixes
+ Ryan C. Gordon <icculus@lokigames.com> - SDL support
+ Peter Gubanov <peter@elecard.net.ru> - MMX IDCT scheduling
+ HÃ¥kan Hjort <d95hjort@dtek.chalmers.se> - Solaris fixes, mlib code
+ Nicolas Joly <njoly@pasteur.fr> - assorted bug fixes
+ Gerd Knorr <kraxel@goldbach.in-berlin.de> - Xv support
+ David I. Lehn <dlehn@vt.edu> - motion_comp mmx code
+ Olie Lho <ollie@sis.com.tw> - MMX yuv2rgb routine
+ David S. Miller <davem@redhat.com> - sparc VIS optimizations
+ Rick Niles <niles@scyld.com> - build fixes
+ Real Ouellet <realo@sympatico.ca> - g200 fixes
+ Bajusz Peter <hyp-x@inf.bme.hu> - motion comp fixes
+ Franck Sicard <Franck.Sicard@miniruth.solsoft.fr> - x11 fixes
+ Brion Vibber <brion@gizmo.usc.edu> - x11 fixes
+ Martin Vogt <mvogt@rhrk.uni-kl.de> - reentrancy fixes
+ Fredrik Vraalsen <vraalsen@cs.uiuc.edu> - general hackage and stuff
+
+(let me know if I forgot anyone)
+
+Thanks to David Schleef for creating me an account on his ppc g4
+machine and making it possible for me to work on the altivec code.
diff --git a/apps/plugins/mpegplayer/libmpeg2/README b/apps/plugins/mpegplayer/libmpeg2/README
new file mode 100644
index 0000000000..2a58846772
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/README
@@ -0,0 +1,204 @@
+
+
+ABOUT LIBMPEG2
+
+libmpeg2 is a free library for decoding mpeg-2 and mpeg-1 video
+streams. It is released under the terms of the GPL license.
+
+The main goals in libmpeg2 development are:
+
+ * Conformance - libmpeg2 is able to decode all mpeg streams that
+ conform to certain restrictions: "constrained parameters" for
+ mpeg-1, and "main profile" for mpeg-2. In practice, this is
+ what most people are using. For streams that follow these
+ restrictions, we believe libmpeg2 is 100% conformant to the
+ mpeg standards - and we have a pretty extensive test suite to
+ check this.
+
+ * Speed - there has been huge efforts there, and we believe
+ libmpeg2 is the fastest library around for what it
+ does. Please tell us if you find a faster one ! With typical
+ video streams as found on DVD's, and doing only decoding with
+ no display, you should be able to get about 110 fps on a
+ PIII/666, or 150 fps on an Athlon/950. This is less than 20
+ cycles per output pixel. In a real player program, the display
+ routines will probably take as much time as the actual
+ decoding !
+
+ * Portability - most of the code is written in C, and when we
+ use platform-specific optimizations (typically assembly
+ routines, currently used for the motion compensation and the
+ inverse cosine transform stages) we always have a generic C
+ routine to fall back on. This should be portable to all
+ architectures - at least we have heard reports from people
+ running this code on x86, ppc, sparc, arm and
+ sh4. Assembly-optimized implementations are available on x86
+ (MMX) and ppc (altivec) architectures. Ultrasparc (VIS) is
+ probably the next on the list - we'll see.
+
+ * Reuseability - we do not want libmpeg2 to include any
+ project-specific code, but it should still include enough
+ features to be used by very diverse projects. We are only
+ starting to get there - the best way to help here is to give
+ us some feedback !
+
+The project homepage is at http://libmpeg2.sourceforge.net/
+
+
+MPEG2DEC
+
+mpeg2dec is a test program for libmpeg2. It decodes mpeg-1 and mpeg-2
+video streams, and also includes a demultiplexer for mpeg-1 and mpeg-2
+program streams. It is purposely kept simple : it does not include
+features like reading files from a DVD, CSS, fullscreen output,
+navigation, etc... The main purpose of mpeg2dec is to have a simple
+test bed for libmpeg2.
+
+The libmpeg2 source code is always distributed in the mpeg2dec
+package, to make it easier for people to test it.
+
+The basic usage is to just type "mpeg2dec file" where file is a
+demultiplexed mpeg video file.
+
+The "-s" option must be used for multiplexed (audio and video) mpeg
+files using the "program stream" format. These files are usualy found
+on the internet or on unencrypted DVDs.
+
+The "-t" option must be used for multiplexed (audio and video) mpeg
+files using the "transport stream" format. These files are usualy
+found in digital TV applications.
+
+The "-o" option is used to select a given output module - for example
+to redirect the output to a file. This is also used for performance
+testing and conformance testing.
+
+The "-c" option is used to disable all optimizations.
+
+
+OTHER PROJECTS USING LIBMPEG2
+
+libmpeg2 is being used by various other projects, including:
+
+ * xine (http://xine.sourceforge.net/) - started as a simple
+ mpeg-2 audio and video decoder, but it since became a
+ full-featured DVD and video media player.
+
+ * VideoLAN (http://www.videolan.org/) - video streaming over an
+ ethernet network, can also be used as a standalone player.
+
+ * MPlayer (http://www.MPlayerHQ.hu) - another good player, it is
+ also very robust against damaged streams.
+
+ * movietime (http://movietime.sourceforge.net/) - still quite
+ young, but it looks very promising !
+
+ * mpeg2decX (http://homepage1.nifty.com/~toku/software_en.html) -
+ a graphical interface for mpeg2dec for macintosh osX.
+
+ * TCVP (http://tcvp.sf.net) - video and music player for unix.
+
+ * drip (http://drip.sourceforge.net/) - a DVD to DIVX transcoder.
+
+ * PoMP
+ (http://www.dmclab.hanyang.ac.kr/research/project/PoDS/PoDS_sw.htm) -
+ a research player optimized to minimize disk power consumption.
+
+ * OMS (http://www.linuxvideo.org/oms/)
+
+ * XMPS (http://xmps.sourceforge.net/)
+
+ * GStreamer (http://www.gstreamer.net/) - a framework for
+ streaming media; it has an mpeg2 decoding plugin based on
+ libmpeg2.
+
+ * mpeglib (http://mpeglib.sourceforge.net/) - a video decoding
+ library that usess libmpeg2 when decoding mpeg streams.
+
+ * daphne (http://daphne.rulecity.com/) - a laserdisc arcade game
+ simulator.
+
+ * GOPchop (http://outflux.net/unix/software/GOPchop/) - a
+ GOP-accurate editor for MPEG2 streams.
+
+If you use libmpeg2 in another project, let us know !
+
+
+TASKS
+
+There are several places where we could easily use some help:
+
+ * Documentation: libmpeg2 still has no documentation. Every
+ project using it has had to figure things out by looking at
+ the header files, at the mpeg2dec sample application, and by
+ asking questions. Writing down a nice documentation would make
+ the code more easily reuseable.
+
+ * Testing: If you find any stream that does not decode right
+ with libmpeg2, let us know ! The best thing would be to mail
+ to the libmpeg2-devel mailing list. Also, it would be nice to
+ build a stress test so we can make sure libmpeg2 never crashes
+ on bad streams.
+
+ * Coding: There is a small TODO list in the mpeg2dec package,
+ you can have a look there ! Most items are pretty terse
+ though.
+
+ * Porting: If you're porting to a new architecture, you might
+ want to experiment with the compile flags defined in
+ configure.in . When you figure out whats fastest on your
+ platform, send us a patch !
+
+ * Assembly optimizations: We only have x86 and altivec
+ optimizations yet, it would be worthwhile writing routines for
+ other architectures, especially those that have SIMD
+ instruction set extensions ! Also the yuv2rgb x86 routines
+ could probably be optimized a lot.
+
+
+CVS SNAPSHOTS
+
+A daily snapshot is created using "make distcheck" every night and
+uploaded to http://libmpeg2.sourceforge.net/files/mpeg2dec-snapshot.tar.gz .
+It is easier to use than the CVS repository, because you do not need
+to have the right versions of automake, autoconf and libtool
+installed. It might be convenient when working on a libmpeg2 port for
+example.
+
+
+CVS REPOSITORY
+
+The latest libmpeg2 and mpeg2dec source code can always be found by
+anonymous CVS:
+
+# export CVSROOT=:pserver:anonymous@cvs.libmpeg2.sourceforge.net:/cvsroot/libmpeg2
+# cvs login (Just press Return when prompted for a password)
+# cvs checkout mpeg2dec
+
+You can also browse the latest changes online at
+http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/libmpeg2/mpeg2dec/
+
+The other CVS modules are mpeg2dec-streams for the test suite, and
+mpeg2dec-livid for the CVS history of the project while it was still
+hosted on the linuxvideo.org servers.
+
+
+MAILING LISTS
+
+See the subscription information at http://libmpeg2.sourceforge.net/lists.html
+
+libmpeg2-devel
+
+This is the main mailing list for technical discussion about
+libmpeg2. Anyone wanting to work on libmpeg2, or maybe just stay
+informed about the development process, should probably subscribe to
+this list.
+
+libmpeg2-checkins
+
+All libmpeg2 checkins are announced there. This is a good way to keep
+track of what goes into CVS.
+
+libmpeg2-announce
+
+This is a very low traffic mailing list, only for announcements of new
+versions of libmpeg2. Only project administrators can post there.
diff --git a/apps/plugins/mpegplayer/libmpeg2/README.rockbox b/apps/plugins/mpegplayer/libmpeg2/README.rockbox
new file mode 100644
index 0000000000..95f5a3d4b5
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/README.rockbox
@@ -0,0 +1,44 @@
+Library: libmpeg2 from mpeg2dec-0.4.0b (Released 2004-01-21)
+Imported: 2006-08-06 by Dave Chapman
+
+
+This directory contains a local version of libmpeg2 imported into
+Rockbox for MPEG video decoding.
+
+
+LICENSING INFORMATION
+
+mpeg2dec and libmpeg2 are licensed under Version 2 of the GNU General
+Public License.
+
+
+IMPORT DETAILS
+
+The following files were imported from the mpeg2dec-0.4.0b
+distribution. Minor changes were made to enable compilation in
+Rockbox and TABs were replaced by spaces to comply with the Rockbox
+coding guidelines.
+
+AUTHORS
+README
+SOURCES
+attributes.h
+cpu_accel.c
+cpu_state.c
+decode.c
+header.c
+idct.c
+motion_comp.c
+mpeg2.h
+mpeg2_internal.h
+slice.c
+video_out.h
+vlc.h
+
+The following files are new, but based on code in mpeg2dec.
+
+Makefile
+mpegplayer.c
+video_out_rockbox.c
+mpeg2dec_config.h
+alloc.c
diff --git a/apps/plugins/mpegplayer/libmpeg2/attributes.h b/apps/plugins/mpegplayer/libmpeg2/attributes.h
new file mode 100644
index 0000000000..24b069223b
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/attributes.h
@@ -0,0 +1,42 @@
+/*
+ * attributes.h
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.5
+ */
+
+/* use gcc attribs to align critical data structures */
+#ifdef ATTRIBUTE_ALIGNED_MAX
+#define ATTR_ALIGN(align) __attribute__ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX < align) ? ATTRIBUTE_ALIGNED_MAX : align)))
+#else
+#define ATTR_ALIGN(align)
+#endif
+
+#if defined(LIKELY) && defined (UNLIKELY)
+#define likely(x) LIKELY(x)
+#define unlikely(x) UNLIKELY(x)
+#else
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif
+
diff --git a/apps/plugins/mpegplayer/libmpeg2/decode.c b/apps/plugins/mpegplayer/libmpeg2/decode.c
new file mode 100644
index 0000000000..9c8081efbe
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/decode.c
@@ -0,0 +1,527 @@
+/*
+ * decode.c
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.114
+ */
+
+#include "plugin.h"
+
+#include "mpeg2dec_config.h"
+
+#include "mpeg2.h"
+#include "attributes.h"
+#include "mpeg2_internal.h"
+
+#define BUFFER_SIZE (1194 * 1024)
+
+#if defined(CPU_COLDFIRE) || (defined(CPU_ARM) && ARM_ARCH >= 6)
+/* twice as large as on other targets because coldfire uses
+ * a secondary, transposed buffer for optimisation */
+static int16_t static_dct_block[128] IBSS_ATTR ATTR_ALIGN(16);
+#define DCT_BLOCKSIZE (128 * sizeof (int16_t))
+#else
+static int16_t static_dct_block[64] IBSS_ATTR ATTR_ALIGN(16);
+#define DCT_BLOCKSIZE (64 * sizeof (int16_t))
+#endif
+
+const mpeg2_info_t * mpeg2_info (mpeg2dec_t * mpeg2dec)
+{
+ return &mpeg2dec->info;
+}
+
+static inline int skip_chunk (mpeg2dec_t * mpeg2dec, int bytes)
+{
+ uint8_t * current;
+ uint32_t shift;
+ uint8_t * limit;
+ uint8_t byte;
+
+ if (!bytes)
+ return 0;
+
+ current = mpeg2dec->buf_start;
+ shift = mpeg2dec->shift;
+ limit = current + bytes;
+
+ do
+ {
+ byte = *current++;
+
+ if (shift == 0x00000100)
+ {
+ int skipped;
+
+ mpeg2dec->shift = 0xffffff00;
+ skipped = current - mpeg2dec->buf_start;
+ mpeg2dec->buf_start = current;
+
+ return skipped;
+ }
+
+ shift = (shift | byte) << 8;
+ }
+ while (current < limit);
+
+ mpeg2dec->shift = shift;
+ mpeg2dec->buf_start = current;
+
+ return 0;
+}
+
+static inline int copy_chunk (mpeg2dec_t * mpeg2dec, int bytes)
+{
+ uint8_t * current;
+ uint32_t shift;
+ uint8_t * chunk_ptr;
+ uint8_t * limit;
+ uint8_t byte;
+
+ if (!bytes)
+ return 0;
+
+ current = mpeg2dec->buf_start;
+ shift = mpeg2dec->shift;
+ chunk_ptr = mpeg2dec->chunk_ptr;
+ limit = current + bytes;
+
+ do
+ {
+ byte = *current++;
+
+ if (shift == 0x00000100)
+ {
+ int copied;
+
+ mpeg2dec->shift = 0xffffff00;
+ mpeg2dec->chunk_ptr = chunk_ptr + 1;
+ copied = current - mpeg2dec->buf_start;
+ mpeg2dec->buf_start = current;
+ return copied;
+ }
+
+ shift = (shift | byte) << 8;
+ *chunk_ptr++ = byte;
+ }
+ while (current < limit);
+
+ mpeg2dec->shift = shift;
+ mpeg2dec->buf_start = current;
+ return 0;
+}
+
+void mpeg2_buffer (mpeg2dec_t * mpeg2dec, uint8_t * start, uint8_t * end)
+{
+ mpeg2dec->buf_start = start;
+ mpeg2dec->buf_end = end;
+}
+
+int mpeg2_getpos (mpeg2dec_t * mpeg2dec)
+{
+ return mpeg2dec->buf_end - mpeg2dec->buf_start;
+}
+
+static inline mpeg2_state_t seek_chunk (mpeg2dec_t * mpeg2dec)
+{
+ int size, skipped;
+
+ size = mpeg2dec->buf_end - mpeg2dec->buf_start;
+ skipped = skip_chunk (mpeg2dec, size);
+
+ if (!skipped)
+ {
+ mpeg2dec->bytes_since_tag += size;
+ return STATE_BUFFER;
+ }
+
+ mpeg2dec->bytes_since_tag += skipped;
+ mpeg2dec->code = mpeg2dec->buf_start[-1];
+
+ return STATE_INTERNAL_NORETURN;
+}
+
+mpeg2_state_t mpeg2_seek_header (mpeg2dec_t * mpeg2dec)
+{
+ while (!(mpeg2dec->code == 0xb3 ||
+ ((mpeg2dec->code == 0xb7 || mpeg2dec->code == 0xb8 ||
+ !mpeg2dec->code) && mpeg2dec->sequence.width != (unsigned)-1)))
+ {
+ if (seek_chunk (mpeg2dec) == STATE_BUFFER)
+ return STATE_BUFFER;
+ }
+
+ mpeg2dec->chunk_start =
+ mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer;
+
+ mpeg2dec->user_data_len = 0;
+
+ return ((mpeg2dec->code == 0xb7) ?
+ mpeg2_header_end(mpeg2dec) : mpeg2_parse_header(mpeg2dec));
+}
+
+#define RECEIVED(code,state) (((state) << 8) + (code))
+
+mpeg2_state_t mpeg2_parse (mpeg2dec_t * mpeg2dec)
+{
+ int size_buffer, size_chunk, copied;
+
+ if (mpeg2dec->action)
+ {
+ mpeg2_state_t state;
+
+ state = mpeg2dec->action (mpeg2dec);
+
+ if (state > STATE_INTERNAL_NORETURN)
+ return state;
+ }
+
+ while (1)
+ {
+ while ((unsigned) (mpeg2dec->code - mpeg2dec->first_decode_slice) <
+ mpeg2dec->nb_decode_slices)
+ {
+ size_buffer = mpeg2dec->buf_end - mpeg2dec->buf_start;
+ size_chunk = mpeg2dec->chunk_buffer + BUFFER_SIZE -
+ mpeg2dec->chunk_ptr;
+
+ if (size_buffer <= size_chunk)
+ {
+ copied = copy_chunk (mpeg2dec, size_buffer);
+
+ if (!copied)
+ {
+ mpeg2dec->bytes_since_tag += size_buffer;
+ mpeg2dec->chunk_ptr += size_buffer;
+ return STATE_BUFFER;
+ }
+ }
+ else
+ {
+ copied = copy_chunk (mpeg2dec, size_chunk);
+
+ if (!copied)
+ {
+ /* filled the chunk buffer without finding a start code */
+ mpeg2dec->bytes_since_tag += size_chunk;
+ mpeg2dec->action = seek_chunk;
+ return STATE_INVALID;
+ }
+ }
+
+ mpeg2dec->bytes_since_tag += copied;
+
+ mpeg2_slice (&mpeg2dec->decoder, mpeg2dec->code,
+ mpeg2dec->chunk_start);
+ mpeg2dec->code = mpeg2dec->buf_start[-1];
+ mpeg2dec->chunk_ptr = mpeg2dec->chunk_start;
+ }
+
+ if ((unsigned) (mpeg2dec->code - 1) >= 0xb0 - 1)
+ break;
+
+ if (seek_chunk (mpeg2dec) == STATE_BUFFER)
+ return STATE_BUFFER;
+ }
+
+ mpeg2dec->action = mpeg2_seek_header;
+
+ switch (mpeg2dec->code)
+ {
+ case 0x00:
+ return mpeg2dec->state;
+ case 0xb3:
+ case 0xb7:
+ case 0xb8:
+ return (mpeg2dec->state == STATE_SLICE) ? STATE_SLICE : STATE_INVALID;
+ default:
+ mpeg2dec->action = seek_chunk;
+ return STATE_INVALID;
+ }
+}
+
+mpeg2_state_t mpeg2_parse_header (mpeg2dec_t * mpeg2dec)
+{
+ static int (* const process_header[9]) (mpeg2dec_t *) =
+ {
+ mpeg2_header_picture,
+ mpeg2_header_extension,
+ mpeg2_header_user_data,
+ mpeg2_header_sequence,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ mpeg2_header_gop
+ };
+
+ int size_buffer, size_chunk, copied;
+
+ mpeg2dec->action = mpeg2_parse_header;
+ mpeg2dec->info.user_data = NULL;
+ mpeg2dec->info.user_data_len = 0;
+
+ while (1)
+ {
+ size_buffer = mpeg2dec->buf_end - mpeg2dec->buf_start;
+ size_chunk = mpeg2dec->chunk_buffer + BUFFER_SIZE -
+ mpeg2dec->chunk_ptr;
+
+ if (size_buffer <= size_chunk)
+ {
+ copied = copy_chunk (mpeg2dec, size_buffer);
+
+ if (!copied)
+ {
+ mpeg2dec->bytes_since_tag += size_buffer;
+ mpeg2dec->chunk_ptr += size_buffer;
+ return STATE_BUFFER;
+ }
+ }
+ else
+ {
+ copied = copy_chunk (mpeg2dec, size_chunk);
+
+ if (!copied)
+ {
+ /* filled the chunk buffer without finding a start code */
+ mpeg2dec->bytes_since_tag += size_chunk;
+ mpeg2dec->code = 0xb4;
+ mpeg2dec->action = mpeg2_seek_header;
+ return STATE_INVALID;
+ }
+ }
+
+ mpeg2dec->bytes_since_tag += copied;
+
+ if (process_header[mpeg2dec->code & 0x0b] (mpeg2dec))
+ {
+ mpeg2dec->code = mpeg2dec->buf_start[-1];
+ mpeg2dec->action = mpeg2_seek_header;
+ return STATE_INVALID;
+ }
+
+ mpeg2dec->code = mpeg2dec->buf_start[-1];
+
+ switch (RECEIVED (mpeg2dec->code, mpeg2dec->state))
+ {
+ /* state transition after a sequence header */
+ case RECEIVED (0x00, STATE_SEQUENCE):
+ case RECEIVED (0xb8, STATE_SEQUENCE):
+ mpeg2_header_sequence_finalize (mpeg2dec);
+ break;
+
+ /* other legal state transitions */
+ case RECEIVED (0x00, STATE_GOP):
+ mpeg2_header_gop_finalize (mpeg2dec);
+ break;
+ case RECEIVED (0x01, STATE_PICTURE):
+ case RECEIVED (0x01, STATE_PICTURE_2ND):
+ mpeg2_header_picture_finalize (mpeg2dec);
+ mpeg2dec->action = mpeg2_header_slice_start;
+ break;
+
+ /* legal headers within a given state */
+ case RECEIVED (0xb2, STATE_SEQUENCE):
+ case RECEIVED (0xb2, STATE_GOP):
+ case RECEIVED (0xb2, STATE_PICTURE):
+ case RECEIVED (0xb2, STATE_PICTURE_2ND):
+ case RECEIVED (0xb5, STATE_SEQUENCE):
+ case RECEIVED (0xb5, STATE_PICTURE):
+ case RECEIVED (0xb5, STATE_PICTURE_2ND):
+ mpeg2dec->chunk_ptr = mpeg2dec->chunk_start;
+ continue;
+
+ default:
+ mpeg2dec->action = mpeg2_seek_header;
+ return STATE_INVALID;
+ }
+
+ mpeg2dec->chunk_start = mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer;
+ mpeg2dec->user_data_len = 0;
+
+ return mpeg2dec->state;
+ }
+}
+
+int mpeg2_convert (mpeg2dec_t * mpeg2dec, mpeg2_convert_t convert, void * arg)
+{
+ mpeg2_convert_init_t convert_init;
+ int error;
+
+ error = convert (MPEG2_CONVERT_SET, NULL, &mpeg2dec->sequence, 0,
+ arg, &convert_init);
+
+ if (!error)
+ {
+ mpeg2dec->convert = convert;
+ mpeg2dec->convert_arg = arg;
+ mpeg2dec->convert_id_size = convert_init.id_size;
+ mpeg2dec->convert_stride = 0;
+ }
+
+ return error;
+}
+
+int mpeg2_stride (mpeg2dec_t * mpeg2dec, int stride)
+{
+ if (!mpeg2dec->convert)
+ {
+ if (stride < (int) mpeg2dec->sequence.width)
+ stride = mpeg2dec->sequence.width;
+
+ mpeg2dec->decoder.stride_frame = stride;
+ }
+ else
+ {
+ mpeg2_convert_init_t convert_init;
+
+ stride = mpeg2dec->convert(MPEG2_CONVERT_STRIDE, NULL,
+ &mpeg2dec->sequence, stride,
+ mpeg2dec->convert_arg,
+ &convert_init);
+
+ mpeg2dec->convert_id_size = convert_init.id_size;
+ mpeg2dec->convert_stride = stride;
+ }
+
+ return stride;
+}
+
+void mpeg2_set_buf (mpeg2dec_t * mpeg2dec, uint8_t * buf[MPEG2_COMPONENTS], void * id)
+{
+ mpeg2_fbuf_t * fbuf;
+
+ if (mpeg2dec->custom_fbuf)
+ {
+ if (mpeg2dec->state == STATE_SEQUENCE)
+ {
+ mpeg2dec->fbuf[2] = mpeg2dec->fbuf[1];
+ mpeg2dec->fbuf[1] = mpeg2dec->fbuf[0];
+ }
+
+ mpeg2_set_fbuf (mpeg2dec, (mpeg2dec->decoder.coding_type ==
+ PIC_FLAG_CODING_TYPE_B));
+
+ fbuf = mpeg2dec->fbuf[0];
+ }
+ else
+ {
+ fbuf = &mpeg2dec->fbuf_alloc[mpeg2dec->alloc_index].fbuf;
+ mpeg2dec->alloc_index_user = ++mpeg2dec->alloc_index;
+ }
+
+ fbuf->buf[0] = buf[0];
+#if MPEG2_COLOR
+ fbuf->buf[1] = buf[1];
+ fbuf->buf[2] = buf[2];
+#endif
+
+ fbuf->id = id;
+}
+
+void mpeg2_custom_fbuf (mpeg2dec_t * mpeg2dec, int custom_fbuf)
+{
+ mpeg2dec->custom_fbuf = custom_fbuf;
+}
+
+void mpeg2_skip (mpeg2dec_t * mpeg2dec, int skip)
+{
+ mpeg2dec->first_decode_slice = 1;
+ mpeg2dec->nb_decode_slices = skip ? 0 : (0xb0 - 1);
+}
+
+void mpeg2_slice_region (mpeg2dec_t * mpeg2dec, int start, int end)
+{
+ start = (start < 1) ? 1 : (start > 0xb0) ? 0xb0 : start;
+ end = (end < start) ? start : (end > 0xb0) ? 0xb0 : end;
+ mpeg2dec->first_decode_slice = start;
+ mpeg2dec->nb_decode_slices = end - start;
+}
+
+void mpeg2_tag_picture (mpeg2dec_t * mpeg2dec, uint32_t tag, uint32_t tag2)
+{
+ mpeg2dec->tag_previous = mpeg2dec->tag_current;
+ mpeg2dec->tag2_previous = mpeg2dec->tag2_current;
+ mpeg2dec->tag_current = tag;
+ mpeg2dec->tag2_current = tag2;
+ mpeg2dec->num_tags++;
+ mpeg2dec->bytes_since_tag = 0;
+}
+
+void mpeg2_reset (mpeg2dec_t * mpeg2dec, int full_reset)
+{
+ mpeg2dec->buf_start = mpeg2dec->buf_end = NULL;
+ mpeg2dec->num_tags = 0;
+ mpeg2dec->shift = 0xffffff00;
+ mpeg2dec->code = 0xb4;
+ mpeg2dec->action = mpeg2_seek_header;
+ mpeg2dec->state = STATE_INVALID;
+ mpeg2dec->first = 1;
+
+ mpeg2_reset_info(&mpeg2dec->info);
+ mpeg2dec->info.gop = NULL;
+ mpeg2dec->info.user_data = NULL;
+ mpeg2dec->info.user_data_len = 0;
+
+ if (full_reset)
+ {
+ mpeg2dec->info.sequence = NULL;
+ mpeg2_header_state_init (mpeg2dec);
+ }
+}
+
+mpeg2dec_t * mpeg2_init (void)
+{
+ mpeg2dec_t * mpeg2dec;
+
+ mpeg2_idct_init ();
+
+ mpeg2dec = (mpeg2dec_t *)mpeg2_bufalloc(sizeof (mpeg2dec_t),
+ MPEG2_ALLOC_MPEG2DEC);
+ if (mpeg2dec == NULL)
+ return NULL;
+
+ mpeg2dec->decoder.DCTblock = static_dct_block;
+
+ rb->memset (mpeg2dec->decoder.DCTblock, 0, DCT_BLOCKSIZE);
+
+ DEBUGF("DCTblock: %p\n", mpeg2dec->decoder.DCTblock);
+
+ mpeg2dec->chunk_buffer = (uint8_t *)mpeg2_bufalloc(BUFFER_SIZE + 4,
+ MPEG2_ALLOC_CHUNK);
+
+ mpeg2dec->sequence.width = (unsigned)-1;
+ mpeg2_reset (mpeg2dec, 1);
+
+ return mpeg2dec;
+}
+
+void mpeg2_close (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2_header_state_init (mpeg2dec);
+#if 0
+ /* These are dedicated buffers in rockbox */
+ mpeg2_free (mpeg2dec->chunk_buffer);
+ mpeg2_free (mpeg2dec);
+#endif
+}
diff --git a/apps/plugins/mpegplayer/libmpeg2/header.c b/apps/plugins/mpegplayer/libmpeg2/header.c
new file mode 100644
index 0000000000..b40193a338
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/header.c
@@ -0,0 +1,1287 @@
+/*
+ * header.c
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 2003 Regis Duchesne <hpreg@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.101
+ */
+
+#include "plugin.h"
+
+#include "mpeg2dec_config.h"
+
+#include "mpeg2.h"
+#include "attributes.h"
+#include "mpeg2_internal.h"
+
+#define SEQ_EXT 2
+#define SEQ_DISPLAY_EXT 4
+#define QUANT_MATRIX_EXT 8
+#define COPYRIGHT_EXT 0x10
+#define PIC_DISPLAY_EXT 0x80
+#define PIC_CODING_EXT 0x100
+
+/* default intra quant matrix, in zig-zag order */
+static const uint8_t default_intra_quantizer_matrix[64] =
+{
+ 8,
+ 16, 16,
+ 19, 16, 19,
+ 22, 22, 22, 22,
+ 22, 22, 26, 24, 26,
+ 27, 27, 27, 26, 26, 26,
+ 26, 27, 27, 27, 29, 29, 29,
+ 34, 34, 34, 29, 29, 29, 27, 27,
+ 29, 29, 32, 32, 34, 34, 37,
+ 38, 37, 35, 35, 34, 35,
+ 38, 38, 40, 40, 40,
+ 48, 48, 46, 46,
+ 56, 56, 58,
+ 69, 69,
+ 83
+};
+
+const uint8_t default_mpeg2_scan_norm[64] =
+{
+ /* Zig-Zag scan pattern */
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+};
+
+const uint8_t default_mpeg2_scan_alt[64] =
+{
+ /* Alternate scan pattern */
+ 0, 8, 16, 24, 1, 9, 2, 10,
+ 17, 25, 32, 40, 48, 56, 57, 49,
+ 41, 33, 26, 18, 3, 11, 4, 12,
+ 19, 27, 34, 42, 50, 58, 35, 43,
+ 51, 59, 20, 28, 5, 13, 6, 14,
+ 21, 29, 36, 44, 52, 60, 37, 45,
+ 53, 61, 22, 30, 7, 15, 23, 31,
+ 38, 46, 54, 62, 39, 47, 55, 63
+};
+
+uint8_t mpeg2_scan_norm[64] IDATA_ATTR;
+uint8_t mpeg2_scan_alt[64] IDATA_ATTR;
+
+void mpeg2_header_state_init (mpeg2dec_t * mpeg2dec)
+{
+ if (mpeg2dec->sequence.width != (unsigned)-1)
+ {
+ mpeg2dec->sequence.width = (unsigned)-1;
+ mpeg2_mem_reset(); /* Clean the memory slate */
+#if 0
+ if (!mpeg2dec->custom_fbuf)
+ {
+ int i;
+ for (i = mpeg2dec->alloc_index_user;
+ i < mpeg2dec->alloc_index; i++)
+ {
+ mpeg2_free(mpeg2dec->fbuf_alloc[i].fbuf.buf[0]);
+#if MPEG2_COLOR
+ mpeg2_free(mpeg2dec->fbuf_alloc[i].fbuf.buf[1]);
+ mpeg2_free(mpeg2dec->fbuf_alloc[i].fbuf.buf[2]);
+#endif
+ }
+ }
+
+ if (mpeg2dec->convert_start)
+ {
+ int i;
+ for (i = 0; i < 3; i++)
+ {
+ mpeg2_free(mpeg2dec->yuv_buf[i][0]);
+#if MPEG2_COLOR
+ mpeg2_free(mpeg2dec->yuv_buf[i][1]);
+ mpeg2_free(mpeg2dec->yuv_buf[i][2]);
+#endif
+ }
+ }
+
+ if (mpeg2dec->decoder.convert_id)
+ {
+ mpeg2_free(mpeg2dec->decoder.convert_id);
+ }
+#endif
+ }
+
+ mpeg2dec->decoder.coding_type = I_TYPE;
+ mpeg2dec->decoder.convert = NULL;
+ mpeg2dec->decoder.convert_id = NULL;
+
+ mpeg2dec->picture = mpeg2dec->pictures;
+
+ mpeg2dec->fbuf[0] = &mpeg2dec->fbuf_alloc[0].fbuf;
+ mpeg2dec->fbuf[1] = &mpeg2dec->fbuf_alloc[1].fbuf;
+ mpeg2dec->fbuf[2] = &mpeg2dec->fbuf_alloc[2].fbuf;
+
+ mpeg2dec->first = 1;
+ mpeg2dec->alloc_index = 0;
+ mpeg2dec->alloc_index_user = 0;
+ mpeg2dec->first_decode_slice = 1;
+ mpeg2dec->nb_decode_slices = 0xb0 - 1;
+ mpeg2dec->convert = NULL;
+ mpeg2dec->convert_start = NULL;
+ mpeg2dec->custom_fbuf = 0;
+ mpeg2dec->yuv_index = 0;
+}
+
+void mpeg2_reset_info (mpeg2_info_t * info)
+{
+ info->current_picture =
+ info->current_picture_2nd = NULL;
+
+ info->display_picture =
+ info->display_picture_2nd = NULL;
+
+ info->current_fbuf =
+ info->display_fbuf =
+ info->discard_fbuf = NULL;
+}
+
+static void info_user_data (mpeg2dec_t * mpeg2dec)
+{
+ if (mpeg2dec->user_data_len)
+ {
+ mpeg2dec->info.user_data = mpeg2dec->chunk_buffer;
+ mpeg2dec->info.user_data_len = mpeg2dec->user_data_len - 3;
+ }
+}
+
+int mpeg2_header_sequence (mpeg2dec_t * mpeg2dec)
+{
+ static const unsigned int frame_period[16] =
+ {
+ 0, 1126125, 1125000, 1080000, 900900, 900000, 540000, 450450, 450000,
+ /* unofficial: xing 15 fps */
+ 1800000,
+ /* unofficial: libmpeg3 "Unofficial economy rates" 5/10/12/15 fps */
+ 5400000, 2700000, 2250000, 1800000, 0, 0
+ };
+
+ uint8_t * buffer = mpeg2dec->chunk_start;
+ mpeg2_sequence_t * sequence = &mpeg2dec->new_sequence;
+ int i;
+
+ if ((buffer[6] & 0x20) != 0x20) /* missing marker_bit */
+ return 1;
+
+ i = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
+
+ if (!(sequence->display_width = sequence->picture_width = i >> 12))
+ return 1;
+
+ if (!(sequence->display_height = sequence->picture_height = i & 0xfff))
+ return 1;
+
+ sequence->width = (sequence->picture_width + 15) & ~15;
+ sequence->height = (sequence->picture_height + 15) & ~15;
+ sequence->chroma_width = sequence->width >> 1;
+ sequence->chroma_height = sequence->height >> 1;
+
+ sequence->flags = SEQ_FLAG_PROGRESSIVE_SEQUENCE |
+ SEQ_VIDEO_FORMAT_UNSPECIFIED;
+
+ sequence->pixel_width = buffer[3] >> 4; /* aspect ratio */
+ sequence->frame_period = frame_period[buffer[3] & 15];
+
+ sequence->byte_rate = (buffer[4]<<10) | (buffer[5]<<2) | (buffer[6]>>6);
+
+ sequence->vbv_buffer_size = ((buffer[6]<<16) | (buffer[7]<<8)) & 0x1ff800;
+
+ if (buffer[7] & 4)
+ sequence->flags |= SEQ_FLAG_CONSTRAINED_PARAMETERS;
+
+ mpeg2dec->copy_matrix = 3;
+
+ if (buffer[7] & 2)
+ {
+ for (i = 0; i < 64; i++)
+ {
+ mpeg2dec->new_quantizer_matrix[0][mpeg2_scan_norm[i]] =
+ (buffer[i+7] << 7) | (buffer[i+8] >> 1);
+ }
+
+ buffer += 64;
+ }
+ else
+ {
+ for (i = 0; i < 64; i++)
+ {
+ mpeg2dec->new_quantizer_matrix[0][mpeg2_scan_norm[i]] =
+ default_intra_quantizer_matrix[i];
+ }
+ }
+
+ if (buffer[7] & 1)
+ {
+ for (i = 0; i < 64; i++)
+ {
+ mpeg2dec->new_quantizer_matrix[1][mpeg2_scan_norm[i]] =
+ buffer[i+8];
+ }
+ }
+ else
+ {
+ rb->memset (mpeg2dec->new_quantizer_matrix[1], 16, 64);
+ }
+
+ sequence->profile_level_id = 0x80;
+ sequence->colour_primaries = 0;
+ sequence->transfer_characteristics = 0;
+ sequence->matrix_coefficients = 0;
+
+ mpeg2dec->ext_state = SEQ_EXT;
+ mpeg2dec->state = STATE_SEQUENCE;
+
+ mpeg2dec->display_offset_x =
+ mpeg2dec->display_offset_y = 0;
+
+ return 0;
+}
+
+static int sequence_ext (mpeg2dec_t * mpeg2dec)
+{
+ uint8_t * buffer = mpeg2dec->chunk_start;
+ mpeg2_sequence_t * sequence = &mpeg2dec->new_sequence;
+ uint32_t flags;
+
+ if (!(buffer[3] & 1))
+ return 1;
+
+ sequence->profile_level_id = (buffer[0] << 4) | (buffer[1] >> 4);
+
+ sequence->picture_width += ((buffer[1] << 13) | (buffer[2] << 5)) & 0x3000;
+ sequence->display_width = sequence->picture_width;
+
+ sequence->picture_height += (buffer[2] << 7) & 0x3000;
+ sequence->display_height = sequence->picture_height;
+
+ sequence->width = (sequence->picture_width + 15) & ~15;
+ sequence->height = (sequence->picture_height + 15) & ~15;
+
+ flags = sequence->flags | SEQ_FLAG_MPEG2;
+
+ if (!(buffer[1] & 8))
+ {
+ flags &= ~SEQ_FLAG_PROGRESSIVE_SEQUENCE;
+ sequence->height = (sequence->height + 31) & ~31;
+ }
+
+ if (buffer[5] & 0x80)
+ flags |= SEQ_FLAG_LOW_DELAY;
+
+ sequence->flags = flags;
+ sequence->chroma_width = sequence->width;
+ sequence->chroma_height = sequence->height;
+
+ switch (buffer[1] & 6)
+ {
+ case 0: /* invalid */
+ return 1;
+ case 2: /* 4:2:0 */
+ sequence->chroma_height >>= 1;
+ /* fallthrough */
+ case 4: /* 4:2:2 */
+ sequence->chroma_width >>= 1;
+ }
+
+ sequence->byte_rate += ((buffer[2]<<25) | (buffer[3]<<17)) & 0x3ffc0000;
+
+ sequence->vbv_buffer_size |= buffer[4] << 21;
+
+ sequence->frame_period =
+ sequence->frame_period * ((buffer[5]&31)+1) / (((buffer[5]>>2)&3)+1);
+
+ mpeg2dec->ext_state = SEQ_DISPLAY_EXT;
+
+ return 0;
+}
+
+static int sequence_display_ext (mpeg2dec_t * mpeg2dec)
+{
+ uint8_t * buffer = mpeg2dec->chunk_start;
+ mpeg2_sequence_t * sequence = &mpeg2dec->new_sequence;
+ int x;
+
+ sequence->flags = (sequence->flags & ~SEQ_MASK_VIDEO_FORMAT) |
+ ((buffer[0] << 4) & SEQ_MASK_VIDEO_FORMAT);
+
+ if (buffer[0] & 1)
+ {
+ sequence->flags |= SEQ_FLAG_COLOUR_DESCRIPTION;
+ sequence->colour_primaries = buffer[1];
+ sequence->transfer_characteristics = buffer[2];
+ sequence->matrix_coefficients = buffer[3];
+ buffer += 3;
+ }
+
+ if (!(buffer[2] & 2)) /* missing marker_bit */
+ return 1;
+
+ x = (buffer[1] << 6) | (buffer[2] >> 2);
+ if (x)
+ sequence->display_width = x;
+
+ x = ((buffer[2] & 1) << 13) | (buffer[3] << 5) | (buffer[4] >> 3);
+ if (x)
+ sequence->display_height = x;
+
+ return 0;
+}
+
+static inline void simplify (unsigned int * u, unsigned int * v)
+{
+ unsigned int a, b, tmp;
+
+ a = *u;
+ b = *v;
+
+ /* find greatest common divisor */
+ while (a)
+ {
+ tmp = a;
+ a = b % tmp;
+ b = tmp;
+ }
+
+ *u /= b;
+ *v /= b;
+}
+
+static inline void finalize_sequence (mpeg2_sequence_t * sequence)
+{
+ int width;
+ int height;
+
+ sequence->byte_rate *= 50;
+
+ if (sequence->flags & SEQ_FLAG_MPEG2)
+ {
+ switch (sequence->pixel_width)
+ {
+ case 1: /* square pixels */
+ sequence->pixel_width =
+ sequence->pixel_height = 1;
+ return;
+ case 2: /* 4:3 aspect ratio */
+ width = 4;
+ height = 3;
+ break;
+ case 3: /* 16:9 aspect ratio */
+ width = 16;
+ height = 9;
+ break;
+ case 4: /* 2.21:1 aspect ratio */
+ width = 221;
+ height = 100;
+ break;
+ default: /* illegal */
+ sequence->pixel_width =
+ sequence->pixel_height = 0;
+ return;
+ }
+
+ width *= sequence->display_height;
+ height *= sequence->display_width;
+ }
+ else
+ {
+ if (sequence->byte_rate == 50 * 0x3ffff)
+ sequence->byte_rate = 0; /* mpeg-1 VBR */
+
+ switch (sequence->pixel_width)
+ {
+ case 0:
+ case 15: /* illegal */
+ sequence->pixel_width =
+ sequence->pixel_height = 0;
+ return;
+ case 1: /* square pixels */
+ sequence->pixel_width =
+ sequence->pixel_height = 1;
+ return;
+ case 3: /* 720x576 16:9 */
+ sequence->pixel_width = 64;
+ sequence->pixel_height = 45;
+ return;
+ case 6: /* 720x480 16:9 */
+ sequence->pixel_width = 32;
+ sequence->pixel_height = 27;
+ return;
+ case 8: /* BT.601 625 lines 4:3 */
+ sequence->pixel_width = 59;
+ sequence->pixel_height = 54;
+ return;
+ case 12: /* BT.601 525 lines 4:3 */
+ sequence->pixel_width = 10;
+ sequence->pixel_height = 11;
+ return;
+ default:
+ height = 88 * sequence->pixel_width + 1171;
+ width = 2000;
+ }
+ }
+
+ sequence->pixel_width = width;
+ sequence->pixel_height = height;
+
+ simplify(&sequence->pixel_width, &sequence->pixel_height);
+}
+
+int mpeg2_guess_aspect (const mpeg2_sequence_t * sequence,
+ unsigned int * pixel_width,
+ unsigned int * pixel_height)
+{
+ static const struct
+ {
+ unsigned int width, height;
+ } video_modes[] =
+ {
+ {720, 576}, /* 625 lines, 13.5 MHz (D1, DV, DVB, DVD) */
+ {704, 576}, /* 625 lines, 13.5 MHz (1/1 D1, DVB, DVD, 4CIF) */
+ {544, 576}, /* 625 lines, 10.125 MHz (DVB, laserdisc) */
+ {528, 576}, /* 625 lines, 10.125 MHz (3/4 D1, DVB, laserdisc) */
+ {480, 576}, /* 625 lines, 9 MHz (2/3 D1, DVB, SVCD) */
+ {352, 576}, /* 625 lines, 6.75 MHz (D2, 1/2 D1, CVD, DVB, DVD) */
+ {352, 288}, /* 625 lines, 6.75 MHz, 1 field (D4, VCD, DVB, DVD, CIF) */
+ {176, 144}, /* 625 lines, 3.375 MHz, half field (QCIF) */
+ {720, 486}, /* 525 lines, 13.5 MHz (D1) */
+ {704, 486}, /* 525 lines, 13.5 MHz */
+ {720, 480}, /* 525 lines, 13.5 MHz (DV, DSS, DVD) */
+ {704, 480}, /* 525 lines, 13.5 MHz (1/1 D1, ATSC, DVD) */
+ {544, 480}, /* 525 lines. 10.125 MHz (DSS, laserdisc) */
+ {528, 480}, /* 525 lines. 10.125 MHz (3/4 D1, laserdisc) */
+ {480, 480}, /* 525 lines, 9 MHz (2/3 D1, SVCD) */
+ {352, 480}, /* 525 lines, 6.75 MHz (D2, 1/2 D1, CVD, DVD) */
+ {352, 240} /* 525 lines. 6.75 MHz, 1 field (D4, VCD, DSS, DVD) */
+ };
+ unsigned int width, height, pix_width, pix_height, i, DAR_16_9;
+
+ *pixel_width = sequence->pixel_width;
+ *pixel_height = sequence->pixel_height;
+ width = sequence->picture_width;
+ height = sequence->picture_height;
+
+ for (i = 0; i < sizeof (video_modes) / sizeof (video_modes[0]); i++)
+ {
+ if (width == video_modes[i].width && height == video_modes[i].height)
+ break;
+ }
+
+ if (i == ARRAYLEN(video_modes) ||
+ (sequence->pixel_width == 1 && sequence->pixel_height == 1) ||
+ width != sequence->display_width || height != sequence->display_height)
+ {
+ return 0;
+ }
+
+ for (pix_height = 1; height * pix_height < 480; pix_height <<= 1);
+ height *= pix_height;
+
+ for (pix_width = 1; width * pix_width <= 352; pix_width <<= 1);
+ width *= pix_width;
+
+ if (!(sequence->flags & SEQ_FLAG_MPEG2))
+ {
+ static unsigned int mpeg1_check[2][2] = {{11, 54}, {27, 45}};
+ DAR_16_9 = (sequence->pixel_height == 27 ||
+ sequence->pixel_height == 45);
+ if (width < 704 ||
+ sequence->pixel_height != mpeg1_check[DAR_16_9][height == 576])
+ return 0;
+ }
+ else
+ {
+ DAR_16_9 = (3 * sequence->picture_width * sequence->pixel_width >
+ 4 * sequence->picture_height * sequence->pixel_height);
+ switch (width)
+ {
+ case 528:
+ case 544:
+ pix_width *= 4;
+ pix_height *= 3;
+ break;
+ case 480:
+ pix_width *= 3;
+ pix_height *= 2;
+ break;
+ }
+ }
+
+ if (DAR_16_9)
+ {
+ pix_width *= 4;
+ pix_height *= 3;
+ }
+
+ if (height == 576)
+ {
+ pix_width *= 59;
+ pix_height *= 54;
+ }
+ else
+ {
+ pix_width *= 10;
+ pix_height *= 11;
+ }
+
+ *pixel_width = pix_width;
+ *pixel_height = pix_height;
+
+ simplify (pixel_width, pixel_height);
+
+ return (height == 576) ? 1 : 2;
+}
+
+static void copy_matrix (mpeg2dec_t * mpeg2dec, int index)
+{
+ if (rb->memcmp (mpeg2dec->quantizer_matrix[index],
+ mpeg2dec->new_quantizer_matrix[index], 64))
+ {
+ rb->memcpy (mpeg2dec->quantizer_matrix[index],
+ mpeg2dec->new_quantizer_matrix[index], 64);
+
+ mpeg2dec->scaled[index] = -1;
+ }
+}
+
+static void finalize_matrix (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2_decoder_t *decoder = &mpeg2dec->decoder;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (mpeg2dec->copy_matrix & (1 << i))
+ copy_matrix (mpeg2dec, i);
+
+ if ((mpeg2dec->copy_matrix & (4 << i)) &&
+ rb->memcmp(mpeg2dec->quantizer_matrix[i],
+ mpeg2dec->new_quantizer_matrix[i+2], 64))
+ {
+ copy_matrix (mpeg2dec, i + 2);
+ decoder->chroma_quantizer[i] = decoder->quantizer_prescale[i+2];
+ }
+ else if (mpeg2dec->copy_matrix & (5 << i))
+ {
+ decoder->chroma_quantizer[i] = decoder->quantizer_prescale[i];
+ }
+ }
+}
+
+static mpeg2_state_t invalid_end_action (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2_reset_info (&mpeg2dec->info);
+
+ mpeg2dec->info.gop = NULL;
+
+ info_user_data (mpeg2dec);
+
+ mpeg2_header_state_init (mpeg2dec);
+
+ mpeg2dec->sequence = mpeg2dec->new_sequence;
+ mpeg2dec->action = mpeg2_seek_header;
+ mpeg2dec->state = STATE_SEQUENCE;
+
+ return STATE_SEQUENCE;
+}
+
+void mpeg2_header_sequence_finalize (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2_sequence_t * sequence = &mpeg2dec->new_sequence;
+ mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
+
+ finalize_sequence(sequence);
+ finalize_matrix(mpeg2dec);
+
+ decoder->mpeg1 = !(sequence->flags & SEQ_FLAG_MPEG2);
+ decoder->width = sequence->width;
+ decoder->height = sequence->height;
+ decoder->vertical_position_extension = sequence->picture_height > 2800;
+ decoder->chroma_format = (sequence->chroma_width == sequence->width) +
+ (sequence->chroma_height == sequence->height);
+
+ if (mpeg2dec->sequence.width != (unsigned)-1)
+ {
+ /*
+ * According to 6.1.1.6, repeat sequence headers should be
+ * identical to the original. However some encoders don't
+ * respect that and change various fields (including bitrate
+ * and aspect ratio) in the repeat sequence headers. So we
+ * choose to be as conservative as possible and only restart
+ * the decoder if the width, height, chroma_width,
+ * chroma_height or low_delay flag are modified.
+ */
+ if (sequence->width != mpeg2dec->sequence.width ||
+ sequence->height != mpeg2dec->sequence.height ||
+ sequence->chroma_width != mpeg2dec->sequence.chroma_width ||
+ sequence->chroma_height != mpeg2dec->sequence.chroma_height ||
+ ((sequence->flags ^ mpeg2dec->sequence.flags) &
+ SEQ_FLAG_LOW_DELAY))
+ {
+ decoder->stride_frame = sequence->width;
+ mpeg2_header_end (mpeg2dec);
+ mpeg2dec->action = invalid_end_action;
+ mpeg2dec->state = STATE_INVALID_END;
+ return;
+ }
+
+ mpeg2dec->state = rb->memcmp(&mpeg2dec->sequence, sequence,
+ sizeof (mpeg2_sequence_t)) ?
+ STATE_SEQUENCE_MODIFIED :
+ STATE_SEQUENCE_REPEATED;
+ }
+ else
+ {
+ decoder->stride_frame = sequence->width;
+ }
+
+ mpeg2dec->sequence = *sequence;
+ mpeg2_reset_info(&mpeg2dec->info);
+ mpeg2dec->info.sequence = &mpeg2dec->sequence;
+ mpeg2dec->info.gop = NULL;
+
+ info_user_data (mpeg2dec);
+}
+
+int mpeg2_header_gop (mpeg2dec_t * mpeg2dec)
+{
+ uint8_t * buffer = mpeg2dec->chunk_start;
+ mpeg2_gop_t * gop = &mpeg2dec->new_gop;
+
+ if (!(buffer[1] & 8))
+ return 1;
+
+ gop->hours = (buffer[0] >> 2) & 31;
+ gop->minutes = ((buffer[0] << 4) | (buffer[1] >> 4)) & 63;
+ gop->seconds = ((buffer[1] << 3) | (buffer[2] >> 5)) & 63;
+ gop->pictures = ((buffer[2] << 1) | (buffer[3] >> 7)) & 63;
+ gop->flags = (buffer[0] >> 7) | ((buffer[3] >> 4) & 6);
+
+ mpeg2dec->state = STATE_GOP;
+ return 0;
+}
+
+void mpeg2_header_gop_finalize (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2dec->gop = mpeg2dec->new_gop;
+ mpeg2_reset_info (&mpeg2dec->info);
+ mpeg2dec->info.gop = &mpeg2dec->gop;
+ info_user_data (mpeg2dec);
+}
+
+void mpeg2_set_fbuf (mpeg2dec_t * mpeg2dec, int b_type)
+{
+ int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ if (mpeg2dec->fbuf[1] != &mpeg2dec->fbuf_alloc[i].fbuf &&
+ mpeg2dec->fbuf[2] != &mpeg2dec->fbuf_alloc[i].fbuf)
+ {
+ mpeg2dec->fbuf[0] = &mpeg2dec->fbuf_alloc[i].fbuf;
+ mpeg2dec->info.current_fbuf = mpeg2dec->fbuf[0];
+
+ if (b_type || (mpeg2dec->sequence.flags & SEQ_FLAG_LOW_DELAY))
+ {
+ if (b_type || mpeg2dec->convert)
+ mpeg2dec->info.discard_fbuf = mpeg2dec->fbuf[0];
+
+ mpeg2dec->info.display_fbuf = mpeg2dec->fbuf[0];
+ }
+
+ break;
+ }
+ }
+}
+
+int mpeg2_header_picture (mpeg2dec_t * mpeg2dec)
+{
+ uint8_t * buffer = mpeg2dec->chunk_start;
+ mpeg2_picture_t * picture = &mpeg2dec->new_picture;
+ mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
+ int type;
+
+ mpeg2dec->state = (mpeg2dec->state != STATE_SLICE_1ST) ?
+ STATE_PICTURE : STATE_PICTURE_2ND;
+ mpeg2dec->ext_state = PIC_CODING_EXT;
+
+ picture->temporal_reference = (buffer[0] << 2) | (buffer[1] >> 6);
+
+ type = (buffer [1] >> 3) & 7;
+
+ if (type == PIC_FLAG_CODING_TYPE_P || type == PIC_FLAG_CODING_TYPE_B)
+ {
+ /* forward_f_code and backward_f_code - used in mpeg1 only */
+ decoder->f_motion.f_code[1] = (buffer[3] >> 2) & 1;
+ decoder->f_motion.f_code[0] =
+ (((buffer[3] << 1) | (buffer[4] >> 7)) & 7) - 1;
+ decoder->b_motion.f_code[1] = (buffer[4] >> 6) & 1;
+ decoder->b_motion.f_code[0] = ((buffer[4] >> 3) & 7) - 1;
+ }
+
+ picture->flags = PIC_FLAG_PROGRESSIVE_FRAME | type;
+ picture->tag = picture->tag2 = 0;
+
+ if (mpeg2dec->num_tags)
+ {
+ if (mpeg2dec->bytes_since_tag >= mpeg2dec->chunk_ptr - buffer + 4)
+ {
+ mpeg2dec->num_tags = 0;
+ picture->tag = mpeg2dec->tag_current;
+ picture->tag2 = mpeg2dec->tag2_current;
+ picture->flags |= PIC_FLAG_TAGS;
+ }
+ else if (mpeg2dec->num_tags > 1)
+ {
+ mpeg2dec->num_tags = 1;
+ picture->tag = mpeg2dec->tag_previous;
+ picture->tag2 = mpeg2dec->tag2_previous;
+ picture->flags |= PIC_FLAG_TAGS;
+ }
+ }
+
+ picture->nb_fields = 2;
+ picture->display_offset[0].x = picture->display_offset[1].x =
+ picture->display_offset[2].x = mpeg2dec->display_offset_x;
+
+ picture->display_offset[0].y = picture->display_offset[1].y =
+ picture->display_offset[2].y = mpeg2dec->display_offset_y;
+
+ /* XXXXXX decode extra_information_picture as well */
+
+ mpeg2dec->q_scale_type = 0;
+ decoder->intra_dc_precision = 7;
+ decoder->frame_pred_frame_dct = 1;
+ decoder->concealment_motion_vectors = 0;
+ decoder->scan = mpeg2_scan_norm;
+ decoder->picture_structure = FRAME_PICTURE;
+ mpeg2dec->copy_matrix = 0;
+
+ return 0;
+}
+
+static int picture_coding_ext (mpeg2dec_t * mpeg2dec)
+{
+ uint8_t * buffer = mpeg2dec->chunk_start;
+ mpeg2_picture_t * picture = &mpeg2dec->new_picture;
+ mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
+ uint32_t flags;
+
+ /* pre subtract 1 for use later in compute_motion_vector */
+ decoder->f_motion.f_code[0] = (buffer[0] & 15) - 1;
+ decoder->f_motion.f_code[1] = (buffer[1] >> 4) - 1;
+ decoder->b_motion.f_code[0] = (buffer[1] & 15) - 1;
+ decoder->b_motion.f_code[1] = (buffer[2] >> 4) - 1;
+
+ flags = picture->flags;
+
+ decoder->intra_dc_precision = 7 - ((buffer[2] >> 2) & 3);
+ decoder->picture_structure = buffer[2] & 3;
+
+ switch (decoder->picture_structure)
+ {
+ case TOP_FIELD:
+ flags |= PIC_FLAG_TOP_FIELD_FIRST;
+ case BOTTOM_FIELD:
+ picture->nb_fields = 1;
+ break;
+ case FRAME_PICTURE:
+ if (!(mpeg2dec->sequence.flags & SEQ_FLAG_PROGRESSIVE_SEQUENCE))
+ {
+ picture->nb_fields = (buffer[3] & 2) ? 3 : 2;
+ flags |= (buffer[3] & 128) ? PIC_FLAG_TOP_FIELD_FIRST : 0;
+ }
+ else
+ {
+ picture->nb_fields = (buffer[3]&2) ? ((buffer[3]&128) ? 6 : 4) : 2;
+ }
+ break;
+ default:
+ return 1;
+ }
+
+ decoder->top_field_first = buffer[3] >> 7;
+ decoder->frame_pred_frame_dct = (buffer[3] >> 6) & 1;
+ decoder->concealment_motion_vectors = (buffer[3] >> 5) & 1;
+ mpeg2dec->q_scale_type = buffer[3] & 16;
+ decoder->intra_vlc_format = (buffer[3] >> 3) & 1;
+ decoder->scan = (buffer[3] & 4) ? mpeg2_scan_alt : mpeg2_scan_norm;
+
+ if (!(buffer[4] & 0x80))
+ flags &= ~PIC_FLAG_PROGRESSIVE_FRAME;
+
+ if (buffer[4] & 0x40)
+ {
+ flags |= (((buffer[4]<<26) | (buffer[5]<<18) | (buffer[6]<<10)) &
+ PIC_MASK_COMPOSITE_DISPLAY) | PIC_FLAG_COMPOSITE_DISPLAY;
+ }
+
+ picture->flags = flags;
+
+ mpeg2dec->ext_state = PIC_DISPLAY_EXT | COPYRIGHT_EXT | QUANT_MATRIX_EXT;
+
+ return 0;
+}
+
+static int picture_display_ext (mpeg2dec_t * mpeg2dec)
+{
+ uint8_t * buffer = mpeg2dec->chunk_start;
+ mpeg2_picture_t * picture = &mpeg2dec->new_picture;
+ int i, nb_pos;
+
+ nb_pos = picture->nb_fields;
+
+ if (mpeg2dec->sequence.flags & SEQ_FLAG_PROGRESSIVE_SEQUENCE)
+ nb_pos >>= 1;
+
+ for (i = 0; i < nb_pos; i++)
+ {
+ int x, y;
+
+ x = ((buffer[4*i] << 24) | (buffer[4*i+1] << 16) |
+ (buffer[4*i+2] << 8) | buffer[4*i+3]) >> (11-2*i);
+
+ y = ((buffer[4*i+2] << 24) | (buffer[4*i+3] << 16) |
+ (buffer[4*i+4] << 8) | buffer[4*i+5]) >> (10-2*i);
+
+ if (! (x & y & 1))
+ return 1;
+
+ picture->display_offset[i].x = mpeg2dec->display_offset_x = x >> 1;
+ picture->display_offset[i].y = mpeg2dec->display_offset_y = y >> 1;
+ }
+
+ for (; i < 3; i++)
+ {
+ picture->display_offset[i].x = mpeg2dec->display_offset_x;
+ picture->display_offset[i].y = mpeg2dec->display_offset_y;
+ }
+
+ return 0;
+}
+
+void mpeg2_header_picture_finalize (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
+ int old_type_b = decoder->coding_type == B_TYPE;
+ int low_delay = mpeg2dec->sequence.flags & SEQ_FLAG_LOW_DELAY;
+
+ finalize_matrix (mpeg2dec);
+ decoder->coding_type = mpeg2dec->new_picture.flags & PIC_MASK_CODING_TYPE;
+
+ if (mpeg2dec->state == STATE_PICTURE)
+ {
+ mpeg2_picture_t * picture;
+ mpeg2_picture_t * other;
+
+ decoder->second_field = 0;
+
+ picture = other = mpeg2dec->pictures;
+
+ if (old_type_b ^ (mpeg2dec->picture < mpeg2dec->pictures + 2))
+ picture += 2;
+ else
+ other += 2;
+
+ mpeg2dec->picture = picture;
+ *picture = mpeg2dec->new_picture;
+
+ if (!old_type_b)
+ {
+ mpeg2dec->fbuf[2] = mpeg2dec->fbuf[1];
+ mpeg2dec->fbuf[1] = mpeg2dec->fbuf[0];
+ }
+
+ mpeg2dec->fbuf[0] = NULL;
+ mpeg2_reset_info (&mpeg2dec->info);
+ mpeg2dec->info.current_picture = picture;
+ mpeg2dec->info.display_picture = picture;
+
+ if (decoder->coding_type != B_TYPE)
+ {
+ if (!low_delay)
+ {
+ if (mpeg2dec->first)
+ {
+ mpeg2dec->info.display_picture = NULL;
+ mpeg2dec->first = 0;
+ }
+ else
+ {
+ mpeg2dec->info.display_picture = other;
+
+ if (other->nb_fields == 1)
+ mpeg2dec->info.display_picture_2nd = other + 1;
+
+ mpeg2dec->info.display_fbuf = mpeg2dec->fbuf[1];
+ }
+ }
+
+ if (!low_delay + !mpeg2dec->convert)
+ {
+ mpeg2dec->info.discard_fbuf =
+ mpeg2dec->fbuf[!low_delay + !mpeg2dec->convert];
+ }
+ }
+
+ if (mpeg2dec->convert)
+ {
+ mpeg2_convert_init_t convert_init;
+
+ if (!mpeg2dec->convert_start)
+ {
+ mpeg2dec->decoder.convert_id =
+ mpeg2_malloc (mpeg2dec->convert_id_size,
+ MPEG2_ALLOC_CONVERT_ID);
+
+ mpeg2dec->convert (MPEG2_CONVERT_START,
+ mpeg2dec->decoder.convert_id,
+ &mpeg2dec->sequence,
+ mpeg2dec->convert_stride,
+ mpeg2dec->convert_arg, &convert_init);
+
+ mpeg2dec->convert_start = convert_init.start;
+ mpeg2dec->decoder.convert = convert_init.copy;
+
+ int y_size = decoder->stride_frame * mpeg2dec->sequence.height;
+
+ mpeg2dec->yuv_buf[0][0] =
+ (uint8_t *) mpeg2_malloc(y_size, MPEG2_ALLOC_YUV);
+#if MPEG2_COLOR
+ int uv_size = y_size >> (2 - mpeg2dec->decoder.chroma_format);
+
+ mpeg2dec->yuv_buf[0][1] =
+ (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
+ mpeg2dec->yuv_buf[0][2] =
+ (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
+#endif
+
+ mpeg2dec->yuv_buf[1][0] =
+ (uint8_t *) mpeg2_malloc(y_size, MPEG2_ALLOC_YUV);
+#if MPEG2_COLOR
+ mpeg2dec->yuv_buf[1][1] =
+ (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
+ mpeg2dec->yuv_buf[1][2] =
+ (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
+#endif
+ y_size = decoder->stride_frame * 32;
+
+ mpeg2dec->yuv_buf[2][0] =
+ (uint8_t *) mpeg2_malloc(y_size, MPEG2_ALLOC_YUV);
+#if MPEG2_COLOR
+ uv_size = y_size >> (2 - mpeg2dec->decoder.chroma_format);
+
+ mpeg2dec->yuv_buf[2][1] =
+ (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
+ mpeg2dec->yuv_buf[2][2] =
+ (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
+#endif
+ }
+
+ if (!mpeg2dec->custom_fbuf)
+ {
+ while (mpeg2dec->alloc_index < 3)
+ {
+ mpeg2_fbuf_t * fbuf;
+
+ fbuf = &mpeg2dec->fbuf_alloc[mpeg2dec->alloc_index++].fbuf;
+ fbuf->id = NULL;
+
+ fbuf->buf[0] =
+ (uint8_t *) mpeg2_malloc (convert_init.buf_size[0],
+ MPEG2_ALLOC_CONVERTED);
+#if MPEG2_COLOR
+ fbuf->buf[1] =
+ (uint8_t *) mpeg2_malloc (convert_init.buf_size[1],
+ MPEG2_ALLOC_CONVERTED);
+ fbuf->buf[2] =
+ (uint8_t *) mpeg2_malloc (convert_init.buf_size[2],
+ MPEG2_ALLOC_CONVERTED);
+#endif
+ }
+
+ mpeg2_set_fbuf (mpeg2dec, (decoder->coding_type == B_TYPE));
+ }
+ }
+ else if (!mpeg2dec->custom_fbuf)
+ {
+ while (mpeg2dec->alloc_index < 3)
+ {
+ mpeg2_fbuf_t * fbuf;
+
+ fbuf = &mpeg2dec->fbuf_alloc[mpeg2dec->alloc_index++].fbuf;
+
+ fbuf->id = NULL;
+
+ int y_size = decoder->stride_frame * mpeg2dec->sequence.height;
+
+ fbuf->buf[0] = (uint8_t *) mpeg2_malloc (y_size,
+ MPEG2_ALLOC_YUV);
+#if MPEG2_COLOR
+ int uv_size = y_size >> (2 - decoder->chroma_format);
+
+ fbuf->buf[1] = (uint8_t *) mpeg2_malloc (uv_size,
+ MPEG2_ALLOC_YUV);
+ fbuf->buf[2] = (uint8_t *) mpeg2_malloc (uv_size,
+ MPEG2_ALLOC_YUV);
+#endif
+ }
+
+ mpeg2_set_fbuf (mpeg2dec, (decoder->coding_type == B_TYPE));
+ }
+ }
+ else
+ {
+ decoder->second_field = 1;
+ mpeg2dec->picture++; /* second field picture */
+
+ *(mpeg2dec->picture) = mpeg2dec->new_picture;
+
+ mpeg2dec->info.current_picture_2nd = mpeg2dec->picture;
+
+ if (low_delay || decoder->coding_type == B_TYPE)
+ mpeg2dec->info.display_picture_2nd = mpeg2dec->picture;
+ }
+
+ info_user_data (mpeg2dec);
+}
+
+static int copyright_ext (mpeg2dec_t * mpeg2dec)
+{
+ (void)mpeg2dec;
+ return 0;
+}
+
+static int quant_matrix_ext (mpeg2dec_t * mpeg2dec)
+{
+ uint8_t * buffer = mpeg2dec->chunk_start;
+ int i, j;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (buffer[0] & (8 >> i))
+ {
+ for (j = 0; j < 64; j++)
+ {
+ mpeg2dec->new_quantizer_matrix[i][mpeg2_scan_norm[j]] =
+ (buffer[j] << (i+5)) | (buffer[j+1] >> (3-i));
+ }
+
+ mpeg2dec->copy_matrix |= 1 << i;
+ buffer += 64;
+ }
+ }
+
+ return 0;
+}
+
+int mpeg2_header_extension (mpeg2dec_t * mpeg2dec)
+{
+ static int (* const parser[9]) (mpeg2dec_t *) =
+ {
+ NULL,
+ sequence_ext,
+ sequence_display_ext,
+ quant_matrix_ext,
+ copyright_ext,
+ NULL,
+ NULL,
+ picture_display_ext,
+ picture_coding_ext
+ };
+
+ int ext, ext_bit;
+
+ ext = mpeg2dec->chunk_start[0] >> 4;
+ ext_bit = 1 << ext;
+
+ if (!(mpeg2dec->ext_state & ext_bit))
+ return 0; /* ignore illegal extensions */
+
+ mpeg2dec->ext_state &= ~ext_bit;
+
+ return parser[ext] (mpeg2dec);
+}
+
+int mpeg2_header_user_data (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2dec->user_data_len += mpeg2dec->chunk_ptr - 1 - mpeg2dec->chunk_start;
+ mpeg2dec->chunk_start = mpeg2dec->chunk_ptr - 1;
+
+ return 0;
+}
+
+static void prescale (mpeg2dec_t * mpeg2dec, int index)
+{
+ static const int non_linear_scale[32] =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 10, 12, 14, 16, 18, 20, 22,
+ 24, 28, 32, 36, 40, 44, 48, 52,
+ 56, 64, 72, 80, 88, 96, 104, 112
+ };
+
+ int i, j, k;
+ mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
+
+ if (mpeg2dec->scaled[index] != mpeg2dec->q_scale_type)
+ {
+ mpeg2dec->scaled[index] = mpeg2dec->q_scale_type;
+
+ for (i = 0; i < 32; i++)
+ {
+ k = mpeg2dec->q_scale_type ? non_linear_scale[i] : (i << 1);
+
+ for (j = 0; j < 64; j++)
+ {
+ decoder->quantizer_prescale[index][i][j] =
+ k * mpeg2dec->quantizer_matrix[index][j];
+ }
+ }
+ }
+}
+
+mpeg2_state_t mpeg2_header_slice_start (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
+
+ mpeg2dec->info.user_data = NULL;
+ mpeg2dec->info.user_data_len = 0;
+ mpeg2dec->state = (mpeg2dec->picture->nb_fields > 1 ||
+ mpeg2dec->state == STATE_PICTURE_2ND) ?
+ STATE_SLICE : STATE_SLICE_1ST;
+
+ if (mpeg2dec->decoder.coding_type != D_TYPE)
+ {
+ prescale (mpeg2dec, 0);
+
+ if (decoder->chroma_quantizer[0] == decoder->quantizer_prescale[2])
+ prescale (mpeg2dec, 2);
+
+ if (mpeg2dec->decoder.coding_type != I_TYPE)
+ {
+ prescale (mpeg2dec, 1);
+
+ if (decoder->chroma_quantizer[1] == decoder->quantizer_prescale[3])
+ prescale (mpeg2dec, 3);
+ }
+ }
+
+ if (!(mpeg2dec->nb_decode_slices))
+ {
+ mpeg2dec->picture->flags |= PIC_FLAG_SKIP;
+ }
+ else if (mpeg2dec->convert_start)
+ {
+ mpeg2dec->convert_start (decoder->convert_id, mpeg2dec->fbuf[0],
+ mpeg2dec->picture, mpeg2dec->info.gop);
+
+ if (mpeg2dec->decoder.coding_type == B_TYPE)
+ {
+ mpeg2_init_fbuf (&mpeg2dec->decoder, mpeg2dec->yuv_buf[2],
+ mpeg2dec->yuv_buf[mpeg2dec->yuv_index ^ 1],
+ mpeg2dec->yuv_buf[mpeg2dec->yuv_index]);
+ }
+ else
+ {
+ mpeg2_init_fbuf (&mpeg2dec->decoder,
+ mpeg2dec->yuv_buf[mpeg2dec->yuv_index ^ 1],
+ mpeg2dec->yuv_buf[mpeg2dec->yuv_index],
+ mpeg2dec->yuv_buf[mpeg2dec->yuv_index]);
+
+ if (mpeg2dec->state == STATE_SLICE)
+ mpeg2dec->yuv_index ^= 1;
+ }
+ }
+ else
+ {
+ int b_type;
+
+ b_type = (mpeg2dec->decoder.coding_type == B_TYPE);
+
+ mpeg2_init_fbuf (&mpeg2dec->decoder, mpeg2dec->fbuf[0]->buf,
+ mpeg2dec->fbuf[b_type + 1]->buf,
+ mpeg2dec->fbuf[b_type]->buf);
+ }
+
+ mpeg2dec->action = NULL;
+
+ return STATE_INTERNAL_NORETURN;
+}
+
+static mpeg2_state_t seek_sequence (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2_reset_info (&mpeg2dec->info);
+
+ mpeg2dec->info.sequence = NULL;
+ mpeg2dec->info.gop = NULL;
+
+ mpeg2_header_state_init (mpeg2dec);
+
+ mpeg2dec->action = mpeg2_seek_header;
+
+ return mpeg2_seek_header (mpeg2dec);
+}
+
+mpeg2_state_t mpeg2_header_end (mpeg2dec_t * mpeg2dec)
+{
+ mpeg2_picture_t * picture;
+ int b_type;
+
+ b_type = (mpeg2dec->decoder.coding_type == B_TYPE);
+ picture = mpeg2dec->pictures;
+
+ if ((mpeg2dec->picture >= picture + 2) ^ b_type)
+ picture = mpeg2dec->pictures + 2;
+
+ mpeg2_reset_info (&mpeg2dec->info);
+
+ if (!(mpeg2dec->sequence.flags & SEQ_FLAG_LOW_DELAY))
+ {
+ mpeg2dec->info.display_picture = picture;
+
+ if (picture->nb_fields == 1)
+ mpeg2dec->info.display_picture_2nd = picture + 1;
+
+ mpeg2dec->info.display_fbuf = mpeg2dec->fbuf[b_type];
+
+ if (!mpeg2dec->convert)
+ mpeg2dec->info.discard_fbuf = mpeg2dec->fbuf[b_type + 1];
+ }
+ else if (!mpeg2dec->convert)
+ {
+ mpeg2dec->info.discard_fbuf = mpeg2dec->fbuf[b_type];
+ }
+
+ mpeg2dec->action = seek_sequence;
+
+ return STATE_END;
+}
diff --git a/apps/plugins/mpegplayer/libmpeg2/idct.c b/apps/plugins/mpegplayer/libmpeg2/idct.c
new file mode 100644
index 0000000000..7f0b9a3c12
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/idct.c
@@ -0,0 +1,274 @@
+/*
+ * idct.c
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.36
+ */
+
+#include "plugin.h"
+
+#include "mpeg2dec_config.h"
+
+#include "mpeg2.h"
+#include "attributes.h"
+#include "mpeg2_internal.h"
+
+#if defined(CPU_COLDFIRE) || defined (CPU_ARM)
+#define IDCT_ASM
+#endif
+
+#ifndef IDCT_ASM
+
+#define W1 2841 /* 2048 * sqrt (2) * cos (1 * pi / 16) */
+#define W2 2676 /* 2048 * sqrt (2) * cos (2 * pi / 16) */
+#define W3 2408 /* 2048 * sqrt (2) * cos (3 * pi / 16) */
+#define W5 1609 /* 2048 * sqrt (2) * cos (5 * pi / 16) */
+#define W6 1108 /* 2048 * sqrt (2) * cos (6 * pi / 16) */
+#define W7 565 /* 2048 * sqrt (2) * cos (7 * pi / 16) */
+
+/*
+ * In legal streams, the IDCT output should be between -384 and +384.
+ * In corrupted streams, it is possible to force the IDCT output to go
+ * to +-3826 - this is the worst case for a column IDCT where the
+ * column inputs are 16-bit values.
+ */
+#define CLIP(i) \
+ ({ typeof (i) _i = (i); \
+ if ((_i & 0xff) != _i) \
+ _i = ~(_i >> (8*sizeof(_i) - 1)); \
+ _i; })
+
+#if 0
+#define BUTTERFLY(t0,t1,W0,W1,d0,d1) \
+ do { \
+ t0 = W0 * d0 + W1 * d1; \
+ t1 = W0 * d1 - W1 * d0; \
+ } while (0)
+#else
+#define BUTTERFLY(t0,t1,W0,W1,d0,d1) \
+ do { \
+ int tmp = W0 * (d0 + d1); \
+ t0 = tmp + (W1 - W0) * d1; \
+ t1 = tmp - (W1 + W0) * d0; \
+ } while (0)
+#endif
+
+static inline void idct_row (int16_t * const block)
+{
+ int d0, d1, d2, d3;
+ int a0, a1, a2, a3, b0, b1, b2, b3;
+ int t0, t1, t2, t3;
+
+ /* shortcut */
+ if (likely (!(block[1] | ((int32_t *)block)[1] | ((int32_t *)block)[2] |
+ ((int32_t *)block)[3])))
+ {
+ uint32_t tmp = (uint16_t) (block[0] >> 1);
+ tmp |= tmp << 16;
+ ((int32_t *)block)[0] = tmp;
+ ((int32_t *)block)[1] = tmp;
+ ((int32_t *)block)[2] = tmp;
+ ((int32_t *)block)[3] = tmp;
+ return;
+ }
+
+ d0 = (block[0] << 11) + 2048;
+ d1 = block[1];
+ d2 = block[2] << 11;
+ d3 = block[3];
+ t0 = d0 + d2;
+ t1 = d0 - d2;
+ BUTTERFLY (t2, t3, W6, W2, d3, d1);
+ a0 = t0 + t2;
+ a1 = t1 + t3;
+ a2 = t1 - t3;
+ a3 = t0 - t2;
+
+ d0 = block[4];
+ d1 = block[5];
+ d2 = block[6];
+ d3 = block[7];
+ BUTTERFLY (t0, t1, W7, W1, d3, d0);
+ BUTTERFLY (t2, t3, W3, W5, d1, d2);
+ b0 = t0 + t2;
+ b3 = t1 + t3;
+ t0 -= t2;
+ t1 -= t3;
+ b1 = ((t0 + t1) >> 8) * 181;
+ b2 = ((t0 - t1) >> 8) * 181;
+
+ block[0] = (a0 + b0) >> 12;
+ block[1] = (a1 + b1) >> 12;
+ block[2] = (a2 + b2) >> 12;
+ block[3] = (a3 + b3) >> 12;
+ block[4] = (a3 - b3) >> 12;
+ block[5] = (a2 - b2) >> 12;
+ block[6] = (a1 - b1) >> 12;
+ block[7] = (a0 - b0) >> 12;
+}
+
+static inline void idct_col (int16_t * const block)
+{
+ int d0, d1, d2, d3;
+ int a0, a1, a2, a3, b0, b1, b2, b3;
+ int t0, t1, t2, t3;
+
+ d0 = (block[8*0] << 11) + 65536;
+ d1 = block[8*1];
+ d2 = block[8*2] << 11;
+ d3 = block[8*3];
+ t0 = d0 + d2;
+ t1 = d0 - d2;
+ BUTTERFLY (t2, t3, W6, W2, d3, d1);
+ a0 = t0 + t2;
+ a1 = t1 + t3;
+ a2 = t1 - t3;
+ a3 = t0 - t2;
+
+ d0 = block[8*4];
+ d1 = block[8*5];
+ d2 = block[8*6];
+ d3 = block[8*7];
+ BUTTERFLY (t0, t1, W7, W1, d3, d0);
+ BUTTERFLY (t2, t3, W3, W5, d1, d2);
+ b0 = t0 + t2;
+ b3 = t1 + t3;
+ t0 -= t2;
+ t1 -= t3;
+ b1 = ((t0 + t1) >> 8) * 181;
+ b2 = ((t0 - t1) >> 8) * 181;
+
+ block[8*0] = (a0 + b0) >> 17;
+ block[8*1] = (a1 + b1) >> 17;
+ block[8*2] = (a2 + b2) >> 17;
+ block[8*3] = (a3 + b3) >> 17;
+ block[8*4] = (a3 - b3) >> 17;
+ block[8*5] = (a2 - b2) >> 17;
+ block[8*6] = (a1 - b1) >> 17;
+ block[8*7] = (a0 - b0) >> 17;
+}
+
+void mpeg2_idct_copy (int16_t * block, uint8_t * dest,
+ const int stride)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ idct_row (block + 8 * i);
+
+ for (i = 0; i < 8; i++)
+ idct_col (block + i);
+
+ do
+ {
+ dest[0] = CLIP (block[0]);
+ dest[1] = CLIP (block[1]);
+ dest[2] = CLIP (block[2]);
+ dest[3] = CLIP (block[3]);
+ dest[4] = CLIP (block[4]);
+ dest[5] = CLIP (block[5]);
+ dest[6] = CLIP (block[6]);
+ dest[7] = CLIP (block[7]);
+
+ ((int32_t *)block)[0] = 0;
+ ((int32_t *)block)[1] = 0;
+ ((int32_t *)block)[2] = 0;
+ ((int32_t *)block)[3] = 0;
+
+ dest += stride;
+ block += 8;
+ }
+ while (--i);
+}
+
+void mpeg2_idct_add (const int last, int16_t * block,
+ uint8_t * dest, const int stride)
+{
+ int i;
+
+ if (last != 129 || (block[0] & (7 << 4)) == (4 << 4))
+ {
+ for (i = 0; i < 8; i++)
+ idct_row (block + 8 * i);
+
+ for (i = 0; i < 8; i++)
+ idct_col (block + i);
+
+ do
+ {
+ dest[0] = CLIP (block[0] + dest[0]);
+ dest[1] = CLIP (block[1] + dest[1]);
+ dest[2] = CLIP (block[2] + dest[2]);
+ dest[3] = CLIP (block[3] + dest[3]);
+ dest[4] = CLIP (block[4] + dest[4]);
+ dest[5] = CLIP (block[5] + dest[5]);
+ dest[6] = CLIP (block[6] + dest[6]);
+ dest[7] = CLIP (block[7] + dest[7]);
+
+ ((int32_t *)block)[0] = 0;
+ ((int32_t *)block)[1] = 0;
+ ((int32_t *)block)[2] = 0;
+ ((int32_t *)block)[3] = 0;
+
+ dest += stride;
+ block += 8;
+ }
+ while (--i);
+ }
+ else
+ {
+ int DC = (block[0] + 64) >> 7;
+ block[0] = block[63] = 0;
+ i = 8;
+
+ do
+ {
+ dest[0] = CLIP (DC + dest[0]);
+ dest[1] = CLIP (DC + dest[1]);
+ dest[2] = CLIP (DC + dest[2]);
+ dest[3] = CLIP (DC + dest[3]);
+ dest[4] = CLIP (DC + dest[4]);
+ dest[5] = CLIP (DC + dest[5]);
+ dest[6] = CLIP (DC + dest[6]);
+ dest[7] = CLIP (DC + dest[7]);
+ dest += stride;
+ }
+ while (--i);
+ }
+}
+
+#endif /* IDCT_ASM */
+
+void mpeg2_idct_init (void)
+{
+ int i, j;
+
+ for (i = 0; i < 64; i++)
+ {
+ j = default_mpeg2_scan_norm[i];
+ mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2);
+
+ j = default_mpeg2_scan_alt[i];
+ mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2);
+ }
+}
diff --git a/apps/plugins/mpegplayer/libmpeg2/idct_arm.S b/apps/plugins/mpegplayer/libmpeg2/idct_arm.S
new file mode 100644
index 0000000000..97a87a8b59
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/idct_arm.S
@@ -0,0 +1,443 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 by Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+
+ .global mpeg2_idct_copy
+ .type mpeg2_idct_copy, %function
+ .global mpeg2_idct_add
+ .type mpeg2_idct_add, %function
+
+
+/* Custom calling convention:
+ * r0 contains block pointer and is non-volatile
+ * all non-volatile c context saved and restored on its behalf
+ */
+.idct:
+ add r12, r0, #128
+1:
+ ldrsh r1, [r0, #0] /* d0 */
+ ldrsh r2, [r0, #2] /* d1 */
+ ldrsh r3, [r0, #4] /* d2 */
+ ldrsh r4, [r0, #6] /* d3 */
+ ldrsh r5, [r0, #8] /* d0 */
+ ldrsh r6, [r0, #10] /* d1 */
+ ldrsh r7, [r0, #12] /* d2 */
+ ldrsh r8, [r0, #14] /* d3 */
+ orrs r9, r2, r3
+ orreqs r9, r4, r5
+ orreqs r9, r6, r7
+ cmpeq r8, #0
+ bne 2f
+ mov r1, r1, asl #15
+ bic r1, r1, #0x8000
+ orr r1, r1, r1, lsr #16
+ str r1, [r0], #4
+ str r1, [r0], #4
+ str r1, [r0], #4
+ str r1, [r0], #4
+ cmp r0, r12
+ blo 1b
+ b 3f
+2:
+ mov r1, r1, asl #11 /* r1 = d0 = (block[0] << 11) + 2048 */
+ add r1, r1, #2048
+ add r1, r1, r3, asl #11 /* r1 = t0 = d0 + (block[2] << 11) */
+ sub r3, r1, r3, asl #12 /* r3 = t1 = d0 - (block[2] << 11) */
+
+ add r9, r2, r4 /* r9 = tmp = (d1+d3)*(1108/4) */
+ add r10, r9, r9, asl #2
+ add r10, r10, r9, asl #4
+ add r9, r10, r9, asl #8
+
+ add r10, r2, r2, asl #4 /* r2 = t2 = tmp + (d1*(1568/32)*8) */
+ add r2, r10, r2, asl #5
+ add r2, r9, r2, asl #3
+
+ add r10, r4, r4, asl #2 /* r4 = t3 = tmp - (d3*(3784/8)*2) */
+ rsb r10, r10, r4, asl #6
+ add r4, r4, r10, asl #3
+ sub r4, r9, r4, asl #1
+ /* t2 & t3 are 1/4 final value here */
+ add r1, r1, r2, asl #2 /* r1 = a0 = t0 + t2 */
+ sub r2, r1, r2, asl #3 /* r2 = a3 = t0 - t2 */
+ add r3, r3, r4, asl #2 /* r3 = a1 = t1 + t3 */
+ sub r4, r3, r4, asl #3 /* r4 = a2 = t1 - t3 */
+
+ add r9, r8, r5 /* r9 = tmp = 565*(d3 + d0) */
+ add r10, r9, r9, asl #4
+ add r10, r10, r10, asl #5
+ add r9, r10, r9, asl #2
+
+ add r10, r5, r5, asl #4 /* r5 = t0 = tmp + (((2276/4)*d0)*4) */
+ add r10, r10, r10, asl #5
+ add r5, r10, r5, asl #3
+ add r5, r9, r5, asl #2
+
+ add r10, r8, r8, asl #2 /* r8 = t1 = tmp - (((3406/2)*d3)*2) */
+ add r10, r10, r10, asl #4
+ add r10, r10, r8, asl #7
+ rsb r8, r8, r10, asl #3
+ sub r8, r9, r8, asl #1
+
+ add r9, r6, r7 /* r9 = tmp = (2408/8)*(d1 + d2) */
+ add r10, r9, r9, asl #3
+ add r10, r10, r10, asl #5
+ add r9, r10, r9, asl #2
+
+ add r10, r7, r7, asl #3 /* r7 = t2 = (tmp*8) - 799*d2 */
+ add r10, r10, r7, asl #4
+ rsb r7, r7, r10, asl #5
+ rsb r7, r7, r9, asl #3
+
+ sub r10, r6, r6, asl #4 /* r6 = t3 = (tmp*8) - 4017*d1 */
+ sub r10, r10, r6, asl #6
+ add r10, r10, r6, asl #12
+ add r6, r10, r6
+ rsb r6, r6, r9, asl #3
+ /* t0 = r5, t1 = r8, t2 = r7, t3 = r6*/
+ add r9, r5, r7 /* r9 = b0 = t0 + t2 */
+ add r10, r8, r6 /* r10 = b3 = t1 + t3 */
+ sub r5, r5, r7 /* t0 -= t2 */
+ sub r8, r8, r6 /* t1 -= t3 */
+ add r6, r5, r8 /* r6 = t0 + t1 */
+ sub r7, r5, r8 /* r7 = t0 - t1 */
+
+ add r11, r6, r6, asr #2 /* r6 = b1 = r6*(181/128) */
+ add r11, r11, r11, asr #5
+ add r6, r11, r6, asr #3
+ add r11, r7, r7, asr #2 /* r7 = b2 = r7*(181/128) */
+ add r11, r11, r11, asr #5
+ add r7, r11, r7, asr #3
+ /* r1 = a0, r3 = a1, r4 = a2, r2 = a3 */
+ /* r9 = b0, r6 = b1*2, r7 = b2*2, r10 = b3 */
+ add r5, r1, r9 /* block[0] = (a0 + b0) >> 12 */
+ mov r5, r5, asr #12
+ strh r5, [r0], #2
+ add r8, r3, r6, asr #1 /* block[1] = (a1 + b1) >> 12 */
+ mov r8, r8, asr #12
+ strh r8, [r0], #2
+ add r5, r4, r7, asr #1 /* block[2] = (a2 + b2) >> 12 */
+ mov r5, r5, asr #12
+ strh r5, [r0], #2
+ add r8, r2, r10 /* block[3] = (a3 + b3) >> 12 */
+ mov r8, r8, asr #12
+ strh r8, [r0], #2
+ sub r5, r2, r10 /* block[4] = (a3 - b3) >> 12 */
+ mov r5, r5, asr #12
+ strh r5, [r0], #2
+ sub r8, r4, r7, asr #1 /* block[5] = (a2 - b2) >> 12 */
+ mov r8, r8, asr #12
+ strh r8, [r0], #2
+ sub r5, r3, r6, asr #1 /* block[6] = (a1 - b1) >> 12 */
+ mov r5, r5, asr #12
+ strh r5, [r0], #2
+ sub r8, r1, r9 /* block[7] = (a0 - b0) >> 12 */
+ mov r8, r8, asr #12
+ strh r8, [r0], #2
+ cmp r0, r12
+ blo 1b
+3:
+ sub r0, r0, #128
+ add r12, r0, #16
+4:
+ ldrsh r1, [r0, #0*8] /* d0 */
+ ldrsh r2, [r0, #2*8] /* d1 */
+ ldrsh r3, [r0, #4*8] /* d2 */
+ ldrsh r4, [r0, #6*8] /* d3 */
+ ldrsh r5, [r0, #8*8] /* d0 */
+ ldrsh r6, [r0, #10*8] /* d1 */
+ ldrsh r7, [r0, #12*8] /* d2 */
+ ldrsh r8, [r0, #14*8] /* d3 */
+
+ mov r1, r1, asl #11 /* r1 = d0 = (block[0] << 11) + 2048 */
+ add r1, r1, #65536
+ add r1, r1, r3, asl #11 /* r1 = t0 = d0 + d2:(block[2] << 11) */
+ sub r3, r1, r3, asl #12 /* r3 = t1 = d0 - d2:(block[2] << 11) */
+
+ add r9, r2, r4 /* r9 = tmp = (d1+d3)*(1108/4) */
+ add r10, r9, r9, asl #2
+ add r10, r10, r9, asl #4
+ add r9, r10, r9, asl #8
+
+ add r11, r2, r2, asl #4 /* r2 = t2 = tmp + (d1*(1568/32)*8) */
+ add r2, r11, r2, asl #5
+ add r2, r9, r2, asl #3
+
+ add r10, r4, r4, asl #2 /* r4 = t3 = tmp - (d3*(3784/8)*2) */
+ rsb r10, r10, r4, asl #6
+ add r4, r4, r10, asl #3
+ sub r4, r9, r4, asl #1
+ /* t2 & t3 are 1/4 final value here */
+ add r1, r1, r2, asl #2 /* r1 = a0 = t0 + t2 */
+ sub r2, r1, r2, asl #3 /* r2 = a3 = t0 - t2 */
+ add r3, r3, r4, asl #2 /* r3 = a1 = t1 + t3 */
+ sub r4, r3, r4, asl #3 /* r4 = a2 = t1 - t3 */
+
+ add r9, r8, r5 /* r9 = tmp = 565*(d3 + d0) */
+ add r10, r9, r9, asl #4
+ add r10, r10, r10, asl #5
+ add r9, r10, r9, asl #2
+
+ add r10, r5, r5, asl #4 /* r5 = t0 = tmp + (((2276/4)*d0)*4) */
+ add r10, r10, r10, asl #5
+ add r5, r10, r5, asl #3
+ add r5, r9, r5, asl #2
+
+ add r10, r8, r8, asl #2 /* r8 = t1 = tmp - (((3406/2)*d3)*2) */
+ add r10, r10, r10, asl #4
+ add r10, r10, r8, asl #7
+ rsb r8, r8, r10, asl #3
+ sub r8, r9, r8, asl #1
+
+ add r9, r6, r7 /* r9 = tmp = (2408/8)*(d1 + d2) */
+ add r10, r9, r9, asl #3
+ add r10, r10, r10, asl #5
+ add r9, r10, r9, asl #2
+
+ add r10, r7, r7, asl #3 /* r7 = t2 = (tmp*8) - 799*d2 */
+ add r10, r10, r7, asl #4
+ rsb r7, r7, r10, asl #5
+ rsb r7, r7, r9, asl #3
+
+ sub r10, r6, r6, asl #4 /* r6 = t3 = (tmp*8) - 4017*d1 */
+ sub r10, r10, r6, asl #6
+ add r10, r10, r6, asl #12
+ add r6, r10, r6
+ rsb r6, r6, r9, asl #3
+ /* t0=r5, t1=r8, t2=r7, t3=r6*/
+ add r9, r5, r7 /* r9 = b0 = t0 + t2 */
+ add r10, r8, r6 /* r10 = b3 = t1 + t3 */
+ sub r5, r5, r7 /* t0 -= t2 */
+ sub r8, r8, r6 /* t1 -= t3 */
+ add r6, r5, r8 /* r6 = t0 + t1 */
+ sub r7, r5, r8 /* r7 = t0 - t1 */
+
+ add r11, r6, r6, asr #2 /* r6 = b1 = r5*(181/128) */
+ add r11, r11, r11, asr #5
+ add r6, r11, r6, asr #3
+ add r11, r7, r7, asr #2 /* r7 = b2 = r6*(181/128) */
+ add r11, r11, r11, asr #5
+ add r7, r11, r7, asr #3
+ /* r1 = a0, r3 = a1, r4 = a2, r2 = a3 */
+ /* r9 = b0, r6 = b1*2, r7 = b2*2, r10 = b3 */
+ add r5, r1, r9 /* block[0] = (a0 + b0) >> 17 */
+ mov r5, r5, asr #17
+ strh r5, [r0, #0*8]
+ add r8, r3, r6, asr #1 /* block[1] = (a1 + b1) >> 17 */
+ mov r8, r8, asr #17
+ strh r8, [r0, #2*8]
+ add r5, r4, r7, asr #1 /* block[2] = (a2 + b2) >> 17 */
+ mov r5, r5, asr #17
+ strh r5, [r0, #4*8]
+ add r8, r2, r10 /* block[3] = (a3 + b3) >> 17 */
+ mov r8, r8, asr #17
+ strh r8, [r0, #6*8]
+ sub r5, r2, r10 /* block[4] = (a3 - b3) >> 17 */
+ mov r5, r5, asr #17
+ strh r5, [r0, #8*8]
+ sub r8, r4, r7, asr #1 /* block[5] = (a2 - b2) >> 17 */
+ mov r8, r8, asr #17
+ strh r8, [r0, #10*8]
+ sub r5, r3, r6, asr #1 /* block[6] = (a1 - b1) >> 17 */
+ mov r5, r5, asr #17
+ strh r5, [r0, #12*8]
+ sub r8, r1, r9 /* block[7] = (a0 - b0) >> 17 */
+ mov r8, r8, asr #17
+ strh r8, [r0, #14*8]
+ add r0, r0, #2
+ cmp r0, r12
+ blo 4b
+ sub r0, r0, #16
+ bx lr
+
+mpeg2_idct_copy:
+ stmfd sp!, { r1-r2, r4-r11, lr }
+ bl .idct
+ ldmfd sp!, { r1-r2 }
+ mov r11, #0
+ add r12, r0, #128
+1:
+ ldrsh r3, [r0, #0]
+ ldrsh r4, [r0, #2]
+ ldrsh r5, [r0, #4]
+ ldrsh r6, [r0, #6]
+ ldrsh r7, [r0, #8]
+ ldrsh r8, [r0, #10]
+ ldrsh r9, [r0, #12]
+ ldrsh r10, [r0, #14]
+ cmp r3, #255
+ mvnhi r3, r3, asr #31
+ strb r3, [r1, #0]
+ str r11, [r0], #4
+ cmp r4, #255
+ mvnhi r4, r4, asr #31
+ strb r4, [r1, #1]
+ cmp r5, #255
+ mvnhi r5, r5, asr #31
+ strb r5, [r1, #2]
+ str r11, [r0], #4
+ cmp r6, #255
+ mvnhi r6, r6, asr #31
+ strb r6, [r1, #3]
+ cmp r7, #255
+ mvnhi r7, r7, asr #31
+ strb r7, [r1, #4]
+ str r11, [r0], #4
+ cmp r8, #255
+ mvnhi r8, r8, asr #31
+ strb r8, [r1, #5]
+ cmp r9, #255
+ mvnhi r9, r9, asr #31
+ strb r9, [r1, #6]
+ str r11, [r0], #4
+ cmp r10, #255
+ mvnhi r10, r10, asr #31
+ strb r10, [r1, #7]
+ add r1, r1, r2
+ cmp r0, r12
+ blo 1b
+ ldmpc regs=r4-r11
+
+mpeg2_idct_add:
+ cmp r0, #129
+ mov r0, r1
+ ldreqsh r1, [r0, #0]
+ bne 1f
+ and r1, r1, #0x70
+ cmp r1, #0x40
+ bne 3f
+1:
+ stmfd sp!, { r2-r11, lr }
+ bl .idct
+ ldmfd sp!, { r1-r2 }
+ mov r11, #0
+ add r12, r0, #128
+2:
+ ldrb r3, [r1, #0]
+ ldrb r4, [r1, #1]
+ ldrb r5, [r1, #2]
+ ldrb r6, [r1, #3]
+ ldrsh r7, [r0, #0]
+ ldrsh r8, [r0, #2]
+ ldrsh r9, [r0, #4]
+ ldrsh r10, [r0, #6]
+ add r7, r7, r3
+ ldrb r3, [r1, #4]
+ cmp r7, #255
+ mvnhi r7, r7, asr #31
+ strb r7, [r1, #0]
+ ldrsh r7, [r0, #8]
+ add r8, r8, r4
+ ldrb r4, [r1, #5]
+ cmp r8, #255
+ mvnhi r8, r8, asr #31
+ strb r8, [r1, #1]
+ ldrsh r8, [r0, #10]
+ add r9, r9, r5
+ ldrb r5, [r1, #6]
+ cmp r9, #255
+ mvnhi r9, r9, asr #31
+ strb r9, [r1, #2]
+ ldrsh r9, [r0, #12]
+ add r10, r10, r6
+ ldrb r6, [r1, #7]
+ cmp r10, #255
+ mvnhi r10, r10, asr #31
+ strb r10, [r1, #3]
+ ldrsh r10, [r0, #14]
+ str r11, [r0], #4
+ add r7, r7, r3
+ cmp r7, #255
+ mvnhi r7, r7, asr #31
+ strb r7, [r1, #4]
+ str r11, [r0], #4
+ add r8, r8, r4
+ cmp r8, #255
+ mvnhi r8, r8, asr #31
+ strb r8, [r1, #5]
+ str r11, [r0], #4
+ add r9, r9, r5
+ cmp r9, #255
+ mvnhi r9, r9, asr #31
+ strb r9, [r1, #6]
+ add r10, r10, r6
+ cmp r10, #255
+ mvnhi r10, r10, asr #31
+ strb r10, [r1, #7]
+ str r11, [r0], #4
+ add r1, r1, r2
+ cmp r0, r12
+ blo 2b
+ ldmpc regs=r4-r11
+3:
+ stmfd sp!, { r4-r5, lr }
+ ldrsh r1, [r0, #0] /* r1 = block[0] */
+ mov r4, #0
+ strh r4, [r0, #0] /* block[0] = 0 */
+ strh r4, [r0, #126] /* block[63] = 0 */
+ add r1, r1, #64 /* r1 = DC << 7 */
+ add r0, r2, r3, asl #3
+4:
+ ldrb r4, [r2, #0]
+ ldrb r5, [r2, #1]
+ ldrb r12, [r2, #2]
+ ldrb lr, [r2, #3]
+ add r4, r4, r1, asr #7
+ cmp r4, #255
+ mvnhi r4, r4, asr #31
+ strb r4, [r2, #0]
+ add r5, r5, r1, asr #7
+ cmp r5, #255
+ mvnhi r5, r5, asr #31
+ strb r5, [r2, #1]
+ add r12, r12, r1, asr #7
+ cmp r12, #255
+ mvnhi r12, r12, asr #31
+ strb r12, [r2, #2]
+ add lr, lr, r1, asr #7
+ cmp lr, #255
+ mvnhi lr, lr, asr #31
+ strb lr, [r2, #3]
+ ldrb r4, [r2, #4]
+ ldrb r5, [r2, #5]
+ ldrb r12, [r2, #6]
+ ldrb lr, [r2, #7]
+ add r4, r4, r1, asr #7
+ cmp r4, #255
+ mvnhi r4, r4, asr #31
+ strb r4, [r2, #4]
+ add r5, r5, r1, asr #7
+ cmp r5, #255
+ mvnhi r5, r5, asr #31
+ strb r5, [r2, #5]
+ add r12, r12, r1, asr #7
+ cmp r12, #255
+ mvnhi r12, r12, asr #31
+ strb r12, [r2, #6]
+ add lr, lr, r1, asr #7
+ cmp lr, #255
+ mvnhi lr, lr, asr #31
+ strb lr, [r2, #7]
+ add r2, r2, r3
+ cmp r2, r0
+ blo 4b
+ ldmpc regs=r4-r5
diff --git a/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S b/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S
new file mode 100644
index 0000000000..dc53cbd7bd
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S
@@ -0,0 +1,297 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2009 by Jens Arnold
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+ .global mpeg2_idct_copy
+ .type mpeg2_idct_copy, %function
+ .global mpeg2_idct_add
+ .type mpeg2_idct_add, %function
+
+/* Custom calling convention:
+ * r0 contains block pointer and is non-volatile
+ * all non-volatile c context saved and restored on its behalf
+ */
+.idct:
+ str lr, [sp, #-4]! @ lr is used
+ add r1, r0, #128 @ secondary, transposed temp buffer
+ mov r14, #8 @ loop counter
+
+.row_loop:
+ ldmia r0!, {r2, r3, r10, r11} @ fetch f0, f2, f4, f6, f1, f3, f5, f7
+ ldrd r4, L_W1357 @ load W1, W3, W5, W7
+
+ smuad r6, r4, r10 @ b0 = W1 * f1 + W3 * f3
+ smultt r7, r5, r10 @ -b1 = W7 * f3
+ smulbt r8, r4, r10 @ -b2 = W1 * f3
+
+ smusdx r9, r10, r5 @ b3 = f1 * W7 - f3 * W5
+ smlabb r7, r4, r11, r7 @ -b1 += W1 * f5
+ rsb r8, r8, #0 @ b2 = -b2
+ smlabb r8, r5, r10, r8 @ b2 += W5 * f1
+
+ smlad r6, r5, r11, r6 @ b0 += W5 * f5 + W7 * f7
+ smlabt r7, r5, r11, r7 @ -b1 += W5 * f7
+ smlatb r8, r5, r11, r8 @ b2 += W7 * f5
+
+ smlsdx r9, r11, r4, r9 @ b3 += f5 * W3 - f7 * W1
+ rsb r7, r7, #0 @ b1 = -b1
+ smlatb r7, r4, r10, r7 @ b1 += W3 * f1
+ smlatt r8, r4, r11, r8 @ b2 += W3 * f7
+
+ ldrd r4, L_W0246 @ load W0, W2, W4, W6
+ add r2, r2, #1 @ f0 += 1
+
+ smulbb r10, r5, r3 @ a0' = W4 * f4
+ smultt r12, r5, r3 @ a3' = W6 * f6
+ smultt r3, r4, r3 @ -a2' = W2 * f6
+
+ rsb r11, r10, #0 @ a1' = -W4 * f4
+ smlabb r10, r4, r2, r10 @ a0' += W0 * f0
+ smlabb r11, r4, r2, r11 @ a1' += W0 * f0
+ smlatt r12, r4, r2, r12 @ a3' += W2 * f2
+ rsb r3, r3, #0 @ a2' = -a2'
+ smlatt r3, r5, r2, r3 @ a2' += W6 * f2
+
+ add r10, r10, r12 @ a0 = a0' + a3'
+ sub r12, r10, r12, lsl #1 @ a3 = a0 - 2 * a3'
+ add r11, r11, r3 @ a1 = a1' + a2'
+ sub r3, r11, r3, lsl #1 @ a2 = a1 - 2 * a2'
+
+ subs r14, r14, #1 @ decrease loop count
+
+ @ Special store order for making the column pass calculate columns in
+ @ the order 0-2-1-3-4-6-5-7, allowing for uxtab16 use in later stages.
+ sub r2, r10, r6 @ block[7] = (a0 - b0)
+ mov r2, r2, asr #12 @ >> 12
+ strh r2, [r1, #7*16]
+ sub r2, r11, r7 @ block[6] = (a1 - b1)
+ mov r2, r2, asr #12 @ >> 12
+ strh r2, [r1, #5*16]
+ sub r2, r3, r8 @ block[5] = (a2 - b2)
+ mov r2, r2, asr #12 @ >> 12
+ strh r2, [r1, #6*16]
+ sub r2, r12, r9 @ block[4] = (a3 - b3)
+ mov r2, r2, asr #12 @ >> 12
+ strh r2, [r1, #4*16]
+ add r2, r12, r9 @ block[3] = (a3 + b3)
+ mov r2, r2, asr #12 @ >> 12
+ strh r2, [r1, #3*16]
+ add r2, r3, r8 @ block[2] = (a2 + b2)
+ mov r2, r2, asr #12 @ >> 12
+ strh r2, [r1, #1*16]
+ add r2, r11, r7 @ block[1] = (a1 + b1)
+ mov r2, r2, asr #12 @ >> 12
+ strh r2, [r1, #2*16]
+ add r2, r10, r6 @ block[0] = (a0 + b0)
+ mov r2, r2, asr #12 @ >> 12
+ strh r2, [r1], #2 @ advance to next temp column
+
+ bne .row_loop
+ b .col_start
+
+ @placed here because of ldrd's offset limit
+L_W1357:
+ .short 2841
+ .short 2408
+ .short 1609
+ .short 565
+
+L_W0246:
+ .short 2048
+ .short 2676
+ .short 2048
+ .short 1108
+
+.col_start:
+ @ r0 now points to the temp buffer, where we need it.
+ sub r1, r1, #128+16 @ point r1 back to the input block
+ mov r14, #8 @ loop counter
+
+.col_loop:
+ ldmia r0!, {r2, r3, r10, r11} @ fetch f0, f2, f4, f6, f1, f3, f5, f7
+ ldrd r4, L_W1357 @ load W1, W3, W5, W7
+
+ smuad r6, r4, r10 @ b0 = W1 * f1 + W3 * f3
+ smultt r7, r5, r10 @ -b1 = W7 * f3
+ smulbt r8, r4, r10 @ -b2 = W1 * f3
+
+ smusdx r9, r10, r5 @ b3 = f1 * W7 - f3 * W5
+ smlabb r7, r4, r11, r7 @ -b1 += W1 * f5
+ rsb r8, r8, #0 @ b2 = -b2
+ smlabb r8, r5, r10, r8 @ b2 += W5 * f1
+
+ smlad r6, r5, r11, r6 @ b0 += W5 * f5 + W7 * f7
+ smlabt r7, r5, r11, r7 @ -b1 += W5 * f7
+ smlatb r8, r5, r11, r8 @ b2 += W7 * f5
+
+ smlsdx r9, r11, r4, r9 @ b3 += f5 * W3 - f7 * W1
+ rsb r7, r7, #0 @ b1 = -b1
+ smlatb r7, r4, r10, r7 @ b1 += W3 * f1
+ smlatt r8, r4, r11, r8 @ b2 += W3 * f7
+
+ ldrd r4, L_W0246 @ load W0, W2, W4, W6
+ add r2, r2, #32 @ DC offset: 0.5
+
+ smulbb r10, r5, r3 @ a0' = W4 * f4
+ smultt r12, r5, r3 @ a3' = W6 * f6
+ smultt r3, r4, r3 @ -a2' = W2 * f6
+
+ rsb r11, r10, #0 @ a1' = -W4 * f4
+ smlabb r10, r4, r2, r10 @ a0' += W0 * f0
+ smlabb r11, r4, r2, r11 @ a1' += W0 * f0
+ smlatt r12, r4, r2, r12 @ a3' += W2 * f2
+ rsb r3, r3, #0 @ a2' = -a2'
+ smlatt r3, r5, r2, r3 @ a2' += W6 * f2
+
+ add r10, r10, r12 @ a0 = a0' + a3'
+ sub r12, r10, r12, lsl #1 @ a3 = a0 - 2 * a3'
+ add r11, r11, r3 @ a1 = a1' + a2'
+ sub r3, r11, r3, lsl #1 @ a2 = a1 - 2 * a2'
+
+ subs r14, r14, #1 @ decrease loop count
+
+ sub r2, r10, r6 @ block[7] = (a0 - b0)
+ mov r2, r2, asr #17 @ >> 17
+ strh r2, [r1, #7*16]
+ sub r2, r11, r7 @ block[6] = (a1 - b1)
+ mov r2, r2, asr #17 @ >> 17
+ strh r2, [r1, #6*16]
+ sub r2, r3, r8 @ block[5] = (a2 - b2)
+ mov r2, r2, asr #17 @ >> 17
+ strh r2, [r1, #5*16]
+ sub r2, r12, r9 @ block[4] = (a3 - b3)
+ mov r2, r2, asr #17 @ >> 17
+ strh r2, [r1, #4*16]
+ add r2, r12, r9 @ block[3] = (a3 + b3)
+ mov r2, r2, asr #17 @ >> 17
+ strh r2, [r1, #3*16]
+ add r2, r3, r8 @ block[2] = (a2 + b2)
+ mov r2, r2, asr #17 @ >> 17
+ strh r2, [r1, #2*16]
+ add r2, r11, r7 @ block[1] = (a1 + b1)
+ mov r2, r2, asr #17 @ >> 17
+ strh r2, [r1, #1*16]
+ add r2, r10, r6 @ block[0] = (a0 + b0)
+ mov r2, r2, asr #17 @ >> 17
+ strh r2, [r1], #2 @ advance to next column
+
+ bne .col_loop
+
+ sub r0, r0, #256 @ point r0 back to the input block
+ ldr pc, [sp], #4
+
+
+mpeg2_idct_copy:
+ stmfd sp!, {r1-r2, r4-r11, lr}
+ bl .idct
+ ldmfd sp!, {r1-r2}
+
+ add r3, r0, #128
+ mov r8, #0
+ mov r9, #0
+ mov r10, #0
+ mov r11, #0
+1: @ idct data is in order 0-2-1-3-4-6-5-7,
+ ldmia r0, {r4-r7} @ see above
+ stmia r0!, {r8-r11}
+ usat16 r4, #8, r4
+ usat16 r5, #8, r5
+ orr r4, r4, r5, lsl #8
+ usat16 r6, #8, r6
+ usat16 r7, #8, r7
+ orr r5, r6, r7, lsl #8
+ strd r4, [r1] @ r4, r5
+ add r1, r1, r2
+ cmp r0, r3
+ blo 1b
+
+ ldmfd sp!, {r4-r11, pc}
+
+mpeg2_idct_add:
+ cmp r0, #129
+ mov r0, r1
+ ldreqsh r1, [r0, #0]
+ bne 1f
+ and r1, r1, #0x70
+ cmp r1, #0x40
+ bne 3f
+1:
+ stmfd sp!, {r2-r11, lr}
+ bl .idct
+ ldmfd sp!, {r1-r2}
+
+ add r3, r0, #128
+ mov r10, #0
+ mov r11, #0
+ mov r12, #0
+ mov lr, #0
+ ldrd r8, [r1] @ r8, r9
+2: @ idct data is in order 0-2-1-3-4-6-5-7,
+ ldmia r0, {r4-r7} @ see above
+ stmia r0!, {r10-r12, lr}
+ uxtab16 r4, r4, r8
+ uxtab16 r5, r5, r8, ror #8
+ usat16 r4, #8, r4
+ usat16 r5, #8, r5
+ orr r4, r4, r5, lsl #8
+ uxtab16 r6, r6, r9
+ uxtab16 r7, r7, r9, ror #8
+ usat16 r6, #8, r6
+ usat16 r7, #8, r7
+ orr r5, r6, r7, lsl #8
+ strd r4, [r1] @ r4, r5
+ add r1, r1, r2
+ cmp r0, r3
+ ldrlod r8, [r1] @ r8, r9
+ blo 2b
+
+ ldmfd sp!, {r4-r11, pc}
+
+3:
+ stmfd sp!, {r4, lr}
+ ldrsh r4, [r0, #0] @ r4 = block[0]
+ mov r12, #0
+ strh r12, [r0, #0] @ block[0] = 0
+ strh r12, [r0, #126] @ block[63] = 0
+ add r4, r4, #64
+ mov r4, r4, asr #7 @ r4 = DC
+ mov r4, r4, lsl #16 @ spread to 2 halfwords
+ orr r4, r4, r4, lsr #16
+ ldrd r0, [r2] @ r0, r1
+ add r12, r2, r3, asl #3
+4:
+ uxtab16 lr, r4, r0, ror #8
+ uxtab16 r0, r4, r0
+ usat16 lr, #8, lr
+ usat16 r0, #8, r0
+ orr r0, r0, lr, lsl #8
+ uxtab16 lr, r4, r1, ror #8
+ uxtab16 r1, r4, r1
+ usat16 lr, #8, lr
+ usat16 r1, #8, r1
+ orr r1, r1, lr, lsl #8
+ strd r0, [r2] @ r0, r1
+ add r2, r2, r3
+ cmp r2, r12
+ ldrlod r0, [r2] @ r0, r1
+ blo 4b
+
+ ldmfd sp!, {r4, pc}
diff --git a/apps/plugins/mpegplayer/libmpeg2/idct_coldfire.S b/apps/plugins/mpegplayer/libmpeg2/idct_coldfire.S
new file mode 100644
index 0000000000..abc54b16cb
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/idct_coldfire.S
@@ -0,0 +1,575 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Jens Arnold
+ * Based on the work of Karim Boucher and Rani Hod
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+ .global mpeg2_idct_copy
+ .type mpeg2_idct_copy, @function
+ .global mpeg2_idct_add
+ .type mpeg2_idct_add, @function
+
+ /* The IDCT itself.
+ * Input: %a0: block pointer
+ * Caller must save all registers. */
+ .align 2
+.idct:
+ move.l %a0, %a6
+
+ move.l #0, %macsr | signed integer mode
+
+ move.l #((2048<<16)+2841), %a0 | W0, W1
+ move.l #((2676<<16)+2408), %a1 | W2, W3
+ move.l #((2048<<16)+1609), %a2 | W4, W5
+ move.l #((1108<<16)+ 565), %a3 | W6, W7
+
+ lea.l (128,%a6), %a4 | secondary, transposed temp buffer
+ moveq.l #8, %d3 | loop counter
+
+.row_loop:
+ movem.l (%a6), %d0-%d2/%a5 | fetch (f0, f2, f4, f6, f1, f3, f5, f7)
+
+ mac.w %a0l, %d2u, %acc0 | %acc0 = W1 * f1
+ mac.w %a1l, %d2l, %acc0 | + W3 * f3
+ mac.w %a2l, %a5u, %acc0 | + W5 * f5
+ mac.w %a3l, %a5l, %acc0 | + W7 * f7
+
+ mac.w %a1l, %d2u, %acc1 | %acc1 = W3 * f1
+ msac.w %a3l, %d2l, %acc1 | - W7 * f3
+ msac.w %a0l, %a5u, %acc1 | - W1 * f5
+ msac.w %a2l, %a5l, %acc1 | - W5 * f7
+
+ mac.w %a2l, %d2u, %acc2 | %acc2 = W5 * f1
+ msac.w %a0l, %d2l, %acc2 | - W1 * f3
+ mac.w %a3l, %a5u, %acc2 | + W7 * f5
+ mac.w %a1l, %a5l, %acc2 | + W3 * f7
+
+ mac.w %a3l, %d2u, %acc3 | %acc3 = W7 * f1
+ msac.w %a2l, %d2l, %acc3 | - W5 * f3
+ mac.w %a1l, %a5u, %acc3 | + W3 * f5
+ msac.w %a0l, %a5l, %acc3 | - W1 * f7
+
+ lea.l (16,%a6), %a6 | Advance to next row; put here to fill EMAC latency
+ add.l #(1<<16), %d0 | f0 += 1;
+
+ movclr.l %acc0, %d4 | b0
+ movclr.l %acc1, %d5 | b1
+ movclr.l %acc2, %d6 | b2
+ movclr.l %acc3, %d7 | b3
+
+ mac.w %a0u, %d0u, %acc0 | %acc0 = W0 * f0
+ mac.w %a2u, %d1u, %acc0 | + W4 * f4
+ move.l %acc0, %acc3
+ mac.w %a1u, %d0l, %acc0 | + W2 * f2
+ mac.w %a3u, %d1l, %acc0 | + W6 * f6
+
+ mac.w %a0u, %d0u, %acc1 | %acc1 = W0 * f0
+ msac.w %a2u, %d1u, %acc1 | - W4 * f4
+ move.l %acc1, %acc2
+ mac.w %a3u, %d0l, %acc1 | + W6 * f2
+ msac.w %a1u, %d1l, %acc1 | - W2 * f6
+
+ | ^ move.l %acc1, %acc2 %acc2 = W0 * f0 - W4 * f4
+ msac.w %a3u, %d0l, %acc2 | - W6 * f2
+ mac.w %a1u, %d1l, %acc2 | + W2 * f6
+
+ | ^ move.l %acc0, %acc3 %acc3 = W0 * f0 + W4 * f4
+ msac.w %a1u, %d0l, %acc3 | - W2 * f2
+ msac.w %a3u, %d1l, %acc3 | - W6 * f6
+
+ moveq.l #12, %d1 | shift amount
+
+ move.l %acc0, %d0 | block[7] = (a0
+ sub.l %d4,%d0 | - b0)
+ asr.l %d1, %d0 | >> 12
+ move.w %d0, (7*16,%a4)
+
+ move.l %acc1, %d0 | block[6] = (a1
+ sub.l %d5,%d0 | - b1)
+ asr.l %d1, %d0 | >> 12
+ move.w %d0, (6*16,%a4)
+
+ move.l %acc2, %d0 | block[5] = (a2
+ sub.l %d6,%d0 | - b2)
+ asr.l %d1, %d0 | >> 12
+ move.w %d0, (5*16,%a4)
+
+ move.l %acc3, %d0 | block[4] = (a3
+ sub.l %d7,%d0 | - b3)
+ asr.l %d1, %d0 | >> 12
+ move.w %d0, (4*16,%a4)
+
+ movclr.l %acc3, %d0 | block[3] = (a3
+ add.l %d7, %d0 | + b3)
+ asr.l %d1, %d0 | >> 12
+ move.w %d0, (3*16,%a4)
+
+ movclr.l %acc2, %d0 | block[2] = (a2
+ add.l %d6, %d0 | + b2)
+ asr.l %d1, %d0 | >> 12
+ move.w %d0, (2*16,%a4)
+
+ movclr.l %acc1, %d0 | block[1] = (a1
+ add.l %d5, %d0 | + b1)
+ asr.l %d1, %d0 | >> 12
+ move.w %d0, (1*16,%a4)
+
+ movclr.l %acc0, %d0 | block[0] = (a0
+ add.l %d4, %d0 | + b0)
+ asr.l %d1, %d0 | >> 12
+ move.w %d0, (%a4)+ | advance to next temp column
+
+ subq.l #1, %d3 | loop 8 times
+ bne.w .row_loop
+
+ | %a6 now points to the temp buffer, where we need it.
+ lea.l (-16-128,%a4), %a4 | point %a4 back to the input block
+ moveq.l #8, %d3 | loop counter
+
+.col_loop:
+ movem.l (%a6), %d0-%d2/%a5 | fetch (f0, f2, f4, f6, f1, f3, f5, f7)
+
+ mac.w %a0l, %d2u, %acc0 | %acc0 = W1 * f1
+ mac.w %a1l, %d2l, %acc0 | + W3 * f3
+ mac.w %a2l, %a5u, %acc0 | + W5 * f5
+ mac.w %a3l, %a5l, %acc0 | + W7 * f7
+
+ mac.w %a1l, %d2u, %acc1 | %acc1 = W3 * f1
+ msac.w %a3l, %d2l, %acc1 | - W7 * f3
+ msac.w %a0l, %a5u, %acc1 | - W1 * f5
+ msac.w %a2l, %a5l, %acc1 | - W5 * f7
+
+ mac.w %a2l, %d2u, %acc2 | %acc2 = W5 * f1
+ msac.w %a0l, %d2l, %acc2 | - W1 * f3
+ mac.w %a3l, %a5u, %acc2 | + W7 * f5
+ mac.w %a1l, %a5l, %acc2 | + W3 * f7
+
+ mac.w %a3l, %d2u, %acc3 | %acc3 = W7 * f1
+ msac.w %a2l, %d2l, %acc3 | - W5 * f3
+ mac.w %a1l, %a5u, %acc3 | + W3 * f5
+ msac.w %a0l, %a5l, %acc3 | - W1 * f7
+
+ lea.l (16,%a6), %a6 | Advance to next row; put here to fill EMAC latency
+ add.l #(32<<16), %d0 | DC offset: 0.5
+
+ movclr.l %acc0, %d4 | b0
+ movclr.l %acc1, %d5 | b1
+ movclr.l %acc2, %d6 | b2
+ movclr.l %acc3, %d7 | b3
+
+ mac.w %a0u, %d0u, %acc0 | %acc0 = W0 * f0
+ mac.w %a2u, %d1u, %acc0 | + W4 * f4
+ move.l %acc0, %acc3
+ mac.w %a1u, %d0l, %acc0 | + W2 * f2
+ mac.w %a3u, %d1l, %acc0 | + W6 * f6
+
+ mac.w %a0u, %d0u, %acc1 | %acc1 = W0 * f0
+ msac.w %a2u, %d1u, %acc1 | - W4 * f4
+ move.l %acc1, %acc2
+ mac.w %a3u, %d0l, %acc1 | + W6 * f2
+ msac.w %a1u, %d1l, %acc1 | - W2 * f6
+
+ | ^ move.l %acc1, %acc2 %acc2 = W0 * f0 - W4 * f4
+ msac.w %a3u, %d0l, %acc2 | - W6 * f2
+ mac.w %a1u, %d1l, %acc2 | + W2 * f6
+
+ | ^ move.l %acc0, %acc3 %acc3 = W0 * f0 + W4 * f4
+ msac.w %a1u, %d0l, %acc3 | - W2 * f2
+ msac.w %a3u, %d1l, %acc3 | - W6 * f6
+
+ moveq.l #17, %d1 | shift amount
+
+ move.l %acc0, %d0 | block[7] = (a0
+ sub.l %d4,%d0 | - b0)
+ asr.l %d1, %d0 | >> 17
+ move.w %d0, (7*16,%a4)
+
+ move.l %acc1, %d0 | block[6] = (a1
+ sub.l %d5,%d0 | - b1)
+ asr.l %d1, %d0 | >> 17
+ move.w %d0, (6*16,%a4)
+
+ move.l %acc2, %d0 | block[5] = (a2
+ sub.l %d6,%d0 | - b2)
+ asr.l %d1, %d0 | >> 17
+ move.w %d0, (5*16,%a4)
+
+ move.l %acc3, %d0 | block[4] = (a3
+ sub.l %d7,%d0 | - b3)
+ asr.l %d1, %d0 | >> 17
+ move.w %d0, (4*16,%a4)
+
+ movclr.l %acc3, %d0 | block[3] = (a3
+ add.l %d7, %d0 | + b3)
+ asr.l %d1, %d0 | >> 17
+ move.w %d0, (3*16,%a4)
+
+ movclr.l %acc2, %d0 | block[2] = (a2
+ add.l %d6, %d0 | + b2)
+ asr.l %d1, %d0 | >> 17
+ move.w %d0, (2*16,%a4)
+
+ movclr.l %acc1, %d0 | block[1] = (a1
+ add.l %d5, %d0 | + b1)
+ asr.l %d1, %d0 | >> 17
+ move.w %d0, (1*16,%a4)
+
+ movclr.l %acc0, %d0 | block[0] = (a0
+ add.l %d4, %d0 | + b0)
+ asr.l %d1, %d0 | >> 17
+ move.w %d0, (%a4)+ | advance to next column
+
+ subq.l #1, %d3 | loop 8 times
+ bne.w .col_loop
+
+ rts
+
+ .align 2
+
+mpeg2_idct_copy:
+ lea.l (-11*4,%sp), %sp
+ movem.l %d2-%d7/%a2-%a6, (%sp) | save some registers
+ move.l (11*4+4,%sp), %a0 | %a0 - block pointer for idct
+
+ bsr.w .idct | apply idct to block
+ movem.l (11*4+4,%sp), %a0-%a2 | %a0 - block pointer
+ | %a1 - destination pointer
+ | %a2 - stride
+
+ move.l #255, %d1 | preload constant for clipping
+ moveq.l #8, %d4 | loop counter
+
+.copy_clip_loop:
+ move.w (%a0), %d0 | load block[0]
+ ext.l %d0 | sign extend
+ cmp.l %d1, %d0 | overflow?
+ bls.b 1f
+ spl.b %d0 | yes: set appropriate limit value in low byte
+1:
+ move.b %d0, %d2 | collect output bytes 0..3 in %d2
+ lsl.l #8, %d2
+
+ move.w (2,%a0), %d0 | load block[1]
+ ext.l %d0 | sign extend
+ cmp.l %d1, %d0 | overflow?
+ bls.b 1f
+ spl.b %d0 | yes: set appropriate limit value in low byte
+1:
+ move.b %d0, %d2 | collect output bytes 0..3 in %d2
+ lsl.l #8, %d2
+ clr.l (%a0)+ | clear block[0] and block[1],
+ | %a0 now pointing to block[2]
+ move.w (%a0), %d0 | do b2 and b3
+ ext.l %d0
+ cmp.l %d1, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.b %d0, %d2
+ lsl.l #8, %d2
+
+ move.w (2,%a0), %d0
+ ext.l %d0
+ cmp.l %d1, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.b %d0, %d2
+ clr.l (%a0)+
+
+ move.w (%a0), %d0 | do b4 and b5
+ ext.l %d0
+ cmp.l %d1, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.b %d0, %d3
+ lsl.l #8, %d3
+
+ move.w (2,%a0), %d0
+ ext.l %d0
+ cmp.l %d1, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.b %d0, %d3
+ lsl.l #8, %d3
+ clr.l (%a0)+
+
+ move.w (%a0), %d0 | do b6 and b7
+ ext.l %d0
+ cmp.l %d1, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.b %d0, %d3
+ lsl.l #8, %d3
+
+ move.w (2,%a0), %d0
+ ext.l %d0
+ cmp.l %d1, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.b %d0, %d3
+ clr.l (%a0)+
+
+ movem.l %d2-%d3, (%a1) | write all 8 output bytes at once
+ add.l %a2, %a1 | advance output pointer
+ subq.l #1, %d4 | loop 8 times
+ bne.w .copy_clip_loop
+
+ movem.l (%sp), %d2-%d7/%a2-%a6
+ lea.l (11*4,%sp), %sp
+ rts
+
+ .align 2
+
+mpeg2_idct_add:
+ lea.l (-11*4,%sp), %sp
+ movem.l %d2-%d7/%a2-%a6, (%sp)
+ movem.l (11*4+4,%sp), %d0/%a0-%a2 | %d0 - last value
+ | %a0 - block pointer
+ | %a1 - destination pointer
+ | %a2 - stride
+
+ cmp.l #129, %d0 | last == 129 ?
+ bne.b .idct_add | no: perform idct + addition
+ move.w (%a0), %d0
+ ext.l %d0 | ((block[0]
+ asr.l #4, %d0 | >> 4)
+ and.l #7, %d0 | & 7)
+ subq.l #4, %d0 | - 4 == 0 ?
+ bne.w .dc_add | no: just perform addition
+
+.idct_add:
+ bsr.w .idct | apply idct
+ movem.l (11*4+8,%sp), %a0-%a2 | reload arguments %a0..%a2
+
+ move.l #255, %d2 | preload constant for clipping
+ clr.l %d3 | used for splitting input words into bytes
+ moveq.l #8, %d4 | loop counter
+
+.add_clip_loop:
+ movem.l (%a1), %d6-%d7 | fetch (b0 b1 b2 b3) (b4 b5 b6 b7)
+ swap %d6 | (b2 b3 b0 b1)
+ swap %d7 | (b6 b7 b4 b5)
+
+ move.w (2,%a0), %d0 | load block[1]
+ ext.l %d0 | sign extend
+ move.b %d6, %d3 | copy b1
+ lsr.l #8, %d6 | prepare 1st buffer for next byte
+ add.l %d3, %d0 | add b1
+ cmp.l %d2, %d0 | overflow ?
+ bls.b 1f
+ spl.b %d0 | yes: set appropriate limit value in low byte
+1:
+ move.w (%a0), %d1 | load block[0]
+ ext.l %d1 | sign extend
+ move.b %d6, %d3 | copy b0
+ lsr.l #8, %d6 | prepare 1st buffer for next byte
+ add.l %d3, %d1 | add b0
+ cmp.l %d2, %d1 | overflow ?
+ bls.b 1f
+ spl.b %d1 | yes: set appropriate limit value in low byte
+1:
+ move.b %d1, %d5 | collect output bytes 0..3 in %d5
+ lsl.l #8, %d5
+ move.b %d0, %d5
+ lsl.l #8, %d5
+ clr.l (%a0)+ | clear block[0] and block[1]
+ | %a0 now pointing to block[2]
+ move.w (2,%a0), %d0 | do b3 and b2
+ ext.l %d0
+ move.b %d6, %d3
+ lsr.l #8, %d6
+ add.l %d3, %d0
+ cmp.l %d2, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.w (%a0), %d1
+ ext.l %d1
+ add.l %d6, %d1
+ cmp.l %d2, %d1
+ bls.b 1f
+ spl.b %d1
+1:
+ move.b %d1, %d5
+ lsl.l #8, %d5
+ move.b %d0, %d5
+ clr.l (%a0)+
+
+ move.w (2,%a0), %d0 | do b5 and b4
+ ext.l %d0
+ move.b %d7, %d3
+ lsr.l #8, %d7
+ add.l %d3, %d0
+ cmp.l %d2, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.w (%a0), %d1
+ ext.l %d1
+ move.b %d7, %d3
+ lsr.l #8, %d7
+ add.l %d3, %d1
+ cmp.l %d2, %d1
+ bls.b 1f
+ spl.b %d1
+1:
+ move.b %d1, %d6
+ lsl.l #8, %d6
+ move.b %d0, %d6
+ lsl.l #8, %d6
+ clr.l (%a0)+
+
+ move.w (2,%a0), %d0 | do b7 and b6
+ ext.l %d0
+ move.b %d7, %d3
+ lsr.l #8, %d7
+ add.l %d3, %d0
+ cmp.l %d2, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.w (%a0), %d1
+ ext.l %d1
+ add.l %d7, %d1
+ cmp.l %d2, %d1
+ bls.b 1f
+ spl.b %d1
+1:
+ move.b %d1, %d6
+ lsl.l #8, %d6
+ move.b %d0, %d6
+ clr.l (%a0)+
+
+ movem.l %d5-%d6, (%a1) | write all 8 output bytes at once
+ add.l %a2, %a1 | advance output pointer
+ subq.l #1, %d4 | loop 8 times
+ bne.w .add_clip_loop
+
+ bra.w .idct_add_end
+
+.dc_add:
+ move.w (%a0), %d0
+ ext.l %d0 | %d0 = (block[0]
+ add.l #64, %d0 | + 64)
+ asr.l #7, %d0 | >> 7
+ clr.w (%a0) | clear block[0]
+ clr.w (63*2,%a0) | and block[63]
+ move.l %d0, %a0 | DC value in %a0
+
+ move.l #255, %d2 | preload constant for clipping
+ clr.l %d3 | for splitting input words into bytes
+ moveq.l #8, %d4 | loop counter
+
+.dc_clip_loop:
+ movem.l (%a1), %d6-%d7 | (b0 b1 b2 b3) (b4 b5 b6 b7)
+ swap %d6 | (b2 b3 b0 b1)
+ swap %d7 | (b6 b7 b4 b5)
+
+ move.l %a0, %d0 | copy DC
+ move.b %d6, %d3 | copy b1
+ lsr.l #8, %d6 | prepare 1st buffer for next byte
+ add.l %d3, %d0 | add b1
+ cmp.l %d2, %d0 | overflow ?
+ bls.b 1f
+ spl.b %d0 | yes: set appropriate limit value in low byte
+1:
+ move.l %a0, %d1 | copy DC
+ move.b %d6, %d3 | copy b0
+ lsr.l #8, %d6 | prepare 1st buffer for next byte
+ add.l %d3, %d1 | add b0
+ cmp.l %d2, %d1 | overflow ?
+ bls.b 1f
+ spl.b %d1 | yes: set appropriate limit value in low byte
+1:
+ move.b %d1, %d5 | collect output bytes 0..3 in %d5
+ lsl.l #8, %d5
+ move.b %d0, %d5
+ lsl.l #8, %d5
+
+ move.l %a0, %d0 | do b3 and b2
+ move.b %d6, %d3
+ lsr.l #8, %d6
+ add.l %d3, %d0
+ cmp.l %d2, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.l %a0, %d1
+ add.l %d6, %d1
+ cmp.l %d2, %d1
+ bls.b 1f
+ spl.b %d1
+1:
+ move.b %d1, %d5
+ lsl.l #8, %d5
+ move.b %d0, %d5
+
+ move.l %a0, %d0 | do b5 and b4
+ move.b %d7, %d3
+ lsr.l #8, %d7
+ add.l %d3, %d0
+ cmp.l %d2, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.l %a0, %d1
+ move.b %d7, %d3
+ lsr.l #8, %d7
+ add.l %d3, %d1
+ cmp.l %d2, %d1
+ bls.b 1f
+ spl.b %d1
+1:
+ move.b %d1, %d6 | do b7 and b6
+ lsl.l #8, %d6
+ move.b %d0, %d6
+ lsl.l #8, %d6
+
+ move.l %a0, %d0
+ move.b %d7, %d3
+ lsr.l #8, %d7
+ add.l %d3, %d0
+ cmp.l %d2, %d0
+ bls.b 1f
+ spl.b %d0
+1:
+ move.l %a0, %d1
+ add.l %d7, %d1
+ cmp.l %d2, %d1
+ bls.b 1f
+ spl.b %d1
+1:
+ move.b %d1, %d6
+ lsl.l #8, %d6
+ move.b %d0, %d6
+
+ movem.l %d5-%d6, (%a1) | write all 8 output bytes at once
+ add.l %a2, %a1 | advance output pointer
+ subq.l #1, %d4 | loop 8 times
+ bne.w .dc_clip_loop
+
+.idct_add_end:
+ movem.l (%sp), %d2-%d7/%a2-%a6
+ lea.l (11*4,%sp), %sp
+ rts
diff --git a/apps/plugins/mpegplayer/libmpeg2/motion_comp.c b/apps/plugins/mpegplayer/libmpeg2/motion_comp.c
new file mode 100644
index 0000000000..d6968f68ce
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/motion_comp.c
@@ -0,0 +1,66 @@
+/*
+ * motion_comp.c
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.17 - lost compatibility previously to
+ * provide simplified and CPU-optimized motion compensation.
+ */
+
+#include "plugin.h"
+
+#include "mpeg2dec_config.h"
+
+#include "mpeg2.h"
+#include "attributes.h"
+#include "mpeg2_internal.h"
+
+/* These are defined in their respective target files - motion_comp_X.c */
+extern mpeg2_mc_fct MC_put_o_16;
+extern mpeg2_mc_fct MC_put_o_8;
+extern mpeg2_mc_fct MC_put_x_16;
+extern mpeg2_mc_fct MC_put_x_8;
+extern mpeg2_mc_fct MC_put_y_16;
+extern mpeg2_mc_fct MC_put_y_8;
+extern mpeg2_mc_fct MC_put_xy_16;
+extern mpeg2_mc_fct MC_put_xy_8;
+
+extern mpeg2_mc_fct MC_avg_o_16;
+extern mpeg2_mc_fct MC_avg_o_8;
+extern mpeg2_mc_fct MC_avg_x_16;
+extern mpeg2_mc_fct MC_avg_x_8;
+extern mpeg2_mc_fct MC_avg_y_16;
+extern mpeg2_mc_fct MC_avg_y_8;
+extern mpeg2_mc_fct MC_avg_xy_16;
+extern mpeg2_mc_fct MC_avg_xy_8;
+
+const mpeg2_mc_t mpeg2_mc =
+{
+ {
+ MC_put_o_16, MC_put_x_16, MC_put_y_16, MC_put_xy_16,
+ MC_put_o_8, MC_put_x_8, MC_put_y_8, MC_put_xy_8
+ },
+ {
+ MC_avg_o_16, MC_avg_x_16, MC_avg_y_16, MC_avg_xy_16,
+ MC_avg_o_8, MC_avg_x_8, MC_avg_y_8, MC_avg_xy_8
+ }
+};
diff --git a/apps/plugins/mpegplayer/libmpeg2/motion_comp.h b/apps/plugins/mpegplayer/libmpeg2/motion_comp.h
new file mode 100644
index 0000000000..4737e72cab
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/motion_comp.h
@@ -0,0 +1,86 @@
+/*
+ * motion_comp.h
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ */
+
+
+#define avg2(a,b) ((a+b+1)>>1)
+#define avg4(a,b,c,d) ((a+b+c+d+2)>>2)
+
+#define predict_o(i) (ref[i])
+#define predict_x(i) (avg2 (ref[i], ref[i+1]))
+#define predict_y(i) (avg2 (ref[i], (ref+stride)[i]))
+#define predict_xy(i) (avg4 (ref[i], ref[i+1], \
+ (ref+stride)[i], (ref+stride)[i+1]))
+
+#define put(predictor,i) dest[i] = predictor (i)
+#define avg(predictor,i) dest[i] = avg2 (predictor (i), dest[i])
+
+/* mc function template */
+#define MC_FUNC(op, xy) \
+ MC_FUNC_16(op, xy) \
+ MC_FUNC_8(op, xy)
+
+#define MC_FUNC_16(op, xy) \
+ void MC_##op##_##xy##_16 (uint8_t * dest, const uint8_t * ref, \
+ const int stride, int height) \
+ { \
+ do { \
+ op (predict_##xy, 0); \
+ op (predict_##xy, 1); \
+ op (predict_##xy, 2); \
+ op (predict_##xy, 3); \
+ op (predict_##xy, 4); \
+ op (predict_##xy, 5); \
+ op (predict_##xy, 6); \
+ op (predict_##xy, 7); \
+ op (predict_##xy, 8); \
+ op (predict_##xy, 9); \
+ op (predict_##xy, 10); \
+ op (predict_##xy, 11); \
+ op (predict_##xy, 12); \
+ op (predict_##xy, 13); \
+ op (predict_##xy, 14); \
+ op (predict_##xy, 15); \
+ ref += stride; \
+ dest += stride; \
+ } while (--height); \
+ }
+
+#define MC_FUNC_8(op, xy) \
+ void MC_##op##_##xy##_8 (uint8_t * dest, const uint8_t * ref, \
+ const int stride, int height) \
+ { \
+ do { \
+ op (predict_##xy, 0); \
+ op (predict_##xy, 1); \
+ op (predict_##xy, 2); \
+ op (predict_##xy, 3); \
+ op (predict_##xy, 4); \
+ op (predict_##xy, 5); \
+ op (predict_##xy, 6); \
+ op (predict_##xy, 7); \
+ ref += stride; \
+ dest += stride; \
+ } while (--height); \
+ }
diff --git a/apps/plugins/mpegplayer/libmpeg2/motion_comp_arm_c.c b/apps/plugins/mpegplayer/libmpeg2/motion_comp_arm_c.c
new file mode 100644
index 0000000000..dcf1df53e9
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/motion_comp_arm_c.c
@@ -0,0 +1,39 @@
+/*
+ * motion_comp_arm.c
+ * Copyright (C) 2004 AGAWA Koji <i (AT) atty (DOT) jp>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ */
+#include <inttypes.h>
+#include "mpeg2.h"
+#include "attributes.h"
+#include "mpeg2_internal.h"
+#include "motion_comp.h"
+
+/* definitions of the actual mc functions */
+
+/* MC_FUNC (put, o) <= ASM */
+MC_FUNC (avg, o)
+/* MC_FUNC (put, x) <= ASM */
+MC_FUNC (avg, x)
+MC_FUNC (put, y)
+MC_FUNC (avg, y)
+MC_FUNC (put, xy)
+MC_FUNC (avg, xy)
diff --git a/apps/plugins/mpegplayer/libmpeg2/motion_comp_arm_s.S b/apps/plugins/mpegplayer/libmpeg2/motion_comp_arm_s.S
new file mode 100644
index 0000000000..1ec1b0686e
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/motion_comp_arm_s.S
@@ -0,0 +1,342 @@
+@ motion_comp_arm_s.S
+@ Copyright (C) 2004 AGAWA Koji <i (AT) atty (DOT) jp>
+@
+@ This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+@ See http://libmpeg2.sourceforge.net/ for updates.
+@
+@ 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.
+@
+@ 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.
+@
+@ 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
+@
+@ $Id$
+
+#include "config.h" /* Rockbox: ARM architecture version */
+
+ .text
+
+@ ----------------------------------------------------------------
+ .align
+ .global MC_put_o_16
+MC_put_o_16:
+ @@ void func(uint8_t * dest, const uint8_t * ref, int stride, int height)
+ @@ pld [r1]
+ stmfd sp!, {r4-r7, lr} @ R14 is also called LR
+ and r4, r1, #3
+ ldr pc, [pc, r4, lsl #2]
+ .word 0
+ .word MC_put_o_16_align0
+ .word MC_put_o_16_align1
+ .word MC_put_o_16_align2
+ .word MC_put_o_16_align3
+
+MC_put_o_16_align0:
+ ldmia r1, {r4-r7}
+ add r1, r1, r2
+ @@ pld [r1]
+ stmia r0, {r4-r7}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne MC_put_o_16_align0
+ ldmpc regs=r4-r7 @@ update PC with LR content.
+
+.macro ADJ_ALIGN_QW shift, R0, R1, R2, R3, R4
+ mov \R0, \R0, lsr #(\shift)
+ orr \R0, \R0, \R1, lsl #(32 - \shift)
+ mov \R1, \R1, lsr #(\shift)
+ orr \R1, \R1, \R2, lsl #(32 - \shift)
+ mov \R2, \R2, lsr #(\shift)
+ orr \R2, \R2, \R3, lsl #(32 - \shift)
+ mov \R3, \R3, lsr #(\shift)
+ orr \R3, \R3, \R4, lsl #(32 - \shift)
+ mov \R4, \R4, lsr #(\shift)
+.endm
+
+MC_put_o_16_align1:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4-r7, r12}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_QW 8, r4, r5, r6, r7, r12
+ stmia r0, {r4-r7}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-r7 @@ update PC with LR content.
+
+MC_put_o_16_align2:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4-r7, r12}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_QW 16, r4, r5, r6, r7, r12
+ stmia r0, {r4-r7}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-r7 @@ update PC with LR content.
+
+MC_put_o_16_align3:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4-r7, r12}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_QW 24, r4, r5, r6, r7, r12
+ stmia r0, {r4-r7}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-r7 @@ update PC with LR content.
+
+@ ----------------------------------------------------------------
+ .align
+ .global MC_put_o_8
+MC_put_o_8:
+ @@ void func(uint8_t * dest, const uint8_t * ref, int stride, int height)
+ @@ pld [r1]
+ stmfd sp!, {r4, r5, lr} @ R14 is also called LR
+ and r4, r1, #3
+ ldr pc, [pc, r4, lsl #2]
+ .word 0
+ .word MC_put_o_8_align0
+ .word MC_put_o_8_align1
+ .word MC_put_o_8_align2
+ .word MC_put_o_8_align3
+
+MC_put_o_8_align0:
+ ldmia r1, {r4, r5}
+ add r1, r1, r2
+ @@ pld [r1]
+ stmia r0, {r4, r5}
+ add r0, r0, r2
+ subs r3, r3, #1
+ bne MC_put_o_8_align0
+ ldmpc regs=r4-r5 @@ update PC with LR content.
+
+.macro ADJ_ALIGN_DW shift, R0, R1, R2
+ mov \R0, \R0, lsr #(\shift)
+ orr \R0, \R0, \R1, lsl #(32 - \shift)
+ mov \R1, \R1, lsr #(\shift)
+ orr \R1, \R1, \R2, lsl #(32 - \shift)
+ mov \R2, \R2, lsr #(\shift)
+.endm
+
+MC_put_o_8_align1:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4, r5, r12}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_DW 8, r4, r5, r12
+ stmia r0, {r4, r5}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-r5 @@ update PC with LR content.
+
+MC_put_o_8_align2:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4, r5, r12}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_DW 16, r4, r5, r12
+ stmia r0, {r4, r5}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-r5 @@ update PC with LR content.
+
+MC_put_o_8_align3:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4, r5, r12}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_DW 24, r4, r5, r12
+ stmia r0, {r4, r5}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-r5 @@ update PC with LR content.
+
+@ ----------------------------------------------------------------
+.macro AVG_PW rW1, rW2
+ mov \rW2, \rW2, lsl #24
+ orr \rW2, \rW2, \rW1, lsr #8
+ eor r9, \rW1, \rW2
+#if ARM_ARCH >= 6
+ uhadd8 \rW2, \rW1, \rW2
+#else
+ and \rW2, \rW1, \rW2
+ and r10, r9, r11
+ add \rW2, \rW2, r10, lsr #1
+#endif
+ and r9, r9, r12
+ add \rW2, \rW2, r9
+.endm
+
+#if ARM_ARCH >= 6
+#define HIGHEST_REG r9
+#else
+#define HIGHEST_REG r11
+#endif
+
+ .align
+ .global MC_put_x_16
+MC_put_x_16:
+ @@ void func(uint8_t * dest, const uint8_t * ref, int stride, int height)
+ @@ pld [r1]
+ stmfd sp!, {r4-HIGHEST_REG, lr} @ R14 is also called LR
+ and r4, r1, #3
+ ldr r12, 2f
+#if ARM_ARCH < 6
+ mvn r11, r12
+#endif
+ ldr pc, [pc, r4, lsl #2]
+2: .word 0x01010101
+ .word MC_put_x_16_align0
+ .word MC_put_x_16_align1
+ .word MC_put_x_16_align2
+ .word MC_put_x_16_align3
+
+MC_put_x_16_align0:
+ ldmia r1, {r4-r8}
+ add r1, r1, r2
+ @@ pld [r1]
+ AVG_PW r7, r8
+ AVG_PW r6, r7
+ AVG_PW r5, r6
+ AVG_PW r4, r5
+ stmia r0, {r5-r8}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne MC_put_x_16_align0
+ ldmpc regs=r4-HIGHEST_REG @@ update PC with LR content.
+
+MC_put_x_16_align1:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4-r8}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_QW 8, r4, r5, r6, r7, r8
+ AVG_PW r7, r8
+ AVG_PW r6, r7
+ AVG_PW r5, r6
+ AVG_PW r4, r5
+ stmia r0, {r5-r8}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-HIGHEST_REG @@ update PC with LR content.
+
+MC_put_x_16_align2:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4-r8}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_QW 16, r4, r5, r6, r7, r8
+ AVG_PW r7, r8
+ AVG_PW r6, r7
+ AVG_PW r5, r6
+ AVG_PW r4, r5
+ stmia r0, {r5-r8}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-HIGHEST_REG @@ update PC with LR content.
+
+MC_put_x_16_align3:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r4-r8}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_QW 24, r4, r5, r6, r7, r8
+ AVG_PW r7, r8
+ AVG_PW r6, r7
+ AVG_PW r5, r6
+ AVG_PW r4, r5
+ stmia r0, {r5-r8}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r4-HIGHEST_REG @@ update PC with LR content.
+
+@ ----------------------------------------------------------------
+ .align
+ .global MC_put_x_8
+MC_put_x_8:
+ @@ void func(uint8_t * dest, const uint8_t * ref, int stride, int height)
+ @@ pld [r1]
+ stmfd sp!, {r6-HIGHEST_REG, lr} @ R14 is also called LR
+ and r6, r1, #3
+ ldr r12, 2f
+#if ARM_ARCH < 6
+ mvn r11, r12
+#endif
+ ldr pc, [pc, r6, lsl #2]
+2: .word 0x01010101
+ .word MC_put_x_8_align0
+ .word MC_put_x_8_align1
+ .word MC_put_x_8_align2
+ .word MC_put_x_8_align3
+
+MC_put_x_8_align0:
+ ldmia r1, {r6-r8}
+ add r1, r1, r2
+ @@ pld [r1]
+ AVG_PW r7, r8
+ AVG_PW r6, r7
+ stmia r0, {r7-r8}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne MC_put_x_8_align0
+ ldmpc regs=r6-HIGHEST_REG @@ update PC with LR content.
+
+MC_put_x_8_align1:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r6-r8}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_DW 8, r6, r7, r8
+ AVG_PW r7, r8
+ AVG_PW r6, r7
+ stmia r0, {r7-r8}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r6-HIGHEST_REG @@ update PC with LR content.
+
+MC_put_x_8_align2:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r6-r8}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_DW 16, r6, r7, r8
+ AVG_PW r7, r8
+ AVG_PW r6, r7
+ stmia r0, {r7-r8}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r6-HIGHEST_REG @@ update PC with LR content.
+
+MC_put_x_8_align3:
+ and r1, r1, #0xFFFFFFFC
+1: ldmia r1, {r6-r8}
+ add r1, r1, r2
+ @@ pld [r1]
+ ADJ_ALIGN_DW 24, r6, r7, r8
+ AVG_PW r7, r8
+ AVG_PW r6, r7
+ stmia r0, {r7-r8}
+ subs r3, r3, #1
+ add r0, r0, r2
+ bne 1b
+ ldmpc regs=r6-HIGHEST_REG @@ update PC with LR content.
diff --git a/apps/plugins/mpegplayer/libmpeg2/motion_comp_c.c b/apps/plugins/mpegplayer/libmpeg2/motion_comp_c.c
new file mode 100644
index 0000000000..9a8640e7e6
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/motion_comp_c.c
@@ -0,0 +1,40 @@
+/*
+ * motion_comp.c
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ */
+#include <inttypes.h>
+#include "mpeg2.h"
+#include "attributes.h"
+#include "mpeg2_internal.h"
+#include "motion_comp.h"
+
+/* definitions of the actual mc functions */
+
+MC_FUNC (put, o)
+MC_FUNC (avg, o)
+MC_FUNC (put, x)
+MC_FUNC (avg, x)
+MC_FUNC (put, y)
+MC_FUNC (avg, y)
+MC_FUNC (put, xy)
+MC_FUNC (avg, xy)
diff --git a/apps/plugins/mpegplayer/libmpeg2/motion_comp_coldfire_c.c b/apps/plugins/mpegplayer/libmpeg2/motion_comp_coldfire_c.c
new file mode 100644
index 0000000000..b97e3510e7
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/motion_comp_coldfire_c.c
@@ -0,0 +1,38 @@
+/*
+ * Based on:
+ * motion_comp_arm.c
+ * Copyright (C) 2004 AGAWA Koji <i (AT) atty (DOT) jp>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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 <inttypes.h>
+#include "mpeg2.h"
+#include "attributes.h"
+#include "mpeg2_internal.h"
+#include "motion_comp.h"
+
+/* definitions of the actual mc functions */
+
+/* MC_FUNC (put, o) <= ASM */
+MC_FUNC (avg, o)
+/* MC_FUNC (put, x) <= ASM */
+MC_FUNC (avg, x)
+MC_FUNC (put, y)
+MC_FUNC (avg, y)
+MC_FUNC (put, xy)
+MC_FUNC (avg, xy)
diff --git a/apps/plugins/mpegplayer/libmpeg2/motion_comp_coldfire_s.S b/apps/plugins/mpegplayer/libmpeg2/motion_comp_coldfire_s.S
new file mode 100644
index 0000000000..55d87cb708
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/motion_comp_coldfire_s.S
@@ -0,0 +1,436 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Jens Arnold
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+.macro LEFT8_PW dW1, dW2 | needs %d0 == 24, clobbers %d2
+ lsl.l #8, \dW1 | changes dW1, keeps dW2
+ move.l \dW2, %d2
+ lsr.l %d0, %d2
+ or.l %d2, \dW1
+.endm
+
+.macro LEFT24_PW dW1, dW2 | needs %d0 == 24, clobbers %d2
+ lsl.l %d0, \dW1 | changes dW1, keeps dW2
+ move.l \dW2, %d2
+ lsr.l #8, %d2
+ or.l %d2, \dW1
+.endm
+
+/*****************************************************************************/
+
+ .align 2
+ .global MC_put_o_8
+ .type MC_put_o_8, @function
+
+MC_put_o_8:
+ movem.l (4,%sp), %a0-%a1 | dest, source
+ move.l %a1, %d0
+ and.l #3, %d0
+ sub.l %d0, %a1 | align source
+ jmp.l (2, %pc, %d0.l*4)
+ bra.w .po8_0
+ bra.w .po8_1
+ bra.w .po8_2
+ | last table entry coincides with target
+
+.po8_3:
+ lea.l (-5*4,%sp), %sp
+ movem.l %d2-%d5/%a2, (%sp) | save some registers
+ move.l (5*4+12,%sp), %a2 | stride
+ move.l (5*4+16,%sp), %d1 | height
+ moveq.l #24, %d0 | shift amount
+1:
+ movem.l (%a1), %d3-%d5
+ add.l %a2, %a1
+ LEFT24_PW %d3, %d4
+ lsl.l %d0, %d4
+ lsr.l #8, %d5
+ or.l %d5, %d4
+ movem.l %d3-%d4, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d1
+ bne.s 1b
+ movem.l (%sp), %d2-%d5/%a2
+ lea.l (5*4,%sp), %sp
+ rts
+
+.po8_2:
+ lea.l (-3*4,%sp), %sp
+ movem.l %d2-%d4, (%sp) | save some registers
+ movem.l (3*4+12,%sp), %d0-%d1 | stride, height
+1:
+ movem.l (%a1), %d2-%d4
+ add.l %d0, %a1
+ swap %d2
+ swap %d3
+ move.w %d3, %d2
+ swap %d4
+ move.w %d4, %d3
+ movem.l %d2-%d3, (%a0)
+ add.l %d0, %a0
+ subq.l #1, %d1
+ bne.s 1b
+ movem.l (%sp), %d2-%d4
+ lea.l (3*4,%sp), %sp
+ rts
+
+.po8_1:
+ lea.l (-5*4,%sp), %sp
+ movem.l %d2-%d5/%a2, (%sp) | save some registers
+ move.l (5*4+12,%sp), %a2 | stride
+ move.l (5*4+16,%sp), %d1 | height
+ moveq.l #24, %d0 | shift amount
+1:
+ movem.l (%a1), %d3-%d5
+ add.l %a2, %a1
+ LEFT8_PW %d3, %d4
+ lsl.l #8, %d4
+ lsr.l %d0, %d5
+ or.l %d5, %d4
+ movem.l %d3-%d4, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d1
+ bne.s 1b
+ movem.l (%sp), %d2-%d5/%a2
+ lea.l (5*4,%sp), %sp
+ rts
+
+.po8_0:
+ movem.l (12,%sp), %d0-%d1 | stride, height
+ subq.l #4, %d0 | adjust for increment within the loop
+1:
+ move.l (%a1)+, (%a0)+
+ move.l (%a1), (%a0)
+ add.l %d0, %a0
+ add.l %d0, %a1
+ subq.l #1, %d1
+ bne.s 1b
+ rts
+
+/*****************************************************************************/
+
+ .align 2
+ .global MC_put_o_16
+ .type MC_put_o_16, @function
+
+MC_put_o_16:
+ lea.l (-7*4,%sp), %sp
+ movem.l %d2-%d7/%a2, (%sp) | save some registers
+ movem.l (7*4+4,%sp), %a0-%a2| dest, source, stride
+ move.l (7*4+16,%sp), %d1 | height
+ move.l %a1, %d0
+ and.l #3, %d0
+ sub.l %d0, %a1
+ jmp.l (2, %pc, %d0.l*4)
+ bra.w .po16_0
+ bra.w .po16_1
+ bra.w .po16_2
+ | last table entry coincides with target
+
+.po16_3:
+ moveq.l #24, %d0 | shift amount
+1:
+ movem.l (%a1), %d3-%d7
+ add.l %a2, %a1
+ LEFT24_PW %d3, %d4
+ LEFT24_PW %d4, %d5
+ LEFT24_PW %d5, %d6
+ lsl.l %d0, %d6
+ lsr.l #8, %d7
+ or.l %d7, %d6
+ movem.l %d3-%d6, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d1
+ bne.s 1b
+ movem.l (%sp), %d2-%d7/%a2
+ lea.l (7*4,%sp), %sp
+ rts
+
+.po16_2:
+1:
+ movem.l (%a1), %d3-%d7
+ add.l %a2, %a1
+ swap %d3
+ swap %d4
+ move.w %d4, %d3
+ swap %d5
+ move.w %d5, %d4
+ swap %d6
+ move.w %d6, %d5
+ swap %d7
+ move.w %d7, %d6
+ movem.l %d3-%d6, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d1
+ bne.s 1b
+ movem.l (%sp), %d2-%d7/%a2
+ lea.l (7*4,%sp), %sp
+ rts
+
+.po16_1:
+ moveq.l #24, %d0 | shift amount
+1:
+ movem.l (%a1), %d3-%d7
+ add.l %a2, %a1
+ LEFT8_PW %d3, %d4
+ LEFT8_PW %d4, %d5
+ LEFT8_PW %d5, %d6
+ lsl.l #8, %d6
+ lsr.l %d0, %d7
+ or.l %d7, %d6
+ movem.l %d3-%d6, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d1
+ bne.s 1b
+ movem.l (%sp), %d2-%d7/%a2
+ lea.l (7*4,%sp), %sp
+ rts
+
+.po16_0:
+1:
+ movem.l (%a1), %d3-%d6
+ add.l %a2, %a1
+ movem.l %d3-%d6, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d1
+ bne.s 1b
+ movem.l (%sp), %d2-%d7/%a2
+ lea.l (7*4,%sp), %sp
+ rts
+
+/*****************************************************************************/
+
+.macro AVG_PW dW1, dW2 | needs %d0 == 24, clobbers %d1, %d2,
+ move.l \dW1, %d1 | changes dW1, keeps dW2
+ lsl.l #8, \dW1
+ move.l \dW2, %d2
+ lsr.l %d0, %d2
+ or.l %d2, \dW1
+ move.l %d1, %d2
+ eor.l \dW1, %d1
+ and.l %d2, \dW1
+ move.l #0xfefefefe, %d2
+ and.l %d1, %d2
+ eor.l %d2, %d1
+ lsr.l #1, %d2
+ add.l %d2, \dW1
+ add.l %d1, \dW1
+.endm
+
+/*****************************************************************************/
+
+ .align 2
+ .global MC_put_x_8
+ .type MC_put_x_8, @function
+
+MC_put_x_8:
+ lea.l (-6*4,%sp), %sp
+ movem.l %d2-%d6/%a2, (%sp) | save some registers
+ movem.l (6*4+4,%sp), %a0-%a2| dest, source, stride
+ move.l (6*4+16,%sp), %d6 | height
+ move.l %a1, %d0
+ and.l #3, %d0
+ sub.l %d0, %a1
+ jmp.l (2, %pc, %d0.l*4)
+ bra.w .px8_0
+ bra.w .px8_1
+ bra.w .px8_2
+ | last table entry coincides with target
+
+.px8_3:
+ moveq.l #24, %d0
+1:
+ movem.l (%a1), %d3-%d5
+ add.l %a2, %a1
+ LEFT24_PW %d3, %d4
+ LEFT24_PW %d4, %d5
+ lsl.l %d0, %d5
+ AVG_PW %d3, %d4
+ AVG_PW %d4, %d5
+ movem.l %d3-%d4, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d6
+ bne.s 1b
+ movem.l (%sp), %d2-%d6/%a2
+ lea.l (6*4,%sp), %sp
+ rts
+
+.px8_2:
+ moveq.l #24, %d0
+1:
+ movem.l (%a1), %d3-%d5
+ add.l %a2, %a1
+ swap %d3
+ swap %d4
+ move.w %d4, %d3
+ swap %d5
+ move.w %d5, %d4
+ AVG_PW %d3, %d4
+ AVG_PW %d4, %d5
+ movem.l %d3-%d4, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d6
+ bne.s 1b
+ movem.l (%sp), %d2-%d6/%a2
+ lea.l (6*4,%sp), %sp
+ rts
+
+.px8_1:
+ moveq.l #24, %d0
+1:
+ movem.l (%a1), %d3-%d5
+ add.l %a2, %a1
+ LEFT8_PW %d3, %d4
+ LEFT8_PW %d4, %d5
+ lsl.l #8, %d5
+ AVG_PW %d3, %d4
+ AVG_PW %d4, %d5
+ movem.l %d3-%d4, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d6
+ bne.s 1b
+ movem.l (%sp), %d2-%d6/%a2
+ lea.l (6*4,%sp), %sp
+ rts
+
+.px8_0:
+ moveq.l #24, %d0
+1:
+ movem.l (%a1), %d3-%d5
+ add.l %a2, %a1
+ AVG_PW %d3, %d4
+ AVG_PW %d4, %d5
+ movem.l %d3-%d4, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %d6
+ bne.s 1b
+ movem.l (%sp), %d2-%d6/%a2
+ lea.l (6*4,%sp), %sp
+ rts
+
+/*****************************************************************************/
+
+ .align 2
+ .global MC_put_x_16
+ .type MC_put_x_16, @function
+
+MC_put_x_16:
+ lea.l (-8*4,%sp), %sp
+ movem.l %d2-%d7/%a2-%a3, (%sp) | save some registers
+ movem.l (8*4+4,%sp), %a0-%a3 | dest, source, stride, height
+ move.l %a1, %d0
+ and.l #3, %d0
+ sub.l %d0, %a1
+ jmp.l (2, %pc, %d0.l*4)
+ bra.w .px16_0
+ bra.w .px16_1
+ bra.w .px16_2
+ | last table entry coincides with target
+
+.px16_3:
+ moveq.l #24, %d0
+1:
+ movem.l (%a1), %d3-%d7
+ add.l %a2, %a1
+ LEFT24_PW %d3, %d4
+ LEFT24_PW %d4, %d5
+ LEFT24_PW %d5, %d6
+ LEFT24_PW %d6, %d7
+ lsl.l %d0, %d7
+ AVG_PW %d3, %d4
+ AVG_PW %d4, %d5
+ AVG_PW %d5, %d6
+ AVG_PW %d6, %d7
+ movem.l %d3-%d6, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %a3
+ tst.l %a3
+ bne.w 1b
+ movem.l (%sp), %d2-%d7/%a2-%a3
+ lea.l (8*4,%sp), %sp
+ rts
+
+.px16_2:
+ moveq.l #24, %d0
+1:
+ movem.l (%a1), %d3-%d7
+ add.l %a2, %a1
+ swap %d3
+ swap %d4
+ move.w %d4, %d3
+ swap %d5
+ move.w %d5, %d4
+ swap %d6
+ move.w %d6, %d5
+ swap %d7
+ move.w %d7, %d6
+ AVG_PW %d3, %d4
+ AVG_PW %d4, %d5
+ AVG_PW %d5, %d6
+ AVG_PW %d6, %d7
+ movem.l %d3-%d6, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %a3
+ tst.l %a3
+ bne.w 1b
+ movem.l (%sp), %d2-%d7/%a2-%a3
+ lea.l (8*4,%sp), %sp
+ rts
+
+.px16_1:
+ moveq.l #24, %d0
+1:
+ movem.l (%a1), %d3-%d7
+ add.l %a2, %a1
+ LEFT8_PW %d3, %d4
+ LEFT8_PW %d4, %d5
+ LEFT8_PW %d5, %d6
+ LEFT8_PW %d6, %d7
+ lsl.l #8, %d7
+ AVG_PW %d3, %d4
+ AVG_PW %d4, %d5
+ AVG_PW %d5, %d6
+ AVG_PW %d6, %d7
+ movem.l %d3-%d6, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %a3
+ tst.l %a3
+ bne.w 1b
+ movem.l (%sp), %d2-%d7/%a2-%a3
+ lea.l (8*4,%sp), %sp
+ rts
+
+.px16_0:
+ moveq.l #24, %d0
+1:
+ movem.l (%a1), %d3-%d7
+ add.l %a2, %a1
+ AVG_PW %d3, %d4
+ AVG_PW %d4, %d5
+ AVG_PW %d5, %d6
+ AVG_PW %d6, %d7
+ movem.l %d3-%d6, (%a0)
+ add.l %a2, %a0
+ subq.l #1, %a3
+ tst.l %a3
+ bne.w 1b
+ movem.l (%sp), %d2-%d7/%a2-%a3
+ lea.l (8*4,%sp), %sp
+ rts
diff --git a/apps/plugins/mpegplayer/libmpeg2/mpeg2.h b/apps/plugins/mpegplayer/libmpeg2/mpeg2.h
new file mode 100644
index 0000000000..bd14ead96e
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/mpeg2.h
@@ -0,0 +1,223 @@
+/*
+ * mpeg2.h
+ * Copyright (C) 2000-2004 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.67
+ */
+
+#ifndef MPEG2_H
+#define MPEG2_H
+
+#include "mpeg2dec_config.h"
+
+#define MPEG2_VERSION(a,b,c) (((a)<<16)|((b)<<8)|(c))
+#define MPEG2_RELEASE MPEG2_VERSION (0, 5, 0) /* 0.5.0 */
+
+#define SEQ_FLAG_MPEG2 1
+#define SEQ_FLAG_CONSTRAINED_PARAMETERS 2
+#define SEQ_FLAG_PROGRESSIVE_SEQUENCE 4
+#define SEQ_FLAG_LOW_DELAY 8
+#define SEQ_FLAG_COLOUR_DESCRIPTION 16
+
+#define SEQ_MASK_VIDEO_FORMAT 0xe0
+#define SEQ_VIDEO_FORMAT_COMPONENT 0x00
+#define SEQ_VIDEO_FORMAT_PAL 0x20
+#define SEQ_VIDEO_FORMAT_NTSC 0x40
+#define SEQ_VIDEO_FORMAT_SECAM 0x60
+#define SEQ_VIDEO_FORMAT_MAC 0x80
+#define SEQ_VIDEO_FORMAT_UNSPECIFIED 0xa0
+
+typedef struct mpeg2_sequence_s
+{
+ unsigned int width, height;
+ unsigned int chroma_width, chroma_height;
+ unsigned int byte_rate;
+ unsigned int vbv_buffer_size;
+ uint32_t flags;
+
+ unsigned int picture_width, picture_height;
+ unsigned int display_width, display_height;
+ unsigned int pixel_width, pixel_height;
+ unsigned int frame_period;
+
+ uint8_t profile_level_id;
+ uint8_t colour_primaries;
+ uint8_t transfer_characteristics;
+ uint8_t matrix_coefficients;
+} mpeg2_sequence_t;
+
+#define GOP_FLAG_DROP_FRAME 1
+#define GOP_FLAG_BROKEN_LINK 2
+#define GOP_FLAG_CLOSED_GOP 4
+
+typedef struct mpeg2_gop_s
+{
+ uint8_t hours;
+ uint8_t minutes;
+ uint8_t seconds;
+ uint8_t pictures;
+ uint32_t flags;
+} mpeg2_gop_t;
+
+#define PIC_MASK_CODING_TYPE 7
+#define PIC_FLAG_CODING_TYPE_I 1
+#define PIC_FLAG_CODING_TYPE_P 2
+#define PIC_FLAG_CODING_TYPE_B 3
+#define PIC_FLAG_CODING_TYPE_D 4
+
+#define PIC_FLAG_TOP_FIELD_FIRST 8
+#define PIC_FLAG_PROGRESSIVE_FRAME 16
+#define PIC_FLAG_COMPOSITE_DISPLAY 32
+#define PIC_FLAG_SKIP 64
+#define PIC_FLAG_TAGS 128
+#define PIC_MASK_COMPOSITE_DISPLAY 0xfffff000
+
+typedef struct mpeg2_picture_s
+{
+ unsigned int temporal_reference;
+ unsigned int nb_fields;
+ uint32_t tag, tag2;
+ uint32_t flags;
+ struct
+ {
+ int x, y;
+ } display_offset[3];
+} mpeg2_picture_t;
+
+typedef struct mpeg2_fbuf_s
+{
+ uint8_t * buf[MPEG2_COMPONENTS];
+ void * id;
+} mpeg2_fbuf_t;
+
+typedef struct mpeg2_info_s
+{
+ const mpeg2_sequence_t * sequence;
+ const mpeg2_gop_t * gop;
+ const mpeg2_picture_t * current_picture;
+ const mpeg2_picture_t * current_picture_2nd;
+ const mpeg2_fbuf_t * current_fbuf;
+ const mpeg2_picture_t * display_picture;
+ const mpeg2_picture_t * display_picture_2nd;
+ const mpeg2_fbuf_t * display_fbuf;
+ const mpeg2_fbuf_t * discard_fbuf;
+ const uint8_t * user_data;
+ unsigned int user_data_len;
+} mpeg2_info_t;
+
+typedef struct mpeg2dec_s mpeg2dec_t;
+typedef struct mpeg2_decoder_s mpeg2_decoder_t;
+
+typedef enum
+{
+ STATE_INTERNAL_NORETURN = -1,
+ STATE_BUFFER = 0,
+ STATE_SEQUENCE = 1,
+ STATE_SEQUENCE_REPEATED = 2,
+ STATE_SEQUENCE_MODIFIED = 3,
+ STATE_GOP = 4,
+ STATE_PICTURE = 5,
+ STATE_SLICE_1ST = 6,
+ STATE_PICTURE_2ND = 7,
+ STATE_SLICE = 8,
+ STATE_END = 9,
+ STATE_INVALID = 10,
+ STATE_INVALID_END = 11,
+} mpeg2_state_t;
+
+typedef struct mpeg2_convert_init_s
+{
+ unsigned int id_size;
+ unsigned int buf_size[MPEG2_COMPONENTS];
+ void (* start)(void * id, const mpeg2_fbuf_t * fbuf,
+ const mpeg2_picture_t * picture, const mpeg2_gop_t * gop);
+ void (* copy)(void * id, uint8_t * const * src, unsigned int v_offset);
+} mpeg2_convert_init_t;
+
+typedef enum
+{
+ MPEG2_CONVERT_SET = 0,
+ MPEG2_CONVERT_STRIDE = 1,
+ MPEG2_CONVERT_START = 2
+} mpeg2_convert_stage_t;
+
+typedef int mpeg2_convert_t (int stage, void * id,
+ const mpeg2_sequence_t * sequence, int stride,
+ void * arg, mpeg2_convert_init_t * result);
+int mpeg2_convert (mpeg2dec_t * mpeg2dec, mpeg2_convert_t convert, void * arg);
+int mpeg2_stride (mpeg2dec_t * mpeg2dec, int stride);
+void mpeg2_set_buf (mpeg2dec_t * mpeg2dec, uint8_t * buf[MPEG2_COMPONENTS],
+ void * id);
+void mpeg2_custom_fbuf (mpeg2dec_t * mpeg2dec, int custom_fbuf);
+
+mpeg2dec_t * mpeg2_init (void);
+const mpeg2_info_t * mpeg2_info (mpeg2dec_t * mpeg2dec);
+void mpeg2_close (mpeg2dec_t * mpeg2dec);
+
+void mpeg2_buffer (mpeg2dec_t * mpeg2dec, uint8_t * start, uint8_t * end);
+int mpeg2_getpos (mpeg2dec_t * mpeg2dec);
+mpeg2_state_t mpeg2_parse (mpeg2dec_t * mpeg2dec);
+
+void mpeg2_reset (mpeg2dec_t * mpeg2dec, int full_reset);
+void mpeg2_skip (mpeg2dec_t * mpeg2dec, int skip);
+void mpeg2_slice_region (mpeg2dec_t * mpeg2dec, int start, int end);
+
+void mpeg2_tag_picture (mpeg2dec_t * mpeg2dec, uint32_t tag, uint32_t tag2);
+
+void mpeg2_init_fbuf (mpeg2_decoder_t * decoder,
+ uint8_t * current_fbuf[MPEG2_COMPONENTS],
+ uint8_t * forward_fbuf[MPEG2_COMPONENTS],
+ uint8_t * backward_fbuf[MPEG2_COMPONENTS]);
+void mpeg2_slice (mpeg2_decoder_t * decoder, int code, const uint8_t * buffer);
+
+int mpeg2_guess_aspect (const mpeg2_sequence_t * sequence,
+ unsigned int * pixel_width,
+ unsigned int * pixel_height);
+
+typedef enum
+{
+ MPEG2_ALLOC_MPEG2DEC = 0,
+ MPEG2_ALLOC_CHUNK = 1,
+ MPEG2_ALLOC_YUV = 2,
+ MPEG2_ALLOC_CONVERT_ID = 3,
+ MPEG2_ALLOC_CONVERTED = 4,
+ MPEG_ALLOC_CODEC_MALLOC,
+ MPEG_ALLOC_CODEC_CALLOC,
+ MPEG_ALLOC_MPEG2_BUFFER,
+ MPEG_ALLOC_AUDIOBUF,
+ MPEG_ALLOC_PCMOUT,
+ MPEG_ALLOC_DISKBUF,
+ __MPEG_ALLOC_FIRST = -256,
+} mpeg2_alloc_t;
+
+void * mpeg2_malloc (unsigned size, mpeg2_alloc_t reason);
+#if 0
+void mpeg2_free (void * buf);
+#endif
+/* allocates a dedicated buffer and locks all previous allocation in place */
+void * mpeg2_bufalloc(unsigned size, mpeg2_alloc_t reason);
+/* clears all non-dedicated buffer space */
+void mpeg2_mem_reset(void);
+void mpeg2_alloc_init(unsigned char* buf, int mallocsize);
+
+#endif /* MPEG2_H */
diff --git a/apps/plugins/mpegplayer/libmpeg2/mpeg2_internal.h b/apps/plugins/mpegplayer/libmpeg2/mpeg2_internal.h
new file mode 100644
index 0000000000..e04562e18d
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/mpeg2_internal.h
@@ -0,0 +1,274 @@
+/*
+ * mpeg2_internal.h
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.89
+ */
+#ifndef MPEG2_INTERNAL_H
+#define MPEG2_INTERNAL_H
+
+#include "config.h" /* for Rockbox CPU_ #defines */
+
+/* macroblock modes */
+#define MACROBLOCK_INTRA 1
+#define MACROBLOCK_PATTERN 2
+#define MACROBLOCK_MOTION_BACKWARD 4
+#define MACROBLOCK_MOTION_FORWARD 8
+#define MACROBLOCK_QUANT 16
+#define DCT_TYPE_INTERLACED 32
+/* motion_type */
+#define MOTION_TYPE_SHIFT 6
+#define MC_FIELD 1
+#define MC_FRAME 2
+#define MC_16X8 2
+#define MC_DMV 3
+
+/* picture structure */
+#define TOP_FIELD 1
+#define BOTTOM_FIELD 2
+#define FRAME_PICTURE 3
+
+/* picture coding type */
+#define I_TYPE 1
+#define P_TYPE 2
+#define B_TYPE 3
+#define D_TYPE 4
+
+typedef void mpeg2_mc_fct (uint8_t *, const uint8_t *, int, int);
+
+typedef struct
+{
+ uint8_t * ref[2][MPEG2_COMPONENTS];
+ uint8_t ** ref2[2];
+ int pmv[2][2];
+ int f_code[2];
+} motion_t;
+
+typedef void motion_parser_t(mpeg2_decoder_t * decoder,
+ motion_t * motion,
+ mpeg2_mc_fct * const * table);
+
+struct mpeg2_decoder_s
+{
+ /* first, state that carries information from one macroblock to the */
+ /* next inside a slice, and is never used outside of mpeg2_slice() */
+
+ /* bit parsing stuff */
+ uint32_t bitstream_buf; /* current 32 bit working set */
+ int bitstream_bits; /* used bits in working set */
+ const uint8_t * bitstream_ptr; /* buffer with stream data */
+
+ uint8_t * dest[MPEG2_COMPONENTS];
+
+ int offset;
+ int stride;
+ int uv_stride;
+ int slice_stride;
+ int slice_uv_stride;
+ int stride_frame;
+ unsigned int limit_x;
+ unsigned int limit_y_16;
+ unsigned int limit_y_8;
+ unsigned int limit_y;
+
+ /* Motion vectors */
+ /* The f_ and b_ correspond to the forward and backward motion */
+ /* predictors */
+ motion_t b_motion;
+ motion_t f_motion;
+ motion_parser_t * motion_parser[5];
+
+ /* predictor for DC coefficients in intra blocks */
+ int16_t dc_dct_pred[MPEG2_COMPONENTS];
+
+ /* DCT coefficients */
+ int16_t * DCTblock; /* put buffer separately to have it in IRAM */
+
+ uint8_t * picture_dest[MPEG2_COMPONENTS];
+ void (* convert) (void * convert_id, uint8_t * const * src,
+ unsigned int v_offset);
+ void * convert_id;
+
+ int dmv_offset;
+ unsigned int v_offset;
+
+ /* now non-slice-specific information */
+
+ /* sequence header stuff */
+ uint16_t * quantizer_matrix[4];
+ uint16_t (* chroma_quantizer[2])[64];
+ uint16_t quantizer_prescale[4][32][64];
+
+ /* The width and height of the picture snapped to macroblock units */
+ int width;
+ int height;
+ int vertical_position_extension;
+ int chroma_format;
+
+ /* picture header stuff */
+
+ /* what type of picture this is (I, P, B, D) */
+ int coding_type;
+
+ /* picture coding extension stuff */
+
+ /* quantization factor for intra dc coefficients */
+ int intra_dc_precision;
+ /* top/bottom/both fields */
+ int picture_structure;
+ /* bool to indicate all predictions are frame based */
+ int frame_pred_frame_dct;
+ /* bool to indicate whether intra blocks have motion vectors */
+ /* (for concealment) */
+ int concealment_motion_vectors;
+ /* bool to use different vlc tables */
+ int intra_vlc_format;
+ /* used for DMV MC */
+ int top_field_first;
+
+ /* stuff derived from bitstream */
+
+ /* pointer to the zigzag scan we're supposed to be using */
+ const uint8_t * scan;
+
+ int second_field;
+
+ int mpeg1;
+};
+
+typedef struct
+{
+ mpeg2_fbuf_t fbuf;
+} fbuf_alloc_t;
+
+struct mpeg2dec_s
+{
+ mpeg2_decoder_t decoder;
+
+ mpeg2_info_t info;
+
+ uint32_t shift;
+ int is_display_initialized;
+ mpeg2_state_t (* action) (struct mpeg2dec_s * mpeg2dec);
+ mpeg2_state_t state;
+ uint32_t ext_state;
+
+ /* allocated in init - gcc has problems allocating such big structures */
+ uint8_t * ATTR_ALIGN(4) chunk_buffer;
+ /* pointer to start of the current chunk */
+ uint8_t * chunk_start;
+ /* pointer to current position in chunk_buffer */
+ uint8_t * chunk_ptr;
+ /* last start code ? */
+ uint8_t code;
+
+ /* picture tags */
+ uint32_t tag_current, tag2_current, tag_previous, tag2_previous;
+ int num_tags;
+ int bytes_since_tag;
+
+ int first;
+ int alloc_index_user;
+ int alloc_index;
+ uint8_t first_decode_slice;
+ uint8_t nb_decode_slices;
+
+ unsigned int user_data_len;
+
+ mpeg2_sequence_t new_sequence;
+ mpeg2_sequence_t sequence;
+ mpeg2_gop_t new_gop;
+ mpeg2_gop_t gop;
+ mpeg2_picture_t new_picture;
+ mpeg2_picture_t pictures[4];
+ mpeg2_picture_t * picture;
+ /*const*/ mpeg2_fbuf_t * fbuf[3]; /* 0: current fbuf, 1-2: prediction fbufs */
+
+ fbuf_alloc_t fbuf_alloc[3];
+ int custom_fbuf;
+
+ uint8_t * yuv_buf[3][MPEG2_COMPONENTS];
+ int yuv_index;
+ mpeg2_convert_t * convert;
+ void * convert_arg;
+ unsigned int convert_id_size;
+ int convert_stride;
+ void (* convert_start) (void * id, const mpeg2_fbuf_t * fbuf,
+ const mpeg2_picture_t * picture,
+ const mpeg2_gop_t * gop);
+
+ uint8_t * buf_start;
+ uint8_t * buf_end;
+
+ int16_t display_offset_x, display_offset_y;
+
+ int copy_matrix;
+ int8_t q_scale_type, scaled[4];
+ uint8_t quantizer_matrix[4][64];
+ uint8_t new_quantizer_matrix[4][64];
+};
+
+/* decode.c */
+mpeg2_state_t mpeg2_seek_header (mpeg2dec_t * mpeg2dec);
+mpeg2_state_t mpeg2_parse_header (mpeg2dec_t * mpeg2dec);
+
+/* header.c */
+void mpeg2_header_state_init (mpeg2dec_t * mpeg2dec);
+void mpeg2_reset_info (mpeg2_info_t * info);
+int mpeg2_header_sequence (mpeg2dec_t * mpeg2dec);
+int mpeg2_header_gop (mpeg2dec_t * mpeg2dec);
+int mpeg2_header_picture (mpeg2dec_t * mpeg2dec);
+int mpeg2_header_extension (mpeg2dec_t * mpeg2dec);
+int mpeg2_header_user_data (mpeg2dec_t * mpeg2dec);
+void mpeg2_header_sequence_finalize (mpeg2dec_t * mpeg2dec);
+void mpeg2_header_gop_finalize (mpeg2dec_t * mpeg2dec);
+void mpeg2_header_picture_finalize (mpeg2dec_t * mpeg2dec);
+mpeg2_state_t mpeg2_header_slice_start (mpeg2dec_t * mpeg2dec);
+mpeg2_state_t mpeg2_header_end (mpeg2dec_t * mpeg2dec);
+void mpeg2_set_fbuf (mpeg2dec_t * mpeg2dec, int b_type);
+
+/* idct.c */
+void mpeg2_idct_init (void);
+void mpeg2_idct_copy(int16_t * block, uint8_t * dest,
+ const int stride);
+void mpeg2_idct_add(const int last, int16_t * block,
+ uint8_t * dest, const int stride);
+
+extern const uint8_t default_mpeg2_scan_norm[64];
+extern const uint8_t default_mpeg2_scan_alt[64];
+extern uint8_t mpeg2_scan_norm[64];
+extern uint8_t mpeg2_scan_alt[64];
+
+/* motion_comp.c */
+void mpeg2_mc_init (void);
+
+typedef struct
+{
+ mpeg2_mc_fct * put [8];
+ mpeg2_mc_fct * avg [8];
+} mpeg2_mc_t;
+
+extern const mpeg2_mc_t mpeg2_mc;
+
+#endif /* MPEG2_INTERNAL_H */
+
diff --git a/apps/plugins/mpegplayer/libmpeg2/mpeg2dec_config.h b/apps/plugins/mpegplayer/libmpeg2/mpeg2dec_config.h
new file mode 100644
index 0000000000..c34ee374df
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/mpeg2dec_config.h
@@ -0,0 +1,15 @@
+/* $Id$ */
+#ifndef MPEG2DEC_CONFIG_H
+#define MPEG2DEC_CONFIG_H
+
+#define ATTRIBUTE_ALIGNED_MAX 16
+
+#ifdef HAVE_LCD_COLOR
+#define MPEG2_COLOR 1
+#define MPEG2_COMPONENTS 3
+#else
+#define MPEG2_COLOR 0
+#define MPEG2_COMPONENTS 1
+#endif
+
+#endif /* MPEG2DEC_CONFIG_H */
diff --git a/apps/plugins/mpegplayer/libmpeg2/slice.c b/apps/plugins/mpegplayer/libmpeg2/slice.c
new file mode 100644
index 0000000000..926333d5d0
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/slice.c
@@ -0,0 +1,2898 @@
+/*
+ * slice.c
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 2003 Peter Gubanov <peter@elecard.net.ru>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.55
+ */
+
+#include "plugin.h"
+
+#include "mpeg2dec_config.h"
+
+#include "mpeg2.h"
+#include "attributes.h"
+#include "mpeg2_internal.h"
+
+#include "vlc.h"
+
+static inline int get_macroblock_modes (mpeg2_decoder_t * const decoder)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ int macroblock_modes;
+ const MBtab * tab;
+
+ switch (decoder->coding_type)
+ {
+ case I_TYPE:
+ tab = MB_I + UBITS (bit_buf, 1);
+ DUMPBITS (bit_buf, bits, tab->len);
+ macroblock_modes = tab->modes;
+
+ if (!(decoder->frame_pred_frame_dct) &&
+ decoder->picture_structure == FRAME_PICTURE)
+ {
+ macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED;
+ DUMPBITS (bit_buf, bits, 1);
+ }
+
+ return macroblock_modes;
+
+ case P_TYPE:
+ tab = MB_P + UBITS (bit_buf, 5);
+ DUMPBITS (bit_buf, bits, tab->len);
+ macroblock_modes = tab->modes;
+
+ if (decoder->picture_structure != FRAME_PICTURE)
+ {
+ if (macroblock_modes & MACROBLOCK_MOTION_FORWARD)
+ {
+ macroblock_modes |= UBITS (bit_buf, 2) << MOTION_TYPE_SHIFT;
+ DUMPBITS (bit_buf, bits, 2);
+ }
+
+ return macroblock_modes | MACROBLOCK_MOTION_FORWARD;
+ }
+ else if (decoder->frame_pred_frame_dct)
+ {
+ if (macroblock_modes & MACROBLOCK_MOTION_FORWARD)
+ macroblock_modes |= MC_FRAME << MOTION_TYPE_SHIFT;
+
+ return macroblock_modes | MACROBLOCK_MOTION_FORWARD;
+ }
+ else
+ {
+ if (macroblock_modes & MACROBLOCK_MOTION_FORWARD)
+ {
+ macroblock_modes |= UBITS (bit_buf, 2) << MOTION_TYPE_SHIFT;
+ DUMPBITS (bit_buf, bits, 2);
+ }
+
+ if (macroblock_modes & (MACROBLOCK_INTRA | MACROBLOCK_PATTERN))
+ {
+ macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED;
+ DUMPBITS (bit_buf, bits, 1);
+ }
+
+ return macroblock_modes | MACROBLOCK_MOTION_FORWARD;
+ }
+
+ case B_TYPE:
+ tab = MB_B + UBITS (bit_buf, 6);
+ DUMPBITS (bit_buf, bits, tab->len);
+ macroblock_modes = tab->modes;
+
+ if (decoder->picture_structure != FRAME_PICTURE)
+ {
+ if (! (macroblock_modes & MACROBLOCK_INTRA))
+ {
+ macroblock_modes |= UBITS (bit_buf, 2) << MOTION_TYPE_SHIFT;
+ DUMPBITS (bit_buf, bits, 2);
+ }
+
+ return macroblock_modes;
+ }
+ else if (decoder->frame_pred_frame_dct)
+ {
+ /* if (! (macroblock_modes & MACROBLOCK_INTRA)) */
+ macroblock_modes |= MC_FRAME << MOTION_TYPE_SHIFT;
+ return macroblock_modes;
+ }
+ else
+ {
+ if (macroblock_modes & MACROBLOCK_INTRA)
+ goto intra;
+
+ macroblock_modes |= UBITS (bit_buf, 2) << MOTION_TYPE_SHIFT;
+ DUMPBITS (bit_buf, bits, 2);
+
+ if (macroblock_modes & (MACROBLOCK_INTRA | MACROBLOCK_PATTERN))
+ {
+ intra:
+ macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED;
+ DUMPBITS (bit_buf, bits, 1);
+ }
+ return macroblock_modes;
+ }
+
+ case D_TYPE:
+ DUMPBITS (bit_buf, bits, 1);
+ return MACROBLOCK_INTRA;
+
+ default:
+ return 0;
+ }
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+static inline void get_quantizer_scale (mpeg2_decoder_t * const decoder)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ int quantizer_scale_code;
+
+ quantizer_scale_code = UBITS (bit_buf, 5);
+ DUMPBITS (bit_buf, bits, 5);
+
+ decoder->quantizer_matrix[0] =
+ decoder->quantizer_prescale[0][quantizer_scale_code];
+
+ decoder->quantizer_matrix[1] =
+ decoder->quantizer_prescale[1][quantizer_scale_code];
+
+ decoder->quantizer_matrix[2] =
+ decoder->chroma_quantizer[0][quantizer_scale_code];
+
+ decoder->quantizer_matrix[3] =
+ decoder->chroma_quantizer[1][quantizer_scale_code];
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+static inline int get_motion_delta (mpeg2_decoder_t * const decoder,
+ const int f_code)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ int delta;
+ int sign;
+ const MVtab * tab;
+
+ if (bit_buf & 0x80000000)
+ {
+ DUMPBITS (bit_buf, bits, 1);
+ return 0;
+ }
+ else if (bit_buf >= 0x0c000000)
+ {
+ tab = MV_4 + UBITS (bit_buf, 4);
+ delta = (tab->delta << f_code) + 1;
+ bits += tab->len + f_code + 1;
+ bit_buf <<= tab->len;
+
+ sign = SBITS (bit_buf, 1);
+ bit_buf <<= 1;
+
+ if (f_code)
+ delta += UBITS (bit_buf, f_code);
+ bit_buf <<= f_code;
+
+ return (delta ^ sign) - sign;
+ }
+ else
+ {
+ tab = MV_10 + UBITS (bit_buf, 10);
+ delta = (tab->delta << f_code) + 1;
+ bits += tab->len + 1;
+ bit_buf <<= tab->len;
+
+ sign = SBITS (bit_buf, 1);
+ bit_buf <<= 1;
+
+ if (f_code)
+ {
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ delta += UBITS (bit_buf, f_code);
+ DUMPBITS (bit_buf, bits, f_code);
+ }
+
+ return (delta ^ sign) - sign;
+
+ }
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+static inline int bound_motion_vector (const int vector, const int f_code)
+{
+ return ((int32_t)vector << (27 - f_code)) >> (27 - f_code);
+}
+
+static inline int get_dmv (mpeg2_decoder_t * const decoder)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ const DMVtab * tab;
+
+ tab = DMV_2 + UBITS (bit_buf, 2);
+ DUMPBITS (bit_buf, bits, tab->len);
+ return tab->dmv;
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+static inline int get_coded_block_pattern (mpeg2_decoder_t * const decoder)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ const CBPtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ if (bit_buf >= 0x20000000)
+ {
+ tab = CBP_7 + (UBITS (bit_buf, 7) - 16);
+ DUMPBITS (bit_buf, bits, tab->len);
+ return tab->cbp;
+ }
+ else
+ {
+ tab = CBP_9 + UBITS (bit_buf, 9);
+ DUMPBITS (bit_buf, bits, tab->len);
+ return tab->cbp;
+ }
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+static inline int get_luma_dc_dct_diff (mpeg2_decoder_t * const decoder)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ const DCtab * tab;
+ int size;
+ int dc_diff;
+
+ if (bit_buf < 0xf8000000)
+ {
+ tab = DC_lum_5 + UBITS (bit_buf, 5);
+ size = tab->size;
+
+ if (size)
+ {
+ bits += tab->len + size;
+ bit_buf <<= tab->len;
+ dc_diff =
+ UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size);
+ bit_buf <<= size;
+ return dc_diff << decoder->intra_dc_precision;
+ }
+ else
+ {
+ DUMPBITS (bit_buf, bits, 3);
+ return 0;
+ }
+ }
+ else
+ {
+ tab = DC_long + (UBITS (bit_buf, 9) - 0x1e0);
+ size = tab->size;
+ DUMPBITS (bit_buf, bits, tab->len);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ dc_diff = UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size);
+ DUMPBITS (bit_buf, bits, size);
+ return dc_diff << decoder->intra_dc_precision;
+ }
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+#if MPEG2_COLOR
+static inline int get_chroma_dc_dct_diff (mpeg2_decoder_t * const decoder)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ const DCtab * tab;
+ int size;
+ int dc_diff;
+
+ if (bit_buf < 0xf8000000)
+ {
+ tab = DC_chrom_5 + UBITS (bit_buf, 5);
+ size = tab->size;
+
+ if (size)
+ {
+ bits += tab->len + size;
+ bit_buf <<= tab->len;
+ dc_diff =
+ UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size);
+ bit_buf <<= size;
+ return dc_diff << decoder->intra_dc_precision;
+ }
+ else
+ {
+ DUMPBITS (bit_buf, bits, 2);
+ return 0;
+ }
+ }
+ else
+ {
+ tab = DC_long + (UBITS (bit_buf, 10) - 0x3e0);
+ size = tab->size;
+ DUMPBITS (bit_buf, bits, tab->len + 1);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ dc_diff = UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size);
+ DUMPBITS (bit_buf, bits, size);
+ return dc_diff << decoder->intra_dc_precision;
+ }
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+#endif /* MPEG2_COLOR */
+
+#define SATURATE(val) \
+ do { \
+ val <<= 4; \
+ if (unlikely (val != (int16_t) val)) \
+ val = (SBITS (val, 1) ^ 2047) << 4; \
+ } while (0)
+
+static void get_intra_block_B14 (mpeg2_decoder_t * const decoder,
+ const uint16_t * const quant_matrix)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ const uint8_t * const scan = decoder->scan;
+ int16_t * const dest = decoder->DCTblock;
+ int mismatch = ~dest[0];
+ int i = 0;
+ int j;
+ int val;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ while (1)
+ {
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
+
+ i += tab->run;
+ if (i >= 64)
+ break; /* end of block */
+
+ normal_code:
+ j = scan[i];
+ bit_buf <<= tab->len;
+ bits += tab->len + 1;
+ val = (tab->level * quant_matrix[j]) >> 4;
+
+ /* if (bitstream_get (1)) val = -val; */
+ val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
+
+ SATURATE (val);
+ dest[j] = val;
+ mismatch ^= val;
+
+ bit_buf <<= 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+ else if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check needed to avoid buffer overflow */
+
+ j = scan[i];
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ val = (SBITS (bit_buf, 12) * quant_matrix[j]) / 16;
+
+ SATURATE (val);
+ dest[j] = val;
+ mismatch ^= val;
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ dest[63] ^= mismatch & 16;
+ DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+}
+
+static void get_intra_block_B15 (mpeg2_decoder_t * const decoder,
+ const uint16_t * const quant_matrix)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ const uint8_t * const scan = decoder->scan;
+ int16_t * const dest = decoder->DCTblock;
+ int mismatch = ~dest[0];
+ int i = 0;
+ int j;
+ int val;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ while (1)
+ {
+ if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B15_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+
+ if (i < 64)
+ {
+ normal_code:
+ j = scan[i];
+ bit_buf <<= tab->len;
+ bits += tab->len + 1;
+ val = (tab->level * quant_matrix[j]) >> 4;
+
+ /* if (bitstream_get (1)) val = -val; */
+ val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
+
+ SATURATE (val);
+ dest[j] = val;
+ mismatch ^= val;
+
+ bit_buf <<= 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+ else
+ {
+ /* end of block. I commented out this code because if we */
+ /* dont exit here we will still exit at the later test :) */
+
+ /* if (i >= 128) break; */ /* end of block */
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check against buffer overflow */
+
+ j = scan[i];
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ val = (SBITS (bit_buf, 12) * quant_matrix[j]) / 16;
+
+ SATURATE (val);
+ dest[j] = val;
+ mismatch ^= val;
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B15_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ dest[63] ^= mismatch & 16;
+ DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+}
+
+static int get_non_intra_block (mpeg2_decoder_t * const decoder,
+ const uint16_t * const quant_matrix)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ const uint8_t * const scan = decoder->scan;
+ int16_t * const dest = decoder->DCTblock;
+ int mismatch = -1;
+ int i = -1;
+ int j;
+ int val;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14DC_5 + (UBITS (bit_buf, 5) - 5);
+ goto entry_1;
+ }
+ else
+ {
+ goto entry_2;
+ }
+
+ while (1)
+ {
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
+
+ entry_1:
+ i += tab->run;
+ if (i >= 64)
+ break; /* end of block */
+
+ normal_code:
+ j = scan[i];
+ bit_buf <<= tab->len;
+ bits += tab->len + 1;
+ val = ((2 * tab->level + 1) * quant_matrix[j]) >> 5;
+
+ /* if (bitstream_get (1)) val = -val; */
+ val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
+
+ SATURATE (val);
+ dest[j] = val;
+ mismatch ^= val;
+
+ bit_buf <<= 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+
+ entry_2:
+ if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check needed to avoid buffer overflow */
+
+ j = scan[i];
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ val = 2 * (SBITS (bit_buf, 12) + SBITS (bit_buf, 1)) + 1;
+ val = (val * quant_matrix[j]) / 32;
+
+ SATURATE (val);
+ dest[j] = val;
+ mismatch ^= val;
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ dest[63] ^= mismatch & 16;
+ DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+ return i;
+}
+
+static void get_mpeg1_intra_block (mpeg2_decoder_t * const decoder)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ const uint8_t * const scan = decoder->scan;
+ const uint16_t * const quant_matrix = decoder->quantizer_matrix[0];
+ int16_t * const dest = decoder->DCTblock;
+ int i = 0;
+ int j;
+ int val;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ while (1)
+ {
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
+
+ i += tab->run;
+ if (i >= 64)
+ break; /* end of block */
+
+ normal_code:
+ j = scan[i];
+ bit_buf <<= tab->len;
+ bits += tab->len + 1;
+ val = (tab->level * quant_matrix[j]) >> 4;
+
+ /* oddification */
+ val = (val - 1) | 1;
+
+ /* if (bitstream_get (1)) val = -val; */
+ val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
+
+ SATURATE (val);
+ dest[j] = val;
+
+ bit_buf <<= 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+ else if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check needed to avoid buffer overflow */
+
+ j = scan[i];
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ val = SBITS (bit_buf, 8);
+
+ if (! (val & 0x7f))
+ {
+ DUMPBITS (bit_buf, bits, 8);
+ val = UBITS (bit_buf, 8) + 2 * val;
+ }
+
+ val = (val * quant_matrix[j]) / 16;
+
+ /* oddification */
+ val = (val + ~SBITS (val, 1)) | 1;
+
+ SATURATE (val);
+ dest[j] = val;
+
+ DUMPBITS (bit_buf, bits, 8);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+}
+
+static int get_mpeg1_non_intra_block (mpeg2_decoder_t * const decoder)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ const uint8_t * const scan = decoder->scan;
+ const uint16_t * const quant_matrix = decoder->quantizer_matrix[1];
+ int16_t * const dest = decoder->DCTblock;
+ int i = -1;
+ int j;
+ int val;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14DC_5 + (UBITS (bit_buf, 5) - 5);
+ goto entry_1;
+ }
+ else
+ {
+ goto entry_2;
+ }
+
+ while (1)
+ {
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
+
+ entry_1:
+ i += tab->run;
+ if (i >= 64)
+ break; /* end of block */
+
+ normal_code:
+ j = scan[i];
+ bit_buf <<= tab->len;
+ bits += tab->len + 1;
+ val = ((2 * tab->level + 1) * quant_matrix[j]) >> 5;
+
+ /* oddification */
+ val = (val - 1) | 1;
+
+ /* if (bitstream_get (1)) val = -val; */
+ val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
+
+ SATURATE (val);
+ dest[j] = val;
+
+ bit_buf <<= 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+
+ entry_2:
+ if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check needed to avoid buffer overflow */
+
+ j = scan[i];
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ val = SBITS (bit_buf, 8);
+
+ if (! (val & 0x7f))
+ {
+ DUMPBITS (bit_buf, bits, 8);
+ val = UBITS (bit_buf, 8) + 2 * val;
+ }
+
+ val = 2 * (val + SBITS (val, 1)) + 1;
+ val = (val * quant_matrix[j]) / 32;
+
+ /* oddification */
+ val = (val + ~SBITS (val, 1)) | 1;
+
+ SATURATE (val);
+ dest[j] = val;
+
+ DUMPBITS (bit_buf, bits, 8);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+ return i;
+}
+
+static inline void slice_intra_DCT (mpeg2_decoder_t * const decoder,
+ const int cc,
+ uint8_t * const dest, const int stride)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ /* Get the intra DC coefficient and inverse quantize it */
+ if (cc == 0)
+ {
+ decoder->dc_dct_pred[0] += get_luma_dc_dct_diff (decoder);
+ decoder->DCTblock[0] = decoder->dc_dct_pred[0];
+
+ }
+#if MPEG2_COLOR
+ else
+ {
+ decoder->dc_dct_pred[cc] += get_chroma_dc_dct_diff (decoder);
+ decoder->DCTblock[0] = decoder->dc_dct_pred[cc];
+ }
+#endif
+
+ if (decoder->mpeg1)
+ {
+ if (decoder->coding_type != D_TYPE)
+ get_mpeg1_intra_block (decoder);
+ }
+ else if (decoder->intra_vlc_format)
+ {
+ get_intra_block_B15 (decoder, decoder->quantizer_matrix[cc ? 2 : 0]);
+ }
+ else
+ {
+ get_intra_block_B14 (decoder, decoder->quantizer_matrix[cc ? 2 : 0]);
+ }
+
+ mpeg2_idct_copy (decoder->DCTblock, dest, stride);
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+static inline void slice_non_intra_DCT (mpeg2_decoder_t * const decoder,
+ const int cc,
+ uint8_t * const dest, const int stride)
+{
+ int last;
+
+ if (decoder->mpeg1)
+ {
+ last = get_mpeg1_non_intra_block (decoder);
+ }
+ else
+ {
+ last = get_non_intra_block (decoder,
+ decoder->quantizer_matrix[cc ? 3 : 1]);
+ }
+
+ mpeg2_idct_add (last, decoder->DCTblock, dest, stride);
+}
+
+#if !MPEG2_COLOR
+static void skip_mpeg1_intra_block (mpeg2_decoder_t * const decoder)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ int i = 0;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ while (1)
+ {
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
+
+ i += tab->run;
+ if (i >= 64)
+ break; /* end of block */
+
+ normal_code:
+ bit_buf <<= tab->len + 1;
+ bits += tab->len + 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ }
+ else if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check needed to avoid buffer overflow */
+
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ if (!(SBITS (bit_buf, 8) & 0x7f))
+ DUMPBITS (bit_buf, bits, 8);
+
+ DUMPBITS (bit_buf, bits, 8);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ DUMPBITS (bit_buf, bits, 2); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+}
+
+static void skip_intra_block_B14 (mpeg2_decoder_t * const decoder)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ int i = 0;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ while (1)
+ {
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
+
+ i += tab->run;
+ if (i >= 64)
+ break; /* end of block */
+
+ normal_code:
+ bit_buf <<= tab->len + 1;
+ bits += tab->len + 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ }
+ else if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check needed to avoid buffer overflow */
+
+ DUMPBITS (bit_buf, bits, 12); /* Can't dump more than 16 atm */
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ DUMPBITS (bit_buf, bits, 2); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+}
+
+static void skip_intra_block_B15 (mpeg2_decoder_t * const decoder)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ int i = 0;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ while (1)
+ {
+ if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B15_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+
+ if (i < 64)
+ {
+ normal_code:
+ bit_buf <<= tab->len + 1;
+ bits += tab->len + 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ }
+ else
+ {
+ /* end of block. I commented out this code because if we */
+ /* dont exit here we will still exit at the later test :) */
+
+ /* if (i >= 128) break; */ /* end of block */
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check against buffer overflow */
+
+ DUMPBITS (bit_buf, bits, 12); /* Can't dump more than 16 atm */
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ }
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B15_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ DUMPBITS (bit_buf, bits, 4); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+}
+
+static void skip_non_intra_block (mpeg2_decoder_t * const decoder)
+{
+ uint32_t bit_buf = decoder->bitstream_buf;
+ int bits = decoder->bitstream_bits;
+ const uint8_t * bit_ptr = decoder->bitstream_ptr;
+ int i = -1;
+ const DCTtab * tab;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14DC_5 + (UBITS (bit_buf, 5) - 5);
+ goto entry_1;
+ }
+ else
+ {
+ goto entry_2;
+ }
+
+ while (1)
+ {
+ if (bit_buf >= 0x28000000)
+ {
+ tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
+
+ entry_1:
+ i += tab->run;
+ if (i >= 64)
+ break; /* end of block */
+
+ normal_code:
+ bit_buf <<= tab->len + 1;
+ bits += tab->len + 1;
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ continue;
+ }
+
+ entry_2:
+ if (bit_buf >= 0x04000000)
+ {
+ tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
+
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+
+ /* escape code */
+
+ i += UBITS (bit_buf << 6, 6) - 64;
+ if (i >= 64)
+ break; /* illegal, check needed to avoid buffer overflow */
+
+ if (decoder->mpeg1)
+ {
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ if (!(SBITS (bit_buf, 8) & 0x7f))
+ DUMPBITS (bit_buf, bits, 8);
+
+ DUMPBITS (bit_buf, bits, 8);
+ }
+ else
+ {
+ DUMPBITS (bit_buf, bits, 12);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ DUMPBITS (bit_buf, bits, 12);
+ }
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ }
+ else if (bit_buf >= 0x02000000)
+ {
+ tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00800000)
+ {
+ tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else if (bit_buf >= 0x00200000)
+ {
+ tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ else
+ {
+ tab = DCT_16 + UBITS (bit_buf, 16);
+ bit_buf <<= 16;
+ GETWORD (bit_buf, bits + 16, bit_ptr);
+ i += tab->run;
+ if (i < 64)
+ goto normal_code;
+ }
+ break; /* illegal, check needed to avoid buffer overflow */
+ }
+
+ DUMPBITS (bit_buf, bits, 2); /* dump end of block code */
+ decoder->bitstream_buf = bit_buf;
+ decoder->bitstream_bits = bits;
+ decoder->bitstream_ptr = bit_ptr;
+}
+
+static void skip_chroma_dc_dct_diff (mpeg2_decoder_t * const decoder)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ const DCtab * tab;
+ int size;
+
+ if (bit_buf < 0xf8000000)
+ {
+ tab = DC_chrom_5 + UBITS (bit_buf, 5);
+ size = tab->size;
+
+ if (size)
+ {
+ bits += tab->len + size;
+ bit_buf <<= tab->len;
+ bit_buf <<= size;
+ }
+ else
+ {
+ DUMPBITS (bit_buf, bits, 2);
+ }
+ }
+ else
+ {
+ tab = DC_long + (UBITS (bit_buf, 10) - 0x3e0);
+ size = tab->size;
+ DUMPBITS (bit_buf, bits, tab->len + 1);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ DUMPBITS (bit_buf, bits, size);
+ }
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+static void skip_chroma_non_intra (mpeg2_decoder_t * const decoder,
+ uint32_t coded_block_pattern)
+{
+ static const uint32_t cbp_mask[3] =
+ {
+ 0x00000030,
+ 0xc0000030,
+ 0xfc000030,
+ };
+
+ uint32_t cbp = coded_block_pattern &
+ cbp_mask[MIN((unsigned)decoder->chroma_format, 2u)];
+
+ while (cbp)
+ {
+ skip_non_intra_block (decoder);
+ cbp &= (cbp - 1);
+ }
+}
+
+static void skip_chroma_intra (mpeg2_decoder_t * const decoder)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+ int i = 2 << decoder->chroma_format;
+
+ if ((unsigned)i > 8)
+ i = 8;
+
+ while (i-- > 0)
+ {
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ skip_chroma_dc_dct_diff (decoder);
+
+ if (decoder->mpeg1)
+ {
+ if (decoder->coding_type != D_TYPE)
+ skip_mpeg1_intra_block (decoder);
+ }
+ else if (decoder->intra_vlc_format)
+ {
+ skip_intra_block_B15 (decoder);
+ }
+ else
+ {
+ skip_intra_block_B14 (decoder);
+ }
+ }
+
+ if (decoder->chroma_format == 0 && decoder->coding_type == D_TYPE)
+ {
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ DUMPBITS (bit_buf, bits, 1);
+ }
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+#endif /* !MPEG2_COLOR */
+
+#define MOTION_420(table, ref, motion_x, motion_y, size, y) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = 2 * decoder->v_offset + motion_y + 2 * y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y_ ## size)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y_ ## size; \
+ motion_y = pos_y - 2 * decoder->v_offset - 2 * y; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ table[xy_half] (decoder->dest[0] + y * decoder->stride + decoder->offset, \
+ ref[0] + (pos_x >> 1) + (pos_y >> 1) * decoder->stride, \
+ decoder->stride, size); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ motion_x /= 2; \
+ motion_y /= 2; \
+ xy_half = ((motion_y & 1) << 1) | (motion_x & 1); \
+ offset = ((decoder->offset + motion_x) >> 1) + \
+ ((((decoder->v_offset + motion_y) >> 1) + y/2) * \
+ decoder->uv_stride); \
+ \
+ table[4+xy_half] (decoder->dest[1] + y/2 * decoder->uv_stride + \
+ (decoder->offset >> 1), ref[1] + offset, \
+ decoder->uv_stride, size/2); \
+ table[4+xy_half] (decoder->dest[2] + y/2 * decoder->uv_stride + \
+ (decoder->offset >> 1), ref[2] + offset, \
+ decoder->uv_stride, size/2); \
+ }
+
+#define MOTION_FIELD_420(table, ref, motion_x, motion_y, \
+ dest_field, op, src_field) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = decoder->v_offset + motion_y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
+ motion_y = pos_y - decoder->v_offset; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ table[xy_half] (decoder->dest[0] + dest_field * decoder->stride + \
+ decoder->offset, \
+ (ref[0] + (pos_x >> 1) + \
+ ((pos_y op) + src_field) * decoder->stride), \
+ 2 * decoder->stride, 8); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ motion_x /= 2; \
+ motion_y /= 2; \
+ xy_half = ((motion_y & 1) << 1) | (motion_x & 1); \
+ offset = ((decoder->offset + motion_x) >> 1) + \
+ (((decoder->v_offset >> 1) + (motion_y op) + src_field) * \
+ decoder->uv_stride); \
+ \
+ table[4+xy_half] (decoder->dest[1] + dest_field * decoder->uv_stride + \
+ (decoder->offset >> 1), ref[1] + offset, \
+ 2 * decoder->uv_stride, 4); \
+ table[4+xy_half] (decoder->dest[2] + dest_field * decoder->uv_stride + \
+ (decoder->offset >> 1), ref[2] + offset, \
+ 2 * decoder->uv_stride, 4); \
+ }
+
+#define MOTION_DMV_420(table, ref, motion_x, motion_y) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = decoder->v_offset + motion_y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
+ motion_y = pos_y - decoder->v_offset; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ offset = (pos_x >> 1) + (pos_y & ~1) * decoder->stride; \
+ table[xy_half] (decoder->dest[0] + decoder->offset, \
+ ref[0] + offset, 2 * decoder->stride, 8); \
+ table[xy_half] (decoder->dest[0] + decoder->stride + decoder->offset, \
+ ref[0] + decoder->stride + offset, \
+ 2 * decoder->stride, 8); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ motion_x /= 2; \
+ motion_y /= 2; \
+ xy_half = ((motion_y & 1) << 1) | (motion_x & 1); \
+ offset = ((decoder->offset + motion_x) >> 1) + \
+ (((decoder->v_offset >> 1) + (motion_y & ~1)) * \
+ decoder->uv_stride); \
+ \
+ table[4+xy_half] (decoder->dest[1] + (decoder->offset >> 1), \
+ ref[1] + offset, 2 * decoder->uv_stride, 4); \
+ table[4+xy_half] (decoder->dest[1] + decoder->uv_stride + \
+ (decoder->offset >> 1), \
+ ref[1] + decoder->uv_stride + offset, \
+ 2 * decoder->uv_stride, 4); \
+ table[4+xy_half] (decoder->dest[2] + (decoder->offset >> 1), \
+ ref[2] + offset, 2 * decoder->uv_stride, 4); \
+ table[4+xy_half] (decoder->dest[2] + decoder->uv_stride + \
+ (decoder->offset >> 1), \
+ ref[2] + decoder->uv_stride + offset, \
+ 2 * decoder->uv_stride, 4); \
+ }
+
+#define MOTION_ZERO_420(table, ref) \
+ table[0] (decoder->dest[0] + decoder->offset, \
+ (ref[0] + decoder->offset + \
+ decoder->v_offset * decoder->stride), decoder->stride, 16); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ offset = ((decoder->offset >> 1) + \
+ (decoder->v_offset >> 1) * decoder->uv_stride); \
+ \
+ table[4] (decoder->dest[1] + (decoder->offset >> 1), \
+ ref[1] + offset, decoder->uv_stride, 8); \
+ table[4] (decoder->dest[2] + (decoder->offset >> 1), \
+ ref[2] + offset, decoder->uv_stride, 8); \
+ }
+
+#define MOTION_422(table, ref, motion_x, motion_y, size, y) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = 2 * decoder->v_offset + motion_y + 2 * y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y_ ## size)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y_ ## size; \
+ motion_y = pos_y - 2 * decoder->v_offset - 2 * y; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ offset = (pos_x >> 1) + (pos_y >> 1) * decoder->stride; \
+ \
+ table[xy_half] (decoder->dest[0] + y * decoder->stride + decoder->offset, \
+ ref[0] + offset, decoder->stride, size); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ offset = (offset + (motion_x & (motion_x < 0))) >> 1; \
+ motion_x /= 2; \
+ xy_half = ((pos_y & 1) << 1) | (motion_x & 1); \
+ \
+ table[4+xy_half] (decoder->dest[1] + y * decoder->uv_stride + \
+ (decoder->offset >> 1), ref[1] + offset, \
+ decoder->uv_stride, size); \
+ table[4+xy_half] (decoder->dest[2] + y * decoder->uv_stride + \
+ (decoder->offset >> 1), ref[2] + offset, \
+ decoder->uv_stride, size); \
+ }
+
+#define MOTION_FIELD_422(table, ref, motion_x, motion_y, \
+ dest_field, op, src_field) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = decoder->v_offset + motion_y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
+ motion_y = pos_y - decoder->v_offset; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ offset = (pos_x >> 1) + ((pos_y op) + src_field) * decoder->stride; \
+ \
+ table[xy_half] (decoder->dest[0] + dest_field * decoder->stride + \
+ decoder->offset, ref[0] + offset, \
+ 2 * decoder->stride, 8); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ offset = (offset + (motion_x & (motion_x < 0))) >> 1; \
+ motion_x /= 2; \
+ xy_half = ((pos_y & 1) << 1) | (motion_x & 1); \
+ \
+ table[4+xy_half] (decoder->dest[1] + dest_field * decoder->uv_stride + \
+ (decoder->offset >> 1), ref[1] + offset, \
+ 2 * decoder->uv_stride, 8); \
+ table[4+xy_half] (decoder->dest[2] + dest_field * decoder->uv_stride + \
+ (decoder->offset >> 1), ref[2] + offset, \
+ 2 * decoder->uv_stride, 8); \
+ }
+
+#define MOTION_DMV_422(table, ref, motion_x, motion_y) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = decoder->v_offset + motion_y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
+ motion_y = pos_y - decoder->v_offset; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ offset = (pos_x >> 1) + (pos_y & ~1) * decoder->stride; \
+ \
+ table[xy_half] (decoder->dest[0] + decoder->offset, \
+ ref[0] + offset, 2 * decoder->stride, 8); \
+ table[xy_half] (decoder->dest[0] + decoder->stride + decoder->offset, \
+ ref[0] + decoder->stride + offset, \
+ 2 * decoder->stride, 8); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ offset = (offset + (motion_x & (motion_x < 0))) >> 1; \
+ motion_x /= 2; \
+ xy_half = ((pos_y & 1) << 1) | (motion_x & 1); \
+ \
+ table[4+xy_half] (decoder->dest[1] + (decoder->offset >> 1), \
+ ref[1] + offset, 2 * decoder->uv_stride, 8); \
+ table[4+xy_half] (decoder->dest[1] + decoder->uv_stride + \
+ (decoder->offset >> 1), \
+ ref[1] + decoder->uv_stride + offset, \
+ 2 * decoder->uv_stride, 8); \
+ table[4+xy_half] (decoder->dest[2] + (decoder->offset >> 1), \
+ ref[2] + offset, 2 * decoder->uv_stride, 8); \
+ table[4+xy_half] (decoder->dest[2] + decoder->uv_stride + \
+ (decoder->offset >> 1), \
+ ref[2] + decoder->uv_stride + offset, \
+ 2 * decoder->uv_stride, 8); \
+ }
+
+#define MOTION_ZERO_422(table, ref) \
+ offset = decoder->offset + decoder->v_offset * decoder->stride; \
+ table[0] (decoder->dest[0] + decoder->offset, \
+ ref[0] + offset, decoder->stride, 16); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ offset >>= 1; \
+ table[4] (decoder->dest[1] + (decoder->offset >> 1), \
+ ref[1] + offset, decoder->uv_stride, 16); \
+ table[4] (decoder->dest[2] + (decoder->offset >> 1), \
+ ref[2] + offset, decoder->uv_stride, 16); \
+ }
+
+#define MOTION_444(table, ref, motion_x, motion_y, size, y) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = 2 * decoder->v_offset + motion_y + 2 * y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y_ ## size)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y_ ## size; \
+ motion_y = pos_y - 2 * decoder->v_offset - 2 * y; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ offset = (pos_x >> 1) + (pos_y >> 1) * decoder->stride; \
+ \
+ table[xy_half] (decoder->dest[0] + y * decoder->stride + decoder->offset, \
+ ref[0] + offset, decoder->stride, size); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ table[xy_half] (decoder->dest[1] + y * decoder->stride + decoder->offset, \
+ ref[1] + offset, decoder->stride, size); \
+ table[xy_half] (decoder->dest[2] + y * decoder->stride + decoder->offset, \
+ ref[2] + offset, decoder->stride, size); \
+ }
+
+#define MOTION_FIELD_444(table, ref, motion_x, motion_y, \
+ dest_field, op, src_field) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = decoder->v_offset + motion_y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
+ motion_y = pos_y - decoder->v_offset; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ offset = (pos_x >> 1) + ((pos_y op) + src_field) * decoder->stride; \
+ \
+ table[xy_half] (decoder->dest[0] + dest_field * decoder->stride + \
+ decoder->offset, ref[0] + offset, \
+ 2 * decoder->stride, 8); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ table[xy_half] (decoder->dest[1] + dest_field * decoder->stride + \
+ decoder->offset, ref[1] + offset, \
+ 2 * decoder->stride, 8); \
+ table[xy_half] (decoder->dest[2] + dest_field * decoder->stride + \
+ decoder->offset, ref[2] + offset, \
+ 2 * decoder->stride, 8); \
+ }
+
+#define MOTION_DMV_444(table, ref, motion_x, motion_y) \
+ pos_x = 2 * decoder->offset + motion_x; \
+ pos_y = decoder->v_offset + motion_y; \
+ \
+ if (unlikely (pos_x > decoder->limit_x)) \
+ { \
+ pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
+ motion_x = pos_x - 2 * decoder->offset; \
+ } \
+ \
+ if (unlikely (pos_y > decoder->limit_y)) \
+ { \
+ pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
+ motion_y = pos_y - decoder->v_offset; \
+ } \
+ \
+ xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
+ offset = (pos_x >> 1) + (pos_y & ~1) * decoder->stride; \
+ \
+ table[xy_half] (decoder->dest[0] + decoder->offset, \
+ ref[0] + offset, 2 * decoder->stride, 8); \
+ table[xy_half] (decoder->dest[0] + decoder->stride + decoder->offset, \
+ ref[0] + decoder->stride + offset, \
+ 2 * decoder->stride, 8); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ table[xy_half] (decoder->dest[1] + decoder->offset, \
+ ref[1] + offset, 2 * decoder->stride, 8); \
+ table[xy_half] (decoder->dest[1] + decoder->stride + decoder->offset, \
+ ref[1] + decoder->stride + offset, \
+ 2 * decoder->stride, 8); \
+ table[xy_half] (decoder->dest[2] + decoder->offset, \
+ ref[2] + offset, 2 * decoder->stride, 8); \
+ table[xy_half] (decoder->dest[2] + decoder->stride + decoder->offset, \
+ ref[2] + decoder->stride + offset, \
+ 2 * decoder->stride, 8); \
+ }
+
+#define MOTION_ZERO_444(table, ref) \
+ offset = decoder->offset + decoder->v_offset * decoder->stride; \
+ \
+ table[0] (decoder->dest[0] + decoder->offset, \
+ ref[0] + offset, decoder->stride, 16); \
+ \
+ if (MPEG2_COLOR) \
+ { \
+ table[4] (decoder->dest[1] + decoder->offset, \
+ ref[1] + offset, decoder->stride, 16); \
+ table[4] (decoder->dest[2] + decoder->offset, \
+ ref[2] + offset, decoder->stride, 16); \
+ }
+
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+static void motion_mp1 (mpeg2_decoder_t * const decoder,
+ motion_t * const motion,
+ mpeg2_mc_fct * const * const table)
+{
+ int motion_x, motion_y;
+ unsigned int pos_x, pos_y, xy_half, offset;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ motion_x = motion->pmv[0][0] +
+ (get_motion_delta (decoder,
+ motion->f_code[0]) << motion->f_code[1]);
+ motion_x = bound_motion_vector (motion_x,
+ motion->f_code[0] + motion->f_code[1]);
+ motion->pmv[0][0] = motion_x;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ motion_y = motion->pmv[0][1] +
+ (get_motion_delta (decoder,
+ motion->f_code[0]) << motion->f_code[1]);
+ motion_y = bound_motion_vector (motion_y,
+ motion->f_code[0] + motion->f_code[1]);
+ motion->pmv[0][1] = motion_y;
+
+ MOTION_420 (table, motion->ref[0], motion_x, motion_y, 16, 0);
+}
+
+#define MOTION_FUNCTIONS(FORMAT, MOTION, MOTION_FIELD, \
+ MOTION_DMV, MOTION_ZERO) \
+ \
+static void motion_fr_frame_##FORMAT (mpeg2_decoder_t * const decoder, \
+ motion_t * const motion, \
+ mpeg2_mc_fct * const * const table) \
+{ \
+ int motion_x, motion_y; \
+ unsigned int pos_x, pos_y, xy_half, offset; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
+ motion->f_code[0]); \
+ motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
+ motion->pmv[1][0] = motion->pmv[0][0] = motion_x; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_y = motion->pmv[0][1] + get_motion_delta (decoder, \
+ motion->f_code[1]); \
+ motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
+ motion->pmv[1][1] = motion->pmv[0][1] = motion_y; \
+ \
+ MOTION (table, motion->ref[0], motion_x, motion_y, 16, 0); \
+} \
+ \
+static void motion_fr_field_##FORMAT (mpeg2_decoder_t * const decoder, \
+ motion_t * const motion, \
+ mpeg2_mc_fct * const * const table) \
+{ \
+ int motion_x, motion_y, field; \
+ unsigned int pos_x, pos_y, xy_half, offset; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ field = UBITS (bit_buf, 1); \
+ DUMPBITS (bit_buf, bits, 1); \
+ \
+ motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
+ motion->f_code[0]); \
+ motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
+ motion->pmv[0][0] = motion_x; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_y = ((motion->pmv[0][1] >> 1) + \
+ get_motion_delta (decoder, motion->f_code[1])); \
+ /* motion_y = bound_motion_vector (motion_y, motion->f_code[1]); */ \
+ motion->pmv[0][1] = motion_y << 1; \
+ \
+ MOTION_FIELD (table, motion->ref[0], motion_x, motion_y, 0, & ~1, field); \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ field = UBITS (bit_buf, 1); \
+ DUMPBITS (bit_buf, bits, 1); \
+ \
+ motion_x = motion->pmv[1][0] + get_motion_delta (decoder, \
+ motion->f_code[0]); \
+ motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
+ motion->pmv[1][0] = motion_x; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_y = ((motion->pmv[1][1] >> 1) + \
+ get_motion_delta (decoder, motion->f_code[1])); \
+ /* motion_y = bound_motion_vector (motion_y, motion->f_code[1]); */ \
+ motion->pmv[1][1] = motion_y << 1; \
+ \
+ MOTION_FIELD (table, motion->ref[0], motion_x, motion_y, 1, & ~1, field); \
+} \
+ \
+static void motion_fr_dmv_##FORMAT (mpeg2_decoder_t * const decoder, \
+ motion_t * const motion, \
+ mpeg2_mc_fct * const * const table) \
+{ \
+ int motion_x, motion_y, dmv_x, dmv_y, m, other_x, other_y; \
+ unsigned int pos_x, pos_y, xy_half, offset; \
+ \
+ (void)table; \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
+ motion->f_code[0]); \
+ motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
+ motion->pmv[1][0] = motion->pmv[0][0] = motion_x; \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ dmv_x = get_dmv (decoder); \
+ \
+ motion_y = ((motion->pmv[0][1] >> 1) + \
+ get_motion_delta (decoder, motion->f_code[1])); \
+ /* motion_y = bound_motion_vector (motion_y, motion->f_code[1]); */ \
+ motion->pmv[1][1] = motion->pmv[0][1] = motion_y << 1; \
+ dmv_y = get_dmv (decoder); \
+ \
+ m = decoder->top_field_first ? 1 : 3; \
+ other_x = ((motion_x * m + (motion_x > 0)) >> 1) + dmv_x; \
+ other_y = ((motion_y * m + (motion_y > 0)) >> 1) + dmv_y - 1; \
+ MOTION_FIELD (mpeg2_mc.put, motion->ref[0], other_x, other_y, 0, | 1, 0); \
+ \
+ m = decoder->top_field_first ? 3 : 1; \
+ other_x = ((motion_x * m + (motion_x > 0)) >> 1) + dmv_x; \
+ other_y = ((motion_y * m + (motion_y > 0)) >> 1) + dmv_y + 1; \
+ MOTION_FIELD (mpeg2_mc.put, motion->ref[0], other_x, other_y, 1, & ~1, 0);\
+ \
+ MOTION_DMV (mpeg2_mc.avg, motion->ref[0], motion_x, motion_y); \
+} \
+ \
+static void motion_reuse_##FORMAT (mpeg2_decoder_t * const decoder, \
+ motion_t * const motion, \
+ mpeg2_mc_fct * const * const table) \
+{ \
+ int motion_x, motion_y; \
+ unsigned int pos_x, pos_y, xy_half, offset; \
+ \
+ motion_x = motion->pmv[0][0]; \
+ motion_y = motion->pmv[0][1]; \
+ \
+ MOTION (table, motion->ref[0], motion_x, motion_y, 16, 0); \
+} \
+ \
+static void motion_zero_##FORMAT (mpeg2_decoder_t * const decoder, \
+ motion_t * const motion, \
+ mpeg2_mc_fct * const * const table) \
+{ \
+ unsigned int offset; \
+ \
+ motion->pmv[0][0] = motion->pmv[0][1] = 0; \
+ motion->pmv[1][0] = motion->pmv[1][1] = 0; \
+ \
+ MOTION_ZERO (table, motion->ref[0]); \
+} \
+ \
+static void motion_fi_field_##FORMAT (mpeg2_decoder_t * const decoder, \
+ motion_t * const motion, \
+ mpeg2_mc_fct * const * const table) \
+{ \
+ int motion_x, motion_y; \
+ uint8_t ** ref_field; \
+ unsigned int pos_x, pos_y, xy_half, offset; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ ref_field = motion->ref2[UBITS (bit_buf, 1)]; \
+ DUMPBITS (bit_buf, bits, 1); \
+ \
+ motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
+ motion->f_code[0]); \
+ motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
+ motion->pmv[1][0] = motion->pmv[0][0] = motion_x; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_y = motion->pmv[0][1] + get_motion_delta (decoder, \
+ motion->f_code[1]); \
+ motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
+ motion->pmv[1][1] = motion->pmv[0][1] = motion_y; \
+ \
+ MOTION (table, ref_field, motion_x, motion_y, 16, 0); \
+} \
+ \
+static void motion_fi_16x8_##FORMAT (mpeg2_decoder_t * const decoder, \
+ motion_t * const motion, \
+ mpeg2_mc_fct * const * const table) \
+{ \
+ int motion_x, motion_y; \
+ uint8_t ** ref_field; \
+ unsigned int pos_x, pos_y, xy_half, offset; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ ref_field = motion->ref2[UBITS (bit_buf, 1)]; \
+ DUMPBITS (bit_buf, bits, 1); \
+ \
+ motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
+ motion->f_code[0]); \
+ motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
+ motion->pmv[0][0] = motion_x; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_y = motion->pmv[0][1] + get_motion_delta (decoder, \
+ motion->f_code[1]); \
+ motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
+ motion->pmv[0][1] = motion_y; \
+ \
+ MOTION (table, ref_field, motion_x, motion_y, 8, 0); \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ ref_field = motion->ref2[UBITS (bit_buf, 1)]; \
+ DUMPBITS (bit_buf, bits, 1); \
+ \
+ motion_x = motion->pmv[1][0] + get_motion_delta (decoder, \
+ motion->f_code[0]); \
+ motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
+ motion->pmv[1][0] = motion_x; \
+ \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_y = motion->pmv[1][1] + get_motion_delta (decoder, \
+ motion->f_code[1]); \
+ motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
+ motion->pmv[1][1] = motion_y; \
+ \
+ MOTION (table, ref_field, motion_x, motion_y, 8, 8); \
+} \
+ \
+static void motion_fi_dmv_##FORMAT (mpeg2_decoder_t * const decoder, \
+ motion_t * const motion, \
+ mpeg2_mc_fct * const * const table) \
+{ \
+ int motion_x, motion_y, other_x, other_y; \
+ unsigned int pos_x, pos_y, xy_half, offset; \
+ \
+ (void)table; \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
+ motion->f_code[0]); \
+ motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
+ motion->pmv[1][0] = motion->pmv[0][0] = motion_x; \
+ NEEDBITS (bit_buf, bits, bit_ptr); \
+ other_x = ((motion_x + (motion_x > 0)) >> 1) + get_dmv (decoder); \
+ \
+ motion_y = motion->pmv[0][1] + get_motion_delta (decoder, \
+ motion->f_code[1]); \
+ motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
+ motion->pmv[1][1] = motion->pmv[0][1] = motion_y; \
+ other_y = (((motion_y + (motion_y > 0)) >> 1) + get_dmv (decoder) + \
+ decoder->dmv_offset); \
+ \
+ MOTION (mpeg2_mc.put, motion->ref[0], motion_x, motion_y, 16, 0); \
+ MOTION (mpeg2_mc.avg, motion->ref[1], other_x, other_y, 16, 0); \
+} \
+
+MOTION_FUNCTIONS (420, MOTION_420, MOTION_FIELD_420, MOTION_DMV_420,
+ MOTION_ZERO_420)
+MOTION_FUNCTIONS (422, MOTION_422, MOTION_FIELD_422, MOTION_DMV_422,
+ MOTION_ZERO_422)
+MOTION_FUNCTIONS (444, MOTION_444, MOTION_FIELD_444, MOTION_DMV_444,
+ MOTION_ZERO_444)
+
+/* like motion_frame, but parsing without actual motion compensation */
+static void motion_fr_conceal (mpeg2_decoder_t * const decoder)
+{
+ int tmp;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ tmp = (decoder->f_motion.pmv[0][0] +
+ get_motion_delta (decoder, decoder->f_motion.f_code[0]));
+ tmp = bound_motion_vector (tmp, decoder->f_motion.f_code[0]);
+ decoder->f_motion.pmv[1][0] = decoder->f_motion.pmv[0][0] = tmp;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ tmp = (decoder->f_motion.pmv[0][1] +
+ get_motion_delta (decoder, decoder->f_motion.f_code[1]));
+ tmp = bound_motion_vector (tmp, decoder->f_motion.f_code[1]);
+ decoder->f_motion.pmv[1][1] = decoder->f_motion.pmv[0][1] = tmp;
+
+ DUMPBITS (bit_buf, bits, 1); /* remove marker_bit */
+}
+
+static void motion_fi_conceal (mpeg2_decoder_t * const decoder)
+{
+ int tmp;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ DUMPBITS (bit_buf, bits, 1); /* remove field_select */
+
+ tmp = decoder->f_motion.pmv[0][0] +
+ get_motion_delta (decoder, decoder->f_motion.f_code[0]);
+ tmp = bound_motion_vector (tmp, decoder->f_motion.f_code[0]);
+
+ decoder->f_motion.pmv[1][0] =
+ decoder->f_motion.pmv[0][0] = tmp;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ tmp = (decoder->f_motion.pmv[0][1] +
+ get_motion_delta (decoder, decoder->f_motion.f_code[1]));
+ tmp = bound_motion_vector (tmp, decoder->f_motion.f_code[1]);
+
+ decoder->f_motion.pmv[1][1] =
+ decoder->f_motion.pmv[0][1] = tmp;
+
+ DUMPBITS (bit_buf, bits, 1); /* remove marker_bit */
+}
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+
+#define MOTION_CALL(routine, direction) \
+do { \
+ if ((direction) & MACROBLOCK_MOTION_FORWARD) \
+ routine (decoder, &decoder->f_motion, mpeg2_mc.put); \
+ \
+ if ((direction) & MACROBLOCK_MOTION_BACKWARD) \
+ { \
+ routine (decoder, &decoder->b_motion, \
+ ((direction) & MACROBLOCK_MOTION_FORWARD ? \
+ mpeg2_mc.avg : mpeg2_mc.put)); \
+ } \
+} while (0)
+
+#define NEXT_MACROBLOCK \
+do { \
+ decoder->offset += 16; \
+ \
+ if (decoder->offset == decoder->width) \
+ { \
+ do { /* just so we can use the break statement */ \
+ if (decoder->convert) \
+ { \
+ decoder->convert (decoder->convert_id, decoder->dest, \
+ decoder->v_offset); \
+ if (decoder->coding_type == B_TYPE) \
+ break; \
+ } \
+ \
+ decoder->dest[0] += decoder->slice_stride; \
+ if (MPEG2_COLOR) \
+ { \
+ decoder->dest[1] += decoder->slice_uv_stride; \
+ decoder->dest[2] += decoder->slice_uv_stride; \
+ } \
+ } while (0); \
+ \
+ decoder->v_offset += 16; \
+ \
+ if (decoder->v_offset > decoder->limit_y) \
+ return; \
+ \
+ decoder->offset = 0; \
+ } \
+} while (0)
+
+void mpeg2_init_fbuf (mpeg2_decoder_t * decoder,
+ uint8_t * current_fbuf[MPEG2_COMPONENTS],
+ uint8_t * forward_fbuf[MPEG2_COMPONENTS],
+ uint8_t * backward_fbuf[MPEG2_COMPONENTS])
+{
+ int offset, stride, height, bottom_field;
+
+ stride = decoder->stride_frame;
+ bottom_field = (decoder->picture_structure == BOTTOM_FIELD);
+ offset = bottom_field ? stride : 0;
+ height = decoder->height;
+
+ decoder->picture_dest[0] = current_fbuf[0] + offset;
+#if MPEG2_COLOR
+ decoder->picture_dest[1] = current_fbuf[1] + (offset >> 1);
+ decoder->picture_dest[2] = current_fbuf[2] + (offset >> 1);
+#endif
+
+ decoder->f_motion.ref[0][0] = forward_fbuf[0] + offset;
+#if MPEG2_COLOR
+ decoder->f_motion.ref[0][1] = forward_fbuf[1] + (offset >> 1);
+ decoder->f_motion.ref[0][2] = forward_fbuf[2] + (offset >> 1);
+#endif
+
+ decoder->b_motion.ref[0][0] = backward_fbuf[0] + offset;
+#if MPEG2_COLOR
+ decoder->b_motion.ref[0][1] = backward_fbuf[1] + (offset >> 1);
+ decoder->b_motion.ref[0][2] = backward_fbuf[2] + (offset >> 1);
+#endif
+
+ if (decoder->picture_structure != FRAME_PICTURE)
+ {
+ decoder->dmv_offset = bottom_field ? 1 : -1;
+ decoder->f_motion.ref2[0] = decoder->f_motion.ref[bottom_field];
+ decoder->f_motion.ref2[1] = decoder->f_motion.ref[!bottom_field];
+ decoder->b_motion.ref2[0] = decoder->b_motion.ref[bottom_field];
+ decoder->b_motion.ref2[1] = decoder->b_motion.ref[!bottom_field];
+ offset = stride - offset;
+
+ if (decoder->second_field && (decoder->coding_type != B_TYPE))
+ forward_fbuf = current_fbuf;
+
+ decoder->f_motion.ref[1][0] = forward_fbuf[0] + offset;
+#if MPEG2_COLOR
+ decoder->f_motion.ref[1][1] = forward_fbuf[1] + (offset >> 1);
+ decoder->f_motion.ref[1][2] = forward_fbuf[2] + (offset >> 1);
+#endif
+ decoder->b_motion.ref[1][0] = backward_fbuf[0] + offset;
+#if MPEG2_COLOR
+ decoder->b_motion.ref[1][1] = backward_fbuf[1] + (offset >> 1);
+ decoder->b_motion.ref[1][2] = backward_fbuf[2] + (offset >> 1);
+#endif
+ stride <<= 1;
+ height >>= 1;
+ }
+
+ decoder->stride = stride;
+ decoder->slice_stride = 16 * stride;
+#if MPEG2_COLOR
+ decoder->uv_stride = stride >> 1;
+ decoder->slice_uv_stride =
+ decoder->slice_stride >> (2 - decoder->chroma_format);
+#endif
+ decoder->limit_x = 2 * decoder->width - 32;
+ decoder->limit_y_16 = 2 * height - 32;
+ decoder->limit_y_8 = 2 * height - 16;
+ decoder->limit_y = height - 16;
+
+ if (decoder->mpeg1)
+ {
+ decoder->motion_parser[0] = motion_zero_420;
+ decoder->motion_parser[MC_FRAME] = motion_mp1;
+ decoder->motion_parser[4] = motion_reuse_420;
+ }
+ else if (decoder->picture_structure == FRAME_PICTURE)
+ {
+ if (decoder->chroma_format == 0)
+ {
+ decoder->motion_parser[0] = motion_zero_420;
+ decoder->motion_parser[MC_FIELD] = motion_fr_field_420;
+ decoder->motion_parser[MC_FRAME] = motion_fr_frame_420;
+ decoder->motion_parser[MC_DMV] = motion_fr_dmv_420;
+ decoder->motion_parser[4] = motion_reuse_420;
+ }
+ else if (decoder->chroma_format == 1)
+ {
+ decoder->motion_parser[0] = motion_zero_422;
+ decoder->motion_parser[MC_FIELD] = motion_fr_field_422;
+ decoder->motion_parser[MC_FRAME] = motion_fr_frame_422;
+ decoder->motion_parser[MC_DMV] = motion_fr_dmv_422;
+ decoder->motion_parser[4] = motion_reuse_422;
+ }
+ else
+ {
+ decoder->motion_parser[0] = motion_zero_444;
+ decoder->motion_parser[MC_FIELD] = motion_fr_field_444;
+ decoder->motion_parser[MC_FRAME] = motion_fr_frame_444;
+ decoder->motion_parser[MC_DMV] = motion_fr_dmv_444;
+ decoder->motion_parser[4] = motion_reuse_444;
+ }
+ }
+ else
+ {
+ if (decoder->chroma_format == 0)
+ {
+ decoder->motion_parser[0] = motion_zero_420;
+ decoder->motion_parser[MC_FIELD] = motion_fi_field_420;
+ decoder->motion_parser[MC_16X8] = motion_fi_16x8_420;
+ decoder->motion_parser[MC_DMV] = motion_fi_dmv_420;
+ decoder->motion_parser[4] = motion_reuse_420;
+ }
+ else if (decoder->chroma_format == 1)
+ {
+ decoder->motion_parser[0] = motion_zero_422;
+ decoder->motion_parser[MC_FIELD] = motion_fi_field_422;
+ decoder->motion_parser[MC_16X8] = motion_fi_16x8_422;
+ decoder->motion_parser[MC_DMV] = motion_fi_dmv_422;
+ decoder->motion_parser[4] = motion_reuse_422;
+ }
+ else
+ {
+ decoder->motion_parser[0] = motion_zero_444;
+ decoder->motion_parser[MC_FIELD] = motion_fi_field_444;
+ decoder->motion_parser[MC_16X8] = motion_fi_16x8_444;
+ decoder->motion_parser[MC_DMV] = motion_fi_dmv_444;
+ decoder->motion_parser[4] = motion_reuse_444;
+ }
+ }
+}
+
+static inline int slice_init (mpeg2_decoder_t * const decoder, int code)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ int offset;
+ const MBAtab * mba;
+
+#if MPEG2_COLOR
+ decoder->dc_dct_pred[0] = decoder->dc_dct_pred[1] =
+ decoder->dc_dct_pred[2] = 16384;
+#else
+ decoder->dc_dct_pred[0] = 16384;
+#endif
+
+ decoder->f_motion.pmv[0][0] = decoder->f_motion.pmv[0][1] = 0;
+ decoder->f_motion.pmv[1][0] = decoder->f_motion.pmv[1][1] = 0;
+ decoder->b_motion.pmv[0][0] = decoder->b_motion.pmv[0][1] = 0;
+ decoder->b_motion.pmv[1][0] = decoder->b_motion.pmv[1][1] = 0;
+
+ if (decoder->vertical_position_extension)
+ {
+ code += UBITS (bit_buf, 3) << 7;
+ DUMPBITS (bit_buf, bits, 3);
+ }
+
+ decoder->v_offset = (code - 1) * 16;
+ offset = 0;
+
+ if (!(decoder->convert) || decoder->coding_type != B_TYPE)
+ {
+ offset = (code - 1) * decoder->slice_stride;
+ }
+
+ decoder->dest[0] = decoder->picture_dest[0] + offset;
+#if MPEG2_COLOR
+ offset >>= (2 - decoder->chroma_format);
+ decoder->dest[1] = decoder->picture_dest[1] + offset;
+ decoder->dest[2] = decoder->picture_dest[2] + offset;
+#endif
+
+ get_quantizer_scale (decoder);
+
+ /* ignore intra_slice and all the extra data */
+ while (bit_buf & 0x80000000)
+ {
+ DUMPBITS (bit_buf, bits, 9);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ }
+
+ /* decode initial macroblock address increment */
+ offset = 0;
+ while (1)
+ {
+ if (bit_buf >= 0x08000000)
+ {
+ mba = MBA_5 + (UBITS (bit_buf, 6) - 2);
+ break;
+ }
+ else if (bit_buf >= 0x01800000)
+ {
+ mba = MBA_11 + (UBITS (bit_buf, 12) - 24);
+ break;
+ }
+ else
+ {
+ switch (UBITS (bit_buf, 12))
+ {
+ case 8: /* macroblock_escape */
+ offset += 33;
+ DUMPBITS (bit_buf, bits, 11);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ case 15: /* macroblock_stuffing (MPEG1 only) */
+ bit_buf &= 0xfffff;
+ DUMPBITS (bit_buf, bits, 11);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ default: /* error */
+ return 1;
+ }
+ }
+ }
+
+ DUMPBITS (bit_buf, bits, mba->len + 1);
+ decoder->offset = (offset + mba->mba) << 4;
+
+ while (decoder->offset - decoder->width >= 0)
+ {
+ decoder->offset -= decoder->width;
+
+ if (!(decoder->convert) || decoder->coding_type != B_TYPE)
+ {
+ decoder->dest[0] += decoder->slice_stride;
+#if MPEG2_COLOR
+ decoder->dest[1] += decoder->slice_uv_stride;
+ decoder->dest[2] += decoder->slice_uv_stride;
+#endif
+ }
+
+ decoder->v_offset += 16;
+ }
+
+ if (decoder->v_offset > decoder->limit_y)
+ return 1;
+
+ return 0;
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
+
+void mpeg2_slice (mpeg2_decoder_t * const decoder, const int code,
+ const uint8_t * const buffer)
+{
+#define bit_buf (decoder->bitstream_buf)
+#define bits (decoder->bitstream_bits)
+#define bit_ptr (decoder->bitstream_ptr)
+
+ bitstream_init (decoder, buffer);
+
+ if (slice_init (decoder, code))
+ return;
+
+ while (1)
+ {
+ int macroblock_modes;
+ int mba_inc;
+ const MBAtab * mba;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+
+ macroblock_modes = get_macroblock_modes (decoder);
+
+ /* maybe integrate MACROBLOCK_QUANT test into get_macroblock_modes ? */
+ if (macroblock_modes & MACROBLOCK_QUANT)
+ get_quantizer_scale (decoder);
+
+ if (macroblock_modes & MACROBLOCK_INTRA)
+ {
+ int DCT_offset, DCT_stride;
+ int offset;
+ uint8_t * dest_y;
+
+ if (decoder->concealment_motion_vectors)
+ {
+ if (decoder->picture_structure == FRAME_PICTURE)
+ motion_fr_conceal (decoder);
+ else
+ motion_fi_conceal (decoder);
+ }
+ else
+ {
+ decoder->f_motion.pmv[0][0] = decoder->f_motion.pmv[0][1] = 0;
+ decoder->f_motion.pmv[1][0] = decoder->f_motion.pmv[1][1] = 0;
+ decoder->b_motion.pmv[0][0] = decoder->b_motion.pmv[0][1] = 0;
+ decoder->b_motion.pmv[1][0] = decoder->b_motion.pmv[1][1] = 0;
+ }
+
+ if (macroblock_modes & DCT_TYPE_INTERLACED)
+ {
+ DCT_offset = decoder->stride;
+ DCT_stride = decoder->stride * 2;
+ }
+ else
+ {
+ DCT_offset = decoder->stride * 8;
+ DCT_stride = decoder->stride;
+ }
+
+ offset = decoder->offset;
+ dest_y = decoder->dest[0] + offset;
+ slice_intra_DCT (decoder, 0, dest_y, DCT_stride);
+ slice_intra_DCT (decoder, 0, dest_y + 8, DCT_stride);
+ slice_intra_DCT (decoder, 0, dest_y + DCT_offset, DCT_stride);
+ slice_intra_DCT (decoder, 0, dest_y + DCT_offset + 8, DCT_stride);
+
+#if MPEG2_COLOR
+ if (likely (decoder->chroma_format == 0))
+ {
+ slice_intra_DCT (decoder, 1, decoder->dest[1] + (offset >> 1),
+ decoder->uv_stride);
+ slice_intra_DCT (decoder, 2, decoder->dest[2] + (offset >> 1),
+ decoder->uv_stride);
+
+ if (decoder->coding_type == D_TYPE)
+ {
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ DUMPBITS (bit_buf, bits, 1);
+ }
+ }
+ else if (likely (decoder->chroma_format == 1))
+ {
+ uint8_t * dest_u = decoder->dest[1] + (offset >> 1);
+ uint8_t * dest_v = decoder->dest[2] + (offset >> 1);
+
+ DCT_stride >>= 1;
+ DCT_offset >>= 1;
+
+ slice_intra_DCT (decoder, 1, dest_u, DCT_stride);
+ slice_intra_DCT (decoder, 2, dest_v, DCT_stride);
+ slice_intra_DCT (decoder, 1, dest_u + DCT_offset, DCT_stride);
+ slice_intra_DCT (decoder, 2, dest_v + DCT_offset, DCT_stride);
+ }
+ else
+ {
+ uint8_t * dest_u = decoder->dest[1] + offset;
+ uint8_t * dest_v = decoder->dest[2] + offset;
+
+ slice_intra_DCT (decoder, 1, dest_u, DCT_stride);
+ slice_intra_DCT (decoder, 2, dest_v, DCT_stride);
+ slice_intra_DCT (decoder, 1, dest_u + DCT_offset, DCT_stride);
+ slice_intra_DCT (decoder, 2, dest_v + DCT_offset, DCT_stride);
+ slice_intra_DCT (decoder, 1, dest_u + 8, DCT_stride);
+ slice_intra_DCT (decoder, 2, dest_v + 8, DCT_stride);
+ slice_intra_DCT (decoder, 1, dest_u + DCT_offset + 8,
+ DCT_stride);
+ slice_intra_DCT (decoder, 2, dest_v + DCT_offset + 8,
+ DCT_stride);
+ }
+#else
+ skip_chroma_intra(decoder);
+#endif /* MPEG2_COLOR */
+ }
+ else
+ {
+ motion_parser_t * parser;
+
+ parser =
+ decoder->motion_parser[macroblock_modes >> MOTION_TYPE_SHIFT];
+ MOTION_CALL (parser, macroblock_modes);
+
+ if (macroblock_modes & MACROBLOCK_PATTERN)
+ {
+ int coded_block_pattern;
+ int DCT_offset, DCT_stride;
+
+ if (macroblock_modes & DCT_TYPE_INTERLACED)
+ {
+ DCT_offset = decoder->stride;
+ DCT_stride = decoder->stride * 2;
+ }
+ else
+ {
+ DCT_offset = decoder->stride * 8;
+ DCT_stride = decoder->stride;
+ }
+
+ coded_block_pattern = get_coded_block_pattern (decoder);
+
+ if (likely (decoder->chroma_format == 0))
+ {
+ int offset = decoder->offset;
+ uint8_t * dest_y = decoder->dest[0] + offset;
+
+ if (coded_block_pattern & 1)
+ slice_non_intra_DCT (decoder, 0, dest_y, DCT_stride);
+
+ if (coded_block_pattern & 2)
+ slice_non_intra_DCT (decoder, 0, dest_y + 8,
+ DCT_stride);
+
+ if (coded_block_pattern & 4)
+ slice_non_intra_DCT (decoder, 0, dest_y + DCT_offset,
+ DCT_stride);
+
+ if (coded_block_pattern & 8)
+ slice_non_intra_DCT (decoder, 0,
+ dest_y + DCT_offset + 8,
+ DCT_stride);
+#if MPEG2_COLOR
+ if (coded_block_pattern & 16)
+ slice_non_intra_DCT (decoder, 1,
+ decoder->dest[1] + (offset >> 1),
+ decoder->uv_stride);
+
+ if (coded_block_pattern & 32)
+ slice_non_intra_DCT (decoder, 2,
+ decoder->dest[2] + (offset >> 1),
+ decoder->uv_stride);
+#endif /* MPEG2_COLOR */
+ }
+ else if (likely (decoder->chroma_format == 1))
+ {
+ int offset;
+ uint8_t * dest_y;
+
+ coded_block_pattern |= bit_buf & (3 << 30);
+ DUMPBITS (bit_buf, bits, 2);
+
+ offset = decoder->offset;
+ dest_y = decoder->dest[0] + offset;
+
+ if (coded_block_pattern & 1)
+ slice_non_intra_DCT (decoder, 0, dest_y, DCT_stride);
+
+ if (coded_block_pattern & 2)
+ slice_non_intra_DCT (decoder, 0, dest_y + 8,
+ DCT_stride);
+
+ if (coded_block_pattern & 4)
+ slice_non_intra_DCT (decoder, 0, dest_y + DCT_offset,
+ DCT_stride);
+
+ if (coded_block_pattern & 8)
+ slice_non_intra_DCT (decoder, 0,
+ dest_y + DCT_offset + 8,
+ DCT_stride);
+#if MPEG2_COLOR
+ DCT_stride >>= 1;
+ DCT_offset = (DCT_offset + offset) >> 1;
+
+ if (coded_block_pattern & 16)
+ slice_non_intra_DCT (decoder, 1,
+ decoder->dest[1] + (offset >> 1),
+ DCT_stride);
+
+ if (coded_block_pattern & 32)
+ slice_non_intra_DCT (decoder, 2,
+ decoder->dest[2] + (offset >> 1),
+ DCT_stride);
+
+ if (coded_block_pattern & (2 << 30))
+ slice_non_intra_DCT (decoder, 1,
+ decoder->dest[1] + DCT_offset,
+ DCT_stride);
+
+ if (coded_block_pattern & (1 << 30))
+ slice_non_intra_DCT (decoder, 2,
+ decoder->dest[2] + DCT_offset,
+ DCT_stride);
+#endif /* MPEG2_COLOR */
+ }
+ else
+ {
+ int offset = decoder->offset;
+ uint8_t * dest_y = decoder->dest[0] + offset;
+#if MPEG2_COLOR
+ uint8_t * dest_u = decoder->dest[1] + offset;
+ uint8_t * dest_v = decoder->dest[2] + offset;
+#endif
+ coded_block_pattern |= bit_buf & (63 << 26);
+ DUMPBITS (bit_buf, bits, 6);
+
+ if (coded_block_pattern & 1)
+ slice_non_intra_DCT (decoder, 0, dest_y, DCT_stride);
+
+ if (coded_block_pattern & 2)
+ slice_non_intra_DCT (decoder, 0, dest_y + 8,
+ DCT_stride);
+
+ if (coded_block_pattern & 4)
+ slice_non_intra_DCT (decoder, 0, dest_y + DCT_offset,
+ DCT_stride);
+
+ if (coded_block_pattern & 8)
+ slice_non_intra_DCT (decoder, 0,
+ dest_y + DCT_offset + 8,
+ DCT_stride);
+#if MPEG2_COLOR
+ if (coded_block_pattern & 16)
+ slice_non_intra_DCT (decoder, 1, dest_u, DCT_stride);
+
+ if (coded_block_pattern & 32)
+ slice_non_intra_DCT (decoder, 2, dest_v, DCT_stride);
+
+ if (coded_block_pattern & (32 << 26))
+ slice_non_intra_DCT (decoder, 1, dest_u + DCT_offset,
+ DCT_stride);
+
+ if (coded_block_pattern & (16 << 26))
+ slice_non_intra_DCT (decoder, 2, dest_v + DCT_offset,
+ DCT_stride);
+
+ if (coded_block_pattern & (8 << 26))
+ slice_non_intra_DCT (decoder, 1, dest_u + 8,
+ DCT_stride);
+
+ if (coded_block_pattern & (4 << 26))
+ slice_non_intra_DCT (decoder, 2, dest_v + 8,
+ DCT_stride);
+
+ if (coded_block_pattern & (2 << 26))
+ slice_non_intra_DCT (decoder, 1,
+ dest_u + DCT_offset + 8,
+ DCT_stride);
+
+ if (coded_block_pattern & (1 << 26))
+ slice_non_intra_DCT (decoder, 2,
+ dest_v + DCT_offset + 8,
+ DCT_stride);
+#endif /* MPEG2_COLOR */
+ }
+#if !MPEG2_COLOR
+ skip_chroma_non_intra(decoder, coded_block_pattern);
+#endif
+ }
+
+#if MPEG2_COLOR
+ decoder->dc_dct_pred[0] = decoder->dc_dct_pred[1] =
+ decoder->dc_dct_pred[2] = 16384;
+#else
+ decoder->dc_dct_pred[0] = 16384;
+#endif
+ }
+
+ NEXT_MACROBLOCK;
+
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ mba_inc = 0;
+
+ while (1)
+ {
+ if (bit_buf >= 0x10000000)
+ {
+ mba = MBA_5 + (UBITS (bit_buf, 5) - 2);
+ break;
+ }
+ else if (bit_buf >= 0x03000000)
+ {
+ mba = MBA_11 + (UBITS (bit_buf, 11) - 24);
+ break;
+ }
+ else
+ {
+ switch (UBITS (bit_buf, 11))
+ {
+ case 8: /* macroblock_escape */
+ mba_inc += 33;
+ /* pass through */
+ case 15: /* macroblock_stuffing (MPEG1 only) */
+ DUMPBITS (bit_buf, bits, 11);
+ NEEDBITS (bit_buf, bits, bit_ptr);
+ continue;
+ default: /* end of slice, or error */
+ return;
+ }
+ }
+ }
+
+ DUMPBITS (bit_buf, bits, mba->len);
+ mba_inc += mba->mba;
+
+ if (mba_inc)
+ {
+#if MPEG2_COLOR
+ decoder->dc_dct_pred[0] = decoder->dc_dct_pred[1] =
+ decoder->dc_dct_pred[2] = 16384;
+#else
+ decoder->dc_dct_pred[0] = 16384;
+#endif
+ if (decoder->coding_type == P_TYPE)
+ {
+ do
+ {
+ MOTION_CALL (decoder->motion_parser[0],
+ MACROBLOCK_MOTION_FORWARD);
+ NEXT_MACROBLOCK;
+ }
+ while (--mba_inc);
+ }
+ else
+ {
+ do
+ {
+ MOTION_CALL (decoder->motion_parser[4], macroblock_modes);
+ NEXT_MACROBLOCK;
+ }
+ while (--mba_inc);
+ }
+ }
+ }
+
+#undef bit_buf
+#undef bits
+#undef bit_ptr
+}
diff --git a/apps/plugins/mpegplayer/libmpeg2/vlc.h b/apps/plugins/mpegplayer/libmpeg2/vlc.h
new file mode 100644
index 0000000000..d1b6a98cde
--- /dev/null
+++ b/apps/plugins/mpegplayer/libmpeg2/vlc.h
@@ -0,0 +1,433 @@
+/*
+ * vlc.h
+ * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ * See http://libmpeg2.sourceforge.net/ for updates.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * $Id$
+ * libmpeg2 sync history:
+ * 2008-07-01 - CVS revision 1.12
+ */
+
+#define GETWORD(bit_buf, shift, bit_ptr) \
+do { \
+ bit_buf |= ((bit_ptr[0] << 8) | bit_ptr[1]) << (shift); \
+ bit_ptr += 2; \
+} while (0)
+
+static inline void bitstream_init (mpeg2_decoder_t * decoder,
+ const uint8_t * start)
+{
+ decoder->bitstream_buf =
+ (start[0] << 24) | (start[1] << 16) | (start[2] << 8) | start[3];
+ decoder->bitstream_ptr = start + 4;
+ decoder->bitstream_bits = -16;
+}
+
+/* make sure that there are at least 16 valid bits in bit_buf */
+#define NEEDBITS(bit_buf, bits, bit_ptr) \
+do { \
+ if (unlikely (bits > 0)) { \
+ GETWORD (bit_buf, bits, bit_ptr); \
+ bits -= 16; \
+ } \
+} while (0)
+
+/* remove num valid bits from bit_buf */
+#define DUMPBITS(bit_buf, bits, num) \
+do { \
+ bit_buf <<= (num); \
+ bits += (num); \
+} while (0)
+
+/* take num bits from the high part of bit_buf and zero extend them */
+#define UBITS(bit_buf,num) (((uint32_t)(bit_buf)) >> (32 - (num)))
+
+/* take num bits from the high part of bit_buf and sign extend them */
+#define SBITS(bit_buf,num) (((int32_t)(bit_buf)) >> (32 - (num)))
+
+typedef struct {
+ uint8_t modes;
+ uint8_t len;
+} MBtab;
+
+typedef struct {
+ uint8_t delta;
+ uint8_t len;
+} MVtab;
+
+typedef struct {
+ int8_t dmv;
+ uint8_t len;
+} DMVtab;
+
+typedef struct {
+ uint8_t cbp;
+ uint8_t len;
+} CBPtab;
+
+typedef struct {
+ uint8_t size;
+ uint8_t len;
+} DCtab;
+
+typedef struct {
+ uint8_t run;
+ uint8_t level;
+ uint8_t len;
+} DCTtab;
+
+typedef struct {
+ uint8_t mba;
+ uint8_t len;
+} MBAtab;
+
+
+#define INTRA MACROBLOCK_INTRA
+#define QUANT MACROBLOCK_QUANT
+
+static const MBtab MB_I [] ICONST_ATTR = {
+ {INTRA|QUANT, 2}, {INTRA, 1}
+};
+
+#define MC MACROBLOCK_MOTION_FORWARD
+#define CODED MACROBLOCK_PATTERN
+
+static const MBtab MB_P [] ICONST_ATTR = {
+ {INTRA|QUANT, 6}, {CODED|QUANT, 5}, {MC|CODED|QUANT, 5}, {INTRA, 5},
+ {MC, 3}, {MC, 3}, {MC, 3}, {MC, 3},
+ {CODED, 2}, {CODED, 2}, {CODED, 2}, {CODED, 2},
+ {CODED, 2}, {CODED, 2}, {CODED, 2}, {CODED, 2},
+ {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1},
+ {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1},
+ {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1},
+ {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}
+};
+
+#define FWD MACROBLOCK_MOTION_FORWARD
+#define BWD MACROBLOCK_MOTION_BACKWARD
+#define INTER MACROBLOCK_MOTION_FORWARD|MACROBLOCK_MOTION_BACKWARD
+
+static const MBtab MB_B [] ICONST_ATTR = {
+ {0, 6}, {INTRA|QUANT, 6},
+ {BWD|CODED|QUANT, 6}, {FWD|CODED|QUANT, 6},
+ {INTER|CODED|QUANT, 5}, {INTER|CODED|QUANT, 5},
+ {INTRA, 5}, {INTRA, 5},
+ {FWD, 4}, {FWD, 4}, {FWD, 4}, {FWD, 4},
+ {FWD|CODED, 4}, {FWD|CODED, 4}, {FWD|CODED, 4}, {FWD|CODED, 4},
+ {BWD, 3}, {BWD, 3}, {BWD, 3}, {BWD, 3},
+ {BWD, 3}, {BWD, 3}, {BWD, 3}, {BWD, 3},
+ {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3},
+ {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3},
+ {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2},
+ {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2},
+ {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2},
+ {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2},
+ {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2},
+ {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2},
+ {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2},
+ {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}
+};
+
+#undef INTRA
+#undef QUANT
+#undef MC
+#undef CODED
+#undef FWD
+#undef BWD
+#undef INTER
+
+
+static const MVtab MV_4 [] ICONST_ATTR = {
+ { 3, 6}, { 2, 4}, { 1, 3}, { 1, 3}, { 0, 2}, { 0, 2}, { 0, 2}, { 0, 2}
+};
+
+static const MVtab MV_10 [] ICONST_ATTR = {
+ { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10},
+ { 0,10}, { 0,10}, { 0,10}, { 0,10}, {15,10}, {14,10}, {13,10}, {12,10},
+ {11,10}, {10,10}, { 9, 9}, { 9, 9}, { 8, 9}, { 8, 9}, { 7, 9}, { 7, 9},
+ { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7},
+ { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7},
+ { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}
+};
+
+
+static const DMVtab DMV_2 [] ICONST_ATTR = {
+ { 0, 1}, { 0, 1}, { 1, 2}, {-1, 2}
+};
+
+
+static const CBPtab CBP_7 [] ICONST_ATTR = {
+ {0x11, 7}, {0x12, 7}, {0x14, 7}, {0x18, 7},
+ {0x21, 7}, {0x22, 7}, {0x24, 7}, {0x28, 7},
+ {0x3f, 6}, {0x3f, 6}, {0x30, 6}, {0x30, 6},
+ {0x09, 6}, {0x09, 6}, {0x06, 6}, {0x06, 6},
+ {0x1f, 5}, {0x1f, 5}, {0x1f, 5}, {0x1f, 5},
+ {0x10, 5}, {0x10, 5}, {0x10, 5}, {0x10, 5},
+ {0x2f, 5}, {0x2f, 5}, {0x2f, 5}, {0x2f, 5},
+ {0x20, 5}, {0x20, 5}, {0x20, 5}, {0x20, 5},
+ {0x07, 5}, {0x07, 5}, {0x07, 5}, {0x07, 5},
+ {0x0b, 5}, {0x0b, 5}, {0x0b, 5}, {0x0b, 5},
+ {0x0d, 5}, {0x0d, 5}, {0x0d, 5}, {0x0d, 5},
+ {0x0e, 5}, {0x0e, 5}, {0x0e, 5}, {0x0e, 5},
+ {0x05, 5}, {0x05, 5}, {0x05, 5}, {0x05, 5},
+ {0x0a, 5}, {0x0a, 5}, {0x0a, 5}, {0x0a, 5},
+ {0x03, 5}, {0x03, 5}, {0x03, 5}, {0x03, 5},
+ {0x0c, 5}, {0x0c, 5}, {0x0c, 5}, {0x0c, 5},
+ {0x01, 4}, {0x01, 4}, {0x01, 4}, {0x01, 4},
+ {0x01, 4}, {0x01, 4}, {0x01, 4}, {0x01, 4},
+ {0x02, 4}, {0x02, 4}, {0x02, 4}, {0x02, 4},
+ {0x02, 4}, {0x02, 4}, {0x02, 4}, {0x02, 4},
+ {0x04, 4}, {0x04, 4}, {0x04, 4}, {0x04, 4},
+ {0x04, 4}, {0x04, 4}, {0x04, 4}, {0x04, 4},
+ {0x08, 4}, {0x08, 4}, {0x08, 4}, {0x08, 4},
+ {0x08, 4}, {0x08, 4}, {0x08, 4}, {0x08, 4},
+ {0x0f, 3}, {0x0f, 3}, {0x0f, 3}, {0x0f, 3},
+ {0x0f, 3}, {0x0f, 3}, {0x0f, 3}, {0x0f, 3},
+ {0x0f, 3}, {0x0f, 3}, {0x0f, 3}, {0x0f, 3},
+ {0x0f, 3}, {0x0f, 3}, {0x0f, 3}, {0x0f, 3}
+};
+
+static const CBPtab CBP_9 [] ICONST_ATTR = {
+ {0, 9}, {0x00, 9}, {0x39, 9}, {0x36, 9},
+ {0x37, 9}, {0x3b, 9}, {0x3d, 9}, {0x3e, 9},
+ {0x17, 8}, {0x17, 8}, {0x1b, 8}, {0x1b, 8},
+ {0x1d, 8}, {0x1d, 8}, {0x1e, 8}, {0x1e, 8},
+ {0x27, 8}, {0x27, 8}, {0x2b, 8}, {0x2b, 8},
+ {0x2d, 8}, {0x2d, 8}, {0x2e, 8}, {0x2e, 8},
+ {0x19, 8}, {0x19, 8}, {0x16, 8}, {0x16, 8},
+ {0x29, 8}, {0x29, 8}, {0x26, 8}, {0x26, 8},
+ {0x35, 8}, {0x35, 8}, {0x3a, 8}, {0x3a, 8},
+ {0x33, 8}, {0x33, 8}, {0x3c, 8}, {0x3c, 8},
+ {0x15, 8}, {0x15, 8}, {0x1a, 8}, {0x1a, 8},
+ {0x13, 8}, {0x13, 8}, {0x1c, 8}, {0x1c, 8},
+ {0x25, 8}, {0x25, 8}, {0x2a, 8}, {0x2a, 8},
+ {0x23, 8}, {0x23, 8}, {0x2c, 8}, {0x2c, 8},
+ {0x31, 8}, {0x31, 8}, {0x32, 8}, {0x32, 8},
+ {0x34, 8}, {0x34, 8}, {0x38, 8}, {0x38, 8}
+};
+
+
+static const DCtab DC_lum_5 [] ICONST_ATTR = {
+ {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2},
+ {0, 3}, {0, 3}, {0, 3}, {0, 3}, {3, 3}, {3, 3}, {3, 3}, {3, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3}, {5, 4}, {5, 4}, {6, 5}
+};
+
+static const DCtab DC_chrom_5 [] ICONST_ATTR = {
+ {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2},
+ {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2},
+ {3, 3}, {3, 3}, {3, 3}, {3, 3}, {4, 4}, {4, 4}, {5, 5}
+};
+
+static const DCtab DC_long [] ICONST_ATTR = {
+ {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, { 6, 5}, { 6, 5},
+ {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, { 6, 5}, { 6, 5},
+ {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, { 7, 6}, { 7, 6},
+ {8, 7}, {8, 7}, {8, 7}, {8, 7}, {9, 8}, {9, 8}, {10, 9}, {11, 9}
+};
+
+
+static const DCTtab DCT_16 [] ICONST_ATTR = {
+ {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0},
+ {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0},
+ {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0},
+ {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0},
+ { 2,18, 0}, { 2,17, 0}, { 2,16, 0}, { 2,15, 0},
+ { 7, 3, 0}, { 17, 2, 0}, { 16, 2, 0}, { 15, 2, 0},
+ { 14, 2, 0}, { 13, 2, 0}, { 12, 2, 0}, { 32, 1, 0},
+ { 31, 1, 0}, { 30, 1, 0}, { 29, 1, 0}, { 28, 1, 0}
+};
+
+static const DCTtab DCT_15 [] ICONST_ATTR = {
+ { 1,40,15}, { 1,39,15}, { 1,38,15}, { 1,37,15},
+ { 1,36,15}, { 1,35,15}, { 1,34,15}, { 1,33,15},
+ { 1,32,15}, { 2,14,15}, { 2,13,15}, { 2,12,15},
+ { 2,11,15}, { 2,10,15}, { 2, 9,15}, { 2, 8,15},
+ { 1,31,14}, { 1,31,14}, { 1,30,14}, { 1,30,14},
+ { 1,29,14}, { 1,29,14}, { 1,28,14}, { 1,28,14},
+ { 1,27,14}, { 1,27,14}, { 1,26,14}, { 1,26,14},
+ { 1,25,14}, { 1,25,14}, { 1,24,14}, { 1,24,14},
+ { 1,23,14}, { 1,23,14}, { 1,22,14}, { 1,22,14},
+ { 1,21,14}, { 1,21,14}, { 1,20,14}, { 1,20,14},
+ { 1,19,14}, { 1,19,14}, { 1,18,14}, { 1,18,14},
+ { 1,17,14}, { 1,17,14}, { 1,16,14}, { 1,16,14}
+};
+
+static const DCTtab DCT_13 [] ICONST_ATTR = {
+ { 11, 2,13}, { 10, 2,13}, { 6, 3,13}, { 4, 4,13},
+ { 3, 5,13}, { 2, 7,13}, { 2, 6,13}, { 1,15,13},
+ { 1,14,13}, { 1,13,13}, { 1,12,13}, { 27, 1,13},
+ { 26, 1,13}, { 25, 1,13}, { 24, 1,13}, { 23, 1,13},
+ { 1,11,12}, { 1,11,12}, { 9, 2,12}, { 9, 2,12},
+ { 5, 3,12}, { 5, 3,12}, { 1,10,12}, { 1,10,12},
+ { 3, 4,12}, { 3, 4,12}, { 8, 2,12}, { 8, 2,12},
+ { 22, 1,12}, { 22, 1,12}, { 21, 1,12}, { 21, 1,12},
+ { 1, 9,12}, { 1, 9,12}, { 20, 1,12}, { 20, 1,12},
+ { 19, 1,12}, { 19, 1,12}, { 2, 5,12}, { 2, 5,12},
+ { 4, 3,12}, { 4, 3,12}, { 1, 8,12}, { 1, 8,12},
+ { 7, 2,12}, { 7, 2,12}, { 18, 1,12}, { 18, 1,12}
+};
+
+static const DCTtab DCT_B14_10 [] ICONST_ATTR = {
+ { 17, 1,10}, { 6, 2,10}, { 1, 7,10}, { 3, 3,10},
+ { 2, 4,10}, { 16, 1,10}, { 15, 1,10}, { 5, 2,10}
+};
+
+static const DCTtab DCT_B14_8 [] ICONST_ATTR = {
+ { 65, 0, 12}, { 65, 0, 12}, { 65, 0, 12}, { 65, 0, 12},
+ { 3, 2, 7}, { 3, 2, 7}, { 10, 1, 7}, { 10, 1, 7},
+ { 1, 4, 7}, { 1, 4, 7}, { 9, 1, 7}, { 9, 1, 7},
+ { 8, 1, 6}, { 8, 1, 6}, { 8, 1, 6}, { 8, 1, 6},
+ { 7, 1, 6}, { 7, 1, 6}, { 7, 1, 6}, { 7, 1, 6},
+ { 2, 2, 6}, { 2, 2, 6}, { 2, 2, 6}, { 2, 2, 6},
+ { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6},
+ { 14, 1, 8}, { 1, 6, 8}, { 13, 1, 8}, { 12, 1, 8},
+ { 4, 2, 8}, { 2, 3, 8}, { 1, 5, 8}, { 11, 1, 8}
+};
+
+static const DCTtab DCT_B14AC_5 [] ICONST_ATTR = {
+ { 1, 3, 5}, { 5, 1, 5}, { 4, 1, 5},
+ { 1, 2, 4}, { 1, 2, 4}, { 3, 1, 4}, { 3, 1, 4},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ {129, 0, 2}, {129, 0, 2}, {129, 0, 2}, {129, 0, 2},
+ {129, 0, 2}, {129, 0, 2}, {129, 0, 2}, {129, 0, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}
+};
+
+static const DCTtab DCT_B14DC_5 [] ICONST_ATTR = {
+ { 1, 3, 5}, { 5, 1, 5}, { 4, 1, 5},
+ { 1, 2, 4}, { 1, 2, 4}, { 3, 1, 4}, { 3, 1, 4},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1},
+ { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1},
+ { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1},
+ { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}
+};
+
+static const DCTtab DCT_B15_10 [] ICONST_ATTR = {
+ { 6, 2, 9}, { 6, 2, 9}, { 15, 1, 9}, { 15, 1, 9},
+ { 3, 4,10}, { 17, 1,10}, { 16, 1, 9}, { 16, 1, 9}
+};
+
+static const DCTtab DCT_B15_8 [] ICONST_ATTR = {
+ { 65, 0, 12}, { 65, 0, 12}, { 65, 0, 12}, { 65, 0, 12},
+ { 8, 1, 7}, { 8, 1, 7}, { 9, 1, 7}, { 9, 1, 7},
+ { 7, 1, 7}, { 7, 1, 7}, { 3, 2, 7}, { 3, 2, 7},
+ { 1, 7, 6}, { 1, 7, 6}, { 1, 7, 6}, { 1, 7, 6},
+ { 1, 6, 6}, { 1, 6, 6}, { 1, 6, 6}, { 1, 6, 6},
+ { 5, 1, 6}, { 5, 1, 6}, { 5, 1, 6}, { 5, 1, 6},
+ { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6},
+ { 2, 5, 8}, { 12, 1, 8}, { 1,11, 8}, { 1,10, 8},
+ { 14, 1, 8}, { 13, 1, 8}, { 4, 2, 8}, { 2, 4, 8},
+ { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5},
+ { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5},
+ { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5},
+ { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5},
+ { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5},
+ { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
+ {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4},
+ {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4},
+ {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4},
+ {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4},
+ { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4},
+ { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4},
+ { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4},
+ { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
+ { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
+ { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
+ { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
+ { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
+ { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
+ { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
+ { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
+ { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
+ { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5},
+ { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5},
+ { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5},
+ { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5},
+ { 10, 1, 7}, { 10, 1, 7}, { 2, 3, 7}, { 2, 3, 7},
+ { 11, 1, 7}, { 11, 1, 7}, { 1, 8, 7}, { 1, 8, 7},
+ { 1, 9, 7}, { 1, 9, 7}, { 1,12, 8}, { 1,13, 8},
+ { 3, 3, 8}, { 5, 2, 8}, { 1,14, 8}, { 1,15, 8}
+};
+
+
+static const MBAtab MBA_5 [] ICONST_ATTR = {
+ {6, 5}, {5, 5}, {4, 4}, {4, 4}, {3, 4}, {3, 4},
+ {2, 3}, {2, 3}, {2, 3}, {2, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}
+};
+
+static const MBAtab MBA_11 [] ICONST_ATTR = {
+ {32, 11}, {31, 11}, {30, 11}, {29, 11},
+ {28, 11}, {27, 11}, {26, 11}, {25, 11},
+ {24, 11}, {23, 11}, {22, 11}, {21, 11},
+ {20, 10}, {20, 10}, {19, 10}, {19, 10},
+ {18, 10}, {18, 10}, {17, 10}, {17, 10},
+ {16, 10}, {16, 10}, {15, 10}, {15, 10},
+ {14, 8}, {14, 8}, {14, 8}, {14, 8},
+ {14, 8}, {14, 8}, {14, 8}, {14, 8},
+ {13, 8}, {13, 8}, {13, 8}, {13, 8},
+ {13, 8}, {13, 8}, {13, 8}, {13, 8},
+ {12, 8}, {12, 8}, {12, 8}, {12, 8},
+ {12, 8}, {12, 8}, {12, 8}, {12, 8},
+ {11, 8}, {11, 8}, {11, 8}, {11, 8},
+ {11, 8}, {11, 8}, {11, 8}, {11, 8},
+ {10, 8}, {10, 8}, {10, 8}, {10, 8},
+ {10, 8}, {10, 8}, {10, 8}, {10, 8},
+ { 9, 8}, { 9, 8}, { 9, 8}, { 9, 8},
+ { 9, 8}, { 9, 8}, { 9, 8}, { 9, 8},
+ { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7},
+ { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7},
+ { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7},
+ { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7},
+ { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7},
+ { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7},
+ { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7},
+ { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7}
+};
diff --git a/apps/plugins/mpegplayer/mpeg_alloc.h b/apps/plugins/mpegplayer/mpeg_alloc.h
new file mode 100644
index 0000000000..9acfbc5dec
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_alloc.h
@@ -0,0 +1,32 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 by Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef MPEG_ALLOC_H
+#define MPEG_ALLOC_H
+
+/* returns the remaining mpeg2 buffer and it's size */
+void * mpeg2_get_buf(size_t *size);
+void *mpeg_malloc(size_t size, mpeg2_alloc_t reason);
+/* Grabs all the buffer available sans margin */
+void *mpeg_malloc_all(size_t *size_out, mpeg2_alloc_t reason);
+/* Initializes the malloc buffer with the given base buffer */
+bool mpeg_alloc_init(unsigned char *buf, size_t mallocsize);
+
+#endif /* MPEG_ALLOC_H */
diff --git a/apps/plugins/mpegplayer/mpeg_misc.c b/apps/plugins/mpegplayer/mpeg_misc.c
new file mode 100644
index 0000000000..31f0644212
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_misc.c
@@ -0,0 +1,227 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Miscellaneous helper API definitions
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "mpegplayer.h"
+
+/** Streams **/
+
+/* Initializes the cursor */
+void stream_scan_init(struct stream_scan *sk)
+{
+ dbuf_l2_init(&sk->l2);
+}
+
+/* Ensures direction is -1 or 1 and margin is properly initialized */
+void stream_scan_normalize(struct stream_scan *sk)
+{
+ if (sk->dir >= 0)
+ {
+ sk->dir = SSCAN_FORWARD;
+ sk->margin = sk->len;
+ }
+ else if (sk->dir < 0)
+ {
+ sk->dir = SSCAN_REVERSE;
+ sk->margin = 0;
+ }
+}
+
+/* Moves a scan cursor. If amount is positive, the increment is in the scan
+ * direction, otherwise opposite the scan direction */
+void stream_scan_offset(struct stream_scan *sk, off_t by)
+{
+ off_t bydir = by*sk->dir;
+ sk->pos += bydir;
+ sk->margin -= bydir;
+ sk->len -= by;
+}
+
+/** Time helpers **/
+void ts_to_hms(uint32_t pts, struct hms *hms)
+{
+ hms->frac = pts % TS_SECOND;
+ hms->sec = pts / TS_SECOND;
+ hms->min = hms->sec / 60;
+ hms->hrs = hms->min / 60;
+ hms->sec %= 60;
+ hms->min %= 60;
+}
+
+void hms_format(char *buf, size_t bufsize, struct hms *hms)
+{
+ /* Only display hours if nonzero */
+ if (hms->hrs != 0)
+ {
+ rb->snprintf(buf, bufsize, "%u:%02u:%02u",
+ hms->hrs, hms->min, hms->sec);
+ }
+ else
+ {
+ rb->snprintf(buf, bufsize, "%u:%02u",
+ hms->min, hms->sec);
+ }
+}
+
+/** Maths **/
+uint32_t muldiv_uint32(uint32_t multiplicand,
+ uint32_t multiplier,
+ uint32_t divisor)
+{
+ if (divisor != 0)
+ {
+ uint64_t prod = (uint64_t)multiplier*multiplicand + divisor/2;
+
+ if ((uint32_t)(prod >> 32) < divisor)
+ return (uint32_t)(prod / divisor);
+ }
+ else if (multiplicand == 0 || multiplier == 0)
+ {
+ return 0; /* 0/0 = 0 : yaya */
+ }
+ /* else (> 0) / 0 = UINT32_MAX */
+
+ return UINT32_MAX; /* Saturate */
+}
+
+
+/** Lists **/
+
+/* Does the list have any members? */
+bool list_is_empty(void **list)
+{
+ return *list == NULL;
+}
+
+/* Is the item inserted into a particular list? */
+bool list_is_member(void **list, void *item)
+{
+ return *rb->find_array_ptr(list, item) != NULL;
+}
+
+/* Removes an item from a list - returns true if item was found
+ * and thus removed. */
+bool list_remove_item(void **list, void *item)
+{
+ return rb->remove_array_ptr(list, item) != -1;
+}
+
+/* Adds a list item, insert last, if not already present. */
+void list_add_item(void **list, void *item)
+{
+ void **item_p = rb->find_array_ptr(list, item);
+ if (*item_p == NULL)
+ *item_p = item;
+}
+
+/* Clears the entire list. */
+void list_clear_all(void **list)
+{
+ while (*list != NULL)
+ *list++ = NULL;
+}
+
+/* Enumerate all items in the array, passing each item in turn to the
+ * callback as well as the data value. The current item may be safely
+ * removed. Other changes during enumeration are undefined. The callback
+ * may return 'false' to stop the enumeration early. */
+void list_enum_items(void **list,
+ list_enum_callback_t callback,
+ void* data)
+{
+ for (;;)
+ {
+ void *item = *list;
+
+ if (item == NULL)
+ break;
+
+ if (callback != NULL && !callback(item, data))
+ break;
+
+ if (*list == item)
+ list++; /* Item still there */
+ }
+}
+
+
+/** System events **/
+static long mpeg_sysevent_id;
+
+void mpeg_sysevent_clear(void)
+{
+ mpeg_sysevent_id = 0;
+}
+
+void mpeg_sysevent_set(void)
+{
+ /* Nonzero and won't invoke anything in default event handler */
+ mpeg_sysevent_id = ACTION_STD_CANCEL;
+}
+
+long mpeg_sysevent(void)
+{
+ return mpeg_sysevent_id;
+}
+
+int mpeg_sysevent_callback(int btn,
+ const struct menu_item_ex *menu,
+ struct gui_synclist *this_list)
+{
+ (void) this_list;
+ switch (btn)
+ {
+ case SYS_USB_CONNECTED:
+ case SYS_POWEROFF:
+ case SYS_REBOOT:
+ mpeg_sysevent_id = btn;
+ return ACTION_STD_CANCEL;
+ }
+
+ return btn;
+ (void)menu;
+}
+
+void mpeg_sysevent_handle(void)
+{
+ long id = mpeg_sysevent();
+ if (id != 0)
+ rb->default_event_handler(id);
+}
+
+
+/** Buttons **/
+
+int mpeg_button_get(int timeout)
+{
+ int button;
+
+ mpeg_sysevent_clear();
+ button = timeout == TIMEOUT_BLOCK ? rb->button_get(true) :
+ rb->button_get_w_tmo(timeout);
+
+ /* Produce keyclick */
+ rb->keyclick_click(true, button);
+
+ return mpeg_sysevent_callback(button, NULL, NULL);
+}
+
diff --git a/apps/plugins/mpegplayer/mpeg_misc.h b/apps/plugins/mpegplayer/mpeg_misc.h
new file mode 100644
index 0000000000..e04db0e19d
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_misc.h
@@ -0,0 +1,258 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Miscellaneous helper API declarations
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef MPEG_MISC_H
+#define MPEG_MISC_H
+
+/* Miscellaneous helpers */
+#ifndef ALIGNED_ATTR
+#define ALIGNED_ATTR(x) __attribute__((aligned(x)))
+#endif
+
+#include "disk_buf.h"
+
+/* Generic states for when things are too simple to care about naming them */
+enum state_enum
+{
+ STATE0 = 0,
+ STATE1,
+ STATE2,
+ STATE3,
+ STATE4,
+ STATE5,
+ STATE6,
+ STATE7,
+ STATE8,
+ STATE9,
+};
+
+/* Macros for comparing memory bytes to a series of constant bytes in an
+ efficient manner - evaluate to true if corresponding bytes match */
+#if defined (CPU_ARM)
+/* ARM must load 32-bit values at addres % 4 == 0 offsets but this data
+ isn't aligned nescessarily, so just byte compare */
+#define CMP_3_CONST(_a, _b) \
+ ({ int _x; \
+ asm volatile ( \
+ "ldrb %[x], [%[a], #0] \n" \
+ "eors %[x], %[x], %[b0] \n" \
+ "ldreqb %[x], [%[a], #1] \n" \
+ "eoreqs %[x], %[x], %[b1] \n" \
+ "ldreqb %[x], [%[a], #2] \n" \
+ "eoreqs %[x], %[x], %[b2] \n" \
+ : [x]"=&r"(_x) \
+ : [a]"r"(_a), \
+ [b0]"i"(((_b) >> 24) & 0xff), \
+ [b1]"i"(((_b) >> 16) & 0xff), \
+ [b2]"i"(((_b) >> 8) & 0xff) \
+ ); \
+ _x == 0; })
+
+#define CMP_4_CONST(_a, _b) \
+ ({ int _x; \
+ asm volatile ( \
+ "ldrb %[x], [%[a], #0] \n" \
+ "eors %[x], %[x], %[b0] \n" \
+ "ldreqb %[x], [%[a], #1] \n" \
+ "eoreqs %[x], %[x], %[b1] \n" \
+ "ldreqb %[x], [%[a], #2] \n" \
+ "eoreqs %[x], %[x], %[b2] \n" \
+ "ldreqb %[x], [%[a], #3] \n" \
+ "eoreqs %[x], %[x], %[b3] \n" \
+ : [x]"=&r"(_x) \
+ : [a]"r"(_a), \
+ [b0]"i"(((_b) >> 24) & 0xff), \
+ [b1]"i"(((_b) >> 16) & 0xff), \
+ [b2]"i"(((_b) >> 8) & 0xff), \
+ [b3]"i"(((_b) ) & 0xff) \
+ ); \
+ _x == 0; })
+
+#elif defined (CPU_COLDFIRE)
+/* Coldfire can just load a 32 bit value at any offset but ASM is not the
+ best way to integrate this with the C code */
+#define CMP_3_CONST(a, b) \
+ (((*(uint32_t *)(a) >> 8) == ((uint32_t)(b) >> 8)))
+
+#define CMP_4_CONST(a, b) \
+ ((*(uint32_t *)(a) == (b)))
+
+#else
+/* Don't know what this is - use bytewise comparisons */
+#define CMP_3_CONST(a, b) \
+ (( ((a)[0] ^ (((b) >> 24) & 0xff)) | \
+ ((a)[1] ^ (((b) >> 16) & 0xff)) | \
+ ((a)[2] ^ (((b) >> 8) & 0xff)) ) == 0)
+
+#define CMP_4_CONST(a, b) \
+ (( ((a)[0] ^ (((b) >> 24) & 0xff)) | \
+ ((a)[1] ^ (((b) >> 16) & 0xff)) | \
+ ((a)[2] ^ (((b) >> 8) & 0xff)) | \
+ ((a)[3] ^ (((b) ) & 0xff)) ) == 0)
+#endif /* CPU_* */
+
+
+/** Streams **/
+
+/* Convert PTS/DTS ticks to our clock ticks */
+#define TS_TO_TICKS(pts) ((uint64_t)CLOCK_RATE*(pts) / TS_SECOND)
+/* Convert our clock ticks to PTS/DTS ticks */
+#define TICKS_TO_TS(ts) ((uint64_t)TS_SECOND*(ts) / CLOCK_RATE)
+/* Convert timecode ticks to our clock ticks */
+#define TC_TO_TICKS(stamp) ((uint64_t)CLOCK_RATE*(stamp) / TC_SECOND)
+/* Convert our clock ticks to timecode ticks */
+#define TICKS_TO_TC(stamp) ((uint64_t)TC_SECOND*(stamp) / CLOCK_RATE)
+/* Convert timecode ticks to timestamp ticks */
+#define TC_TO_TS(stamp) ((stamp) / 600)
+
+/*
+ * S = start position, E = end position
+ *
+ * pos:
+ * initialize to search start position (S)
+ *
+ * len:
+ * initialize to = ABS(S-E)
+ * scanning = remaining bytes in scan direction
+ *
+ * dir:
+ * scan direction; >= 0 == forward, < 0 == reverse
+ *
+ * margin:
+ * amount of data to right of cursor - initialize by stream_scan_normalize
+ *
+ * data:
+ * Extra data used/returned by the function implemented
+ *
+ * Forward scan:
+ * S pos E
+ * | *<-margin->| dir->
+ * | |<--len--->|
+ *
+ * Reverse scan:
+ * E pos S
+ * |<-len->*<-margin->| <-dir
+ * | | |
+ */
+struct stream_scan
+{
+ off_t pos; /* Initial scan position (file offset) */
+ ssize_t len; /* Maximum length of scan */
+ off_t dir; /* Direction - >= 0; forward, < 0 backward */
+ ssize_t margin; /* Used by function to track margin between position and data end */
+ intptr_t data; /* */
+ struct dbuf_l2_cache l2;
+};
+
+#define SSCAN_REVERSE (-1)
+#define SSCAN_FORWARD 1
+
+/* Initializes the cursor */
+void stream_scan_init(struct stream_scan *sk);
+
+/* Ensures direction is -1 or 1 and margin is properly initialized */
+void stream_scan_normalize(struct stream_scan *sk);
+
+/* Moves a scan cursor. If amount is positive, the increment is in the scan
+ * direction, otherwise opposite the scan direction */
+void stream_scan_offset(struct stream_scan *sk, off_t by);
+
+/** Time helpers **/
+struct hms
+{
+ unsigned int hrs;
+ unsigned int min;
+ unsigned int sec;
+ unsigned int frac;
+};
+
+void ts_to_hms(uint32_t ts, struct hms *hms);
+void hms_format(char *buf, size_t bufsize, struct hms *hms);
+
+/** Maths **/
+
+/* Moving average */
+#define AVERAGE(var, x, count) \
+ ({ typeof (count) _c = (count); \
+ ((var) * (_c-1) + (x)) / (_c); })
+
+/* Multiply two unsigned 32-bit integers yielding a 64-bit result and
+ * divide by another unsigned 32-bit integer to yield a 32-bit result.
+ * Rounds to nearest with saturation. */
+uint32_t muldiv_uint32(uint32_t multiplicand,
+ uint32_t multiplier,
+ uint32_t divisor);
+
+
+/** Lists **/
+
+/* Does the list have any members? */
+bool list_is_empty(void **list);
+
+/* Is the item inserted into a particular list? */
+bool list_is_member(void **list, void *item);
+
+/* Removes an item from a list - returns true if item was found
+ * and thus removed. */
+bool list_remove_item(void **list, void *item);
+
+/* Adds a list item, insert last, if not already present. */
+void list_add_item(void **list, void *item);
+
+/* Clears the entire list. */
+void list_clear_all(void **list);
+
+/* Enumerate all items in the array. */
+typedef bool (*list_enum_callback_t)(void *item, void* data);
+
+void list_enum_items(void **list,
+ list_enum_callback_t callback,
+ void *data);
+
+
+/** System events **/
+
+/* Clear event */
+void mpeg_sysevent_clear(void);
+
+/* Set to ACTION_STD_CANCEL */
+void mpeg_sysevent_set(void);
+
+/* Get event code */
+long mpeg_sysevent(void);
+
+/* Call with a system event code and used as menu callback */
+int mpeg_sysevent_callback(int btn, const struct menu_item_ex *menu,
+ struct gui_synclist *this_list);
+
+/* Handle recorded event */
+void mpeg_sysevent_handle(void);
+
+
+/** Buttons **/
+
+/* Get button codes while remembering important events for later
+ * processing; return of ACTION_STD_CANCEL means plugin should
+ * abort and handle the event */
+int mpeg_button_get(int timeout);
+
+#endif /* MPEG_MISC_H */
diff --git a/apps/plugins/mpegplayer/mpeg_parser.c b/apps/plugins/mpegplayer/mpeg_parser.c
new file mode 100644
index 0000000000..cc57b0c43c
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_parser.c
@@ -0,0 +1,1203 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Parser for MPEG streams
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "mpegplayer.h"
+
+struct stream_parser str_parser SHAREDBSS_ATTR;
+
+static void parser_init_state(void)
+{
+ str_parser.last_seek_time = 0;
+ str_parser.format = STREAM_FMT_UNKNOWN;
+ str_parser.start_pts = INVALID_TIMESTAMP;
+ str_parser.end_pts = INVALID_TIMESTAMP;
+ str_parser.flags = 0;
+ str_parser.dims.w = 0;
+ str_parser.dims.h = 0;
+}
+
+/* Place the stream in a state to begin parsing - sync will be performed
+ * first */
+void str_initialize(struct stream *str, off_t pos)
+{
+ /* Initial positions start here */
+ str->hdr.win_left = str->hdr.win_right = pos;
+ /* No packet */
+ str->curr_packet = NULL;
+ /* Pick up parsing from this point in the buffer */
+ str->curr_packet_end = disk_buf_offset2ptr(pos);
+ /* No flags */
+ str->pkt_flags = 0;
+ /* Sync first */
+ str->state = SSTATE_SYNC;
+}
+
+/* Place the stream in an end of data state */
+void str_end_of_stream(struct stream *str)
+{
+ /* Offsets that prevent this stream from being included in the
+ * min left/max right window so that no buffering is triggered on
+ * its behalf. Set right to the min first so a thread reading the
+ * overall window gets doesn't see this as valid no matter what the
+ * file length. */
+ str->hdr.win_right = OFF_T_MIN;
+ str->hdr.win_left = OFF_T_MAX;
+ /* No packets */
+ str->curr_packet = str->curr_packet_end = NULL;
+ /* No flags */
+ str->pkt_flags = 0;
+ /* Fin */
+ str->state = SSTATE_END;
+}
+
+/* Return a timestamp at address p+offset if the marker bits are in tact */
+static inline uint32_t read_pts(uint8_t *p, off_t offset)
+{
+ return TS_CHECK_MARKERS(p, offset) ?
+ TS_FROM_HEADER(p, offset) : INVALID_TIMESTAMP;
+}
+
+static inline bool validate_timestamp(uint32_t ts)
+{
+ return ts >= str_parser.start_pts && ts <= str_parser.end_pts;
+}
+
+/* Find a start code before or after a given position */
+uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code)
+{
+ stream_scan_normalize(sk);
+
+ if (sk->dir < 0)
+ {
+ /* Reverse scan - start with at least the min needed */
+ stream_scan_offset(sk, 4);
+ }
+
+ code &= 0xff; /* Only the low byte matters */
+
+ while (sk->len >= 0 && sk->margin >= 4)
+ {
+ uint8_t *p;
+ off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
+ ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p);
+
+ if (pos < 0 || len < 4)
+ break;
+
+ if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == code)
+ {
+ return p;
+ }
+
+ stream_scan_offset(sk, 1);
+ }
+
+ return NULL;
+}
+
+/* Find a PES packet header for any stream - return stream to which it
+ * belongs */
+unsigned mpeg_parser_scan_pes(struct stream_scan *sk)
+{
+ stream_scan_normalize(sk);
+
+ if (sk->dir < 0)
+ {
+ /* Reverse scan - start with at least the min needed */
+ stream_scan_offset(sk, 4);
+ }
+
+ while (sk->len >= 0 && sk->margin >= 4)
+ {
+ uint8_t *p;
+ off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
+ ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p);
+
+ if (pos < 0 || len < 4)
+ break;
+
+ if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
+ {
+ unsigned id = p[3];
+ if (id >= 0xb9)
+ return id; /* PES header */
+ /* else some video stream element */
+ }
+
+ stream_scan_offset(sk, 1);
+ }
+
+ return -1;
+}
+
+/* Return the first SCR found from the scan direction */
+uint32_t mpeg_parser_scan_scr(struct stream_scan *sk)
+{
+ uint8_t *p = mpeg_parser_scan_start_code(sk, MPEG_STREAM_PACK_HEADER);
+
+ if (p != NULL && sk->margin >= 9) /* 9 bytes total required */
+ {
+ sk->data = 9;
+
+ if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */
+ {
+ /* Lookhead p+8 */
+ if (MPEG2_CHECK_PACK_SCR_MARKERS(p, 4))
+ return MPEG2_PACK_HEADER_SCR(p, 4);
+ }
+ else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */
+ {
+ /* Lookahead p+8 */
+ if (TS_CHECK_MARKERS(p, 4))
+ return TS_FROM_HEADER(p, 4);
+ }
+ /* Weird pack header */
+ sk->data = 5;
+ }
+
+ return INVALID_TIMESTAMP;
+}
+
+uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id)
+{
+ stream_scan_normalize(sk);
+
+ if (sk->dir < 0)
+ {
+ /* Reverse scan - start with at least the min needed */
+ stream_scan_offset(sk, 4);
+ }
+
+ while (sk->len >= 0 && sk->margin >= 4)
+ {
+ uint8_t *p;
+ off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
+ ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 30, &p);
+
+ if (pos < 0 || len < 4)
+ break;
+
+ if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == id)
+ {
+ uint8_t *h = p;
+
+ if (sk->margin < 7)
+ {
+ /* Insufficient data */
+ }
+ else if ((h[6] & 0xc0) == 0x80) /* mpeg2 */
+ {
+ if (sk->margin >= 14 && (h[7] & 0x80) != 0x00)
+ {
+ sk->data = 14;
+ return read_pts(h, 9);
+ }
+ }
+ else /* mpeg1 */
+ {
+ ssize_t l = 6;
+ ssize_t margin = sk->margin;
+
+ /* Skip stuffing_byte */
+ while (margin > 7 && h[l] == 0xff && ++l <= 22)
+ --margin;
+
+ if (margin >= 7)
+ {
+ if ((h[l] & 0xc0) == 0x40)
+ {
+ /* Skip STD_buffer_scale and STD_buffer_size */
+ margin -= 2;
+ l += 2;
+ }
+
+ if (margin >= 5)
+ {
+ /* Header points to the mpeg1 pes header */
+ h += l;
+
+ if ((h[0] & 0xe0) == 0x20)
+ {
+ /* PTS or PTS_DTS indicated */
+ sk->data = (h + 5) - p;
+ return read_pts(h, 0);
+ }
+ }
+ }
+ }
+ /* No PTS present - keep searching for a matching PES header with
+ * one */
+ }
+
+ stream_scan_offset(sk, 1);
+ }
+
+ return INVALID_TIMESTAMP;
+}
+
+static bool init_video_info(void)
+{
+ DEBUGF("Getting movie size\n");
+
+ /* The decoder handles this in order to initialize its knowledge of the
+ * movie parameters making seeking easier */
+ str_send_msg(&video_str, STREAM_RESET, 0);
+ if (str_send_msg(&video_str, VIDEO_GET_SIZE,
+ (intptr_t)&str_parser.dims) != 0)
+ {
+ return true;
+ }
+
+ DEBUGF(" failed\n");
+ return false;
+}
+
+static bool init_times(struct stream *str)
+{
+ struct stream tmp_str;
+ const ssize_t filesize = disk_buf_filesize();
+ const ssize_t max_probe = MIN(512*1024, filesize);
+ bool found_stream;
+
+ /* Simply find the first earliest timestamp - this will be the one
+ * used when streaming anyway */
+ DEBUGF("Finding start_pts: 0x%02x\n", str->id);
+
+ found_stream = false;
+ str->start_pts = INVALID_TIMESTAMP;
+ str->end_pts = INVALID_TIMESTAMP;
+
+ tmp_str.id = str->id;
+ tmp_str.hdr.pos = 0;
+ tmp_str.hdr.limit = max_probe;
+
+ /* Probe for many for the start because some stamps could be anomalous.
+ * Video also can also have things out of order. Just see what it's got.
+ */
+ while (1)
+ {
+ switch (parser_get_next_data(&tmp_str, STREAM_PM_RANDOM_ACCESS))
+ {
+ case STREAM_DATA_END:
+ break;
+ case STREAM_OK:
+ found_stream = true;
+ if (tmp_str.pkt_flags & PKT_HAS_TS)
+ {
+ if (tmp_str.pts < str->start_pts)
+ str->start_pts = tmp_str.pts;
+ }
+ continue;
+ }
+
+ break;
+ }
+
+ if (!found_stream)
+ {
+ DEBUGF(" stream not found:0x%02x\n", str->id);
+ return false;
+ }
+
+ DEBUGF(" start:%u\n", (unsigned)str->start_pts);
+
+ /* Use the decoder thread to perform a synchronized search - no
+ * decoding should take place but just a simple run through timestamps
+ * and durations as the decoder would see them. This should give the
+ * precise time at the end of the last frame for the stream. */
+ DEBUGF("Finding end_pts: 0x%02x\n", str->id);
+
+ str_parser.parms.sd.time = MAX_TIMESTAMP;
+ str_parser.parms.sd.sk.pos = filesize - max_probe;
+ str_parser.parms.sd.sk.len = max_probe;
+ str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
+
+ str_send_msg(str, STREAM_RESET, 0);
+
+ if (str_send_msg(str, STREAM_FIND_END_TIME,
+ (intptr_t)&str_parser.parms.sd) == STREAM_PERFECT_MATCH)
+ {
+ str->end_pts = str_parser.parms.sd.time;
+ DEBUGF(" end:%u\n", (unsigned)str->end_pts);
+ }
+
+ return true;
+}
+
+static bool check_times(const struct stream *str)
+{
+ return str->start_pts < str->end_pts &&
+ str->end_pts != INVALID_TIMESTAMP;
+}
+
+/* Return the best-fit file offset of a timestamp in the PES where
+ * timstamp <= time < next timestamp. Will try to return something reasonably
+ * valid if best-fit could not be made. */
+static off_t mpeg_parser_seek_PTS(uint32_t time, unsigned id)
+{
+ ssize_t pos_left = 0;
+ ssize_t pos_right = disk_buf.filesize;
+ ssize_t pos, pos_new;
+ uint32_t time_left = str_parser.start_pts;
+ uint32_t time_right = str_parser.end_pts;
+ uint32_t pts = 0;
+ uint32_t prevpts = 0;
+ enum state_enum state = STATE0;
+ struct stream_scan sk;
+
+ stream_scan_init(&sk);
+
+ /* Initial estimate taken from average bitrate - later interpolations are
+ * taken similarly based on the remaining file interval */
+ pos_new = muldiv_uint32(time - time_left, pos_right - pos_left,
+ time_right - time_left) + pos_left;
+
+ /* return this estimated position if nothing better comes up */
+ pos = pos_new;
+
+ DEBUGF("Seeking stream 0x%02x\n", id);
+ DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n",
+ (unsigned)time_left, (unsigned)time, (unsigned)time_right,
+ (long)pos_left, (long)pos_new, (long)pos_right);
+
+ sk.dir = SSCAN_REVERSE;
+
+ while (state < STATE9)
+ {
+ uint32_t currpts;
+ sk.pos = pos_new;
+ sk.len = (sk.dir < 0) ? pos_new - pos_left : pos_right - pos_new;
+
+ currpts = mpeg_parser_scan_pts(&sk, id);
+
+ if (currpts != INVALID_TIMESTAMP)
+ {
+ ssize_t pos_adj; /* Adjustment to over or under-estimate */
+
+ /* Found a valid timestamp - see were it lies in relation to
+ * target */
+ if (currpts < time)
+ {
+ /* Time at current position is before seek time - move
+ * forward */
+ if (currpts > pts)
+ {
+ /* This is less than the desired time but greater than
+ * the currently seeked one; move the position up */
+ pts = currpts;
+ pos = sk.pos;
+ }
+
+ /* No next timestamp can be sooner */
+ pos_left = sk.pos + sk.data;
+ time_left = currpts;
+
+ if (pos_right <= pos_left)
+ break; /* If the window disappeared - we're done */
+
+ pos_new = muldiv_uint32(time - time_left,
+ pos_right - pos_left,
+ time_right - time_left);
+ /* Point is ahead of us - fudge estimate a bit high */
+ pos_adj = pos_new / 10;
+
+ if (pos_adj > 512*1024)
+ pos_adj = 512*1024;
+
+ pos_new += pos_left + pos_adj;
+
+ if (pos_new >= pos_right)
+ {
+ /* Estimate could push too far */
+ pos_new = pos_right;
+ }
+
+ state = STATE2; /* Last scan was early */
+ sk.dir = SSCAN_REVERSE;
+
+ DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
+ (unsigned)time_left, (unsigned)time, (unsigned)currpts,
+ (unsigned)time_right, (long)pos_left, (long)pos_new,
+ (long)pos_right);
+ }
+ else if (currpts > time)
+ {
+ /* Time at current position is past seek time - move
+ backward */
+ pos_right = sk.pos;
+ time_right = currpts;
+
+ if (pos_right <= pos_left)
+ break; /* If the window disappeared - we're done */
+
+ pos_new = muldiv_uint32(time - time_left,
+ pos_right - pos_left,
+ time_right - time_left);
+ /* Overshot the seek point - fudge estimate a bit low */
+ pos_adj = pos_new / 10;
+
+ if (pos_adj > 512*1024)
+ pos_adj = 512*1024;
+
+ pos_new += pos_left - pos_adj;
+
+ state = STATE3; /* Last scan was late */
+ sk.dir = SSCAN_REVERSE;
+
+ DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
+ (unsigned)time_left, (unsigned)time, (unsigned)currpts,
+ (unsigned)time_right, (long)pos_left, (long)pos_new,
+ (long)pos_right);
+ }
+ else
+ {
+ /* Exact match - it happens */
+ DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
+ (unsigned)time_left, (unsigned)time, (unsigned)currpts,
+ (unsigned)time_right, (long)pos_left, (long)pos_new,
+ (long)pos_right);
+ pts = currpts;
+ pos = sk.pos;
+ state = STATE9;
+ }
+ }
+ else
+ {
+ /* Nothing found */
+
+ switch (state)
+ {
+ case STATE1:
+ /* We already tried the bruteforce scan and failed again - no
+ * more stamps could possibly exist in the interval */
+ DEBUGF("!! no timestamp 2x\n");
+ break;
+ case STATE0:
+ /* Hardly likely except at very beginning - just do L->R scan
+ * to find something */
+ DEBUGF("!! no timestamp on first probe: %ld\n", sk.pos);
+ case STATE2:
+ case STATE3:
+ /* Could just be missing timestamps because the interval is
+ * narrowing down. A large block of data from another stream
+ * may also be in the midst of our chosen points which could
+ * cluster at either extreme end. If anything is there, this
+ * will find it. */
+ pos_new = pos_left;
+ sk.dir = SSCAN_FORWARD;
+ DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
+ (unsigned)time_left, (unsigned)time, (unsigned)currpts,
+ (unsigned)time_right, (long)pos_left, (long)pos_new,
+ (long)pos_right);
+ state = STATE1;
+ break;
+ default:
+ DEBUGF("?? Invalid state: %d\n", state);
+ }
+ }
+
+ /* Same timestamp twice = quit */
+ if (currpts == prevpts)
+ {
+ DEBUGF("!! currpts == prevpts (stop)\n");
+ state = STATE9;
+ }
+
+ prevpts = currpts;
+ }
+
+#if defined(DEBUG) || defined(SIMULATOR)
+ /* The next pts after the seeked-to position should be greater -
+ * most of the time - frames out of presentation order may muck it
+ * up a slight bit */
+ sk.pos = pos + 1;
+ sk.len = disk_buf.filesize;
+ sk.dir = SSCAN_FORWARD;
+
+ uint32_t nextpts = mpeg_parser_scan_pts(&sk, id);
+ DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n",
+ (long)pos, (unsigned)pts, (unsigned)time, (unsigned)nextpts);
+
+ if (pts <= time && time < nextpts)
+ {
+ /* Smile - it worked */
+ DEBUGF(" :) pts<=time<next pts\n");
+ }
+ else
+ {
+ /* See where things ended up */
+ if (pts > time)
+ {
+ /* Hmm */
+ DEBUGF(" :\\ pts>time\n");
+ }
+ if (pts >= nextpts)
+ {
+ /* Weird - probably because of encoded order & tends to be right
+ * anyway if other criteria are met */
+ DEBUGF(" :p pts>=next pts\n");
+ }
+ if (time >= nextpts)
+ {
+ /* Ugh */
+ DEBUGF(" :( time>=nextpts\n");
+ }
+ }
+#endif
+
+ return pos;
+}
+
+static void prepare_audio(uint32_t time)
+{
+ off_t pos;
+
+ if (!str_send_msg(&audio_str, STREAM_NEEDS_SYNC, time))
+ {
+ DEBUGF("Audio was ready\n");
+ return;
+ }
+
+ pos = mpeg_parser_seek_PTS(time, audio_str.id);
+ str_send_msg(&audio_str, STREAM_RESET, 0);
+
+ str_parser.parms.sd.time = time;
+ str_parser.parms.sd.sk.pos = pos;
+ str_parser.parms.sd.sk.len = 1024*1024;
+ str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
+
+ str_send_msg(&audio_str, STREAM_SYNC, (intptr_t)&str_parser.parms.sd);
+}
+
+/* This function demuxes the streams and gives the next stream data
+ * pointer.
+ *
+ * STREAM_PM_STREAMING is for operation during playback. If the nescessary
+ * data and worst-case lookahead margin is not available, the stream is
+ * registered for notification when the data becomes available. If parsing
+ * extends beyond the end of the file or the end of stream marker is reached,
+ * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
+ *
+ * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
+ * If the file cache misses for the current position + lookahead, it will be
+ * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
+ * returned.
+ *
+ * The results from one mode may be used as input to the other. Random access
+ * requires cooperation amongst threads to avoid evicting another stream's
+ * data.
+ */
+static int parse_demux(struct stream *str, enum stream_parse_mode type)
+{
+ #define INC_BUF(offset) \
+ ({ off_t _o = (offset); \
+ str->hdr.win_right += _o; \
+ if ((p += _o) >= disk_buf.end) \
+ p -= disk_buf.size; })
+
+ static const int mpeg1_skip_table[16] =
+ { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ uint8_t *p = str->curr_packet_end;
+
+ str->pkt_flags = 0;
+
+ while (1)
+ {
+ uint8_t *header;
+ unsigned id;
+ ssize_t length, bytes;
+
+ switch (type)
+ {
+ case STREAM_PM_STREAMING:
+ /* Has the end been reached already? */
+ switch (str->state)
+ {
+ case SSTATE_PARSE: /* Expected case first if no jumptable */
+ /* Are we at the end of file? */
+ if (str->hdr.win_left < disk_buf.filesize)
+ break;
+ str_end_of_stream(str);
+ return STREAM_DATA_END;
+
+ case SSTATE_SYNC:
+ /* Is sync at the end of file? */
+ if (str->hdr.win_right < disk_buf.filesize)
+ break;
+ str_end_of_stream(str);
+ /* Fall-through */
+ case SSTATE_END:
+ return STREAM_DATA_END;
+ }
+
+ if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD))
+ {
+ /* This data range is not buffered yet - register stream to
+ * be notified when it becomes available. Stream is obliged
+ * to enter a TSTATE_DATA state if it must wait. */
+ int res = str_next_data_not_ready(str);
+
+ if (res != STREAM_OK)
+ return res;
+ }
+ break;
+ /* STREAM_PM_STREAMING: */
+
+ case STREAM_PM_RANDOM_ACCESS:
+ str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET);
+
+ if (str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit ||
+ disk_buf_getbuffer(MIN_BUFAHEAD, &p, NULL, NULL) <= 0)
+ {
+ str_end_of_stream(str);
+ return STREAM_DATA_END;
+ }
+
+ str->state = SSTATE_SYNC;
+ str->hdr.win_left = str->hdr.pos;
+ str->curr_packet = NULL;
+ str->curr_packet_end = p;
+ break;
+ /* STREAM_PM_RANDOM_ACCESS: */
+ }
+
+ if (str->state == SSTATE_SYNC)
+ {
+ /* Scanning for start code */
+ if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
+ {
+ INC_BUF(1);
+ continue;
+ }
+ }
+
+ /* Found a start code - enter parse state */
+ str->state = SSTATE_PARSE;
+
+ /* Pack header, skip it */
+ if (CMP_4_CONST(p, PACK_START_CODE))
+ {
+ /* Max lookahead: 14 */
+ if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */
+ {
+ /* Max delta: 14 + 7 = 21 */
+ /* Skip pack header and any stuffing bytes*/
+ bytes = 14 + (p[13] & 7);
+ }
+ else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */
+ {
+ bytes = 12;
+ }
+ else /* unknown - skip it */
+ {
+ DEBUGF("weird pack header!\n");
+ bytes = 5;
+ }
+
+ INC_BUF(bytes);
+ }
+
+ /* System header, parse and skip it - 6 bytes + size */
+ if (CMP_4_CONST(p, SYSTEM_HEADER_START_CODE))
+ {
+ /* Skip start code */
+ /* Max Delta = 65535 + 6 = 65541 */
+ bytes = 6 + ((p[4] << 8) | p[5]);
+ INC_BUF(bytes);
+ }
+
+ /* Packet header, parse it */
+ if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
+ {
+ /* Problem? Meh...probably not but just a corrupted section.
+ * Try to resync the parser which will probably succeed. */
+ DEBUGF("packet start code prefix not found: 0x%02x\n"
+ " wl:%lu wr:%lu\n"
+ " p:%p cp:%p cpe:%p\n"
+ " dbs:%p dbe:%p dbt:%p\n",
+ str->id, str->hdr.win_left, str->hdr.win_right,
+ p, str->curr_packet, str->curr_packet_end,
+ disk_buf.start, disk_buf.end, disk_buf.tail);
+ str->state = SSTATE_SYNC;
+ INC_BUF(1); /* Next byte - this one's no good */
+ continue;
+ }
+
+ /* We retrieve basic infos */
+ /* Maximum packet length: 6 + 65535 = 65541 */
+ id = p[3];
+ length = ((p[4] << 8) | p[5]) + 6;
+
+ if (id != str->id)
+ {
+ switch (id)
+ {
+ case MPEG_STREAM_PROGRAM_END:
+ /* end of stream */
+ str_end_of_stream(str);
+ DEBUGF("MPEG program end: 0x%02x\n", str->id);
+ return STREAM_DATA_END;
+ case MPEG_STREAM_PACK_HEADER:
+ case MPEG_STREAM_SYSTEM_HEADER:
+ /* These shouldn't be here - no increment or resync
+ * since we'll pick it up above. */
+ continue;
+ default:
+ /* It's not the packet we're looking for, skip it */
+ INC_BUF(length);
+ continue;
+ }
+ }
+
+ /* Ok, it's our packet */
+ header = p;
+
+ if ((header[6] & 0xc0) == 0x80) /* mpeg2 */
+ {
+ /* Max Lookahead: 18 */
+ /* Min length: 9 */
+ /* Max length: 9 + 255 = 264 */
+ length = 9 + header[8];
+
+ /* header points to the mpeg2 pes header */
+ if ((header[7] & 0x80) != 0)
+ {
+ /* header has a pts */
+ uint32_t pts = read_pts(header, 9);
+
+ if (pts != INVALID_TIMESTAMP)
+ {
+ str->pts = pts;
+#if 0
+ /* DTS isn't used for anything since things just get
+ decoded ASAP but keep the code around */
+ if (STREAM_IS_VIDEO(id))
+ {
+ /* Video stream - header may have a dts as well */
+ str->dts = pts;
+
+ if (header[7] & 0x40) != 0x00)
+ {
+ pts = read_pts(header, 14);
+ if (pts != INVALID_TIMESTAMP)
+ str->dts = pts;
+ }
+ }
+#endif
+ str->pkt_flags |= PKT_HAS_TS;
+ }
+ }
+ }
+ else /* mpeg1 */
+ {
+ /* Max lookahead: 24 + 2 + 9 = 35 */
+ /* Max len_skip: 24 + 2 = 26 */
+ /* Min length: 7 */
+ /* Max length: 24 + 2 + 9 = 35 */
+ off_t len_skip;
+ uint8_t * ptsbuf;
+
+ length = 7;
+
+ while (header[length - 1] == 0xff)
+ {
+ if (++length > 23)
+ {
+ DEBUGF("Too much stuffing" );
+ break;
+ }
+ }
+
+ if ((header[length - 1] & 0xc0) == 0x40)
+ length += 2;
+
+ len_skip = length;
+ length += mpeg1_skip_table[header[length - 1] >> 4];
+
+ /* Header points to the mpeg1 pes header */
+ ptsbuf = header + len_skip;
+
+ if ((ptsbuf[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf, -1))
+ {
+ /* header has a pts */
+ uint32_t pts = read_pts(ptsbuf, -1);
+
+ if (pts != INVALID_TIMESTAMP)
+ {
+ str->pts = pts;
+#if 0
+ /* DTS isn't used for anything since things just get
+ decoded ASAP but keep the code around */
+ if (STREAM_IS_VIDEO(id))
+ {
+ /* Video stream - header may have a dts as well */
+ str->dts = pts;
+
+ if (ptsbuf[-1] & 0xf0) == 0x30)
+ {
+ pts = read_pts(ptsbuf, 4);
+
+ if (pts != INVALID_TIMESTAMP)
+ str->dts = pts;
+ }
+ }
+#endif
+ str->pkt_flags |= PKT_HAS_TS;
+ }
+ }
+ }
+
+ p += length;
+ /* Max bytes: 6 + 65535 - 7 = 65534 */
+ bytes = 6 + (header[4] << 8) + header[5] - length;
+
+ str->curr_packet = p;
+ str->curr_packet_end = p + bytes;
+ str->hdr.win_left = str->hdr.win_right + length;
+ str->hdr.win_right = str->hdr.win_left + bytes;
+
+ if (str->hdr.win_right > disk_buf.filesize)
+ {
+ /* No packet that exceeds end of file can be valid */
+ str_end_of_stream(str);
+ return STREAM_DATA_END;
+ }
+
+ return STREAM_OK;
+ } /* end while */
+
+ #undef INC_BUF
+}
+
+/* This simply reads data from the file one page at a time and returns a
+ * pointer to it in the buffer. */
+static int parse_elementary(struct stream *str, enum stream_parse_mode type)
+{
+ uint8_t *p;
+ ssize_t len = 0;
+
+ str->pkt_flags = 0;
+
+ switch (type)
+ {
+ case STREAM_PM_STREAMING:
+ /* Has the end been reached already? */
+ if (str->state == SSTATE_END)
+ return STREAM_DATA_END;
+
+ /* Are we at the end of file? */
+ if (str->hdr.win_left >= disk_buf.filesize)
+ {
+ str_end_of_stream(str);
+ return STREAM_DATA_END;
+ }
+
+ if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD))
+ {
+ /* This data range is not buffered yet - register stream to
+ * be notified when it becomes available. Stream is obliged
+ * to enter a TSTATE_DATA state if it must wait. */
+ int res = str_next_data_not_ready(str);
+
+ if (res != STREAM_OK)
+ return res;
+ }
+
+ len = DISK_BUF_PAGE_SIZE;
+
+ if ((size_t)(str->hdr.win_right + len) > (size_t)disk_buf.filesize)
+ len = disk_buf.filesize - str->hdr.win_right;
+
+ if (len <= 0)
+ {
+ str_end_of_stream(str);
+ return STREAM_DATA_END;
+ }
+
+ p = str->curr_packet_end;
+ if (p >= disk_buf.end)
+ p -= disk_buf.size;
+ break;
+ /* STREAM_PM_STREAMING: */
+
+ case STREAM_PM_RANDOM_ACCESS:
+ str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET);
+ len = disk_buf_getbuffer(DISK_BUF_PAGE_SIZE, &p, NULL, NULL);
+
+ if (len <= 0 || str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit)
+ {
+ str_end_of_stream(str);
+ return STREAM_DATA_END;
+ }
+ break;
+ /* STREAM_PM_RANDOM_ACCESS: */
+ }
+
+ str->state = SSTATE_PARSE;
+ str->curr_packet = p;
+ str->curr_packet_end = p + len;
+ str->hdr.win_left = str->hdr.win_right;
+ str->hdr.win_right = str->hdr.win_left + len;
+
+ return STREAM_OK;
+}
+
+bool parser_prepare_image(uint32_t time)
+{
+ struct stream_scan sk;
+ int tries;
+ int result;
+
+ stream_scan_init(&sk);
+
+ if (!str_send_msg(&video_str, STREAM_NEEDS_SYNC, time))
+ {
+ DEBUGF("Image was ready\n");
+ return true; /* Should already have the image */
+ }
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ rb->cpu_boost(true); /* No interference with trigger_cpu_boost */
+#endif
+
+ str_send_msg(&video_str, STREAM_RESET, 0);
+
+ sk.pos = parser_can_seek() ?
+ mpeg_parser_seek_PTS(time, video_str.id) : 0;
+ sk.len = sk.pos;
+ sk.dir = SSCAN_REVERSE;
+
+ tries = 1;
+try_again:
+
+ if (mpeg_parser_scan_start_code(&sk, MPEG_START_GOP))
+ {
+ DEBUGF("GOP found at: %ld\n", sk.pos);
+
+ unsigned id = mpeg_parser_scan_pes(&sk);
+
+ if (id != video_str.id && sk.pos > 0)
+ {
+ /* Not part of our stream */
+ DEBUGF(" wrong stream: 0x%02x\n", id);
+ goto try_again;
+ }
+
+ /* This will hit the PES header since it's known to be there */
+ uint32_t pts = mpeg_parser_scan_pts(&sk, id);
+
+ if (pts == INVALID_TIMESTAMP || pts > time)
+ {
+ DEBUGF(" wrong timestamp: %u\n", (unsigned)pts);
+ goto try_again;
+ }
+ }
+
+ str_parser.parms.sd.time = time;
+ str_parser.parms.sd.sk.pos = MAX(sk.pos, 0);
+ str_parser.parms.sd.sk.len = 1024*1024;
+ str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
+
+ DEBUGF("thumb pos:%ld len:%ld\n", str_parser.parms.sd.sk.pos,
+ (long)str_parser.parms.sd.sk.len);
+
+ result = str_send_msg(&video_str, STREAM_SYNC,
+ (intptr_t)&str_parser.parms.sd);
+
+ if (result != STREAM_PERFECT_MATCH)
+ {
+ /* Two tries should be all that is nescessary to find the exact frame
+ * if the first GOP actually started later than the timestamp - the
+ * GOP just prior must then start on or earlier. */
+ if (++tries <= 2)
+ goto try_again;
+ }
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ rb->cpu_boost(false);
+#endif
+
+ return result > STREAM_OK;
+}
+
+/* Seek parser to the specified time and return absolute time.
+ * No actual hard stuff is performed here. That's done when streaming is
+ * about to begin or something from the current position is requested */
+uint32_t parser_seek_time(uint32_t time)
+{
+ if (!parser_can_seek())
+ time = 0;
+ else if (time > str_parser.duration)
+ time = str_parser.duration;
+
+ str_parser.last_seek_time = time + str_parser.start_pts;
+ return str_parser.last_seek_time;
+}
+
+void parser_prepare_streaming(void)
+{
+ struct stream_window sw;
+
+ DEBUGF("parser_prepare_streaming\n");
+
+ /* Prepare initial video frame */
+ parser_prepare_image(str_parser.last_seek_time);
+
+ /* Sync audio stream */
+ if (audio_str.start_pts != INVALID_TIMESTAMP)
+ prepare_audio(str_parser.last_seek_time);
+
+ /* Prequeue some data and set buffer window */
+ if (!stream_get_window(&sw))
+ sw.left = sw.right = disk_buf.filesize;
+
+ DEBUGF(" swl:%ld swr:%ld\n", sw.left, sw.right);
+
+ if (sw.right > disk_buf.filesize - 4*MIN_BUFAHEAD)
+ sw.right = disk_buf.filesize - 4*MIN_BUFAHEAD;
+
+ disk_buf_prepare_streaming(sw.left,
+ sw.right - sw.left + 4*MIN_BUFAHEAD);
+}
+
+int parser_init_stream(void)
+{
+ if (disk_buf.in_file < 0)
+ return STREAM_ERROR;
+
+ /* TODO: Actually find which streams are available */
+ audio_str.id = MPEG_STREAM_AUDIO_FIRST;
+ video_str.id = MPEG_STREAM_VIDEO_FIRST;
+
+ /* Try to pull a video PES - if not found, try video init anyway which
+ * should succeed if it really is a video-only stream */
+ video_str.hdr.pos = 0;
+ video_str.hdr.limit = 256*1024;
+
+ if (parse_demux(&video_str, STREAM_PM_RANDOM_ACCESS) == STREAM_OK)
+ {
+ /* Found a video packet - assume program stream */
+ str_parser.format = STREAM_FMT_MPEG_PS;
+ str_parser.next_data = parse_demux;
+ }
+ else
+ {
+ /* No PES element found - assume video elementary stream */
+ str_parser.format = STREAM_FMT_MPV;
+ str_parser.next_data = parse_elementary;
+ }
+
+ if (!init_video_info())
+ {
+ /* Cannot determine video size, etc. */
+ parser_init_state();
+ return STREAM_UNSUPPORTED;
+ }
+
+ if (str_parser.format == STREAM_FMT_MPEG_PS)
+ {
+ /* Initalize start_pts and end_pts with the length (in 45kHz units) of
+ * the movie. INVALID_TIMESTAMP if the time could not be determined */
+ if (!init_times(&video_str) || !check_times(&video_str))
+ {
+ /* Must have video at least */
+ parser_init_state();
+ return STREAM_UNSUPPORTED;
+ }
+
+ str_parser.flags |= STREAMF_CAN_SEEK;
+
+ if (init_times(&audio_str))
+ {
+ /* Audio will be part of playback pool */
+ stream_add_stream(&audio_str);
+
+ if (check_times(&audio_str))
+ {
+ /* Overall duration is maximum span */
+ str_parser.start_pts = MIN(audio_str.start_pts, video_str.start_pts);
+ str_parser.end_pts = MAX(audio_str.end_pts, video_str.end_pts);
+ }
+ else
+ {
+ /* Bad times on audio - use video times */
+ str_parser.start_pts = video_str.start_pts;
+ str_parser.end_pts = video_str.end_pts;
+
+ /* Questionable: could use bitrate seek and match video to that */
+ audio_str.start_pts = video_str.start_pts;
+ audio_str.end_pts = video_str.end_pts;
+ }
+ }
+ else
+ {
+ /* No audio stream - use video only */
+ str_parser.start_pts = video_str.start_pts;
+ str_parser.end_pts = video_str.end_pts;
+ }
+
+ str_parser.last_seek_time = str_parser.start_pts;
+ }
+ else
+ {
+ /* There's no way to handle times on this without a full file
+ * scan */
+ audio_str.start_pts = INVALID_TIMESTAMP;
+ audio_str.end_pts = INVALID_TIMESTAMP;
+ video_str.start_pts = 0;
+ video_str.end_pts = INVALID_TIMESTAMP;
+ str_parser.start_pts = 0;
+ str_parser.end_pts = INVALID_TIMESTAMP;
+ }
+
+ /* Add video to playback pool */
+ stream_add_stream(&video_str);
+
+ /* Cache duration - it's used very often */
+ str_parser.duration = str_parser.end_pts - str_parser.start_pts;
+
+ DEBUGF("Movie info:\n"
+ " size:%dx%d\n"
+ " start:%u\n"
+ " end:%u\n"
+ " duration:%u\n",
+ str_parser.dims.w, str_parser.dims.h,
+ (unsigned)str_parser.start_pts,
+ (unsigned)str_parser.end_pts,
+ (unsigned)str_parser.duration);
+
+ return STREAM_OK;
+}
+
+void parser_close_stream(void)
+{
+ stream_remove_streams();
+ parser_init_state();
+}
+
+bool parser_init(void)
+{
+ parser_init_state();
+ return true;
+}
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
new file mode 100644
index 0000000000..c904de466d
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -0,0 +1,1454 @@
+#include "plugin.h"
+#include "lib/helper.h"
+#include "lib/configfile.h"
+
+#include "mpegplayer.h"
+#include "mpeg_settings.h"
+
+struct mpeg_settings settings;
+
+#define THUMB_DELAY (75*HZ/100)
+
+/* button definitions */
+#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#define MPEG_START_TIME_SELECT BUTTON_ON
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_OFF
+
+#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_3G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_1G2G_PAD)
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_SCROLL_FWD
+#define MPEG_START_TIME_DOWN BUTTON_SCROLL_BACK
+#define MPEG_START_TIME_EXIT BUTTON_MENU
+
+#elif CONFIG_KEYPAD == GIGABEAT_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#define MPEG_START_TIME_RC_SELECT (BUTTON_RC_PLAY | BUTTON_REL)
+#define MPEG_START_TIME_RC_LEFT BUTTON_RC_REW
+#define MPEG_START_TIME_RC_RIGHT BUTTON_RC_FF
+#define MPEG_START_TIME_RC_UP BUTTON_RC_VOL_UP
+#define MPEG_START_TIME_RC_DOWN BUTTON_RC_VOL_DOWN
+#define MPEG_START_TIME_RC_EXIT (BUTTON_RC_PLAY | BUTTON_REPEAT)
+
+#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#define MPEG_START_TIME_RC_SELECT (BUTTON_RC_PLAY | BUTTON_REL)
+#define MPEG_START_TIME_RC_LEFT BUTTON_RC_REW
+#define MPEG_START_TIME_RC_RIGHT BUTTON_RC_FF
+#define MPEG_START_TIME_RC_UP BUTTON_RC_VOL_UP
+#define MPEG_START_TIME_RC_DOWN BUTTON_RC_VOL_DOWN
+#define MPEG_START_TIME_RC_EXIT (BUTTON_RC_PLAY | BUTTON_REPEAT)
+
+#elif CONFIG_KEYPAD == IRIVER_H10_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_SCROLL_UP
+#define MPEG_START_TIME_DOWN BUTTON_SCROLL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_SCROLL_BACK
+#define MPEG_START_TIME_RIGHT2 BUTTON_SCROLL_FWD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_SCROLL_BACK
+#define MPEG_START_TIME_RIGHT2 BUTTON_SCROLL_FWD
+#define MPEG_START_TIME_EXIT (BUTTON_HOME|BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == SANSA_C200_PAD) || \
+(CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
+(CONFIG_KEYPAD == SANSA_M200_PAD)
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == MROBE500_PAD
+#define MPEG_START_TIME_SELECT BUTTON_RC_HEART
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_RC_PLAY
+#define MPEG_START_TIME_DOWN BUTTON_RC_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_RC_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_RC_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == MROBE100_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_PLAY
+#define MPEG_START_TIME_RIGHT2 BUTTON_MENU
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
+#define MPEG_START_TIME_SELECT BUTTON_RC_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_RC_REW
+#define MPEG_START_TIME_RIGHT BUTTON_RC_FF
+#define MPEG_START_TIME_UP BUTTON_RC_VOL_UP
+#define MPEG_START_TIME_DOWN BUTTON_RC_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_RC_REC
+
+#elif CONFIG_KEYPAD == COWON_D2_PAD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_PLAY
+#define MPEG_START_TIME_RIGHT2 BUTTON_MENU
+#define MPEG_START_TIME_EXIT BUTTON_BACK
+
+#elif (CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD)
+#define MPEG_START_TIME_SELECT (BUTTON_PLAY|BUTTON_REL)
+#define MPEG_START_TIME_LEFT BUTTON_BACK
+#define MPEG_START_TIME_RIGHT BUTTON_MENU
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT (BUTTON_PLAY|BUTTON_REPEAT)
+
+#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_PREV
+#define MPEG_START_TIME_RIGHT BUTTON_NEXT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == ONDAVX747_PAD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == ONDAVX777_PAD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
+ (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_REW
+
+#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_PREV
+#define MPEG_START_TIME_RIGHT BUTTON_NEXT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_LEFT2 BUTTON_OK
+#define MPEG_START_TIME_RIGHT2 BUTTON_CANCEL
+#define MPEG_START_TIME_EXIT BUTTON_REC
+
+#elif CONFIG_KEYPAD == MPIO_HD200_PAD
+#define MPEG_START_TIME_SELECT BUTTON_FUNC
+#define MPEG_START_TIME_LEFT BUTTON_REW
+#define MPEG_START_TIME_RIGHT BUTTON_FF
+#define MPEG_START_TIME_UP BUTTON_VOL_UP
+#define MPEG_START_TIME_DOWN BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_REC
+
+#elif CONFIG_KEYPAD == MPIO_HD300_PAD
+#define MPEG_START_TIME_SELECT BUTTON_ENTER
+#define MPEG_START_TIME_LEFT BUTTON_REW
+#define MPEG_START_TIME_RIGHT BUTTON_FF
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_REC
+
+#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_BACK
+
+#elif (CONFIG_KEYPAD == HM60X_PAD) || (CONFIG_KEYPAD == HM801_PAD)
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == SONY_NWZ_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_BACK
+
+#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_BACK
+
+#elif CONFIG_KEYPAD == DX50_PAD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_VOL_UP
+#define MPEG_START_TIME_DOWN BUTTON_VOL_DOWN
+
+#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == XDUOO_X3_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_PREV
+#define MPEG_START_TIME_RIGHT BUTTON_NEXT
+#define MPEG_START_TIME_UP BUTTON_HOME
+#define MPEG_START_TIME_DOWN BUTTON_OPTION
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_PREV
+#define MPEG_START_TIME_RIGHT BUTTON_NEXT
+#define MPEG_START_TIME_UP BUTTON_HOME
+#define MPEG_START_TIME_DOWN BUTTON_OPTION
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_PREV
+#define MPEG_START_TIME_RIGHT BUTTON_NEXT
+#define MPEG_START_TIME_UP BUTTON_HOME
+#define MPEG_START_TIME_DOWN BUTTON_OPTION
+#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
+#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_HOME
+#define MPEG_START_TIME_RIGHT BUTTON_VOL_DOWN
+#define MPEG_START_TIME_UP BUTTON_PREV
+#define MPEG_START_TIME_DOWN BUTTON_NEXT
+#define MPEG_START_TIME_LEFT2 (BUTTON_POWER + BUTTON_HOME)
+#define MPEG_START_TIME_RIGHT2 (BUTTON_POWER + BUTTON_VOL_DOWN)
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == EROSQ_PAD
+#define MPEG_START_TIME_SELECT BUTTON_PLAY
+#define MPEG_START_TIME_LEFT BUTTON_SCROLL_BACK
+#define MPEG_START_TIME_RIGHT BUTTON_SCROLL_FWD
+#define MPEG_START_TIME_UP BUTTON_PREV
+#define MPEG_START_TIME_DOWN BUTTON_NEXT
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define MPEG_START_TIME_SELECT BUTTON_SELECT
+#define MPEG_START_TIME_LEFT BUTTON_LEFT
+#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
+#define MPEG_START_TIME_UP BUTTON_UP
+#define MPEG_START_TIME_DOWN BUTTON_DOWN
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
+#else
+#error No keymap defined!
+#endif
+
+#ifdef HAVE_TOUCHSCREEN
+#ifndef MPEG_START_TIME_SELECT
+#define MPEG_START_TIME_SELECT BUTTON_CENTER
+#endif
+#ifndef MPEG_START_TIME_LEFT
+#define MPEG_START_TIME_LEFT BUTTON_MIDLEFT
+#endif
+#ifndef MPEG_START_TIME_RIGHT
+#define MPEG_START_TIME_RIGHT BUTTON_MIDRIGHT
+#endif
+#ifndef MPEG_START_TIME_UP
+#define MPEG_START_TIME_UP BUTTON_TOPMIDDLE
+#endif
+#ifndef MPEG_START_TIME_DOWN
+#define MPEG_START_TIME_DOWN BUTTON_BOTTOMMIDDLE
+#endif
+#ifndef MPEG_START_TIME_LEFT2
+#define MPEG_START_TIME_LEFT2 BUTTON_TOPRIGHT
+#endif
+#ifndef MPEG_START_TIME_RIGHT2
+#define MPEG_START_TIME_RIGHT2 BUTTON_TOPLEFT
+#endif
+#ifndef MPEG_START_TIME_EXIT
+#define MPEG_START_TIME_EXIT BUTTON_TOPLEFT
+#endif
+#endif
+
+static struct configdata config[] =
+{
+ {TYPE_INT, 0, 2, { .int_p = &settings.showfps }, "Show FPS", NULL},
+ {TYPE_INT, 0, 2, { .int_p = &settings.limitfps }, "Limit FPS", NULL},
+ {TYPE_INT, 0, 2, { .int_p = &settings.skipframes }, "Skip frames", NULL},
+ {TYPE_INT, 0, INT_MAX, { .int_p = &settings.resume_count }, "Resume count",
+ NULL},
+ {TYPE_INT, 0, MPEG_RESUME_NUM_OPTIONS,
+ { .int_p = &settings.resume_options }, "Resume options", NULL},
+#if MPEG_OPTION_DITHERING_ENABLED
+ {TYPE_INT, 0, INT_MAX, { .int_p = &settings.displayoptions },
+ "Display options", NULL},
+#endif
+ {TYPE_INT, 0, 2, { .int_p = &settings.tone_controls }, "Tone controls",
+ NULL},
+ {TYPE_INT, 0, 2, { .int_p = &settings.channel_modes }, "Channel modes",
+ NULL},
+ {TYPE_INT, 0, 2, { .int_p = &settings.crossfeed }, "Crossfeed", NULL},
+ {TYPE_INT, 0, 2, { .int_p = &settings.equalizer }, "Equalizer", NULL},
+ {TYPE_INT, 0, 2, { .int_p = &settings.dithering }, "Dithering", NULL},
+ {TYPE_INT, 0, 2, { .int_p = &settings.play_mode }, "Play mode", NULL},
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+ {TYPE_INT, -1, INT_MAX, { .int_p = &settings.backlight_brightness },
+ "Backlight brightness", NULL},
+#endif
+};
+
+static const struct opt_items noyes[2] = {
+ { STR(LANG_SET_BOOL_NO) },
+ { STR(LANG_SET_BOOL_YES) },
+};
+
+static const struct opt_items singleall[2] = {
+ { STR(LANG_SINGLE) },
+ { STR(LANG_ALL) },
+};
+
+static const struct opt_items globaloff[2] = {
+ { STR(LANG_OFF) },
+ { STR(LANG_USE_SOUND_SETTING) },
+};
+
+static void mpeg_settings(void);
+static bool mpeg_set_option(const char* string,
+ void* variable,
+ enum optiontype type,
+ const struct opt_items* options,
+ int numoptions,
+ void (*function)(int))
+{
+ mpeg_sysevent_clear();
+
+ /* This eats SYS_POWEROFF - :\ */
+ bool usb = rb->set_option(string, variable, type, options, numoptions,
+ function);
+
+ if (usb)
+ mpeg_sysevent_set();
+
+ return usb;
+}
+
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS /* Only used for this atm */
+static bool mpeg_set_int(const char *string, const char *unit,
+ int voice_unit, const int *variable,
+ void (*function)(int), int step,
+ int min,
+ int max,
+ const char* (*formatter)(char*, size_t, int, const char*),
+ int32_t (*get_talk_id)(int, int))
+{
+ mpeg_sysevent_clear();
+
+ bool usb = rb->set_int_ex(string, unit, voice_unit, variable, function,
+ step, min, max, formatter, get_talk_id);
+
+ if (usb)
+ mpeg_sysevent_set();
+
+ return usb;
+}
+
+static int32_t backlight_brightness_getlang(int value, int unit)
+{
+ if (value < 0)
+ return LANG_USE_COMMON_SETTING;
+
+ return TALK_ID(value + MIN_BRIGHTNESS_SETTING, unit);
+}
+
+void mpeg_backlight_update_brightness(int value)
+{
+ if (value >= 0)
+ {
+ value += MIN_BRIGHTNESS_SETTING;
+ backlight_brightness_set(value);
+ }
+ else
+ {
+ backlight_brightness_use_setting();
+ }
+}
+
+static void backlight_brightness_function(int value)
+{
+ mpeg_backlight_update_brightness(value);
+}
+
+static const char* backlight_brightness_formatter(char *buf, size_t length,
+ int value, const char *input)
+{
+ (void)input;
+
+ if (value < 0)
+ return rb->str(LANG_USE_COMMON_SETTING);
+ else
+ rb->snprintf(buf, length, "%d", value + MIN_BRIGHTNESS_SETTING);
+ return buf;
+}
+#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
+
+/* Sync a particular audio setting to global or mpegplayer forced off */
+static void sync_audio_setting(int setting, bool global)
+{
+ switch (setting)
+ {
+ case MPEG_AUDIO_TONE_CONTROLS:
+ #ifdef AUDIOHW_HAVE_BASS
+ rb->sound_set(SOUND_BASS, (global || settings.tone_controls)
+ ? rb->global_settings->bass
+ : rb->sound_default(SOUND_BASS));
+ #endif
+ #ifdef AUDIOHW_HAVE_TREBLE
+ rb->sound_set(SOUND_TREBLE, (global || settings.tone_controls)
+ ? rb->global_settings->treble
+ : rb->sound_default(SOUND_TREBLE));
+ #endif
+
+ #ifdef AUDIOHW_HAVE_EQ
+ for (int band = 0;; band++)
+ {
+ int setting = rb->sound_enum_hw_eq_band_setting(band, AUDIOHW_EQ_GAIN);
+
+ if (setting == -1)
+ break;
+
+ rb->sound_set(setting, (global || settings.tone_controls)
+ ? rb->global_settings->hw_eq_bands[band].gain
+ : rb->sound_default(setting));
+ }
+ #endif /* AUDIOHW_HAVE_EQ */
+ break;
+
+ case MPEG_AUDIO_CHANNEL_MODES:
+ rb->sound_set(SOUND_CHANNELS, (global || settings.channel_modes)
+ ? rb->global_settings->channel_config
+ : SOUND_CHAN_STEREO);
+ break;
+
+ case MPEG_AUDIO_CROSSFEED:
+ rb->dsp_set_crossfeed_type((global || settings.crossfeed) ?
+ rb->global_settings->crossfeed :
+ CROSSFEED_TYPE_NONE);
+ break;
+
+ case MPEG_AUDIO_EQUALIZER:
+ rb->dsp_eq_enable((global || settings.equalizer) ?
+ rb->global_settings->eq_enabled : false);
+ break;
+
+ case MPEG_AUDIO_DITHERING:
+ rb->dsp_dither_enable((global || settings.dithering) ?
+ rb->global_settings->dithering_enabled : false);
+ break;
+ }
+}
+
+/* Sync all audio settings to global or mpegplayer forced off */
+static void sync_audio_settings(bool global)
+{
+ static const int setting_index[] =
+ {
+ MPEG_AUDIO_TONE_CONTROLS,
+ MPEG_AUDIO_CHANNEL_MODES,
+ MPEG_AUDIO_CROSSFEED,
+ MPEG_AUDIO_EQUALIZER,
+ MPEG_AUDIO_DITHERING,
+ };
+ unsigned i;
+
+ for (i = 0; i < ARRAYLEN(setting_index); i++)
+ {
+ sync_audio_setting(setting_index[i], global);
+ }
+}
+
+#ifndef HAVE_LCD_COLOR
+/* Cheapo splash implementation for the grey surface */
+static void grey_splash(int ticks, const unsigned char *fmt, ...)
+{
+ unsigned char buffer[256];
+ int x, y, w, h;
+ int oldfg, oldmode;
+
+ va_list ap;
+ va_start(ap, fmt);
+
+ rb->vsnprintf(buffer, sizeof (buffer), fmt, ap);
+
+ va_end(ap);
+
+ grey_getstringsize(buffer, &w, &h);
+
+ oldfg = grey_get_foreground();
+ oldmode = grey_get_drawmode();
+
+ grey_set_drawmode(DRMODE_FG);
+ grey_set_foreground(GREY_LIGHTGRAY);
+
+ x = (LCD_WIDTH - w) / 2;
+ y = (LCD_HEIGHT - h) / 2;
+
+ grey_fillrect(x - 1, y - 1, w + 2, h + 2);
+
+ grey_set_foreground(GREY_BLACK);
+
+ grey_putsxy(x, y, buffer);
+ grey_drawrect(x - 2, y - 2, w + 4, h + 4);
+
+ grey_set_foreground(oldfg);
+ grey_set_drawmode(oldmode);
+
+ grey_update();
+
+ if (ticks > 0)
+ rb->sleep(ticks);
+}
+#endif /* !HAVE_LCD_COLOR */
+
+static void show_loading(struct vo_rect *rc)
+{
+ int oldmode = mylcd_get_drawmode();
+ mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+ mylcd_fillrect(rc->l-1, rc->t-1, rc->r - rc->l + 2, rc->b - rc->t + 2);
+ mylcd_set_drawmode(oldmode);
+ mylcd_splash(0, "Loading...");
+}
+
+static void draw_slider(uint32_t range, uint32_t pos, struct vo_rect *rc)
+{
+ #define SLIDER_WIDTH (LCD_WIDTH-SLIDER_LMARGIN-SLIDER_RMARGIN)
+ #define SLIDER_X SLIDER_LMARGIN
+ #define SLIDER_Y (LCD_HEIGHT-SLIDER_HEIGHT-SLIDER_BMARGIN)
+ #define SLIDER_HEIGHT 8
+ #define SLIDER_TEXTMARGIN 1
+ #define SLIDER_LMARGIN 1
+ #define SLIDER_RMARGIN 1
+ #define SLIDER_TMARGIN 1
+ #define SLIDER_BMARGIN 1
+ #define SCREEN_MARGIN 1
+
+ struct hms hms;
+ char str[32];
+ int text_w, text_h, text_y;
+
+ /* Put positition on left */
+ ts_to_hms(pos, &hms);
+ hms_format(str, sizeof(str), &hms);
+ mylcd_getstringsize(str, NULL, &text_h);
+ text_y = SLIDER_Y - SLIDER_TEXTMARGIN - text_h;
+
+ if (rc == NULL)
+ {
+ int oldmode = mylcd_get_drawmode();
+ mylcd_set_drawmode(DRMODE_BG | DRMODE_INVERSEVID);
+ mylcd_fillrect(SLIDER_X, text_y, SLIDER_WIDTH,
+ LCD_HEIGHT - SLIDER_BMARGIN - text_y
+ - SLIDER_TMARGIN);
+ mylcd_set_drawmode(oldmode);
+
+ mylcd_putsxy(SLIDER_X, text_y, str);
+
+ /* Put duration on right */
+ ts_to_hms(range, &hms);
+ hms_format(str, sizeof(str), &hms);
+ mylcd_getstringsize(str, &text_w, NULL);
+
+ mylcd_putsxy(SLIDER_X + SLIDER_WIDTH - text_w, text_y, str);
+
+ /* Draw slider */
+ mylcd_drawrect(SLIDER_X, SLIDER_Y, SLIDER_WIDTH, SLIDER_HEIGHT);
+ mylcd_fillrect(SLIDER_X, SLIDER_Y,
+ muldiv_uint32(pos, SLIDER_WIDTH, range),
+ SLIDER_HEIGHT);
+
+ /* Update screen */
+ mylcd_update_rect(SLIDER_X, text_y - SLIDER_TMARGIN, SLIDER_WIDTH,
+ LCD_HEIGHT - SLIDER_BMARGIN - text_y + SLIDER_TEXTMARGIN);
+ }
+ else
+ {
+ /* Just return slider rectangle */
+ rc->l = SLIDER_X;
+ rc->t = text_y - SLIDER_TMARGIN;
+ rc->r = rc->l + SLIDER_WIDTH;
+ rc->b = rc->t + LCD_HEIGHT - SLIDER_BMARGIN - text_y;
+ }
+}
+
+static bool display_thumb_image(const struct vo_rect *rc)
+{
+ bool retval = true;
+ unsigned ltgray = MYLCD_LIGHTGRAY;
+ unsigned dkgray = MYLCD_DARKGRAY;
+
+ int oldcolor = mylcd_get_foreground();
+
+ if (!stream_display_thumb(rc))
+ {
+ /* Display "No Frame" and erase any border */
+ const char * const str = "No Frame";
+ int x, y, w, h;
+
+ mylcd_getstringsize(str, &w, &h);
+ x = (rc->r + rc->l - w) / 2;
+ y = (rc->b + rc->t - h) / 2;
+ mylcd_putsxy(x, y, str);
+
+ mylcd_update_rect(x, y, w, h);
+
+ ltgray = dkgray = mylcd_get_background();
+ retval = false;
+ }
+
+ /* Draw a raised border around the frame (or erase if no frame) */
+
+ mylcd_set_foreground(ltgray);
+
+ mylcd_hline(rc->l-1, rc->r-1, rc->t-1);
+ mylcd_vline(rc->l-1, rc->t, rc->b-1);
+
+ mylcd_set_foreground(dkgray);
+
+ mylcd_hline(rc->l-1, rc->r, rc->b);
+ mylcd_vline(rc->r, rc->t-1, rc->b);
+
+ mylcd_set_foreground(oldcolor);
+
+ mylcd_update_rect(rc->l-1, rc->t-1, rc->r - rc->l + 2, 1);
+ mylcd_update_rect(rc->l-1, rc->t, 1, rc->b - rc->t);
+ mylcd_update_rect(rc->l-1, rc->b, rc->r - rc->l + 2, 1);
+ mylcd_update_rect(rc->r, rc->t, 1, rc->b - rc->t);
+
+ return retval;
+}
+
+/* Add an amount to the specified time - with saturation */
+static uint32_t increment_time(uint32_t val, int32_t amount, uint32_t range)
+{
+ if (amount < 0)
+ {
+ uint32_t off = -amount;
+ if (range > off && val >= off)
+ val -= off;
+ else
+ val = 0;
+ }
+ else if (amount > 0)
+ {
+ uint32_t off = amount;
+ if (range > off && val <= range - off)
+ val += off;
+ else
+ val = range;
+ }
+
+ return val;
+}
+
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+static void get_start_time_lcd_enable_hook(unsigned short id, void *param)
+{
+ (void)id;
+ (void)param;
+ rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_0, 0);
+}
+#endif /* HAVE_LCD_ENABLE */
+
+static int get_start_time(uint32_t duration)
+{
+ int button = 0;
+ int tmo = TIMEOUT_NOBLOCK;
+ uint32_t resume_time = settings.resume_time;
+ struct vo_rect rc_vid, rc_bound;
+ uint32_t aspect_vid, aspect_bound;
+ bool sliding = false;
+
+ enum state_enum slider_state = STATE0;
+
+ mylcd_clear_display();
+ mylcd_update();
+
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+ rb->add_event(LCD_EVENT_ACTIVATION, get_start_time_lcd_enable_hook);
+#endif
+
+ draw_slider(0, 100, &rc_bound);
+ rc_bound.b = rc_bound.t - SLIDER_TMARGIN;
+ rc_bound.t = SCREEN_MARGIN;
+
+ DEBUGF("rc_bound: %d, %d, %d, %d\n", rc_bound.l, rc_bound.t,
+ rc_bound.r, rc_bound.b);
+
+ rc_vid.l = rc_vid.t = 0;
+ if (!stream_vo_get_size((struct vo_ext *)&rc_vid.r))
+ {
+ /* Can't get size - fill whole thing */
+ rc_vid.r = rc_bound.r - rc_bound.l;
+ rc_vid.b = rc_bound.b - rc_bound.t;
+ }
+
+ /* Get aspect ratio of bounding rectangle and video in u16.16 */
+ aspect_bound = ((rc_bound.r - rc_bound.l) << 16) /
+ (rc_bound.b - rc_bound.t);
+
+ DEBUGF("aspect_bound: %u.%02u\n", (unsigned)(aspect_bound >> 16),
+ (unsigned)(100*(aspect_bound & 0xffff) >> 16));
+
+ aspect_vid = (rc_vid.r << 16) / rc_vid.b;
+
+ DEBUGF("aspect_vid: %u.%02u\n", (unsigned)(aspect_vid >> 16),
+ (unsigned)(100*(aspect_vid & 0xffff) >> 16));
+
+ if (aspect_vid >= aspect_bound)
+ {
+ /* Video proportionally wider than or same as bounding rectangle */
+ if (rc_vid.r > rc_bound.r - rc_bound.l)
+ {
+ rc_vid.r = rc_bound.r - rc_bound.l;
+ rc_vid.b = (rc_vid.r << 16) / aspect_vid;
+ }
+ /* else already fits */
+ }
+ else
+ {
+ /* Video proportionally narrower than bounding rectangle */
+ if (rc_vid.b > rc_bound.b - rc_bound.t)
+ {
+ rc_vid.b = rc_bound.b - rc_bound.t;
+ rc_vid.r = (aspect_vid * rc_vid.b) >> 16;
+ }
+ /* else already fits */
+ }
+
+ /* Even width and height >= 2 */
+ rc_vid.r = (rc_vid.r < 2) ? 2 : (rc_vid.r & ~1);
+ rc_vid.b = (rc_vid.b < 2) ? 2 : (rc_vid.b & ~1);
+
+ /* Center display in bounding rectangle */
+ rc_vid.l = ((rc_bound.l + rc_bound.r) - rc_vid.r) / 2;
+ rc_vid.r += rc_vid.l;
+
+ rc_vid.t = ((rc_bound.t + rc_bound.b) - rc_vid.b) / 2;
+ rc_vid.b += rc_vid.t;
+
+ DEBUGF("rc_vid: %d, %d, %d, %d\n", rc_vid.l, rc_vid.t,
+ rc_vid.r, rc_vid.b);
+
+#ifndef HAVE_LCD_COLOR
+ stream_gray_show(true);
+#endif
+
+ while (slider_state < STATE9)
+ {
+ button = mpeg_button_get(tmo);
+
+ switch (button)
+ {
+ case BUTTON_NONE:
+ break;
+
+ /* Coarse (1 minute) control */
+ case MPEG_START_TIME_DOWN:
+ case MPEG_START_TIME_DOWN | BUTTON_REPEAT:
+#ifdef MPEG_START_TIME_RC_DOWN
+ case MPEG_START_TIME_RC_DOWN:
+ case MPEG_START_TIME_RC_DOWN | BUTTON_REPEAT:
+#endif
+ resume_time = increment_time(resume_time, -60*TS_SECOND, duration);
+ slider_state = STATE0;
+ break;
+
+ case MPEG_START_TIME_UP:
+ case MPEG_START_TIME_UP | BUTTON_REPEAT:
+#ifdef MPEG_START_TIME_RC_UP
+ case MPEG_START_TIME_RC_UP:
+ case MPEG_START_TIME_RC_UP | BUTTON_REPEAT:
+#endif
+ resume_time = increment_time(resume_time, 60*TS_SECOND, duration);
+ slider_state = STATE0;
+ break;
+
+ /* Fine (1 second) control */
+ case MPEG_START_TIME_LEFT:
+ case MPEG_START_TIME_LEFT | BUTTON_REPEAT:
+#ifdef MPEG_START_TIME_RC_LEFT
+ case MPEG_START_TIME_RC_LEFT:
+ case MPEG_START_TIME_RC_LEFT | BUTTON_REPEAT:
+#endif
+#ifdef MPEG_START_TIME_LEFT2
+ case MPEG_START_TIME_LEFT2:
+ case MPEG_START_TIME_LEFT2 | BUTTON_REPEAT:
+#endif
+ resume_time = increment_time(resume_time, -TS_SECOND, duration);
+ slider_state = STATE0;
+ break;
+
+ case MPEG_START_TIME_RIGHT:
+ case MPEG_START_TIME_RIGHT | BUTTON_REPEAT:
+#ifdef MPEG_START_TIME_RC_RIGHT
+ case MPEG_START_TIME_RC_RIGHT:
+ case MPEG_START_TIME_RC_RIGHT | BUTTON_REPEAT:
+#endif
+#ifdef MPEG_START_TIME_RIGHT2
+ case MPEG_START_TIME_RIGHT2:
+ case MPEG_START_TIME_RIGHT2 | BUTTON_REPEAT:
+#endif
+ resume_time = increment_time(resume_time, TS_SECOND, duration);
+ slider_state = STATE0;
+ break;
+
+ case MPEG_START_TIME_SELECT:
+#ifdef MPEG_START_TIME_RC_SELECT
+ case MPEG_START_TIME_RC_SELECT:
+#endif
+ settings.resume_time = resume_time;
+ button = MPEG_START_SEEK;
+ slider_state = STATE9;
+ break;
+
+ case MPEG_START_TIME_EXIT:
+#ifdef MPEG_START_TIME_RC_EXIT
+ case MPEG_START_TIME_RC_EXIT:
+#endif
+ button = MPEG_START_EXIT;
+ slider_state = STATE9;
+ break;
+
+ case ACTION_STD_CANCEL:
+ button = MPEG_START_QUIT;
+ slider_state = STATE9;
+ break;
+
+#ifdef HAVE_LCD_ENABLE
+ case LCD_ENABLE_EVENT_0:
+ if (slider_state == STATE2)
+ display_thumb_image(&rc_vid);
+ continue;
+#endif
+
+ default:
+ rb->default_event_handler(button);
+ rb->yield();
+ continue;
+ }
+
+ switch (slider_state)
+ {
+ case STATE0:
+ if (!sliding)
+ {
+ if (rb->global_settings->talk_menu)
+ {
+ rb->talk_disable(true);
+#ifdef PLUGIN_USE_IRAM
+ mpegplayer_iram_restore();
+#endif
+ }
+ trigger_cpu_boost();
+ sliding = true;
+ }
+ stream_seek(resume_time, SEEK_SET);
+ show_loading(&rc_bound);
+ draw_slider(duration, resume_time, NULL);
+ slider_state = STATE1;
+ tmo = THUMB_DELAY;
+ break;
+ case STATE1:
+ display_thumb_image(&rc_vid);
+ slider_state = STATE2;
+ tmo = TIMEOUT_BLOCK;
+ if (sliding)
+ {
+ cancel_cpu_boost();
+ if (rb->global_settings->talk_menu)
+ {
+#ifdef PLUGIN_USE_IRAM
+ mpegplayer_iram_preserve();
+#endif
+ rb->talk_disable(false);
+ talk_val(resume_time / TS_SECOND, UNIT_TIME, false);
+ talk_val(resume_time * 100 / duration, UNIT_PERCENT, true);
+ }
+ sliding = false;
+ }
+ default:
+ break;
+ }
+
+ rb->yield();
+ }
+
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+ rb->remove_event(LCD_EVENT_ACTIVATION, get_start_time_lcd_enable_hook);
+#endif
+#ifndef HAVE_LCD_COLOR
+ stream_gray_show(false);
+ grey_clear_display();
+ grey_update();
+#endif
+
+ cancel_cpu_boost();
+
+ return button;
+}
+
+static int show_start_menu(uint32_t duration)
+{
+ int selected = 0;
+ int result = 0;
+ bool menu_quit = false;
+
+ MENUITEM_STRINGLIST(menu, "Mpegplayer Menu", mpeg_sysevent_callback,
+ ID2P(LANG_RESTART_PLAYBACK),
+ ID2P(LANG_RESUME_PLAYBACK),
+ ID2P(LANG_SET_RESUME_TIME),
+ ID2P(LANG_SETTINGS),
+ ID2P(LANG_MENU_QUIT));
+
+ if (rb->global_settings->talk_menu)
+ {
+#ifdef PLUGIN_USE_IRAM
+ mpegplayer_iram_preserve();
+#endif
+ rb->talk_disable(false);
+ }
+
+ rb->button_clear_queue();
+
+ while (!menu_quit)
+ {
+ mpeg_sysevent_clear();
+ result = rb->do_menu(&menu, &selected, NULL, false);
+
+ switch (result)
+ {
+ case MPEG_START_RESTART:
+ settings.resume_time = 0;
+ menu_quit = true;
+ break;
+
+ case MPEG_START_RESUME:
+ menu_quit = true;
+ break;
+
+ case MPEG_START_SEEK:
+ if (!stream_can_seek())
+ {
+ rb->splash(HZ, ID2P(LANG_UNAVAILABLE));
+ break;
+ }
+
+ result = get_start_time(duration);
+
+ if (result != MPEG_START_EXIT)
+ menu_quit = true;
+ break;
+
+ case MPEG_START_SETTINGS:
+ mpeg_settings();
+ break;
+
+ default:
+ result = MPEG_START_QUIT;
+ menu_quit = true;
+ break;
+ }
+
+ if (mpeg_sysevent() != 0)
+ {
+ result = MPEG_START_QUIT;
+ menu_quit = true;
+ }
+ }
+
+ if (rb->global_settings->talk_menu)
+ {
+ rb->talk_disable(true);
+#ifdef PLUGIN_USE_IRAM
+ mpegplayer_iram_restore();
+#endif
+ }
+
+ return result;
+}
+
+/* Return the desired resume action */
+int mpeg_start_menu(uint32_t duration)
+{
+ mpeg_sysevent_clear();
+
+ switch (settings.resume_options)
+ {
+ case MPEG_RESUME_MENU_IF_INCOMPLETE:
+ if (!stream_can_seek() || settings.resume_time == 0)
+ {
+ case MPEG_RESUME_RESTART:
+ settings.resume_time = 0;
+ return MPEG_START_RESTART;
+ }
+ default:
+ case MPEG_RESUME_MENU_ALWAYS:
+ return show_start_menu(duration);
+ case MPEG_RESUME_ALWAYS:
+ return MPEG_START_SEEK;
+ }
+}
+
+int mpeg_menu(void)
+{
+ int result;
+
+ MENUITEM_STRINGLIST(menu, "Mpegplayer Menu", mpeg_sysevent_callback,
+ ID2P(LANG_SETTINGS),
+ ID2P(LANG_RESUME_PLAYBACK),
+ ID2P(LANG_MENU_QUIT));
+
+ if (rb->global_settings->talk_menu)
+ {
+#ifdef PLUGIN_USE_IRAM
+ mpegplayer_iram_preserve();
+#endif
+ rb->talk_disable(false);
+ }
+
+ rb->button_clear_queue();
+
+ mpeg_sysevent_clear();
+
+ result = rb->do_menu(&menu, NULL, NULL, false);
+
+ switch (result)
+ {
+ case MPEG_MENU_SETTINGS:
+ mpeg_settings();
+ break;
+
+ case MPEG_MENU_RESUME:
+ break;
+
+ case MPEG_MENU_QUIT:
+ break;
+
+ default:
+ break;
+ }
+
+ if (mpeg_sysevent() != 0)
+ result = MPEG_MENU_QUIT;
+
+ if (rb->global_settings->talk_menu)
+ {
+ rb->talk_disable(true);
+#ifdef PLUGIN_USE_IRAM
+ mpegplayer_iram_restore();
+#endif
+ }
+
+ return result;
+}
+
+static void display_options(void)
+{
+ int selected = 0;
+ int result;
+ bool menu_quit = false;
+
+ MENUITEM_STRINGLIST(menu, "Display Options", mpeg_sysevent_callback,
+#if MPEG_OPTION_DITHERING_ENABLED
+ ID2P(LANG_DITHERING),
+#endif
+ ID2P(LANG_DISPLAY_FPS),
+ ID2P(LANG_LIMIT_FPS),
+ ID2P(LANG_SKIP_FRAMES),
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+ ID2P(LANG_BACKLIGHT_BRIGHTNESS),
+#endif
+ );
+
+ rb->button_clear_queue();
+
+ while (!menu_quit)
+ {
+ mpeg_sysevent_clear();
+ result = rb->do_menu(&menu, &selected, NULL, false);
+
+ switch (result)
+ {
+#if MPEG_OPTION_DITHERING_ENABLED
+ case MPEG_OPTION_DITHERING:
+ result = (settings.displayoptions & LCD_YUV_DITHER) ? 1 : 0;
+ mpeg_set_option(rb->str(LANG_DITHERING), &result, INT, noyes, 2, NULL);
+ settings.displayoptions =
+ (settings.displayoptions & ~LCD_YUV_DITHER)
+ | ((result != 0) ? LCD_YUV_DITHER : 0);
+ rb->lcd_yuv_set_options(settings.displayoptions);
+ break;
+#endif /* MPEG_OPTION_DITHERING_ENABLED */
+
+ case MPEG_OPTION_DISPLAY_FPS:
+ mpeg_set_option(rb->str(LANG_DISPLAY_FPS), &settings.showfps, INT,
+ noyes, 2, NULL);
+ break;
+
+ case MPEG_OPTION_LIMIT_FPS:
+ mpeg_set_option(rb->str(LANG_LIMIT_FPS), &settings.limitfps, INT,
+ noyes, 2, NULL);
+ break;
+
+ case MPEG_OPTION_SKIP_FRAMES:
+ mpeg_set_option(rb->str(LANG_SKIP_FRAMES), &settings.skipframes, INT,
+ noyes, 2, NULL);
+ break;
+
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+ case MPEG_OPTION_BACKLIGHT_BRIGHTNESS:
+ result = settings.backlight_brightness;
+ mpeg_backlight_update_brightness(result);
+ mpeg_set_int(rb->str(LANG_BACKLIGHT_BRIGHTNESS), NULL, UNIT_INT, &result,
+ backlight_brightness_function, 1, -1,
+ MAX_BRIGHTNESS_SETTING - MIN_BRIGHTNESS_SETTING,
+ backlight_brightness_formatter,
+ backlight_brightness_getlang);
+ settings.backlight_brightness = result;
+ mpeg_backlight_update_brightness(-1);
+ break;
+#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
+
+ default:
+ menu_quit = true;
+ break;
+ }
+
+ if (mpeg_sysevent() != 0)
+ menu_quit = true;
+ }
+}
+
+static void audio_options(void)
+{
+ int selected = 0;
+ int result;
+ bool menu_quit = false;
+
+ MENUITEM_STRINGLIST(menu, "Audio Options", mpeg_sysevent_callback,
+ ID2P(LANG_TONE_CONTROLS),
+ ID2P(LANG_CHANNEL_CONFIGURATION),
+ ID2P(LANG_CROSSFEED),
+ ID2P(LANG_EQUALIZER),
+ ID2P(LANG_DITHERING));
+
+ rb->button_clear_queue();
+
+ while (!menu_quit)
+ {
+ mpeg_sysevent_clear();
+ result = rb->do_menu(&menu, &selected, NULL, false);
+
+ switch (result)
+ {
+ case MPEG_AUDIO_TONE_CONTROLS:
+ mpeg_set_option(rb->str(LANG_TONE_CONTROLS), &settings.tone_controls, INT,
+ globaloff, 2, NULL);
+ sync_audio_setting(result, false);
+ break;
+
+ case MPEG_AUDIO_CHANNEL_MODES:
+ mpeg_set_option(rb->str(LANG_CHANNEL_CONFIGURATION), &settings.channel_modes,
+ INT, globaloff, 2, NULL);
+ sync_audio_setting(result, false);
+ break;
+
+ case MPEG_AUDIO_CROSSFEED:
+ mpeg_set_option(rb->str(LANG_CROSSFEED), &settings.crossfeed, INT,
+ globaloff, 2, NULL);
+ sync_audio_setting(result, false);
+ break;
+
+ case MPEG_AUDIO_EQUALIZER:
+ mpeg_set_option(rb->str(LANG_EQUALIZER), &settings.equalizer, INT,
+ globaloff, 2, NULL);
+ sync_audio_setting(result, false);
+ break;
+
+ case MPEG_AUDIO_DITHERING:
+ mpeg_set_option(rb->str(LANG_DITHERING), &settings.dithering, INT,
+ globaloff, 2, NULL);
+ sync_audio_setting(result, false);
+ break;
+
+ default:
+ menu_quit = true;
+ break;
+ }
+
+ if (mpeg_sysevent() != 0)
+ menu_quit = true;
+ }
+}
+
+static void resume_options(void)
+{
+ static const struct opt_items items[MPEG_RESUME_NUM_OPTIONS] = {
+ [MPEG_RESUME_MENU_ALWAYS] =
+ { STR(LANG_FORCE_START_MENU) },
+ [MPEG_RESUME_MENU_IF_INCOMPLETE] =
+ { STR(LANG_CONDITIONAL_START_MENU) },
+ [MPEG_RESUME_ALWAYS] =
+ { STR(LANG_AUTO_RESUME) },
+ [MPEG_RESUME_RESTART] =
+ { STR(LANG_RESTART_PLAYBACK) },
+ };
+
+ mpeg_set_option(rb->str(LANG_MENU_RESUME_OPTIONS), &settings.resume_options,
+ INT, items, MPEG_RESUME_NUM_OPTIONS, NULL);
+}
+
+static void clear_resume_count(void)
+{
+ settings.resume_count = 0;
+ configfile_save(SETTINGS_FILENAME, config, ARRAYLEN(config),
+ SETTINGS_VERSION);
+}
+
+static void mpeg_settings(void)
+{
+ int selected = 0;
+ int result;
+ bool menu_quit = false;
+
+ MENUITEM_STRINGLIST(menu, "Settings", mpeg_sysevent_callback,
+ ID2P(LANG_MENU_DISPLAY_OPTIONS),
+ ID2P(LANG_MENU_AUDIO_OPTIONS),
+ ID2P(LANG_MENU_RESUME_OPTIONS),
+ ID2P(LANG_MENU_PLAY_MODE),
+ ID2P(LANG_CLEAR_ALL_RESUMES));
+
+ rb->button_clear_queue();
+
+ while (!menu_quit)
+ {
+ mpeg_sysevent_clear();
+
+ result = rb->do_menu(&menu, &selected, NULL, false);
+
+ switch (result)
+ {
+ case MPEG_SETTING_DISPLAY_SETTINGS:
+ display_options();
+ break;
+
+ case MPEG_SETTING_AUDIO_SETTINGS:
+ audio_options();
+ break;
+
+ case MPEG_SETTING_ENABLE_START_MENU:
+ resume_options();
+ break;
+
+ case MPEG_SETTING_PLAY_MODE:
+ mpeg_set_option(rb->str(LANG_MENU_PLAY_MODE), &settings.play_mode,
+ INT, singleall, 2, NULL);
+ break;
+
+ case MPEG_SETTING_CLEAR_RESUMES:
+ clear_resume_count();
+ break;
+
+ default:
+ menu_quit = true;
+ break;
+ }
+
+ if (mpeg_sysevent() != 0)
+ menu_quit = true;
+ }
+}
+
+void init_settings(const char* filename)
+{
+ /* Set the default settings */
+ settings.showfps = 0; /* Do not show FPS */
+ settings.limitfps = 1; /* Limit FPS */
+ settings.skipframes = 1; /* Skip frames */
+ settings.play_mode = 0; /* Play single video */
+ settings.resume_options = MPEG_RESUME_MENU_ALWAYS; /* Enable start menu */
+ settings.resume_count = 0;
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+ settings.backlight_brightness = -1; /* Use default setting */
+#endif
+#if MPEG_OPTION_DITHERING_ENABLED
+ settings.displayoptions = 0; /* No visual effects */
+#endif
+ settings.tone_controls = false;
+ settings.channel_modes = false;
+ settings.crossfeed = false;
+ settings.equalizer = false;
+ settings.dithering = false;
+
+ if (configfile_load(SETTINGS_FILENAME, config, ARRAYLEN(config),
+ SETTINGS_MIN_VERSION) < 0)
+ {
+ /* Generate a new config file with default values */
+ configfile_save(SETTINGS_FILENAME, config, ARRAYLEN(config),
+ SETTINGS_VERSION);
+ }
+
+ rb->strlcpy(settings.resume_filename, filename, MAX_PATH);
+
+ /* get the resume time for the current mpeg if it exists */
+ if ((settings.resume_time = configfile_get_value
+ (SETTINGS_FILENAME, filename)) < 0)
+ {
+ settings.resume_time = 0;
+ }
+
+#if MPEG_OPTION_DITHERING_ENABLED
+ rb->lcd_yuv_set_options(settings.displayoptions);
+#endif
+
+ /* Set our audio options */
+ sync_audio_settings(false);
+}
+
+void save_settings(void)
+{
+ unsigned i;
+ for (i = 0; i < ARRAYLEN(config); i++)
+ {
+ configfile_update_entry(SETTINGS_FILENAME, config[i].name,
+ *(config[i].int_p));
+ }
+
+ /* If this was a new resume entry then update the total resume count */
+ if (configfile_update_entry(SETTINGS_FILENAME, settings.resume_filename,
+ settings.resume_time) == 0)
+ {
+ configfile_update_entry(SETTINGS_FILENAME, "Resume count",
+ ++settings.resume_count);
+ }
+
+ /* Restore audio options */
+ sync_audio_settings(true);
+}
diff --git a/apps/plugins/mpegplayer/mpeg_settings.h b/apps/plugins/mpegplayer/mpeg_settings.h
new file mode 100644
index 0000000000..b1704ef707
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_settings.h
@@ -0,0 +1,110 @@
+
+#include "plugin.h"
+
+#define SETTINGS_VERSION 5
+#define SETTINGS_MIN_VERSION 1
+#define SETTINGS_FILENAME "mpegplayer.cfg"
+
+#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200) || defined(SANSA_C200) \
+ || defined(IRIVER_H10) || defined(COWON_D2) || defined(PHILIPS_HDD1630) \
+ || defined(SANSA_FUZE) || defined(SANSA_E200V2) || defined(SANSA_FUZEV2) \
+ || defined(TOSHIBA_GIGABEAT_S) || defined(PHILIPS_SA9200)
+#define MPEG_OPTION_DITHERING_ENABLED 1
+#endif
+
+#ifndef MPEG_OPTION_DITHERING_ENABLED
+#define MPEG_OPTION_DITHERING_ENABLED 0
+#endif
+
+enum mpeg_option_id
+{
+#if MPEG_OPTION_DITHERING_ENABLED
+ MPEG_OPTION_DITHERING,
+#endif
+ MPEG_OPTION_DISPLAY_FPS,
+ MPEG_OPTION_LIMIT_FPS,
+ MPEG_OPTION_SKIP_FRAMES,
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+ MPEG_OPTION_BACKLIGHT_BRIGHTNESS,
+#endif
+};
+
+enum mpeg_audio_option_id
+{
+ MPEG_AUDIO_TONE_CONTROLS,
+ MPEG_AUDIO_CHANNEL_MODES,
+ MPEG_AUDIO_CROSSFEED,
+ MPEG_AUDIO_EQUALIZER,
+ MPEG_AUDIO_DITHERING,
+};
+
+enum mpeg_resume_id
+{
+ MPEG_RESUME_MENU_ALWAYS = 0,
+ MPEG_RESUME_MENU_IF_INCOMPLETE,
+ MPEG_RESUME_RESTART,
+ MPEG_RESUME_ALWAYS,
+ MPEG_RESUME_NUM_OPTIONS,
+};
+
+enum mpeg_start_id
+{
+ MPEG_START_RESTART,
+ MPEG_START_RESUME,
+ MPEG_START_SEEK,
+ MPEG_START_SETTINGS,
+ MPEG_START_QUIT,
+ MPEG_START_EXIT,
+};
+
+enum mpeg_setting_id
+{
+ MPEG_SETTING_DISPLAY_SETTINGS,
+ MPEG_SETTING_AUDIO_SETTINGS,
+ MPEG_SETTING_ENABLE_START_MENU,
+ MPEG_SETTING_PLAY_MODE,
+ MPEG_SETTING_CLEAR_RESUMES,
+};
+
+enum mpeg_menu_id
+{
+ MPEG_MENU_SETTINGS,
+ MPEG_MENU_RESUME,
+ MPEG_MENU_QUIT,
+};
+
+struct mpeg_settings {
+ int showfps; /* flag to display fps */
+ int limitfps; /* flag to limit fps */
+ int skipframes; /* flag to skip frames */
+ int resume_options; /* type of resume action at start */
+ int resume_count; /* total # of resumes in config file */
+ int resume_time; /* resume time for current mpeg (in half minutes) */
+ char resume_filename[MAX_PATH]; /* filename of current mpeg */
+#if MPEG_OPTION_DITHERING_ENABLED
+ int displayoptions;
+#endif
+ int play_mode; /* play single file or all files in directory */
+ /* Audio options - simple on/off specification */
+ int tone_controls;
+ int channel_modes;
+ int crossfeed;
+ int equalizer;
+ int dithering;
+ /* Backlight options */
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+ int backlight_brightness;
+#endif
+};
+
+extern struct mpeg_settings settings;
+
+int mpeg_start_menu(uint32_t duration);
+int mpeg_menu(void);
+
+void init_settings(const char* filename);
+void save_settings(void);
+
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+void mpeg_backlight_update_brightness(int value);
+#endif
diff --git a/apps/plugins/mpegplayer/mpeg_stream.h b/apps/plugins/mpegplayer/mpeg_stream.h
new file mode 100644
index 0000000000..26fdaf07b4
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_stream.h
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Stream definitions for MPEG
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef MPEG_STREAM_H
+#define MPEG_STREAM_H
+
+/* Codes for various header byte sequences - MSB represents lowest memory
+ address */
+#define PACKET_START_CODE_PREFIX 0x00000100ul
+#define END_CODE 0x000001b9ul
+#define PACK_START_CODE 0x000001baul
+#define SYSTEM_HEADER_START_CODE 0x000001bbul
+
+/* p = base pointer, b0 - b4 = byte offsets from p */
+/* We only care about the MS 32 bits of the 33 and so the ticks are 45kHz */
+#define TS_FROM_HEADER(p, b0) \
+ ((uint32_t)((((p)[(b0)+0] & 0x0e) << 28) | \
+ (((p)[(b0)+1] ) << 21) | \
+ (((p)[(b0)+2] & 0xfe) << 13) | \
+ (((p)[(b0)+3] ) << 6) | \
+ (((p)[(b0)+4] ) >> 2)))
+
+#define TS_CHECK_MARKERS(p, b0) \
+ (((((p)[(b0)+0] & 0x01) << 2) | \
+ (((p)[(b0)+2] & 0x01) << 1) | \
+ (((p)[(b0)+4] & 0x01) )) == 0x07)
+
+/* Get the SCR in our 45kHz ticks. Ignore the 9-bit extension */
+#define MPEG2_PACK_HEADER_SCR(p, b0) \
+ ((uint32_t)((((p)[(b0)+0] & 0x38) << 26) | \
+ (((p)[(b0)+0] & 0x03) << 27) | \
+ (((p)[(b0)+1] ) << 19) | \
+ (((p)[(b0)+2] & 0xf8) << 11) | \
+ (((p)[(b0)+2] & 0x03) << 12) | \
+ (((p)[(b0)+3] ) << 4) | \
+ (((p)[(b0)+4] ) >> 4)))
+
+#define MPEG2_CHECK_PACK_SCR_MARKERS(ph, b0) \
+ (((((ph)[(b0)+0] & 0x04) ) | \
+ (((ph)[(b0)+2] & 0x04) >> 1) | \
+ (((ph)[(b0)+4] & 0x04) >> 2)) == 0x07)
+
+#define INVALID_TIMESTAMP (~(uint32_t)0)
+#define MAX_TIMESTAMP (INVALID_TIMESTAMP-1)
+#define TS_SECOND (45000) /* Timestamp ticks per second */
+#define TC_SECOND (27000000) /* MPEG timecode ticks per second */
+
+/* These values immediately follow the start code prefix '00 00 01' */
+
+/* Video start codes */
+#define MPEG_START_PICTURE 0x00
+#define MPEG_START_SLICE_FIRST 0x01
+#define MPEG_START_SLICE_LAST 0xaf
+#define MPEG_START_RESERVED_1 0xb0
+#define MPEG_START_RESERVED_2 0xb1
+#define MPEG_START_USER_DATA 0xb2
+#define MPEG_START_SEQUENCE_HEADER 0xb3
+#define MPEG_START_SEQUENCE_ERROR 0xb4
+#define MPEG_START_EXTENSION 0xb5
+#define MPEG_START_RESERVED_3 0xb6
+#define MPEG_START_SEQUENCE_END 0xb7
+#define MPEG_START_GOP 0xb8
+
+/* Stream IDs */
+#define MPEG_STREAM_PROGRAM_END 0xb9
+#define MPEG_STREAM_PACK_HEADER 0xba
+#define MPEG_STREAM_SYSTEM_HEADER 0xbb
+#define MPEG_STREAM_PROGRAM_STREAM_MAP 0xbc
+#define MPEG_STREAM_PRIVATE_1 0xbd
+#define MPEG_STREAM_PADDING 0xbe
+#define MPEG_STREAM_PRIVATE_2 0xbf
+#define MPEG_STREAM_AUDIO_FIRST 0xc0
+#define MPEG_STREAM_AUDIO_LAST 0xcf
+#define MPEG_STREAM_VIDEO_FIRST 0xe0
+#define MPEG_STREAM_VIDEO_LAST 0xef
+#define MPEG_STREAM_ECM 0xf0
+#define MPEG_STREAM_EMM 0xf1
+/* ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A or
+ * ISO/IEC 13818-6_DSMCC_stream */
+#define MPEG_STREAM_MISC_1 0xf2
+/* ISO/IEC_13522_stream */
+#define MPEG_STREAM_MISC_2 0xf3
+/* ITU-T Rec. H.222.1 type A - E */
+#define MPEG_STREAM_MISC_3 0xf4
+#define MPEG_STREAM_MISC_4 0xf5
+#define MPEG_STREAM_MISC_5 0xf6
+#define MPEG_STREAM_MISC_6 0xf7
+#define MPEG_STREAM_MISC_7 0xf8
+#define MPEG_STREAM_ANCILLARY 0xf9
+#define MPEG_STREAM_RESERVED_FIRST 0xfa
+#define MPEG_STREAM_RESERVED_LAST 0xfe
+/* Program stream directory */
+#define MPEG_STREAM_PROGRAM_DIRECTORY 0xff
+
+#define STREAM_IS_AUDIO(s) (((s) & 0xf0) == 0xc0)
+#define STREAM_IS_VIDEO(s) (((s) & 0xf0) == 0xe0)
+
+#define MPEG_MAX_PACKET_SIZE (64*1024+16)
+
+/* Largest MPEG audio frame - MPEG1, Layer II, 384kbps, 32kHz, pad */
+#define MPA_MAX_FRAME_SIZE 1729
+
+#endif /* MPEG_STREAM_H */
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
new file mode 100644
index 0000000000..e66b4df146
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -0,0 +1,2638 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * mpegplayer main entrypoint and UI implementation
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * NOTES:
+ *
+ * mpegplayer is structured as follows:
+ *
+ * +-->Video Thread-->Video Output-->LCD
+ * |
+ * UI-->Stream Manager-->+-->Audio Thread-->PCM buffer--Audio Device
+ * | | | | (ref. clock)
+ * | | +-->Buffer Thread |
+ * Stream Data | | (clock intf./
+ * Requests | File Cache drift adj.)
+ * | Disk I/O
+ * Stream services
+ * (timing, etc.)
+ *
+ * Thread list:
+ * 1) The main thread - Handles user input, settings, basic playback control
+ * and USB connect.
+ *
+ * 2) Stream Manager thread - Handles playback state, events from streams
+ * such as when a stream is finished, stream commands, PCM state. The
+ * layer in which this thread run also handles arbitration of data
+ * requests between the streams and the disk buffer. The actual specific
+ * transport layer code may get moved out to support multiple container
+ * formats.
+ *
+ * 3) Buffer thread - Buffers data in the background, generates notifications
+ * to streams when their data has been buffered, and watches streams'
+ * progress to keep data available during playback. Handles synchronous
+ * random access requests when the file cache is missed.
+ *
+ * 4) Video thread (running on the COP for PortalPlayer targets) - Decodes
+ * the video stream and renders video frames to the LCD. Handles
+ * miscellaneous video tasks like frame and thumbnail printing.
+ *
+ * 5) Audio thread (running on the main CPU to maintain consistency with the
+ * audio FIQ hander on PP) - Decodes audio frames and places them into
+ * the PCM buffer for rendering by the audio device.
+ *
+ * Streams are neither aware of one another nor care about one another. All
+ * streams shall have their own thread (unless it is _really_ efficient to
+ * have a single thread handle a couple minor streams). All coordination of
+ * the streams is done through the stream manager. The clocking is controlled
+ * by and exposed by the stream manager to other streams and implemented at
+ * the PCM level.
+ *
+ * Notes about MPEG files:
+ *
+ * MPEG System Clock is 27MHz - i.e. 27000000 ticks/second.
+ *
+ * FPS is represented in terms of a frame period - this is always an
+ * integer number of 27MHz ticks.
+ *
+ * e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of
+ * 900900 27MHz ticks.
+ *
+ * In libmpeg2, info->sequence->frame_period contains the frame_period.
+ *
+ * Working with Rockbox's 100Hz tick, the common frame rates would need
+ * to be as follows (1):
+ *
+ * FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz
+ * --------|-----------------------------------------------------------
+ * 10* | 2700000 | 10 | 4410 | 4800
+ * 12* | 2250000 | 8.3333 | 3675 | 4000
+ * 15* | 1800000 | 6.6667 | 2940 | 3200
+ * 23.9760 | 1126125 | 4.170833333 | 1839.3375 | 2002
+ * 24 | 1125000 | 4.166667 | 1837.5 | 2000
+ * 25 | 1080000 | 4 | 1764 | 1920
+ * 29.9700 | 900900 | 3.336667 | 1471,47 | 1601.6
+ * 30 | 900000 | 3.333333 | 1470 | 1600
+ *
+ * *Unofficial framerates
+ *
+ * (1) But we don't really care since the audio clock is used anyway and has
+ * very fine resolution ;-)
+ *****************************************************************************/
+#include "plugin.h"
+#include "mpegplayer.h"
+#include "lib/helper.h"
+#include "mpeg_settings.h"
+#include "video_out.h"
+#include "stream_thread.h"
+#include "stream_mgr.h"
+
+
+/* button definitions */
+#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#define MPEG_MENU BUTTON_MODE
+#define MPEG_STOP BUTTON_OFF
+#define MPEG_PAUSE BUTTON_ON
+#define MPEG_VOLDOWN BUTTON_DOWN
+#define MPEG_VOLUP BUTTON_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_1G2G_PAD)
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
+#define MPEG_VOLDOWN BUTTON_SCROLL_BACK
+#define MPEG_VOLUP BUTTON_SCROLL_FWD
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
+#define MPEG_MENU (BUTTON_REC | BUTTON_REL)
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_DOWN
+#define MPEG_VOLUP BUTTON_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == GIGABEAT_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_SELECT
+#define MPEG_PAUSE2 BUTTON_A
+#define MPEG_VOLDOWN BUTTON_LEFT
+#define MPEG_VOLUP BUTTON_RIGHT
+#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
+#define MPEG_VOLUP2 BUTTON_VOL_UP
+#define MPEG_RW BUTTON_UP
+#define MPEG_FF BUTTON_DOWN
+
+#define MPEG_RC_MENU BUTTON_RC_DSP
+#define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
+#define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
+#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
+#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
+#define MPEG_RC_RW BUTTON_RC_REW
+#define MPEG_RC_FF BUTTON_RC_FF
+
+#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_SELECT
+#define MPEG_PAUSE2 BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_LEFT
+#define MPEG_VOLUP BUTTON_RIGHT
+#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
+#define MPEG_VOLUP2 BUTTON_VOL_UP
+#define MPEG_RW BUTTON_UP
+#define MPEG_RW2 BUTTON_PREV
+#define MPEG_FF BUTTON_DOWN
+#define MPEG_FF2 BUTTON_NEXT
+#define MPEG_SHOW_OSD BUTTON_BACK
+
+#define MPEG_RC_MENU BUTTON_RC_DSP
+#define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
+#define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
+#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
+#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
+#define MPEG_RC_RW BUTTON_RC_REW
+#define MPEG_RC_FF BUTTON_RC_FF
+
+#elif CONFIG_KEYPAD == IRIVER_H10_PAD
+#define MPEG_MENU BUTTON_LEFT
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
+#define MPEG_VOLUP BUTTON_SCROLL_UP
+#define MPEG_RW BUTTON_REW
+#define MPEG_FF BUTTON_FF
+
+#elif CONFIG_KEYPAD == SANSA_E200_PAD
+#define MPEG_MENU BUTTON_SELECT
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_RIGHT
+#define MPEG_VOLDOWN BUTTON_SCROLL_BACK
+#define MPEG_VOLUP BUTTON_SCROLL_FWD
+#define MPEG_RW BUTTON_UP
+#define MPEG_FF BUTTON_DOWN
+
+#elif CONFIG_KEYPAD == SANSA_FUZE_PAD
+#define MPEG_MENU BUTTON_SELECT
+#define MPEG_STOP (BUTTON_HOME|BUTTON_REPEAT)
+#define MPEG_PAUSE BUTTON_UP
+#define MPEG_VOLDOWN BUTTON_SCROLL_BACK
+#define MPEG_VOLUP BUTTON_SCROLL_FWD
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+
+#elif CONFIG_KEYPAD == SANSA_C200_PAD || \
+CONFIG_KEYPAD == SANSA_CLIP_PAD || \
+CONFIG_KEYPAD == SANSA_M200_PAD
+#define MPEG_MENU BUTTON_SELECT
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_UP
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == MROBE500_PAD
+#define MPEG_STOP BUTTON_POWER
+
+#define MPEG_RC_MENU BUTTON_RC_HEART
+#define MPEG_RC_STOP BUTTON_RC_DOWN
+#define MPEG_RC_PAUSE BUTTON_RC_PLAY
+#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
+#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
+#define MPEG_RC_RW BUTTON_RC_REW
+#define MPEG_RC_FF BUTTON_RC_FF
+
+#elif CONFIG_KEYPAD == MROBE100_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_DOWN
+#define MPEG_VOLUP BUTTON_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
+#define MPEG_MENU BUTTON_RC_MENU
+#define MPEG_STOP BUTTON_RC_REC
+#define MPEG_PAUSE BUTTON_RC_PLAY
+#define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
+#define MPEG_VOLUP BUTTON_RC_VOL_UP
+#define MPEG_RW BUTTON_RC_REW
+#define MPEG_FF BUTTON_RC_FF
+
+#elif CONFIG_KEYPAD == COWON_D2_PAD
+#define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
+//#define MPEG_STOP BUTTON_POWER
+#define MPEG_VOLDOWN BUTTON_MINUS
+#define MPEG_VOLUP BUTTON_PLUS
+
+#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_BACK
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_UP
+#define MPEG_VOLUP BUTTON_DOWN
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP (BUTTON_PLAY|BUTTON_REPEAT)
+#define MPEG_PAUSE (BUTTON_PLAY|BUTTON_REL)
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_DOWN
+#define MPEG_FF BUTTON_UP
+
+#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_SELECT
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_PREV
+#define MPEG_FF BUTTON_NEXT
+
+#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_UP
+#define MPEG_FF BUTTON_DOWN
+
+#elif CONFIG_KEYPAD == ONDAVX747_PAD
+#define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
+//#define MPEG_STOP BUTTON_POWER
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+
+#elif CONFIG_KEYPAD == ONDAVX777_PAD
+#define MPEG_MENU BUTTON_POWER
+
+#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
+ (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
+#define MPEG_MENU BUTTON_REW
+#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
+#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define MPEG_VOLDOWN BUTTON_DOWN
+#define MPEG_VOLUP BUTTON_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+#define MPEG_SHOW_OSD BUTTON_FFWD
+
+#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_REC
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_DOWN
+#define MPEG_VOLUP BUTTON_UP
+#define MPEG_RW BUTTON_PREV
+#define MPEG_FF BUTTON_NEXT
+
+#elif CONFIG_KEYPAD == MPIO_HD200_PAD
+#define MPEG_MENU BUTTON_FUNC
+#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_REW
+#define MPEG_FF BUTTON_FF
+
+#elif CONFIG_KEYPAD == MPIO_HD300_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
+#define MPEG_VOLDOWN BUTTON_DOWN
+#define MPEG_VOLUP BUTTON_UP
+#define MPEG_RW BUTTON_REW
+#define MPEG_FF BUTTON_FF
+
+#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
+#define MPEG_MENU BUTTON_POWER
+#define MPEG_PAUSE (BUTTON_PLAYPAUSE | BUTTON_REL)
+#define MPEG_STOP (BUTTON_PLAYPAUSE | BUTTON_REPEAT)
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
+#define MPEG_MENU BUTTON_POWER
+#define MPEG_PAUSE (BUTTON_SELECT | BUTTON_REL)
+#define MPEG_STOP (BUTTON_SELECT | BUTTON_REPEAT)
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_PAUSE BUTTON_SELECT
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_VOLDOWN BUTTON_DOWN
+#define MPEG_VOLUP BUTTON_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == HM60X_PAD
+#define MPEG_MENU BUTTON_POWER
+#define MPEG_PAUSE BUTTON_SELECT
+#define MPEG_STOP (BUTTON_SELECT | BUTTON_POWER)
+#define MPEG_VOLDOWN (BUTTON_POWER | BUTTON_DOWN)
+#define MPEG_VOLUP (BUTTON_POWER | BUTTON_UP)
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == HM801_PAD
+#define MPEG_MENU BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_STOP (BUTTON_POWER | BUTTON_PLAY)
+#define MPEG_VOLDOWN (BUTTON_POWER | BUTTON_DOWN)
+#define MPEG_VOLUP (BUTTON_POWER | BUTTON_UP)
+#define MPEG_RW BUTTON_PREV
+#define MPEG_FF BUTTON_NEXT
+
+#elif CONFIG_KEYPAD == SONY_NWZ_PAD
+#define MPEG_MENU BUTTON_BACK
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_VOLDOWN BUTTON_UP
+#define MPEG_VOLUP BUTTON_DOWN
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_PAUSE BUTTON_PLAYPAUSE
+#define MPEG_STOP BUTTON_BACK
+#define MPEG_VOLDOWN BUTTON_DOWN
+#define MPEG_VOLUP BUTTON_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == DX50_PAD
+#define MPEG_MENU BUTTON_POWER
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_STOP (BUTTON_PLAY|BUTTON_REPEAT)
+
+#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
+#define MPEG_MENU BUTTON_POWER
+#define MPEG_PAUSE BUTTON_MENU
+#define MPEG_STOP (BUTTON_MENU|BUTTON_REPEAT)
+
+#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD
+#define MPEG_MENU BUTTON_POWER
+#define MPEG_PAUSE BUTTON_SELECT
+#define MPEG_STOP BUTTON_DOWN
+#define MPEG_VOLDOWN BUTTON_VOLDOWN
+#define MPEG_VOLUP BUTTON_VOLUP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == XDUOO_X3_PAD
+#define MPEG_MENU BUTTON_PLAY
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_HOME
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_PREV
+#define MPEG_FF BUTTON_NEXT
+
+#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
+#define MPEG_MENU BUTTON_PLAY
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_HOME
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_PREV
+#define MPEG_FF BUTTON_NEXT
+
+#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
+#define MPEG_MENU BUTTON_PLAY
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_HOME
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_PREV
+#define MPEG_FF BUTTON_NEXT
+
+#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
+#define MPEG_MENU BUTTON_PLAY
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_HOME
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_PREV
+#define MPEG_FF BUTTON_NEXT
+
+#elif CONFIG_KEYPAD == EROSQ_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_PREV
+#define MPEG_FF BUTTON_NEXT
+
+#elif CONFIG_KEYPAD == FIIO_M3K_PAD
+#define MPEG_MENU BUTTON_MENU
+#define MPEG_STOP BUTTON_POWER
+#define MPEG_PAUSE BUTTON_PLAY
+#define MPEG_VOLDOWN BUTTON_VOL_DOWN
+#define MPEG_VOLUP BUTTON_VOL_UP
+#define MPEG_RW BUTTON_LEFT
+#define MPEG_FF BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
+#else
+#error No keymap defined!
+#endif
+
+#ifdef HAVE_TOUCHSCREEN
+#ifndef MPEG_MENU
+#define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
+#endif
+#ifndef MPEG_STOP
+#define MPEG_STOP BUTTON_TOPLEFT
+#endif
+#ifndef MPEG_PAUSE
+#define MPEG_PAUSE BUTTON_CENTER
+#endif
+#ifndef MPEG_VOLDOWN
+#define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
+#endif
+#ifndef MPEG_VOLUP
+#define MPEG_VOLUP BUTTON_TOPMIDDLE
+#endif
+#ifndef MPEG_RW
+#define MPEG_RW BUTTON_MIDLEFT
+#endif
+#ifndef MPEG_FF
+#define MPEG_FF BUTTON_MIDRIGHT
+#endif
+#endif
+
+/* One thing we can do here for targets with remotes is having a display
+ * always on the remote instead of always forcing a popup on the main display */
+
+#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
+ /* 3% of 30min file == 54s step size */
+#define MIN_FF_REWIND_STEP (TS_SECOND/2)
+#define OSD_MIN_UPDATE_INTERVAL (HZ/2)
+#define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */
+
+enum video_action
+{
+ VIDEO_STOP = 0,
+ VIDEO_PREV,
+ VIDEO_NEXT,
+ VIDEO_ACTION_MANUAL = 0x8000, /* Flag that says user did it */
+};
+
+/* OSD status - same order as icon array */
+enum osd_status_enum
+{
+ OSD_STATUS_STOPPED = 0,
+ OSD_STATUS_PAUSED,
+ OSD_STATUS_PLAYING,
+ OSD_STATUS_FF,
+ OSD_STATUS_RW,
+ OSD_STATUS_COUNT,
+ OSD_STATUS_MASK = 0x7
+};
+
+enum osd_bits
+{
+ OSD_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
+ /* Refresh the... */
+ OSD_REFRESH_VOLUME = 0x0001, /* ...volume display */
+ OSD_REFRESH_TIME = 0x0002, /* ...time display+progress */
+ OSD_REFRESH_STATUS = 0x0004, /* ...playback status icon */
+ OSD_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
+ OSD_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
+ OSD_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
+ OSD_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
+ OSD_SHOW = 0x4000, /* OR bitflag - show the OSD */
+#ifdef HAVE_HEADPHONE_DETECTION
+ OSD_HP_PAUSE = 0x2000, /* OR bitflag - headphones caused pause */
+#endif
+ OSD_HIDE = 0x0000, /* hide the OSD (aid readability) */
+ OSD_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
+};
+
+/* Status icons selected according to font height */
+extern const unsigned char mpegplayer_status_icons_8x8x1[];
+extern const unsigned char mpegplayer_status_icons_12x12x1[];
+extern const unsigned char mpegplayer_status_icons_16x16x1[];
+
+/* Main border areas that contain OSD elements */
+#define OSD_BDR_L 2
+#define OSD_BDR_T 2
+#define OSD_BDR_R 2
+#define OSD_BDR_B 2
+
+struct osd
+{
+ long hide_tick;
+ long show_for;
+ long print_tick;
+ long print_delay;
+ long resume_tick;
+ long resume_delay;
+ long next_auto_refresh;
+ int x;
+ int y;
+ int width;
+ int height;
+ unsigned fgcolor;
+ unsigned bgcolor;
+ unsigned prog_fillcolor;
+ struct vo_rect update_rect;
+ struct vo_rect prog_rect;
+ struct vo_rect time_rect;
+ struct vo_rect dur_rect;
+ struct vo_rect vol_rect;
+ const unsigned char *icons;
+ struct vo_rect stat_rect;
+ int status;
+ uint32_t curr_time;
+ unsigned auto_refresh;
+ unsigned flags;
+ int font;
+};
+
+struct fps
+{
+ /* FPS Display */
+ struct vo_rect rect; /* OSD coordinates */
+ int pf_x; /* Screen coordinates */
+ int pf_y;
+ int pf_width;
+ int pf_height;
+ long update_tick; /* When to next update FPS reading */
+ #define FPS_FORMAT "%d.%02d"
+ #define FPS_DIMSTR "999.99" /* For establishing rect size */
+ #define FPS_BUFSIZE sizeof("999.99")
+};
+
+static struct osd osd;
+static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */
+
+#ifdef LCD_PORTRAIT
+static fb_data* get_framebuffer(void)
+{
+ struct viewport *vp_main = *(rb->screens[SCREEN_MAIN]->current_viewport);
+ return vp_main->buffer->fb_ptr;
+}
+#endif
+
+static void osd_show(unsigned show);
+
+#ifdef LCD_LANDSCAPE
+ #define __X (x + osd.x)
+ #define __Y (y + osd.y)
+ #define __W width
+ #define __H height
+#else
+ #define __X (LCD_WIDTH - (y + osd.y) - height)
+ #define __Y (x + osd.x)
+ #define __W height
+ #define __H width
+#endif
+
+#ifdef HAVE_LCD_COLOR
+/* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
+static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
+{
+ int r1 = RGB_UNPACK_RED(c1);
+ int g1 = RGB_UNPACK_GREEN(c1);
+ int b1 = RGB_UNPACK_BLUE(c1);
+
+ int r2 = RGB_UNPACK_RED(c2);
+ int g2 = RGB_UNPACK_GREEN(c2);
+ int b2 = RGB_UNPACK_BLUE(c2);
+
+ return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
+ amount*(g2 - g1) / 255 + g1,
+ amount*(b2 - b1) / 255 + b1);
+}
+#endif
+
+#ifdef PLUGIN_USE_IRAM
+/* IRAM preserving mechanism to enable talking menus */
+static char *iram_saved_copy;
+extern char iramstart[], iramend[];
+
+static void iram_saving_init(void)
+{
+#ifndef SIMULATOR
+ size_t size;
+ iram_saved_copy = (char *)rb->plugin_get_buffer(&size);
+
+ if (size >= (size_t)(iramend-iramstart))
+ iram_saved_copy += size - (size_t)(iramend - iramstart);
+ else
+#endif
+ iram_saved_copy = NULL;
+
+ return;
+}
+
+void mpegplayer_iram_preserve(void)
+{
+ if (iram_saved_copy)
+ {
+ rb->memcpy(iram_saved_copy, iramstart, iramend-iramstart);
+#ifdef HAVE_CPUCACHE_INVALIDATE
+ /* make the icache (if it exists) up to date with the new code */
+ rb->cpucache_invalidate();
+#endif /* HAVE_CPUCACHE_INVALIDATE */
+ }
+ return;
+}
+
+void mpegplayer_iram_restore(void)
+{
+ if (iram_saved_copy)
+ {
+ rb->audio_hard_stop();
+ rb->memcpy(iramstart, iram_saved_copy, iramend-iramstart);
+#ifdef HAVE_CPUCACHE_INVALIDATE
+ /* make the icache (if it exists) up to date with the new code */
+ rb->cpucache_invalidate();
+#endif /* HAVE_CPUCACHE_INVALIDATE */
+ }
+ return;
+}
+#endif
+
+/* Drawing functions that operate rotated on LCD_PORTRAIT displays -
+ * most are just wrappers of lcd_* functions with transforms applied.
+ * The origin is the upper-left corner of the OSD area */
+static void draw_update_rect(int x, int y, int width, int height)
+{
+ mylcd_update_rect(__X, __Y, __W, __H);
+}
+
+static void draw_clear_area(int x, int y, int width, int height)
+{
+#ifdef HAVE_LCD_COLOR
+ rb->screen_clear_area(rb->screens[SCREEN_MAIN], __X, __Y, __W, __H);
+#else
+ int oldmode = grey_get_drawmode();
+ grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+ grey_fillrect(__X, __Y, __W, __H);
+ grey_set_drawmode(oldmode);
+#endif
+}
+
+static void draw_clear_area_rect(const struct vo_rect *rc)
+{
+ draw_clear_area(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t);
+}
+
+static void draw_fillrect(int x, int y, int width, int height)
+{
+ mylcd_fillrect(__X, __Y, __W, __H);
+}
+
+static void draw_hline(int x1, int x2, int y)
+{
+#ifdef LCD_LANDSCAPE
+ mylcd_hline(x1 + osd.x, x2 + osd.x, y + osd.y);
+#else
+ y = LCD_WIDTH - (y + osd.y) - 1;
+ mylcd_vline(y, x1 + osd.x, x2 + osd.x);
+#endif
+}
+
+static void draw_vline(int x, int y1, int y2)
+{
+#ifdef LCD_LANDSCAPE
+ mylcd_vline(x + osd.x, y1 + osd.y, y2 + osd.y);
+#else
+ y1 = LCD_WIDTH - (y1 + osd.y) - 1;
+ y2 = LCD_WIDTH - (y2 + osd.y) - 1;
+ mylcd_hline(y1, y2, x + osd.x);
+#endif
+}
+
+static void draw_scrollbar_draw(int x, int y, int width, int height,
+ uint32_t min, uint32_t max, uint32_t val)
+{
+ unsigned oldfg = mylcd_get_foreground();
+
+ draw_hline(x + 1, x + width - 2, y);
+ draw_hline(x + 1, x + width - 2, y + height - 1);
+ draw_vline(x, y + 1, y + height - 2);
+ draw_vline(x + width - 1, y + 1, y + height - 2);
+
+ val = muldiv_uint32(width - 2, val, max - min);
+ val = MIN(val, (uint32_t)(width - 2));
+
+ draw_fillrect(x + 1, y + 1, val, height - 2);
+
+ mylcd_set_foreground(osd.prog_fillcolor);
+
+ draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
+
+ mylcd_set_foreground(oldfg);
+}
+
+static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
+ int max, int val)
+{
+ draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
+ min, max, val);
+}
+
+static void draw_setfont(int font)
+{
+ osd.font = font;
+ mylcd_setfont(font);
+}
+
+#ifdef LCD_PORTRAIT
+/* Portrait displays need rotated text rendering */
+
+/* Limited function that only renders in DRMODE_FG and uses absolute screen
+ * coordinates */
+static void draw_oriented_mono_bitmap_part(const unsigned char *src,
+ int src_x, int src_y,
+ int stride, int x, int y,
+ int width, int height)
+{
+ const unsigned char *src_end;
+ fb_data *dst, *dst_end;
+ unsigned fg_pattern;
+
+ if (x + width > SCREEN_WIDTH)
+ width = SCREEN_WIDTH - x; /* Clip right */
+ if (x < 0)
+ width += x, x = 0; /* Clip left */
+ if (width <= 0)
+ return; /* nothing left to do */
+
+ if (y + height > SCREEN_HEIGHT)
+ height = SCREEN_HEIGHT - y; /* Clip bottom */
+ if (y < 0)
+ height += y, y = 0; /* Clip top */
+ if (height <= 0)
+ return; /* nothing left to do */
+
+ fg_pattern = rb->lcd_get_foreground();
+ /*bg_pattern =*/ rb->lcd_get_background();
+
+ src += stride * (src_y >> 3) + src_x; /* move starting point */
+ src_y &= 7;
+ src_end = src + width;
+
+ dst = get_framebuffer() + (LCD_WIDTH - y) + x*LCD_WIDTH;
+ do
+ {
+ const unsigned char *src_col = src++;
+ unsigned data = *src_col >> src_y;
+ int numbits = 8 - src_y;
+
+ fb_data *dst_col = dst;
+ dst_end = dst_col - height;
+ dst += LCD_WIDTH;
+
+ do
+ {
+ dst_col--;
+
+ if (data & 1)
+ *dst_col = FB_SCALARPACK(fg_pattern);
+#if 0
+ else
+ *dst_col = bg_pattern;
+#endif
+ data >>= 1;
+ if (--numbits == 0) {
+ src_col += stride;
+ data = *src_col;
+ numbits = 8;
+ }
+ }
+ while (dst_col > dst_end);
+ }
+ while (src < src_end);
+}
+
+
+#define ALPHA_COLOR_FONT_DEPTH 2
+#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
+#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
+#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
+#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
+#ifdef CPU_ARM
+#define BLEND_INIT do {} while (0)
+#define BLEND_FINISH do {} while(0)
+#define BLEND_START(acc, color, alpha) \
+ asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha))
+#define BLEND_CONT(acc, color, alpha) \
+ asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
+#define BLEND_OUT(acc) do {} while (0)
+#elif defined(CPU_COLDFIRE)
+#define ALPHA_BITMAP_READ_WORDS
+#define BLEND_INIT \
+ unsigned long _macsr = coldfire_get_macsr(); \
+ coldfire_set_macsr(EMAC_UNSIGNED)
+#define BLEND_FINISH \
+ coldfire_set_macsr(_macsr)
+#define BLEND_START(acc, color, alpha) \
+ asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
+#define BLEND_CONT BLEND_START
+#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
+#else
+#define BLEND_INIT do {} while (0)
+#define BLEND_FINISH do {} while(0)
+#define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha))
+#define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha))
+#define BLEND_OUT(acc) do {} while (0)
+#endif
+
+/* Blend the given two colors */
+static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
+{
+#if LCD_DEPTH == 16
+ a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
+#if (LCD_PIXELFORMAT == RGB565SWAPPED)
+ c1 = swap16(c1);
+ c2 = swap16(c2);
+#endif
+ unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f;
+ unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
+ unsigned p;
+ BLEND_START(p, c1l, a);
+ BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
+ BLEND_OUT(p);
+ p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
+ p |= (p >> 16);
+#if (LCD_PIXELFORMAT == RGB565SWAPPED)
+ return swap16(p);
+#else
+ return p;
+#endif
+
+#else /* LCD_DEPTH == 24 */
+ unsigned s = c1;
+ unsigned d = c2;
+ unsigned s1 = s & 0xff00ff;
+ unsigned d1 = d & 0xff00ff;
+ a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
+ d1 = (d1 + ((s1 - d1) * a >> ALPHA_COLOR_LOOKUP_SHIFT)) & 0xff00ff;
+ s &= 0xff00;
+ d &= 0xff00;
+ d = (d + ((s - d) * a >> ALPHA_COLOR_LOOKUP_SHIFT)) & 0xff00;
+
+ return d1 | d;
+#endif
+}
+
+static void draw_oriented_alpha_bitmap_part(const unsigned char *src,
+ int src_x, int src_y,
+ int stride, int x, int y,
+ int width, int height)
+{
+ fb_data *dst, *dst_start;
+ unsigned fg_pattern;
+
+ if (x + width > SCREEN_WIDTH)
+ width = SCREEN_WIDTH - x; /* Clip right */
+ if (x < 0)
+ width += x, x = 0; /* Clip left */
+ if (width <= 0)
+ return; /* nothing left to do */
+
+ if (y + height > SCREEN_HEIGHT)
+ height = SCREEN_HEIGHT - y; /* Clip bottom */
+ if (y < 0)
+ height += y, y = 0; /* Clip top */
+ if (height <= 0)
+ return; /* nothing left to do */
+
+ /* initialize blending */
+ BLEND_INIT;
+
+ fg_pattern = rb->lcd_get_foreground();
+ /*bg_pattern=*/ rb->lcd_get_background();
+
+ dst_start = get_framebuffer() + (LCD_WIDTH - y - 1) + x*LCD_WIDTH;
+ int col, row = height;
+ unsigned data, pixels;
+ unsigned skip_end = (stride - width);
+ unsigned skip_start = src_y * stride + src_x;
+
+#ifdef ALPHA_BITMAP_READ_WORDS
+ uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
+ skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
+ src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
+ data = letoh32(*src_w++);
+#else
+ src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
+ data = *src;
+#endif
+ pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
+ data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
+#ifdef ALPHA_BITMAP_READ_WORDS
+ pixels = 8 - pixels;
+#endif
+
+ do
+ {
+ col = width;
+ dst = dst_start--;
+#ifdef ALPHA_BITMAP_READ_WORDS
+#define UPDATE_SRC_ALPHA do { \
+ if (--pixels) \
+ data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
+ else \
+ { \
+ data = letoh32(*src_w++); \
+ pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
+ } \
+ } while (0)
+#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
+#define UPDATE_SRC_ALPHA do { \
+ if (pixels ^= 1) \
+ data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
+ else \
+ data = *(++src); \
+ } while (0)
+#else
+#define UPDATE_SRC_ALPHA do { \
+ if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
+ data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
+ else \
+ data = *(++src); \
+ } while (0)
+#endif
+ do
+ {
+ unsigned color = blend_two_colors(FB_UNPACK_SCALAR_LCD(*dst), fg_pattern,
+ data & ALPHA_COLOR_LOOKUP_SIZE );
+ *dst= FB_SCALARPACK(color);
+ dst += LCD_WIDTH;
+ UPDATE_SRC_ALPHA;
+ }
+ while (--col);
+#ifdef ALPHA_BITMAP_READ_WORDS
+ if (skip_end < pixels)
+ {
+ pixels -= skip_end;
+ data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
+ } else {
+ pixels = skip_end - pixels;
+ src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
+ pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
+ data = letoh32(*src_w++);
+ data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
+ pixels = 8 - pixels;
+ }
+#else
+ if (skip_end)
+ {
+ pixels += skip_end;
+ if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
+ {
+ src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
+ pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
+ data = *src;
+ data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
+ } else
+ data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
+ }
+#endif
+ } while (--row);
+}
+
+static void draw_putsxy_oriented(int x, int y, const char *str)
+{
+ unsigned short ch;
+ unsigned short *ucs;
+ int ofs = MIN(x, 0);
+ struct font* pf = rb->font_get(osd.font);
+
+ ucs = rb->bidi_l2v(str, 1);
+
+ x += osd.x;
+ y += osd.y;
+
+ while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
+ {
+ int width;
+ const unsigned char *bits;
+
+ /* get proportional width and glyph bits */
+ width = rb->font_get_width(pf, ch);
+
+ if (ofs > width) {
+ ofs -= width;
+ continue;
+ }
+
+ bits = rb->font_get_bits(pf, ch);
+
+ if (pf->depth)
+ draw_oriented_alpha_bitmap_part(bits, ofs, 0, width, x, y,
+ width - ofs, pf->height);
+ else
+ draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
+ width - ofs, pf->height);
+
+ x += width - ofs;
+ ofs = 0;
+ }
+}
+#else
+static void draw_oriented_mono_bitmap_part(const unsigned char *src,
+ int src_x, int src_y,
+ int stride, int x, int y,
+ int width, int height)
+{
+ int mode = mylcd_get_drawmode();
+ mylcd_set_drawmode(DRMODE_FG);
+ mylcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height);
+ mylcd_set_drawmode(mode);
+}
+
+static void draw_putsxy_oriented(int x, int y, const char *str)
+{
+ int mode = mylcd_get_drawmode();
+ mylcd_set_drawmode(DRMODE_FG);
+ mylcd_putsxy(x + osd.x, y + osd.y, str);
+ mylcd_set_drawmode(mode);
+}
+#endif /* LCD_PORTRAIT */
+
+/** FPS Display **/
+
+/* Post-frame callback (on video thread) - update the FPS rectangle from the
+ * framebuffer */
+static void fps_post_frame_callback(void)
+{
+ vo_lock();
+ mylcd_update_rect(fps.pf_x, fps.pf_y,
+ fps.pf_width, fps.pf_height);
+ vo_unlock();
+}
+
+/* Set up to have the callback only update the intersection of the video
+ * rectangle and the FPS text rectangle - if they don't intersect, then
+ * the callback is set to NULL */
+static void fps_update_post_frame_callback(void)
+{
+ void (*cb)(void) = NULL;
+
+ if (settings.showfps) {
+ struct vo_rect cliprect;
+
+ if (stream_vo_get_clip(&cliprect)) {
+ /* Oriented screen coordinates -> OSD coordinates */
+ vo_rect_offset(&cliprect, -osd.x, -osd.y);
+
+ if (vo_rect_intersect(&cliprect, &cliprect, &fps.rect)) {
+ int x = cliprect.l;
+ int y = cliprect.t;
+ int width = cliprect.r - cliprect.l;
+ int height = cliprect.b - cliprect.t;
+
+ /* OSD coordinates -> framebuffer coordinates */
+ fps.pf_x = __X;
+ fps.pf_y = __Y;
+ fps.pf_width = __W;
+ fps.pf_height = __H;
+
+ cb = fps_post_frame_callback;
+ }
+ }
+ }
+
+ stream_set_callback(VIDEO_SET_POST_FRAME_CALLBACK, cb);
+}
+
+/* Refresh the FPS display */
+static void fps_refresh(void)
+{
+ char str[FPS_BUFSIZE];
+ struct video_output_stats stats;
+ int w, h, sw;
+ long tick;
+
+ tick = *rb->current_tick;
+
+ if (TIME_BEFORE(tick, fps.update_tick))
+ return;
+
+ fps.update_tick = tick + FPS_UPDATE_INTERVAL;
+
+ stream_video_stats(&stats);
+
+ rb->snprintf(str, FPS_BUFSIZE, FPS_FORMAT,
+ stats.fps / 100, stats.fps % 100);
+
+ w = fps.rect.r - fps.rect.l;
+ h = fps.rect.b - fps.rect.t;
+
+ draw_clear_area(fps.rect.l, fps.rect.t, w, h);
+ mylcd_getstringsize(str, &sw, NULL);
+ draw_putsxy_oriented(fps.rect.r - sw, fps.rect.t, str);
+
+ vo_lock();
+ draw_update_rect(fps.rect.l, fps.rect.t, w, h);
+ vo_unlock();
+}
+
+/* Initialize the FPS display */
+static void fps_init(void)
+{
+ fps.update_tick = *rb->current_tick;
+ fps.rect.l = fps.rect.t = 0;
+ mylcd_getstringsize(FPS_DIMSTR, &fps.rect.r, &fps.rect.b);
+ vo_rect_offset(&fps.rect, -osd.x, -osd.y);
+ fps_update_post_frame_callback();
+}
+
+/** OSD **/
+
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+/* So we can refresh the overlay */
+static void osd_lcd_enable_hook(unsigned short id, void* param)
+{
+ (void)id;
+ (void)param;
+ rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
+}
+#endif
+
+static void osdbacklight_hw_on_video_mode(bool video_on)
+{
+ if (video_on) {
+#ifdef HAVE_BACKLIGHT
+ /* Turn off backlight timeout */
+ backlight_ignore_timeout();
+#endif
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+ rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
+#endif
+ } else {
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+ rb->add_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
+#endif
+#ifdef HAVE_BACKLIGHT
+ /* Revert to user's backlight settings */
+ backlight_use_settings();
+#endif
+ }
+}
+
+#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+static void osd_backlight_brightness_video_mode(bool video_on)
+{
+ if (settings.backlight_brightness < 0)
+ return;
+
+ mpeg_backlight_update_brightness(
+ video_on ? settings.backlight_brightness : -1);
+}
+#else
+#define osd_backlight_brightness_video_mode(video_on)
+#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
+
+static void osd_text_init(void)
+{
+ struct hms hms;
+ char buf[32];
+ int phys;
+ int spc_width;
+
+ draw_setfont(FONT_UI);
+
+ osd.x = 0;
+ osd.width = SCREEN_WIDTH;
+
+ vo_rect_clear(&osd.time_rect);
+ vo_rect_clear(&osd.stat_rect);
+ vo_rect_clear(&osd.prog_rect);
+ vo_rect_clear(&osd.vol_rect);
+
+ ts_to_hms(stream_get_duration(), &hms);
+ hms_format(buf, sizeof (buf), &hms);
+ mylcd_getstringsize(buf, &osd.time_rect.r, &osd.time_rect.b);
+
+ /* Choose well-sized bitmap images relative to font height */
+ if (osd.time_rect.b < 12) {
+ osd.icons = mpegplayer_status_icons_8x8x1;
+ osd.stat_rect.r = osd.stat_rect.b = 8;
+ } else if (osd.time_rect.b < 16) {
+ osd.icons = mpegplayer_status_icons_12x12x1;
+ osd.stat_rect.r = osd.stat_rect.b = 12;
+ } else {
+ osd.icons = mpegplayer_status_icons_16x16x1;
+ osd.stat_rect.r = osd.stat_rect.b = 16;
+ }
+
+ if (osd.stat_rect.b < osd.time_rect.b) {
+ vo_rect_offset(&osd.stat_rect, 0,
+ (osd.time_rect.b - osd.stat_rect.b) / 2 + OSD_BDR_T);
+ vo_rect_offset(&osd.time_rect, OSD_BDR_L, OSD_BDR_T);
+ } else {
+ vo_rect_offset(&osd.time_rect, OSD_BDR_L,
+ osd.stat_rect.b - osd.time_rect.b + OSD_BDR_T);
+ vo_rect_offset(&osd.stat_rect, 0, OSD_BDR_T);
+ }
+
+ osd.dur_rect = osd.time_rect;
+
+ phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
+ rb->snprintf(buf, sizeof(buf), "%d%s", phys,
+ rb->sound_unit(SOUND_VOLUME));
+
+ mylcd_getstringsize(" ", &spc_width, NULL);
+ mylcd_getstringsize(buf, &osd.vol_rect.r, &osd.vol_rect.b);
+
+ osd.prog_rect.r = SCREEN_WIDTH - OSD_BDR_L - spc_width -
+ osd.vol_rect.r - OSD_BDR_R;
+ osd.prog_rect.b = 3*osd.stat_rect.b / 4;
+ vo_rect_offset(&osd.prog_rect, osd.time_rect.l,
+ osd.time_rect.b);
+
+ vo_rect_offset(&osd.stat_rect,
+ (osd.prog_rect.r + osd.prog_rect.l - osd.stat_rect.r) / 2,
+ 0);
+
+ vo_rect_offset(&osd.dur_rect,
+ osd.prog_rect.r - osd.dur_rect.r, 0);
+
+ vo_rect_offset(&osd.vol_rect, osd.prog_rect.r + spc_width,
+ (osd.prog_rect.b + osd.prog_rect.t - osd.vol_rect.b) / 2);
+
+ osd.height = OSD_BDR_T + MAX(osd.prog_rect.b, osd.vol_rect.b) -
+ MIN(osd.time_rect.t, osd.stat_rect.t) + OSD_BDR_B;
+
+#ifdef HAVE_LCD_COLOR
+ osd.height = ALIGN_UP(osd.height, 2);
+#endif
+ osd.y = SCREEN_HEIGHT - osd.height;
+
+ draw_setfont(FONT_SYSFIXED);
+}
+
+static void osd_init(void)
+{
+ osd.flags = 0;
+ osd.show_for = HZ*4;
+ osd.print_delay = 75*HZ/100;
+ osd.resume_delay = HZ/2;
+#ifdef HAVE_LCD_COLOR
+ osd.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
+ osd.fgcolor = LCD_WHITE;
+ osd.prog_fillcolor = LCD_BLACK;
+#else
+ osd.bgcolor = GREY_LIGHTGRAY;
+ osd.fgcolor = GREY_BLACK;
+ osd.prog_fillcolor = GREY_WHITE;
+#endif
+ osd.curr_time = 0;
+ osd.status = OSD_STATUS_STOPPED;
+ osd.auto_refresh = OSD_REFRESH_TIME;
+ osd.next_auto_refresh = *rb->current_tick;
+ osd_text_init();
+ fps_init();
+}
+
+#ifdef HAVE_HEADPHONE_DETECTION
+static void osd_set_hp_pause_flag(bool set)
+{
+ if (set)
+ osd.flags |= OSD_HP_PAUSE;
+ else
+ osd.flags &= ~OSD_HP_PAUSE;
+}
+#else
+#define osd_set_hp_pause_flag(set)
+#endif /* HAVE_HEADPHONE_DETECTION */
+
+static void osd_schedule_refresh(unsigned refresh)
+{
+ long tick = *rb->current_tick;
+
+ if (refresh & OSD_REFRESH_VIDEO)
+ osd.print_tick = tick + osd.print_delay;
+
+ if (refresh & OSD_REFRESH_RESUME)
+ osd.resume_tick = tick + osd.resume_delay;
+
+ osd.auto_refresh |= refresh;
+}
+
+static void osd_cancel_refresh(unsigned refresh)
+{
+ osd.auto_refresh &= ~refresh;
+}
+
+/* Refresh the background area */
+static void osd_refresh_background(void)
+{
+ char buf[32];
+ struct hms hms;
+
+ unsigned bg = mylcd_get_background();
+ mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+
+#ifdef HAVE_LCD_COLOR
+ /* Draw a "raised" area for our graphics */
+ mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 192));
+ draw_hline(0, osd.width, 0);
+
+ mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 80));
+ draw_hline(0, osd.width, 1);
+
+ mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 48));
+ draw_hline(0, osd.width, osd.height-2);
+
+ mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 128));
+ draw_hline(0, osd.width, osd.height-1);
+
+ mylcd_set_background(bg);
+ draw_clear_area(0, 2, osd.width, osd.height - 4);
+#else
+ /* Give contrast with the main background */
+ mylcd_set_background(MYLCD_WHITE);
+ draw_hline(0, osd.width, 0);
+
+ mylcd_set_background(MYLCD_DARKGRAY);
+ draw_hline(0, osd.width, osd.height-1);
+
+ mylcd_set_background(bg);
+ draw_clear_area(0, 1, osd.width, osd.height - 2);
+#endif
+
+ vo_rect_set_ext(&osd.update_rect, 0, 0, osd.width, osd.height);
+ mylcd_set_drawmode(DRMODE_SOLID);
+
+ if (stream_get_duration() != INVALID_TIMESTAMP) {
+ /* Draw the movie duration */
+ ts_to_hms(stream_get_duration(), &hms);
+ hms_format(buf, sizeof (buf), &hms);
+ draw_putsxy_oriented(osd.dur_rect.l, osd.dur_rect.t, buf);
+ }
+ /* else don't know the duration */
+}
+
+/* Refresh the current time display + the progress bar */
+static void osd_refresh_time(void)
+{
+ char buf[32];
+ struct hms hms;
+
+ uint32_t duration = stream_get_duration();
+
+ draw_scrollbar_draw_rect(&osd.prog_rect, 0, duration,
+ osd.curr_time);
+
+ ts_to_hms(osd.curr_time, &hms);
+ hms_format(buf, sizeof (buf), &hms);
+
+ draw_clear_area_rect(&osd.time_rect);
+ draw_putsxy_oriented(osd.time_rect.l, osd.time_rect.t, buf);
+
+ vo_rect_union(&osd.update_rect, &osd.update_rect,
+ &osd.prog_rect);
+ vo_rect_union(&osd.update_rect, &osd.update_rect,
+ &osd.time_rect);
+}
+
+/* Refresh the volume display area */
+static void osd_refresh_volume(void)
+{
+ char buf[32];
+ int width;
+
+ int volume = rb->global_settings->volume;
+ rb->snprintf(buf, sizeof (buf), "%d%s",
+ rb->sound_val2phys(SOUND_VOLUME, volume),
+ rb->sound_unit(SOUND_VOLUME));
+ mylcd_getstringsize(buf, &width, NULL);
+
+ /* Right-justified */
+ draw_clear_area_rect(&osd.vol_rect);
+ draw_putsxy_oriented(osd.vol_rect.r - width, osd.vol_rect.t, buf);
+
+ vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.vol_rect);
+}
+
+/* Refresh the status icon */
+static void osd_refresh_status(void)
+{
+ int icon_size = osd.stat_rect.r - osd.stat_rect.l;
+
+ draw_clear_area_rect(&osd.stat_rect);
+
+#ifdef HAVE_LCD_COLOR
+ /* Draw status icon with a drop shadow */
+ unsigned oldfg = mylcd_get_foreground();
+ int i = 1;
+
+ mylcd_set_foreground(draw_blendcolor(mylcd_get_background(),
+ MYLCD_BLACK, 96));
+
+ while (1)
+ {
+ draw_oriented_mono_bitmap_part(osd.icons,
+ icon_size*osd.status,
+ 0,
+ icon_size*OSD_STATUS_COUNT,
+ osd.stat_rect.l + osd.x + i,
+ osd.stat_rect.t + osd.y + i,
+ icon_size, icon_size);
+
+ if (--i < 0)
+ break;
+
+ mylcd_set_foreground(oldfg);
+ }
+
+ vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
+#else
+ draw_oriented_mono_bitmap_part(osd.icons,
+ icon_size*osd.status,
+ 0,
+ icon_size*OSD_STATUS_COUNT,
+ osd.stat_rect.l + osd.x,
+ osd.stat_rect.t + osd.y,
+ icon_size, icon_size);
+ vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
+#endif
+}
+
+/* Update the current status which determines which icon is displayed */
+static bool osd_update_status(void)
+{
+ int status;
+
+ switch (stream_status())
+ {
+ default:
+ status = OSD_STATUS_STOPPED;
+ break;
+ case STREAM_PAUSED:
+ /* If paused with a pending resume, coerce it to OSD_STATUS_PLAYING */
+ status = (osd.auto_refresh & OSD_REFRESH_RESUME) ?
+ OSD_STATUS_PLAYING : OSD_STATUS_PAUSED;
+ break;
+ case STREAM_PLAYING:
+ status = OSD_STATUS_PLAYING;
+ break;
+ }
+
+ if (status != osd.status) {
+ /* A refresh is needed */
+ osd.status = status;
+ return true;
+ }
+
+ return false;
+}
+
+/* Update the current time that will be displayed */
+static void osd_update_time(void)
+{
+ uint32_t start;
+ osd.curr_time = stream_get_seek_time(&start);
+ osd.curr_time -= start;
+}
+
+/* Refresh various parts of the OSD - showing it if it is hidden */
+static void osd_refresh(int hint)
+{
+ long tick;
+ unsigned oldbg, oldfg;
+
+ tick = *rb->current_tick;
+
+ if (settings.showfps)
+ fps_refresh();
+
+ if (hint == OSD_REFRESH_DEFAULT) {
+ /* The default which forces no updates */
+
+ /* Make sure Rockbox doesn't turn off the player because of
+ too little activity */
+ if (osd.status == OSD_STATUS_PLAYING)
+ rb->reset_poweroff_timer();
+
+ /* Redraw the current or possibly extract a new video frame */
+ if ((osd.auto_refresh & OSD_REFRESH_VIDEO) &&
+ TIME_AFTER(tick, osd.print_tick)) {
+ osd.auto_refresh &= ~OSD_REFRESH_VIDEO;
+ stream_draw_frame(false);
+ }
+
+ /* Restart playback if the timout was reached */
+ if ((osd.auto_refresh & OSD_REFRESH_RESUME) &&
+ TIME_AFTER(tick, osd.resume_tick)) {
+ osd.auto_refresh &= ~(OSD_REFRESH_RESUME | OSD_REFRESH_VIDEO);
+ stream_resume();
+ }
+
+ /* If not visible, return */
+ if (!(osd.flags & OSD_SHOW))
+ return;
+
+ /* Hide if the visibility duration was reached */
+ if (TIME_AFTER(tick, osd.hide_tick)) {
+ osd_show(OSD_HIDE);
+ return;
+ }
+ } else {
+ /* A forced update of some region */
+
+ /* Show if currently invisible */
+ if (!(osd.flags & OSD_SHOW)) {
+ /* Avoid call back into this function - it will be drawn */
+ osd_show(OSD_SHOW | OSD_NODRAW);
+ hint = OSD_REFRESH_ALL;
+ }
+
+ /* Move back timeouts for frame print and hide */
+ osd.print_tick = tick + osd.print_delay;
+ osd.hide_tick = tick + osd.show_for;
+ }
+
+ if (TIME_AFTER(tick, osd.next_auto_refresh)) {
+ /* Refresh whatever graphical elements are due automatically */
+ osd.next_auto_refresh = tick + OSD_MIN_UPDATE_INTERVAL;
+
+ if (osd.auto_refresh & OSD_REFRESH_STATUS) {
+ if (osd_update_status())
+ hint |= OSD_REFRESH_STATUS;
+ }
+
+ if (osd.auto_refresh & OSD_REFRESH_TIME) {
+ osd_update_time();
+ hint |= OSD_REFRESH_TIME;
+ }
+ }
+
+ if (hint == 0)
+ return; /* No drawing needed */
+
+ /* Set basic drawing params that are used. Elements that perform variations
+ * will restore them. */
+ oldfg = mylcd_get_foreground();
+ oldbg = mylcd_get_background();
+
+ draw_setfont(FONT_UI);
+ mylcd_set_foreground(osd.fgcolor);
+ mylcd_set_background(osd.bgcolor);
+
+ vo_rect_clear(&osd.update_rect);
+
+ if (hint & OSD_REFRESH_BACKGROUND) {
+ osd_refresh_background();
+ hint |= OSD_REFRESH_ALL; /* Requires a redraw of everything */
+ }
+
+ if (hint & OSD_REFRESH_TIME) {
+ osd_refresh_time();
+ }
+
+ if (hint & OSD_REFRESH_VOLUME) {
+ osd_refresh_volume();
+ }
+
+ if (hint & OSD_REFRESH_STATUS) {
+ osd_refresh_status();
+ }
+
+ /* Go back to defaults */
+ draw_setfont(FONT_SYSFIXED);
+ mylcd_set_foreground(oldfg);
+ mylcd_set_background(oldbg);
+
+ /* Update the dirty rectangle */
+ vo_lock();
+
+ draw_update_rect(osd.update_rect.l,
+ osd.update_rect.t,
+ osd.update_rect.r - osd.update_rect.l,
+ osd.update_rect.b - osd.update_rect.t);
+
+ vo_unlock();
+}
+
+/* Show/Hide the OSD */
+static void osd_show(unsigned show)
+{
+ if (((show ^ osd.flags) & OSD_SHOW) == 0)
+ {
+ if (show & OSD_SHOW) {
+ osd.hide_tick = *rb->current_tick + osd.show_for;
+ }
+ return;
+ }
+
+ if (show & OSD_SHOW) {
+ /* Clip away the part of video that is covered */
+ struct vo_rect rc = { 0, 0, SCREEN_WIDTH, osd.y };
+
+ osd.flags |= OSD_SHOW;
+
+ if (osd.status != OSD_STATUS_PLAYING) {
+ /* Not playing - set brightness to mpegplayer setting */
+ osd_backlight_brightness_video_mode(true);
+ }
+
+ stream_vo_set_clip(&rc);
+
+ if (!(show & OSD_NODRAW))
+ osd_refresh(OSD_REFRESH_ALL);
+ } else {
+ /* Uncover clipped video area and redraw it */
+ osd.flags &= ~OSD_SHOW;
+
+ draw_clear_area(0, 0, osd.width, osd.height);
+
+ if (!(show & OSD_NODRAW)) {
+ vo_lock();
+ draw_update_rect(0, 0, osd.width, osd.height);
+ vo_unlock();
+
+ stream_vo_set_clip(NULL);
+ stream_draw_frame(false);
+ } else {
+ stream_vo_set_clip(NULL);
+ }
+
+ if (osd.status != OSD_STATUS_PLAYING) {
+ /* Not playing - restore backlight brightness */
+ osd_backlight_brightness_video_mode(false);
+ }
+ }
+}
+
+/* Set the current status - update screen if specified */
+static void osd_set_status(int status)
+{
+ bool draw = (status & OSD_NODRAW) == 0;
+
+ status &= OSD_STATUS_MASK;
+
+ if (osd.status != status) {
+
+ osd.status = status;
+
+ if (draw)
+ osd_refresh(OSD_REFRESH_STATUS);
+ }
+}
+
+/* Get the current status value */
+static int osd_get_status(void)
+{
+ return osd.status & OSD_STATUS_MASK;
+}
+
+/* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;)
+ * Returns last button code
+ */
+static int osd_ff_rw(int btn, unsigned refresh, uint32_t *new_time)
+{
+ unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
+ const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
+ uint32_t start;
+ uint32_t time = stream_get_seek_time(&start);
+ const uint32_t duration = stream_get_duration();
+ unsigned int max_step = 0;
+ uint32_t ff_rw_count = 0;
+ unsigned status = osd.status;
+ int new_btn;
+
+ osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME |
+ OSD_REFRESH_TIME);
+
+ time -= start; /* Absolute clock => stream-relative */
+
+ switch (btn)
+ {
+ case MPEG_FF:
+#ifdef MPEG_FF2
+ case MPEG_FF2:
+#endif
+#ifdef MPEG_RC_FF
+ case MPEG_RC_FF:
+#endif
+ osd_set_status(OSD_STATUS_FF);
+ new_btn = btn | BUTTON_REPEAT; /* simplify code below */
+ break;
+ case MPEG_RW:
+#ifdef MPEG_RW2
+ case MPEG_RW2:
+#endif
+#ifdef MPEG_RC_RW
+ case MPEG_RC_RW:
+#endif
+ osd_set_status(OSD_STATUS_RW);
+ new_btn = btn | BUTTON_REPEAT; /* simplify code below */
+ break;
+ default:
+ new_btn = BUTTON_NONE; /* Fail tests below but still do proper exit */
+ }
+
+ while (1)
+ {
+ stream_keep_disk_active();
+
+ if (new_btn == (btn | BUTTON_REPEAT)) {
+ if (osd.status == OSD_STATUS_FF) {
+ /* fast forwarding, calc max step relative to end */
+ max_step = muldiv_uint32(duration - (time + ff_rw_count),
+ FF_REWIND_MAX_PERCENT, 100);
+ } else {
+ /* rewinding, calc max step relative to start */
+ max_step = muldiv_uint32(time - ff_rw_count,
+ FF_REWIND_MAX_PERCENT, 100);
+ }
+
+ max_step = MAX(max_step, MIN_FF_REWIND_STEP);
+
+ if (step > max_step)
+ step = max_step;
+
+ ff_rw_count += step;
+
+ /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
+ step += step >> ff_rw_accel;
+
+ if (osd.status == OSD_STATUS_FF) {
+ if (duration - time <= ff_rw_count)
+ ff_rw_count = duration - time;
+
+ osd.curr_time = time + ff_rw_count;
+ } else {
+ if (time <= ff_rw_count)
+ ff_rw_count = time;
+
+ osd.curr_time = time - ff_rw_count;
+ }
+
+ osd_refresh(OSD_REFRESH_TIME);
+
+ new_btn = mpeg_button_get(TIMEOUT_BLOCK);
+ }
+ else {
+ if (new_btn == (btn | BUTTON_REL)) {
+ if (osd.status == OSD_STATUS_FF)
+ time += ff_rw_count;
+ else if (osd.status == OSD_STATUS_RW)
+ time -= ff_rw_count;
+ }
+
+ *new_time = time;
+
+ osd_schedule_refresh(refresh);
+ osd_set_status(status);
+ osd_schedule_refresh(OSD_REFRESH_TIME);
+
+ return new_btn;
+ }
+ }
+}
+
+/* Return adjusted STREAM_* status */
+static int osd_stream_status(void)
+{
+ int status = stream_status();
+
+ /* Coerce to STREAM_PLAYING if paused with a pending resume */
+ if (status == STREAM_PAUSED) {
+ if (osd.auto_refresh & OSD_REFRESH_RESUME)
+ status = STREAM_PLAYING;
+ }
+
+ return status;
+}
+
+/* Change the current audio volume by a specified amount */
+static void osd_set_volume(int delta)
+{
+ int vol = rb->global_settings->volume;
+ int limit;
+
+ vol += delta;
+
+ if (delta < 0) {
+ /* Volume down - clip to lower limit */
+ limit = rb->sound_min(SOUND_VOLUME);
+ if (vol < limit)
+ vol = limit;
+ } else {
+ /* Volume up - clip to upper limit */
+ limit = rb->sound_max(SOUND_VOLUME);
+ if (vol > limit)
+ vol = limit;
+ }
+
+ /* Sync the global settings */
+ if (vol != rb->global_settings->volume) {
+ rb->sound_set(SOUND_VOLUME, vol);
+ rb->global_settings->volume = vol;
+ }
+
+ /* Update the volume display */
+ osd_refresh(OSD_REFRESH_VOLUME);
+}
+
+/* Begin playback at the specified time */
+static int osd_play(uint32_t time)
+{
+ int retval;
+
+ osd_set_hp_pause_flag(false);
+ osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
+
+ retval = stream_seek(time, SEEK_SET);
+
+ if (retval >= STREAM_OK) {
+ osdbacklight_hw_on_video_mode(true);
+ osd_backlight_brightness_video_mode(true);
+ stream_show_vo(true);
+
+ retval = stream_play();
+
+ if (retval >= STREAM_OK)
+ osd_set_status(OSD_STATUS_PLAYING | OSD_NODRAW);
+ }
+
+ return retval;
+}
+
+/* Halt playback - pause engine and return logical state */
+static int osd_halt(void)
+{
+ int status = stream_pause();
+
+ /* Coerce to STREAM_PLAYING if paused with a pending resume */
+ if (status == STREAM_PAUSED) {
+ if (osd_get_status() == OSD_STATUS_PLAYING)
+ status = STREAM_PLAYING;
+ }
+
+ /* Cancel some auto refreshes - caller will restart them if desired */
+ osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
+
+ /* No backlight fiddling here - callers does the right thing */
+
+ return status;
+}
+
+/* Pause playback if playing */
+static int osd_pause(void)
+{
+ unsigned refresh = osd.auto_refresh;
+ int status = osd_halt();
+
+ osd_set_hp_pause_flag(false);
+
+ if (status == STREAM_PLAYING && (refresh & OSD_REFRESH_RESUME)) {
+ /* Resume pending - change to a still video frame update */
+ osd_schedule_refresh(OSD_REFRESH_VIDEO);
+ }
+
+ osd_set_status(OSD_STATUS_PAUSED);
+
+ osdbacklight_hw_on_video_mode(false);
+ /* Leave brightness alone and restore it when OSD is hidden */
+
+ if (stream_can_seek() && rb->global_settings->pause_rewind) {
+ stream_seek(-rb->global_settings->pause_rewind*TS_SECOND,
+ SEEK_CUR);
+ osd_schedule_refresh(OSD_REFRESH_VIDEO);
+ /* Update time display now */
+ osd_update_time();
+ osd_refresh(OSD_REFRESH_TIME);
+ }
+
+ return status;
+}
+
+/* Resume playback if halted or paused */
+static void osd_resume(void)
+{
+ /* Cancel video and resume auto refresh - the resyc when starting
+ * playback will perform those tasks */
+ osd_set_hp_pause_flag(false);
+ osdbacklight_hw_on_video_mode(true);
+ osd_backlight_brightness_video_mode(true);
+ osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
+ osd_set_status(OSD_STATUS_PLAYING);
+ stream_resume();
+}
+
+/* Stop playback - remember the resume point if not closed */
+static void osd_stop(void)
+{
+ uint32_t resume_time;
+
+ osd_set_hp_pause_flag(false);
+ osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
+ osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW);
+ osd_show(OSD_HIDE);
+
+ stream_stop();
+
+ resume_time = stream_get_resume_time();
+
+ if (resume_time != INVALID_TIMESTAMP)
+ settings.resume_time = resume_time;
+
+ osdbacklight_hw_on_video_mode(false);
+ osd_backlight_brightness_video_mode(false);
+}
+
+/* Perform a seek by button if seeking is possible for this stream.
+ *
+ * A delay will be inserted before restarting in case the user decides to
+ * seek again soon after.
+ *
+ * Returns last button code
+ */
+static int osd_seek_btn(int btn)
+{
+ int status;
+ unsigned refresh = 0;
+ uint32_t time;
+
+ if (!stream_can_seek())
+ return true;
+
+ /* Halt playback - not strictly necessary but nice when doing
+ * buttons */
+ status = osd_halt();
+
+ if (status == STREAM_STOPPED)
+ return true;
+
+ osd_show(OSD_SHOW);
+
+ /* Obtain a new playback point according to the buttons */
+ if (status == STREAM_PLAYING)
+ refresh = OSD_REFRESH_RESUME; /* delay resume if playing */
+ else
+ refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
+
+ btn = osd_ff_rw(btn, refresh, &time);
+
+ /* Tell engine to resume at that time */
+ stream_seek(time, SEEK_SET);
+
+ return btn;
+}
+
+/* Perform a seek by time if seeking is possible for this stream
+ *
+ * If playing, the seeking is immediate, otherise a delay is added to showing
+ * a still if paused in case the user does another seek soon after.
+ *
+ * If seeking isn't possible, a time of zero performs a skip to the
+ * beginning.
+ */
+static void osd_seek_time(uint32_t time)
+{
+ int status;
+ unsigned refresh = 0;
+
+ if (!stream_can_seek() && time != 0)
+ return;
+
+ stream_wait_status();
+ status = osd_stream_status();
+
+ if (status == STREAM_STOPPED)
+ return;
+
+ if (status == STREAM_PLAYING) /* merely preserve resume */
+ refresh = osd.auto_refresh & OSD_REFRESH_RESUME;
+ else
+ refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
+
+ /* Cancel print or resume if pending */
+ osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
+
+ /* Tell engine to seek to the given time - no state change */
+ stream_seek(time, SEEK_SET);
+
+ osd_update_time();
+ osd_refresh(OSD_REFRESH_TIME);
+ osd_schedule_refresh(refresh);
+}
+
+/* Has this file one of the supported extensions? */
+static bool is_videofile(const char* file)
+{
+ static const char * const extensions[] =
+ {
+ /* Should match apps/plugins/viewers.config */
+ "mpg", "mpeg", "mpv", "m2v"
+ };
+
+ const char* ext = rb->strrchr(file, '.');
+ int i;
+
+ if (!ext)
+ return false;
+
+ for (i = ARRAYLEN(extensions) - 1; i >= 0; i--)
+ {
+ if (!rb->strcasecmp(ext + 1, extensions[i]))
+ break;
+ }
+
+ return i >= 0;
+}
+
+/* deliver the next/previous video file in the current directory.
+ returns false if there is none. */
+static bool get_videofile(int direction, char* videofile, size_t bufsize)
+{
+ struct tree_context *tree = rb->tree_get_context();
+ struct entry *dircache = rb->tree_get_entries(tree);
+ int i, step, end, found = 0;
+ char *videoname = rb->strrchr(videofile, '/') + 1;
+ size_t rest = bufsize - (videoname - videofile) - 1;
+
+ if (direction == VIDEO_NEXT) {
+ i = 0;
+ step = 1;
+ end = tree->filesindir;
+ } else {
+ i = tree->filesindir-1;
+ step = -1;
+ end = -1;
+ }
+ for (; i != end; i += step)
+ {
+ const char* name = dircache[i].name;
+ if (!rb->strcmp(name, videoname)) {
+ found = 1;
+ continue;
+ }
+ if (found && rb->strlen(name) <= rest &&
+ !(dircache[i].attr & ATTR_DIRECTORY) && is_videofile(name))
+ {
+ rb->strcpy(videoname, name);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef HAVE_HEADPHONE_DETECTION
+/* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
+static void osd_handle_phone_plug(bool inserted)
+{
+ if (rb->global_settings->unplug_mode == 0)
+ return;
+
+ /* Wait for any incomplete state transition to complete first */
+ stream_wait_status();
+
+ int status = osd_stream_status();
+
+ if (inserted) {
+ if (rb->global_settings->unplug_mode > 1) {
+ if (status == STREAM_PAUSED &&
+ (osd.flags & OSD_HP_PAUSE)) {
+ osd_resume();
+ }
+ }
+ } else {
+ if (status == STREAM_PLAYING) {
+ osd_pause();
+
+ osd_set_hp_pause_flag(true);
+ }
+ }
+}
+#endif
+
+static int button_loop(void)
+{
+ int next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
+
+ rb->lcd_setfont(FONT_SYSFIXED);
+#ifdef HAVE_LCD_COLOR
+ rb->lcd_set_foreground(LCD_WHITE);
+ rb->lcd_set_background(LCD_BLACK);
+#endif
+ rb->lcd_clear_display();
+ rb->lcd_update();
+
+#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
+ rb->lcd_set_mode(LCD_MODE_YUV);
+#endif
+
+ osd_init();
+
+ /* Start playback at the specified starting time */
+ if (osd_play(settings.resume_time) < STREAM_OK) {
+ rb->splash(HZ*2, "Playback failed");
+ return VIDEO_STOP;
+ }
+
+ /* Gently poll the video player for EOS and handle UI */
+ while (stream_status() != STREAM_STOPPED)
+ {
+ int button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL/2);
+
+ switch (button)
+ {
+ case BUTTON_NONE:
+ {
+ osd_refresh(OSD_REFRESH_DEFAULT);
+ continue;
+ } /* BUTTON_NONE: */
+
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+ case LCD_ENABLE_EVENT_1:
+ {
+ /* Draw the current frame if prepared already */
+ stream_draw_frame(true);
+ break;
+ } /* LCD_ENABLE_EVENT_1: */
+#endif
+
+ case MPEG_VOLUP:
+ case MPEG_VOLUP|BUTTON_REPEAT:
+#ifdef MPEG_VOLUP2
+ case MPEG_VOLUP2:
+ case MPEG_VOLUP2|BUTTON_REPEAT:
+#endif
+#ifdef MPEG_RC_VOLUP
+ case MPEG_RC_VOLUP:
+ case MPEG_RC_VOLUP|BUTTON_REPEAT:
+#endif
+ {
+ osd_set_volume(+1);
+ break;
+ } /* MPEG_VOLUP*: */
+
+ case MPEG_VOLDOWN:
+ case MPEG_VOLDOWN|BUTTON_REPEAT:
+#ifdef MPEG_VOLDOWN2
+ case MPEG_VOLDOWN2:
+ case MPEG_VOLDOWN2|BUTTON_REPEAT:
+#endif
+#ifdef MPEG_RC_VOLDOWN
+ case MPEG_RC_VOLDOWN:
+ case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
+#endif
+ {
+ osd_set_volume(-1);
+ break;
+ } /* MPEG_VOLDOWN*: */
+
+ case MPEG_MENU:
+#ifdef MPEG_RC_MENU
+ case MPEG_RC_MENU:
+#endif
+ {
+ int state = osd_halt(); /* save previous state */
+ int result;
+
+ /* Hide video output */
+ osd_show(OSD_HIDE | OSD_NODRAW);
+ stream_show_vo(false);
+ osd_backlight_brightness_video_mode(false);
+
+#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
+ rb->lcd_set_mode(LCD_MODE_RGB565);
+#endif
+
+ result = mpeg_menu();
+
+ next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
+
+ fps_update_post_frame_callback();
+
+ /* The menu can change the font, so restore */
+ rb->lcd_setfont(FONT_SYSFIXED);
+#ifdef HAVE_LCD_COLOR
+ rb->lcd_set_foreground(LCD_WHITE);
+ rb->lcd_set_background(LCD_BLACK);
+#endif
+ rb->lcd_clear_display();
+ rb->lcd_update();
+
+ switch (result)
+ {
+ case MPEG_MENU_QUIT:
+ next_action = VIDEO_STOP;
+ osd_stop();
+ break;
+
+ default:
+#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
+ rb->lcd_set_mode(LCD_MODE_YUV);
+#endif
+ /* If not stopped, show video again */
+ if (state != STREAM_STOPPED) {
+ osd_show(OSD_SHOW);
+ stream_show_vo(true);
+ }
+
+ /* If stream was playing, restart it */
+ if (state == STREAM_PLAYING) {
+ osd_resume();
+ }
+ break;
+ }
+ break;
+ } /* MPEG_MENU: */
+
+#ifdef MPEG_SHOW_OSD
+ case MPEG_SHOW_OSD:
+ case MPEG_SHOW_OSD | BUTTON_REPEAT:
+ /* Show if not visible */
+ osd_show(OSD_SHOW);
+ /* Make sure it refreshes */
+ osd_refresh(OSD_REFRESH_DEFAULT);
+ break;
+#endif
+
+ case MPEG_STOP:
+#ifdef MPEG_RC_STOP
+ case MPEG_RC_STOP:
+#endif
+ case ACTION_STD_CANCEL:
+ {
+ cancel_playback:
+ next_action = VIDEO_STOP;
+ osd_stop();
+ break;
+ } /* MPEG_STOP: */
+
+ case MPEG_PAUSE:
+#ifdef MPEG_PAUSE2
+ case MPEG_PAUSE2:
+#endif
+#ifdef MPEG_RC_PAUSE
+ case MPEG_RC_PAUSE:
+#endif
+ {
+ int status = osd_stream_status();
+
+ if (status == STREAM_PLAYING) {
+ /* Playing => Paused */
+ osd_pause();
+ }
+ else if (status == STREAM_PAUSED) {
+ /* Paused => Playing */
+ osd_resume();
+ }
+
+ break;
+ } /* MPEG_PAUSE*: */
+
+ case MPEG_RW:
+#ifdef MPEG_RW2
+ case MPEG_RW2:
+#endif
+#ifdef MPEG_RC_RW
+ case MPEG_RC_RW:
+#endif
+ {
+ int old_button = button;
+
+ /* If button has been released: skip to next/previous file */
+ button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
+
+ if ((old_button | BUTTON_REL) == button) {
+ /* Check current playback position */
+ osd_update_time();
+
+ if (settings.play_mode == 0 || osd.curr_time >= 3*TS_SECOND) {
+ /* Start the current video from the beginning */
+ osd_seek_time(0*TS_SECOND);
+ }
+ else {
+ /* Release within 3 seconds of start: skip to previous
+ * file */
+ osd_stop();
+ next_action = VIDEO_PREV | VIDEO_ACTION_MANUAL;
+ }
+ }
+ else if ((button & ~BUTTON_REPEAT) == old_button) {
+ button = osd_seek_btn(old_button);
+ }
+
+ if (button == ACTION_STD_CANCEL)
+ goto cancel_playback; /* jump to stop handling above */
+
+ rb->default_event_handler(button);
+ break;
+ } /* MPEG_RW: */
+
+ case MPEG_FF:
+#ifdef MPEG_FF2
+ case MPEG_FF2:
+#endif
+#ifdef MPEG_RC_FF
+ case MPEG_RC_FF:
+#endif
+ {
+ int old_button = button;
+
+ if (settings.play_mode != 0)
+ button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
+
+ if ((old_button | BUTTON_REL) == button) {
+ /* If button has been released: skip to next file */
+ osd_stop();
+ next_action = VIDEO_NEXT | VIDEO_ACTION_MANUAL;
+ }
+ else if ((button & ~BUTTON_REPEAT) == old_button) {
+ button = osd_seek_btn(old_button);
+ }
+
+ if (button == ACTION_STD_CANCEL)
+ goto cancel_playback; /* jump to stop handling above */
+
+ rb->default_event_handler(button);
+ break;
+ } /* MPEG_FF: */
+
+#ifdef HAVE_HEADPHONE_DETECTION
+ case SYS_PHONE_PLUGGED:
+ case SYS_PHONE_UNPLUGGED:
+ {
+ osd_handle_phone_plug(button == SYS_PHONE_PLUGGED);
+ break;
+ } /* SYS_PHONE_*: */
+#endif
+
+ default:
+ {
+ osd_refresh(OSD_REFRESH_DEFAULT);
+ rb->default_event_handler(button);
+ break;
+ } /* default: */
+ }
+
+ rb->yield();
+ } /* end while */
+
+ osd_stop();
+
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+ /* Be sure hook is removed before exiting since the stop will put it
+ * back because of the backlight restore. */
+ rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
+#endif
+
+ rb->lcd_setfont(FONT_UI);
+
+ return next_action;
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ static char videofile[MAX_PATH];
+ int status = PLUGIN_OK; /* assume success */
+ bool quit = false;
+
+#if defined(PLUGIN_USE_IRAM) && !defined(SIMULATOR)
+ bool preserved_talk_state;
+#endif
+
+ if (parameter == NULL) {
+ /* No file = GTFO */
+ rb->splash(HZ*2, "No File");
+ return PLUGIN_ERROR;
+ }
+
+ /* Disable all talking before initializing IRAM */
+ rb->talk_disable(true);
+
+#ifdef PLUGIN_USE_IRAM
+ iram_saving_init();
+
+#ifndef SIMULATOR
+ preserved_talk_state = rb->global_settings->talk_menu;
+ if (!iram_saved_copy)
+ rb->global_settings->talk_menu = false;
+#endif
+#endif
+
+#ifdef HAVE_LCD_COLOR
+ rb->lcd_set_backdrop(NULL);
+ rb->lcd_set_foreground(LCD_WHITE);
+ rb->lcd_set_background(LCD_BLACK);
+#endif
+
+ rb->lcd_clear_display();
+ rb->lcd_update();
+
+ rb->strcpy(videofile, (const char*) parameter);
+
+ if (stream_init() < STREAM_OK) {
+ /* Fatal because this should not fail */
+ DEBUGF("Could not initialize streams\n");
+ status = PLUGIN_ERROR;
+ } else {
+ int next_action = VIDEO_STOP;
+ bool get_videofile_says = true;
+
+ while (!quit)
+ {
+ init_settings(videofile);
+
+ int result = stream_open(videofile);
+ bool manual_skip = false;
+
+ if (result >= STREAM_OK) {
+ /* start menu */
+ rb->lcd_clear_display();
+ rb->lcd_update();
+ result = mpeg_start_menu(stream_get_duration());
+
+ next_action = VIDEO_STOP;
+ if (result != MPEG_START_QUIT) {
+ /* Enter button loop and process UI */
+ next_action = button_loop();
+ manual_skip = next_action & VIDEO_ACTION_MANUAL;
+ next_action &= ~VIDEO_ACTION_MANUAL;
+ }
+
+ stream_close();
+
+ rb->lcd_clear_display();
+ rb->lcd_update();
+
+ save_settings();
+ } else {
+ /* Problem with file; display message about it - not
+ * considered a plugin error */
+ long tick;
+ const char *errstring;
+
+ DEBUGF("Could not open %s\n", videofile);
+ switch (result)
+ {
+ case STREAM_UNSUPPORTED:
+ errstring = "Unsupported format";
+ break;
+ default:
+ errstring = "Error opening file: %d";
+ }
+
+ tick = *rb->current_tick + HZ*2;
+
+ rb->splashf(0, errstring, result);
+
+ /* Be sure it doesn't get stuck in an unbreakable loop of bad
+ * files, just in case! Otherwise, keep searching in the
+ * chosen direction until a good one is found. */
+ while (!quit && TIME_BEFORE(*rb->current_tick, tick))
+ {
+ int button = mpeg_button_get(HZ*2);
+
+ switch (button)
+ {
+ case MPEG_STOP:
+ case ACTION_STD_CANCEL:
+ /* Abort the search and exit */
+ next_action = VIDEO_STOP;
+ quit = true;
+ break;
+
+ case BUTTON_NONE:
+ if (settings.play_mode != 0) {
+ if (next_action == VIDEO_STOP) {
+ /* Default to next file */
+ next_action = VIDEO_NEXT;
+ }
+ else if (next_action == VIDEO_PREV &&
+ !get_videofile_says) {
+ /* Was first file already; avoid endlessly
+ * retrying it */
+ next_action = VIDEO_STOP;
+ }
+ }
+ break;
+
+ default:
+ rb->default_event_handler(button);
+ } /* switch */
+ } /* while */
+ }
+
+ /* return value of button_loop says, what's next */
+ switch (next_action)
+ {
+ case VIDEO_NEXT:
+ {
+ get_videofile_says = get_videofile(VIDEO_NEXT, videofile,
+ sizeof(videofile));
+ /* quit after finished the last videofile */
+ quit = !get_videofile_says;
+
+ if (manual_skip)
+ {
+ rb->system_sound_play(get_videofile_says ?
+ SOUND_TRACK_SKIP : SOUND_TRACK_NO_MORE);
+ }
+
+ break;
+ }
+ case VIDEO_PREV:
+ {
+ get_videofile_says = get_videofile(VIDEO_PREV, videofile,
+ sizeof(videofile));
+ /* if there is no previous file, play the same videofile */
+
+ if (manual_skip)
+ {
+ rb->system_sound_play(get_videofile_says ?
+ SOUND_TRACK_SKIP : SOUND_TRACK_NO_MORE);
+ }
+
+ break;
+ }
+ case VIDEO_STOP:
+ {
+ quit = true;
+ break;
+ }
+ }
+ } /* while */
+ }
+
+#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
+ rb->lcd_set_mode(LCD_MODE_RGB565);
+#endif
+
+ stream_exit();
+
+#if defined(PLUGIN_USE_IRAM) && !defined(SIMULATOR)
+ if (!iram_saved_copy)
+ rb->global_settings->talk_menu = preserved_talk_state;
+#endif
+
+ rb->talk_disable(false);
+
+ /* Actually handle delayed processing of system events of interest
+ * that were captured in other button loops */
+ mpeg_sysevent_handle();
+
+ return status;
+}
diff --git a/apps/plugins/mpegplayer/mpegplayer.h b/apps/plugins/mpegplayer/mpegplayer.h
new file mode 100644
index 0000000000..51fb9a8f8a
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpegplayer.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Main mpegplayer config header.
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef MPEGPLAYER_H
+#define MPEGPLAYER_H
+
+#ifdef HAVE_SCHEDULER_BOOSTCTRL
+#define trigger_cpu_boost rb->trigger_cpu_boost
+#define cancel_cpu_boost rb->cancel_cpu_boost
+#endif
+/* #else function-like empty macros are defined in the headers */
+
+/* Should be enough for now */
+#define MPEGPLAYER_MAX_STREAMS 4
+
+/* Memory allotments for various subsystems */
+#define MIN_MEMMARGIN (4*1024)
+
+/** Video thread **/
+#define LIBMPEG2_ALLOC_SIZE (2*1024*1024)
+
+/** MPEG audio buffer **/
+#define AUDIOBUF_GUARD_SIZE (MPA_MAX_FRAME_SIZE + 2*MAD_BUFFER_GUARD)
+#define AUDIOBUF_SIZE (64*1024)
+#define AUDIOBUF_ALLOC_SIZE (AUDIOBUF_SIZE+AUDIOBUF_GUARD_SIZE)
+
+/** PCM buffer **/
+#define CLOCK_RATE 44100 /* Our clock rate in ticks/second (samplerate) */
+
+/* Define this as "1" to have a test tone instead of silence clip */
+#define SILENCE_TEST_TONE 0
+
+/* NOTE: Sizes make no frame header allowance when considering duration */
+#define PCMOUT_BUFSIZE (CLOCK_RATE/2*4) /* 1/2s */
+#define PCMOUT_GUARD_SIZE (PCMOUT_BUFSIZE) /* guarantee contiguous sizes */
+#define PCMOUT_ALLOC_SIZE (PCMOUT_BUFSIZE + PCMOUT_GUARD_SIZE)
+ /* Start pcm playback @ 25% full */
+#define PCMOUT_PLAY_WM (PCMOUT_BUFSIZE/4)
+#define PCMOUT_LOW_WM (0)
+
+/** disk buffer **/
+#define DISK_BUF_LOW_WATERMARK (1024*1024)
+/* 65535+6 is required since each PES has a 6 byte header with a 16 bit
+ * packet length field */
+#define DISK_GUARDBUF_SIZE ALIGN_UP(65535+6, 4)
+
+#ifdef HAVE_LCD_COLOR
+#define mylcd_splash rb->splash
+#else
+#include "lib/grey.h"
+#define mylcd_splash grey_splash
+#endif
+
+#include "lib/mylcd.h"
+
+#include "libmpeg2/mpeg2.h"
+#include "video_out.h"
+#include "mpeg_stream.h"
+#include "mpeg_misc.h"
+#include "mpeg_alloc.h"
+#include "stream_thread.h"
+#include "parser.h"
+#include "pcm_output.h"
+#include "disk_buf.h"
+#include "stream_mgr.h"
+
+#define LCD_ENABLE_EVENT_0 MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0)
+#define LCD_ENABLE_EVENT_1 MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 1)
+
+#ifdef PLUGIN_USE_IRAM
+/* IRAM preserving mechanism to enable talking menus */
+extern void mpegplayer_iram_preserve(void);
+extern void mpegplayer_iram_restore(void);
+#endif
+
+#endif /* MPEGPLAYER_H */
diff --git a/apps/plugins/mpegplayer/mpegplayer.make b/apps/plugins/mpegplayer/mpegplayer.make
new file mode 100644
index 0000000000..af2156787e
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpegplayer.make
@@ -0,0 +1,32 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+MPEGSRCDIR := $(APPSDIR)/plugins/mpegplayer
+MPEGBUILDDIR := $(BUILDDIR)/apps/plugins/mpegplayer
+
+ROCKS += $(MPEGBUILDDIR)/mpegplayer.rock
+
+MPEG_SRC := $(call preprocess, $(MPEGSRCDIR)/SOURCES)
+MPEG_OBJ := $(call c2obj, $(MPEG_SRC))
+
+# add source files to OTHER_SRC to get automatic dependencies
+OTHER_SRC += $(MPEG_SRC)
+
+# Set '-fgnu89-inline' if supported (GCCVER >= 4.1.3, GCCNUM > 401)
+ifeq ($(shell expr $(GCCNUM) \> 401),1)
+ MPEGCFLAGS = $(PLUGINFLAGS) -fgnu89-inline
+else
+ MPEGCFLAGS = $(PLUGINFLAGS)
+endif
+
+$(MPEGBUILDDIR)/mpegplayer.rock: $(MPEG_OBJ) $(CODECDIR)/libmad-mpeg.a
+
+$(MPEGBUILDDIR)/%.o: $(MPEGSRCDIR)/%.c $(MPEGSRCDIR)/mpegplayer.make
+ $(SILENT)mkdir -p $(dir $@)
+ $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(MPEGCFLAGS) -c $< -o $@
diff --git a/apps/plugins/mpegplayer/parser.h b/apps/plugins/mpegplayer/parser.h
new file mode 100644
index 0000000000..ba2181e98b
--- /dev/null
+++ b/apps/plugins/mpegplayer/parser.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * AV parser inteface declarations
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef PARSER_H
+#define PARSER_H
+
+enum stream_formats
+{
+ STREAM_FMT_UNKNOWN = -1,
+ STREAM_FMT_MPEG_TS, /* MPEG transport stream */
+ STREAM_FMT_MPEG_PS, /* MPEG program stream */
+ STREAM_FMT_MPV, /* MPEG Video only (1 or 2) */
+ STREAM_FMT_MPA, /* MPEG Audio only */
+};
+
+/* Structure used by a thread that handles a single demuxed data stream and
+ * receives commands from the stream manager */
+enum stream_parse_states
+{
+ /* Stream is... */
+ SSTATE_SYNC, /* synchronizing by trying to find a start code */
+ SSTATE_PARSE, /* parsing the stream looking for packets */
+ SSTATE_END, /* at the end of data */
+};
+
+enum stream_parse_mode
+{
+ STREAM_PM_STREAMING = 0, /* Next packet when streaming */
+ STREAM_PM_RANDOM_ACCESS, /* Random-access parsing */
+};
+
+enum stream_parser_flags
+{
+ STREAMF_CAN_SEEK = 0x1, /* Seeking possible for this stream */
+};
+
+struct stream_parser
+{
+ /* Common generic parser data */
+ enum stream_formats format; /* Stream format */
+ uint32_t start_pts; /* The movie start time as represented by
+ the first audio PTS tag in the
+ stream converted to half minutes */
+ uint32_t end_pts; /* The movie end time as represented by
+ the maximum audio PTS tag in the
+ stream converted to half minutes */
+ uint32_t duration; /* Duration in PTS units */
+ unsigned flags; /* Various attributes set at init */
+ struct vo_ext dims; /* Movie dimensions in pixels */
+ uint32_t last_seek_time;
+ int (*next_data)(struct stream *str, enum stream_parse_mode type);
+ union /* A place for reusable no-cache parameters */
+ {
+ struct str_sync_data sd;
+ } parms;
+};
+
+extern struct stream_parser str_parser;
+
+/* MPEG parsing */
+uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code);
+unsigned mpeg_parser_scan_pes(struct stream_scan *sk);
+uint32_t mpeg_parser_scan_scr(struct stream_scan *sk);
+uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id);
+off_t mpeg_stream_stream_seek_PTS(uint32_t time, int id);
+
+/* General parsing */
+bool parser_init(void);
+void str_initialize(struct stream *str, off_t pos);
+bool parser_prepare_image(uint32_t time);
+bool parser_get_video_size(struct vo_ext *sz);
+int parser_init_stream(void);
+void parser_close_stream(void);
+static inline bool parser_can_seek(void)
+ { return str_parser.flags & STREAMF_CAN_SEEK; }
+uint32_t parser_seek_time(uint32_t time);
+void parser_prepare_streaming(void);
+void str_end_of_stream(struct stream *str);
+
+static inline int parser_get_next_data(struct stream *str,
+ enum stream_parse_mode type)
+ { return str_parser.next_data(str, type); }
+
+#endif /* PARSER_H */
diff --git a/apps/plugins/mpegplayer/pcm_output.c b/apps/plugins/mpegplayer/pcm_output.c
new file mode 100644
index 0000000000..5e95d16316
--- /dev/null
+++ b/apps/plugins/mpegplayer/pcm_output.c
@@ -0,0 +1,396 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * PCM output buffer definitions
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "mpegplayer.h"
+
+/* PCM channel we're using */
+#define MPEG_PCM_CHANNEL PCM_MIXER_CHAN_PLAYBACK
+
+/* Pointers */
+
+/* Start of buffer */
+static struct pcm_frame_header * ALIGNED_ATTR(4) pcm_buffer;
+/* End of buffer (not guard) */
+static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_end;
+/* Read pointer */
+static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_head IBSS_ATTR;
+/* Write pointer */
+static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR;
+
+/* Bytes */
+static ssize_t pcmbuf_curr_size IBSS_ATTR; /* Size of currently playing frame */
+static ssize_t pcmbuf_read IBSS_ATTR; /* Number of bytes read by DMA */
+static ssize_t pcmbuf_written IBSS_ATTR; /* Number of bytes written by source */
+static ssize_t pcmbuf_threshold IBSS_ATTR; /* Non-silence threshold */
+
+/* Clock */
+static uint32_t clock_start IBSS_ATTR; /* Clock at playback start */
+static uint32_t volatile clock_tick IBSS_ATTR; /* Our base clock */
+static uint32_t volatile clock_time IBSS_ATTR; /* Timestamp adjusted */
+
+static int pcm_skipped = 0;
+static int pcm_underruns = 0;
+
+static unsigned int old_sampr = 0;
+
+/* Small silence clip. ~5.80ms @ 44.1kHz */
+static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 };
+
+/* Delete all buffer contents */
+static void pcm_reset_buffer(void)
+{
+ pcmbuf_threshold = PCMOUT_PLAY_WM;
+ pcmbuf_curr_size = pcmbuf_read = pcmbuf_written = 0;
+ pcmbuf_head = pcmbuf_tail = pcm_buffer;
+ pcm_skipped = pcm_underruns = 0;
+}
+
+/* Advance a PCM buffer pointer by size bytes circularly */
+static inline void pcm_advance_buffer(struct pcm_frame_header **p,
+ size_t size)
+{
+ *p = SKIPBYTES(*p, size);
+ if (*p >= pcmbuf_end)
+ *p = SKIPBYTES(*p, -PCMOUT_BUFSIZE);
+}
+
+/* Return physical space used */
+static inline ssize_t pcm_output_bytes_used(void)
+{
+ return pcmbuf_written - pcmbuf_read; /* wrap-safe */
+}
+
+/* Return physical space free */
+static inline ssize_t pcm_output_bytes_free(void)
+{
+ return PCMOUT_BUFSIZE - pcm_output_bytes_used();
+}
+
+/* Audio DMA handler */
+static void get_more(const void **start, size_t *size)
+{
+ ssize_t sz;
+
+ /* Free-up the last frame played frame if any */
+ pcmbuf_read += pcmbuf_curr_size;
+ pcmbuf_curr_size = 0;
+
+ sz = pcm_output_bytes_used();
+
+ if (sz > pcmbuf_threshold)
+ {
+ pcmbuf_threshold = PCMOUT_LOW_WM;
+
+ while (1)
+ {
+ uint32_t time = pcmbuf_head->time;
+ int32_t offset = time - clock_time;
+
+ sz = pcmbuf_head->size;
+
+ if (sz < (ssize_t)(PCM_HDR_SIZE + 4) ||
+ (sz & 3) != 0)
+ {
+ /* Just show a warning about this - will never happen
+ * without a corrupted buffer */
+ DEBUGF("get_more: invalid size (%ld)\n", (long)sz);
+ }
+
+ if (offset < -100*CLOCK_RATE/1000)
+ {
+ /* Frame more than 100ms late - drop it */
+ pcm_advance_buffer(&pcmbuf_head, sz);
+ pcmbuf_read += sz;
+ pcm_skipped++;
+ if (pcm_output_bytes_used() > 0)
+ continue;
+
+ /* Ran out so revert to default watermark */
+ pcmbuf_threshold = PCMOUT_PLAY_WM;
+ pcm_underruns++;
+ }
+ else if (offset < 100*CLOCK_RATE/1000)
+ {
+ /* Frame less than 100ms early - play it */
+ struct pcm_frame_header *head = pcmbuf_head;
+
+ pcm_advance_buffer(&pcmbuf_head, sz);
+ pcmbuf_curr_size = sz;
+
+ sz -= PCM_HDR_SIZE;
+
+ /* Audio is time master - keep clock synchronized */
+ clock_time = time + (sz >> 2);
+
+ /* Update base clock */
+ clock_tick += sz >> 2;
+
+ *start = head->data;
+ *size = sz;
+ return;
+ }
+ /* Frame will be dropped - play silence clip */
+ break;
+ }
+ }
+ else
+ {
+ /* Ran out so revert to default watermark */
+ if (pcmbuf_threshold == PCMOUT_LOW_WM)
+ pcm_underruns++;
+
+ pcmbuf_threshold = PCMOUT_PLAY_WM;
+ }
+
+ /* Keep clock going at all times */
+ clock_time += sizeof (silence) / 4;
+ clock_tick += sizeof (silence) / 4;
+
+ *start = silence;
+ *size = sizeof (silence);
+
+ if (sz < 0)
+ pcmbuf_read = pcmbuf_written;
+}
+
+/** Public interface **/
+
+/* Return a buffer pointer if at least size bytes are available and if so,
+ * give the actual free space */
+void * pcm_output_get_buffer(ssize_t *size)
+{
+ ssize_t sz = *size;
+ ssize_t free = pcm_output_bytes_free() - PCM_HDR_SIZE;
+
+ if (sz >= 0 && free >= sz)
+ {
+ *size = free; /* return actual free space (- header) */
+ return pcmbuf_tail->data;
+ }
+
+ /* Leave *size alone so caller doesn't have to reinit */
+ return NULL;
+}
+
+/* Commit the buffer returned by pcm_ouput_get_buffer; timestamp is PCM
+ * clock time units, not video format time units */
+bool pcm_output_commit_data(ssize_t size, uint32_t timestamp)
+{
+ if (size <= 0 || (size & 3))
+ return false; /* invalid */
+
+ size += PCM_HDR_SIZE;
+
+ if (size > pcm_output_bytes_free())
+ return false; /* too big */
+
+ pcmbuf_tail->size = size;
+ pcmbuf_tail->time = timestamp;
+
+ pcm_advance_buffer(&pcmbuf_tail, size);
+ pcmbuf_written += size;
+
+ return true;
+}
+
+/* Returns 'true' if the buffer is completely empty */
+bool pcm_output_empty(void)
+{
+ return pcm_output_bytes_used() <= 0;
+}
+
+/* Flushes the buffer - clock keeps counting */
+void pcm_output_flush(void)
+{
+ rb->pcm_play_lock();
+
+ enum channel_status status = rb->mixer_channel_status(MPEG_PCM_CHANNEL);
+
+ /* Stop PCM to clear current buffer */
+ if (status != CHANNEL_STOPPED)
+ rb->mixer_channel_stop(MPEG_PCM_CHANNEL);
+
+ rb->pcm_play_unlock();
+
+ pcm_reset_buffer();
+
+ /* Restart if playing state was current */
+ if (status == CHANNEL_PLAYING)
+ rb->mixer_channel_play_data(MPEG_PCM_CHANNEL,
+ get_more, NULL, 0);
+}
+
+/* Seek the reference clock to the specified time - next audio data ready to
+ go to DMA should be on the buffer with the same time index or else the PCM
+ buffer should be empty */
+void pcm_output_set_clock(uint32_t time)
+{
+ rb->pcm_play_lock();
+
+ clock_start = time;
+ clock_tick = time;
+ clock_time = time;
+
+ rb->pcm_play_unlock();
+}
+
+/* Return the clock as synchronized by audio frame timestamps */
+uint32_t pcm_output_get_clock(void)
+{
+ uint32_t time, rem;
+
+ /* Reread if data race detected - rem will be 0 if driver hasn't yet
+ * updated to the new buffer size. Also be sure pcm state doesn't
+ * cause indefinite loop.
+ *
+ * FYI: NOT scrutinized for rd/wr reordering on different cores. */
+ do
+ {
+ time = clock_time;
+ rem = rb->mixer_channel_get_bytes_waiting(MPEG_PCM_CHANNEL) >> 2;
+ }
+ while (UNLIKELY(time != clock_time ||
+ (rem == 0 &&
+ rb->mixer_channel_status(MPEG_PCM_CHANNEL) == CHANNEL_PLAYING))
+ );
+
+ return time - rem;
+
+}
+
+/* Return the raw clock as counted from the last pcm_output_set_clock
+ * call */
+uint32_t pcm_output_get_ticks(uint32_t *start)
+{
+ uint32_t tick, rem;
+
+ /* Same procedure as pcm_output_get_clock */
+ do
+ {
+ tick = clock_tick;
+ rem = rb->mixer_channel_get_bytes_waiting(MPEG_PCM_CHANNEL) >> 2;
+ }
+ while (UNLIKELY(tick != clock_tick ||
+ (rem == 0 &&
+ rb->mixer_channel_status(MPEG_PCM_CHANNEL) == CHANNEL_PLAYING))
+ );
+
+ if (start)
+ *start = clock_start;
+
+ return tick - rem;
+}
+
+/* Pauses/Starts pcm playback - and the clock */
+void pcm_output_play_pause(bool play)
+{
+ rb->pcm_play_lock();
+
+ if (rb->mixer_channel_status(MPEG_PCM_CHANNEL) != CHANNEL_STOPPED)
+ {
+ rb->mixer_channel_play_pause(MPEG_PCM_CHANNEL, play);
+ rb->pcm_play_unlock();
+ }
+ else
+ {
+ rb->pcm_play_unlock();
+
+ if (play)
+ {
+ rb->mixer_channel_set_amplitude(MPEG_PCM_CHANNEL, MIX_AMP_UNITY);
+ rb->mixer_channel_play_data(MPEG_PCM_CHANNEL,
+ get_more, NULL, 0);
+ }
+ }
+}
+
+/* Stops all playback and resets the clock */
+void pcm_output_stop(void)
+{
+ rb->pcm_play_lock();
+
+ if (rb->mixer_channel_status(MPEG_PCM_CHANNEL) != CHANNEL_STOPPED)
+ rb->mixer_channel_stop(MPEG_PCM_CHANNEL);
+
+ rb->pcm_play_unlock();
+
+ pcm_output_flush();
+ pcm_output_set_clock(0);
+}
+
+/* Drains any data if the start threshold hasn't been reached */
+void pcm_output_drain(void)
+{
+ rb->pcm_play_lock();
+ pcmbuf_threshold = PCMOUT_LOW_WM;
+ rb->pcm_play_unlock();
+}
+
+bool pcm_output_init(void)
+{
+ pcm_buffer = mpeg_malloc(PCMOUT_ALLOC_SIZE, MPEG_ALLOC_PCMOUT);
+ if (pcm_buffer == NULL)
+ return false;
+
+ pcmbuf_end = SKIPBYTES(pcm_buffer, PCMOUT_BUFSIZE);
+
+ pcm_reset_buffer();
+
+#if INPUT_SRC_CAPS != 0
+ /* Select playback */
+ rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
+ rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
+#endif
+
+#if SILENCE_TEST_TONE
+ /* Make the silence clip a square wave */
+ const int16_t silence_amp = INT16_MAX / 16;
+ unsigned i;
+
+ for (i = 0; i < ARRAYLEN(silence); i += 2)
+ {
+ if (i < ARRAYLEN(silence)/2)
+ {
+ silence[i] = silence_amp;
+ silence[i+1] = silence_amp;
+ }
+ else
+ {
+ silence[i] = -silence_amp;
+ silence[i+1] = -silence_amp;
+ }
+ }
+#endif
+
+ old_sampr = rb->mixer_get_frequency();
+ rb->mixer_set_frequency(CLOCK_RATE);
+ rb->pcmbuf_fade(false, true);
+ return true;
+}
+
+void pcm_output_exit(void)
+{
+ rb->pcmbuf_fade(false, false);
+ if (old_sampr != 0)
+ rb->mixer_set_frequency(old_sampr);
+}
diff --git a/apps/plugins/mpegplayer/pcm_output.h b/apps/plugins/mpegplayer/pcm_output.h
new file mode 100644
index 0000000000..bae00cd045
--- /dev/null
+++ b/apps/plugins/mpegplayer/pcm_output.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * PCM output buffer declarations
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef PCM_OUTPUT_H
+#define PCM_OUTPUT_H
+
+#define PCM_HDR_SIZE (sizeof (struct pcm_frame_header))
+struct pcm_frame_header /* Header added to pcm data every time a decoded
+ audio frame is sent out */
+{
+ uint32_t size; /* size of this frame - including header */
+ uint32_t time; /* timestamp for this frame in audio ticks */
+ unsigned char data[]; /* open array of audio data */
+} ALIGNED_ATTR(4);
+
+bool pcm_output_init(void);
+void pcm_output_exit(void);
+void pcm_output_flush(void);
+void pcm_output_set_clock(uint32_t time);
+uint32_t pcm_output_get_clock(void);
+uint32_t pcm_output_get_ticks(uint32_t *start);
+void pcm_output_play_pause(bool play);
+void pcm_output_stop(void);
+void pcm_output_drain(void);
+void * pcm_output_get_buffer(ssize_t *size);
+bool pcm_output_commit_data(ssize_t size, uint32_t timestamp);
+bool pcm_output_empty(void);
+
+#endif /* PCM_OUTPUT_H */
diff --git a/apps/plugins/mpegplayer/stream_mgr.c b/apps/plugins/mpegplayer/stream_mgr.c
new file mode 100644
index 0000000000..3cac8c0f57
--- /dev/null
+++ b/apps/plugins/mpegplayer/stream_mgr.c
@@ -0,0 +1,1163 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * AV stream manager implementation
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "mpegplayer.h"
+#include "lib/grey.h"
+#include "mpeg_settings.h"
+
+#ifndef HAVE_LCD_COLOR
+GREY_INFO_STRUCT_IRAM
+#endif
+
+static struct event_queue stream_mgr_queue SHAREDBSS_ATTR;
+static struct queue_sender_list stream_mgr_queue_send SHAREDBSS_ATTR;
+static uint32_t stream_mgr_thread_stack[DEFAULT_STACK_SIZE*2/sizeof(uint32_t)];
+
+struct stream_mgr stream_mgr SHAREDBSS_ATTR;
+
+/* Forward decs */
+static int stream_on_close(void);
+
+struct str_broadcast_data
+{
+ long cmd; /* Command to send to stream */
+ intptr_t data; /* Data to send with command */
+};
+
+static inline void stream_mgr_lock(void)
+{
+ rb->mutex_lock(&stream_mgr.str_mtx);
+}
+
+static inline void stream_mgr_unlock(void)
+{
+ rb->mutex_unlock(&stream_mgr.str_mtx);
+}
+
+static inline void actl_lock(void)
+{
+ rb->mutex_lock(&stream_mgr.actl_mtx);
+}
+
+static inline void actl_unlock(void)
+{
+ rb->mutex_unlock(&stream_mgr.actl_mtx);
+}
+
+static inline void stream_mgr_post_msg(long id, intptr_t data)
+{
+ rb->queue_post(stream_mgr.q, id, data);
+}
+
+static inline intptr_t stream_mgr_send_msg(long id, intptr_t data)
+{
+ return rb->queue_send(stream_mgr.q, id, data);
+}
+
+static inline void stream_mgr_reply_msg(intptr_t retval)
+{
+ rb->queue_reply(stream_mgr.q, retval);
+}
+
+int str_next_data_not_ready(struct stream *str)
+{
+ /* Save the current window since it actually might be ready by the time
+ * the registration is received by buffering. */
+ off_t win_right = str->hdr.win_right;
+
+ if (str->hdr.win_right < disk_buf.filesize - MIN_BUFAHEAD &&
+ disk_buf.filesize > MIN_BUFAHEAD)
+ {
+ /* Set right edge to where probing left off + the minimum margin */
+ str->hdr.win_right += MIN_BUFAHEAD;
+ }
+ else
+ {
+ /* Request would be passed the end of the file */
+ str->hdr.win_right = disk_buf.filesize;
+ }
+
+ switch (disk_buf_send_msg(DISK_BUF_DATA_NOTIFY, (intptr_t)str))
+ {
+ case DISK_BUF_NOTIFY_OK:
+ /* Was ready - restore window and process */
+ str->hdr.win_right = win_right;
+ return STREAM_OK;
+
+ case DISK_BUF_NOTIFY_ERROR:
+ /* Error - quit parsing */
+ str_end_of_stream(str);
+ return STREAM_DATA_END;
+
+ default:
+ /* Not ready - go wait for notification from buffering. */
+ str->pkt_flags = 0;
+ return STREAM_DATA_NOT_READY;
+ }
+}
+
+void str_data_notify_received(struct stream *str)
+{
+ /* Normalize win_right back to the packet length */
+ if (str->state == SSTATE_END)
+ return;
+
+ if (str->curr_packet == NULL)
+ {
+ /* Nothing was yet parsed since init */
+ str->hdr.win_right = str->hdr.win_left;
+ }
+ else
+ {
+ /* Restore window based upon current packet */
+ str->hdr.win_right = str->hdr.win_left +
+ (str->curr_packet_end - str->curr_packet);
+ }
+}
+
+/* Set stream manager to a "no-file" state */
+static void stream_mgr_init_state(void)
+{
+ stream_mgr.filename = NULL;
+ stream_mgr.resume_time = INVALID_TIMESTAMP;
+ stream_mgr.seeked = false;
+}
+
+/* Add a stream to the playback pool */
+void stream_add_stream(struct stream *str)
+{
+ actl_lock();
+
+ list_remove_item(stream_mgr.strl, str);
+ list_add_item(stream_mgr.strl, str);
+
+ actl_unlock();
+}
+
+/* Callback for various list-moving operations */
+static bool strl_enum_callback(struct stream *str, void *data)
+{
+ actl_lock();
+
+ list_remove_item(stream_mgr.strl, str);
+
+ if (*(int*)data == 1)
+ list_add_item(stream_mgr.actl, str);
+
+ actl_unlock();
+
+ return true;
+}
+
+/* Clear all streams from active and playback pools */
+void stream_remove_streams(void)
+{
+ int add_item = 0;
+ list_enum_items(stream_mgr.strl,
+ (list_enum_callback_t)strl_enum_callback, (void *)&add_item);
+}
+
+/* Move the playback pool to the active list */
+void move_strl_to_actl(void)
+{
+ int add_item = 1;
+ list_enum_items(stream_mgr.strl,
+ (list_enum_callback_t)strl_enum_callback, (void *)&add_item);
+}
+
+/* Remove a stream from the active list and return it to the pool */
+static bool actl_stream_remove(struct stream *str)
+{
+ bool retval;
+
+ actl_lock();
+
+ retval = list_remove_item(stream_mgr.actl, str);
+
+ if (retval)
+ list_add_item(stream_mgr.strl, str);
+
+ actl_unlock();
+
+ return retval;
+}
+
+/* Broadcast a message