summaryrefslogtreecommitdiff
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-9d3d925295112a0080bc1d70fad170db9e1af2a9.tar.gz
rockbox-9d3d925295112a0080bc1d70fad170db9e1af2a9.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 @@
14264 </voice> 14264 </voice>
14265</phrase> 14265</phrase>
14266<phrase> 14266<phrase>
14267 id: LANG_MENU_AUDIO_OPTIONS
14268 desc: in mpegplayer menus
14269 user: core
14270 <source>
14271 *: "Audio Options"
14272 lowmem: none
14273 </source>
14274 <dest>
14275 *: "Audio Options"
14276 lowmem: none
14277 </dest>
14278 <voice>
14279 *: "Audio Options"
14280 lowmem: none
14281 </voice>
14282</phrase>
14283<phrase>
14284 id: LANG_MENU_RESUME_OPTIONS
14285 desc: in mpegplayer menus
14286 user: core
14287 <source>
14288 *: "Resume Options"
14289 lowmem: none
14290 </source>
14291 <dest>
14292 *: "Resume Options"
14293 lowmem: none
14294 </dest>
14295 <voice>
14296 *: "Resume Options"
14297 lowmem: none
14298 </voice>
14299</phrase>
14300<phrase>
14301 id: LANG_MENU_PLAY_MODE
14302 desc: in mpegplayer menus
14303 user: core
14304 <source>
14305 *: "Play Mode"
14306 lowmem: none
14307 </source>
14308 <dest>
14309 *: "Play Mode"
14310 lowmem: none
14311 </dest>
14312 <voice>
14313 *: "Play Mode"
14314 lowmem: none
14315 </voice>
14316</phrase>
14317<phrase>
14318 id: LANG_SINGLE
14319 desc: in mpegplayer menus
14320 user: core
14321 <source>
14322 *: "Single"
14323 lowmem: none
14324 </source>
14325 <dest>
14326 *: "Single"
14327 lowmem: none
14328 </dest>
14329 <voice>
14330 *: "Single"
14331 lowmem: none
14332 </voice>
14333</phrase>
14334<phrase>
14335 id: LANG_USE_SOUND_SETTING
14336 desc: in mpegplayer menus
14337 user: core
14338 <source>
14339 *: "Use sound setting"
14340 lowmem: none
14341 </source>
14342 <dest>
14343 *: "Use sound setting"
14344 lowmem: none
14345 </dest>
14346 <voice>
14347 *: "Use sound setting"
14348 lowmem: none
14349 </voice>
14350</phrase>
14351<phrase>
14352 id: LANG_RESTART_PLAYBACK
14353 desc: in the mpegplayer settings menu
14354 user: core
14355 <source>
14356 *: "Play from beginning"
14357 lowmem: none
14358 </source>
14359 <dest>
14360 *: "Play from beginning"
14361 lowmem: none
14362 </dest>
14363 <voice>
14364 *: "Play from beginning"
14365 lowmem: none
14366 </voice>
14367</phrase>
14368<phrase>
14369 id: LANG_SET_RESUME_TIME
14370 desc: in the mpegplayer settings menu
14371 user: core
14372 <source>
14373 *: "Set resume time (min)"
14374 lowmem: none
14375 </source>
14376 <dest>
14377 *: "Set resume time (min)"
14378 lowmem: none
14379 </dest>
14380 <voice>
14381 *: "Set resume time"
14382 lowmem: none
14383 </voice>
14384</phrase>
14385<phrase>
14267 id: LANG_DISPLAY_FPS 14386 id: LANG_DISPLAY_FPS
14268 desc: in the pictureflow settings menus 14387 desc: in the mpegplayer and pictureflow settings menus
14269 user: core 14388 user: core
14270 <source> 14389 <source>
14271 *: "Display FPS" 14390 *: "Display FPS"
@@ -14278,6 +14397,176 @@
14278 </voice> 14397 </voice>
14279</phrase> 14398</phrase>
14280<phrase> 14399<phrase>
14400 id: LANG_LIMIT_FPS
14401 desc: in the mpegplayer settings menu
14402 user: core
14403 <source>
14404 *: "Limit FPS"
14405 lowmem: none
14406 </source>
14407 <dest>
14408 *: "Limit FPS"
14409 lowmem: none
14410 </dest>
14411 <voice>
14412 *: "Limit FPS"
14413 lowmem: none
14414 </voice>
14415</phrase>
14416<phrase>
14417 id: LANG_SKIP_FRAMES
14418 desc: in the mpegplayer settings menu
14419 user: core
14420 <source>
14421 *: "Skip frames"
14422 lowmem: none
14423 </source>
14424 <dest>
14425 *: "Skip frames"
14426 lowmem: none
14427 </dest>
14428 <voice>
14429 *: "Skip frames"
14430 lowmem: none
14431 </voice>
14432</phrase>
14433<phrase>
14434 id: LANG_BACKLIGHT_BRIGHTNESS
14435 desc: in the mpegplayer settings menu
14436 user: core
14437 <source>
14438 *: "Backlight brightness"
14439 lowmem: none
14440 </source>
14441 <dest>
14442 *: "Backlight brightness"
14443 lowmem: none
14444 </dest>
14445 <voice>
14446 *: "Backlight brightness"
14447 lowmem: none
14448 </voice>
14449</phrase>
14450<phrase>
14451 id: LANG_USE_COMMON_SETTING
14452 desc: in the mpegplayer settings menu
14453 user: core
14454 <source>
14455 *: "Use common setting"
14456 lowmem: none
14457 </source>
14458 <dest>
14459 *: "Use common setting"
14460 lowmem: none
14461 </dest>
14462 <voice>
14463 *: "Use common setting"
14464 lowmem: none
14465 </voice>
14466</phrase>
14467<phrase>
14468 id: LANG_TONE_CONTROLS
14469 desc: in the mpegplayer settings menu
14470 user: core
14471 <source>
14472 *: "Tone controls"
14473 lowmem: none
14474 </source>
14475 <dest>
14476 *: "Tone controls"
14477 lowmem: none
14478 </dest>
14479 <voice>
14480 *: "Tone controls"
14481 lowmem: none
14482 </voice>
14483</phrase>
14484<phrase>
14485 id: LANG_FORCE_START_MENU
14486 desc: in mpegplayer menus
14487 user: core
14488 <source>
14489 *: "Start menu"
14490 lowmem: none
14491 </source>
14492 <dest>
14493 *: "Start menu"
14494 lowmem: none
14495 </dest>
14496 <voice>
14497 *: "Start menu"
14498 lowmem: none
14499 </voice>
14500</phrase>
14501<phrase>
14502 id: LANG_CONDITIONAL_START_MENU
14503 desc: in mpegplayer menus
14504 user: core
14505 <source>
14506 *: "Start menu if not completed"
14507 lowmem: none
14508 </source>
14509 <dest>
14510 *: "Start menu if not completed"
14511 lowmem: none
14512 </dest>
14513 <voice>
14514 *: "Start menu if not completed"
14515 lowmem: none
14516 </voice>
14517</phrase>
14518<phrase>
14519 id: LANG_AUTO_RESUME
14520 desc: in mpegplayer menus
14521 user: core
14522 <source>
14523 *: "Resume automatically"
14524 lowmem: none
14525 </source>
14526 <dest>
14527 *: "Resume automatically"
14528 lowmem: none
14529 </dest>
14530 <voice>
14531 *: "Resume automatically"
14532 lowmem: none
14533 </voice>
14534</phrase>
14535<phrase>
14536 id: LANG_CLEAR_ALL_RESUMES
14537 desc: in the mpegplayer settings menu
14538 user: core
14539 <source>
14540 *: "Clear all resumes"
14541 lowmem: none
14542 </source>
14543 <dest>
14544 *: "Clear all resumes"
14545 lowmem: none
14546 </dest>
14547 <voice>
14548 *: "Clear all resumes"
14549 lowmem: none
14550 </voice>
14551</phrase>
14552<phrase>
14553 id: LANG_UNAVAILABLE
14554 desc: in mpegplayer settings
14555 user: core
14556 <source>
14557 *: "Unavailable"
14558 lowmem: none
14559 </source>
14560 <dest>
14561 *: "Unavailable"
14562 lowmem: none
14563 </dest>
14564 <voice>
14565 *: "Unavailable"
14566 lowmem: none
14567 </voice>
14568</phrase>
14569<phrase>
14281 id: LANG_TOGGLE_ITEM 14570 id: LANG_TOGGLE_ITEM
14282 desc: in main_menu_config 14571 desc: in main_menu_config
14283 user: core 14572 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
68minesweeper,games 68minesweeper,games
69mosaique,demos 69mosaique,demos
70mp3_encoder,apps 70mp3_encoder,apps
71mpegplayer,viewers
71multiboot_select,apps 72multiboot_select,apps
72nim,games 73nim,games
73open_plugins,viewers 74open_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
78pdbox 78pdbox
79#endif 79#endif
80 80
81#if !defined(RB_PROFILE) && MEMORYSIZE > 2 /* mpegplayer allocates at least 2MB of RAM */
82mpegplayer
83#endif
84
81/* Lua needs at least 160 KB to work in */ 85/* Lua needs at least 160 KB to work in */
82#if PLUGIN_BUFFER_SIZE >= 0x80000 86#if PLUGIN_BUFFER_SIZE >= 0x80000
83lua 87lua
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
49#endif 49#endif
50#endif 50#endif
51 51
52/* MPEGplayer */
53mpegplayer_status_icons_8x8x1.bmp
54mpegplayer_status_icons_12x12x1.bmp
55mpegplayer_status_icons_16x16x1.bmp
56
52#if LCD_WIDTH == 160 && LCD_HEIGHT == 128 && LCD_DEPTH < 16 57#if LCD_WIDTH == 160 && LCD_HEIGHT == 128 && LCD_DEPTH < 16
53superdom_boarditems.160x128x1.bmp 58superdom_boarditems.160x128x1.bmp
54#endif 59#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 @@
1libmpeg2/decode.c
2libmpeg2/header.c
3libmpeg2/idct.c
4libmpeg2/motion_comp.c
5libmpeg2/slice.c
6
7#ifdef CPU_COLDFIRE
8libmpeg2/idct_coldfire.S
9libmpeg2/motion_comp_coldfire_c.c
10libmpeg2/motion_comp_coldfire_s.S
11#elif defined CPU_ARM
12#if ARM_ARCH >= 6
13libmpeg2/idct_armv6.S
14#else
15libmpeg2/idct_arm.S
16#endif
17libmpeg2/motion_comp_arm_c.c
18libmpeg2/motion_comp_arm_s.S
19#else /* other CPU or SIM */
20libmpeg2/motion_comp_c.c
21#endif /* CPU_* */
22
23
24
25alloc.c
26video_out_rockbox.c
27video_thread.c
28pcm_output.c
29audio_thread.c
30disk_buf.c
31mpeg_settings.c
32stream_mgr.c
33mpegplayer.c
34mpeg_parser.c
35mpeg_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 @@
1/*
2 * alloc.c
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.13
26 */
27
28#include "plugin.h"
29#include "mpegplayer.h"
30#include <system.h>
31
32/* Main allocator */
33static off_t mem_ptr;
34static size_t bufsize;
35static unsigned char* mallocbuf;
36
37/* libmpeg2 allocator */
38static off_t mpeg2_mem_ptr SHAREDBSS_ATTR;
39static size_t mpeg2_bufsize SHAREDBSS_ATTR;
40static unsigned char *mpeg2_mallocbuf SHAREDBSS_ATTR;
41static unsigned char *mpeg2_bufallocbuf SHAREDBSS_ATTR;
42
43#if defined(DEBUG) || defined(SIMULATOR)
44const char * mpeg_get_reason_str(int reason)
45{
46 const char *str;
47
48 switch (reason)
49 {
50 case MPEG2_ALLOC_MPEG2DEC:
51 str = "MPEG2_ALLOC_MPEG2DEC";
52 break;
53 case MPEG2_ALLOC_CHUNK:
54 str = "MPEG2_ALLOC_CHUNK";
55 break;
56 case MPEG2_ALLOC_YUV:
57 str = "MPEG2_ALLOC_YUV";
58 break;
59 case MPEG2_ALLOC_CONVERT_ID:
60 str = "MPEG2_ALLOC_CONVERT_ID";
61 break;
62 case MPEG2_ALLOC_CONVERTED:
63 str = "MPEG2_ALLOC_CONVERTED";
64 break;
65 case MPEG_ALLOC_MPEG2_BUFFER:
66 str = "MPEG_ALLOC_MPEG2_BUFFER";
67 break;
68 case MPEG_ALLOC_AUDIOBUF:
69 str = "MPEG_ALLOC_AUDIOBUF";
70 break;
71 case MPEG_ALLOC_PCMOUT:
72 str = "MPEG_ALLOC_PCMOUT";
73 break;
74 case MPEG_ALLOC_DISKBUF:
75 str = "MPEG_ALLOC_DISKBUF";
76 break;
77 case MPEG_ALLOC_CODEC_MALLOC:
78 str = "MPEG_ALLOC_CODEC_MALLOC";
79 break;
80 case MPEG_ALLOC_CODEC_CALLOC:
81 str = "MPEG_ALLOC_CODEC_CALLOC";
82 break;
83 default:
84 str = "Unknown";
85 }
86
87 return str;
88}
89#endif
90
91static void * mpeg_malloc_internal (unsigned char *mallocbuf,
92 off_t *mem_ptr,
93 size_t bufsize,
94 unsigned size,
95 int reason)
96{
97 void *x;
98
99 DEBUGF("mpeg_alloc_internal: bs:%lu s:%u reason:%s (%d)\n",
100 (unsigned long)bufsize, size, mpeg_get_reason_str(reason), reason);
101
102 if ((size_t) (*mem_ptr + size) > bufsize)
103 {
104 DEBUGF("OUT OF MEMORY\n");
105 return NULL;
106 }
107
108 x = &mallocbuf[*mem_ptr];
109 *mem_ptr += (size + 3) & ~3; /* Keep memory 32-bit aligned */
110
111 return x;
112 (void)reason;
113}
114
115void *mpeg_malloc(size_t size, mpeg2_alloc_t reason)
116{
117 return mpeg_malloc_internal(mallocbuf, &mem_ptr, bufsize, size,
118 reason);
119}
120
121void *mpeg_malloc_all(size_t *size_out, mpeg2_alloc_t reason)
122{
123 /* Can steal all but MIN_MEMMARGIN */
124 if (bufsize - mem_ptr < MIN_MEMMARGIN)
125 return NULL;
126
127 *size_out = bufsize - mem_ptr - MIN_MEMMARGIN;
128 return mpeg_malloc(*size_out, reason);
129}
130
131bool mpeg_alloc_init(unsigned char *buf, size_t mallocsize)
132{
133 mem_ptr = 0;
134 /* Cache-align buffer or 4-byte align */
135 mallocbuf = buf;
136 bufsize = mallocsize;
137 ALIGN_BUFFER(mallocbuf, bufsize, CACHEALIGN_UP(4));
138
139 /* Separate allocator for video */
140 mpeg2_mem_ptr = 0;
141 mpeg2_mallocbuf = mallocbuf;
142 mpeg2_bufallocbuf = mallocbuf;
143 mpeg2_bufsize = CACHEALIGN_UP(LIBMPEG2_ALLOC_SIZE);
144
145 if (mpeg_malloc_internal(mallocbuf, &mem_ptr,
146 bufsize, mpeg2_bufsize,
147 MPEG_ALLOC_MPEG2_BUFFER) == NULL)
148 {
149 return false;
150 }
151
152 IF_COP(rb->commit_discard_dcache());
153 return true;
154}
155
156/* allocate non-dedicated buffer space which mpeg2_mem_reset will free */
157void * mpeg2_malloc(unsigned size, mpeg2_alloc_t reason)
158{
159 void *ptr = mpeg_malloc_internal(mpeg2_mallocbuf, &mpeg2_mem_ptr,
160 mpeg2_bufsize, size, reason);
161 /* libmpeg2 expects zero-initialized allocations */
162 if (ptr)
163 rb->memset(ptr, 0, size);
164
165 return ptr;
166}
167
168/* allocate dedicated buffer - memory behind buffer pointer becomes dedicated
169 so order is important */
170void * mpeg2_bufalloc(unsigned size, mpeg2_alloc_t reason)
171{
172 void *buf = mpeg2_malloc(size, reason);
173
174 if (buf == NULL)
175 return NULL;
176
177 mpeg2_bufallocbuf = &mpeg2_mallocbuf[mpeg2_mem_ptr];
178 return buf;
179}
180
181/* return unused buffer portion and size */
182void * mpeg2_get_buf(size_t *size)
183{
184 if ((size_t)mpeg2_mem_ptr + 32 >= mpeg2_bufsize)
185 return NULL;
186
187 *size = mpeg2_bufsize - mpeg2_mem_ptr;
188 return &mpeg2_mallocbuf[mpeg2_mem_ptr];
189}
190
191/* de-allocate all non-dedicated buffer space */
192void mpeg2_mem_reset(void)
193{
194 DEBUGF("mpeg2_mem_reset\n");
195 mpeg2_mem_ptr = mpeg2_bufallocbuf - mpeg2_mallocbuf;
196}
197
198/* The following are expected by libmad */
199void * codec_malloc(size_t size)
200{
201 void* ptr;
202
203 ptr = mpeg_malloc_internal(mallocbuf, &mem_ptr,
204 bufsize, size, MPEG_ALLOC_CODEC_MALLOC);
205
206 if (ptr)
207 rb->memset(ptr,0,size);
208
209 return ptr;
210}
211
212void * codec_calloc(size_t nmemb, size_t size)
213{
214 void* ptr;
215
216 ptr = mpeg_malloc_internal(mallocbuf, &mem_ptr,
217 bufsize, nmemb*size,
218 MPEG_ALLOC_CODEC_CALLOC);
219
220 if (ptr)
221 rb->memset(ptr,0,size);
222
223 return ptr;
224}
225
226void codec_free(void* ptr)
227{
228 DEBUGF("codec_free - %p\n", ptr);
229#if 0
230 mem_ptr = (void *)ptr - (void *)mallocbuf;
231#endif
232 (void)ptr;
233}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * mpegplayer audio thread implementation
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "plugin.h"
24#include "mpegplayer.h"
25#include "codecs/libmad/bit.h"
26#include "codecs/libmad/mad.h"
27
28/** Audio stream and thread **/
29struct pts_queue_slot;
30struct audio_thread_data
31{
32 struct queue_event ev; /* Our event queue to receive commands */
33 int state; /* Thread state */
34 int status; /* Media status (STREAM_PLAYING, etc.) */
35 int mad_errors; /* A count of the errors in each frame */
36 unsigned samplerate; /* Current stream sample rate */
37 int nchannels; /* Number of audio channels */
38 struct dsp_config *dsp; /* The DSP we're using */
39 struct dsp_buffer src; /* Current audio data for DSP processing */
40};
41
42/* The audio thread is stolen from the core codec thread */
43static struct event_queue audio_str_queue SHAREDBSS_ATTR;
44static struct queue_sender_list audio_str_queue_send SHAREDBSS_ATTR;
45struct stream audio_str IBSS_ATTR;
46
47/* libmad related definitions */
48static struct mad_stream stream IBSS_ATTR;
49static struct mad_frame frame IBSS_ATTR;
50static struct mad_synth synth IBSS_ATTR;
51
52/*sbsample buffer for mad_frame*/
53mad_fixed_t sbsample[2][36][32];
54
55/* 2567 bytes */
56static unsigned char mad_main_data[MAD_BUFFER_MDLEN];
57
58/* There isn't enough room for this in IRAM on PortalPlayer, but there
59 is for Coldfire. */
60
61/* 4608 bytes */
62#if defined(CPU_COLDFIRE) || defined(CPU_S5L870X)
63static mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR;
64#else
65static mad_fixed_t mad_frame_overlap[2][32][18];
66#endif
67
68/** A queue for saving needed information about MPEG audio packets **/
69#define AUDIODESC_QUEUE_LEN (1 << 5) /* 32 should be way more than sufficient -
70 if not, the case is handled */
71#define AUDIODESC_QUEUE_MASK (AUDIODESC_QUEUE_LEN-1)
72struct audio_frame_desc
73{
74 uint32_t time; /* Time stamp for packet in audio ticks */
75 ssize_t size; /* Number of unprocessed bytes left in packet */
76};
77
78 /* This starts out wr == rd but will never be emptied to zero during
79 streaming again in order to support initializing the first packet's
80 timestamp without a special case */
81struct
82{
83 /* Compressed audio data */
84 uint8_t *start; /* Start of encoded audio buffer */
85 uint8_t *ptr; /* Pointer to next encoded audio data */
86 ssize_t used; /* Number of bytes in MPEG audio buffer */
87 /* Compressed audio data descriptors */
88 unsigned read, write;
89 struct audio_frame_desc *curr; /* Current slot */
90 struct audio_frame_desc descs[AUDIODESC_QUEUE_LEN];
91} audio_queue;
92
93static inline int audiodesc_queue_count(void)
94{
95 return audio_queue.write - audio_queue.read;
96}
97
98static inline bool audiodesc_queue_full(void)
99{
100 return audio_queue.used >= MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD ||
101 audiodesc_queue_count() >= AUDIODESC_QUEUE_LEN;
102}
103
104/* Increments the queue tail postion - should be used to preincrement */
105static inline void audiodesc_queue_add_tail(void)
106{
107 if (audiodesc_queue_full())
108 {
109 DEBUGF("audiodesc_queue_add_tail: audiodesc queue full!\n");
110 return;
111 }
112
113 audio_queue.write++;
114}
115
116/* Increments the queue head position - leaves one slot as current */
117static inline bool audiodesc_queue_remove_head(void)
118{
119 if (audio_queue.write == audio_queue.read)
120 return false;
121
122 audio_queue.read++;
123 return true;
124}
125
126/* Returns the "tail" at the index just behind the write index */
127static inline struct audio_frame_desc * audiodesc_queue_tail(void)
128{
129 return &audio_queue.descs[(audio_queue.write - 1) & AUDIODESC_QUEUE_MASK];
130}
131
132/* Returns a pointer to the current head */
133static inline struct audio_frame_desc * audiodesc_queue_head(void)
134{
135 return &audio_queue.descs[audio_queue.read & AUDIODESC_QUEUE_MASK];
136}
137
138/* Resets the pts queue - call when starting and seeking */
139static void audio_queue_reset(void)
140{
141 audio_queue.ptr = audio_queue.start;
142 audio_queue.used = 0;
143 audio_queue.read = 0;
144 audio_queue.write = 0;
145 rb->memset(audio_queue.descs, 0, sizeof (audio_queue.descs));
146 audio_queue.curr = audiodesc_queue_head();
147}
148
149static void audio_queue_advance_pos(ssize_t len)
150{
151 audio_queue.ptr += len;
152 audio_queue.used -= len;
153 audio_queue.curr->size -= len;
154}
155
156static int audio_buffer(struct stream *str, enum stream_parse_mode type)
157{
158 int ret = STREAM_OK;
159
160 /* Carry any overshoot to the next size since we're technically
161 -size bytes into it already. If size is negative an audio
162 frame was split across packets. Old has to be saved before
163 moving the head. */
164 if (audio_queue.curr->size <= 0 && audiodesc_queue_remove_head())
165 {
166 struct audio_frame_desc *old = audio_queue.curr;
167 audio_queue.curr = audiodesc_queue_head();
168 audio_queue.curr->size += old->size;
169 old->size = 0;
170 }
171
172 /* Add packets to compressed audio buffer until it's full or the
173 * timestamp queue is full - whichever happens first */
174 while (!audiodesc_queue_full())
175 {
176 ret = parser_get_next_data(str, type);
177 struct audio_frame_desc *curr;
178 ssize_t len;
179
180 if (ret != STREAM_OK)
181 break;
182
183 /* Get data from next audio packet */
184 len = str->curr_packet_end - str->curr_packet;
185
186 if (str->pkt_flags & PKT_HAS_TS)
187 {
188 audiodesc_queue_add_tail();
189 curr = audiodesc_queue_tail();
190 curr->time = TS_TO_TICKS(str->pts);
191 /* pts->size should have been zeroed when slot was
192 freed */
193 }
194 else
195 {
196 /* Add to the one just behind the tail - this may be
197 * the head or the previouly added tail - whether or
198 * not we'll ever reach this is quite in question
199 * since audio always seems to have every packet
200 * timestamped */
201 curr = audiodesc_queue_tail();
202 }
203
204 curr->size += len;
205
206 /* Slide any remainder over to beginning */
207 if (audio_queue.ptr > audio_queue.start && audio_queue.used > 0)
208 {
209 rb->memmove(audio_queue.start, audio_queue.ptr,
210 audio_queue.used);
211 }
212
213 /* Splice this packet onto any remainder */
214 rb->memcpy(audio_queue.start + audio_queue.used,
215 str->curr_packet, len);
216
217 audio_queue.used += len;
218 audio_queue.ptr = audio_queue.start;
219
220 rb->yield();
221 }
222
223 return ret;
224}
225
226/* Initialise libmad */
227static void init_mad(void)
228{
229 /* init the sbsample buffer */
230 frame.sbsample_prev = &sbsample;
231 frame.sbsample = &sbsample;
232
233 /* We do this so libmad doesn't try to call codec_calloc(). This needs to
234 * be called before mad_stream_init(), mad_frame_inti() and
235 * mad_synth_init(). */
236 frame.overlap = &mad_frame_overlap;
237 stream.main_data = &mad_main_data;
238
239 /* Call mad initialization. Those will zero the arrays frame.overlap,
240 * frame.sbsample and frame.sbsample_prev. Therefore there is no need to
241 * zero them here. */
242 mad_stream_init(&stream);
243 mad_frame_init(&frame);
244 mad_synth_init(&synth);
245}
246
247/* Sync audio stream to a particular frame - see main decoder loop for
248 * detailed remarks */
249static int audio_sync(struct audio_thread_data *td,
250 struct str_sync_data *sd)
251{
252 int retval = STREAM_MATCH;
253 uint32_t sdtime = TS_TO_TICKS(clip_time(&audio_str, sd->time));
254 uint32_t time;
255 uint32_t duration = 0;
256 struct stream *str;
257 struct stream tmp_str;
258 struct mad_header header;
259 struct mad_stream stream;
260
261 if (td->ev.id == STREAM_SYNC)
262 {
263 /* Actually syncing for playback - use real stream */
264 time = 0;
265 str = &audio_str;
266 }
267 else
268 {
269 /* Probing - use temp stream */
270 time = INVALID_TIMESTAMP;
271 str = &tmp_str;
272 str->id = audio_str.id;
273 }
274
275 str->hdr.pos = sd->sk.pos;
276 str->hdr.limit = sd->sk.pos + sd->sk.len;
277
278 mad_stream_init(&stream);
279 mad_header_init(&header);
280
281 while (1)
282 {
283 if (audio_buffer(str, STREAM_PM_RANDOM_ACCESS) == STREAM_DATA_END)
284 {
285 DEBUGF("audio_sync:STR_DATA_END\n aqu:%ld swl:%ld swr:%ld\n",
286 (long)audio_queue.used, str->hdr.win_left, str->hdr.win_right);
287 if (audio_queue.used <= MAD_BUFFER_GUARD)
288 goto sync_data_end;
289 }
290
291 stream.error = 0;
292 mad_stream_buffer(&stream, audio_queue.ptr, audio_queue.used);
293
294 if (stream.sync && mad_stream_sync(&stream) < 0)
295 {
296 DEBUGF(" audio: mad_stream_sync failed\n");
297 audio_queue_advance_pos(MAX(audio_queue.curr->size - 1, 1));
298 continue;
299 }
300
301 stream.sync = 0;
302
303 if (mad_header_decode(&header, &stream) < 0)
304 {
305 DEBUGF(" audio: mad_header_decode failed:%s\n",
306 mad_stream_errorstr(&stream));
307 audio_queue_advance_pos(1);
308 continue;
309 }
310
311 duration = 32*MAD_NSBSAMPLES(&header);
312 time = audio_queue.curr->time;
313
314 DEBUGF(" audio: ft:%u t:%u fe:%u nsamp:%u sampr:%u\n",
315 (unsigned)TICKS_TO_TS(time), (unsigned)sd->time,
316 (unsigned)TICKS_TO_TS(time + duration),
317 (unsigned)duration, header.samplerate);
318
319 audio_queue_advance_pos(stream.this_frame - audio_queue.ptr);
320
321 if (time <= sdtime && sdtime < time + duration)
322 {
323 DEBUGF(" audio: ft<=t<fe\n");
324 retval = STREAM_PERFECT_MATCH;
325 break;
326 }
327 else if (time > sdtime)
328 {
329 DEBUGF(" audio: ft>t\n");
330 break;
331 }
332
333 audio_queue_advance_pos(stream.next_frame - audio_queue.ptr);
334 audio_queue.curr->time += duration;
335
336 rb->yield();
337 }
338
339sync_data_end:
340 if (td->ev.id == STREAM_FIND_END_TIME)
341 {
342 if (time != INVALID_TIMESTAMP)
343 {
344 time = TICKS_TO_TS(time);
345 duration = TICKS_TO_TS(duration);
346 sd->time = time + duration;
347 retval = STREAM_PERFECT_MATCH;
348 }
349 else
350 {
351 retval = STREAM_NOT_FOUND;
352 }
353 }
354
355 DEBUGF(" audio header: 0x%02X%02X%02X%02X\n",
356 (unsigned)audio_queue.ptr[0], (unsigned)audio_queue.ptr[1],
357 (unsigned)audio_queue.ptr[2], (unsigned)audio_queue.ptr[3]);
358
359 return retval;
360 (void)td;
361}
362
363static void audio_thread_msg(struct audio_thread_data *td)
364{
365 while (1)
366 {
367 intptr_t reply = 0;
368
369 switch (td->ev.id)
370 {
371 case STREAM_PLAY:
372 td->status = STREAM_PLAYING;
373
374 switch (td->state)
375 {
376 case TSTATE_INIT:
377 td->state = TSTATE_DECODE;
378 case TSTATE_DECODE:
379 case TSTATE_RENDER_WAIT:
380 break;
381
382 case TSTATE_EOS:
383 /* At end of stream - no playback possible so fire the
384 * completion event */
385 stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
386 break;
387 }
388
389 break;
390
391 case STREAM_PAUSE:
392 td->status = STREAM_PAUSED;
393 reply = td->state != TSTATE_EOS;
394 break;
395
396 case STREAM_STOP:
397 if (td->state == TSTATE_DATA)
398 stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY);
399
400 td->status = STREAM_STOPPED;
401 td->state = TSTATE_EOS;
402
403 reply = true;
404 break;
405
406 case STREAM_RESET:
407 if (td->state == TSTATE_DATA)
408 stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY);
409
410 td->status = STREAM_STOPPED;
411 td->state = TSTATE_INIT;
412 td->samplerate = 0;
413 td->nchannels = 0;
414
415 init_mad();
416 td->mad_errors = 0;
417
418 audio_queue_reset();
419
420 reply = true;
421 break;
422
423 case STREAM_NEEDS_SYNC:
424 reply = true; /* Audio always needs to */
425 break;
426
427 case STREAM_SYNC:
428 case STREAM_FIND_END_TIME:
429 if (td->state != TSTATE_INIT)
430 break;
431
432 reply = audio_sync(td, (struct str_sync_data *)td->ev.data);
433 break;
434
435 case DISK_BUF_DATA_NOTIFY:
436 /* Our bun is done */
437 if (td->state != TSTATE_DATA)
438 break;
439
440 td->state = TSTATE_DECODE;
441 str_data_notify_received(&audio_str);
442 break;
443
444 case STREAM_QUIT:
445 /* Time to go - make thread exit */
446 td->state = TSTATE_EOS;
447 return;
448 }
449
450 str_reply_msg(&audio_str, reply);
451
452 if (td->status == STREAM_PLAYING)
453 {
454 switch (td->state)
455 {
456 case TSTATE_DECODE:
457 case TSTATE_RENDER_WAIT:
458 /* These return when in playing state */
459 return;
460 }
461 }
462
463 str_get_msg(&audio_str, &td->ev);
464 }
465}
466
467static void audio_thread(void)
468{
469 struct audio_thread_data td;
470#ifdef HAVE_PRIORITY_SCHEDULING
471 /* Up the priority since the core DSP over-yields internally */
472 int old_priority = rb->thread_set_priority(rb->thread_self(),
473 PRIORITY_PLAYBACK-4);
474#endif
475
476 rb->memset(&td, 0, sizeof (td));
477 td.status = STREAM_STOPPED;
478 td.state = TSTATE_EOS;
479
480 /* We need this here to init the EMAC for Coldfire targets */
481 init_mad();
482
483 td.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO);
484 rb->dsp_configure(td.dsp, DSP_SET_OUT_FREQUENCY, CLOCK_RATE);
485#ifdef HAVE_PITCHCONTROL
486 rb->sound_set_pitch(PITCH_SPEED_100);
487 rb->dsp_set_timestretch(PITCH_SPEED_100);
488#endif
489 rb->dsp_configure(td.dsp, DSP_RESET, 0);
490 rb->dsp_configure(td.dsp, DSP_FLUSH, 0);
491 rb->dsp_configure(td.dsp, DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS);
492
493 goto message_wait;
494
495 /* This is the decoding loop. */
496 while (1)
497 {
498 td.state = TSTATE_DECODE;
499
500 /* Check for any pending messages and process them */
501 if (str_have_msg(&audio_str))
502 {
503 message_wait:
504 /* Wait for a message to be queued */
505 str_get_msg(&audio_str, &td.ev);
506
507 message_process:
508 /* Process a message already dequeued */
509 audio_thread_msg(&td);
510
511 switch (td.state)
512 {
513 /* These states are the only ones that should return */
514 case TSTATE_DECODE: goto audio_decode;
515 case TSTATE_RENDER_WAIT: goto render_wait;
516 /* Anything else is interpreted as an exit */
517 default:
518 {
519#ifdef HAVE_PRIORITY_SCHEDULING
520 rb->thread_set_priority(rb->thread_self(), old_priority);
521#endif
522 return;
523 }
524 }
525 }
526
527 audio_decode:
528
529 /** Buffering **/
530 switch (audio_buffer(&audio_str, STREAM_PM_STREAMING))
531 {
532 case STREAM_DATA_NOT_READY:
533 {
534 td.state = TSTATE_DATA;
535 goto message_wait;
536 } /* STREAM_DATA_NOT_READY: */
537
538 case STREAM_DATA_END:
539 {
540 if (audio_queue.used > MAD_BUFFER_GUARD)
541 break; /* Still have frames to decode */
542
543 /* Used up remainder of compressed audio buffer. Wait for
544 * samples on PCM buffer to finish playing. */
545 audio_queue_reset();
546
547 while (1)
548 {
549 if (pcm_output_empty())
550 {
551 td.state = TSTATE_EOS;
552 stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
553 break;
554 }
555
556 pcm_output_drain();
557 str_get_msg_w_tmo(&audio_str, &td.ev, 1);
558
559 if (td.ev.id != SYS_TIMEOUT)
560 break;
561 }
562
563 goto message_wait;
564 } /* STREAM_DATA_END: */
565 }
566
567 /** Decoding **/
568 mad_stream_buffer(&stream, audio_queue.ptr, audio_queue.used);
569
570 int mad_stat = mad_frame_decode(&frame, &stream);
571
572 ssize_t len = stream.next_frame - audio_queue.ptr;
573
574 if (mad_stat != 0)
575 {
576 DEBUGF("audio: Stream error: %s\n",
577 mad_stream_errorstr(&stream));
578
579 /* If something's goofed - try to perform resync by moving
580 * at least one byte at a time */
581 audio_queue_advance_pos(MAX(len, 1));
582
583 if (stream.error == MAD_ERROR_BUFLEN)
584 {
585 /* This makes the codec support partially corrupted files */
586 if (++td.mad_errors <= MPA_MAX_FRAME_SIZE)
587 {
588 stream.error = 0;
589 rb->yield();
590 continue;
591 }
592 DEBUGF("audio: Too many errors\n");
593 }
594 else if (MAD_RECOVERABLE(stream.error))
595 {
596 /* libmad says it can recover - just keep on decoding */
597 rb->yield();
598 continue;
599 }
600 else
601 {
602 /* Some other unrecoverable error */
603 DEBUGF("audio: Unrecoverable error\n");
604 }
605
606 /* This is too hard - bail out */
607 td.state = TSTATE_EOS;
608 td.status = STREAM_ERROR;
609 stream_generate_event(&audio_str, STREAM_EV_COMPLETE, 0);
610
611 goto message_wait;
612 }
613
614 /* Adjust sizes by the frame size */
615 audio_queue_advance_pos(len);
616 td.mad_errors = 0; /* Clear errors */
617
618 /* Generate the pcm samples */
619 mad_synth_frame(&synth, &frame);
620
621 /** Output **/
622 if (frame.header.samplerate != td.samplerate)
623 {
624 td.samplerate = frame.header.samplerate;
625 rb->dsp_configure(td.dsp, DSP_SET_FREQUENCY,
626 td.samplerate);
627 }
628
629 if (MAD_NCHANNELS(&frame.header) != td.nchannels)
630 {
631 td.nchannels = MAD_NCHANNELS(&frame.header);
632 rb->dsp_configure(td.dsp, DSP_SET_STEREO_MODE,
633 td.nchannels == 1 ?
634 STEREO_MONO : STEREO_NONINTERLEAVED);
635 }
636
637 td.src.remcount = synth.pcm.length;
638 td.src.pin[0] = synth.pcm.samples[0];
639 td.src.pin[1] = synth.pcm.samples[1];
640 td.src.proc_mask = 0;
641
642 td.state = TSTATE_RENDER_WAIT;
643
644 /* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */
645 render_wait:
646 rb->yield();
647
648 while (1)
649 {
650 struct dsp_buffer dst;
651 dst.remcount = 0;
652 dst.bufcount = MAX(td.src.remcount, 1024);
653
654 ssize_t size = dst.bufcount * 2 * sizeof(int16_t);
655
656 /* Wait for required amount of free buffer space */
657 while ((dst.p16out = pcm_output_get_buffer(&size)) == NULL)
658 {
659 /* Wait one frame */
660 int timeout = dst.bufcount*HZ / td.samplerate;
661 str_get_msg_w_tmo(&audio_str, &td.ev, MAX(timeout, 1));
662 if (td.ev.id != SYS_TIMEOUT)
663 goto message_process;
664 }
665
666 dst.bufcount = size / (2 * sizeof (int16_t));
667 rb->dsp_process(td.dsp, &td.src, &dst);
668
669 if (dst.remcount > 0)
670 {
671 /* Make this data available to DMA */
672 pcm_output_commit_data(dst.remcount * 2 * sizeof(int16_t),
673 audio_queue.curr->time);
674
675 /* As long as we're on this timestamp, the time is just
676 incremented by the number of samples */
677 audio_queue.curr->time += dst.remcount;
678 }
679 else if (td.src.remcount <= 0)
680 {
681 break;
682 }
683 }
684 } /* end decoding loop */
685}
686
687/* Initializes the audio thread resources and starts the thread */
688bool audio_thread_init(void)
689{
690 /* Initialise the encoded audio buffer and its descriptors */
691 audio_queue.start = mpeg_malloc(AUDIOBUF_ALLOC_SIZE,
692 MPEG_ALLOC_AUDIOBUF);
693 if (audio_queue.start == NULL)
694 return false;
695
696 /* Start the audio thread */
697 audio_str.hdr.q = &audio_str_queue;
698 rb->queue_init(audio_str.hdr.q, false);
699
700 /* We steal the codec thread for audio */
701 rb->codec_thread_do_callback(audio_thread, &audio_str.thread);
702
703 rb->queue_enable_queue_send(audio_str.hdr.q, &audio_str_queue_send,
704 audio_str.thread);
705
706 /* Wait for thread to initialize */
707 str_send_msg(&audio_str, STREAM_NULL, 0);
708
709 return true;
710}
711
712/* Stops the audio thread */
713void audio_thread_exit(void)
714{
715 if (audio_str.thread != 0)
716 {
717 str_post_msg(&audio_str, STREAM_QUIT, 0);
718 rb->codec_thread_do_callback(NULL, NULL);
719 audio_str.thread = 0;
720 }
721}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * mpegplayer buffering routines
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "plugin.h"
24#include "mpegplayer.h"
25#include <system.h>
26
27static struct mutex disk_buf_mtx SHAREDBSS_ATTR;
28static struct event_queue disk_buf_queue SHAREDBSS_ATTR;
29static struct queue_sender_list disk_buf_queue_send SHAREDBSS_ATTR;
30static uint32_t disk_buf_stack[DEFAULT_STACK_SIZE*2/sizeof(uint32_t)];
31
32struct disk_buf disk_buf SHAREDBSS_ATTR;
33static void *nf_list[MPEGPLAYER_MAX_STREAMS+1];
34
35static inline void disk_buf_lock(void)
36{
37 rb->mutex_lock(&disk_buf_mtx);
38}
39
40static inline void disk_buf_unlock(void)
41{
42 rb->mutex_unlock(&disk_buf_mtx);
43}
44
45static inline void disk_buf_on_clear_data_notify(struct stream_hdr *sh)
46{
47 DEBUGF("DISK_BUF_CLEAR_DATA_NOTIFY: 0x%02X (cleared)\n",
48 STR_FROM_HDR(sh)->id);
49 list_remove_item(nf_list, sh);
50}
51
52inline bool disk_buf_is_data_ready(struct stream_hdr *sh,
53 ssize_t margin)
54{
55 /* Data window available? */
56 off_t right = sh->win_right;
57
58 /* Margins past end-of-file can still return true */
59 if (right > disk_buf.filesize - margin)
60 right = disk_buf.filesize - margin;
61
62 return sh->win_left >= disk_buf.win_left &&
63 right + margin <= disk_buf.win_right;
64}
65
66void dbuf_l2_init(struct dbuf_l2_cache *l2_p)
67{
68 l2_p->addr = OFF_T_MAX; /* Mark as invalid */
69}
70
71static int disk_buf_on_data_notify(struct stream_hdr *sh)
72{
73 DEBUGF("DISK_BUF_DATA_NOTIFY: 0x%02X ", STR_FROM_HDR(sh)->id);
74
75 if (sh->win_left <= sh->win_right)
76 {
77 /* Check if the data is already ready already */
78 if (disk_buf_is_data_ready(sh, 0))
79 {
80 /* It was - don't register */
81 DEBUGF("(was ready)\n"
82 " swl:%lu swr:%lu\n"
83 " dwl:%lu dwr:%lu\n",
84 sh->win_left, sh->win_right,
85 disk_buf.win_left, disk_buf.win_right);
86 /* Be sure it's not listed though if multiple requests were made */
87 list_remove_item(nf_list, sh);
88 return DISK_BUF_NOTIFY_OK;
89 }
90
91 switch (disk_buf.state)
92 {
93 case TSTATE_DATA:
94 case TSTATE_BUFFERING:
95 case TSTATE_INIT:
96 disk_buf.state = TSTATE_BUFFERING;
97 list_add_item(nf_list, sh);
98 DEBUGF("(registered)\n"
99 " swl:%lu swr:%lu\n"
100 " dwl:%lu dwr:%lu\n",
101 sh->win_left, sh->win_right,
102 disk_buf.win_left, disk_buf.win_right);
103 return DISK_BUF_NOTIFY_REGISTERED;
104 }
105 }
106
107 DEBUGF("(error)\n");
108 return DISK_BUF_NOTIFY_ERROR;
109}
110
111static bool check_data_notifies_callback(struct stream_hdr *sh, void *data)
112{
113 if (disk_buf_is_data_ready(sh, 0))
114 {
115 /* Remove from list then post notification - post because send
116 * could result in a wait for each thread to finish resulting
117 * in deadlock */
118 list_remove_item(nf_list, sh);
119 str_post_msg(STR_FROM_HDR(sh), DISK_BUF_DATA_NOTIFY, 0);
120 DEBUGF("DISK_BUF_DATA_NOTIFY: 0x%02X (notified)\n",
121 STR_FROM_HDR(sh)->id);
122 }
123
124 return true;
125 (void)data;
126}
127
128/* Check registered streams and notify them if their data is available */
129static inline void check_data_notifies(void)
130{
131 list_enum_items(nf_list,
132 (list_enum_callback_t)check_data_notifies_callback,
133 NULL);
134}
135
136/* Clear all registered notifications - do not post them */
137static inline void clear_data_notifies(void)
138{
139 list_clear_all(nf_list);
140}
141
142/* Background buffering when streaming */
143static inline void disk_buf_buffer(void)
144{
145 struct stream_window sw;
146
147 switch (disk_buf.state)
148 {
149 default:
150 {
151 size_t wm;
152 uint32_t time;
153
154 /* Get remaining minimum data based upon the stream closest to the
155 * right edge of the window */
156 if (!stream_get_window(&sw))
157 break;
158
159 time = stream_get_ticks(NULL);
160 wm = muldiv_uint32(5*CLOCK_RATE, sw.right - disk_buf.pos_last,
161 time - disk_buf.time_last);
162 wm = MIN(wm, (size_t)disk_buf.size);
163 wm = MAX(wm, DISK_BUF_LOW_WATERMARK);
164
165 disk_buf.time_last = time;
166 disk_buf.pos_last = sw.right;
167
168 /* Fast attack, slow decay */
169 disk_buf.low_wm = (wm > (size_t)disk_buf.low_wm) ?
170 wm : AVERAGE(disk_buf.low_wm, wm, 16);
171
172#if 0
173 rb->splashf(0, "*%10ld %10ld", disk_buf.low_wm,
174 disk_buf.win_right - sw.right);
175#endif
176
177 if (disk_buf.win_right - sw.right > disk_buf.low_wm)
178 break;
179
180 disk_buf.state = TSTATE_BUFFERING;
181 } /* default: */
182
183 /* Fall-through */
184 case TSTATE_BUFFERING:
185 {
186 ssize_t len, n;
187 uint32_t tag, *tag_p;
188
189 /* Limit buffering up to the stream with the least progress */
190 if (!stream_get_window(&sw))
191 {
192 disk_buf.state = TSTATE_DATA;
193 rb->storage_sleep();
194 break;
195 }
196
197 /* Wrap pointer */
198 if (disk_buf.tail >= disk_buf.end)
199 disk_buf.tail = disk_buf.start;
200
201 len = disk_buf.size - disk_buf.win_right + sw.left;
202
203 if (len < DISK_BUF_PAGE_SIZE)
204 {
205 /* Free space is less than one page */
206 disk_buf.state = TSTATE_DATA;
207 disk_buf.low_wm = DISK_BUF_LOW_WATERMARK;
208 rb->storage_sleep();
209 break;
210 }
211
212 len = disk_buf.tail - disk_buf.start;
213 tag = MAP_OFFSET_TO_TAG(disk_buf.win_right);
214 tag_p = &disk_buf.cache[len >> DISK_BUF_PAGE_SHIFT];
215
216 if (*tag_p != tag)
217 {
218 if (disk_buf.need_seek)
219 {
220 rb->lseek(disk_buf.in_file, disk_buf.win_right, SEEK_SET);
221 disk_buf.need_seek = false;
222 }
223
224 n = rb->read(disk_buf.in_file, disk_buf.tail, DISK_BUF_PAGE_SIZE);
225
226 if (n <= 0)
227 {
228 /* Error or end of stream */
229 disk_buf.state = TSTATE_EOS;
230 rb->storage_sleep();
231 break;
232 }
233
234 if (len < DISK_GUARDBUF_SIZE)
235 {
236 /* Autoguard guard-o-rama - maintain guardbuffer coherency */
237 rb->memcpy(disk_buf.end + len, disk_buf.tail,
238 MIN(DISK_GUARDBUF_SIZE - len, n));
239 }
240
241 /* Update the cache entry for this page */
242 *tag_p = tag;
243 }
244 else
245 {
246 /* Skipping a read */
247 n = MIN(DISK_BUF_PAGE_SIZE,
248 disk_buf.filesize - disk_buf.win_right);
249 disk_buf.need_seek = true;
250 }
251
252 disk_buf.tail += DISK_BUF_PAGE_SIZE;
253
254 /* Keep left edge moving forward */
255
256 /* Advance right edge in temp variable first, then move
257 * left edge if overflow would occur. This avoids a stream
258 * thinking its data might be available when it actually
259 * may not end up that way after a slide of the window. */
260 len = disk_buf.win_right + n;
261
262 if (len - disk_buf.win_left > disk_buf.size)
263 disk_buf.win_left += n;
264
265 disk_buf.win_right = len;
266
267 /* Continue buffering until filled or file end */
268 rb->yield();
269 } /* TSTATE_BUFFERING: */
270
271 case TSTATE_EOS:
272 break;
273 } /* end switch */
274}
275
276static void disk_buf_on_reset(ssize_t pos)
277{
278 int page;
279 uint32_t tag;
280 off_t anchor;
281
282 disk_buf.state = TSTATE_INIT;
283 disk_buf.status = STREAM_STOPPED;
284 clear_data_notifies();
285
286 if (pos >= disk_buf.filesize)
287 {
288 /* Anchor on page immediately following the one containing final
289 * data */
290 anchor = disk_buf.file_pages * DISK_BUF_PAGE_SIZE;
291 disk_buf.win_left = disk_buf.filesize;
292 }
293 else
294 {
295 anchor = pos & ~DISK_BUF_PAGE_MASK;
296 disk_buf.win_left = anchor;
297 }
298
299 /* Collect all valid data already buffered that is contiguous with the
300 * current position - probe to left, then to right */
301 if (anchor > 0)
302 {
303 page = MAP_OFFSET_TO_PAGE(anchor);
304 tag = MAP_OFFSET_TO_TAG(anchor);
305
306 do
307 {
308 if (--tag, --page < 0)
309 page = disk_buf.pgcount - 1;
310
311 if (disk_buf.cache[page] != tag)
312 break;
313
314 disk_buf.win_left = tag << DISK_BUF_PAGE_SHIFT;
315 }
316 while (tag > 0);
317 }
318
319 if (anchor < disk_buf.filesize)
320 {
321 page = MAP_OFFSET_TO_PAGE(anchor);
322 tag = MAP_OFFSET_TO_TAG(anchor);
323
324 do
325 {
326 if (disk_buf.cache[page] != tag)
327 break;
328
329 if (++tag, ++page >= disk_buf.pgcount)
330 page = 0;
331
332 anchor += DISK_BUF_PAGE_SIZE;
333 }
334 while (anchor < disk_buf.filesize);
335 }
336
337 if (anchor >= disk_buf.filesize)
338 {
339 disk_buf.win_right = disk_buf.filesize;
340 disk_buf.state = TSTATE_EOS;
341 }
342 else
343 {
344 disk_buf.win_right = anchor;
345 }
346
347 disk_buf.tail = disk_buf.start + MAP_OFFSET_TO_BUFFER(anchor);
348
349 DEBUGF("disk buf reset\n"
350 " dwl:%ld dwr:%ld\n",
351 disk_buf.win_left, disk_buf.win_right);
352
353 /* Next read position is at right edge */
354 rb->lseek(disk_buf.in_file, disk_buf.win_right, SEEK_SET);
355 disk_buf.need_seek = false;
356
357 disk_buf_reply_msg(disk_buf.win_right - disk_buf.win_left);
358}
359
360static void disk_buf_on_stop(void)
361{
362 bool was_buffering = disk_buf.state == TSTATE_BUFFERING;
363
364 disk_buf.state = TSTATE_EOS;
365 disk_buf.status = STREAM_STOPPED;
366 clear_data_notifies();
367
368 disk_buf_reply_msg(was_buffering);
369}
370
371static void disk_buf_on_play_pause(bool play, bool forcefill)
372{
373 struct stream_window sw;
374
375 if (disk_buf.state != TSTATE_EOS)
376 {
377 if (forcefill)
378 {
379 /* Force buffer filling to top */
380 disk_buf.state = TSTATE_BUFFERING;
381 }
382 else if (disk_buf.state != TSTATE_BUFFERING)
383 {
384 /* If not filling already, simply monitor */
385 disk_buf.state = TSTATE_DATA;
386 }
387 }
388 /* else end of stream - no buffering to do */
389
390 disk_buf.pos_last = stream_get_window(&sw) ? sw.right : 0;
391 disk_buf.time_last = stream_get_ticks(NULL);
392
393 disk_buf.status = play ? STREAM_PLAYING : STREAM_PAUSED;
394}
395
396static int disk_buf_on_load_range(struct dbuf_range *rng)
397{
398 uint32_t tag = rng->tag_start;
399 uint32_t tag_end = rng->tag_end;
400 int page = rng->pg_start;
401
402 /* Check if a seek is required */
403 bool need_seek = rb->lseek(disk_buf.in_file, 0, SEEK_CUR)
404 != (off_t)(tag << DISK_BUF_PAGE_SHIFT);
405
406 do
407 {
408 uint32_t *tag_p = &disk_buf.cache[page];
409
410 if (*tag_p != tag)
411 {
412 /* Page not cached - load it */
413 ssize_t o, n;
414
415 if (need_seek)
416 {
417 rb->lseek(disk_buf.in_file, tag << DISK_BUF_PAGE_SHIFT,
418 SEEK_SET);
419 need_seek = false;
420 }
421
422 o = page << DISK_BUF_PAGE_SHIFT;
423 n = rb->read(disk_buf.in_file, disk_buf.start + o,
424 DISK_BUF_PAGE_SIZE);
425
426 if (n < 0)
427 {
428 /* Read error */
429 return DISK_BUF_NOTIFY_ERROR;
430 }
431
432 if (n == 0)
433 {
434 /* End of file */
435 break;
436 }
437
438 if (o < DISK_GUARDBUF_SIZE)
439 {
440 /* Autoguard guard-o-rama - maintain guardbuffer coherency */
441 rb->memcpy(disk_buf.end + o, disk_buf.start + o,
442 MIN(DISK_GUARDBUF_SIZE - o, n));
443 }
444
445 /* Update the cache entry */
446 *tag_p = tag;
447 }
448 else
449 {
450 /* Skipping a disk read - must seek on next one */
451 need_seek = true;
452 }
453
454 if (++page >= disk_buf.pgcount)
455 page = 0;
456 }
457 while (++tag <= tag_end);
458
459 return DISK_BUF_NOTIFY_OK;
460}
461
462static void disk_buf_thread(void)
463{
464 struct queue_event ev;
465
466 disk_buf.state = TSTATE_EOS;
467 disk_buf.status = STREAM_STOPPED;
468
469 while (1)
470 {
471 if (disk_buf.state != TSTATE_EOS)
472 {
473 /* Poll buffer status and messages */
474 rb->queue_wait_w_tmo(disk_buf.q, &ev,
475 disk_buf.state == TSTATE_BUFFERING ?
476 0 : HZ/5);
477 }
478 else
479 {
480 /* Sit idle and wait for commands */
481 rb->queue_wait(disk_buf.q, &ev);
482 }
483
484 switch (ev.id)
485 {
486 case SYS_TIMEOUT:
487 if (disk_buf.state == TSTATE_EOS)
488 break;
489
490 disk_buf_buffer();
491
492 /* Check for any due notifications if any are pending */
493 if (*nf_list != NULL)
494 check_data_notifies();
495
496 /* Still more data left? */
497 if (disk_buf.state != TSTATE_EOS)
498 continue;
499
500 /* Nope - end of stream */
501 break;
502
503 case DISK_BUF_CACHE_RANGE:
504 disk_buf_reply_msg(disk_buf_on_load_range(
505 (struct dbuf_range *)ev.data));
506 break;
507
508 case STREAM_RESET:
509 disk_buf_on_reset(ev.data);
510 break;
511
512 case STREAM_STOP:
513 disk_buf_on_stop();
514 break;
515
516 case STREAM_PAUSE:
517 case STREAM_PLAY:
518 disk_buf_on_play_pause(ev.id == STREAM_PLAY, ev.data != 0);
519 disk_buf_reply_msg(1);
520 break;
521
522 case STREAM_QUIT:
523 disk_buf.state = TSTATE_EOS;
524 return;
525
526 case DISK_BUF_DATA_NOTIFY:
527 disk_buf_reply_msg(disk_buf_on_data_notify(
528 (struct stream_hdr *)ev.data));
529 break;
530
531 case DISK_BUF_CLEAR_DATA_NOTIFY:
532 disk_buf_on_clear_data_notify((struct stream_hdr *)ev.data);
533 disk_buf_reply_msg(1);
534 break;
535 }
536 }
537}
538
539/* Caches some data from the current file */
540static ssize_t disk_buf_probe(off_t start, size_t length, void **p)
541{
542 off_t end;
543 uint32_t tag, tag_end;
544 int page;
545
546 /* Can't read past end of file */
547 if (length > (size_t)(disk_buf.filesize - start))
548 {
549 length = disk_buf.filesize - start;
550 }
551
552 /* Can't cache more than the whole buffer size */
553 if (length > (size_t)disk_buf.size)
554 {
555 length = disk_buf.size;
556 }
557 /* Zero-length probes permitted */
558
559 end = start + length;
560
561 /* Prepare the range probe */
562 tag = MAP_OFFSET_TO_TAG(start);
563 tag_end = MAP_OFFSET_TO_TAG(end);
564 page = MAP_OFFSET_TO_PAGE(start);
565
566 /* If the end is on a page boundary, check one less or an extra
567 * one will be probed */
568 if (tag_end > tag && (end & DISK_BUF_PAGE_MASK) == 0)
569 {
570 tag_end--;
571 }
572
573 if (p != NULL)
574 {
575 *p = disk_buf.start + (page << DISK_BUF_PAGE_SHIFT)
576 + (start & DISK_BUF_PAGE_MASK);
577 }
578
579 /* Obtain initial load point. If all data was cached, no message is sent
580 * otherwise begin on the first page that is not cached. Since we have to
581 * send the message anyway, the buffering thread will determine what else
582 * requires loading on its end in order to cache the specified range. */
583 do
584 {
585 if (disk_buf.cache[page] != tag)
586 {
587 static struct dbuf_range rng IBSS_ATTR;
588 intptr_t result;
589
590 DEBUGF("disk_buf: cache miss\n");
591 rng.tag_start = tag;
592 rng.tag_end = tag_end;
593 rng.pg_start = page;
594
595 result = rb->queue_send(disk_buf.q, DISK_BUF_CACHE_RANGE,
596 (intptr_t)&rng);
597
598 return result == DISK_BUF_NOTIFY_OK ? (ssize_t)length : -1;
599 }
600
601 if (++page >= disk_buf.pgcount)
602 page = 0;
603 }
604 while (++tag <= tag_end);
605
606 return length;
607}
608
609/* Attempt to get a pointer to size bytes on the buffer. Returns real amount of
610 * data available as well as the size of non-wrapped data after *p. */
611ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap,
612 size_t *sizewrap)
613{
614 disk_buf_lock();
615
616 size = disk_buf_probe(disk_buf.offset, size, pp);
617
618 if (size != (size_t)-1 && pwrap && sizewrap)
619 {
620 uint8_t *p = (uint8_t *)*pp;
621
622 if (p + size > disk_buf.end + DISK_GUARDBUF_SIZE)
623 {
624 /* Return pointer to wraparound and the size of same */
625 size_t nowrap = (disk_buf.end + DISK_GUARDBUF_SIZE) - p;
626 *pwrap = disk_buf.start + DISK_GUARDBUF_SIZE;
627 *sizewrap = size - nowrap;
628 }
629 else
630 {
631 *pwrap = NULL;
632 *sizewrap = 0;
633 }
634 }
635
636 disk_buf_unlock();
637
638 return size;
639}
640
641ssize_t _disk_buf_getbuffer_l2(struct dbuf_l2_cache *l2,
642 size_t size, void **pp)
643{
644 off_t offs;
645 off_t l2_addr;
646 size_t l2_size;
647 void *l2_p;
648
649 if (l2 == NULL)
650 {
651 /* Shouldn't have to check this normally */
652 DEBUGF("disk_buf_getbuffer_l2: l2 = NULL!\n");
653 }
654
655 if (size > DISK_BUF_L2_CACHE_SIZE)
656 {
657 /* Asking for too much; just go through L1 */
658 return disk_buf_getbuffer(size, pp, NULL, NULL);
659 }
660
661 offs = disk_buf.offset; /* Other calls keep this within bounds */
662 l2_addr = l2->addr;
663
664 if (offs >= l2_addr && offs < l2_addr + DISK_BUF_L2_CACHE_SIZE)
665 {
666 /* Data is in the local buffer */
667 offs &= DISK_BUF_L2_CACHE_MASK;
668
669 *pp = l2->data + offs;
670 if (offs + size > l2->size)
671 size = l2->size - offs; /* Keep size within file limits */
672
673 return size;
674 }
675
676 /* Have to probe main buffer */
677 l2_addr = offs & ~DISK_BUF_L2_CACHE_MASK;
678 l2_size = DISK_BUF_L2_CACHE_SIZE*2; /* 2nd half is a guard buffer */
679
680 disk_buf_lock();
681
682 l2_size = disk_buf_probe(l2_addr, l2_size, &l2_p);
683
684 if (l2_size != (size_t)-1)
685 {
686 rb->memcpy(l2->data, l2_p, l2_size);
687
688 l2->addr = l2_addr;
689 l2->size = l2_size;
690 offs -= l2_addr;
691
692 *pp = l2->data + offs;
693 if (offs + size > l2->size)
694 size = l2->size - offs; /* Keep size within file limits */
695 }
696 else
697 {
698 size = -1;
699 }
700
701 disk_buf_unlock();
702
703 return size;
704}
705
706
707/* Read size bytes of data into a buffer - advances the buffer pointer
708 * and returns the real size read. */
709ssize_t disk_buf_read(void *buffer, size_t size)
710{
711 uint8_t *p;
712
713 disk_buf_lock();
714
715 size = disk_buf_probe(disk_buf.offset, size, PUN_PTR(void **, &p));
716
717 if (size != (size_t)-1)
718 {
719 if (p + size > disk_buf.end + DISK_GUARDBUF_SIZE)
720 {
721 /* Read wraps */
722 size_t nowrap = (disk_buf.end + DISK_GUARDBUF_SIZE) - p;
723 rb->memcpy(buffer, p, nowrap);
724 rb->memcpy(buffer + nowrap, disk_buf.start + DISK_GUARDBUF_SIZE,
725 size - nowrap);
726 }
727 else
728 {
729 /* Read wasn't wrapped or guardbuffer holds it */
730 rb->memcpy(buffer, p, size);
731 }
732
733 disk_buf.offset += size;
734 }
735
736 disk_buf_unlock();
737
738 return size;
739}
740
741ssize_t disk_buf_lseek(off_t offset, int whence)
742{
743 disk_buf_lock();
744
745 /* The offset returned is the result of the current thread's action and
746 * may be invalidated so a local result is returned and not the value
747 * of disk_buf.offset directly */
748 switch (whence)
749 {
750 case SEEK_SET:
751 /* offset is just the offset */
752 break;
753 case SEEK_CUR:
754 offset += disk_buf.offset;
755 break;
756 case SEEK_END:
757 offset = disk_buf.filesize + offset;
758 break;
759 default:
760 disk_buf_unlock();
761 return -2; /* Invalid request */
762 }
763
764 if (offset < 0 || offset > disk_buf.filesize)
765 {
766 offset = -3;
767 }
768 else
769 {
770 disk_buf.offset = offset;
771 }
772
773 disk_buf_unlock();
774
775 return offset;
776}
777
778/* Prepare the buffer to enter the streaming state. Evaluates the available
779 * streaming window. */
780ssize_t disk_buf_prepare_streaming(off_t pos, size_t len)
781{
782 disk_buf_lock();
783
784 if (pos < 0)
785 pos = 0;
786 else if (pos > disk_buf.filesize)
787 pos = disk_buf.filesize;
788
789 DEBUGF("prepare streaming:\n pos:%ld len:%lu\n", pos, (unsigned long)len);
790
791 pos = disk_buf_lseek(pos, SEEK_SET);
792 len = disk_buf_probe(pos, len, NULL);
793
794 DEBUGF(" probe done: pos:%ld len:%lu\n", pos, (unsigned long)len);
795
796 len = disk_buf_send_msg(STREAM_RESET, pos);
797
798 disk_buf_unlock();
799
800 return len;
801}
802
803/* Set the streaming window to an arbitrary position within the file. Makes no
804 * probes to validate data. Use after calling another function to cause data
805 * to be cached and correct values are known. */
806ssize_t disk_buf_set_streaming_window(off_t left, off_t right)
807{
808 ssize_t len;
809
810 disk_buf_lock();
811
812 if (left < 0)
813 left = 0;
814 else if (left > disk_buf.filesize)
815 left = disk_buf.filesize;
816
817 if (left > right)
818 right = left;
819
820 if (right > disk_buf.filesize)
821 right = disk_buf.filesize;
822
823 disk_buf.win_left = left;
824 disk_buf.win_right = right;
825 disk_buf.tail = disk_buf.start + ((right + DISK_BUF_PAGE_SIZE-1) &
826 ~DISK_BUF_PAGE_MASK) % disk_buf.size;
827
828 len = disk_buf.win_right - disk_buf.win_left;
829
830 disk_buf_unlock();
831
832 return len;
833}
834
835void * disk_buf_offset2ptr(off_t offset)
836{
837 if (offset < 0)
838 offset = 0;
839 else if (offset > disk_buf.filesize)
840 offset = disk_buf.filesize;
841
842 return disk_buf.start + (offset % disk_buf.size);
843}
844
845void disk_buf_close(void)
846{
847 disk_buf_lock();
848
849 if (disk_buf.in_file >= 0)
850 {
851 rb->close(disk_buf.in_file);
852 disk_buf.in_file = -1;
853
854 /* Invalidate entire cache */
855 rb->memset(disk_buf.cache, 0xff,
856 disk_buf.pgcount*sizeof (*disk_buf.cache));
857 disk_buf.file_pages = 0;
858 disk_buf.filesize = 0;
859 disk_buf.offset = 0;
860 }
861
862 disk_buf_unlock();
863}
864
865int disk_buf_open(const char *filename)
866{
867 int fd;
868
869 disk_buf_lock();
870
871 disk_buf_close();
872
873 fd = rb->open(filename, O_RDONLY);
874
875 if (fd >= 0)
876 {
877 ssize_t filesize = rb->filesize(fd);
878
879 if (filesize <= 0)
880 {
881 rb->close(disk_buf.in_file);
882 }
883 else
884 {
885 disk_buf.filesize = filesize;
886 /* Number of file pages rounded up toward +inf */
887 disk_buf.file_pages = ((size_t)filesize + DISK_BUF_PAGE_SIZE-1)
888 / DISK_BUF_PAGE_SIZE;
889 disk_buf.in_file = fd;
890 }
891 }
892
893 disk_buf_unlock();
894
895 return fd;
896}
897
898intptr_t disk_buf_send_msg(long id, intptr_t data)
899{
900 return rb->queue_send(disk_buf.q, id, data);
901}
902
903void disk_buf_post_msg(long id, intptr_t data)
904{
905 rb->queue_post(disk_buf.q, id, data);
906}
907
908void disk_buf_reply_msg(intptr_t retval)
909{
910 rb->queue_reply(disk_buf.q, retval);
911}
912
913bool disk_buf_init(void)
914{
915 disk_buf.thread = 0;
916
917 rb->mutex_init(&disk_buf_mtx);
918
919 disk_buf.q = &disk_buf_queue;
920 rb->queue_init(disk_buf.q, false);
921
922 disk_buf.state = TSTATE_EOS;
923 disk_buf.status = STREAM_STOPPED;
924
925 disk_buf.in_file = -1;
926 disk_buf.filesize = 0;
927 disk_buf.win_left = 0;
928 disk_buf.win_right = 0;
929 disk_buf.time_last = 0;
930 disk_buf.pos_last = 0;
931 disk_buf.low_wm = DISK_BUF_LOW_WATERMARK;
932
933 disk_buf.start = mpeg_malloc_all((size_t*)&disk_buf.size, MPEG_ALLOC_DISKBUF);
934 if (disk_buf.start == NULL)
935 return false;
936
937#if NUM_CORES > 1
938 CACHEALIGN_BUFFER(disk_buf.start, disk_buf.size);
939 disk_buf.start = UNCACHED_ADDR(disk_buf.start);
940#endif
941 disk_buf.size -= DISK_GUARDBUF_SIZE;
942 disk_buf.pgcount = disk_buf.size / DISK_BUF_PAGE_SIZE;
943
944 /* Fit it as tightly as possible */
945 while (disk_buf.pgcount*(sizeof (*disk_buf.cache) + DISK_BUF_PAGE_SIZE)
946 > (size_t)disk_buf.size)
947 {
948 disk_buf.pgcount--;
949 }
950
951 disk_buf.cache = (typeof (disk_buf.cache))disk_buf.start;
952 disk_buf.start += sizeof (*disk_buf.cache)*disk_buf.pgcount;
953 disk_buf.size = disk_buf.pgcount*DISK_BUF_PAGE_SIZE;
954 disk_buf.end = disk_buf.start + disk_buf.size;
955 disk_buf.tail = disk_buf.start;
956
957 DEBUGF("disk_buf info:\n"
958 " page count: %d\n"
959 " size: %ld\n",
960 disk_buf.pgcount, (long)disk_buf.size);
961
962 rb->memset(disk_buf.cache, 0xff,
963 disk_buf.pgcount*sizeof (*disk_buf.cache));
964
965 disk_buf.thread = rb->create_thread(
966 disk_buf_thread, disk_buf_stack, sizeof(disk_buf_stack), 0,
967 "mpgbuffer" IF_PRIO(, PRIORITY_BUFFERING) IF_COP(, CPU));
968
969 rb->queue_enable_queue_send(disk_buf.q, &disk_buf_queue_send,
970 disk_buf.thread);
971
972 if (disk_buf.thread == 0)
973 return false;
974
975 /* Wait for thread to initialize */
976 disk_buf_send_msg(STREAM_NULL, 0);
977
978 return true;
979}
980
981void disk_buf_exit(void)
982{
983 if (disk_buf.thread != 0)
984 {
985 rb->queue_post(disk_buf.q, STREAM_QUIT, 0);
986 rb->thread_wait(disk_buf.thread);
987 disk_buf.thread = 0;
988 }
989}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * AV disk buffer declarations
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef DISK_BUF_H
24#define DISK_BUF_H
25
26#ifndef OFF_T_MAX
27#define OFF_T_MAX (~((off_t)1 << (sizeof (off_t)*8 - 1)))
28#endif
29
30#ifndef OFF_T_MIN
31#define OFF_T_MIN ((off_t)1 << (sizeof (off_t)*8 - 1))
32#endif
33
34#define DISK_BUF_PAGE_SHIFT 15 /* 32KB cache lines */
35#define DISK_BUF_PAGE_SIZE (1 << DISK_BUF_PAGE_SHIFT)
36#define DISK_BUF_PAGE_MASK (DISK_BUF_PAGE_SIZE-1)
37
38enum
39{
40 DISK_BUF_NOTIFY_ERROR = -1,
41 DISK_BUF_NOTIFY_NULL = 0,
42 DISK_BUF_NOTIFY_OK,
43 DISK_BUF_NOTIFY_TIMEDOUT,
44 DISK_BUF_NOTIFY_PROCESS_EVENT,
45 DISK_BUF_NOTIFY_REGISTERED,
46};
47
48/** Macros to map file offsets to cached data **/
49
50/* Returns a cache tag given a file offset */
51#define MAP_OFFSET_TO_TAG(o) \
52 ((o) >> DISK_BUF_PAGE_SHIFT)
53
54/* Returns the cache page number given a file offset */
55#define MAP_OFFSET_TO_PAGE(o) \
56 (MAP_OFFSET_TO_TAG(o) % disk_buf.pgcount)
57
58/* Returns the buffer offset given a file offset */
59#define MAP_OFFSET_TO_BUFFER(o) \
60 (MAP_OFFSET_TO_PAGE(o) * DISK_BUF_PAGE_SIZE)
61
62struct dbuf_range
63{
64 uint32_t tag_start;
65 uint32_t tag_end;
66 int pg_start;
67};
68
69#define DISK_BUF_L2_CACHE_SHIFT 6
70#define DISK_BUF_L2_CACHE_SIZE (1 << DISK_BUF_L2_CACHE_SHIFT)
71#define DISK_BUF_L2_CACHE_MASK (DISK_BUF_L2_CACHE_SIZE-1)
72
73struct dbuf_l2_cache
74{
75 off_t addr; /* L2 file offset */
76 size_t size; /* Real size */
77 uint8_t data[DISK_BUF_L2_CACHE_SIZE*2]; /* Local data and guard */
78};
79
80void dbuf_l2_init(struct dbuf_l2_cache *l2_p);
81
82/* This object is an extension of the stream manager and handles some
83 * playback events as well as buffering */
84struct disk_buf
85{
86 unsigned int thread;
87 struct event_queue *q;
88 uint8_t *start; /* Start pointer */
89 uint8_t *end; /* End of buffer pointer less MPEG_GUARDBUF_SIZE. The
90 guard space is used to wrap data at the buffer start to
91 pass continuous data packets */
92 uint8_t *tail; /* Location of last data + 1 filled into the buffer */
93 ssize_t size; /* The buffer length _not_ including the guard space (end-start) */
94 int pgcount; /* Total number of available cached pages */
95 uint32_t *cache; /* Pointer to cache structure - allocated on buffer */
96 int in_file; /* File being read */
97 ssize_t filesize; /* Size of file in_file in bytes */
98 int file_pages; /* Number of pages in file (rounded up) */
99 off_t offset; /* Current position (random access) */
100 off_t win_left; /* Left edge of buffer window (streaming) */
101 off_t win_right; /* Right edge of buffer window (streaming) */
102 uint32_t time_last; /* Last time watermark was checked */
103 off_t pos_last; /* Last position at watermark check time */
104 ssize_t low_wm; /* The low watermark for automatic rebuffering */
105 int status; /* Status as stream */
106 int state; /* Current thread state */
107 bool need_seek; /* Need to seek because a read was not contiguous */
108};
109
110extern struct disk_buf disk_buf SHAREDBSS_ATTR;
111
112struct stream_hdr;
113bool disk_buf_is_data_ready(struct stream_hdr *sh, ssize_t margin);
114
115bool disk_buf_init(void);
116void disk_buf_exit(void);
117
118static inline int disk_buf_status(void)
119 { return disk_buf.status; }
120
121int disk_buf_open(const char *filename);
122void disk_buf_close(void);
123ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap,
124 size_t *sizewrap);
125#define disk_buf_getbuffer(size, pp, pwrap, sizewrap) \
126 _disk_buf_getbuffer((size), PUN_PTR(void **, (pp)), \
127 PUN_PTR(void **, (pwrap)), (sizewrap))
128
129ssize_t _disk_buf_getbuffer_l2(struct dbuf_l2_cache *l2,
130 size_t size, void **pp);
131#define disk_buf_getbuffer_l2(l2, size, pp) \
132 _disk_buf_getbuffer_l2((l2), (size), PUN_PTR(void **, (pp)))
133
134ssize_t disk_buf_read(void *buffer, size_t size);
135ssize_t disk_buf_lseek(off_t offset, int whence);
136
137static inline off_t disk_buf_ftell(void)
138 { return disk_buf.offset; }
139
140static inline ssize_t disk_buf_filesize(void)
141 { return disk_buf.filesize; }
142
143ssize_t disk_buf_prepare_streaming(off_t pos, size_t len);
144ssize_t disk_buf_set_streaming_window(off_t left, off_t right);
145void * disk_buf_offset2ptr(off_t offset);
146int disk_buf_check_streaming_window(off_t left, off_t right);
147
148intptr_t disk_buf_send_msg(long id, intptr_t data);
149void disk_buf_post_msg(long id, intptr_t data);
150void disk_buf_reply_msg(intptr_t retval);
151
152#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 @@
1Aaron Holtzman <aholtzma@ess.engr.uvic.ca> started the project and
2made the initial working implementation.
3
4Michel Lespinasse <walken@zoy.org> did major changes for speed and
5mpeg conformance and is the current maintainer. Most of the current
6code was (re)written by him.
7
8Other contributors include:
9 Bruno Barreyra <barreyra@ufl.edu> - build fixes
10 Gildas Bazin <gbazin@netcourrier.com> - mingw32 port
11 Alexander W. Chin <alexc@newt.phys.unsw.edu.au> - progressive_seq fix
12 Stephen Crowley <stephenc@dns2.digitalpassage.com> - build fixes
13 Didier Gautheron <dgautheron@magic.fr> - bug fixes
14 Ryan C. Gordon <icculus@lokigames.com> - SDL support
15 Peter Gubanov <peter@elecard.net.ru> - MMX IDCT scheduling
16 HÃ¥kan Hjort <d95hjort@dtek.chalmers.se> - Solaris fixes, mlib code
17 Nicolas Joly <njoly@pasteur.fr> - assorted bug fixes
18 Gerd Knorr <kraxel@goldbach.in-berlin.de> - Xv support
19 David I. Lehn <dlehn@vt.edu> - motion_comp mmx code
20 Olie Lho <ollie@sis.com.tw> - MMX yuv2rgb routine
21 David S. Miller <davem@redhat.com> - sparc VIS optimizations
22 Rick Niles <niles@scyld.com> - build fixes
23 Real Ouellet <realo@sympatico.ca> - g200 fixes
24 Bajusz Peter <hyp-x@inf.bme.hu> - motion comp fixes
25 Franck Sicard <Franck.Sicard@miniruth.solsoft.fr> - x11 fixes
26 Brion Vibber <brion@gizmo.usc.edu> - x11 fixes
27 Martin Vogt <mvogt@rhrk.uni-kl.de> - reentrancy fixes
28 Fredrik Vraalsen <vraalsen@cs.uiuc.edu> - general hackage and stuff
29
30(let me know if I forgot anyone)
31
32Thanks to David Schleef for creating me an account on his ppc g4
33machine 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 @@
1
2
3ABOUT LIBMPEG2
4
5libmpeg2 is a free library for decoding mpeg-2 and mpeg-1 video
6streams. It is released under the terms of the GPL license.
7
8The main goals in libmpeg2 development are:
9
10 * Conformance - libmpeg2 is able to decode all mpeg streams that
11 conform to certain restrictions: "constrained parameters" for
12 mpeg-1, and "main profile" for mpeg-2. In practice, this is
13 what most people are using. For streams that follow these
14 restrictions, we believe libmpeg2 is 100% conformant to the
15 mpeg standards - and we have a pretty extensive test suite to
16 check this.
17
18 * Speed - there has been huge efforts there, and we believe
19 libmpeg2 is the fastest library around for what it
20 does. Please tell us if you find a faster one ! With typical
21 video streams as found on DVD's, and doing only decoding with
22 no display, you should be able to get about 110 fps on a
23 PIII/666, or 150 fps on an Athlon/950. This is less than 20
24 cycles per output pixel. In a real player program, the display
25 routines will probably take as much time as the actual
26 decoding !
27
28 * Portability - most of the code is written in C, and when we
29 use platform-specific optimizations (typically assembly
30 routines, currently used for the motion compensation and the
31 inverse cosine transform stages) we always have a generic C
32 routine to fall back on. This should be portable to all
33 architectures - at least we have heard reports from people
34 running this code on x86, ppc, sparc, arm and
35 sh4. Assembly-optimized implementations are available on x86
36 (MMX) and ppc (altivec) architectures. Ultrasparc (VIS) is
37 probably the next on the list - we'll see.
38
39 * Reuseability - we do not want libmpeg2 to include any
40 project-specific code, but it should still include enough
41 features to be used by very diverse projects. We are only
42 starting to get there - the best way to help here is to give
43 us some feedback !
44
45The project homepage is at http://libmpeg2.sourceforge.net/
46
47
48MPEG2DEC
49
50mpeg2dec is a test program for libmpeg2. It decodes mpeg-1 and mpeg-2
51video streams, and also includes a demultiplexer for mpeg-1 and mpeg-2
52program streams. It is purposely kept simple : it does not include
53features like reading files from a DVD, CSS, fullscreen output,
54navigation, etc... The main purpose of mpeg2dec is to have a simple
55test bed for libmpeg2.
56
57The libmpeg2 source code is always distributed in the mpeg2dec
58package, to make it easier for people to test it.
59
60The basic usage is to just type "mpeg2dec file" where file is a
61demultiplexed mpeg video file.
62
63The "-s" option must be used for multiplexed (audio and video) mpeg
64files using the "program stream" format. These files are usualy found
65on the internet or on unencrypted DVDs.
66
67The "-t" option must be used for multiplexed (audio and video) mpeg
68files using the "transport stream" format. These files are usualy
69found in digital TV applications.
70
71The "-o" option is used to select a given output module - for example
72to redirect the output to a file. This is also used for performance
73testing and conformance testing.
74
75The "-c" option is used to disable all optimizations.
76
77
78OTHER PROJECTS USING LIBMPEG2
79
80libmpeg2 is being used by various other projects, including:
81
82 * xine (http://xine.sourceforge.net/) - started as a simple
83 mpeg-2 audio and video decoder, but it since became a
84 full-featured DVD and video media player.
85
86 * VideoLAN (http://www.videolan.org/) - video streaming over an
87 ethernet network, can also be used as a standalone player.
88
89 * MPlayer (http://www.MPlayerHQ.hu) - another good player, it is
90 also very robust against damaged streams.
91
92 * movietime (http://movietime.sourceforge.net/) - still quite
93 young, but it looks very promising !
94
95 * mpeg2decX (http://homepage1.nifty.com/~toku/software_en.html) -
96 a graphical interface for mpeg2dec for macintosh osX.
97
98 * TCVP (http://tcvp.sf.net) - video and music player for unix.
99
100 * drip (http://drip.sourceforge.net/) - a DVD to DIVX transcoder.
101
102 * PoMP
103 (http://www.dmclab.hanyang.ac.kr/research/project/PoDS/PoDS_sw.htm) -
104 a research player optimized to minimize disk power consumption.
105
106 * OMS (http://www.linuxvideo.org/oms/)
107
108 * XMPS (http://xmps.sourceforge.net/)
109
110 * GStreamer (http://www.gstreamer.net/) - a framework for
111 streaming media; it has an mpeg2 decoding plugin based on
112 libmpeg2.
113
114 * mpeglib (http://mpeglib.sourceforge.net/) - a video decoding
115 library that usess libmpeg2 when decoding mpeg streams.
116
117 * daphne (http://daphne.rulecity.com/) - a laserdisc arcade game
118 simulator.
119
120 * GOPchop (http://outflux.net/unix/software/GOPchop/) - a
121 GOP-accurate editor for MPEG2 streams.
122
123If you use libmpeg2 in another project, let us know !
124
125
126TASKS
127
128There are several places where we could easily use some help:
129
130 * Documentation: libmpeg2 still has no documentation. Every
131 project using it has had to figure things out by looking at
132 the header files, at the mpeg2dec sample application, and by
133 asking questions. Writing down a nice documentation would make
134 the code more easily reuseable.
135
136 * Testing: If you find any stream that does not decode right
137 with libmpeg2, let us know ! The best thing would be to mail
138 to the libmpeg2-devel mailing list. Also, it would be nice to
139 build a stress test so we can make sure libmpeg2 never crashes
140 on bad streams.
141
142 * Coding: There is a small TODO list in the mpeg2dec package,
143 you can have a look there ! Most items are pretty terse
144 though.
145
146 * Porting: If you're porting to a new architecture, you might
147 want to experiment with the compile flags defined in
148 configure.in . When you figure out whats fastest on your
149 platform, send us a patch !
150
151 * Assembly optimizations: We only have x86 and altivec
152 optimizations yet, it would be worthwhile writing routines for
153 other architectures, especially those that have SIMD
154 instruction set extensions ! Also the yuv2rgb x86 routines
155 could probably be optimized a lot.
156
157
158CVS SNAPSHOTS
159
160A daily snapshot is created using "make distcheck" every night and
161uploaded to http://libmpeg2.sourceforge.net/files/mpeg2dec-snapshot.tar.gz .
162It is easier to use than the CVS repository, because you do not need
163to have the right versions of automake, autoconf and libtool
164installed. It might be convenient when working on a libmpeg2 port for
165example.
166
167
168CVS REPOSITORY
169
170The latest libmpeg2 and mpeg2dec source code can always be found by
171anonymous CVS:
172
173# export CVSROOT=:pserver:anonymous@cvs.libmpeg2.sourceforge.net:/cvsroot/libmpeg2
174# cvs login (Just press Return when prompted for a password)
175# cvs checkout mpeg2dec
176
177You can also browse the latest changes online at
178http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/libmpeg2/mpeg2dec/
179
180The other CVS modules are mpeg2dec-streams for the test suite, and
181mpeg2dec-livid for the CVS history of the project while it was still
182hosted on the linuxvideo.org servers.
183
184
185MAILING LISTS
186
187See the subscription information at http://libmpeg2.sourceforge.net/lists.html
188
189libmpeg2-devel
190
191This is the main mailing list for technical discussion about
192libmpeg2. Anyone wanting to work on libmpeg2, or maybe just stay
193informed about the development process, should probably subscribe to
194this list.
195
196libmpeg2-checkins
197
198All libmpeg2 checkins are announced there. This is a good way to keep
199track of what goes into CVS.
200
201libmpeg2-announce
202
203This is a very low traffic mailing list, only for announcements of new
204versions 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 @@
1Library: libmpeg2 from mpeg2dec-0.4.0b (Released 2004-01-21)
2Imported: 2006-08-06 by Dave Chapman
3
4
5This directory contains a local version of libmpeg2 imported into
6Rockbox for MPEG video decoding.
7
8
9LICENSING INFORMATION
10
11mpeg2dec and libmpeg2 are licensed under Version 2 of the GNU General
12Public License.
13
14
15IMPORT DETAILS
16
17The following files were imported from the mpeg2dec-0.4.0b
18distribution. Minor changes were made to enable compilation in
19Rockbox and TABs were replaced by spaces to comply with the Rockbox
20coding guidelines.
21
22AUTHORS
23README
24SOURCES
25attributes.h
26cpu_accel.c
27cpu_state.c
28decode.c
29header.c
30idct.c
31motion_comp.c
32mpeg2.h
33mpeg2_internal.h
34slice.c
35video_out.h
36vlc.h
37
38The following files are new, but based on code in mpeg2dec.
39
40Makefile
41mpegplayer.c
42video_out_rockbox.c
43mpeg2dec_config.h
44alloc.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 @@
1/*
2 * attributes.h
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.5
26 */
27
28/* use gcc attribs to align critical data structures */
29#ifdef ATTRIBUTE_ALIGNED_MAX
30#define ATTR_ALIGN(align) __attribute__ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX < align) ? ATTRIBUTE_ALIGNED_MAX : align)))
31#else
32#define ATTR_ALIGN(align)
33#endif
34
35#if defined(LIKELY) && defined (UNLIKELY)
36#define likely(x) LIKELY(x)
37#define unlikely(x) UNLIKELY(x)
38#else
39#define likely(x) (x)
40#define unlikely(x) (x)
41#endif
42
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 @@
1/*
2 * decode.c
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.114
26 */
27
28#include "plugin.h"
29
30#include "mpeg2dec_config.h"
31
32#include "mpeg2.h"
33#include "attributes.h"
34#include "mpeg2_internal.h"
35
36#define BUFFER_SIZE (1194 * 1024)
37
38#if defined(CPU_COLDFIRE) || (defined(CPU_ARM) && ARM_ARCH >= 6)
39/* twice as large as on other targets because coldfire uses
40 * a secondary, transposed buffer for optimisation */
41static int16_t static_dct_block[128] IBSS_ATTR ATTR_ALIGN(16);
42#define DCT_BLOCKSIZE (128 * sizeof (int16_t))
43#else
44static int16_t static_dct_block[64] IBSS_ATTR ATTR_ALIGN(16);
45#define DCT_BLOCKSIZE (64 * sizeof (int16_t))
46#endif
47
48const mpeg2_info_t * mpeg2_info (mpeg2dec_t * mpeg2dec)
49{
50 return &mpeg2dec->info;
51}
52
53static inline int skip_chunk (mpeg2dec_t * mpeg2dec, int bytes)
54{
55 uint8_t * current;
56 uint32_t shift;
57 uint8_t * limit;
58 uint8_t byte;
59
60 if (!bytes)
61 return 0;
62
63 current = mpeg2dec->buf_start;
64 shift = mpeg2dec->shift;
65 limit = current + bytes;
66
67 do
68 {
69 byte = *current++;
70
71 if (shift == 0x00000100)
72 {
73 int skipped;
74
75 mpeg2dec->shift = 0xffffff00;
76 skipped = current - mpeg2dec->buf_start;
77 mpeg2dec->buf_start = current;
78
79 return skipped;
80 }
81
82 shift = (shift | byte) << 8;
83 }
84 while (current < limit);
85
86 mpeg2dec->shift = shift;
87 mpeg2dec->buf_start = current;
88
89 return 0;
90}
91
92static inline int copy_chunk (mpeg2dec_t * mpeg2dec, int bytes)
93{
94 uint8_t * current;
95 uint32_t shift;
96 uint8_t * chunk_ptr;
97 uint8_t * limit;
98 uint8_t byte;
99
100 if (!bytes)
101 return 0;
102
103 current = mpeg2dec->buf_start;
104 shift = mpeg2dec->shift;
105 chunk_ptr = mpeg2dec->chunk_ptr;
106 limit = current + bytes;
107
108 do
109 {
110 byte = *current++;
111
112 if (shift == 0x00000100)
113 {
114 int copied;
115
116 mpeg2dec->shift = 0xffffff00;
117 mpeg2dec->chunk_ptr = chunk_ptr + 1;
118 copied = current - mpeg2dec->buf_start;
119 mpeg2dec->buf_start = current;
120 return copied;
121 }
122
123 shift = (shift | byte) << 8;
124 *chunk_ptr++ = byte;
125 }
126 while (current < limit);
127
128 mpeg2dec->shift = shift;
129 mpeg2dec->buf_start = current;
130 return 0;
131}
132
133void mpeg2_buffer (mpeg2dec_t * mpeg2dec, uint8_t * start, uint8_t * end)
134{
135 mpeg2dec->buf_start = start;
136 mpeg2dec->buf_end = end;
137}
138
139int mpeg2_getpos (mpeg2dec_t * mpeg2dec)
140{
141 return mpeg2dec->buf_end - mpeg2dec->buf_start;
142}
143
144static inline mpeg2_state_t seek_chunk (mpeg2dec_t * mpeg2dec)
145{
146 int size, skipped;
147
148 size = mpeg2dec->buf_end - mpeg2dec->buf_start;
149 skipped = skip_chunk (mpeg2dec, size);
150
151 if (!skipped)
152 {
153 mpeg2dec->bytes_since_tag += size;
154 return STATE_BUFFER;
155 }
156
157 mpeg2dec->bytes_since_tag += skipped;
158 mpeg2dec->code = mpeg2dec->buf_start[-1];
159
160 return STATE_INTERNAL_NORETURN;
161}
162
163mpeg2_state_t mpeg2_seek_header (mpeg2dec_t * mpeg2dec)
164{
165 while (!(mpeg2dec->code == 0xb3 ||
166 ((mpeg2dec->code == 0xb7 || mpeg2dec->code == 0xb8 ||
167 !mpeg2dec->code) && mpeg2dec->sequence.width != (unsigned)-1)))
168 {
169 if (seek_chunk (mpeg2dec) == STATE_BUFFER)
170 return STATE_BUFFER;
171 }
172
173 mpeg2dec->chunk_start =
174 mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer;
175
176 mpeg2dec->user_data_len = 0;
177
178 return ((mpeg2dec->code == 0xb7) ?
179 mpeg2_header_end(mpeg2dec) : mpeg2_parse_header(mpeg2dec));
180}
181
182#define RECEIVED(code,state) (((state) << 8) + (code))
183
184mpeg2_state_t mpeg2_parse (mpeg2dec_t * mpeg2dec)
185{
186 int size_buffer, size_chunk, copied;
187
188 if (mpeg2dec->action)
189 {
190 mpeg2_state_t state;
191
192 state = mpeg2dec->action (mpeg2dec);
193
194 if (state > STATE_INTERNAL_NORETURN)
195 return state;
196 }
197
198 while (1)
199 {
200 while ((unsigned) (mpeg2dec->code - mpeg2dec->first_decode_slice) <
201 mpeg2dec->nb_decode_slices)
202 {
203 size_buffer = mpeg2dec->buf_end - mpeg2dec->buf_start;
204 size_chunk = mpeg2dec->chunk_buffer + BUFFER_SIZE -
205 mpeg2dec->chunk_ptr;
206
207 if (size_buffer <= size_chunk)
208 {
209 copied = copy_chunk (mpeg2dec, size_buffer);
210
211 if (!copied)
212 {
213 mpeg2dec->bytes_since_tag += size_buffer;
214 mpeg2dec->chunk_ptr += size_buffer;
215 return STATE_BUFFER;
216 }
217 }
218 else
219 {
220 copied = copy_chunk (mpeg2dec, size_chunk);
221
222 if (!copied)
223 {
224 /* filled the chunk buffer without finding a start code */
225 mpeg2dec->bytes_since_tag += size_chunk;
226 mpeg2dec->action = seek_chunk;
227 return STATE_INVALID;
228 }
229 }
230
231 mpeg2dec->bytes_since_tag += copied;
232
233 mpeg2_slice (&mpeg2dec->decoder, mpeg2dec->code,
234 mpeg2dec->chunk_start);
235 mpeg2dec->code = mpeg2dec->buf_start[-1];
236 mpeg2dec->chunk_ptr = mpeg2dec->chunk_start;
237 }
238
239 if ((unsigned) (mpeg2dec->code - 1) >= 0xb0 - 1)
240 break;
241
242 if (seek_chunk (mpeg2dec) == STATE_BUFFER)
243 return STATE_BUFFER;
244 }
245
246 mpeg2dec->action = mpeg2_seek_header;
247
248 switch (mpeg2dec->code)
249 {
250 case 0x00:
251 return mpeg2dec->state;
252 case 0xb3:
253 case 0xb7:
254 case 0xb8:
255 return (mpeg2dec->state == STATE_SLICE) ? STATE_SLICE : STATE_INVALID;
256 default:
257 mpeg2dec->action = seek_chunk;
258 return STATE_INVALID;
259 }
260}
261
262mpeg2_state_t mpeg2_parse_header (mpeg2dec_t * mpeg2dec)
263{
264 static int (* const process_header[9]) (mpeg2dec_t *) =
265 {
266 mpeg2_header_picture,
267 mpeg2_header_extension,
268 mpeg2_header_user_data,
269 mpeg2_header_sequence,
270 NULL,
271 NULL,
272 NULL,
273 NULL,
274 mpeg2_header_gop
275 };
276
277 int size_buffer, size_chunk, copied;
278
279 mpeg2dec->action = mpeg2_parse_header;
280 mpeg2dec->info.user_data = NULL;
281 mpeg2dec->info.user_data_len = 0;
282
283 while (1)
284 {
285 size_buffer = mpeg2dec->buf_end - mpeg2dec->buf_start;
286 size_chunk = mpeg2dec->chunk_buffer + BUFFER_SIZE -
287 mpeg2dec->chunk_ptr;
288
289 if (size_buffer <= size_chunk)
290 {
291 copied = copy_chunk (mpeg2dec, size_buffer);
292
293 if (!copied)
294 {
295 mpeg2dec->bytes_since_tag += size_buffer;
296 mpeg2dec->chunk_ptr += size_buffer;
297 return STATE_BUFFER;
298 }
299 }
300 else
301 {
302 copied = copy_chunk (mpeg2dec, size_chunk);
303
304 if (!copied)
305 {
306 /* filled the chunk buffer without finding a start code */
307 mpeg2dec->bytes_since_tag += size_chunk;
308 mpeg2dec->code = 0xb4;
309 mpeg2dec->action = mpeg2_seek_header;
310 return STATE_INVALID;
311 }
312 }
313
314 mpeg2dec->bytes_since_tag += copied;
315
316 if (process_header[mpeg2dec->code & 0x0b] (mpeg2dec))
317 {
318 mpeg2dec->code = mpeg2dec->buf_start[-1];
319 mpeg2dec->action = mpeg2_seek_header;
320 return STATE_INVALID;
321 }
322
323 mpeg2dec->code = mpeg2dec->buf_start[-1];
324
325 switch (RECEIVED (mpeg2dec->code, mpeg2dec->state))
326 {
327 /* state transition after a sequence header */
328 case RECEIVED (0x00, STATE_SEQUENCE):
329 case RECEIVED (0xb8, STATE_SEQUENCE):
330 mpeg2_header_sequence_finalize (mpeg2dec);
331 break;
332
333 /* other legal state transitions */
334 case RECEIVED (0x00, STATE_GOP):
335 mpeg2_header_gop_finalize (mpeg2dec);
336 break;
337 case RECEIVED (0x01, STATE_PICTURE):
338 case RECEIVED (0x01, STATE_PICTURE_2ND):
339 mpeg2_header_picture_finalize (mpeg2dec);
340 mpeg2dec->action = mpeg2_header_slice_start;
341 break;
342
343 /* legal headers within a given state */
344 case RECEIVED (0xb2, STATE_SEQUENCE):
345 case RECEIVED (0xb2, STATE_GOP):
346 case RECEIVED (0xb2, STATE_PICTURE):
347 case RECEIVED (0xb2, STATE_PICTURE_2ND):
348 case RECEIVED (0xb5, STATE_SEQUENCE):
349 case RECEIVED (0xb5, STATE_PICTURE):
350 case RECEIVED (0xb5, STATE_PICTURE_2ND):
351 mpeg2dec->chunk_ptr = mpeg2dec->chunk_start;
352 continue;
353
354 default:
355 mpeg2dec->action = mpeg2_seek_header;
356 return STATE_INVALID;
357 }
358
359 mpeg2dec->chunk_start = mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer;
360 mpeg2dec->user_data_len = 0;
361
362 return mpeg2dec->state;
363 }
364}
365
366int mpeg2_convert (mpeg2dec_t * mpeg2dec, mpeg2_convert_t convert, void * arg)
367{
368 mpeg2_convert_init_t convert_init;
369 int error;
370
371 error = convert (MPEG2_CONVERT_SET, NULL, &mpeg2dec->sequence, 0,
372 arg, &convert_init);
373
374 if (!error)
375 {
376 mpeg2dec->convert = convert;
377 mpeg2dec->convert_arg = arg;
378 mpeg2dec->convert_id_size = convert_init.id_size;
379 mpeg2dec->convert_stride = 0;
380 }
381
382 return error;
383}
384
385int mpeg2_stride (mpeg2dec_t * mpeg2dec, int stride)
386{
387 if (!mpeg2dec->convert)
388 {
389 if (stride < (int) mpeg2dec->sequence.width)
390 stride = mpeg2dec->sequence.width;
391
392 mpeg2dec->decoder.stride_frame = stride;
393 }
394 else
395 {
396 mpeg2_convert_init_t convert_init;
397
398 stride = mpeg2dec->convert(MPEG2_CONVERT_STRIDE, NULL,
399 &mpeg2dec->sequence, stride,
400 mpeg2dec->convert_arg,
401 &convert_init);
402
403 mpeg2dec->convert_id_size = convert_init.id_size;
404 mpeg2dec->convert_stride = stride;
405 }
406
407 return stride;
408}
409
410void mpeg2_set_buf (mpeg2dec_t * mpeg2dec, uint8_t * buf[MPEG2_COMPONENTS], void * id)
411{
412 mpeg2_fbuf_t * fbuf;
413
414 if (mpeg2dec->custom_fbuf)
415 {
416 if (mpeg2dec->state == STATE_SEQUENCE)
417 {
418 mpeg2dec->fbuf[2] = mpeg2dec->fbuf[1];
419 mpeg2dec->fbuf[1] = mpeg2dec->fbuf[0];
420 }
421
422 mpeg2_set_fbuf (mpeg2dec, (mpeg2dec->decoder.coding_type ==
423 PIC_FLAG_CODING_TYPE_B));
424
425 fbuf = mpeg2dec->fbuf[0];
426 }
427 else
428 {
429 fbuf = &mpeg2dec->fbuf_alloc[mpeg2dec->alloc_index].fbuf;
430 mpeg2dec->alloc_index_user = ++mpeg2dec->alloc_index;
431 }
432
433 fbuf->buf[0] = buf[0];
434#if MPEG2_COLOR
435 fbuf->buf[1] = buf[1];
436 fbuf->buf[2] = buf[2];
437#endif
438
439 fbuf->id = id;
440}
441
442void mpeg2_custom_fbuf (mpeg2dec_t * mpeg2dec, int custom_fbuf)
443{
444 mpeg2dec->custom_fbuf = custom_fbuf;
445}
446
447void mpeg2_skip (mpeg2dec_t * mpeg2dec, int skip)
448{
449 mpeg2dec->first_decode_slice = 1;
450 mpeg2dec->nb_decode_slices = skip ? 0 : (0xb0 - 1);
451}
452
453void mpeg2_slice_region (mpeg2dec_t * mpeg2dec, int start, int end)
454{
455 start = (start < 1) ? 1 : (start > 0xb0) ? 0xb0 : start;
456 end = (end < start) ? start : (end > 0xb0) ? 0xb0 : end;
457 mpeg2dec->first_decode_slice = start;
458 mpeg2dec->nb_decode_slices = end - start;
459}
460
461void mpeg2_tag_picture (mpeg2dec_t * mpeg2dec, uint32_t tag, uint32_t tag2)
462{
463 mpeg2dec->tag_previous = mpeg2dec->tag_current;
464 mpeg2dec->tag2_previous = mpeg2dec->tag2_current;
465 mpeg2dec->tag_current = tag;
466 mpeg2dec->tag2_current = tag2;
467 mpeg2dec->num_tags++;
468 mpeg2dec->bytes_since_tag = 0;
469}
470
471void mpeg2_reset (mpeg2dec_t * mpeg2dec, int full_reset)
472{
473 mpeg2dec->buf_start = mpeg2dec->buf_end = NULL;
474 mpeg2dec->num_tags = 0;
475 mpeg2dec->shift = 0xffffff00;
476 mpeg2dec->code = 0xb4;
477 mpeg2dec->action = mpeg2_seek_header;
478 mpeg2dec->state = STATE_INVALID;
479 mpeg2dec->first = 1;
480
481 mpeg2_reset_info(&mpeg2dec->info);
482 mpeg2dec->info.gop = NULL;
483 mpeg2dec->info.user_data = NULL;
484 mpeg2dec->info.user_data_len = 0;
485
486 if (full_reset)
487 {
488 mpeg2dec->info.sequence = NULL;
489 mpeg2_header_state_init (mpeg2dec);
490 }
491}
492
493mpeg2dec_t * mpeg2_init (void)
494{
495 mpeg2dec_t * mpeg2dec;
496
497 mpeg2_idct_init ();
498
499 mpeg2dec = (mpeg2dec_t *)mpeg2_bufalloc(sizeof (mpeg2dec_t),
500 MPEG2_ALLOC_MPEG2DEC);
501 if (mpeg2dec == NULL)
502 return NULL;
503
504 mpeg2dec->decoder.DCTblock = static_dct_block;
505
506 rb->memset (mpeg2dec->decoder.DCTblock, 0, DCT_BLOCKSIZE);
507
508 DEBUGF("DCTblock: %p\n", mpeg2dec->decoder.DCTblock);
509
510 mpeg2dec->chunk_buffer = (uint8_t *)mpeg2_bufalloc(BUFFER_SIZE + 4,
511 MPEG2_ALLOC_CHUNK);
512
513 mpeg2dec->sequence.width = (unsigned)-1;
514 mpeg2_reset (mpeg2dec, 1);
515
516 return mpeg2dec;
517}
518
519void mpeg2_close (mpeg2dec_t * mpeg2dec)
520{
521 mpeg2_header_state_init (mpeg2dec);
522#if 0
523 /* These are dedicated buffers in rockbox */
524 mpeg2_free (mpeg2dec->chunk_buffer);
525 mpeg2_free (mpeg2dec);
526#endif
527}
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 @@
1/*
2 * header.c
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 2003 Regis Duchesne <hpreg@zoy.org>
5 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
6 *
7 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
8 * See http://libmpeg2.sourceforge.net/ for updates.
9 *
10 * mpeg2dec is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * mpeg2dec is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 * $Id$
25 * libmpeg2 sync history:
26 * 2008-07-01 - CVS revision 1.101
27 */
28
29#include "plugin.h"
30
31#include "mpeg2dec_config.h"
32
33#include "mpeg2.h"
34#include "attributes.h"
35#include "mpeg2_internal.h"
36
37#define SEQ_EXT 2
38#define SEQ_DISPLAY_EXT 4
39#define QUANT_MATRIX_EXT 8
40#define COPYRIGHT_EXT 0x10
41#define PIC_DISPLAY_EXT 0x80
42#define PIC_CODING_EXT 0x100
43
44/* default intra quant matrix, in zig-zag order */
45static const uint8_t default_intra_quantizer_matrix[64] =
46{
47 8,
48 16, 16,
49 19, 16, 19,
50 22, 22, 22, 22,
51 22, 22, 26, 24, 26,
52 27, 27, 27, 26, 26, 26,
53 26, 27, 27, 27, 29, 29, 29,
54 34, 34, 34, 29, 29, 29, 27, 27,
55 29, 29, 32, 32, 34, 34, 37,
56 38, 37, 35, 35, 34, 35,
57 38, 38, 40, 40, 40,
58 48, 48, 46, 46,
59 56, 56, 58,
60 69, 69,
61 83
62};
63
64const uint8_t default_mpeg2_scan_norm[64] =
65{
66 /* Zig-Zag scan pattern */
67 0, 1, 8, 16, 9, 2, 3, 10,
68 17, 24, 32, 25, 18, 11, 4, 5,
69 12, 19, 26, 33, 40, 48, 41, 34,
70 27, 20, 13, 6, 7, 14, 21, 28,
71 35, 42, 49, 56, 57, 50, 43, 36,
72 29, 22, 15, 23, 30, 37, 44, 51,
73 58, 59, 52, 45, 38, 31, 39, 46,
74 53, 60, 61, 54, 47, 55, 62, 63
75};
76
77const uint8_t default_mpeg2_scan_alt[64] =
78{
79 /* Alternate scan pattern */
80 0, 8, 16, 24, 1, 9, 2, 10,
81 17, 25, 32, 40, 48, 56, 57, 49,
82 41, 33, 26, 18, 3, 11, 4, 12,
83 19, 27, 34, 42, 50, 58, 35, 43,
84 51, 59, 20, 28, 5, 13, 6, 14,
85 21, 29, 36, 44, 52, 60, 37, 45,
86 53, 61, 22, 30, 7, 15, 23, 31,
87 38, 46, 54, 62, 39, 47, 55, 63
88};
89
90uint8_t mpeg2_scan_norm[64] IDATA_ATTR;
91uint8_t mpeg2_scan_alt[64] IDATA_ATTR;
92
93void mpeg2_header_state_init (mpeg2dec_t * mpeg2dec)
94{
95 if (mpeg2dec->sequence.width != (unsigned)-1)
96 {
97 mpeg2dec->sequence.width = (unsigned)-1;
98 mpeg2_mem_reset(); /* Clean the memory slate */
99#if 0
100 if (!mpeg2dec->custom_fbuf)
101 {
102 int i;
103 for (i = mpeg2dec->alloc_index_user;
104 i < mpeg2dec->alloc_index; i++)
105 {
106 mpeg2_free(mpeg2dec->fbuf_alloc[i].fbuf.buf[0]);
107#if MPEG2_COLOR
108 mpeg2_free(mpeg2dec->fbuf_alloc[i].fbuf.buf[1]);
109 mpeg2_free(mpeg2dec->fbuf_alloc[i].fbuf.buf[2]);
110#endif
111 }
112 }
113
114 if (mpeg2dec->convert_start)
115 {
116 int i;
117 for (i = 0; i < 3; i++)
118 {
119 mpeg2_free(mpeg2dec->yuv_buf[i][0]);
120#if MPEG2_COLOR
121 mpeg2_free(mpeg2dec->yuv_buf[i][1]);
122 mpeg2_free(mpeg2dec->yuv_buf[i][2]);
123#endif
124 }
125 }
126
127 if (mpeg2dec->decoder.convert_id)
128 {
129 mpeg2_free(mpeg2dec->decoder.convert_id);
130 }
131#endif
132 }
133
134 mpeg2dec->decoder.coding_type = I_TYPE;
135 mpeg2dec->decoder.convert = NULL;
136 mpeg2dec->decoder.convert_id = NULL;
137
138 mpeg2dec->picture = mpeg2dec->pictures;
139
140 mpeg2dec->fbuf[0] = &mpeg2dec->fbuf_alloc[0].fbuf;
141 mpeg2dec->fbuf[1] = &mpeg2dec->fbuf_alloc[1].fbuf;
142 mpeg2dec->fbuf[2] = &mpeg2dec->fbuf_alloc[2].fbuf;
143
144 mpeg2dec->first = 1;
145 mpeg2dec->alloc_index = 0;
146 mpeg2dec->alloc_index_user = 0;
147 mpeg2dec->first_decode_slice = 1;
148 mpeg2dec->nb_decode_slices = 0xb0 - 1;
149 mpeg2dec->convert = NULL;
150 mpeg2dec->convert_start = NULL;
151 mpeg2dec->custom_fbuf = 0;
152 mpeg2dec->yuv_index = 0;
153}
154
155void mpeg2_reset_info (mpeg2_info_t * info)
156{
157 info->current_picture =
158 info->current_picture_2nd = NULL;
159
160 info->display_picture =
161 info->display_picture_2nd = NULL;
162
163 info->current_fbuf =
164 info->display_fbuf =
165 info->discard_fbuf = NULL;
166}
167
168static void info_user_data (mpeg2dec_t * mpeg2dec)
169{
170 if (mpeg2dec->user_data_len)
171 {
172 mpeg2dec->info.user_data = mpeg2dec->chunk_buffer;
173 mpeg2dec->info.user_data_len = mpeg2dec->user_data_len - 3;
174 }
175}
176
177int mpeg2_header_sequence (mpeg2dec_t * mpeg2dec)
178{
179 static const unsigned int frame_period[16] =
180 {
181 0, 1126125, 1125000, 1080000, 900900, 900000, 540000, 450450, 450000,
182 /* unofficial: xing 15 fps */
183 1800000,
184 /* unofficial: libmpeg3 "Unofficial economy rates" 5/10/12/15 fps */
185 5400000, 2700000, 2250000, 1800000, 0, 0
186 };
187
188 uint8_t * buffer = mpeg2dec->chunk_start;
189 mpeg2_sequence_t * sequence = &mpeg2dec->new_sequence;
190 int i;
191
192 if ((buffer[6] & 0x20) != 0x20) /* missing marker_bit */
193 return 1;
194
195 i = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
196
197 if (!(sequence->display_width = sequence->picture_width = i >> 12))
198 return 1;
199
200 if (!(sequence->display_height = sequence->picture_height = i & 0xfff))
201 return 1;
202
203 sequence->width = (sequence->picture_width + 15) & ~15;
204 sequence->height = (sequence->picture_height + 15) & ~15;
205 sequence->chroma_width = sequence->width >> 1;
206 sequence->chroma_height = sequence->height >> 1;
207
208 sequence->flags = SEQ_FLAG_PROGRESSIVE_SEQUENCE |
209 SEQ_VIDEO_FORMAT_UNSPECIFIED;
210
211 sequence->pixel_width = buffer[3] >> 4; /* aspect ratio */
212 sequence->frame_period = frame_period[buffer[3] & 15];
213
214 sequence->byte_rate = (buffer[4]<<10) | (buffer[5]<<2) | (buffer[6]>>6);
215
216 sequence->vbv_buffer_size = ((buffer[6]<<16) | (buffer[7]<<8)) & 0x1ff800;
217
218 if (buffer[7] & 4)
219 sequence->flags |= SEQ_FLAG_CONSTRAINED_PARAMETERS;
220
221 mpeg2dec->copy_matrix = 3;
222
223 if (buffer[7] & 2)
224 {
225 for (i = 0; i < 64; i++)
226 {
227 mpeg2dec->new_quantizer_matrix[0][mpeg2_scan_norm[i]] =
228 (buffer[i+7] << 7) | (buffer[i+8] >> 1);
229 }
230
231 buffer += 64;
232 }
233 else
234 {
235 for (i = 0; i < 64; i++)
236 {
237 mpeg2dec->new_quantizer_matrix[0][mpeg2_scan_norm[i]] =
238 default_intra_quantizer_matrix[i];
239 }
240 }
241
242 if (buffer[7] & 1)
243 {
244 for (i = 0; i < 64; i++)
245 {
246 mpeg2dec->new_quantizer_matrix[1][mpeg2_scan_norm[i]] =
247 buffer[i+8];
248 }
249 }
250 else
251 {
252 rb->memset (mpeg2dec->new_quantizer_matrix[1], 16, 64);
253 }
254
255 sequence->profile_level_id = 0x80;
256 sequence->colour_primaries = 0;
257 sequence->transfer_characteristics = 0;
258 sequence->matrix_coefficients = 0;
259
260 mpeg2dec->ext_state = SEQ_EXT;
261 mpeg2dec->state = STATE_SEQUENCE;
262
263 mpeg2dec->display_offset_x =
264 mpeg2dec->display_offset_y = 0;
265
266 return 0;
267}
268
269static int sequence_ext (mpeg2dec_t * mpeg2dec)
270{
271 uint8_t * buffer = mpeg2dec->chunk_start;
272 mpeg2_sequence_t * sequence = &mpeg2dec->new_sequence;
273 uint32_t flags;
274
275 if (!(buffer[3] & 1))
276 return 1;
277
278 sequence->profile_level_id = (buffer[0] << 4) | (buffer[1] >> 4);
279
280 sequence->picture_width += ((buffer[1] << 13) | (buffer[2] << 5)) & 0x3000;
281 sequence->display_width = sequence->picture_width;
282
283 sequence->picture_height += (buffer[2] << 7) & 0x3000;
284 sequence->display_height = sequence->picture_height;
285
286 sequence->width = (sequence->picture_width + 15) & ~15;
287 sequence->height = (sequence->picture_height + 15) & ~15;
288
289 flags = sequence->flags | SEQ_FLAG_MPEG2;
290
291 if (!(buffer[1] & 8))
292 {
293 flags &= ~SEQ_FLAG_PROGRESSIVE_SEQUENCE;
294 sequence->height = (sequence->height + 31) & ~31;
295 }
296
297 if (buffer[5] & 0x80)
298 flags |= SEQ_FLAG_LOW_DELAY;
299
300 sequence->flags = flags;
301 sequence->chroma_width = sequence->width;
302 sequence->chroma_height = sequence->height;
303
304 switch (buffer[1] & 6)
305 {
306 case 0: /* invalid */
307 return 1;
308 case 2: /* 4:2:0 */
309 sequence->chroma_height >>= 1;
310 /* fallthrough */
311 case 4: /* 4:2:2 */
312 sequence->chroma_width >>= 1;
313 }
314
315 sequence->byte_rate += ((buffer[2]<<25) | (buffer[3]<<17)) & 0x3ffc0000;
316
317 sequence->vbv_buffer_size |= buffer[4] << 21;
318
319 sequence->frame_period =
320 sequence->frame_period * ((buffer[5]&31)+1) / (((buffer[5]>>2)&3)+1);
321
322 mpeg2dec->ext_state = SEQ_DISPLAY_EXT;
323
324 return 0;
325}
326
327static int sequence_display_ext (mpeg2dec_t * mpeg2dec)
328{
329 uint8_t * buffer = mpeg2dec->chunk_start;
330 mpeg2_sequence_t * sequence = &mpeg2dec->new_sequence;
331 int x;
332
333 sequence->flags = (sequence->flags & ~SEQ_MASK_VIDEO_FORMAT) |
334 ((buffer[0] << 4) & SEQ_MASK_VIDEO_FORMAT);
335
336 if (buffer[0] & 1)
337 {
338 sequence->flags |= SEQ_FLAG_COLOUR_DESCRIPTION;
339 sequence->colour_primaries = buffer[1];
340 sequence->transfer_characteristics = buffer[2];
341 sequence->matrix_coefficients = buffer[3];
342 buffer += 3;
343 }
344
345 if (!(buffer[2] & 2)) /* missing marker_bit */
346 return 1;
347
348 x = (buffer[1] << 6) | (buffer[2] >> 2);
349 if (x)
350 sequence->display_width = x;
351
352 x = ((buffer[2] & 1) << 13) | (buffer[3] << 5) | (buffer[4] >> 3);
353 if (x)
354 sequence->display_height = x;
355
356 return 0;
357}
358
359static inline void simplify (unsigned int * u, unsigned int * v)
360{
361 unsigned int a, b, tmp;
362
363 a = *u;
364 b = *v;
365
366 /* find greatest common divisor */
367 while (a)
368 {
369 tmp = a;
370 a = b % tmp;
371 b = tmp;
372 }
373
374 *u /= b;
375 *v /= b;
376}
377
378static inline void finalize_sequence (mpeg2_sequence_t * sequence)
379{
380 int width;
381 int height;
382
383 sequence->byte_rate *= 50;
384
385 if (sequence->flags & SEQ_FLAG_MPEG2)
386 {
387 switch (sequence->pixel_width)
388 {
389 case 1: /* square pixels */
390 sequence->pixel_width =
391 sequence->pixel_height = 1;
392 return;
393 case 2: /* 4:3 aspect ratio */
394 width = 4;
395 height = 3;
396 break;
397 case 3: /* 16:9 aspect ratio */
398 width = 16;
399 height = 9;
400 break;
401 case 4: /* 2.21:1 aspect ratio */
402 width = 221;
403 height = 100;
404 break;
405 default: /* illegal */
406 sequence->pixel_width =
407 sequence->pixel_height = 0;
408 return;
409 }
410
411 width *= sequence->display_height;
412 height *= sequence->display_width;
413 }
414 else
415 {
416 if (sequence->byte_rate == 50 * 0x3ffff)
417 sequence->byte_rate = 0; /* mpeg-1 VBR */
418
419 switch (sequence->pixel_width)
420 {
421 case 0:
422 case 15: /* illegal */
423 sequence->pixel_width =
424 sequence->pixel_height = 0;
425 return;
426 case 1: /* square pixels */
427 sequence->pixel_width =
428 sequence->pixel_height = 1;
429 return;
430 case 3: /* 720x576 16:9 */
431 sequence->pixel_width = 64;
432 sequence->pixel_height = 45;
433 return;
434 case 6: /* 720x480 16:9 */
435 sequence->pixel_width = 32;
436 sequence->pixel_height = 27;
437 return;
438 case 8: /* BT.601 625 lines 4:3 */
439 sequence->pixel_width = 59;
440 sequence->pixel_height = 54;
441 return;
442 case 12: /* BT.601 525 lines 4:3 */
443 sequence->pixel_width = 10;
444 sequence->pixel_height = 11;
445 return;
446 default:
447 height = 88 * sequence->pixel_width + 1171;
448 width = 2000;
449 }
450 }
451
452 sequence->pixel_width = width;
453 sequence->pixel_height = height;
454
455 simplify(&sequence->pixel_width, &sequence->pixel_height);
456}
457
458int mpeg2_guess_aspect (const mpeg2_sequence_t * sequence,
459 unsigned int * pixel_width,
460 unsigned int * pixel_height)
461{
462 static const struct
463 {
464 unsigned int width, height;
465 } video_modes[] =
466 {
467 {720, 576}, /* 625 lines, 13.5 MHz (D1, DV, DVB, DVD) */
468 {704, 576}, /* 625 lines, 13.5 MHz (1/1 D1, DVB, DVD, 4CIF) */
469 {544, 576}, /* 625 lines, 10.125 MHz (DVB, laserdisc) */
470 {528, 576}, /* 625 lines, 10.125 MHz (3/4 D1, DVB, laserdisc) */
471 {480, 576}, /* 625 lines, 9 MHz (2/3 D1, DVB, SVCD) */
472 {352, 576}, /* 625 lines, 6.75 MHz (D2, 1/2 D1, CVD, DVB, DVD) */
473 {352, 288}, /* 625 lines, 6.75 MHz, 1 field (D4, VCD, DVB, DVD, CIF) */
474 {176, 144}, /* 625 lines, 3.375 MHz, half field (QCIF) */
475 {720, 486}, /* 525 lines, 13.5 MHz (D1) */
476 {704, 486}, /* 525 lines, 13.5 MHz */
477 {720, 480}, /* 525 lines, 13.5 MHz (DV, DSS, DVD) */
478 {704, 480}, /* 525 lines, 13.5 MHz (1/1 D1, ATSC, DVD) */
479 {544, 480}, /* 525 lines. 10.125 MHz (DSS, laserdisc) */
480 {528, 480}, /* 525 lines. 10.125 MHz (3/4 D1, laserdisc) */
481 {480, 480}, /* 525 lines, 9 MHz (2/3 D1, SVCD) */
482 {352, 480}, /* 525 lines, 6.75 MHz (D2, 1/2 D1, CVD, DVD) */
483 {352, 240} /* 525 lines. 6.75 MHz, 1 field (D4, VCD, DSS, DVD) */
484 };
485 unsigned int width, height, pix_width, pix_height, i, DAR_16_9;
486
487 *pixel_width = sequence->pixel_width;
488 *pixel_height = sequence->pixel_height;
489 width = sequence->picture_width;
490 height = sequence->picture_height;
491
492 for (i = 0; i < sizeof (video_modes) / sizeof (video_modes[0]); i++)
493 {
494 if (width == video_modes[i].width && height == video_modes[i].height)
495 break;
496 }
497
498 if (i == ARRAYLEN(video_modes) ||
499 (sequence->pixel_width == 1 && sequence->pixel_height == 1) ||
500 width != sequence->display_width || height != sequence->display_height)
501 {
502 return 0;
503 }
504
505 for (pix_height = 1; height * pix_height < 480; pix_height <<= 1);
506 height *= pix_height;
507
508 for (pix_width = 1; width * pix_width <= 352; pix_width <<= 1);
509 width *= pix_width;
510
511 if (!(sequence->flags & SEQ_FLAG_MPEG2))
512 {
513 static unsigned int mpeg1_check[2][2] = {{11, 54}, {27, 45}};
514 DAR_16_9 = (sequence->pixel_height == 27 ||
515 sequence->pixel_height == 45);
516 if (width < 704 ||
517 sequence->pixel_height != mpeg1_check[DAR_16_9][height == 576])
518 return 0;
519 }
520 else
521 {
522 DAR_16_9 = (3 * sequence->picture_width * sequence->pixel_width >
523 4 * sequence->picture_height * sequence->pixel_height);
524 switch (width)
525 {
526 case 528:
527 case 544:
528 pix_width *= 4;
529 pix_height *= 3;
530 break;
531 case 480:
532 pix_width *= 3;
533 pix_height *= 2;
534 break;
535 }
536 }
537
538 if (DAR_16_9)
539 {
540 pix_width *= 4;
541 pix_height *= 3;
542 }
543
544 if (height == 576)
545 {
546 pix_width *= 59;
547 pix_height *= 54;
548 }
549 else
550 {
551 pix_width *= 10;
552 pix_height *= 11;
553 }
554
555 *pixel_width = pix_width;
556 *pixel_height = pix_height;
557
558 simplify (pixel_width, pixel_height);
559
560 return (height == 576) ? 1 : 2;
561}
562
563static void copy_matrix (mpeg2dec_t * mpeg2dec, int index)
564{
565 if (rb->memcmp (mpeg2dec->quantizer_matrix[index],
566 mpeg2dec->new_quantizer_matrix[index], 64))
567 {
568 rb->memcpy (mpeg2dec->quantizer_matrix[index],
569 mpeg2dec->new_quantizer_matrix[index], 64);
570
571 mpeg2dec->scaled[index] = -1;
572 }
573}
574
575static void finalize_matrix (mpeg2dec_t * mpeg2dec)
576{
577 mpeg2_decoder_t *decoder = &mpeg2dec->decoder;
578 int i;
579
580 for (i = 0; i < 2; i++)
581 {
582 if (mpeg2dec->copy_matrix & (1 << i))
583 copy_matrix (mpeg2dec, i);
584
585 if ((mpeg2dec->copy_matrix & (4 << i)) &&
586 rb->memcmp(mpeg2dec->quantizer_matrix[i],
587 mpeg2dec->new_quantizer_matrix[i+2], 64))
588 {
589 copy_matrix (mpeg2dec, i + 2);
590 decoder->chroma_quantizer[i] = decoder->quantizer_prescale[i+2];
591 }
592 else if (mpeg2dec->copy_matrix & (5 << i))
593 {
594 decoder->chroma_quantizer[i] = decoder->quantizer_prescale[i];
595 }
596 }
597}
598
599static mpeg2_state_t invalid_end_action (mpeg2dec_t * mpeg2dec)
600{
601 mpeg2_reset_info (&mpeg2dec->info);
602
603 mpeg2dec->info.gop = NULL;
604
605 info_user_data (mpeg2dec);
606
607 mpeg2_header_state_init (mpeg2dec);
608
609 mpeg2dec->sequence = mpeg2dec->new_sequence;
610 mpeg2dec->action = mpeg2_seek_header;
611 mpeg2dec->state = STATE_SEQUENCE;
612
613 return STATE_SEQUENCE;
614}
615
616void mpeg2_header_sequence_finalize (mpeg2dec_t * mpeg2dec)
617{
618 mpeg2_sequence_t * sequence = &mpeg2dec->new_sequence;
619 mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
620
621 finalize_sequence(sequence);
622 finalize_matrix(mpeg2dec);
623
624 decoder->mpeg1 = !(sequence->flags & SEQ_FLAG_MPEG2);
625 decoder->width = sequence->width;
626 decoder->height = sequence->height;
627 decoder->vertical_position_extension = sequence->picture_height > 2800;
628 decoder->chroma_format = (sequence->chroma_width == sequence->width) +
629 (sequence->chroma_height == sequence->height);
630
631 if (mpeg2dec->sequence.width != (unsigned)-1)
632 {
633 /*
634 * According to 6.1.1.6, repeat sequence headers should be
635 * identical to the original. However some encoders don't
636 * respect that and change various fields (including bitrate
637 * and aspect ratio) in the repeat sequence headers. So we
638 * choose to be as conservative as possible and only restart
639 * the decoder if the width, height, chroma_width,
640 * chroma_height or low_delay flag are modified.
641 */
642 if (sequence->width != mpeg2dec->sequence.width ||
643 sequence->height != mpeg2dec->sequence.height ||
644 sequence->chroma_width != mpeg2dec->sequence.chroma_width ||
645 sequence->chroma_height != mpeg2dec->sequence.chroma_height ||
646 ((sequence->flags ^ mpeg2dec->sequence.flags) &
647 SEQ_FLAG_LOW_DELAY))
648 {
649 decoder->stride_frame = sequence->width;
650 mpeg2_header_end (mpeg2dec);
651 mpeg2dec->action = invalid_end_action;
652 mpeg2dec->state = STATE_INVALID_END;
653 return;
654 }
655
656 mpeg2dec->state = rb->memcmp(&mpeg2dec->sequence, sequence,
657 sizeof (mpeg2_sequence_t)) ?
658 STATE_SEQUENCE_MODIFIED :
659 STATE_SEQUENCE_REPEATED;
660 }
661 else
662 {
663 decoder->stride_frame = sequence->width;
664 }
665
666 mpeg2dec->sequence = *sequence;
667 mpeg2_reset_info(&mpeg2dec->info);
668 mpeg2dec->info.sequence = &mpeg2dec->sequence;
669 mpeg2dec->info.gop = NULL;
670
671 info_user_data (mpeg2dec);
672}
673
674int mpeg2_header_gop (mpeg2dec_t * mpeg2dec)
675{
676 uint8_t * buffer = mpeg2dec->chunk_start;
677 mpeg2_gop_t * gop = &mpeg2dec->new_gop;
678
679 if (!(buffer[1] & 8))
680 return 1;
681
682 gop->hours = (buffer[0] >> 2) & 31;
683 gop->minutes = ((buffer[0] << 4) | (buffer[1] >> 4)) & 63;
684 gop->seconds = ((buffer[1] << 3) | (buffer[2] >> 5)) & 63;
685 gop->pictures = ((buffer[2] << 1) | (buffer[3] >> 7)) & 63;
686 gop->flags = (buffer[0] >> 7) | ((buffer[3] >> 4) & 6);
687
688 mpeg2dec->state = STATE_GOP;
689 return 0;
690}
691
692void mpeg2_header_gop_finalize (mpeg2dec_t * mpeg2dec)
693{
694 mpeg2dec->gop = mpeg2dec->new_gop;
695 mpeg2_reset_info (&mpeg2dec->info);
696 mpeg2dec->info.gop = &mpeg2dec->gop;
697 info_user_data (mpeg2dec);
698}
699
700void mpeg2_set_fbuf (mpeg2dec_t * mpeg2dec, int b_type)
701{
702 int i;
703
704 for (i = 0; i < 3; i++)
705 {
706 if (mpeg2dec->fbuf[1] != &mpeg2dec->fbuf_alloc[i].fbuf &&
707 mpeg2dec->fbuf[2] != &mpeg2dec->fbuf_alloc[i].fbuf)
708 {
709 mpeg2dec->fbuf[0] = &mpeg2dec->fbuf_alloc[i].fbuf;
710 mpeg2dec->info.current_fbuf = mpeg2dec->fbuf[0];
711
712 if (b_type || (mpeg2dec->sequence.flags & SEQ_FLAG_LOW_DELAY))
713 {
714 if (b_type || mpeg2dec->convert)
715 mpeg2dec->info.discard_fbuf = mpeg2dec->fbuf[0];
716
717 mpeg2dec->info.display_fbuf = mpeg2dec->fbuf[0];
718 }
719
720 break;
721 }
722 }
723}
724
725int mpeg2_header_picture (mpeg2dec_t * mpeg2dec)
726{
727 uint8_t * buffer = mpeg2dec->chunk_start;
728 mpeg2_picture_t * picture = &mpeg2dec->new_picture;
729 mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
730 int type;
731
732 mpeg2dec->state = (mpeg2dec->state != STATE_SLICE_1ST) ?
733 STATE_PICTURE : STATE_PICTURE_2ND;
734 mpeg2dec->ext_state = PIC_CODING_EXT;
735
736 picture->temporal_reference = (buffer[0] << 2) | (buffer[1] >> 6);
737
738 type = (buffer [1] >> 3) & 7;
739
740 if (type == PIC_FLAG_CODING_TYPE_P || type == PIC_FLAG_CODING_TYPE_B)
741 {
742 /* forward_f_code and backward_f_code - used in mpeg1 only */
743 decoder->f_motion.f_code[1] = (buffer[3] >> 2) & 1;
744 decoder->f_motion.f_code[0] =
745 (((buffer[3] << 1) | (buffer[4] >> 7)) & 7) - 1;
746 decoder->b_motion.f_code[1] = (buffer[4] >> 6) & 1;
747 decoder->b_motion.f_code[0] = ((buffer[4] >> 3) & 7) - 1;
748 }
749
750 picture->flags = PIC_FLAG_PROGRESSIVE_FRAME | type;
751 picture->tag = picture->tag2 = 0;
752
753 if (mpeg2dec->num_tags)
754 {
755 if (mpeg2dec->bytes_since_tag >= mpeg2dec->chunk_ptr - buffer + 4)
756 {
757 mpeg2dec->num_tags = 0;
758 picture->tag = mpeg2dec->tag_current;
759 picture->tag2 = mpeg2dec->tag2_current;
760 picture->flags |= PIC_FLAG_TAGS;
761 }
762 else if (mpeg2dec->num_tags > 1)
763 {
764 mpeg2dec->num_tags = 1;
765 picture->tag = mpeg2dec->tag_previous;
766 picture->tag2 = mpeg2dec->tag2_previous;
767 picture->flags |= PIC_FLAG_TAGS;
768 }
769 }
770
771 picture->nb_fields = 2;
772 picture->display_offset[0].x = picture->display_offset[1].x =
773 picture->display_offset[2].x = mpeg2dec->display_offset_x;
774
775 picture->display_offset[0].y = picture->display_offset[1].y =
776 picture->display_offset[2].y = mpeg2dec->display_offset_y;
777
778 /* XXXXXX decode extra_information_picture as well */
779
780 mpeg2dec->q_scale_type = 0;
781 decoder->intra_dc_precision = 7;
782 decoder->frame_pred_frame_dct = 1;
783 decoder->concealment_motion_vectors = 0;
784 decoder->scan = mpeg2_scan_norm;
785 decoder->picture_structure = FRAME_PICTURE;
786 mpeg2dec->copy_matrix = 0;
787
788 return 0;
789}
790
791static int picture_coding_ext (mpeg2dec_t * mpeg2dec)
792{
793 uint8_t * buffer = mpeg2dec->chunk_start;
794 mpeg2_picture_t * picture = &mpeg2dec->new_picture;
795 mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
796 uint32_t flags;
797
798 /* pre subtract 1 for use later in compute_motion_vector */
799 decoder->f_motion.f_code[0] = (buffer[0] & 15) - 1;
800 decoder->f_motion.f_code[1] = (buffer[1] >> 4) - 1;
801 decoder->b_motion.f_code[0] = (buffer[1] & 15) - 1;
802 decoder->b_motion.f_code[1] = (buffer[2] >> 4) - 1;
803
804 flags = picture->flags;
805
806 decoder->intra_dc_precision = 7 - ((buffer[2] >> 2) & 3);
807 decoder->picture_structure = buffer[2] & 3;
808
809 switch (decoder->picture_structure)
810 {
811 case TOP_FIELD:
812 flags |= PIC_FLAG_TOP_FIELD_FIRST;
813 case BOTTOM_FIELD:
814 picture->nb_fields = 1;
815 break;
816 case FRAME_PICTURE:
817 if (!(mpeg2dec->sequence.flags & SEQ_FLAG_PROGRESSIVE_SEQUENCE))
818 {
819 picture->nb_fields = (buffer[3] & 2) ? 3 : 2;
820 flags |= (buffer[3] & 128) ? PIC_FLAG_TOP_FIELD_FIRST : 0;
821 }
822 else
823 {
824 picture->nb_fields = (buffer[3]&2) ? ((buffer[3]&128) ? 6 : 4) : 2;
825 }
826 break;
827 default:
828 return 1;
829 }
830
831 decoder->top_field_first = buffer[3] >> 7;
832 decoder->frame_pred_frame_dct = (buffer[3] >> 6) & 1;
833 decoder->concealment_motion_vectors = (buffer[3] >> 5) & 1;
834 mpeg2dec->q_scale_type = buffer[3] & 16;
835 decoder->intra_vlc_format = (buffer[3] >> 3) & 1;
836 decoder->scan = (buffer[3] & 4) ? mpeg2_scan_alt : mpeg2_scan_norm;
837
838 if (!(buffer[4] & 0x80))
839 flags &= ~PIC_FLAG_PROGRESSIVE_FRAME;
840
841 if (buffer[4] & 0x40)
842 {
843 flags |= (((buffer[4]<<26) | (buffer[5]<<18) | (buffer[6]<<10)) &
844 PIC_MASK_COMPOSITE_DISPLAY) | PIC_FLAG_COMPOSITE_DISPLAY;
845 }
846
847 picture->flags = flags;
848
849 mpeg2dec->ext_state = PIC_DISPLAY_EXT | COPYRIGHT_EXT | QUANT_MATRIX_EXT;
850
851 return 0;
852}
853
854static int picture_display_ext (mpeg2dec_t * mpeg2dec)
855{
856 uint8_t * buffer = mpeg2dec->chunk_start;
857 mpeg2_picture_t * picture = &mpeg2dec->new_picture;
858 int i, nb_pos;
859
860 nb_pos = picture->nb_fields;
861
862 if (mpeg2dec->sequence.flags & SEQ_FLAG_PROGRESSIVE_SEQUENCE)
863 nb_pos >>= 1;
864
865 for (i = 0; i < nb_pos; i++)
866 {
867 int x, y;
868
869 x = ((buffer[4*i] << 24) | (buffer[4*i+1] << 16) |
870 (buffer[4*i+2] << 8) | buffer[4*i+3]) >> (11-2*i);
871
872 y = ((buffer[4*i+2] << 24) | (buffer[4*i+3] << 16) |
873 (buffer[4*i+4] << 8) | buffer[4*i+5]) >> (10-2*i);
874
875 if (! (x & y & 1))
876 return 1;
877
878 picture->display_offset[i].x = mpeg2dec->display_offset_x = x >> 1;
879 picture->display_offset[i].y = mpeg2dec->display_offset_y = y >> 1;
880 }
881
882 for (; i < 3; i++)
883 {
884 picture->display_offset[i].x = mpeg2dec->display_offset_x;
885 picture->display_offset[i].y = mpeg2dec->display_offset_y;
886 }
887
888 return 0;
889}
890
891void mpeg2_header_picture_finalize (mpeg2dec_t * mpeg2dec)
892{
893 mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
894 int old_type_b = decoder->coding_type == B_TYPE;
895 int low_delay = mpeg2dec->sequence.flags & SEQ_FLAG_LOW_DELAY;
896
897 finalize_matrix (mpeg2dec);
898 decoder->coding_type = mpeg2dec->new_picture.flags & PIC_MASK_CODING_TYPE;
899
900 if (mpeg2dec->state == STATE_PICTURE)
901 {
902 mpeg2_picture_t * picture;
903 mpeg2_picture_t * other;
904
905 decoder->second_field = 0;
906
907 picture = other = mpeg2dec->pictures;
908
909 if (old_type_b ^ (mpeg2dec->picture < mpeg2dec->pictures + 2))
910 picture += 2;
911 else
912 other += 2;
913
914 mpeg2dec->picture = picture;
915 *picture = mpeg2dec->new_picture;
916
917 if (!old_type_b)
918 {
919 mpeg2dec->fbuf[2] = mpeg2dec->fbuf[1];
920 mpeg2dec->fbuf[1] = mpeg2dec->fbuf[0];
921 }
922
923 mpeg2dec->fbuf[0] = NULL;
924 mpeg2_reset_info (&mpeg2dec->info);
925 mpeg2dec->info.current_picture = picture;
926 mpeg2dec->info.display_picture = picture;
927
928 if (decoder->coding_type != B_TYPE)
929 {
930 if (!low_delay)
931 {
932 if (mpeg2dec->first)
933 {
934 mpeg2dec->info.display_picture = NULL;
935 mpeg2dec->first = 0;
936 }
937 else
938 {
939 mpeg2dec->info.display_picture = other;
940
941 if (other->nb_fields == 1)
942 mpeg2dec->info.display_picture_2nd = other + 1;
943
944 mpeg2dec->info.display_fbuf = mpeg2dec->fbuf[1];
945 }
946 }
947
948 if (!low_delay + !mpeg2dec->convert)
949 {
950 mpeg2dec->info.discard_fbuf =
951 mpeg2dec->fbuf[!low_delay + !mpeg2dec->convert];
952 }
953 }
954
955 if (mpeg2dec->convert)
956 {
957 mpeg2_convert_init_t convert_init;
958
959 if (!mpeg2dec->convert_start)
960 {
961 mpeg2dec->decoder.convert_id =
962 mpeg2_malloc (mpeg2dec->convert_id_size,
963 MPEG2_ALLOC_CONVERT_ID);
964
965 mpeg2dec->convert (MPEG2_CONVERT_START,
966 mpeg2dec->decoder.convert_id,
967 &mpeg2dec->sequence,
968 mpeg2dec->convert_stride,
969 mpeg2dec->convert_arg, &convert_init);
970
971 mpeg2dec->convert_start = convert_init.start;
972 mpeg2dec->decoder.convert = convert_init.copy;
973
974 int y_size = decoder->stride_frame * mpeg2dec->sequence.height;
975
976 mpeg2dec->yuv_buf[0][0] =
977 (uint8_t *) mpeg2_malloc(y_size, MPEG2_ALLOC_YUV);
978#if MPEG2_COLOR
979 int uv_size = y_size >> (2 - mpeg2dec->decoder.chroma_format);
980
981 mpeg2dec->yuv_buf[0][1] =
982 (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
983 mpeg2dec->yuv_buf[0][2] =
984 (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
985#endif
986
987 mpeg2dec->yuv_buf[1][0] =
988 (uint8_t *) mpeg2_malloc(y_size, MPEG2_ALLOC_YUV);
989#if MPEG2_COLOR
990 mpeg2dec->yuv_buf[1][1] =
991 (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
992 mpeg2dec->yuv_buf[1][2] =
993 (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
994#endif
995 y_size = decoder->stride_frame * 32;
996
997 mpeg2dec->yuv_buf[2][0] =
998 (uint8_t *) mpeg2_malloc(y_size, MPEG2_ALLOC_YUV);
999#if MPEG2_COLOR
1000 uv_size = y_size >> (2 - mpeg2dec->decoder.chroma_format);
1001
1002 mpeg2dec->yuv_buf[2][1] =
1003 (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
1004 mpeg2dec->yuv_buf[2][2] =
1005 (uint8_t *) mpeg2_malloc(uv_size, MPEG2_ALLOC_YUV);
1006#endif
1007 }
1008
1009 if (!mpeg2dec->custom_fbuf)
1010 {
1011 while (mpeg2dec->alloc_index < 3)
1012 {
1013 mpeg2_fbuf_t * fbuf;
1014
1015 fbuf = &mpeg2dec->fbuf_alloc[mpeg2dec->alloc_index++].fbuf;
1016 fbuf->id = NULL;
1017
1018 fbuf->buf[0] =
1019 (uint8_t *) mpeg2_malloc (convert_init.buf_size[0],
1020 MPEG2_ALLOC_CONVERTED);
1021#if MPEG2_COLOR
1022 fbuf->buf[1] =
1023 (uint8_t *) mpeg2_malloc (convert_init.buf_size[1],
1024 MPEG2_ALLOC_CONVERTED);
1025 fbuf->buf[2] =
1026 (uint8_t *) mpeg2_malloc (convert_init.buf_size[2],
1027 MPEG2_ALLOC_CONVERTED);
1028#endif
1029 }
1030
1031 mpeg2_set_fbuf (mpeg2dec, (decoder->coding_type == B_TYPE));
1032 }
1033 }
1034 else if (!mpeg2dec->custom_fbuf)
1035 {
1036 while (mpeg2dec->alloc_index < 3)
1037 {
1038 mpeg2_fbuf_t * fbuf;
1039
1040 fbuf = &mpeg2dec->fbuf_alloc[mpeg2dec->alloc_index++].fbuf;
1041
1042 fbuf->id = NULL;
1043
1044 int y_size = decoder->stride_frame * mpeg2dec->sequence.height;
1045
1046 fbuf->buf[0] = (uint8_t *) mpeg2_malloc (y_size,
1047 MPEG2_ALLOC_YUV);
1048#if MPEG2_COLOR
1049 int uv_size = y_size >> (2 - decoder->chroma_format);
1050
1051 fbuf->buf[1] = (uint8_t *) mpeg2_malloc (uv_size,
1052 MPEG2_ALLOC_YUV);
1053 fbuf->buf[2] = (uint8_t *) mpeg2_malloc (uv_size,
1054 MPEG2_ALLOC_YUV);
1055#endif
1056 }
1057
1058 mpeg2_set_fbuf (mpeg2dec, (decoder->coding_type == B_TYPE));
1059 }
1060 }
1061 else
1062 {
1063 decoder->second_field = 1;
1064 mpeg2dec->picture++; /* second field picture */
1065
1066 *(mpeg2dec->picture) = mpeg2dec->new_picture;
1067
1068 mpeg2dec->info.current_picture_2nd = mpeg2dec->picture;
1069
1070 if (low_delay || decoder->coding_type == B_TYPE)
1071 mpeg2dec->info.display_picture_2nd = mpeg2dec->picture;
1072 }
1073
1074 info_user_data (mpeg2dec);
1075}
1076
1077static int copyright_ext (mpeg2dec_t * mpeg2dec)
1078{
1079 (void)mpeg2dec;
1080 return 0;
1081}
1082
1083static int quant_matrix_ext (mpeg2dec_t * mpeg2dec)
1084{
1085 uint8_t * buffer = mpeg2dec->chunk_start;
1086 int i, j;
1087
1088 for (i = 0; i < 4; i++)
1089 {
1090 if (buffer[0] & (8 >> i))
1091 {
1092 for (j = 0; j < 64; j++)
1093 {
1094 mpeg2dec->new_quantizer_matrix[i][mpeg2_scan_norm[j]] =
1095 (buffer[j] << (i+5)) | (buffer[j+1] >> (3-i));
1096 }
1097
1098 mpeg2dec->copy_matrix |= 1 << i;
1099 buffer += 64;
1100 }
1101 }
1102
1103 return 0;
1104}
1105
1106int mpeg2_header_extension (mpeg2dec_t * mpeg2dec)
1107{
1108 static int (* const parser[9]) (mpeg2dec_t *) =
1109 {
1110 NULL,
1111 sequence_ext,
1112 sequence_display_ext,
1113 quant_matrix_ext,
1114 copyright_ext,
1115 NULL,
1116 NULL,
1117 picture_display_ext,
1118 picture_coding_ext
1119 };
1120
1121 int ext, ext_bit;
1122
1123 ext = mpeg2dec->chunk_start[0] >> 4;
1124 ext_bit = 1 << ext;
1125
1126 if (!(mpeg2dec->ext_state & ext_bit))
1127 return 0; /* ignore illegal extensions */
1128
1129 mpeg2dec->ext_state &= ~ext_bit;
1130
1131 return parser[ext] (mpeg2dec);
1132}
1133
1134int mpeg2_header_user_data (mpeg2dec_t * mpeg2dec)
1135{
1136 mpeg2dec->user_data_len += mpeg2dec->chunk_ptr - 1 - mpeg2dec->chunk_start;
1137 mpeg2dec->chunk_start = mpeg2dec->chunk_ptr - 1;
1138
1139 return 0;
1140}
1141
1142static void prescale (mpeg2dec_t * mpeg2dec, int index)
1143{
1144 static const int non_linear_scale[32] =
1145 {
1146 0, 1, 2, 3, 4, 5, 6, 7,
1147 8, 10, 12, 14, 16, 18, 20, 22,
1148 24, 28, 32, 36, 40, 44, 48, 52,
1149 56, 64, 72, 80, 88, 96, 104, 112
1150 };
1151
1152 int i, j, k;
1153 mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
1154
1155 if (mpeg2dec->scaled[index] != mpeg2dec->q_scale_type)
1156 {
1157 mpeg2dec->scaled[index] = mpeg2dec->q_scale_type;
1158
1159 for (i = 0; i < 32; i++)
1160 {
1161 k = mpeg2dec->q_scale_type ? non_linear_scale[i] : (i << 1);
1162
1163 for (j = 0; j < 64; j++)
1164 {
1165 decoder->quantizer_prescale[index][i][j] =
1166 k * mpeg2dec->quantizer_matrix[index][j];
1167 }
1168 }
1169 }
1170}
1171
1172mpeg2_state_t mpeg2_header_slice_start (mpeg2dec_t * mpeg2dec)
1173{
1174 mpeg2_decoder_t * decoder = &mpeg2dec->decoder;
1175
1176 mpeg2dec->info.user_data = NULL;
1177 mpeg2dec->info.user_data_len = 0;
1178 mpeg2dec->state = (mpeg2dec->picture->nb_fields > 1 ||
1179 mpeg2dec->state == STATE_PICTURE_2ND) ?
1180 STATE_SLICE : STATE_SLICE_1ST;
1181
1182 if (mpeg2dec->decoder.coding_type != D_TYPE)
1183 {
1184 prescale (mpeg2dec, 0);
1185
1186 if (decoder->chroma_quantizer[0] == decoder->quantizer_prescale[2])
1187 prescale (mpeg2dec, 2);
1188
1189 if (mpeg2dec->decoder.coding_type != I_TYPE)
1190 {
1191 prescale (mpeg2dec, 1);
1192
1193 if (decoder->chroma_quantizer[1] == decoder->quantizer_prescale[3])
1194 prescale (mpeg2dec, 3);
1195 }
1196 }
1197
1198 if (!(mpeg2dec->nb_decode_slices))
1199 {
1200 mpeg2dec->picture->flags |= PIC_FLAG_SKIP;
1201 }
1202 else if (mpeg2dec->convert_start)
1203 {
1204 mpeg2dec->convert_start (decoder->convert_id, mpeg2dec->fbuf[0],
1205 mpeg2dec->picture, mpeg2dec->info.gop);
1206
1207 if (mpeg2dec->decoder.coding_type == B_TYPE)
1208 {
1209 mpeg2_init_fbuf (&mpeg2dec->decoder, mpeg2dec->yuv_buf[2],
1210 mpeg2dec->yuv_buf[mpeg2dec->yuv_index ^ 1],
1211 mpeg2dec->yuv_buf[mpeg2dec->yuv_index]);
1212 }
1213 else
1214 {
1215 mpeg2_init_fbuf (&mpeg2dec->decoder,
1216 mpeg2dec->yuv_buf[mpeg2dec->yuv_index ^ 1],
1217 mpeg2dec->yuv_buf[mpeg2dec->yuv_index],
1218 mpeg2dec->yuv_buf[mpeg2dec->yuv_index]);
1219
1220 if (mpeg2dec->state == STATE_SLICE)
1221 mpeg2dec->yuv_index ^= 1;
1222 }
1223 }
1224 else
1225 {
1226 int b_type;
1227
1228 b_type = (mpeg2dec->decoder.coding_type == B_TYPE);
1229
1230 mpeg2_init_fbuf (&mpeg2dec->decoder, mpeg2dec->fbuf[0]->buf,
1231 mpeg2dec->fbuf[b_type + 1]->buf,
1232 mpeg2dec->fbuf[b_type]->buf);
1233 }
1234
1235 mpeg2dec->action = NULL;
1236
1237 return STATE_INTERNAL_NORETURN;
1238}
1239
1240static mpeg2_state_t seek_sequence (mpeg2dec_t * mpeg2dec)
1241{
1242 mpeg2_reset_info (&mpeg2dec->info);
1243
1244 mpeg2dec->info.sequence = NULL;
1245 mpeg2dec->info.gop = NULL;
1246
1247 mpeg2_header_state_init (mpeg2dec);
1248
1249 mpeg2dec->action = mpeg2_seek_header;
1250
1251 return mpeg2_seek_header (mpeg2dec);
1252}
1253
1254mpeg2_state_t mpeg2_header_end (mpeg2dec_t * mpeg2dec)
1255{
1256 mpeg2_picture_t * picture;
1257 int b_type;
1258
1259 b_type = (mpeg2dec->decoder.coding_type == B_TYPE);
1260 picture = mpeg2dec->pictures;
1261
1262 if ((mpeg2dec->picture >= picture + 2) ^ b_type)
1263 picture = mpeg2dec->pictures + 2;
1264
1265 mpeg2_reset_info (&mpeg2dec->info);
1266
1267 if (!(mpeg2dec->sequence.flags & SEQ_FLAG_LOW_DELAY))
1268 {
1269 mpeg2dec->info.display_picture = picture;
1270
1271 if (picture->nb_fields == 1)
1272 mpeg2dec->info.display_picture_2nd = picture + 1;
1273
1274 mpeg2dec->info.display_fbuf = mpeg2dec->fbuf[b_type];
1275
1276 if (!mpeg2dec->convert)
1277 mpeg2dec->info.discard_fbuf = mpeg2dec->fbuf[b_type + 1];
1278 }
1279 else if (!mpeg2dec->convert)
1280 {
1281 mpeg2dec->info.discard_fbuf = mpeg2dec->fbuf[b_type];
1282 }
1283
1284 mpeg2dec->action = seek_sequence;
1285
1286 return STATE_END;
1287}
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 @@
1/*
2 * idct.c
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.36
26 */
27
28#include "plugin.h"
29
30#include "mpeg2dec_config.h"
31
32#include "mpeg2.h"
33#include "attributes.h"
34#include "mpeg2_internal.h"
35
36#if defined(CPU_COLDFIRE) || defined (CPU_ARM)
37#define IDCT_ASM
38#endif
39
40#ifndef IDCT_ASM
41
42#define W1 2841 /* 2048 * sqrt (2) * cos (1 * pi / 16) */
43#define W2 2676 /* 2048 * sqrt (2) * cos (2 * pi / 16) */
44#define W3 2408 /* 2048 * sqrt (2) * cos (3 * pi / 16) */
45#define W5 1609 /* 2048 * sqrt (2) * cos (5 * pi / 16) */
46#define W6 1108 /* 2048 * sqrt (2) * cos (6 * pi / 16) */
47#define W7 565 /* 2048 * sqrt (2) * cos (7 * pi / 16) */
48
49/*
50 * In legal streams, the IDCT output should be between -384 and +384.
51 * In corrupted streams, it is possible to force the IDCT output to go
52 * to +-3826 - this is the worst case for a column IDCT where the
53 * column inputs are 16-bit values.
54 */
55#define CLIP(i) \
56 ({ typeof (i) _i = (i); \
57 if ((_i & 0xff) != _i) \
58 _i = ~(_i >> (8*sizeof(_i) - 1)); \
59 _i; })
60
61#if 0
62#define BUTTERFLY(t0,t1,W0,W1,d0,d1) \
63 do { \
64 t0 = W0 * d0 + W1 * d1; \
65 t1 = W0 * d1 - W1 * d0; \
66 } while (0)
67#else
68#define BUTTERFLY(t0,t1,W0,W1,d0,d1) \
69 do { \
70 int tmp = W0 * (d0 + d1); \
71 t0 = tmp + (W1 - W0) * d1; \
72 t1 = tmp - (W1 + W0) * d0; \
73 } while (0)
74#endif
75
76static inline void idct_row (int16_t * const block)
77{
78 int d0, d1, d2, d3;
79 int a0, a1, a2, a3, b0, b1, b2, b3;
80 int t0, t1, t2, t3;
81
82 /* shortcut */
83 if (likely (!(block[1] | ((int32_t *)block)[1] | ((int32_t *)block)[2] |
84 ((int32_t *)block)[3])))
85 {
86 uint32_t tmp = (uint16_t) (block[0] >> 1);
87 tmp |= tmp << 16;
88 ((int32_t *)block)[0] = tmp;
89 ((int32_t *)block)[1] = tmp;
90 ((int32_t *)block)[2] = tmp;
91 ((int32_t *)block)[3] = tmp;
92 return;
93 }
94
95 d0 = (block[0] << 11) + 2048;
96 d1 = block[1];
97 d2 = block[2] << 11;
98 d3 = block[3];
99 t0 = d0 + d2;
100 t1 = d0 - d2;
101 BUTTERFLY (t2, t3, W6, W2, d3, d1);
102 a0 = t0 + t2;
103 a1 = t1 + t3;
104 a2 = t1 - t3;
105 a3 = t0 - t2;
106
107 d0 = block[4];
108 d1 = block[5];
109 d2 = block[6];
110 d3 = block[7];
111 BUTTERFLY (t0, t1, W7, W1, d3, d0);
112 BUTTERFLY (t2, t3, W3, W5, d1, d2);
113 b0 = t0 + t2;
114 b3 = t1 + t3;
115 t0 -= t2;
116 t1 -= t3;
117 b1 = ((t0 + t1) >> 8) * 181;
118 b2 = ((t0 - t1) >> 8) * 181;
119
120 block[0] = (a0 + b0) >> 12;
121 block[1] = (a1 + b1) >> 12;
122 block[2] = (a2 + b2) >> 12;
123 block[3] = (a3 + b3) >> 12;
124 block[4] = (a3 - b3) >> 12;
125 block[5] = (a2 - b2) >> 12;
126 block[6] = (a1 - b1) >> 12;
127 block[7] = (a0 - b0) >> 12;
128}
129
130static inline void idct_col (int16_t * const block)
131{
132 int d0, d1, d2, d3;
133 int a0, a1, a2, a3, b0, b1, b2, b3;
134 int t0, t1, t2, t3;
135
136 d0 = (block[8*0] << 11) + 65536;
137 d1 = block[8*1];
138 d2 = block[8*2] << 11;
139 d3 = block[8*3];
140 t0 = d0 + d2;
141 t1 = d0 - d2;
142 BUTTERFLY (t2, t3, W6, W2, d3, d1);
143 a0 = t0 + t2;
144 a1 = t1 + t3;
145 a2 = t1 - t3;
146 a3 = t0 - t2;
147
148 d0 = block[8*4];
149 d1 = block[8*5];
150 d2 = block[8*6];
151 d3 = block[8*7];
152 BUTTERFLY (t0, t1, W7, W1, d3, d0);
153 BUTTERFLY (t2, t3, W3, W5, d1, d2);
154 b0 = t0 + t2;
155 b3 = t1 + t3;
156 t0 -= t2;
157 t1 -= t3;
158 b1 = ((t0 + t1) >> 8) * 181;
159 b2 = ((t0 - t1) >> 8) * 181;
160
161 block[8*0] = (a0 + b0) >> 17;
162 block[8*1] = (a1 + b1) >> 17;
163 block[8*2] = (a2 + b2) >> 17;
164 block[8*3] = (a3 + b3) >> 17;
165 block[8*4] = (a3 - b3) >> 17;
166 block[8*5] = (a2 - b2) >> 17;
167 block[8*6] = (a1 - b1) >> 17;
168 block[8*7] = (a0 - b0) >> 17;
169}
170
171void mpeg2_idct_copy (int16_t * block, uint8_t * dest,
172 const int stride)
173{
174 int i;
175
176 for (i = 0; i < 8; i++)
177 idct_row (block + 8 * i);
178
179 for (i = 0; i < 8; i++)
180 idct_col (block + i);
181
182 do
183 {
184 dest[0] = CLIP (block[0]);
185 dest[1] = CLIP (block[1]);
186 dest[2] = CLIP (block[2]);
187 dest[3] = CLIP (block[3]);
188 dest[4] = CLIP (block[4]);
189 dest[5] = CLIP (block[5]);
190 dest[6] = CLIP (block[6]);
191 dest[7] = CLIP (block[7]);
192
193 ((int32_t *)block)[0] = 0;
194 ((int32_t *)block)[1] = 0;
195 ((int32_t *)block)[2] = 0;
196 ((int32_t *)block)[3] = 0;
197
198 dest += stride;
199 block += 8;
200 }
201 while (--i);
202}
203
204void mpeg2_idct_add (const int last, int16_t * block,
205 uint8_t * dest, const int stride)
206{
207 int i;
208
209 if (last != 129 || (block[0] & (7 << 4)) == (4 << 4))
210 {
211 for (i = 0; i < 8; i++)
212 idct_row (block + 8 * i);
213
214 for (i = 0; i < 8; i++)
215 idct_col (block + i);
216
217 do
218 {
219 dest[0] = CLIP (block[0] + dest[0]);
220 dest[1] = CLIP (block[1] + dest[1]);
221 dest[2] = CLIP (block[2] + dest[2]);
222 dest[3] = CLIP (block[3] + dest[3]);
223 dest[4] = CLIP (block[4] + dest[4]);
224 dest[5] = CLIP (block[5] + dest[5]);
225 dest[6] = CLIP (block[6] + dest[6]);
226 dest[7] = CLIP (block[7] + dest[7]);
227
228 ((int32_t *)block)[0] = 0;
229 ((int32_t *)block)[1] = 0;
230 ((int32_t *)block)[2] = 0;
231 ((int32_t *)block)[3] = 0;
232
233 dest += stride;
234 block += 8;
235 }
236 while (--i);
237 }
238 else
239 {
240 int DC = (block[0] + 64) >> 7;
241 block[0] = block[63] = 0;
242 i = 8;
243
244 do
245 {
246 dest[0] = CLIP (DC + dest[0]);
247 dest[1] = CLIP (DC + dest[1]);
248 dest[2] = CLIP (DC + dest[2]);
249 dest[3] = CLIP (DC + dest[3]);
250 dest[4] = CLIP (DC + dest[4]);
251 dest[5] = CLIP (DC + dest[5]);
252 dest[6] = CLIP (DC + dest[6]);
253 dest[7] = CLIP (DC + dest[7]);
254 dest += stride;
255 }
256 while (--i);
257 }
258}
259
260#endif /* IDCT_ASM */
261
262void mpeg2_idct_init (void)
263{
264 int i, j;
265
266 for (i = 0; i < 64; i++)
267 {
268 j = default_mpeg2_scan_norm[i];
269 mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2);
270
271 j = default_mpeg2_scan_alt[i];
272 mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2);
273 }
274}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "config.h"
23
24 .global mpeg2_idct_copy
25 .type mpeg2_idct_copy, %function
26 .global mpeg2_idct_add
27 .type mpeg2_idct_add, %function
28
29
30/* Custom calling convention:
31 * r0 contains block pointer and is non-volatile
32 * all non-volatile c context saved and restored on its behalf
33 */
34.idct:
35 add r12, r0, #128
361:
37 ldrsh r1, [r0, #0] /* d0 */
38 ldrsh r2, [r0, #2] /* d1 */
39 ldrsh r3, [r0, #4] /* d2 */
40 ldrsh r4, [r0, #6] /* d3 */
41 ldrsh r5, [r0, #8] /* d0 */
42 ldrsh r6, [r0, #10] /* d1 */
43 ldrsh r7, [r0, #12] /* d2 */
44 ldrsh r8, [r0, #14] /* d3 */
45 orrs r9, r2, r3
46 orreqs r9, r4, r5
47 orreqs r9, r6, r7
48 cmpeq r8, #0
49 bne 2f
50 mov r1, r1, asl #15
51 bic r1, r1, #0x8000
52 orr r1, r1, r1, lsr #16
53 str r1, [r0], #4
54 str r1, [r0], #4
55 str r1, [r0], #4
56 str r1, [r0], #4
57 cmp r0, r12
58 blo 1b
59 b 3f
602:
61 mov r1, r1, asl #11 /* r1 = d0 = (block[0] << 11) + 2048 */
62 add r1, r1, #2048
63 add r1, r1, r3, asl #11 /* r1 = t0 = d0 + (block[2] << 11) */
64 sub r3, r1, r3, asl #12 /* r3 = t1 = d0 - (block[2] << 11) */
65
66 add r9, r2, r4 /* r9 = tmp = (d1+d3)*(1108/4) */
67 add r10, r9, r9, asl #2
68 add r10, r10, r9, asl #4
69 add r9, r10, r9, asl #8
70
71 add r10, r2, r2, asl #4 /* r2 = t2 = tmp + (d1*(1568/32)*8) */
72 add r2, r10, r2, asl #5
73 add r2, r9, r2, asl #3
74
75 add r10, r4, r4, asl #2 /* r4 = t3 = tmp - (d3*(3784/8)*2) */
76 rsb r10, r10, r4, asl #6
77 add r4, r4, r10, asl #3
78 sub r4, r9, r4, asl #1
79 /* t2 & t3 are 1/4 final value here */
80 add r1, r1, r2, asl #2 /* r1 = a0 = t0 + t2 */
81 sub r2, r1, r2, asl #3 /* r2 = a3 = t0 - t2 */
82 add r3, r3, r4, asl #2 /* r3 = a1 = t1 + t3 */
83 sub r4, r3, r4, asl #3 /* r4 = a2 = t1 - t3 */
84
85 add r9, r8, r5 /* r9 = tmp = 565*(d3 + d0) */
86 add r10, r9, r9, asl #4
87 add r10, r10, r10, asl #5
88 add r9, r10, r9, asl #2
89
90 add r10, r5, r5, asl #4 /* r5 = t0 = tmp + (((2276/4)*d0)*4) */
91 add r10, r10, r10, asl #5
92 add r5, r10, r5, asl #3
93 add r5, r9, r5, asl #2
94
95 add r10, r8, r8, asl #2 /* r8 = t1 = tmp - (((3406/2)*d3)*2) */
96 add r10, r10, r10, asl #4
97 add r10, r10, r8, asl #7
98 rsb r8, r8, r10, asl #3
99 sub r8, r9, r8, asl #1
100
101 add r9, r6, r7 /* r9 = tmp = (2408/8)*(d1 + d2) */
102 add r10, r9, r9, asl #3
103 add r10, r10, r10, asl #5
104 add r9, r10, r9, asl #2
105
106 add r10, r7, r7, asl #3 /* r7 = t2 = (tmp*8) - 799*d2 */
107 add r10, r10, r7, asl #4
108 rsb r7, r7, r10, asl #5
109 rsb r7, r7, r9, asl #3
110
111 sub r10, r6, r6, asl #4 /* r6 = t3 = (tmp*8) - 4017*d1 */
112 sub r10, r10, r6, asl #6
113 add r10, r10, r6, asl #12
114 add r6, r10, r6
115 rsb r6, r6, r9, asl #3
116 /* t0 = r5, t1 = r8, t2 = r7, t3 = r6*/
117 add r9, r5, r7 /* r9 = b0 = t0 + t2 */
118 add r10, r8, r6 /* r10 = b3 = t1 + t3 */
119 sub r5, r5, r7 /* t0 -= t2 */
120 sub r8, r8, r6 /* t1 -= t3 */
121 add r6, r5, r8 /* r6 = t0 + t1 */
122 sub r7, r5, r8 /* r7 = t0 - t1 */
123
124 add r11, r6, r6, asr #2 /* r6 = b1 = r6*(181/128) */
125 add r11, r11, r11, asr #5
126 add r6, r11, r6, asr #3
127 add r11, r7, r7, asr #2 /* r7 = b2 = r7*(181/128) */
128 add r11, r11, r11, asr #5
129 add r7, r11, r7, asr #3
130 /* r1 = a0, r3 = a1, r4 = a2, r2 = a3 */
131 /* r9 = b0, r6 = b1*2, r7 = b2*2, r10 = b3 */
132 add r5, r1, r9 /* block[0] = (a0 + b0) >> 12 */
133 mov r5, r5, asr #12
134 strh r5, [r0], #2
135 add r8, r3, r6, asr #1 /* block[1] = (a1 + b1) >> 12 */
136 mov r8, r8, asr #12
137 strh r8, [r0], #2
138 add r5, r4, r7, asr #1 /* block[2] = (a2 + b2) >> 12 */
139 mov r5, r5, asr #12
140 strh r5, [r0], #2
141 add r8, r2, r10 /* block[3] = (a3 + b3) >> 12 */
142 mov r8, r8, asr #12
143 strh r8, [r0], #2
144 sub r5, r2, r10 /* block[4] = (a3 - b3) >> 12 */
145 mov r5, r5, asr #12
146 strh r5, [r0], #2
147 sub r8, r4, r7, asr #1 /* block[5] = (a2 - b2) >> 12 */
148 mov r8, r8, asr #12
149 strh r8, [r0], #2
150 sub r5, r3, r6, asr #1 /* block[6] = (a1 - b1) >> 12 */
151 mov r5, r5, asr #12
152 strh r5, [r0], #2
153 sub r8, r1, r9 /* block[7] = (a0 - b0) >> 12 */
154 mov r8, r8, asr #12
155 strh r8, [r0], #2
156 cmp r0, r12
157 blo 1b
1583:
159 sub r0, r0, #128
160 add r12, r0, #16
1614:
162 ldrsh r1, [r0, #0*8] /* d0 */
163 ldrsh r2, [r0, #2*8] /* d1 */
164 ldrsh r3, [r0, #4*8] /* d2 */
165 ldrsh r4, [r0, #6*8] /* d3 */
166 ldrsh r5, [r0, #8*8] /* d0 */
167 ldrsh r6, [r0, #10*8] /* d1 */
168 ldrsh r7, [r0, #12*8] /* d2 */
169 ldrsh r8, [r0, #14*8] /* d3 */
170
171 mov r1, r1, asl #11 /* r1 = d0 = (block[0] << 11) + 2048 */
172 add r1, r1, #65536
173 add r1, r1, r3, asl #11 /* r1 = t0 = d0 + d2:(block[2] << 11) */
174 sub r3, r1, r3, asl #12 /* r3 = t1 = d0 - d2:(block[2] << 11) */
175
176 add r9, r2, r4 /* r9 = tmp = (d1+d3)*(1108/4) */
177 add r10, r9, r9, asl #2
178 add r10, r10, r9, asl #4
179 add r9, r10, r9, asl #8
180
181 add r11, r2, r2, asl #4 /* r2 = t2 = tmp + (d1*(1568/32)*8) */
182 add r2, r11, r2, asl #5
183 add r2, r9, r2, asl #3
184
185 add r10, r4, r4, asl #2 /* r4 = t3 = tmp - (d3*(3784/8)*2) */
186 rsb r10, r10, r4, asl #6
187 add r4, r4, r10, asl #3
188 sub r4, r9, r4, asl #1
189 /* t2 & t3 are 1/4 final value here */
190 add r1, r1, r2, asl #2 /* r1 = a0 = t0 + t2 */
191 sub r2, r1, r2, asl #3 /* r2 = a3 = t0 - t2 */
192 add r3, r3, r4, asl #2 /* r3 = a1 = t1 + t3 */
193 sub r4, r3, r4, asl #3 /* r4 = a2 = t1 - t3 */
194
195 add r9, r8, r5 /* r9 = tmp = 565*(d3 + d0) */
196 add r10, r9, r9, asl #4
197 add r10, r10, r10, asl #5
198 add r9, r10, r9, asl #2
199
200 add r10, r5, r5, asl #4 /* r5 = t0 = tmp + (((2276/4)*d0)*4) */
201 add r10, r10, r10, asl #5
202 add r5, r10, r5, asl #3
203 add r5, r9, r5, asl #2
204
205 add r10, r8, r8, asl #2 /* r8 = t1 = tmp - (((3406/2)*d3)*2) */
206 add r10, r10, r10, asl #4
207 add r10, r10, r8, asl #7
208 rsb r8, r8, r10, asl #3
209 sub r8, r9, r8, asl #1
210
211 add r9, r6, r7 /* r9 = tmp = (2408/8)*(d1 + d2) */
212 add r10, r9, r9, asl #3
213 add r10, r10, r10, asl #5
214 add r9, r10, r9, asl #2
215
216 add r10, r7, r7, asl #3 /* r7 = t2 = (tmp*8) - 799*d2 */
217 add r10, r10, r7, asl #4
218 rsb r7, r7, r10, asl #5
219 rsb r7, r7, r9, asl #3
220
221 sub r10, r6, r6, asl #4 /* r6 = t3 = (tmp*8) - 4017*d1 */
222 sub r10, r10, r6, asl #6
223 add r10, r10, r6, asl #12
224 add r6, r10, r6
225 rsb r6, r6, r9, asl #3
226 /* t0=r5, t1=r8, t2=r7, t3=r6*/
227 add r9, r5, r7 /* r9 = b0 = t0 + t2 */
228 add r10, r8, r6 /* r10 = b3 = t1 + t3 */
229 sub r5, r5, r7 /* t0 -= t2 */
230 sub r8, r8, r6 /* t1 -= t3 */
231 add r6, r5, r8 /* r6 = t0 + t1 */
232 sub r7, r5, r8 /* r7 = t0 - t1 */
233
234 add r11, r6, r6, asr #2 /* r6 = b1 = r5*(181/128) */
235 add r11, r11, r11, asr #5
236 add r6, r11, r6, asr #3
237 add r11, r7, r7, asr #2 /* r7 = b2 = r6*(181/128) */
238 add r11, r11, r11, asr #5
239 add r7, r11, r7, asr #3
240 /* r1 = a0, r3 = a1, r4 = a2, r2 = a3 */
241 /* r9 = b0, r6 = b1*2, r7 = b2*2, r10 = b3 */
242 add r5, r1, r9 /* block[0] = (a0 + b0) >> 17 */
243 mov r5, r5, asr #17
244 strh r5, [r0, #0*8]
245 add r8, r3, r6, asr #1 /* block[1] = (a1 + b1) >> 17 */
246 mov r8, r8, asr #17
247 strh r8, [r0, #2*8]
248 add r5, r4, r7, asr #1 /* block[2] = (a2 + b2) >> 17 */
249 mov r5, r5, asr #17
250 strh r5, [r0, #4*8]
251 add r8, r2, r10 /* block[3] = (a3 + b3) >> 17 */
252 mov r8, r8, asr #17
253 strh r8, [r0, #6*8]
254 sub r5, r2, r10 /* block[4] = (a3 - b3) >> 17 */
255 mov r5, r5, asr #17
256 strh r5, [r0, #8*8]
257 sub r8, r4, r7, asr #1 /* block[5] = (a2 - b2) >> 17 */
258 mov r8, r8, asr #17
259 strh r8, [r0, #10*8]
260 sub r5, r3, r6, asr #1 /* block[6] = (a1 - b1) >> 17 */
261 mov r5, r5, asr #17
262 strh r5, [r0, #12*8]
263 sub r8, r1, r9 /* block[7] = (a0 - b0) >> 17 */
264 mov r8, r8, asr #17
265 strh r8, [r0, #14*8]
266 add r0, r0, #2
267 cmp r0, r12
268 blo 4b
269 sub r0, r0, #16
270 bx lr
271
272mpeg2_idct_copy:
273 stmfd sp!, { r1-r2, r4-r11, lr }
274 bl .idct
275 ldmfd sp!, { r1-r2 }
276 mov r11, #0
277 add r12, r0, #128
2781:
279 ldrsh r3, [r0, #0]
280 ldrsh r4, [r0, #2]
281 ldrsh r5, [r0, #4]
282 ldrsh r6, [r0, #6]
283 ldrsh r7, [r0, #8]
284 ldrsh r8, [r0, #10]
285 ldrsh r9, [r0, #12]
286 ldrsh r10, [r0, #14]
287 cmp r3, #255
288 mvnhi r3, r3, asr #31
289 strb r3, [r1, #0]
290 str r11, [r0], #4
291 cmp r4, #255
292 mvnhi r4, r4, asr #31
293 strb r4, [r1, #1]
294 cmp r5, #255
295 mvnhi r5, r5, asr #31
296 strb r5, [r1, #2]
297 str r11, [r0], #4
298 cmp r6, #255
299 mvnhi r6, r6, asr #31
300 strb r6, [r1, #3]
301 cmp r7, #255
302 mvnhi r7, r7, asr #31
303 strb r7, [r1, #4]
304 str r11, [r0], #4
305 cmp r8, #255
306 mvnhi r8, r8, asr #31
307 strb r8, [r1, #5]
308 cmp r9, #255
309 mvnhi r9, r9, asr #31
310 strb r9, [r1, #6]
311 str r11, [r0], #4
312 cmp r10, #255
313 mvnhi r10, r10, asr #31
314 strb r10, [r1, #7]
315 add r1, r1, r2
316 cmp r0, r12
317 blo 1b
318 ldmpc regs=r4-r11
319
320mpeg2_idct_add:
321 cmp r0, #129
322 mov r0, r1
323 ldreqsh r1, [r0, #0]
324 bne 1f
325 and r1, r1, #0x70
326 cmp r1, #0x40
327 bne 3f
3281:
329 stmfd sp!, { r2-r11, lr }
330 bl .idct
331 ldmfd sp!, { r1-r2 }
332 mov r11, #0
333 add r12, r0, #128
3342:
335 ldrb r3, [r1, #0]
336 ldrb r4, [r1, #1]
337 ldrb r5, [r1, #2]
338 ldrb r6, [r1, #3]
339 ldrsh r7, [r0, #0]
340 ldrsh r8, [r0, #2]
341 ldrsh r9, [r0, #4]
342 ldrsh r10, [r0, #6]
343 add r7, r7, r3
344 ldrb r3, [r1, #4]
345 cmp r7, #255
346 mvnhi r7, r7, asr #31
347 strb r7, [r1, #0]
348 ldrsh r7, [r0, #8]
349 add r8, r8, r4
350 ldrb r4, [r1, #5]
351 cmp r8, #255
352 mvnhi r8, r8, asr #31
353 strb r8, [r1, #1]
354 ldrsh r8, [r0, #10]
355 add r9, r9, r5
356 ldrb r5, [r1, #6]
357 cmp r9, #255
358 mvnhi r9, r9, asr #31
359 strb r9, [r1, #2]
360 ldrsh r9, [r0, #12]
361 add r10, r10, r6
362 ldrb r6, [r1, #7]
363 cmp r10, #255
364 mvnhi r10, r10, asr #31
365 strb r10, [r1, #3]
366 ldrsh r10, [r0, #14]
367 str r11, [r0], #4
368 add r7, r7, r3
369 cmp r7, #255
370 mvnhi r7, r7, asr #31
371 strb r7, [r1, #4]
372 str r11, [r0], #4
373 add r8, r8, r4
374 cmp r8, #255
375 mvnhi r8, r8, asr #31
376 strb r8, [r1, #5]
377 str r11, [r0], #4
378 add r9, r9, r5
379 cmp r9, #255
380 mvnhi r9, r9, asr #31
381 strb r9, [r1, #6]
382 add r10, r10, r6
383 cmp r10, #255
384 mvnhi r10, r10, asr #31
385 strb r10, [r1, #7]
386 str r11, [r0], #4
387 add r1, r1, r2
388 cmp r0, r12
389 blo 2b
390 ldmpc regs=r4-r11
3913:
392 stmfd sp!, { r4-r5, lr }
393 ldrsh r1, [r0, #0] /* r1 = block[0] */
394 mov r4, #0
395 strh r4, [r0, #0] /* block[0] = 0 */
396 strh r4, [r0, #126] /* block[63] = 0 */
397 add r1, r1, #64 /* r1 = DC << 7 */
398 add r0, r2, r3, asl #3
3994:
400 ldrb r4, [r2, #0]
401 ldrb r5, [r2, #1]
402 ldrb r12, [r2, #2]
403 ldrb lr, [r2, #3]
404 add r4, r4, r1, asr #7
405 cmp r4, #255
406 mvnhi r4, r4, asr #31
407 strb r4, [r2, #0]
408 add r5, r5, r1, asr #7
409 cmp r5, #255
410 mvnhi r5, r5, asr #31
411 strb r5, [r2, #1]
412 add r12, r12, r1, asr #7
413 cmp r12, #255
414 mvnhi r12, r12, asr #31
415 strb r12, [r2, #2]
416 add lr, lr, r1, asr #7
417 cmp lr, #255
418 mvnhi lr, lr, asr #31
419 strb lr, [r2, #3]
420 ldrb r4, [r2, #4]
421 ldrb r5, [r2, #5]
422 ldrb r12, [r2, #6]
423 ldrb lr, [r2, #7]
424 add r4, r4, r1, asr #7
425 cmp r4, #255
426 mvnhi r4, r4, asr #31
427 strb r4, [r2, #4]
428 add r5, r5, r1, asr #7
429 cmp r5, #255
430 mvnhi r5, r5, asr #31
431 strb r5, [r2, #5]
432 add r12, r12, r1, asr #7
433 cmp r12, #255
434 mvnhi r12, r12, asr #31
435 strb r12, [r2, #6]
436 add lr, lr, r1, asr #7
437 cmp lr, #255
438 mvnhi lr, lr, asr #31
439 strb lr, [r2, #7]
440 add r2, r2, r3
441 cmp r2, r0
442 blo 4b
443 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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009 by Jens Arnold
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22
23 .global mpeg2_idct_copy
24 .type mpeg2_idct_copy, %function
25 .global mpeg2_idct_add
26 .type mpeg2_idct_add, %function
27
28/* Custom calling convention:
29 * r0 contains block pointer and is non-volatile
30 * all non-volatile c context saved and restored on its behalf
31 */
32.idct:
33 str lr, [sp, #-4]! @ lr is used
34 add r1, r0, #128 @ secondary, transposed temp buffer
35 mov r14, #8 @ loop counter
36
37.row_loop:
38 ldmia r0!, {r2, r3, r10, r11} @ fetch f0, f2, f4, f6, f1, f3, f5, f7
39 ldrd r4, L_W1357 @ load W1, W3, W5, W7
40
41 smuad r6, r4, r10 @ b0 = W1 * f1 + W3 * f3
42 smultt r7, r5, r10 @ -b1 = W7 * f3
43 smulbt r8, r4, r10 @ -b2 = W1 * f3
44
45 smusdx r9, r10, r5 @ b3 = f1 * W7 - f3 * W5
46 smlabb r7, r4, r11, r7 @ -b1 += W1 * f5
47 rsb r8, r8, #0 @ b2 = -b2
48 smlabb r8, r5, r10, r8 @ b2 += W5 * f1
49
50 smlad r6, r5, r11, r6 @ b0 += W5 * f5 + W7 * f7
51 smlabt r7, r5, r11, r7 @ -b1 += W5 * f7
52 smlatb r8, r5, r11, r8 @ b2 += W7 * f5
53
54 smlsdx r9, r11, r4, r9 @ b3 += f5 * W3 - f7 * W1
55 rsb r7, r7, #0 @ b1 = -b1
56 smlatb r7, r4, r10, r7 @ b1 += W3 * f1
57 smlatt r8, r4, r11, r8 @ b2 += W3 * f7
58
59 ldrd r4, L_W0246 @ load W0, W2, W4, W6
60 add r2, r2, #1 @ f0 += 1
61
62 smulbb r10, r5, r3 @ a0' = W4 * f4
63 smultt r12, r5, r3 @ a3' = W6 * f6
64 smultt r3, r4, r3 @ -a2' = W2 * f6
65
66 rsb r11, r10, #0 @ a1' = -W4 * f4
67 smlabb r10, r4, r2, r10 @ a0' += W0 * f0
68 smlabb r11, r4, r2, r11 @ a1' += W0 * f0
69 smlatt r12, r4, r2, r12 @ a3' += W2 * f2
70 rsb r3, r3, #0 @ a2' = -a2'
71 smlatt r3, r5, r2, r3 @ a2' += W6 * f2
72
73 add r10, r10, r12 @ a0 = a0' + a3'
74 sub r12, r10, r12, lsl #1 @ a3 = a0 - 2 * a3'
75 add r11, r11, r3 @ a1 = a1' + a2'
76 sub r3, r11, r3, lsl #1 @ a2 = a1 - 2 * a2'
77
78 subs r14, r14, #1 @ decrease loop count
79
80 @ Special store order for making the column pass calculate columns in
81 @ the order 0-2-1-3-4-6-5-7, allowing for uxtab16 use in later stages.
82 sub r2, r10, r6 @ block[7] = (a0 - b0)
83 mov r2, r2, asr #12 @ >> 12
84 strh r2, [r1, #7*16]
85 sub r2, r11, r7 @ block[6] = (a1 - b1)
86 mov r2, r2, asr #12 @ >> 12
87 strh r2, [r1, #5*16]
88 sub r2, r3, r8 @ block[5] = (a2 - b2)
89 mov r2, r2, asr #12 @ >> 12
90 strh r2, [r1, #6*16]
91 sub r2, r12, r9 @ block[4] = (a3 - b3)
92 mov r2, r2, asr #12 @ >> 12
93 strh r2, [r1, #4*16]
94 add r2, r12, r9 @ block[3] = (a3 + b3)
95 mov r2, r2, asr #12 @ >> 12
96 strh r2, [r1, #3*16]
97 add r2, r3, r8 @ block[2] = (a2 + b2)
98 mov r2, r2, asr #12 @ >> 12
99 strh r2, [r1, #1*16]
100 add r2, r11, r7 @ block[1] = (a1 + b1)
101 mov r2, r2, asr #12 @ >> 12
102 strh r2, [r1, #2*16]
103 add r2, r10, r6 @ block[0] = (a0 + b0)
104 mov r2, r2, asr #12 @ >> 12
105 strh r2, [r1], #2 @ advance to next temp column
106
107 bne .row_loop
108 b .col_start
109
110 @placed here because of ldrd's offset limit
111L_W1357:
112 .short 2841
113 .short 2408
114 .short 1609
115 .short 565
116
117L_W0246:
118 .short 2048
119 .short 2676
120 .short 2048
121 .short 1108
122
123.col_start:
124 @ r0 now points to the temp buffer, where we need it.
125 sub r1, r1, #128+16 @ point r1 back to the input block
126 mov r14, #8 @ loop counter
127
128.col_loop:
129 ldmia r0!, {r2, r3, r10, r11} @ fetch f0, f2, f4, f6, f1, f3, f5, f7
130 ldrd r4, L_W1357 @ load W1, W3, W5, W7
131
132 smuad r6, r4, r10 @ b0 = W1 * f1 + W3 * f3
133 smultt r7, r5, r10 @ -b1 = W7 * f3
134 smulbt r8, r4, r10 @ -b2 = W1 * f3
135
136 smusdx r9, r10, r5 @ b3 = f1 * W7 - f3 * W5
137 smlabb r7, r4, r11, r7 @ -b1 += W1 * f5
138 rsb r8, r8, #0 @ b2 = -b2
139 smlabb r8, r5, r10, r8 @ b2 += W5 * f1
140
141 smlad r6, r5, r11, r6 @ b0 += W5 * f5 + W7 * f7
142 smlabt r7, r5, r11, r7 @ -b1 += W5 * f7
143 smlatb r8, r5, r11, r8 @ b2 += W7 * f5
144
145 smlsdx r9, r11, r4, r9 @ b3 += f5 * W3 - f7 * W1
146 rsb r7, r7, #0 @ b1 = -b1
147 smlatb r7, r4, r10, r7 @ b1 += W3 * f1
148 smlatt r8, r4, r11, r8 @ b2 += W3 * f7
149
150 ldrd r4, L_W0246 @ load W0, W2, W4, W6
151 add r2, r2, #32 @ DC offset: 0.5
152
153 smulbb r10, r5, r3 @ a0' = W4 * f4
154 smultt r12, r5, r3 @ a3' = W6 * f6
155 smultt r3, r4, r3 @ -a2' = W2 * f6
156
157 rsb r11, r10, #0 @ a1' = -W4 * f4
158 smlabb r10, r4, r2, r10 @ a0' += W0 * f0
159 smlabb r11, r4, r2, r11 @ a1' += W0 * f0
160 smlatt r12, r4, r2, r12 @ a3' += W2 * f2
161 rsb r3, r3, #0 @ a2' = -a2'
162 smlatt r3, r5, r2, r3 @ a2' += W6 * f2
163
164 add r10, r10, r12 @ a0 = a0' + a3'
165 sub r12, r10, r12, lsl #1 @ a3 = a0 - 2 * a3'
166 add r11, r11, r3 @ a1 = a1' + a2'
167 sub r3, r11, r3, lsl #1 @ a2 = a1 - 2 * a2'
168
169 subs r14, r14, #1 @ decrease loop count
170
171 sub r2, r10, r6 @ block[7] = (a0 - b0)
172 mov r2, r2, asr #17 @ >> 17
173 strh r2, [r1, #7*16]
174 sub r2, r11, r7 @ block[6] = (a1 - b1)
175 mov r2, r2, asr #17 @ >> 17
176 strh r2, [r1, #6*16]
177 sub r2, r3, r8 @ block[5] = (a2 - b2)
178 mov r2, r2, asr #17 @ >> 17
179 strh r2, [r1, #5*16]
180 sub r2, r12, r9 @ block[4] = (a3 - b3)
181 mov r2, r2, asr #17 @ >> 17
182 strh r2, [r1, #4*16]
183 add r2, r12, r9 @ block[3] = (a3 + b3)
184 mov r2, r2, asr #17 @ >> 17
185 strh r2, [r1, #3*16]
186 add r2, r3, r8 @ block[2] = (a2 + b2)
187 mov r2, r2, asr #17 @ >> 17
188 strh r2, [r1, #2*16]
189 add r2, r11, r7 @ block[1] = (a1 + b1)
190 mov r2, r2, asr #17 @ >> 17
191 strh r2, [r1, #1*16]
192 add r2, r10, r6 @ block[0] = (a0 + b0)
193 mov r2, r2, asr #17 @ >> 17
194 strh r2, [r1], #2 @ advance to next column
195
196 bne .col_loop
197
198 sub r0, r0, #256 @ point r0 back to the input block
199 ldr pc, [sp], #4
200
201
202mpeg2_idct_copy:
203 stmfd sp!, {r1-r2, r4-r11, lr}
204 bl .idct
205 ldmfd sp!, {r1-r2}
206
207 add r3, r0, #128
208 mov r8, #0
209 mov r9, #0
210 mov r10, #0
211 mov r11, #0
2121: @ idct data is in order 0-2-1-3-4-6-5-7,
213 ldmia r0, {r4-r7} @ see above
214 stmia r0!, {r8-r11}
215 usat16 r4, #8, r4
216 usat16 r5, #8, r5
217 orr r4, r4, r5, lsl #8
218 usat16 r6, #8, r6
219 usat16 r7, #8, r7
220 orr r5, r6, r7, lsl #8
221 strd r4, [r1] @ r4, r5
222 add r1, r1, r2
223 cmp r0, r3
224 blo 1b
225
226 ldmfd sp!, {r4-r11, pc}
227
228mpeg2_idct_add:
229 cmp r0, #129
230 mov r0, r1
231 ldreqsh r1, [r0, #0]
232 bne 1f
233 and r1, r1, #0x70
234 cmp r1, #0x40
235 bne 3f
2361:
237 stmfd sp!, {r2-r11, lr}
238 bl .idct
239 ldmfd sp!, {r1-r2}
240
241 add r3, r0, #128
242 mov r10, #0
243 mov r11, #0
244 mov r12, #0
245 mov lr, #0
246 ldrd r8, [r1] @ r8, r9
2472: @ idct data is in order 0-2-1-3-4-6-5-7,
248 ldmia r0, {r4-r7} @ see above
249 stmia r0!, {r10-r12, lr}
250 uxtab16 r4, r4, r8
251 uxtab16 r5, r5, r8, ror #8
252 usat16 r4, #8, r4
253 usat16 r5, #8, r5
254 orr r4, r4, r5, lsl #8
255 uxtab16 r6, r6, r9
256 uxtab16 r7, r7, r9, ror #8
257 usat16 r6, #8, r6
258 usat16 r7, #8, r7
259 orr r5, r6, r7, lsl #8
260 strd r4, [r1] @ r4, r5
261 add r1, r1, r2
262 cmp r0, r3
263 ldrlod r8, [r1] @ r8, r9
264 blo 2b
265
266 ldmfd sp!, {r4-r11, pc}
267
2683:
269 stmfd sp!, {r4, lr}
270 ldrsh r4, [r0, #0] @ r4 = block[0]
271 mov r12, #0
272 strh r12, [r0, #0] @ block[0] = 0
273 strh r12, [r0, #126] @ block[63] = 0
274 add r4, r4, #64
275 mov r4, r4, asr #7 @ r4 = DC
276 mov r4, r4, lsl #16 @ spread to 2 halfwords
277 orr r4, r4, r4, lsr #16
278 ldrd r0, [r2] @ r0, r1
279 add r12, r2, r3, asl #3
2804:
281 uxtab16 lr, r4, r0, ror #8
282 uxtab16 r0, r4, r0
283 usat16 lr, #8, lr
284 usat16 r0, #8, r0
285 orr r0, r0, lr, lsl #8
286 uxtab16 lr, r4, r1, ror #8
287 uxtab16 r1, r4, r1
288 usat16 lr, #8, lr
289 usat16 r1, #8, r1
290 orr r1, r1, lr, lsl #8
291 strd r0, [r2] @ r0, r1
292 add r2, r2, r3
293 cmp r2, r12
294 ldrlod r0, [r2] @ r0, r1
295 blo 4b
296
297 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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Jens Arnold
11 * Based on the work of Karim Boucher and Rani Hod
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22
23 .global mpeg2_idct_copy
24 .type mpeg2_idct_copy, @function
25 .global mpeg2_idct_add
26 .type mpeg2_idct_add, @function
27
28 /* The IDCT itself.
29 * Input: %a0: block pointer
30 * Caller must save all registers. */
31 .align 2
32.idct:
33 move.l %a0, %a6
34
35 move.l #0, %macsr | signed integer mode
36
37 move.l #((2048<<16)+2841), %a0 | W0, W1
38 move.l #((2676<<16)+2408), %a1 | W2, W3
39 move.l #((2048<<16)+1609), %a2 | W4, W5
40 move.l #((1108<<16)+ 565), %a3 | W6, W7
41
42 lea.l (128,%a6), %a4 | secondary, transposed temp buffer
43 moveq.l #8, %d3 | loop counter
44
45.row_loop:
46 movem.l (%a6), %d0-%d2/%a5 | fetch (f0, f2, f4, f6, f1, f3, f5, f7)
47
48 mac.w %a0l, %d2u, %acc0 | %acc0 = W1 * f1
49 mac.w %a1l, %d2l, %acc0 | + W3 * f3
50 mac.w %a2l, %a5u, %acc0 | + W5 * f5
51 mac.w %a3l, %a5l, %acc0 | + W7 * f7
52
53 mac.w %a1l, %d2u, %acc1 | %acc1 = W3 * f1
54 msac.w %a3l, %d2l, %acc1 | - W7 * f3
55 msac.w %a0l, %a5u, %acc1 | - W1 * f5
56 msac.w %a2l, %a5l, %acc1 | - W5 * f7
57
58 mac.w %a2l, %d2u, %acc2 | %acc2 = W5 * f1
59 msac.w %a0l, %d2l, %acc2 | - W1 * f3
60 mac.w %a3l, %a5u, %acc2 | + W7 * f5
61 mac.w %a1l, %a5l, %acc2 | + W3 * f7
62
63 mac.w %a3l, %d2u, %acc3 | %acc3 = W7 * f1
64 msac.w %a2l, %d2l, %acc3 | - W5 * f3
65 mac.w %a1l, %a5u, %acc3 | + W3 * f5
66 msac.w %a0l, %a5l, %acc3 | - W1 * f7
67
68 lea.l (16,%a6), %a6 | Advance to next row; put here to fill EMAC latency
69 add.l #(1<<16), %d0 | f0 += 1;
70
71 movclr.l %acc0, %d4 | b0
72 movclr.l %acc1, %d5 | b1
73 movclr.l %acc2, %d6 | b2
74 movclr.l %acc3, %d7 | b3
75
76 mac.w %a0u, %d0u, %acc0 | %acc0 = W0 * f0
77 mac.w %a2u, %d1u, %acc0 | + W4 * f4
78 move.l %acc0, %acc3
79 mac.w %a1u, %d0l, %acc0 | + W2 * f2
80 mac.w %a3u, %d1l, %acc0 | + W6 * f6
81
82 mac.w %a0u, %d0u, %acc1 | %acc1 = W0 * f0
83 msac.w %a2u, %d1u, %acc1 | - W4 * f4
84 move.l %acc1, %acc2
85 mac.w %a3u, %d0l, %acc1 | + W6 * f2
86 msac.w %a1u, %d1l, %acc1 | - W2 * f6
87
88 | ^ move.l %acc1, %acc2 %acc2 = W0 * f0 - W4 * f4
89 msac.w %a3u, %d0l, %acc2 | - W6 * f2
90 mac.w %a1u, %d1l, %acc2 | + W2 * f6
91
92 | ^ move.l %acc0, %acc3 %acc3 = W0 * f0 + W4 * f4
93 msac.w %a1u, %d0l, %acc3 | - W2 * f2
94 msac.w %a3u, %d1l, %acc3 | - W6 * f6
95
96 moveq.l #12, %d1 | shift amount
97
98 move.l %acc0, %d0 | block[7] = (a0
99 sub.l %d4,%d0 | - b0)
100 asr.l %d1, %d0 | >> 12
101 move.w %d0, (7*16,%a4)
102
103 move.l %acc1, %d0 | block[6] = (a1
104 sub.l %d5,%d0 | - b1)
105 asr.l %d1, %d0 | >> 12
106 move.w %d0, (6*16,%a4)
107
108 move.l %acc2, %d0 | block[5] = (a2
109 sub.l %d6,%d0 | - b2)
110 asr.l %d1, %d0 | >> 12
111 move.w %d0, (5*16,%a4)
112
113 move.l %acc3, %d0 | block[4] = (a3
114 sub.l %d7,%d0 | - b3)
115 asr.l %d1, %d0 | >> 12
116 move.w %d0, (4*16,%a4)
117
118 movclr.l %acc3, %d0 | block[3] = (a3
119 add.l %d7, %d0 | + b3)
120 asr.l %d1, %d0 | >> 12
121 move.w %d0, (3*16,%a4)
122
123 movclr.l %acc2, %d0 | block[2] = (a2
124 add.l %d6, %d0 | + b2)
125 asr.l %d1, %d0 | >> 12
126 move.w %d0, (2*16,%a4)
127
128 movclr.l %acc1, %d0 | block[1] = (a1
129 add.l %d5, %d0 | + b1)
130 asr.l %d1, %d0 | >> 12
131 move.w %d0, (1*16,%a4)
132
133 movclr.l %acc0, %d0 | block[0] = (a0
134 add.l %d4, %d0 | + b0)
135 asr.l %d1, %d0 | >> 12
136 move.w %d0, (%a4)+ | advance to next temp column
137
138 subq.l #1, %d3 | loop 8 times
139 bne.w .row_loop
140
141 | %a6 now points to the temp buffer, where we need it.
142 lea.l (-16-128,%a4), %a4 | point %a4 back to the input block
143 moveq.l #8, %d3 | loop counter
144
145.col_loop:
146 movem.l (%a6), %d0-%d2/%a5 | fetch (f0, f2, f4, f6, f1, f3, f5, f7)
147
148 mac.w %a0l, %d2u, %acc0 | %acc0 = W1 * f1
149 mac.w %a1l, %d2l, %acc0 | + W3 * f3
150 mac.w %a2l, %a5u, %acc0 | + W5 * f5
151 mac.w %a3l, %a5l, %acc0 | + W7 * f7
152
153 mac.w %a1l, %d2u, %acc1 | %acc1 = W3 * f1
154 msac.w %a3l, %d2l, %acc1 | - W7 * f3
155 msac.w %a0l, %a5u, %acc1 | - W1 * f5
156 msac.w %a2l, %a5l, %acc1 | - W5 * f7
157
158 mac.w %a2l, %d2u, %acc2 | %acc2 = W5 * f1
159 msac.w %a0l, %d2l, %acc2 | - W1 * f3
160 mac.w %a3l, %a5u, %acc2 | + W7 * f5
161 mac.w %a1l, %a5l, %acc2 | + W3 * f7
162
163 mac.w %a3l, %d2u, %acc3 | %acc3 = W7 * f1
164 msac.w %a2l, %d2l, %acc3 | - W5 * f3
165 mac.w %a1l, %a5u, %acc3 | + W3 * f5
166 msac.w %a0l, %a5l, %acc3 | - W1 * f7
167
168 lea.l (16,%a6), %a6 | Advance to next row; put here to fill EMAC latency
169 add.l #(32<<16), %d0 | DC offset: 0.5
170
171 movclr.l %acc0, %d4 | b0
172 movclr.l %acc1, %d5 | b1
173 movclr.l %acc2, %d6 | b2
174 movclr.l %acc3, %d7 | b3
175
176 mac.w %a0u, %d0u, %acc0 | %acc0 = W0 * f0
177 mac.w %a2u, %d1u, %acc0 | + W4 * f4
178 move.l %acc0, %acc3
179 mac.w %a1u, %d0l, %acc0 | + W2 * f2
180 mac.w %a3u, %d1l, %acc0 | + W6 * f6
181
182 mac.w %a0u, %d0u, %acc1 | %acc1 = W0 * f0
183 msac.w %a2u, %d1u, %acc1 | - W4 * f4
184 move.l %acc1, %acc2
185 mac.w %a3u, %d0l, %acc1 | + W6 * f2
186 msac.w %a1u, %d1l, %acc1 | - W2 * f6
187
188 | ^ move.l %acc1, %acc2 %acc2 = W0 * f0 - W4 * f4
189 msac.w %a3u, %d0l, %acc2 | - W6 * f2
190 mac.w %a1u, %d1l, %acc2 | + W2 * f6
191
192 | ^ move.l %acc0, %acc3 %acc3 = W0 * f0 + W4 * f4
193 msac.w %a1u, %d0l, %acc3 | - W2 * f2
194 msac.w %a3u, %d1l, %acc3 | - W6 * f6
195
196 moveq.l #17, %d1 | shift amount
197
198 move.l %acc0, %d0 | block[7] = (a0
199 sub.l %d4,%d0 | - b0)
200 asr.l %d1, %d0 | >> 17
201 move.w %d0, (7*16,%a4)
202
203 move.l %acc1, %d0 | block[6] = (a1
204 sub.l %d5,%d0 | - b1)
205 asr.l %d1, %d0 | >> 17
206 move.w %d0, (6*16,%a4)
207
208 move.l %acc2, %d0 | block[5] = (a2
209 sub.l %d6,%d0 | - b2)
210 asr.l %d1, %d0 | >> 17
211 move.w %d0, (5*16,%a4)
212
213 move.l %acc3, %d0 | block[4] = (a3
214 sub.l %d7,%d0 | - b3)
215 asr.l %d1, %d0 | >> 17
216 move.w %d0, (4*16,%a4)
217
218 movclr.l %acc3, %d0 | block[3] = (a3
219 add.l %d7, %d0 | + b3)
220 asr.l %d1, %d0 | >> 17
221 move.w %d0, (3*16,%a4)
222
223 movclr.l %acc2, %d0 | block[2] = (a2
224 add.l %d6, %d0 | + b2)
225 asr.l %d1, %d0 | >> 17
226 move.w %d0, (2*16,%a4)
227
228 movclr.l %acc1, %d0 | block[1] = (a1
229 add.l %d5, %d0 | + b1)
230 asr.l %d1, %d0 | >> 17
231 move.w %d0, (1*16,%a4)
232
233 movclr.l %acc0, %d0 | block[0] = (a0
234 add.l %d4, %d0 | + b0)
235 asr.l %d1, %d0 | >> 17
236 move.w %d0, (%a4)+ | advance to next column
237
238 subq.l #1, %d3 | loop 8 times
239 bne.w .col_loop
240
241 rts
242
243 .align 2
244
245mpeg2_idct_copy:
246 lea.l (-11*4,%sp), %sp
247 movem.l %d2-%d7/%a2-%a6, (%sp) | save some registers
248 move.l (11*4+4,%sp), %a0 | %a0 - block pointer for idct
249
250 bsr.w .idct | apply idct to block
251 movem.l (11*4+4,%sp), %a0-%a2 | %a0 - block pointer
252 | %a1 - destination pointer
253 | %a2 - stride
254
255 move.l #255, %d1 | preload constant for clipping
256 moveq.l #8, %d4 | loop counter
257
258.copy_clip_loop:
259 move.w (%a0), %d0 | load block[0]
260 ext.l %d0 | sign extend
261 cmp.l %d1, %d0 | overflow?
262 bls.b 1f
263 spl.b %d0 | yes: set appropriate limit value in low byte
2641:
265 move.b %d0, %d2 | collect output bytes 0..3 in %d2
266 lsl.l #8, %d2
267
268 move.w (2,%a0), %d0 | load block[1]
269 ext.l %d0 | sign extend
270 cmp.l %d1, %d0 | overflow?
271 bls.b 1f
272 spl.b %d0 | yes: set appropriate limit value in low byte
2731:
274 move.b %d0, %d2 | collect output bytes 0..3 in %d2
275 lsl.l #8, %d2
276 clr.l (%a0)+ | clear block[0] and block[1],
277 | %a0 now pointing to block[2]
278 move.w (%a0), %d0 | do b2 and b3
279 ext.l %d0
280 cmp.l %d1, %d0
281 bls.b 1f
282 spl.b %d0
2831:
284 move.b %d0, %d2
285 lsl.l #8, %d2
286
287 move.w (2,%a0), %d0
288 ext.l %d0
289 cmp.l %d1, %d0
290 bls.b 1f
291 spl.b %d0
2921:
293 move.b %d0, %d2
294 clr.l (%a0)+
295
296 move.w (%a0), %d0 | do b4 and b5
297 ext.l %d0
298 cmp.l %d1, %d0
299 bls.b 1f
300 spl.b %d0
3011:
302 move.b %d0, %d3
303 lsl.l #8, %d3
304
305 move.w (2,%a0), %d0
306 ext.l %d0
307 cmp.l %d1, %d0
308 bls.b 1f
309 spl.b %d0
3101:
311 move.b %d0, %d3
312 lsl.l #8, %d3
313 clr.l (%a0)+
314
315 move.w (%a0), %d0 | do b6 and b7
316 ext.l %d0
317 cmp.l %d1, %d0
318 bls.b 1f
319 spl.b %d0
3201:
321 move.b %d0, %d3
322 lsl.l #8, %d3
323
324 move.w (2,%a0), %d0
325 ext.l %d0
326 cmp.l %d1, %d0
327 bls.b 1f
328 spl.b %d0
3291:
330 move.b %d0, %d3
331 clr.l (%a0)+
332
333 movem.l %d2-%d3, (%a1) | write all 8 output bytes at once
334 add.l %a2, %a1 | advance output pointer
335 subq.l #1, %d4 | loop 8 times
336 bne.w .copy_clip_loop
337
338 movem.l (%sp), %d2-%d7/%a2-%a6
339 lea.l (11*4,%sp), %sp
340 rts
341
342 .align 2
343
344mpeg2_idct_add:
345 lea.l (-11*4,%sp), %sp
346 movem.l %d2-%d7/%a2-%a6, (%sp)
347 movem.l (11*4+4,%sp), %d0/%a0-%a2 | %d0 - last value
348 | %a0 - block pointer
349 | %a1 - destination pointer
350 | %a2 - stride
351
352 cmp.l #129, %d0 | last == 129 ?
353 bne.b .idct_add | no: perform idct + addition
354 move.w (%a0), %d0
355 ext.l %d0 | ((block[0]
356 asr.l #4, %d0 | >> 4)
357 and.l #7, %d0 | & 7)
358 subq.l #4, %d0 | - 4 == 0 ?
359 bne.w .dc_add | no: just perform addition
360
361.idct_add:
362 bsr.w .idct | apply idct
363 movem.l (11*4+8,%sp), %a0-%a2 | reload arguments %a0..%a2
364
365 move.l #255, %d2 | preload constant for clipping
366 clr.l %d3 | used for splitting input words into bytes
367 moveq.l #8, %d4 | loop counter
368
369.add_clip_loop:
370 movem.l (%a1), %d6-%d7 | fetch (b0 b1 b2 b3) (b4 b5 b6 b7)
371 swap %d6 | (b2 b3 b0 b1)
372 swap %d7 | (b6 b7 b4 b5)
373
374 move.w (2,%a0), %d0 | load block[1]
375 ext.l %d0 | sign extend
376 move.b %d6, %d3 | copy b1
377 lsr.l #8, %d6 | prepare 1st buffer for next byte
378 add.l %d3, %d0 | add b1
379 cmp.l %d2, %d0 | overflow ?
380 bls.b 1f
381 spl.b %d0 | yes: set appropriate limit value in low byte
3821:
383 move.w (%a0), %d1 | load block[0]
384 ext.l %d1 | sign extend
385 move.b %d6, %d3 | copy b0
386 lsr.l #8, %d6 | prepare 1st buffer for next byte
387 add.l %d3, %d1 | add b0
388 cmp.l %d2, %d1 | overflow ?
389 bls.b 1f
390 spl.b %d1 | yes: set appropriate limit value in low byte
3911:
392 move.b %d1, %d5 | collect output bytes 0..3 in %d5
393 lsl.l #8, %d5
394 move.b %d0, %d5
395 lsl.l #8, %d5
396 clr.l (%a0)+ | clear block[0] and block[1]
397 | %a0 now pointing to block[2]
398 move.w (2,%a0), %d0 | do b3 and b2
399 ext.l %d0
400 move.b %d6, %d3
401 lsr.l #8, %d6
402 add.l %d3, %d0
403 cmp.l %d2, %d0
404 bls.b 1f
405 spl.b %d0
4061:
407 move.w (%a0), %d1
408 ext.l %d1
409 add.l %d6, %d1
410 cmp.l %d2, %d1
411 bls.b 1f
412 spl.b %d1
4131:
414 move.b %d1, %d5
415 lsl.l #8, %d5
416 move.b %d0, %d5
417 clr.l (%a0)+
418
419 move.w (2,%a0), %d0 | do b5 and b4
420 ext.l %d0
421 move.b %d7, %d3
422 lsr.l #8, %d7
423 add.l %d3, %d0
424 cmp.l %d2, %d0
425 bls.b 1f
426 spl.b %d0
4271:
428 move.w (%a0), %d1
429 ext.l %d1
430 move.b %d7, %d3
431 lsr.l #8, %d7
432 add.l %d3, %d1
433 cmp.l %d2, %d1
434 bls.b 1f
435 spl.b %d1
4361:
437 move.b %d1, %d6
438 lsl.l #8, %d6
439 move.b %d0, %d6
440 lsl.l #8, %d6
441 clr.l (%a0)+
442
443 move.w (2,%a0), %d0 | do b7 and b6
444 ext.l %d0
445 move.b %d7, %d3
446 lsr.l #8, %d7
447 add.l %d3, %d0
448 cmp.l %d2, %d0
449 bls.b 1f
450 spl.b %d0
4511:
452 move.w (%a0), %d1
453 ext.l %d1
454 add.l %d7, %d1
455 cmp.l %d2, %d1
456 bls.b 1f
457 spl.b %d1
4581:
459 move.b %d1, %d6
460 lsl.l #8, %d6
461 move.b %d0, %d6
462 clr.l (%a0)+
463
464 movem.l %d5-%d6, (%a1) | write all 8 output bytes at once
465 add.l %a2, %a1 | advance output pointer
466 subq.l #1, %d4 | loop 8 times
467 bne.w .add_clip_loop
468
469 bra.w .idct_add_end
470
471.dc_add:
472 move.w (%a0), %d0
473 ext.l %d0 | %d0 = (block[0]
474 add.l #64, %d0 | + 64)
475 asr.l #7, %d0 | >> 7
476 clr.w (%a0) | clear block[0]
477 clr.w (63*2,%a0) | and block[63]
478 move.l %d0, %a0 | DC value in %a0
479
480 move.l #255, %d2 | preload constant for clipping
481 clr.l %d3 | for splitting input words into bytes
482 moveq.l #8, %d4 | loop counter
483
484.dc_clip_loop:
485 movem.l (%a1), %d6-%d7 | (b0 b1 b2 b3) (b4 b5 b6 b7)
486 swap %d6 | (b2 b3 b0 b1)
487 swap %d7 | (b6 b7 b4 b5)
488
489 move.l %a0, %d0 | copy DC
490 move.b %d6, %d3 | copy b1
491 lsr.l #8, %d6 | prepare 1st buffer for next byte
492 add.l %d3, %d0 | add b1
493 cmp.l %d2, %d0 | overflow ?
494 bls.b 1f
495 spl.b %d0 | yes: set appropriate limit value in low byte
4961:
497 move.l %a0, %d1 | copy DC
498 move.b %d6, %d3 | copy b0
499 lsr.l #8, %d6 | prepare 1st buffer for next byte
500 add.l %d3, %d1 | add b0
501 cmp.l %d2, %d1 | overflow ?
502 bls.b 1f
503 spl.b %d1 | yes: set appropriate limit value in low byte
5041:
505 move.b %d1, %d5 | collect output bytes 0..3 in %d5
506 lsl.l #8, %d5
507 move.b %d0, %d5
508 lsl.l #8, %d5
509
510 move.l %a0, %d0 | do b3 and b2
511 move.b %d6, %d3
512 lsr.l #8, %d6
513 add.l %d3, %d0
514 cmp.l %d2, %d0
515 bls.b 1f
516 spl.b %d0
5171:
518 move.l %a0, %d1
519 add.l %d6, %d1
520 cmp.l %d2, %d1
521 bls.b 1f
522 spl.b %d1
5231:
524 move.b %d1, %d5
525 lsl.l #8, %d5
526 move.b %d0, %d5
527
528 move.l %a0, %d0 | do b5 and b4
529 move.b %d7, %d3
530 lsr.l #8, %d7
531 add.l %d3, %d0
532 cmp.l %d2, %d0
533 bls.b 1f
534 spl.b %d0
5351:
536 move.l %a0, %d1
537 move.b %d7, %d3
538 lsr.l #8, %d7
539 add.l %d3, %d1
540 cmp.l %d2, %d1
541 bls.b 1f
542 spl.b %d1
5431:
544 move.b %d1, %d6 | do b7 and b6
545 lsl.l #8, %d6
546 move.b %d0, %d6
547 lsl.l #8, %d6
548
549 move.l %a0, %d0
550 move.b %d7, %d3
551 lsr.l #8, %d7
552 add.l %d3, %d0
553 cmp.l %d2, %d0
554 bls.b 1f
555 spl.b %d0
5561:
557 move.l %a0, %d1
558 add.l %d7, %d1
559 cmp.l %d2, %d1
560 bls.b 1f
561 spl.b %d1
5621:
563 move.b %d1, %d6
564 lsl.l #8, %d6
565 move.b %d0, %d6
566
567 movem.l %d5-%d6, (%a1) | write all 8 output bytes at once
568 add.l %a2, %a1 | advance output pointer
569 subq.l #1, %d4 | loop 8 times
570 bne.w .dc_clip_loop
571
572.idct_add_end:
573 movem.l (%sp), %d2-%d7/%a2-%a6
574 lea.l (11*4,%sp), %sp
575 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 @@
1/*
2 * motion_comp.c
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.17 - lost compatibility previously to
26 * provide simplified and CPU-optimized motion compensation.
27 */
28
29#include "plugin.h"
30
31#include "mpeg2dec_config.h"
32
33#include "mpeg2.h"
34#include "attributes.h"
35#include "mpeg2_internal.h"
36
37/* These are defined in their respective target files - motion_comp_X.c */
38extern mpeg2_mc_fct MC_put_o_16;
39extern mpeg2_mc_fct MC_put_o_8;
40extern mpeg2_mc_fct MC_put_x_16;
41extern mpeg2_mc_fct MC_put_x_8;
42extern mpeg2_mc_fct MC_put_y_16;
43extern mpeg2_mc_fct MC_put_y_8;
44extern mpeg2_mc_fct MC_put_xy_16;
45extern mpeg2_mc_fct MC_put_xy_8;
46
47extern mpeg2_mc_fct MC_avg_o_16;
48extern mpeg2_mc_fct MC_avg_o_8;
49extern mpeg2_mc_fct MC_avg_x_16;
50extern mpeg2_mc_fct MC_avg_x_8;
51extern mpeg2_mc_fct MC_avg_y_16;
52extern mpeg2_mc_fct MC_avg_y_8;
53extern mpeg2_mc_fct MC_avg_xy_16;
54extern mpeg2_mc_fct MC_avg_xy_8;
55
56const mpeg2_mc_t mpeg2_mc =
57{
58 {
59 MC_put_o_16, MC_put_x_16, MC_put_y_16, MC_put_xy_16,
60 MC_put_o_8, MC_put_x_8, MC_put_y_8, MC_put_xy_8
61 },
62 {
63 MC_avg_o_16, MC_avg_x_16, MC_avg_y_16, MC_avg_xy_16,
64 MC_avg_o_8, MC_avg_x_8, MC_avg_y_8, MC_avg_xy_8
65 }
66};
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 @@
1/*
2 * motion_comp.h
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 */
25
26
27#define avg2(a,b) ((a+b+1)>>1)
28#define avg4(a,b,c,d) ((a+b+c+d+2)>>2)
29
30#define predict_o(i) (ref[i])
31#define predict_x(i) (avg2 (ref[i], ref[i+1]))
32#define predict_y(i) (avg2 (ref[i], (ref+stride)[i]))
33#define predict_xy(i) (avg4 (ref[i], ref[i+1], \
34 (ref+stride)[i], (ref+stride)[i+1]))
35
36#define put(predictor,i) dest[i] = predictor (i)
37#define avg(predictor,i) dest[i] = avg2 (predictor (i), dest[i])
38
39/* mc function template */
40#define MC_FUNC(op, xy) \
41 MC_FUNC_16(op, xy) \
42 MC_FUNC_8(op, xy)
43
44#define MC_FUNC_16(op, xy) \
45 void MC_##op##_##xy##_16 (uint8_t * dest, const uint8_t * ref, \
46 const int stride, int height) \
47 { \
48 do { \
49 op (predict_##xy, 0); \
50 op (predict_##xy, 1); \
51 op (predict_##xy, 2); \
52 op (predict_##xy, 3); \
53 op (predict_##xy, 4); \
54 op (predict_##xy, 5); \
55 op (predict_##xy, 6); \
56 op (predict_##xy, 7); \
57 op (predict_##xy, 8); \
58 op (predict_##xy, 9); \
59 op (predict_##xy, 10); \
60 op (predict_##xy, 11); \
61 op (predict_##xy, 12); \
62 op (predict_##xy, 13); \
63 op (predict_##xy, 14); \
64 op (predict_##xy, 15); \
65 ref += stride; \
66 dest += stride; \
67 } while (--height); \
68 }
69
70#define MC_FUNC_8(op, xy) \
71 void MC_##op##_##xy##_8 (uint8_t * dest, const uint8_t * ref, \
72 const int stride, int height) \
73 { \
74 do { \
75 op (predict_##xy, 0); \
76 op (predict_##xy, 1); \
77 op (predict_##xy, 2); \
78 op (predict_##xy, 3); \
79 op (predict_##xy, 4); \
80 op (predict_##xy, 5); \
81 op (predict_##xy, 6); \
82 op (predict_##xy, 7); \
83 ref += stride; \
84 dest += stride; \
85 } while (--height); \
86 }
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 @@
1/*
2 * motion_comp_arm.c
3 * Copyright (C) 2004 AGAWA Koji <i (AT) atty (DOT) jp>
4 *
5 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
6 * See http://libmpeg2.sourceforge.net/ for updates.
7 *
8 * mpeg2dec is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * mpeg2dec is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * $Id$
23 */
24#include <inttypes.h>
25#include "mpeg2.h"
26#include "attributes.h"
27#include "mpeg2_internal.h"
28#include "motion_comp.h"
29
30/* definitions of the actual mc functions */
31
32/* MC_FUNC (put, o) <= ASM */
33MC_FUNC (avg, o)
34/* MC_FUNC (put, x) <= ASM */
35MC_FUNC (avg, x)
36MC_FUNC (put, y)
37MC_FUNC (avg, y)
38MC_FUNC (put, xy)
39MC_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 @@
1@ motion_comp_arm_s.S
2@ Copyright (C) 2004 AGAWA Koji <i (AT) atty (DOT) jp>
3@
4@ This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
5@ See http://libmpeg2.sourceforge.net/ for updates.
6@
7@ mpeg2dec is free software; you can redistribute it and/or modify
8@ it under the terms of the GNU General Public License as published by
9@ the Free Software Foundation; either version 2 of the License, or
10@ (at your option) any later version.
11@
12@ mpeg2dec is distributed in the hope that it will be useful,
13@ but WITHOUT ANY WARRANTY; without even the implied warranty of
14@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15@ GNU General Public License for more details.
16@
17@ You should have received a copy of the GNU General Public License
18@ along with this program; if not, write to the Free Software
19@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20@
21@ $Id$
22
23#include "config.h" /* Rockbox: ARM architecture version */
24
25 .text
26
27@ ----------------------------------------------------------------
28 .align
29 .global MC_put_o_16
30MC_put_o_16:
31 @@ void func(uint8_t * dest, const uint8_t * ref, int stride, int height)
32 @@ pld [r1]
33 stmfd sp!, {r4-r7, lr} @ R14 is also called LR
34 and r4, r1, #3
35 ldr pc, [pc, r4, lsl #2]
36 .word 0
37 .word MC_put_o_16_align0
38 .word MC_put_o_16_align1
39 .word MC_put_o_16_align2
40 .word MC_put_o_16_align3
41
42MC_put_o_16_align0:
43 ldmia r1, {r4-r7}
44 add r1, r1, r2
45 @@ pld [r1]
46 stmia r0, {r4-r7}
47 subs r3, r3, #1
48 add r0, r0, r2
49 bne MC_put_o_16_align0
50 ldmpc regs=r4-r7 @@ update PC with LR content.
51
52.macro ADJ_ALIGN_QW shift, R0, R1, R2, R3, R4
53 mov \R0, \R0, lsr #(\shift)
54 orr \R0, \R0, \R1, lsl #(32 - \shift)
55 mov \R1, \R1, lsr #(\shift)
56 orr \R1, \R1, \R2, lsl #(32 - \shift)
57 mov \R2, \R2, lsr #(\shift)
58 orr \R2, \R2, \R3, lsl #(32 - \shift)
59 mov \R3, \R3, lsr #(\shift)
60 orr \R3, \R3, \R4, lsl #(32 - \shift)
61 mov \R4, \R4, lsr #(\shift)
62.endm
63
64MC_put_o_16_align1:
65 and r1, r1, #0xFFFFFFFC
661: ldmia r1, {r4-r7, r12}
67 add r1, r1, r2
68 @@ pld [r1]
69 ADJ_ALIGN_QW 8, r4, r5, r6, r7, r12
70 stmia r0, {r4-r7}
71 subs r3, r3, #1
72 add r0, r0, r2
73 bne 1b
74 ldmpc regs=r4-r7 @@ update PC with LR content.
75
76MC_put_o_16_align2:
77 and r1, r1, #0xFFFFFFFC
781: ldmia r1, {r4-r7, r12}
79 add r1, r1, r2
80 @@ pld [r1]
81 ADJ_ALIGN_QW 16, r4, r5, r6, r7, r12
82 stmia r0, {r4-r7}
83 subs r3, r3, #1
84 add r0, r0, r2
85 bne 1b
86 ldmpc regs=r4-r7 @@ update PC with LR content.
87
88MC_put_o_16_align3:
89 and r1, r1, #0xFFFFFFFC
901: ldmia r1, {r4-r7, r12}
91 add r1, r1, r2
92 @@ pld [r1]
93 ADJ_ALIGN_QW 24, r4, r5, r6, r7, r12
94 stmia r0, {r4-r7}
95 subs r3, r3, #1
96 add r0, r0, r2
97 bne 1b
98 ldmpc regs=r4-r7 @@ update PC with LR content.
99
100@ ----------------------------------------------------------------
101 .align
102 .global MC_put_o_8
103MC_put_o_8:
104 @@ void func(uint8_t * dest, const uint8_t * ref, int stride, int height)
105 @@ pld [r1]
106 stmfd sp!, {r4, r5, lr} @ R14 is also called LR
107 and r4, r1, #3
108 ldr pc, [pc, r4, lsl #2]
109 .word 0
110 .word MC_put_o_8_align0
111 .word MC_put_o_8_align1
112 .word MC_put_o_8_align2
113 .word MC_put_o_8_align3
114
115MC_put_o_8_align0:
116 ldmia r1, {r4, r5}
117 add r1, r1, r2
118 @@ pld [r1]
119 stmia r0, {r4, r5}
120 add r0, r0, r2
121 subs r3, r3, #1
122 bne MC_put_o_8_align0
123 ldmpc regs=r4-r5 @@ update PC with LR content.
124
125.macro ADJ_ALIGN_DW shift, R0, R1, R2
126 mov \R0, \R0, lsr #(\shift)
127 orr \R0, \R0, \R1, lsl #(32 - \shift)
128 mov \R1, \R1, lsr #(\shift)
129 orr \R1, \R1, \R2, lsl #(32 - \shift)
130 mov \R2, \R2, lsr #(\shift)
131.endm
132
133MC_put_o_8_align1:
134 and r1, r1, #0xFFFFFFFC
1351: ldmia r1, {r4, r5, r12}
136 add r1, r1, r2
137 @@ pld [r1]
138 ADJ_ALIGN_DW 8, r4, r5, r12
139 stmia r0, {r4, r5}
140 subs r3, r3, #1
141 add r0, r0, r2
142 bne 1b
143 ldmpc regs=r4-r5 @@ update PC with LR content.
144
145MC_put_o_8_align2:
146 and r1, r1, #0xFFFFFFFC
1471: ldmia r1, {r4, r5, r12}
148 add r1, r1, r2
149 @@ pld [r1]
150 ADJ_ALIGN_DW 16, r4, r5, r12
151 stmia r0, {r4, r5}
152 subs r3, r3, #1
153 add r0, r0, r2
154 bne 1b
155 ldmpc regs=r4-r5 @@ update PC with LR content.
156
157MC_put_o_8_align3:
158 and r1, r1, #0xFFFFFFFC
1591: ldmia r1, {r4, r5, r12}
160 add r1, r1, r2
161 @@ pld [r1]
162 ADJ_ALIGN_DW 24, r4, r5, r12
163 stmia r0, {r4, r5}
164 subs r3, r3, #1
165 add r0, r0, r2
166 bne 1b
167 ldmpc regs=r4-r5 @@ update PC with LR content.
168
169@ ----------------------------------------------------------------
170.macro AVG_PW rW1, rW2
171 mov \rW2, \rW2, lsl #24
172 orr \rW2, \rW2, \rW1, lsr #8
173 eor r9, \rW1, \rW2
174#if ARM_ARCH >= 6
175 uhadd8 \rW2, \rW1, \rW2
176#else
177 and \rW2, \rW1, \rW2
178 and r10, r9, r11
179 add \rW2, \rW2, r10, lsr #1
180#endif
181 and r9, r9, r12
182 add \rW2, \rW2, r9
183.endm
184
185#if ARM_ARCH >= 6
186#define HIGHEST_REG r9
187#else
188#define HIGHEST_REG r11
189#endif
190
191 .align
192 .global MC_put_x_16
193MC_put_x_16:
194 @@ void func(uint8_t * dest, const uint8_t * ref, int stride, int height)
195 @@ pld [r1]
196 stmfd sp!, {r4-HIGHEST_REG, lr} @ R14 is also called LR
197 and r4, r1, #3
198 ldr r12, 2f
199#if ARM_ARCH < 6
200 mvn r11, r12
201#endif
202 ldr pc, [pc, r4, lsl #2]
2032: .word 0x01010101
204 .word MC_put_x_16_align0
205 .word MC_put_x_16_align1
206 .word MC_put_x_16_align2
207 .word MC_put_x_16_align3
208
209MC_put_x_16_align0:
210 ldmia r1, {r4-r8}
211 add r1, r1, r2
212 @@ pld [r1]
213 AVG_PW r7, r8
214 AVG_PW r6, r7
215 AVG_PW r5, r6
216 AVG_PW r4, r5
217 stmia r0, {r5-r8}
218 subs r3, r3, #1
219 add r0, r0, r2
220 bne MC_put_x_16_align0
221 ldmpc regs=r4-HIGHEST_REG @@ update PC with LR content.
222
223MC_put_x_16_align1:
224 and r1, r1, #0xFFFFFFFC
2251: ldmia r1, {r4-r8}
226 add r1, r1, r2
227 @@ pld [r1]
228 ADJ_ALIGN_QW 8, r4, r5, r6, r7, r8
229 AVG_PW r7, r8
230 AVG_PW r6, r7
231 AVG_PW r5, r6
232 AVG_PW r4, r5
233 stmia r0, {r5-r8}
234 subs r3, r3, #1
235 add r0, r0, r2
236 bne 1b
237 ldmpc regs=r4-HIGHEST_REG @@ update PC with LR content.
238
239MC_put_x_16_align2:
240 and r1, r1, #0xFFFFFFFC
2411: ldmia r1, {r4-r8}
242 add r1, r1, r2
243 @@ pld [r1]
244 ADJ_ALIGN_QW 16, r4, r5, r6, r7, r8
245 AVG_PW r7, r8
246 AVG_PW r6, r7
247 AVG_PW r5, r6
248 AVG_PW r4, r5
249 stmia r0, {r5-r8}
250 subs r3, r3, #1
251 add r0, r0, r2
252 bne 1b
253 ldmpc regs=r4-HIGHEST_REG @@ update PC with LR content.
254
255MC_put_x_16_align3:
256 and r1, r1, #0xFFFFFFFC
2571: ldmia r1, {r4-r8}
258 add r1, r1, r2
259 @@ pld [r1]
260 ADJ_ALIGN_QW 24, r4, r5, r6, r7, r8
261 AVG_PW r7, r8
262 AVG_PW r6, r7
263 AVG_PW r5, r6
264 AVG_PW r4, r5
265 stmia r0, {r5-r8}
266 subs r3, r3, #1
267 add r0, r0, r2
268 bne 1b
269 ldmpc regs=r4-HIGHEST_REG @@ update PC with LR content.
270
271@ ----------------------------------------------------------------
272 .align
273 .global MC_put_x_8
274MC_put_x_8:
275 @@ void func(uint8_t * dest, const uint8_t * ref, int stride, int height)
276 @@ pld [r1]
277 stmfd sp!, {r6-HIGHEST_REG, lr} @ R14 is also called LR
278 and r6, r1, #3
279 ldr r12, 2f
280#if ARM_ARCH < 6
281 mvn r11, r12
282#endif
283 ldr pc, [pc, r6, lsl #2]
2842: .word 0x01010101
285 .word MC_put_x_8_align0
286 .word MC_put_x_8_align1
287 .word MC_put_x_8_align2
288 .word MC_put_x_8_align3
289
290MC_put_x_8_align0:
291 ldmia r1, {r6-r8}
292 add r1, r1, r2
293 @@ pld [r1]
294 AVG_PW r7, r8
295 AVG_PW r6, r7
296 stmia r0, {r7-r8}
297 subs r3, r3, #1
298 add r0, r0, r2
299 bne MC_put_x_8_align0
300 ldmpc regs=r6-HIGHEST_REG @@ update PC with LR content.
301
302MC_put_x_8_align1:
303 and r1, r1, #0xFFFFFFFC
3041: ldmia r1, {r6-r8}
305 add r1, r1, r2
306 @@ pld [r1]
307 ADJ_ALIGN_DW 8, r6, r7, r8
308 AVG_PW r7, r8
309 AVG_PW r6, r7
310 stmia r0, {r7-r8}
311 subs r3, r3, #1
312 add r0, r0, r2
313 bne 1b
314 ldmpc regs=r6-HIGHEST_REG @@ update PC with LR content.
315
316MC_put_x_8_align2:
317 and r1, r1, #0xFFFFFFFC
3181: ldmia r1, {r6-r8}
319 add r1, r1, r2
320 @@ pld [r1]
321 ADJ_ALIGN_DW 16, r6, r7, r8
322 AVG_PW r7, r8
323 AVG_PW r6, r7
324 stmia r0, {r7-r8}
325 subs r3, r3, #1
326 add r0, r0, r2
327 bne 1b
328 ldmpc regs=r6-HIGHEST_REG @@ update PC with LR content.
329
330MC_put_x_8_align3:
331 and r1, r1, #0xFFFFFFFC
3321: ldmia r1, {r6-r8}
333 add r1, r1, r2
334 @@ pld [r1]
335 ADJ_ALIGN_DW 24, r6, r7, r8
336 AVG_PW r7, r8
337 AVG_PW r6, r7
338 stmia r0, {r7-r8}
339 subs r3, r3, #1
340 add r0, r0, r2
341 bne 1b
342 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 @@
1/*
2 * motion_comp.c
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 */
25#include <inttypes.h>
26#include "mpeg2.h"
27#include "attributes.h"
28#include "mpeg2_internal.h"
29#include "motion_comp.h"
30
31/* definitions of the actual mc functions */
32
33MC_FUNC (put, o)
34MC_FUNC (avg, o)
35MC_FUNC (put, x)
36MC_FUNC (avg, x)
37MC_FUNC (put, y)
38MC_FUNC (avg, y)
39MC_FUNC (put, xy)
40MC_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 @@
1/*
2 * Based on:
3 * motion_comp_arm.c
4 * Copyright (C) 2004 AGAWA Koji <i (AT) atty (DOT) jp>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23#include <inttypes.h>
24#include "mpeg2.h"
25#include "attributes.h"
26#include "mpeg2_internal.h"
27#include "motion_comp.h"
28
29/* definitions of the actual mc functions */
30
31/* MC_FUNC (put, o) <= ASM */
32MC_FUNC (avg, o)
33/* MC_FUNC (put, x) <= ASM */
34MC_FUNC (avg, x)
35MC_FUNC (put, y)
36MC_FUNC (avg, y)
37MC_FUNC (put, xy)
38MC_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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Jens Arnold
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22.macro LEFT8_PW dW1, dW2 | needs %d0 == 24, clobbers %d2
23 lsl.l #8, \dW1 | changes dW1, keeps dW2
24 move.l \dW2, %d2
25 lsr.l %d0, %d2
26 or.l %d2, \dW1
27.endm
28
29.macro LEFT24_PW dW1, dW2 | needs %d0 == 24, clobbers %d2
30 lsl.l %d0, \dW1 | changes dW1, keeps dW2
31 move.l \dW2, %d2
32 lsr.l #8, %d2
33 or.l %d2, \dW1
34.endm
35
36/*****************************************************************************/
37
38 .align 2
39 .global MC_put_o_8
40 .type MC_put_o_8, @function
41
42MC_put_o_8:
43 movem.l (4,%sp), %a0-%a1 | dest, source
44 move.l %a1, %d0
45 and.l #3, %d0
46 sub.l %d0, %a1 | align source
47 jmp.l (2, %pc, %d0.l*4)
48 bra.w .po8_0
49 bra.w .po8_1
50 bra.w .po8_2
51 | last table entry coincides with target
52
53.po8_3:
54 lea.l (-5*4,%sp), %sp
55 movem.l %d2-%d5/%a2, (%sp) | save some registers
56 move.l (5*4+12,%sp), %a2 | stride
57 move.l (5*4+16,%sp), %d1 | height
58 moveq.l #24, %d0 | shift amount
591:
60 movem.l (%a1), %d3-%d5
61 add.l %a2, %a1
62 LEFT24_PW %d3, %d4
63 lsl.l %d0, %d4
64 lsr.l #8, %d5
65 or.l %d5, %d4
66 movem.l %d3-%d4, (%a0)
67 add.l %a2, %a0
68 subq.l #1, %d1
69 bne.s 1b
70 movem.l (%sp), %d2-%d5/%a2
71 lea.l (5*4,%sp), %sp
72 rts
73
74.po8_2:
75 lea.l (-3*4,%sp), %sp
76 movem.l %d2-%d4, (%sp) | save some registers
77 movem.l (3*4+12,%sp), %d0-%d1 | stride, height
781:
79 movem.l (%a1), %d2-%d4
80 add.l %d0, %a1
81 swap %d2
82 swap %d3
83 move.w %d3, %d2
84 swap %d4
85 move.w %d4, %d3
86 movem.l %d2-%d3, (%a0)
87 add.l %d0, %a0
88 subq.l #1, %d1
89 bne.s 1b
90 movem.l (%sp), %d2-%d4
91 lea.l (3*4,%sp), %sp
92 rts
93
94.po8_1:
95 lea.l (-5*4,%sp), %sp
96 movem.l %d2-%d5/%a2, (%sp) | save some registers
97 move.l (5*4+12,%sp), %a2 | stride
98 move.l (5*4+16,%sp), %d1 | height
99 moveq.l #24, %d0 | shift amount
1001:
101 movem.l (%a1), %d3-%d5
102 add.l %a2, %a1
103 LEFT8_PW %d3, %d4
104 lsl.l #8, %d4
105 lsr.l %d0, %d5
106 or.l %d5, %d4
107 movem.l %d3-%d4, (%a0)
108 add.l %a2, %a0
109 subq.l #1, %d1
110 bne.s 1b
111 movem.l (%sp), %d2-%d5/%a2
112 lea.l (5*4,%sp), %sp
113 rts
114
115.po8_0:
116 movem.l (12,%sp), %d0-%d1 | stride, height
117 subq.l #4, %d0 | adjust for increment within the loop
1181:
119 move.l (%a1)+, (%a0)+
120 move.l (%a1), (%a0)
121 add.l %d0, %a0
122 add.l %d0, %a1
123 subq.l #1, %d1
124 bne.s 1b
125 rts
126
127/*****************************************************************************/
128
129 .align 2
130 .global MC_put_o_16
131 .type MC_put_o_16, @function
132
133MC_put_o_16:
134 lea.l (-7*4,%sp), %sp
135 movem.l %d2-%d7/%a2, (%sp) | save some registers
136 movem.l (7*4+4,%sp), %a0-%a2| dest, source, stride
137 move.l (7*4+16,%sp), %d1 | height
138 move.l %a1, %d0
139 and.l #3, %d0
140 sub.l %d0, %a1
141 jmp.l (2, %pc, %d0.l*4)
142 bra.w .po16_0
143 bra.w .po16_1
144 bra.w .po16_2
145 | last table entry coincides with target
146
147.po16_3:
148 moveq.l #24, %d0 | shift amount
1491:
150 movem.l (%a1), %d3-%d7
151 add.l %a2, %a1
152 LEFT24_PW %d3, %d4
153 LEFT24_PW %d4, %d5
154 LEFT24_PW %d5, %d6
155 lsl.l %d0, %d6
156 lsr.l #8, %d7
157 or.l %d7, %d6
158 movem.l %d3-%d6, (%a0)
159 add.l %a2, %a0
160 subq.l #1, %d1
161 bne.s 1b
162 movem.l (%sp), %d2-%d7/%a2
163 lea.l (7*4,%sp), %sp
164 rts
165
166.po16_2:
1671:
168 movem.l (%a1), %d3-%d7
169 add.l %a2, %a1
170 swap %d3
171 swap %d4
172 move.w %d4, %d3
173 swap %d5
174 move.w %d5, %d4
175 swap %d6
176 move.w %d6, %d5
177 swap %d7
178 move.w %d7, %d6
179 movem.l %d3-%d6, (%a0)
180 add.l %a2, %a0
181 subq.l #1, %d1
182 bne.s 1b
183 movem.l (%sp), %d2-%d7/%a2
184 lea.l (7*4,%sp), %sp
185 rts
186
187.po16_1:
188 moveq.l #24, %d0 | shift amount
1891:
190 movem.l (%a1), %d3-%d7
191 add.l %a2, %a1
192 LEFT8_PW %d3, %d4
193 LEFT8_PW %d4, %d5
194 LEFT8_PW %d5, %d6
195 lsl.l #8, %d6
196 lsr.l %d0, %d7
197 or.l %d7, %d6
198 movem.l %d3-%d6, (%a0)
199 add.l %a2, %a0
200 subq.l #1, %d1
201 bne.s 1b
202 movem.l (%sp), %d2-%d7/%a2
203 lea.l (7*4,%sp), %sp
204 rts
205
206.po16_0:
2071:
208 movem.l (%a1), %d3-%d6
209 add.l %a2, %a1
210 movem.l %d3-%d6, (%a0)
211 add.l %a2, %a0
212 subq.l #1, %d1
213 bne.s 1b
214 movem.l (%sp), %d2-%d7/%a2
215 lea.l (7*4,%sp), %sp
216 rts
217
218/*****************************************************************************/
219
220.macro AVG_PW dW1, dW2 | needs %d0 == 24, clobbers %d1, %d2,
221 move.l \dW1, %d1 | changes dW1, keeps dW2
222 lsl.l #8, \dW1
223 move.l \dW2, %d2
224 lsr.l %d0, %d2
225 or.l %d2, \dW1
226 move.l %d1, %d2
227 eor.l \dW1, %d1
228 and.l %d2, \dW1
229 move.l #0xfefefefe, %d2
230 and.l %d1, %d2
231 eor.l %d2, %d1
232 lsr.l #1, %d2
233 add.l %d2, \dW1
234 add.l %d1, \dW1
235.endm
236
237/*****************************************************************************/
238
239 .align 2
240 .global MC_put_x_8
241 .type MC_put_x_8, @function
242
243MC_put_x_8:
244 lea.l (-6*4,%sp), %sp
245 movem.l %d2-%d6/%a2, (%sp) | save some registers
246 movem.l (6*4+4,%sp), %a0-%a2| dest, source, stride
247 move.l (6*4+16,%sp), %d6 | height
248 move.l %a1, %d0
249 and.l #3, %d0
250 sub.l %d0, %a1
251 jmp.l (2, %pc, %d0.l*4)
252 bra.w .px8_0
253 bra.w .px8_1
254 bra.w .px8_2
255 | last table entry coincides with target
256
257.px8_3:
258 moveq.l #24, %d0
2591:
260 movem.l (%a1), %d3-%d5
261 add.l %a2, %a1
262 LEFT24_PW %d3, %d4
263 LEFT24_PW %d4, %d5
264 lsl.l %d0, %d5
265 AVG_PW %d3, %d4
266 AVG_PW %d4, %d5
267 movem.l %d3-%d4, (%a0)
268 add.l %a2, %a0
269 subq.l #1, %d6
270 bne.s 1b
271 movem.l (%sp), %d2-%d6/%a2
272 lea.l (6*4,%sp), %sp
273 rts
274
275.px8_2:
276 moveq.l #24, %d0
2771:
278 movem.l (%a1), %d3-%d5
279 add.l %a2, %a1
280 swap %d3
281 swap %d4
282 move.w %d4, %d3
283 swap %d5
284 move.w %d5, %d4
285 AVG_PW %d3, %d4
286 AVG_PW %d4, %d5
287 movem.l %d3-%d4, (%a0)
288 add.l %a2, %a0
289 subq.l #1, %d6
290 bne.s 1b
291 movem.l (%sp), %d2-%d6/%a2
292 lea.l (6*4,%sp), %sp
293 rts
294
295.px8_1:
296 moveq.l #24, %d0
2971:
298 movem.l (%a1), %d3-%d5
299 add.l %a2, %a1
300 LEFT8_PW %d3, %d4
301 LEFT8_PW %d4, %d5
302 lsl.l #8, %d5
303 AVG_PW %d3, %d4
304 AVG_PW %d4, %d5
305 movem.l %d3-%d4, (%a0)
306 add.l %a2, %a0
307 subq.l #1, %d6
308 bne.s 1b
309 movem.l (%sp), %d2-%d6/%a2
310 lea.l (6*4,%sp), %sp
311 rts
312
313.px8_0:
314 moveq.l #24, %d0
3151:
316 movem.l (%a1), %d3-%d5
317 add.l %a2, %a1
318 AVG_PW %d3, %d4
319 AVG_PW %d4, %d5
320 movem.l %d3-%d4, (%a0)
321 add.l %a2, %a0
322 subq.l #1, %d6
323 bne.s 1b
324 movem.l (%sp), %d2-%d6/%a2
325 lea.l (6*4,%sp), %sp
326 rts
327
328/*****************************************************************************/
329
330 .align 2
331 .global MC_put_x_16
332 .type MC_put_x_16, @function
333
334MC_put_x_16:
335 lea.l (-8*4,%sp), %sp
336 movem.l %d2-%d7/%a2-%a3, (%sp) | save some registers
337 movem.l (8*4+4,%sp), %a0-%a3 | dest, source, stride, height
338 move.l %a1, %d0
339 and.l #3, %d0
340 sub.l %d0, %a1
341 jmp.l (2, %pc, %d0.l*4)
342 bra.w .px16_0
343 bra.w .px16_1
344 bra.w .px16_2
345 | last table entry coincides with target
346
347.px16_3:
348 moveq.l #24, %d0
3491:
350 movem.l (%a1), %d3-%d7
351 add.l %a2, %a1
352 LEFT24_PW %d3, %d4
353 LEFT24_PW %d4, %d5
354 LEFT24_PW %d5, %d6
355 LEFT24_PW %d6, %d7
356 lsl.l %d0, %d7
357 AVG_PW %d3, %d4
358 AVG_PW %d4, %d5
359 AVG_PW %d5, %d6
360 AVG_PW %d6, %d7
361 movem.l %d3-%d6, (%a0)
362 add.l %a2, %a0
363 subq.l #1, %a3
364 tst.l %a3
365 bne.w 1b
366 movem.l (%sp), %d2-%d7/%a2-%a3
367 lea.l (8*4,%sp), %sp
368 rts
369
370.px16_2:
371 moveq.l #24, %d0
3721:
373 movem.l (%a1), %d3-%d7
374 add.l %a2, %a1
375 swap %d3
376 swap %d4
377 move.w %d4, %d3
378 swap %d5
379 move.w %d5, %d4
380 swap %d6
381 move.w %d6, %d5
382 swap %d7
383 move.w %d7, %d6
384 AVG_PW %d3, %d4
385 AVG_PW %d4, %d5
386 AVG_PW %d5, %d6
387 AVG_PW %d6, %d7
388 movem.l %d3-%d6, (%a0)
389 add.l %a2, %a0
390 subq.l #1, %a3
391 tst.l %a3
392 bne.w 1b
393 movem.l (%sp), %d2-%d7/%a2-%a3
394 lea.l (8*4,%sp), %sp
395 rts
396
397.px16_1:
398 moveq.l #24, %d0
3991:
400 movem.l (%a1), %d3-%d7
401 add.l %a2, %a1
402 LEFT8_PW %d3, %d4
403 LEFT8_PW %d4, %d5
404 LEFT8_PW %d5, %d6
405 LEFT8_PW %d6, %d7
406 lsl.l #8, %d7
407 AVG_PW %d3, %d4
408 AVG_PW %d4, %d5
409 AVG_PW %d5, %d6
410 AVG_PW %d6, %d7
411 movem.l %d3-%d6, (%a0)
412 add.l %a2, %a0
413 subq.l #1, %a3
414 tst.l %a3
415 bne.w 1b
416 movem.l (%sp), %d2-%d7/%a2-%a3
417 lea.l (8*4,%sp), %sp
418 rts
419
420.px16_0:
421 moveq.l #24, %d0
4221:
423 movem.l (%a1), %d3-%d7
424 add.l %a2, %a1
425 AVG_PW %d3, %d4
426 AVG_PW %d4, %d5
427 AVG_PW %d5, %d6
428 AVG_PW %d6, %d7
429 movem.l %d3-%d6, (%a0)
430 add.l %a2, %a0
431 subq.l #1, %a3
432 tst.l %a3
433 bne.w 1b
434 movem.l (%sp), %d2-%d7/%a2-%a3
435 lea.l (8*4,%sp), %sp
436 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 @@
1/*
2 * mpeg2.h
3 * Copyright (C) 2000-2004 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.67
26 */
27
28#ifndef MPEG2_H
29#define MPEG2_H
30
31#include "mpeg2dec_config.h"
32
33#define MPEG2_VERSION(a,b,c) (((a)<<16)|((b)<<8)|(c))
34#define MPEG2_RELEASE MPEG2_VERSION (0, 5, 0) /* 0.5.0 */
35
36#define SEQ_FLAG_MPEG2 1
37#define SEQ_FLAG_CONSTRAINED_PARAMETERS 2
38#define SEQ_FLAG_PROGRESSIVE_SEQUENCE 4
39#define SEQ_FLAG_LOW_DELAY 8
40#define SEQ_FLAG_COLOUR_DESCRIPTION 16
41
42#define SEQ_MASK_VIDEO_FORMAT 0xe0
43#define SEQ_VIDEO_FORMAT_COMPONENT 0x00
44#define SEQ_VIDEO_FORMAT_PAL 0x20
45#define SEQ_VIDEO_FORMAT_NTSC 0x40
46#define SEQ_VIDEO_FORMAT_SECAM 0x60
47#define SEQ_VIDEO_FORMAT_MAC 0x80
48#define SEQ_VIDEO_FORMAT_UNSPECIFIED 0xa0
49
50typedef struct mpeg2_sequence_s
51{
52 unsigned int width, height;
53 unsigned int chroma_width, chroma_height;
54 unsigned int byte_rate;
55 unsigned int vbv_buffer_size;
56 uint32_t flags;
57
58 unsigned int picture_width, picture_height;
59 unsigned int display_width, display_height;
60 unsigned int pixel_width, pixel_height;
61 unsigned int frame_period;
62
63 uint8_t profile_level_id;
64 uint8_t colour_primaries;
65 uint8_t transfer_characteristics;
66 uint8_t matrix_coefficients;
67} mpeg2_sequence_t;
68
69#define GOP_FLAG_DROP_FRAME 1
70#define GOP_FLAG_BROKEN_LINK 2
71#define GOP_FLAG_CLOSED_GOP 4
72
73typedef struct mpeg2_gop_s
74{
75 uint8_t hours;
76 uint8_t minutes;
77 uint8_t seconds;
78 uint8_t pictures;
79 uint32_t flags;
80} mpeg2_gop_t;
81
82#define PIC_MASK_CODING_TYPE 7
83#define PIC_FLAG_CODING_TYPE_I 1
84#define PIC_FLAG_CODING_TYPE_P 2
85#define PIC_FLAG_CODING_TYPE_B 3
86#define PIC_FLAG_CODING_TYPE_D 4
87
88#define PIC_FLAG_TOP_FIELD_FIRST 8
89#define PIC_FLAG_PROGRESSIVE_FRAME 16
90#define PIC_FLAG_COMPOSITE_DISPLAY 32
91#define PIC_FLAG_SKIP 64
92#define PIC_FLAG_TAGS 128
93#define PIC_MASK_COMPOSITE_DISPLAY 0xfffff000
94
95typedef struct mpeg2_picture_s
96{
97 unsigned int temporal_reference;
98 unsigned int nb_fields;
99 uint32_t tag, tag2;
100 uint32_t flags;
101 struct
102 {
103 int x, y;
104 } display_offset[3];
105} mpeg2_picture_t;
106
107typedef struct mpeg2_fbuf_s
108{
109 uint8_t * buf[MPEG2_COMPONENTS];
110 void * id;
111} mpeg2_fbuf_t;
112
113typedef struct mpeg2_info_s
114{
115 const mpeg2_sequence_t * sequence;
116 const mpeg2_gop_t * gop;
117 const mpeg2_picture_t * current_picture;
118 const mpeg2_picture_t * current_picture_2nd;
119 const mpeg2_fbuf_t * current_fbuf;
120 const mpeg2_picture_t * display_picture;
121 const mpeg2_picture_t * display_picture_2nd;
122 const mpeg2_fbuf_t * display_fbuf;
123 const mpeg2_fbuf_t * discard_fbuf;
124 const uint8_t * user_data;
125 unsigned int user_data_len;
126} mpeg2_info_t;
127
128typedef struct mpeg2dec_s mpeg2dec_t;
129typedef struct mpeg2_decoder_s mpeg2_decoder_t;
130
131typedef enum
132{
133 STATE_INTERNAL_NORETURN = -1,
134 STATE_BUFFER = 0,
135 STATE_SEQUENCE = 1,
136 STATE_SEQUENCE_REPEATED = 2,
137 STATE_SEQUENCE_MODIFIED = 3,
138 STATE_GOP = 4,
139 STATE_PICTURE = 5,
140 STATE_SLICE_1ST = 6,
141 STATE_PICTURE_2ND = 7,
142 STATE_SLICE = 8,
143 STATE_END = 9,
144 STATE_INVALID = 10,
145 STATE_INVALID_END = 11,
146} mpeg2_state_t;
147
148typedef struct mpeg2_convert_init_s
149{
150 unsigned int id_size;
151 unsigned int buf_size[MPEG2_COMPONENTS];
152 void (* start)(void * id, const mpeg2_fbuf_t * fbuf,
153 const mpeg2_picture_t * picture, const mpeg2_gop_t * gop);
154 void (* copy)(void * id, uint8_t * const * src, unsigned int v_offset);
155} mpeg2_convert_init_t;
156
157typedef enum
158{
159 MPEG2_CONVERT_SET = 0,
160 MPEG2_CONVERT_STRIDE = 1,
161 MPEG2_CONVERT_START = 2
162} mpeg2_convert_stage_t;
163
164typedef int mpeg2_convert_t (int stage, void * id,
165 const mpeg2_sequence_t * sequence, int stride,
166 void * arg, mpeg2_convert_init_t * result);
167int mpeg2_convert (mpeg2dec_t * mpeg2dec, mpeg2_convert_t convert, void * arg);
168int mpeg2_stride (mpeg2dec_t * mpeg2dec, int stride);
169void mpeg2_set_buf (mpeg2dec_t * mpeg2dec, uint8_t * buf[MPEG2_COMPONENTS],
170 void * id);
171void mpeg2_custom_fbuf (mpeg2dec_t * mpeg2dec, int custom_fbuf);
172
173mpeg2dec_t * mpeg2_init (void);
174const mpeg2_info_t * mpeg2_info (mpeg2dec_t * mpeg2dec);
175void mpeg2_close (mpeg2dec_t * mpeg2dec);
176
177void mpeg2_buffer (mpeg2dec_t * mpeg2dec, uint8_t * start, uint8_t * end);
178int mpeg2_getpos (mpeg2dec_t * mpeg2dec);
179mpeg2_state_t mpeg2_parse (mpeg2dec_t * mpeg2dec);
180
181void mpeg2_reset (mpeg2dec_t * mpeg2dec, int full_reset);
182void mpeg2_skip (mpeg2dec_t * mpeg2dec, int skip);
183void mpeg2_slice_region (mpeg2dec_t * mpeg2dec, int start, int end);
184
185void mpeg2_tag_picture (mpeg2dec_t * mpeg2dec, uint32_t tag, uint32_t tag2);
186
187void mpeg2_init_fbuf (mpeg2_decoder_t * decoder,
188 uint8_t * current_fbuf[MPEG2_COMPONENTS],
189 uint8_t * forward_fbuf[MPEG2_COMPONENTS],
190 uint8_t * backward_fbuf[MPEG2_COMPONENTS]);
191void mpeg2_slice (mpeg2_decoder_t * decoder, int code, const uint8_t * buffer);
192
193int mpeg2_guess_aspect (const mpeg2_sequence_t * sequence,
194 unsigned int * pixel_width,
195 unsigned int * pixel_height);
196
197typedef enum
198{
199 MPEG2_ALLOC_MPEG2DEC = 0,
200 MPEG2_ALLOC_CHUNK = 1,
201 MPEG2_ALLOC_YUV = 2,
202 MPEG2_ALLOC_CONVERT_ID = 3,
203 MPEG2_ALLOC_CONVERTED = 4,
204 MPEG_ALLOC_CODEC_MALLOC,
205 MPEG_ALLOC_CODEC_CALLOC,
206 MPEG_ALLOC_MPEG2_BUFFER,
207 MPEG_ALLOC_AUDIOBUF,
208 MPEG_ALLOC_PCMOUT,
209 MPEG_ALLOC_DISKBUF,
210 __MPEG_ALLOC_FIRST = -256,
211} mpeg2_alloc_t;
212
213void * mpeg2_malloc (unsigned size, mpeg2_alloc_t reason);
214#if 0
215void mpeg2_free (void * buf);
216#endif
217/* allocates a dedicated buffer and locks all previous allocation in place */
218void * mpeg2_bufalloc(unsigned size, mpeg2_alloc_t reason);
219/* clears all non-dedicated buffer space */
220void mpeg2_mem_reset(void);
221void mpeg2_alloc_init(unsigned char* buf, int mallocsize);
222
223#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 @@
1/*
2 * mpeg2_internal.h
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.89
26 */
27#ifndef MPEG2_INTERNAL_H
28#define MPEG2_INTERNAL_H
29
30#include "config.h" /* for Rockbox CPU_ #defines */
31
32/* macroblock modes */
33#define MACROBLOCK_INTRA 1
34#define MACROBLOCK_PATTERN 2
35#define MACROBLOCK_MOTION_BACKWARD 4
36#define MACROBLOCK_MOTION_FORWARD 8
37#define MACROBLOCK_QUANT 16
38#define DCT_TYPE_INTERLACED 32
39/* motion_type */
40#define MOTION_TYPE_SHIFT 6
41#define MC_FIELD 1
42#define MC_FRAME 2
43#define MC_16X8 2
44#define MC_DMV 3
45
46/* picture structure */
47#define TOP_FIELD 1
48#define BOTTOM_FIELD 2
49#define FRAME_PICTURE 3
50
51/* picture coding type */
52#define I_TYPE 1
53#define P_TYPE 2
54#define B_TYPE 3
55#define D_TYPE 4
56
57typedef void mpeg2_mc_fct (uint8_t *, const uint8_t *, int, int);
58
59typedef struct
60{
61 uint8_t * ref[2][MPEG2_COMPONENTS];
62 uint8_t ** ref2[2];
63 int pmv[2][2];
64 int f_code[2];
65} motion_t;
66
67typedef void motion_parser_t(mpeg2_decoder_t * decoder,
68 motion_t * motion,
69 mpeg2_mc_fct * const * table);
70
71struct mpeg2_decoder_s
72{
73 /* first, state that carries information from one macroblock to the */
74 /* next inside a slice, and is never used outside of mpeg2_slice() */
75
76 /* bit parsing stuff */
77 uint32_t bitstream_buf; /* current 32 bit working set */
78 int bitstream_bits; /* used bits in working set */
79 const uint8_t * bitstream_ptr; /* buffer with stream data */
80
81 uint8_t * dest[MPEG2_COMPONENTS];
82
83 int offset;
84 int stride;
85 int uv_stride;
86 int slice_stride;
87 int slice_uv_stride;
88 int stride_frame;
89 unsigned int limit_x;
90 unsigned int limit_y_16;
91 unsigned int limit_y_8;
92 unsigned int limit_y;
93
94 /* Motion vectors */
95 /* The f_ and b_ correspond to the forward and backward motion */
96 /* predictors */
97 motion_t b_motion;
98 motion_t f_motion;
99 motion_parser_t * motion_parser[5];
100
101 /* predictor for DC coefficients in intra blocks */
102 int16_t dc_dct_pred[MPEG2_COMPONENTS];
103
104 /* DCT coefficients */
105 int16_t * DCTblock; /* put buffer separately to have it in IRAM */
106
107 uint8_t * picture_dest[MPEG2_COMPONENTS];
108 void (* convert) (void * convert_id, uint8_t * const * src,
109 unsigned int v_offset);
110 void * convert_id;
111
112 int dmv_offset;
113 unsigned int v_offset;
114
115 /* now non-slice-specific information */
116
117 /* sequence header stuff */
118 uint16_t * quantizer_matrix[4];
119 uint16_t (* chroma_quantizer[2])[64];
120 uint16_t quantizer_prescale[4][32][64];
121
122 /* The width and height of the picture snapped to macroblock units */
123 int width;
124 int height;
125 int vertical_position_extension;
126 int chroma_format;
127
128 /* picture header stuff */
129
130 /* what type of picture this is (I, P, B, D) */
131 int coding_type;
132
133 /* picture coding extension stuff */
134
135 /* quantization factor for intra dc coefficients */
136 int intra_dc_precision;
137 /* top/bottom/both fields */
138 int picture_structure;
139 /* bool to indicate all predictions are frame based */
140 int frame_pred_frame_dct;
141 /* bool to indicate whether intra blocks have motion vectors */
142 /* (for concealment) */
143 int concealment_motion_vectors;
144 /* bool to use different vlc tables */
145 int intra_vlc_format;
146 /* used for DMV MC */
147 int top_field_first;
148
149 /* stuff derived from bitstream */
150
151 /* pointer to the zigzag scan we're supposed to be using */
152 const uint8_t * scan;
153
154 int second_field;
155
156 int mpeg1;
157};
158
159typedef struct
160{
161 mpeg2_fbuf_t fbuf;
162} fbuf_alloc_t;
163
164struct mpeg2dec_s
165{
166 mpeg2_decoder_t decoder;
167
168 mpeg2_info_t info;
169
170 uint32_t shift;
171 int is_display_initialized;
172 mpeg2_state_t (* action) (struct mpeg2dec_s * mpeg2dec);
173 mpeg2_state_t state;
174 uint32_t ext_state;
175
176 /* allocated in init - gcc has problems allocating such big structures */
177 uint8_t * ATTR_ALIGN(4) chunk_buffer;
178 /* pointer to start of the current chunk */
179 uint8_t * chunk_start;
180 /* pointer to current position in chunk_buffer */
181 uint8_t * chunk_ptr;
182 /* last start code ? */
183 uint8_t code;
184
185 /* picture tags */
186 uint32_t tag_current, tag2_current, tag_previous, tag2_previous;
187 int num_tags;
188 int bytes_since_tag;
189
190 int first;
191 int alloc_index_user;
192 int alloc_index;
193 uint8_t first_decode_slice;
194 uint8_t nb_decode_slices;
195
196 unsigned int user_data_len;
197
198 mpeg2_sequence_t new_sequence;
199 mpeg2_sequence_t sequence;
200 mpeg2_gop_t new_gop;
201 mpeg2_gop_t gop;
202 mpeg2_picture_t new_picture;
203 mpeg2_picture_t pictures[4];
204 mpeg2_picture_t * picture;
205 /*const*/ mpeg2_fbuf_t * fbuf[3]; /* 0: current fbuf, 1-2: prediction fbufs */
206
207 fbuf_alloc_t fbuf_alloc[3];
208 int custom_fbuf;
209
210 uint8_t * yuv_buf[3][MPEG2_COMPONENTS];
211 int yuv_index;
212 mpeg2_convert_t * convert;
213 void * convert_arg;
214 unsigned int convert_id_size;
215 int convert_stride;
216 void (* convert_start) (void * id, const mpeg2_fbuf_t * fbuf,
217 const mpeg2_picture_t * picture,
218 const mpeg2_gop_t * gop);
219
220 uint8_t * buf_start;
221 uint8_t * buf_end;
222
223 int16_t display_offset_x, display_offset_y;
224
225 int copy_matrix;
226 int8_t q_scale_type, scaled[4];
227 uint8_t quantizer_matrix[4][64];
228 uint8_t new_quantizer_matrix[4][64];
229};
230
231/* decode.c */
232mpeg2_state_t mpeg2_seek_header (mpeg2dec_t * mpeg2dec);
233mpeg2_state_t mpeg2_parse_header (mpeg2dec_t * mpeg2dec);
234
235/* header.c */
236void mpeg2_header_state_init (mpeg2dec_t * mpeg2dec);
237void mpeg2_reset_info (mpeg2_info_t * info);
238int mpeg2_header_sequence (mpeg2dec_t * mpeg2dec);
239int mpeg2_header_gop (mpeg2dec_t * mpeg2dec);
240int mpeg2_header_picture (mpeg2dec_t * mpeg2dec);
241int mpeg2_header_extension (mpeg2dec_t * mpeg2dec);
242int mpeg2_header_user_data (mpeg2dec_t * mpeg2dec);
243void mpeg2_header_sequence_finalize (mpeg2dec_t * mpeg2dec);
244void mpeg2_header_gop_finalize (mpeg2dec_t * mpeg2dec);
245void mpeg2_header_picture_finalize (mpeg2dec_t * mpeg2dec);
246mpeg2_state_t mpeg2_header_slice_start (mpeg2dec_t * mpeg2dec);
247mpeg2_state_t mpeg2_header_end (mpeg2dec_t * mpeg2dec);
248void mpeg2_set_fbuf (mpeg2dec_t * mpeg2dec, int b_type);
249
250/* idct.c */
251void mpeg2_idct_init (void);
252void mpeg2_idct_copy(int16_t * block, uint8_t * dest,
253 const int stride);
254void mpeg2_idct_add(const int last, int16_t * block,
255 uint8_t * dest, const int stride);
256
257extern const uint8_t default_mpeg2_scan_norm[64];
258extern const uint8_t default_mpeg2_scan_alt[64];
259extern uint8_t mpeg2_scan_norm[64];
260extern uint8_t mpeg2_scan_alt[64];
261
262/* motion_comp.c */
263void mpeg2_mc_init (void);
264
265typedef struct
266{
267 mpeg2_mc_fct * put [8];
268 mpeg2_mc_fct * avg [8];
269} mpeg2_mc_t;
270
271extern const mpeg2_mc_t mpeg2_mc;
272
273#endif /* MPEG2_INTERNAL_H */
274
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 @@
1/* $Id$ */
2#ifndef MPEG2DEC_CONFIG_H
3#define MPEG2DEC_CONFIG_H
4
5#define ATTRIBUTE_ALIGNED_MAX 16
6
7#ifdef HAVE_LCD_COLOR
8#define MPEG2_COLOR 1
9#define MPEG2_COMPONENTS 3
10#else
11#define MPEG2_COLOR 0
12#define MPEG2_COMPONENTS 1
13#endif
14
15#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 @@
1/*
2 * slice.c
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 2003 Peter Gubanov <peter@elecard.net.ru>
5 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
6 *
7 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
8 * See http://libmpeg2.sourceforge.net/ for updates.
9 *
10 * mpeg2dec is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * mpeg2dec is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 * $Id$
25 * libmpeg2 sync history:
26 * 2008-07-01 - CVS revision 1.55
27 */
28
29#include "plugin.h"
30
31#include "mpeg2dec_config.h"
32
33#include "mpeg2.h"
34#include "attributes.h"
35#include "mpeg2_internal.h"
36
37#include "vlc.h"
38
39static inline int get_macroblock_modes (mpeg2_decoder_t * const decoder)
40{
41#define bit_buf (decoder->bitstream_buf)
42#define bits (decoder->bitstream_bits)
43#define bit_ptr (decoder->bitstream_ptr)
44
45 int macroblock_modes;
46 const MBtab * tab;
47
48 switch (decoder->coding_type)
49 {
50 case I_TYPE:
51 tab = MB_I + UBITS (bit_buf, 1);
52 DUMPBITS (bit_buf, bits, tab->len);
53 macroblock_modes = tab->modes;
54
55 if (!(decoder->frame_pred_frame_dct) &&
56 decoder->picture_structure == FRAME_PICTURE)
57 {
58 macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED;
59 DUMPBITS (bit_buf, bits, 1);
60 }
61
62 return macroblock_modes;
63
64 case P_TYPE:
65 tab = MB_P + UBITS (bit_buf, 5);
66 DUMPBITS (bit_buf, bits, tab->len);
67 macroblock_modes = tab->modes;
68
69 if (decoder->picture_structure != FRAME_PICTURE)
70 {
71 if (macroblock_modes & MACROBLOCK_MOTION_FORWARD)
72 {
73 macroblock_modes |= UBITS (bit_buf, 2) << MOTION_TYPE_SHIFT;
74 DUMPBITS (bit_buf, bits, 2);
75 }
76
77 return macroblock_modes | MACROBLOCK_MOTION_FORWARD;
78 }
79 else if (decoder->frame_pred_frame_dct)
80 {
81 if (macroblock_modes & MACROBLOCK_MOTION_FORWARD)
82 macroblock_modes |= MC_FRAME << MOTION_TYPE_SHIFT;
83
84 return macroblock_modes | MACROBLOCK_MOTION_FORWARD;
85 }
86 else
87 {
88 if (macroblock_modes & MACROBLOCK_MOTION_FORWARD)
89 {
90 macroblock_modes |= UBITS (bit_buf, 2) << MOTION_TYPE_SHIFT;
91 DUMPBITS (bit_buf, bits, 2);
92 }
93
94 if (macroblock_modes & (MACROBLOCK_INTRA | MACROBLOCK_PATTERN))
95 {
96 macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED;
97 DUMPBITS (bit_buf, bits, 1);
98 }
99
100 return macroblock_modes | MACROBLOCK_MOTION_FORWARD;
101 }
102
103 case B_TYPE:
104 tab = MB_B + UBITS (bit_buf, 6);
105 DUMPBITS (bit_buf, bits, tab->len);
106 macroblock_modes = tab->modes;
107
108 if (decoder->picture_structure != FRAME_PICTURE)
109 {
110 if (! (macroblock_modes & MACROBLOCK_INTRA))
111 {
112 macroblock_modes |= UBITS (bit_buf, 2) << MOTION_TYPE_SHIFT;
113 DUMPBITS (bit_buf, bits, 2);
114 }
115
116 return macroblock_modes;
117 }
118 else if (decoder->frame_pred_frame_dct)
119 {
120 /* if (! (macroblock_modes & MACROBLOCK_INTRA)) */
121 macroblock_modes |= MC_FRAME << MOTION_TYPE_SHIFT;
122 return macroblock_modes;
123 }
124 else
125 {
126 if (macroblock_modes & MACROBLOCK_INTRA)
127 goto intra;
128
129 macroblock_modes |= UBITS (bit_buf, 2) << MOTION_TYPE_SHIFT;
130 DUMPBITS (bit_buf, bits, 2);
131
132 if (macroblock_modes & (MACROBLOCK_INTRA | MACROBLOCK_PATTERN))
133 {
134 intra:
135 macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED;
136 DUMPBITS (bit_buf, bits, 1);
137 }
138 return macroblock_modes;
139 }
140
141 case D_TYPE:
142 DUMPBITS (bit_buf, bits, 1);
143 return MACROBLOCK_INTRA;
144
145 default:
146 return 0;
147 }
148#undef bit_buf
149#undef bits
150#undef bit_ptr
151}
152
153static inline void get_quantizer_scale (mpeg2_decoder_t * const decoder)
154{
155#define bit_buf (decoder->bitstream_buf)
156#define bits (decoder->bitstream_bits)
157#define bit_ptr (decoder->bitstream_ptr)
158
159 int quantizer_scale_code;
160
161 quantizer_scale_code = UBITS (bit_buf, 5);
162 DUMPBITS (bit_buf, bits, 5);
163
164 decoder->quantizer_matrix[0] =
165 decoder->quantizer_prescale[0][quantizer_scale_code];
166
167 decoder->quantizer_matrix[1] =
168 decoder->quantizer_prescale[1][quantizer_scale_code];
169
170 decoder->quantizer_matrix[2] =
171 decoder->chroma_quantizer[0][quantizer_scale_code];
172
173 decoder->quantizer_matrix[3] =
174 decoder->chroma_quantizer[1][quantizer_scale_code];
175#undef bit_buf
176#undef bits
177#undef bit_ptr
178}
179
180static inline int get_motion_delta (mpeg2_decoder_t * const decoder,
181 const int f_code)
182{
183#define bit_buf (decoder->bitstream_buf)
184#define bits (decoder->bitstream_bits)
185#define bit_ptr (decoder->bitstream_ptr)
186
187 int delta;
188 int sign;
189 const MVtab * tab;
190
191 if (bit_buf & 0x80000000)
192 {
193 DUMPBITS (bit_buf, bits, 1);
194 return 0;
195 }
196 else if (bit_buf >= 0x0c000000)
197 {
198 tab = MV_4 + UBITS (bit_buf, 4);
199 delta = (tab->delta << f_code) + 1;
200 bits += tab->len + f_code + 1;
201 bit_buf <<= tab->len;
202
203 sign = SBITS (bit_buf, 1);
204 bit_buf <<= 1;
205
206 if (f_code)
207 delta += UBITS (bit_buf, f_code);
208 bit_buf <<= f_code;
209
210 return (delta ^ sign) - sign;
211 }
212 else
213 {
214 tab = MV_10 + UBITS (bit_buf, 10);
215 delta = (tab->delta << f_code) + 1;
216 bits += tab->len + 1;
217 bit_buf <<= tab->len;
218
219 sign = SBITS (bit_buf, 1);
220 bit_buf <<= 1;
221
222 if (f_code)
223 {
224 NEEDBITS (bit_buf, bits, bit_ptr);
225 delta += UBITS (bit_buf, f_code);
226 DUMPBITS (bit_buf, bits, f_code);
227 }
228
229 return (delta ^ sign) - sign;
230
231 }
232#undef bit_buf
233#undef bits
234#undef bit_ptr
235}
236
237static inline int bound_motion_vector (const int vector, const int f_code)
238{
239 return ((int32_t)vector << (27 - f_code)) >> (27 - f_code);
240}
241
242static inline int get_dmv (mpeg2_decoder_t * const decoder)
243{
244#define bit_buf (decoder->bitstream_buf)
245#define bits (decoder->bitstream_bits)
246#define bit_ptr (decoder->bitstream_ptr)
247
248 const DMVtab * tab;
249
250 tab = DMV_2 + UBITS (bit_buf, 2);
251 DUMPBITS (bit_buf, bits, tab->len);
252 return tab->dmv;
253
254#undef bit_buf
255#undef bits
256#undef bit_ptr
257}
258
259static inline int get_coded_block_pattern (mpeg2_decoder_t * const decoder)
260{
261#define bit_buf (decoder->bitstream_buf)
262#define bits (decoder->bitstream_bits)
263#define bit_ptr (decoder->bitstream_ptr)
264
265 const CBPtab * tab;
266
267 NEEDBITS (bit_buf, bits, bit_ptr);
268
269 if (bit_buf >= 0x20000000)
270 {
271 tab = CBP_7 + (UBITS (bit_buf, 7) - 16);
272 DUMPBITS (bit_buf, bits, tab->len);
273 return tab->cbp;
274 }
275 else
276 {
277 tab = CBP_9 + UBITS (bit_buf, 9);
278 DUMPBITS (bit_buf, bits, tab->len);
279 return tab->cbp;
280 }
281
282#undef bit_buf
283#undef bits
284#undef bit_ptr
285}
286
287static inline int get_luma_dc_dct_diff (mpeg2_decoder_t * const decoder)
288{
289#define bit_buf (decoder->bitstream_buf)
290#define bits (decoder->bitstream_bits)
291#define bit_ptr (decoder->bitstream_ptr)
292
293 const DCtab * tab;
294 int size;
295 int dc_diff;
296
297 if (bit_buf < 0xf8000000)
298 {
299 tab = DC_lum_5 + UBITS (bit_buf, 5);
300 size = tab->size;
301
302 if (size)
303 {
304 bits += tab->len + size;
305 bit_buf <<= tab->len;
306 dc_diff =
307 UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size);
308 bit_buf <<= size;
309 return dc_diff << decoder->intra_dc_precision;
310 }
311 else
312 {
313 DUMPBITS (bit_buf, bits, 3);
314 return 0;
315 }
316 }
317 else
318 {
319 tab = DC_long + (UBITS (bit_buf, 9) - 0x1e0);
320 size = tab->size;
321 DUMPBITS (bit_buf, bits, tab->len);
322 NEEDBITS (bit_buf, bits, bit_ptr);
323 dc_diff = UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size);
324 DUMPBITS (bit_buf, bits, size);
325 return dc_diff << decoder->intra_dc_precision;
326 }
327
328#undef bit_buf
329#undef bits
330#undef bit_ptr
331}
332
333#if MPEG2_COLOR
334static inline int get_chroma_dc_dct_diff (mpeg2_decoder_t * const decoder)
335{
336#define bit_buf (decoder->bitstream_buf)
337#define bits (decoder->bitstream_bits)
338#define bit_ptr (decoder->bitstream_ptr)
339
340 const DCtab * tab;
341 int size;
342 int dc_diff;
343
344 if (bit_buf < 0xf8000000)
345 {
346 tab = DC_chrom_5 + UBITS (bit_buf, 5);
347 size = tab->size;
348
349 if (size)
350 {
351 bits += tab->len + size;
352 bit_buf <<= tab->len;
353 dc_diff =
354 UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size);
355 bit_buf <<= size;
356 return dc_diff << decoder->intra_dc_precision;
357 }
358 else
359 {
360 DUMPBITS (bit_buf, bits, 2);
361 return 0;
362 }
363 }
364 else
365 {
366 tab = DC_long + (UBITS (bit_buf, 10) - 0x3e0);
367 size = tab->size;
368 DUMPBITS (bit_buf, bits, tab->len + 1);
369 NEEDBITS (bit_buf, bits, bit_ptr);
370 dc_diff = UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size);
371 DUMPBITS (bit_buf, bits, size);
372 return dc_diff << decoder->intra_dc_precision;
373 }
374
375#undef bit_buf
376#undef bits
377#undef bit_ptr
378}
379#endif /* MPEG2_COLOR */
380
381#define SATURATE(val) \
382 do { \
383 val <<= 4; \
384 if (unlikely (val != (int16_t) val)) \
385 val = (SBITS (val, 1) ^ 2047) << 4; \
386 } while (0)
387
388static void get_intra_block_B14 (mpeg2_decoder_t * const decoder,
389 const uint16_t * const quant_matrix)
390{
391 uint32_t bit_buf = decoder->bitstream_buf;
392 int bits = decoder->bitstream_bits;
393 const uint8_t * bit_ptr = decoder->bitstream_ptr;
394 const uint8_t * const scan = decoder->scan;
395 int16_t * const dest = decoder->DCTblock;
396 int mismatch = ~dest[0];
397 int i = 0;
398 int j;
399 int val;
400 const DCTtab * tab;
401
402 NEEDBITS (bit_buf, bits, bit_ptr);
403
404 while (1)
405 {
406 if (bit_buf >= 0x28000000)
407 {
408 tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
409
410 i += tab->run;
411 if (i >= 64)
412 break; /* end of block */
413
414 normal_code:
415 j = scan[i];
416 bit_buf <<= tab->len;
417 bits += tab->len + 1;
418 val = (tab->level * quant_matrix[j]) >> 4;
419
420 /* if (bitstream_get (1)) val = -val; */
421 val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
422
423 SATURATE (val);
424 dest[j] = val;
425 mismatch ^= val;
426
427 bit_buf <<= 1;
428 NEEDBITS (bit_buf, bits, bit_ptr);
429
430 continue;
431 }
432 else if (bit_buf >= 0x04000000)
433 {
434 tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
435
436 i += tab->run;
437 if (i < 64)
438 goto normal_code;
439
440 /* escape code */
441
442 i += UBITS (bit_buf << 6, 6) - 64;
443 if (i >= 64)
444 break; /* illegal, check needed to avoid buffer overflow */
445
446 j = scan[i];
447
448 DUMPBITS (bit_buf, bits, 12);
449 NEEDBITS (bit_buf, bits, bit_ptr);
450 val = (SBITS (bit_buf, 12) * quant_matrix[j]) / 16;
451
452 SATURATE (val);
453 dest[j] = val;
454 mismatch ^= val;
455
456 DUMPBITS (bit_buf, bits, 12);
457 NEEDBITS (bit_buf, bits, bit_ptr);
458
459 continue;
460 }
461 else if (bit_buf >= 0x02000000)
462 {
463 tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
464 i += tab->run;
465 if (i < 64)
466 goto normal_code;
467 }
468 else if (bit_buf >= 0x00800000)
469 {
470 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
471 i += tab->run;
472 if (i < 64)
473 goto normal_code;
474 }
475 else if (bit_buf >= 0x00200000)
476 {
477 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
478 i += tab->run;
479 if (i < 64)
480 goto normal_code;
481 }
482 else
483 {
484 tab = DCT_16 + UBITS (bit_buf, 16);
485 bit_buf <<= 16;
486 GETWORD (bit_buf, bits + 16, bit_ptr);
487 i += tab->run;
488 if (i < 64)
489 goto normal_code;
490 }
491 break; /* illegal, check needed to avoid buffer overflow */
492 }
493
494 dest[63] ^= mismatch & 16;
495 DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
496 decoder->bitstream_buf = bit_buf;
497 decoder->bitstream_bits = bits;
498 decoder->bitstream_ptr = bit_ptr;
499}
500
501static void get_intra_block_B15 (mpeg2_decoder_t * const decoder,
502 const uint16_t * const quant_matrix)
503{
504 uint32_t bit_buf = decoder->bitstream_buf;
505 int bits = decoder->bitstream_bits;
506 const uint8_t * bit_ptr = decoder->bitstream_ptr;
507 const uint8_t * const scan = decoder->scan;
508 int16_t * const dest = decoder->DCTblock;
509 int mismatch = ~dest[0];
510 int i = 0;
511 int j;
512 int val;
513 const DCTtab * tab;
514
515 NEEDBITS (bit_buf, bits, bit_ptr);
516
517 while (1)
518 {
519 if (bit_buf >= 0x04000000)
520 {
521 tab = DCT_B15_8 + (UBITS (bit_buf, 8) - 4);
522
523 i += tab->run;
524
525 if (i < 64)
526 {
527 normal_code:
528 j = scan[i];
529 bit_buf <<= tab->len;
530 bits += tab->len + 1;
531 val = (tab->level * quant_matrix[j]) >> 4;
532
533 /* if (bitstream_get (1)) val = -val; */
534 val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
535
536 SATURATE (val);
537 dest[j] = val;
538 mismatch ^= val;
539
540 bit_buf <<= 1;
541 NEEDBITS (bit_buf, bits, bit_ptr);
542
543 continue;
544 }
545 else
546 {
547 /* end of block. I commented out this code because if we */
548 /* dont exit here we will still exit at the later test :) */
549
550 /* if (i >= 128) break; */ /* end of block */
551
552 /* escape code */
553
554 i += UBITS (bit_buf << 6, 6) - 64;
555 if (i >= 64)
556 break; /* illegal, check against buffer overflow */
557
558 j = scan[i];
559
560 DUMPBITS (bit_buf, bits, 12);
561 NEEDBITS (bit_buf, bits, bit_ptr);
562 val = (SBITS (bit_buf, 12) * quant_matrix[j]) / 16;
563
564 SATURATE (val);
565 dest[j] = val;
566 mismatch ^= val;
567
568 DUMPBITS (bit_buf, bits, 12);
569 NEEDBITS (bit_buf, bits, bit_ptr);
570
571 continue;
572 }
573 }
574 else if (bit_buf >= 0x02000000)
575 {
576 tab = DCT_B15_10 + (UBITS (bit_buf, 10) - 8);
577 i += tab->run;
578 if (i < 64)
579 goto normal_code;
580 }
581 else if (bit_buf >= 0x00800000)
582 {
583 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
584 i += tab->run;
585 if (i < 64)
586 goto normal_code;
587 }
588 else if (bit_buf >= 0x00200000)
589 {
590 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
591 i += tab->run;
592 if (i < 64)
593 goto normal_code;
594 }
595 else
596 {
597 tab = DCT_16 + UBITS (bit_buf, 16);
598 bit_buf <<= 16;
599 GETWORD (bit_buf, bits + 16, bit_ptr);
600 i += tab->run;
601 if (i < 64)
602 goto normal_code;
603 }
604 break; /* illegal, check needed to avoid buffer overflow */
605 }
606
607 dest[63] ^= mismatch & 16;
608 DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
609 decoder->bitstream_buf = bit_buf;
610 decoder->bitstream_bits = bits;
611 decoder->bitstream_ptr = bit_ptr;
612}
613
614static int get_non_intra_block (mpeg2_decoder_t * const decoder,
615 const uint16_t * const quant_matrix)
616{
617 uint32_t bit_buf = decoder->bitstream_buf;
618 int bits = decoder->bitstream_bits;
619 const uint8_t * bit_ptr = decoder->bitstream_ptr;
620 const uint8_t * const scan = decoder->scan;
621 int16_t * const dest = decoder->DCTblock;
622 int mismatch = -1;
623 int i = -1;
624 int j;
625 int val;
626 const DCTtab * tab;
627
628 NEEDBITS (bit_buf, bits, bit_ptr);
629
630 if (bit_buf >= 0x28000000)
631 {
632 tab = DCT_B14DC_5 + (UBITS (bit_buf, 5) - 5);
633 goto entry_1;
634 }
635 else
636 {
637 goto entry_2;
638 }
639
640 while (1)
641 {
642 if (bit_buf >= 0x28000000)
643 {
644 tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
645
646 entry_1:
647 i += tab->run;
648 if (i >= 64)
649 break; /* end of block */
650
651 normal_code:
652 j = scan[i];
653 bit_buf <<= tab->len;
654 bits += tab->len + 1;
655 val = ((2 * tab->level + 1) * quant_matrix[j]) >> 5;
656
657 /* if (bitstream_get (1)) val = -val; */
658 val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
659
660 SATURATE (val);
661 dest[j] = val;
662 mismatch ^= val;
663
664 bit_buf <<= 1;
665 NEEDBITS (bit_buf, bits, bit_ptr);
666
667 continue;
668 }
669
670 entry_2:
671 if (bit_buf >= 0x04000000)
672 {
673 tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
674
675 i += tab->run;
676 if (i < 64)
677 goto normal_code;
678
679 /* escape code */
680
681 i += UBITS (bit_buf << 6, 6) - 64;
682 if (i >= 64)
683 break; /* illegal, check needed to avoid buffer overflow */
684
685 j = scan[i];
686
687 DUMPBITS (bit_buf, bits, 12);
688 NEEDBITS (bit_buf, bits, bit_ptr);
689 val = 2 * (SBITS (bit_buf, 12) + SBITS (bit_buf, 1)) + 1;
690 val = (val * quant_matrix[j]) / 32;
691
692 SATURATE (val);
693 dest[j] = val;
694 mismatch ^= val;
695
696 DUMPBITS (bit_buf, bits, 12);
697 NEEDBITS (bit_buf, bits, bit_ptr);
698
699 continue;
700 }
701 else if (bit_buf >= 0x02000000)
702 {
703 tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
704 i += tab->run;
705 if (i < 64)
706 goto normal_code;
707 }
708 else if (bit_buf >= 0x00800000)
709 {
710 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
711 i += tab->run;
712 if (i < 64)
713 goto normal_code;
714 }
715 else if (bit_buf >= 0x00200000)
716 {
717 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
718 i += tab->run;
719 if (i < 64)
720 goto normal_code;
721 }
722 else
723 {
724 tab = DCT_16 + UBITS (bit_buf, 16);
725 bit_buf <<= 16;
726 GETWORD (bit_buf, bits + 16, bit_ptr);
727 i += tab->run;
728 if (i < 64)
729 goto normal_code;
730 }
731 break; /* illegal, check needed to avoid buffer overflow */
732 }
733
734 dest[63] ^= mismatch & 16;
735 DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
736 decoder->bitstream_buf = bit_buf;
737 decoder->bitstream_bits = bits;
738 decoder->bitstream_ptr = bit_ptr;
739 return i;
740}
741
742static void get_mpeg1_intra_block (mpeg2_decoder_t * const decoder)
743{
744 uint32_t bit_buf = decoder->bitstream_buf;
745 int bits = decoder->bitstream_bits;
746 const uint8_t * bit_ptr = decoder->bitstream_ptr;
747 const uint8_t * const scan = decoder->scan;
748 const uint16_t * const quant_matrix = decoder->quantizer_matrix[0];
749 int16_t * const dest = decoder->DCTblock;
750 int i = 0;
751 int j;
752 int val;
753 const DCTtab * tab;
754
755 NEEDBITS (bit_buf, bits, bit_ptr);
756
757 while (1)
758 {
759 if (bit_buf >= 0x28000000)
760 {
761 tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
762
763 i += tab->run;
764 if (i >= 64)
765 break; /* end of block */
766
767 normal_code:
768 j = scan[i];
769 bit_buf <<= tab->len;
770 bits += tab->len + 1;
771 val = (tab->level * quant_matrix[j]) >> 4;
772
773 /* oddification */
774 val = (val - 1) | 1;
775
776 /* if (bitstream_get (1)) val = -val; */
777 val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
778
779 SATURATE (val);
780 dest[j] = val;
781
782 bit_buf <<= 1;
783 NEEDBITS (bit_buf, bits, bit_ptr);
784
785 continue;
786 }
787 else if (bit_buf >= 0x04000000)
788 {
789 tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
790
791 i += tab->run;
792 if (i < 64)
793 goto normal_code;
794
795 /* escape code */
796
797 i += UBITS (bit_buf << 6, 6) - 64;
798 if (i >= 64)
799 break; /* illegal, check needed to avoid buffer overflow */
800
801 j = scan[i];
802
803 DUMPBITS (bit_buf, bits, 12);
804 NEEDBITS (bit_buf, bits, bit_ptr);
805 val = SBITS (bit_buf, 8);
806
807 if (! (val & 0x7f))
808 {
809 DUMPBITS (bit_buf, bits, 8);
810 val = UBITS (bit_buf, 8) + 2 * val;
811 }
812
813 val = (val * quant_matrix[j]) / 16;
814
815 /* oddification */
816 val = (val + ~SBITS (val, 1)) | 1;
817
818 SATURATE (val);
819 dest[j] = val;
820
821 DUMPBITS (bit_buf, bits, 8);
822 NEEDBITS (bit_buf, bits, bit_ptr);
823
824 continue;
825 }
826 else if (bit_buf >= 0x02000000)
827 {
828 tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
829 i += tab->run;
830 if (i < 64)
831 goto normal_code;
832 }
833 else if (bit_buf >= 0x00800000)
834 {
835 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
836 i += tab->run;
837 if (i < 64)
838 goto normal_code;
839 }
840 else if (bit_buf >= 0x00200000)
841 {
842 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
843 i += tab->run;
844 if (i < 64)
845 goto normal_code;
846 }
847 else
848 {
849 tab = DCT_16 + UBITS (bit_buf, 16);
850 bit_buf <<= 16;
851 GETWORD (bit_buf, bits + 16, bit_ptr);
852 i += tab->run;
853 if (i < 64)
854 goto normal_code;
855 }
856 break; /* illegal, check needed to avoid buffer overflow */
857 }
858
859 DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
860 decoder->bitstream_buf = bit_buf;
861 decoder->bitstream_bits = bits;
862 decoder->bitstream_ptr = bit_ptr;
863}
864
865static int get_mpeg1_non_intra_block (mpeg2_decoder_t * const decoder)
866{
867 uint32_t bit_buf = decoder->bitstream_buf;
868 int bits = decoder->bitstream_bits;
869 const uint8_t * bit_ptr = decoder->bitstream_ptr;
870 const uint8_t * const scan = decoder->scan;
871 const uint16_t * const quant_matrix = decoder->quantizer_matrix[1];
872 int16_t * const dest = decoder->DCTblock;
873 int i = -1;
874 int j;
875 int val;
876 const DCTtab * tab;
877
878 NEEDBITS (bit_buf, bits, bit_ptr);
879 if (bit_buf >= 0x28000000)
880 {
881 tab = DCT_B14DC_5 + (UBITS (bit_buf, 5) - 5);
882 goto entry_1;
883 }
884 else
885 {
886 goto entry_2;
887 }
888
889 while (1)
890 {
891 if (bit_buf >= 0x28000000)
892 {
893 tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
894
895 entry_1:
896 i += tab->run;
897 if (i >= 64)
898 break; /* end of block */
899
900 normal_code:
901 j = scan[i];
902 bit_buf <<= tab->len;
903 bits += tab->len + 1;
904 val = ((2 * tab->level + 1) * quant_matrix[j]) >> 5;
905
906 /* oddification */
907 val = (val - 1) | 1;
908
909 /* if (bitstream_get (1)) val = -val; */
910 val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1);
911
912 SATURATE (val);
913 dest[j] = val;
914
915 bit_buf <<= 1;
916 NEEDBITS (bit_buf, bits, bit_ptr);
917
918 continue;
919 }
920
921 entry_2:
922 if (bit_buf >= 0x04000000)
923 {
924 tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
925
926 i += tab->run;
927 if (i < 64)
928 goto normal_code;
929
930 /* escape code */
931
932 i += UBITS (bit_buf << 6, 6) - 64;
933 if (i >= 64)
934 break; /* illegal, check needed to avoid buffer overflow */
935
936 j = scan[i];
937
938 DUMPBITS (bit_buf, bits, 12);
939 NEEDBITS (bit_buf, bits, bit_ptr);
940 val = SBITS (bit_buf, 8);
941
942 if (! (val & 0x7f))
943 {
944 DUMPBITS (bit_buf, bits, 8);
945 val = UBITS (bit_buf, 8) + 2 * val;
946 }
947
948 val = 2 * (val + SBITS (val, 1)) + 1;
949 val = (val * quant_matrix[j]) / 32;
950
951 /* oddification */
952 val = (val + ~SBITS (val, 1)) | 1;
953
954 SATURATE (val);
955 dest[j] = val;
956
957 DUMPBITS (bit_buf, bits, 8);
958 NEEDBITS (bit_buf, bits, bit_ptr);
959
960 continue;
961
962 }
963 else if (bit_buf >= 0x02000000)
964 {
965 tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
966 i += tab->run;
967 if (i < 64)
968 goto normal_code;
969 }
970 else if (bit_buf >= 0x00800000)
971 {
972 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
973 i += tab->run;
974 if (i < 64)
975 goto normal_code;
976 }
977 else if (bit_buf >= 0x00200000)
978 {
979 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
980 i += tab->run;
981 if (i < 64)
982 goto normal_code;
983 }
984 else
985 {
986 tab = DCT_16 + UBITS (bit_buf, 16);
987 bit_buf <<= 16;
988 GETWORD (bit_buf, bits + 16, bit_ptr);
989 i += tab->run;
990 if (i < 64)
991 goto normal_code;
992 }
993 break; /* illegal, check needed to avoid buffer overflow */
994 }
995
996 DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */
997 decoder->bitstream_buf = bit_buf;
998 decoder->bitstream_bits = bits;
999 decoder->bitstream_ptr = bit_ptr;
1000 return i;
1001}
1002
1003static inline void slice_intra_DCT (mpeg2_decoder_t * const decoder,
1004 const int cc,
1005 uint8_t * const dest, const int stride)
1006{
1007#define bit_buf (decoder->bitstream_buf)
1008#define bits (decoder->bitstream_bits)
1009#define bit_ptr (decoder->bitstream_ptr)
1010
1011 NEEDBITS (bit_buf, bits, bit_ptr);
1012 /* Get the intra DC coefficient and inverse quantize it */
1013 if (cc == 0)
1014 {
1015 decoder->dc_dct_pred[0] += get_luma_dc_dct_diff (decoder);
1016 decoder->DCTblock[0] = decoder->dc_dct_pred[0];
1017
1018 }
1019#if MPEG2_COLOR
1020 else
1021 {
1022 decoder->dc_dct_pred[cc] += get_chroma_dc_dct_diff (decoder);
1023 decoder->DCTblock[0] = decoder->dc_dct_pred[cc];
1024 }
1025#endif
1026
1027 if (decoder->mpeg1)
1028 {
1029 if (decoder->coding_type != D_TYPE)
1030 get_mpeg1_intra_block (decoder);
1031 }
1032 else if (decoder->intra_vlc_format)
1033 {
1034 get_intra_block_B15 (decoder, decoder->quantizer_matrix[cc ? 2 : 0]);
1035 }
1036 else
1037 {
1038 get_intra_block_B14 (decoder, decoder->quantizer_matrix[cc ? 2 : 0]);
1039 }
1040
1041 mpeg2_idct_copy (decoder->DCTblock, dest, stride);
1042
1043#undef bit_buf
1044#undef bits
1045#undef bit_ptr
1046}
1047
1048static inline void slice_non_intra_DCT (mpeg2_decoder_t * const decoder,
1049 const int cc,
1050 uint8_t * const dest, const int stride)
1051{
1052 int last;
1053
1054 if (decoder->mpeg1)
1055 {
1056 last = get_mpeg1_non_intra_block (decoder);
1057 }
1058 else
1059 {
1060 last = get_non_intra_block (decoder,
1061 decoder->quantizer_matrix[cc ? 3 : 1]);
1062 }
1063
1064 mpeg2_idct_add (last, decoder->DCTblock, dest, stride);
1065}
1066
1067#if !MPEG2_COLOR
1068static void skip_mpeg1_intra_block (mpeg2_decoder_t * const decoder)
1069{
1070 uint32_t bit_buf = decoder->bitstream_buf;
1071 int bits = decoder->bitstream_bits;
1072 const uint8_t * bit_ptr = decoder->bitstream_ptr;
1073 int i = 0;
1074 const DCTtab * tab;
1075
1076 NEEDBITS (bit_buf, bits, bit_ptr);
1077
1078 while (1)
1079 {
1080 if (bit_buf >= 0x28000000)
1081 {
1082 tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
1083
1084 i += tab->run;
1085 if (i >= 64)
1086 break; /* end of block */
1087
1088 normal_code:
1089 bit_buf <<= tab->len + 1;
1090 bits += tab->len + 1;
1091 NEEDBITS (bit_buf, bits, bit_ptr);
1092 continue;
1093 }
1094 else if (bit_buf >= 0x04000000)
1095 {
1096 tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
1097
1098 i += tab->run;
1099 if (i < 64)
1100 goto normal_code;
1101
1102 /* escape code */
1103
1104 i += UBITS (bit_buf << 6, 6) - 64;
1105 if (i >= 64)
1106 break; /* illegal, check needed to avoid buffer overflow */
1107
1108 DUMPBITS (bit_buf, bits, 12);
1109 NEEDBITS (bit_buf, bits, bit_ptr);
1110
1111 if (!(SBITS (bit_buf, 8) & 0x7f))
1112 DUMPBITS (bit_buf, bits, 8);
1113
1114 DUMPBITS (bit_buf, bits, 8);
1115 NEEDBITS (bit_buf, bits, bit_ptr);
1116
1117 continue;
1118 }
1119 else if (bit_buf >= 0x02000000)
1120 {
1121 tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
1122 i += tab->run;
1123 if (i < 64)
1124 goto normal_code;
1125 }
1126 else if (bit_buf >= 0x00800000)
1127 {
1128 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
1129 i += tab->run;
1130 if (i < 64)
1131 goto normal_code;
1132 }
1133 else if (bit_buf >= 0x00200000)
1134 {
1135 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
1136 i += tab->run;
1137 if (i < 64)
1138 goto normal_code;
1139 }
1140 else
1141 {
1142 tab = DCT_16 + UBITS (bit_buf, 16);
1143 bit_buf <<= 16;
1144 GETWORD (bit_buf, bits + 16, bit_ptr);
1145 i += tab->run;
1146 if (i < 64)
1147 goto normal_code;
1148 }
1149 break; /* illegal, check needed to avoid buffer overflow */
1150 }
1151
1152 DUMPBITS (bit_buf, bits, 2); /* dump end of block code */
1153 decoder->bitstream_buf = bit_buf;
1154 decoder->bitstream_bits = bits;
1155 decoder->bitstream_ptr = bit_ptr;
1156}
1157
1158static void skip_intra_block_B14 (mpeg2_decoder_t * const decoder)
1159{
1160 uint32_t bit_buf = decoder->bitstream_buf;
1161 int bits = decoder->bitstream_bits;
1162 const uint8_t * bit_ptr = decoder->bitstream_ptr;
1163 int i = 0;
1164 const DCTtab * tab;
1165
1166 NEEDBITS (bit_buf, bits, bit_ptr);
1167
1168 while (1)
1169 {
1170 if (bit_buf >= 0x28000000)
1171 {
1172 tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
1173
1174 i += tab->run;
1175 if (i >= 64)
1176 break; /* end of block */
1177
1178 normal_code:
1179 bit_buf <<= tab->len + 1;
1180 bits += tab->len + 1;
1181 NEEDBITS (bit_buf, bits, bit_ptr);
1182 continue;
1183 }
1184 else if (bit_buf >= 0x04000000)
1185 {
1186 tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
1187
1188 i += tab->run;
1189 if (i < 64)
1190 goto normal_code;
1191
1192 /* escape code */
1193
1194 i += UBITS (bit_buf << 6, 6) - 64;
1195 if (i >= 64)
1196 break; /* illegal, check needed to avoid buffer overflow */
1197
1198 DUMPBITS (bit_buf, bits, 12); /* Can't dump more than 16 atm */
1199 NEEDBITS (bit_buf, bits, bit_ptr);
1200 DUMPBITS (bit_buf, bits, 12);
1201 NEEDBITS (bit_buf, bits, bit_ptr);
1202 continue;
1203 }
1204 else if (bit_buf >= 0x02000000)
1205 {
1206 tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
1207 i += tab->run;
1208 if (i < 64)
1209 goto normal_code;
1210 }
1211 else if (bit_buf >= 0x00800000)
1212 {
1213 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
1214 i += tab->run;
1215 if (i < 64)
1216 goto normal_code;
1217 }
1218 else if (bit_buf >= 0x00200000)
1219 {
1220 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
1221 i += tab->run;
1222 if (i < 64)
1223 goto normal_code;
1224 }
1225 else
1226 {
1227 tab = DCT_16 + UBITS (bit_buf, 16);
1228 bit_buf <<= 16;
1229 GETWORD (bit_buf, bits + 16, bit_ptr);
1230 i += tab->run;
1231 if (i < 64)
1232 goto normal_code;
1233 }
1234 break; /* illegal, check needed to avoid buffer overflow */
1235 }
1236
1237 DUMPBITS (bit_buf, bits, 2); /* dump end of block code */
1238 decoder->bitstream_buf = bit_buf;
1239 decoder->bitstream_bits = bits;
1240 decoder->bitstream_ptr = bit_ptr;
1241}
1242
1243static void skip_intra_block_B15 (mpeg2_decoder_t * const decoder)
1244{
1245 uint32_t bit_buf = decoder->bitstream_buf;
1246 int bits = decoder->bitstream_bits;
1247 const uint8_t * bit_ptr = decoder->bitstream_ptr;
1248 int i = 0;
1249 const DCTtab * tab;
1250
1251 NEEDBITS (bit_buf, bits, bit_ptr);
1252
1253 while (1)
1254 {
1255 if (bit_buf >= 0x04000000)
1256 {
1257 tab = DCT_B15_8 + (UBITS (bit_buf, 8) - 4);
1258
1259 i += tab->run;
1260
1261 if (i < 64)
1262 {
1263 normal_code:
1264 bit_buf <<= tab->len + 1;
1265 bits += tab->len + 1;
1266 NEEDBITS (bit_buf, bits, bit_ptr);
1267 continue;
1268 }
1269 else
1270 {
1271 /* end of block. I commented out this code because if we */
1272 /* dont exit here we will still exit at the later test :) */
1273
1274 /* if (i >= 128) break; */ /* end of block */
1275
1276 /* escape code */
1277
1278 i += UBITS (bit_buf << 6, 6) - 64;
1279 if (i >= 64)
1280 break; /* illegal, check against buffer overflow */
1281
1282 DUMPBITS (bit_buf, bits, 12); /* Can't dump more than 16 atm */
1283 NEEDBITS (bit_buf, bits, bit_ptr);
1284 DUMPBITS (bit_buf, bits, 12);
1285 NEEDBITS (bit_buf, bits, bit_ptr);
1286 continue;
1287 }
1288 }
1289 else if (bit_buf >= 0x02000000)
1290 {
1291 tab = DCT_B15_10 + (UBITS (bit_buf, 10) - 8);
1292 i += tab->run;
1293 if (i < 64)
1294 goto normal_code;
1295 }
1296 else if (bit_buf >= 0x00800000)
1297 {
1298 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
1299 i += tab->run;
1300 if (i < 64)
1301 goto normal_code;
1302 }
1303 else if (bit_buf >= 0x00200000)
1304 {
1305 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
1306 i += tab->run;
1307 if (i < 64)
1308 goto normal_code;
1309 }
1310 else
1311 {
1312 tab = DCT_16 + UBITS (bit_buf, 16);
1313 bit_buf <<= 16;
1314 GETWORD (bit_buf, bits + 16, bit_ptr);
1315 i += tab->run;
1316 if (i < 64)
1317 goto normal_code;
1318 }
1319 break; /* illegal, check needed to avoid buffer overflow */
1320 }
1321
1322 DUMPBITS (bit_buf, bits, 4); /* dump end of block code */
1323 decoder->bitstream_buf = bit_buf;
1324 decoder->bitstream_bits = bits;
1325 decoder->bitstream_ptr = bit_ptr;
1326}
1327
1328static void skip_non_intra_block (mpeg2_decoder_t * const decoder)
1329{
1330 uint32_t bit_buf = decoder->bitstream_buf;
1331 int bits = decoder->bitstream_bits;
1332 const uint8_t * bit_ptr = decoder->bitstream_ptr;
1333 int i = -1;
1334 const DCTtab * tab;
1335
1336 NEEDBITS (bit_buf, bits, bit_ptr);
1337
1338 if (bit_buf >= 0x28000000)
1339 {
1340 tab = DCT_B14DC_5 + (UBITS (bit_buf, 5) - 5);
1341 goto entry_1;
1342 }
1343 else
1344 {
1345 goto entry_2;
1346 }
1347
1348 while (1)
1349 {
1350 if (bit_buf >= 0x28000000)
1351 {
1352 tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5);
1353
1354 entry_1:
1355 i += tab->run;
1356 if (i >= 64)
1357 break; /* end of block */
1358
1359 normal_code:
1360 bit_buf <<= tab->len + 1;
1361 bits += tab->len + 1;
1362 NEEDBITS (bit_buf, bits, bit_ptr);
1363
1364 continue;
1365 }
1366
1367 entry_2:
1368 if (bit_buf >= 0x04000000)
1369 {
1370 tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4);
1371
1372 i += tab->run;
1373 if (i < 64)
1374 goto normal_code;
1375
1376 /* escape code */
1377
1378 i += UBITS (bit_buf << 6, 6) - 64;
1379 if (i >= 64)
1380 break; /* illegal, check needed to avoid buffer overflow */
1381
1382 if (decoder->mpeg1)
1383 {
1384 DUMPBITS (bit_buf, bits, 12);
1385 NEEDBITS (bit_buf, bits, bit_ptr);
1386
1387 if (!(SBITS (bit_buf, 8) & 0x7f))
1388 DUMPBITS (bit_buf, bits, 8);
1389
1390 DUMPBITS (bit_buf, bits, 8);
1391 }
1392 else
1393 {
1394 DUMPBITS (bit_buf, bits, 12);
1395 NEEDBITS (bit_buf, bits, bit_ptr);
1396 DUMPBITS (bit_buf, bits, 12);
1397 }
1398
1399 NEEDBITS (bit_buf, bits, bit_ptr);
1400 continue;
1401 }
1402 else if (bit_buf >= 0x02000000)
1403 {
1404 tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8);
1405 i += tab->run;
1406 if (i < 64)
1407 goto normal_code;
1408 }
1409 else if (bit_buf >= 0x00800000)
1410 {
1411 tab = DCT_13 + (UBITS (bit_buf, 13) - 16);
1412 i += tab->run;
1413 if (i < 64)
1414 goto normal_code;
1415 }
1416 else if (bit_buf >= 0x00200000)
1417 {
1418 tab = DCT_15 + (UBITS (bit_buf, 15) - 16);
1419 i += tab->run;
1420 if (i < 64)
1421 goto normal_code;
1422 }
1423 else
1424 {
1425 tab = DCT_16 + UBITS (bit_buf, 16);
1426 bit_buf <<= 16;
1427 GETWORD (bit_buf, bits + 16, bit_ptr);
1428 i += tab->run;
1429 if (i < 64)
1430 goto normal_code;
1431 }
1432 break; /* illegal, check needed to avoid buffer overflow */
1433 }
1434
1435 DUMPBITS (bit_buf, bits, 2); /* dump end of block code */
1436 decoder->bitstream_buf = bit_buf;
1437 decoder->bitstream_bits = bits;
1438 decoder->bitstream_ptr = bit_ptr;
1439}
1440
1441static void skip_chroma_dc_dct_diff (mpeg2_decoder_t * const decoder)
1442{
1443#define bit_buf (decoder->bitstream_buf)
1444#define bits (decoder->bitstream_bits)
1445#define bit_ptr (decoder->bitstream_ptr)
1446
1447 const DCtab * tab;
1448 int size;
1449
1450 if (bit_buf < 0xf8000000)
1451 {
1452 tab = DC_chrom_5 + UBITS (bit_buf, 5);
1453 size = tab->size;
1454
1455 if (size)
1456 {
1457 bits += tab->len + size;
1458 bit_buf <<= tab->len;
1459 bit_buf <<= size;
1460 }
1461 else
1462 {
1463 DUMPBITS (bit_buf, bits, 2);
1464 }
1465 }
1466 else
1467 {
1468 tab = DC_long + (UBITS (bit_buf, 10) - 0x3e0);
1469 size = tab->size;
1470 DUMPBITS (bit_buf, bits, tab->len + 1);
1471 NEEDBITS (bit_buf, bits, bit_ptr);
1472 DUMPBITS (bit_buf, bits, size);
1473 }
1474
1475#undef bit_buf
1476#undef bits
1477#undef bit_ptr
1478}
1479
1480static void skip_chroma_non_intra (mpeg2_decoder_t * const decoder,
1481 uint32_t coded_block_pattern)
1482{
1483 static const uint32_t cbp_mask[3] =
1484 {
1485 0x00000030,
1486 0xc0000030,
1487 0xfc000030,
1488 };
1489
1490 uint32_t cbp = coded_block_pattern &
1491 cbp_mask[MIN((unsigned)decoder->chroma_format, 2u)];
1492
1493 while (cbp)
1494 {
1495 skip_non_intra_block (decoder);
1496 cbp &= (cbp - 1);
1497 }
1498}
1499
1500static void skip_chroma_intra (mpeg2_decoder_t * const decoder)
1501{
1502#define bit_buf (decoder->bitstream_buf)
1503#define bits (decoder->bitstream_bits)
1504#define bit_ptr (decoder->bitstream_ptr)
1505 int i = 2 << decoder->chroma_format;
1506
1507 if ((unsigned)i > 8)
1508 i = 8;
1509
1510 while (i-- > 0)
1511 {
1512 NEEDBITS (bit_buf, bits, bit_ptr);
1513
1514 skip_chroma_dc_dct_diff (decoder);
1515
1516 if (decoder->mpeg1)
1517 {
1518 if (decoder->coding_type != D_TYPE)
1519 skip_mpeg1_intra_block (decoder);
1520 }
1521 else if (decoder->intra_vlc_format)
1522 {
1523 skip_intra_block_B15 (decoder);
1524 }
1525 else
1526 {
1527 skip_intra_block_B14 (decoder);
1528 }
1529 }
1530
1531 if (decoder->chroma_format == 0 && decoder->coding_type == D_TYPE)
1532 {
1533 NEEDBITS (bit_buf, bits, bit_ptr);
1534 DUMPBITS (bit_buf, bits, 1);
1535 }
1536
1537#undef bit_buf
1538#undef bits
1539#undef bit_ptr
1540}
1541#endif /* !MPEG2_COLOR */
1542
1543#define MOTION_420(table, ref, motion_x, motion_y, size, y) \
1544 pos_x = 2 * decoder->offset + motion_x; \
1545 pos_y = 2 * decoder->v_offset + motion_y + 2 * y; \
1546 \
1547 if (unlikely (pos_x > decoder->limit_x)) \
1548 { \
1549 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1550 motion_x = pos_x - 2 * decoder->offset; \
1551 } \
1552 \
1553 if (unlikely (pos_y > decoder->limit_y_ ## size)) \
1554 { \
1555 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y_ ## size; \
1556 motion_y = pos_y - 2 * decoder->v_offset - 2 * y; \
1557 } \
1558 \
1559 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1560 table[xy_half] (decoder->dest[0] + y * decoder->stride + decoder->offset, \
1561 ref[0] + (pos_x >> 1) + (pos_y >> 1) * decoder->stride, \
1562 decoder->stride, size); \
1563 \
1564 if (MPEG2_COLOR) \
1565 { \
1566 motion_x /= 2; \
1567 motion_y /= 2; \
1568 xy_half = ((motion_y & 1) << 1) | (motion_x & 1); \
1569 offset = ((decoder->offset + motion_x) >> 1) + \
1570 ((((decoder->v_offset + motion_y) >> 1) + y/2) * \
1571 decoder->uv_stride); \
1572 \
1573 table[4+xy_half] (decoder->dest[1] + y/2 * decoder->uv_stride + \
1574 (decoder->offset >> 1), ref[1] + offset, \
1575 decoder->uv_stride, size/2); \
1576 table[4+xy_half] (decoder->dest[2] + y/2 * decoder->uv_stride + \
1577 (decoder->offset >> 1), ref[2] + offset, \
1578 decoder->uv_stride, size/2); \
1579 }
1580
1581#define MOTION_FIELD_420(table, ref, motion_x, motion_y, \
1582 dest_field, op, src_field) \
1583 pos_x = 2 * decoder->offset + motion_x; \
1584 pos_y = decoder->v_offset + motion_y; \
1585 \
1586 if (unlikely (pos_x > decoder->limit_x)) \
1587 { \
1588 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1589 motion_x = pos_x - 2 * decoder->offset; \
1590 } \
1591 \
1592 if (unlikely (pos_y > decoder->limit_y)) \
1593 { \
1594 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
1595 motion_y = pos_y - decoder->v_offset; \
1596 } \
1597 \
1598 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1599 table[xy_half] (decoder->dest[0] + dest_field * decoder->stride + \
1600 decoder->offset, \
1601 (ref[0] + (pos_x >> 1) + \
1602 ((pos_y op) + src_field) * decoder->stride), \
1603 2 * decoder->stride, 8); \
1604 \
1605 if (MPEG2_COLOR) \
1606 { \
1607 motion_x /= 2; \
1608 motion_y /= 2; \
1609 xy_half = ((motion_y & 1) << 1) | (motion_x & 1); \
1610 offset = ((decoder->offset + motion_x) >> 1) + \
1611 (((decoder->v_offset >> 1) + (motion_y op) + src_field) * \
1612 decoder->uv_stride); \
1613 \
1614 table[4+xy_half] (decoder->dest[1] + dest_field * decoder->uv_stride + \
1615 (decoder->offset >> 1), ref[1] + offset, \
1616 2 * decoder->uv_stride, 4); \
1617 table[4+xy_half] (decoder->dest[2] + dest_field * decoder->uv_stride + \
1618 (decoder->offset >> 1), ref[2] + offset, \
1619 2 * decoder->uv_stride, 4); \
1620 }
1621
1622#define MOTION_DMV_420(table, ref, motion_x, motion_y) \
1623 pos_x = 2 * decoder->offset + motion_x; \
1624 pos_y = decoder->v_offset + motion_y; \
1625 \
1626 if (unlikely (pos_x > decoder->limit_x)) \
1627 { \
1628 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1629 motion_x = pos_x - 2 * decoder->offset; \
1630 } \
1631 \
1632 if (unlikely (pos_y > decoder->limit_y)) \
1633 { \
1634 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
1635 motion_y = pos_y - decoder->v_offset; \
1636 } \
1637 \
1638 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1639 offset = (pos_x >> 1) + (pos_y & ~1) * decoder->stride; \
1640 table[xy_half] (decoder->dest[0] + decoder->offset, \
1641 ref[0] + offset, 2 * decoder->stride, 8); \
1642 table[xy_half] (decoder->dest[0] + decoder->stride + decoder->offset, \
1643 ref[0] + decoder->stride + offset, \
1644 2 * decoder->stride, 8); \
1645 \
1646 if (MPEG2_COLOR) \
1647 { \
1648 motion_x /= 2; \
1649 motion_y /= 2; \
1650 xy_half = ((motion_y & 1) << 1) | (motion_x & 1); \
1651 offset = ((decoder->offset + motion_x) >> 1) + \
1652 (((decoder->v_offset >> 1) + (motion_y & ~1)) * \
1653 decoder->uv_stride); \
1654 \
1655 table[4+xy_half] (decoder->dest[1] + (decoder->offset >> 1), \
1656 ref[1] + offset, 2 * decoder->uv_stride, 4); \
1657 table[4+xy_half] (decoder->dest[1] + decoder->uv_stride + \
1658 (decoder->offset >> 1), \
1659 ref[1] + decoder->uv_stride + offset, \
1660 2 * decoder->uv_stride, 4); \
1661 table[4+xy_half] (decoder->dest[2] + (decoder->offset >> 1), \
1662 ref[2] + offset, 2 * decoder->uv_stride, 4); \
1663 table[4+xy_half] (decoder->dest[2] + decoder->uv_stride + \
1664 (decoder->offset >> 1), \
1665 ref[2] + decoder->uv_stride + offset, \
1666 2 * decoder->uv_stride, 4); \
1667 }
1668
1669#define MOTION_ZERO_420(table, ref) \
1670 table[0] (decoder->dest[0] + decoder->offset, \
1671 (ref[0] + decoder->offset + \
1672 decoder->v_offset * decoder->stride), decoder->stride, 16); \
1673 \
1674 if (MPEG2_COLOR) \
1675 { \
1676 offset = ((decoder->offset >> 1) + \
1677 (decoder->v_offset >> 1) * decoder->uv_stride); \
1678 \
1679 table[4] (decoder->dest[1] + (decoder->offset >> 1), \
1680 ref[1] + offset, decoder->uv_stride, 8); \
1681 table[4] (decoder->dest[2] + (decoder->offset >> 1), \
1682 ref[2] + offset, decoder->uv_stride, 8); \
1683 }
1684
1685#define MOTION_422(table, ref, motion_x, motion_y, size, y) \
1686 pos_x = 2 * decoder->offset + motion_x; \
1687 pos_y = 2 * decoder->v_offset + motion_y + 2 * y; \
1688 \
1689 if (unlikely (pos_x > decoder->limit_x)) \
1690 { \
1691 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1692 motion_x = pos_x - 2 * decoder->offset; \
1693 } \
1694 \
1695 if (unlikely (pos_y > decoder->limit_y_ ## size)) \
1696 { \
1697 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y_ ## size; \
1698 motion_y = pos_y - 2 * decoder->v_offset - 2 * y; \
1699 } \
1700 \
1701 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1702 offset = (pos_x >> 1) + (pos_y >> 1) * decoder->stride; \
1703 \
1704 table[xy_half] (decoder->dest[0] + y * decoder->stride + decoder->offset, \
1705 ref[0] + offset, decoder->stride, size); \
1706 \
1707 if (MPEG2_COLOR) \
1708 { \
1709 offset = (offset + (motion_x & (motion_x < 0))) >> 1; \
1710 motion_x /= 2; \
1711 xy_half = ((pos_y & 1) << 1) | (motion_x & 1); \
1712 \
1713 table[4+xy_half] (decoder->dest[1] + y * decoder->uv_stride + \
1714 (decoder->offset >> 1), ref[1] + offset, \
1715 decoder->uv_stride, size); \
1716 table[4+xy_half] (decoder->dest[2] + y * decoder->uv_stride + \
1717 (decoder->offset >> 1), ref[2] + offset, \
1718 decoder->uv_stride, size); \
1719 }
1720
1721#define MOTION_FIELD_422(table, ref, motion_x, motion_y, \
1722 dest_field, op, src_field) \
1723 pos_x = 2 * decoder->offset + motion_x; \
1724 pos_y = decoder->v_offset + motion_y; \
1725 \
1726 if (unlikely (pos_x > decoder->limit_x)) \
1727 { \
1728 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1729 motion_x = pos_x - 2 * decoder->offset; \
1730 } \
1731 \
1732 if (unlikely (pos_y > decoder->limit_y)) \
1733 { \
1734 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
1735 motion_y = pos_y - decoder->v_offset; \
1736 } \
1737 \
1738 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1739 offset = (pos_x >> 1) + ((pos_y op) + src_field) * decoder->stride; \
1740 \
1741 table[xy_half] (decoder->dest[0] + dest_field * decoder->stride + \
1742 decoder->offset, ref[0] + offset, \
1743 2 * decoder->stride, 8); \
1744 \
1745 if (MPEG2_COLOR) \
1746 { \
1747 offset = (offset + (motion_x & (motion_x < 0))) >> 1; \
1748 motion_x /= 2; \
1749 xy_half = ((pos_y & 1) << 1) | (motion_x & 1); \
1750 \
1751 table[4+xy_half] (decoder->dest[1] + dest_field * decoder->uv_stride + \
1752 (decoder->offset >> 1), ref[1] + offset, \
1753 2 * decoder->uv_stride, 8); \
1754 table[4+xy_half] (decoder->dest[2] + dest_field * decoder->uv_stride + \
1755 (decoder->offset >> 1), ref[2] + offset, \
1756 2 * decoder->uv_stride, 8); \
1757 }
1758
1759#define MOTION_DMV_422(table, ref, motion_x, motion_y) \
1760 pos_x = 2 * decoder->offset + motion_x; \
1761 pos_y = decoder->v_offset + motion_y; \
1762 \
1763 if (unlikely (pos_x > decoder->limit_x)) \
1764 { \
1765 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1766 motion_x = pos_x - 2 * decoder->offset; \
1767 } \
1768 \
1769 if (unlikely (pos_y > decoder->limit_y)) \
1770 { \
1771 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
1772 motion_y = pos_y - decoder->v_offset; \
1773 } \
1774 \
1775 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1776 offset = (pos_x >> 1) + (pos_y & ~1) * decoder->stride; \
1777 \
1778 table[xy_half] (decoder->dest[0] + decoder->offset, \
1779 ref[0] + offset, 2 * decoder->stride, 8); \
1780 table[xy_half] (decoder->dest[0] + decoder->stride + decoder->offset, \
1781 ref[0] + decoder->stride + offset, \
1782 2 * decoder->stride, 8); \
1783 \
1784 if (MPEG2_COLOR) \
1785 { \
1786 offset = (offset + (motion_x & (motion_x < 0))) >> 1; \
1787 motion_x /= 2; \
1788 xy_half = ((pos_y & 1) << 1) | (motion_x & 1); \
1789 \
1790 table[4+xy_half] (decoder->dest[1] + (decoder->offset >> 1), \
1791 ref[1] + offset, 2 * decoder->uv_stride, 8); \
1792 table[4+xy_half] (decoder->dest[1] + decoder->uv_stride + \
1793 (decoder->offset >> 1), \
1794 ref[1] + decoder->uv_stride + offset, \
1795 2 * decoder->uv_stride, 8); \
1796 table[4+xy_half] (decoder->dest[2] + (decoder->offset >> 1), \
1797 ref[2] + offset, 2 * decoder->uv_stride, 8); \
1798 table[4+xy_half] (decoder->dest[2] + decoder->uv_stride + \
1799 (decoder->offset >> 1), \
1800 ref[2] + decoder->uv_stride + offset, \
1801 2 * decoder->uv_stride, 8); \
1802 }
1803
1804#define MOTION_ZERO_422(table, ref) \
1805 offset = decoder->offset + decoder->v_offset * decoder->stride; \
1806 table[0] (decoder->dest[0] + decoder->offset, \
1807 ref[0] + offset, decoder->stride, 16); \
1808 \
1809 if (MPEG2_COLOR) \
1810 { \
1811 offset >>= 1; \
1812 table[4] (decoder->dest[1] + (decoder->offset >> 1), \
1813 ref[1] + offset, decoder->uv_stride, 16); \
1814 table[4] (decoder->dest[2] + (decoder->offset >> 1), \
1815 ref[2] + offset, decoder->uv_stride, 16); \
1816 }
1817
1818#define MOTION_444(table, ref, motion_x, motion_y, size, y) \
1819 pos_x = 2 * decoder->offset + motion_x; \
1820 pos_y = 2 * decoder->v_offset + motion_y + 2 * y; \
1821 \
1822 if (unlikely (pos_x > decoder->limit_x)) \
1823 { \
1824 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1825 motion_x = pos_x - 2 * decoder->offset; \
1826 } \
1827 \
1828 if (unlikely (pos_y > decoder->limit_y_ ## size)) \
1829 { \
1830 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y_ ## size; \
1831 motion_y = pos_y - 2 * decoder->v_offset - 2 * y; \
1832 } \
1833 \
1834 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1835 offset = (pos_x >> 1) + (pos_y >> 1) * decoder->stride; \
1836 \
1837 table[xy_half] (decoder->dest[0] + y * decoder->stride + decoder->offset, \
1838 ref[0] + offset, decoder->stride, size); \
1839 \
1840 if (MPEG2_COLOR) \
1841 { \
1842 table[xy_half] (decoder->dest[1] + y * decoder->stride + decoder->offset, \
1843 ref[1] + offset, decoder->stride, size); \
1844 table[xy_half] (decoder->dest[2] + y * decoder->stride + decoder->offset, \
1845 ref[2] + offset, decoder->stride, size); \
1846 }
1847
1848#define MOTION_FIELD_444(table, ref, motion_x, motion_y, \
1849 dest_field, op, src_field) \
1850 pos_x = 2 * decoder->offset + motion_x; \
1851 pos_y = decoder->v_offset + motion_y; \
1852 \
1853 if (unlikely (pos_x > decoder->limit_x)) \
1854 { \
1855 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1856 motion_x = pos_x - 2 * decoder->offset; \
1857 } \
1858 \
1859 if (unlikely (pos_y > decoder->limit_y)) \
1860 { \
1861 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
1862 motion_y = pos_y - decoder->v_offset; \
1863 } \
1864 \
1865 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1866 offset = (pos_x >> 1) + ((pos_y op) + src_field) * decoder->stride; \
1867 \
1868 table[xy_half] (decoder->dest[0] + dest_field * decoder->stride + \
1869 decoder->offset, ref[0] + offset, \
1870 2 * decoder->stride, 8); \
1871 \
1872 if (MPEG2_COLOR) \
1873 { \
1874 table[xy_half] (decoder->dest[1] + dest_field * decoder->stride + \
1875 decoder->offset, ref[1] + offset, \
1876 2 * decoder->stride, 8); \
1877 table[xy_half] (decoder->dest[2] + dest_field * decoder->stride + \
1878 decoder->offset, ref[2] + offset, \
1879 2 * decoder->stride, 8); \
1880 }
1881
1882#define MOTION_DMV_444(table, ref, motion_x, motion_y) \
1883 pos_x = 2 * decoder->offset + motion_x; \
1884 pos_y = decoder->v_offset + motion_y; \
1885 \
1886 if (unlikely (pos_x > decoder->limit_x)) \
1887 { \
1888 pos_x = ((int)pos_x < 0) ? 0 : decoder->limit_x; \
1889 motion_x = pos_x - 2 * decoder->offset; \
1890 } \
1891 \
1892 if (unlikely (pos_y > decoder->limit_y)) \
1893 { \
1894 pos_y = ((int)pos_y < 0) ? 0 : decoder->limit_y; \
1895 motion_y = pos_y - decoder->v_offset; \
1896 } \
1897 \
1898 xy_half = ((pos_y & 1) << 1) | (pos_x & 1); \
1899 offset = (pos_x >> 1) + (pos_y & ~1) * decoder->stride; \
1900 \
1901 table[xy_half] (decoder->dest[0] + decoder->offset, \
1902 ref[0] + offset, 2 * decoder->stride, 8); \
1903 table[xy_half] (decoder->dest[0] + decoder->stride + decoder->offset, \
1904 ref[0] + decoder->stride + offset, \
1905 2 * decoder->stride, 8); \
1906 \
1907 if (MPEG2_COLOR) \
1908 { \
1909 table[xy_half] (decoder->dest[1] + decoder->offset, \
1910 ref[1] + offset, 2 * decoder->stride, 8); \
1911 table[xy_half] (decoder->dest[1] + decoder->stride + decoder->offset, \
1912 ref[1] + decoder->stride + offset, \
1913 2 * decoder->stride, 8); \
1914 table[xy_half] (decoder->dest[2] + decoder->offset, \
1915 ref[2] + offset, 2 * decoder->stride, 8); \
1916 table[xy_half] (decoder->dest[2] + decoder->stride + decoder->offset, \
1917 ref[2] + decoder->stride + offset, \
1918 2 * decoder->stride, 8); \
1919 }
1920
1921#define MOTION_ZERO_444(table, ref) \
1922 offset = decoder->offset + decoder->v_offset * decoder->stride; \
1923 \
1924 table[0] (decoder->dest[0] + decoder->offset, \
1925 ref[0] + offset, decoder->stride, 16); \
1926 \
1927 if (MPEG2_COLOR) \
1928 { \
1929 table[4] (decoder->dest[1] + decoder->offset, \
1930 ref[1] + offset, decoder->stride, 16); \
1931 table[4] (decoder->dest[2] + decoder->offset, \
1932 ref[2] + offset, decoder->stride, 16); \
1933 }
1934
1935#define bit_buf (decoder->bitstream_buf)
1936#define bits (decoder->bitstream_bits)
1937#define bit_ptr (decoder->bitstream_ptr)
1938
1939static void motion_mp1 (mpeg2_decoder_t * const decoder,
1940 motion_t * const motion,
1941 mpeg2_mc_fct * const * const table)
1942{
1943 int motion_x, motion_y;
1944 unsigned int pos_x, pos_y, xy_half, offset;
1945
1946 NEEDBITS (bit_buf, bits, bit_ptr);
1947 motion_x = motion->pmv[0][0] +
1948 (get_motion_delta (decoder,
1949 motion->f_code[0]) << motion->f_code[1]);
1950 motion_x = bound_motion_vector (motion_x,
1951 motion->f_code[0] + motion->f_code[1]);
1952 motion->pmv[0][0] = motion_x;
1953
1954 NEEDBITS (bit_buf, bits, bit_ptr);
1955 motion_y = motion->pmv[0][1] +
1956 (get_motion_delta (decoder,
1957 motion->f_code[0]) << motion->f_code[1]);
1958 motion_y = bound_motion_vector (motion_y,
1959 motion->f_code[0] + motion->f_code[1]);
1960 motion->pmv[0][1] = motion_y;
1961
1962 MOTION_420 (table, motion->ref[0], motion_x, motion_y, 16, 0);
1963}
1964
1965#define MOTION_FUNCTIONS(FORMAT, MOTION, MOTION_FIELD, \
1966 MOTION_DMV, MOTION_ZERO) \
1967 \
1968static void motion_fr_frame_##FORMAT (mpeg2_decoder_t * const decoder, \
1969 motion_t * const motion, \
1970 mpeg2_mc_fct * const * const table) \
1971{ \
1972 int motion_x, motion_y; \
1973 unsigned int pos_x, pos_y, xy_half, offset; \
1974 \
1975 NEEDBITS (bit_buf, bits, bit_ptr); \
1976 motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
1977 motion->f_code[0]); \
1978 motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
1979 motion->pmv[1][0] = motion->pmv[0][0] = motion_x; \
1980 \
1981 NEEDBITS (bit_buf, bits, bit_ptr); \
1982 motion_y = motion->pmv[0][1] + get_motion_delta (decoder, \
1983 motion->f_code[1]); \
1984 motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
1985 motion->pmv[1][1] = motion->pmv[0][1] = motion_y; \
1986 \
1987 MOTION (table, motion->ref[0], motion_x, motion_y, 16, 0); \
1988} \
1989 \
1990static void motion_fr_field_##FORMAT (mpeg2_decoder_t * const decoder, \
1991 motion_t * const motion, \
1992 mpeg2_mc_fct * const * const table) \
1993{ \
1994 int motion_x, motion_y, field; \
1995 unsigned int pos_x, pos_y, xy_half, offset; \
1996 \
1997 NEEDBITS (bit_buf, bits, bit_ptr); \
1998 field = UBITS (bit_buf, 1); \
1999 DUMPBITS (bit_buf, bits, 1); \
2000 \
2001 motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
2002 motion->f_code[0]); \
2003 motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
2004 motion->pmv[0][0] = motion_x; \
2005 \
2006 NEEDBITS (bit_buf, bits, bit_ptr); \
2007 motion_y = ((motion->pmv[0][1] >> 1) + \
2008 get_motion_delta (decoder, motion->f_code[1])); \
2009 /* motion_y = bound_motion_vector (motion_y, motion->f_code[1]); */ \
2010 motion->pmv[0][1] = motion_y << 1; \
2011 \
2012 MOTION_FIELD (table, motion->ref[0], motion_x, motion_y, 0, & ~1, field); \
2013 \
2014 NEEDBITS (bit_buf, bits, bit_ptr); \
2015 field = UBITS (bit_buf, 1); \
2016 DUMPBITS (bit_buf, bits, 1); \
2017 \
2018 motion_x = motion->pmv[1][0] + get_motion_delta (decoder, \
2019 motion->f_code[0]); \
2020 motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
2021 motion->pmv[1][0] = motion_x; \
2022 \
2023 NEEDBITS (bit_buf, bits, bit_ptr); \
2024 motion_y = ((motion->pmv[1][1] >> 1) + \
2025 get_motion_delta (decoder, motion->f_code[1])); \
2026 /* motion_y = bound_motion_vector (motion_y, motion->f_code[1]); */ \
2027 motion->pmv[1][1] = motion_y << 1; \
2028 \
2029 MOTION_FIELD (table, motion->ref[0], motion_x, motion_y, 1, & ~1, field); \
2030} \
2031 \
2032static void motion_fr_dmv_##FORMAT (mpeg2_decoder_t * const decoder, \
2033 motion_t * const motion, \
2034 mpeg2_mc_fct * const * const table) \
2035{ \
2036 int motion_x, motion_y, dmv_x, dmv_y, m, other_x, other_y; \
2037 unsigned int pos_x, pos_y, xy_half, offset; \
2038 \
2039 (void)table; \
2040 NEEDBITS (bit_buf, bits, bit_ptr); \
2041 motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
2042 motion->f_code[0]); \
2043 motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
2044 motion->pmv[1][0] = motion->pmv[0][0] = motion_x; \
2045 NEEDBITS (bit_buf, bits, bit_ptr); \
2046 dmv_x = get_dmv (decoder); \
2047 \
2048 motion_y = ((motion->pmv[0][1] >> 1) + \
2049 get_motion_delta (decoder, motion->f_code[1])); \
2050 /* motion_y = bound_motion_vector (motion_y, motion->f_code[1]); */ \
2051 motion->pmv[1][1] = motion->pmv[0][1] = motion_y << 1; \
2052 dmv_y = get_dmv (decoder); \
2053 \
2054 m = decoder->top_field_first ? 1 : 3; \
2055 other_x = ((motion_x * m + (motion_x > 0)) >> 1) + dmv_x; \
2056 other_y = ((motion_y * m + (motion_y > 0)) >> 1) + dmv_y - 1; \
2057 MOTION_FIELD (mpeg2_mc.put, motion->ref[0], other_x, other_y, 0, | 1, 0); \
2058 \
2059 m = decoder->top_field_first ? 3 : 1; \
2060 other_x = ((motion_x * m + (motion_x > 0)) >> 1) + dmv_x; \
2061 other_y = ((motion_y * m + (motion_y > 0)) >> 1) + dmv_y + 1; \
2062 MOTION_FIELD (mpeg2_mc.put, motion->ref[0], other_x, other_y, 1, & ~1, 0);\
2063 \
2064 MOTION_DMV (mpeg2_mc.avg, motion->ref[0], motion_x, motion_y); \
2065} \
2066 \
2067static void motion_reuse_##FORMAT (mpeg2_decoder_t * const decoder, \
2068 motion_t * const motion, \
2069 mpeg2_mc_fct * const * const table) \
2070{ \
2071 int motion_x, motion_y; \
2072 unsigned int pos_x, pos_y, xy_half, offset; \
2073 \
2074 motion_x = motion->pmv[0][0]; \
2075 motion_y = motion->pmv[0][1]; \
2076 \
2077 MOTION (table, motion->ref[0], motion_x, motion_y, 16, 0); \
2078} \
2079 \
2080static void motion_zero_##FORMAT (mpeg2_decoder_t * const decoder, \
2081 motion_t * const motion, \
2082 mpeg2_mc_fct * const * const table) \
2083{ \
2084 unsigned int offset; \
2085 \
2086 motion->pmv[0][0] = motion->pmv[0][1] = 0; \
2087 motion->pmv[1][0] = motion->pmv[1][1] = 0; \
2088 \
2089 MOTION_ZERO (table, motion->ref[0]); \
2090} \
2091 \
2092static void motion_fi_field_##FORMAT (mpeg2_decoder_t * const decoder, \
2093 motion_t * const motion, \
2094 mpeg2_mc_fct * const * const table) \
2095{ \
2096 int motion_x, motion_y; \
2097 uint8_t ** ref_field; \
2098 unsigned int pos_x, pos_y, xy_half, offset; \
2099 \
2100 NEEDBITS (bit_buf, bits, bit_ptr); \
2101 ref_field = motion->ref2[UBITS (bit_buf, 1)]; \
2102 DUMPBITS (bit_buf, bits, 1); \
2103 \
2104 motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
2105 motion->f_code[0]); \
2106 motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
2107 motion->pmv[1][0] = motion->pmv[0][0] = motion_x; \
2108 \
2109 NEEDBITS (bit_buf, bits, bit_ptr); \
2110 motion_y = motion->pmv[0][1] + get_motion_delta (decoder, \
2111 motion->f_code[1]); \
2112 motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
2113 motion->pmv[1][1] = motion->pmv[0][1] = motion_y; \
2114 \
2115 MOTION (table, ref_field, motion_x, motion_y, 16, 0); \
2116} \
2117 \
2118static void motion_fi_16x8_##FORMAT (mpeg2_decoder_t * const decoder, \
2119 motion_t * const motion, \
2120 mpeg2_mc_fct * const * const table) \
2121{ \
2122 int motion_x, motion_y; \
2123 uint8_t ** ref_field; \
2124 unsigned int pos_x, pos_y, xy_half, offset; \
2125 \
2126 NEEDBITS (bit_buf, bits, bit_ptr); \
2127 ref_field = motion->ref2[UBITS (bit_buf, 1)]; \
2128 DUMPBITS (bit_buf, bits, 1); \
2129 \
2130 motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
2131 motion->f_code[0]); \
2132 motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
2133 motion->pmv[0][0] = motion_x; \
2134 \
2135 NEEDBITS (bit_buf, bits, bit_ptr); \
2136 motion_y = motion->pmv[0][1] + get_motion_delta (decoder, \
2137 motion->f_code[1]); \
2138 motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
2139 motion->pmv[0][1] = motion_y; \
2140 \
2141 MOTION (table, ref_field, motion_x, motion_y, 8, 0); \
2142 \
2143 NEEDBITS (bit_buf, bits, bit_ptr); \
2144 ref_field = motion->ref2[UBITS (bit_buf, 1)]; \
2145 DUMPBITS (bit_buf, bits, 1); \
2146 \
2147 motion_x = motion->pmv[1][0] + get_motion_delta (decoder, \
2148 motion->f_code[0]); \
2149 motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
2150 motion->pmv[1][0] = motion_x; \
2151 \
2152 NEEDBITS (bit_buf, bits, bit_ptr); \
2153 motion_y = motion->pmv[1][1] + get_motion_delta (decoder, \
2154 motion->f_code[1]); \
2155 motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
2156 motion->pmv[1][1] = motion_y; \
2157 \
2158 MOTION (table, ref_field, motion_x, motion_y, 8, 8); \
2159} \
2160 \
2161static void motion_fi_dmv_##FORMAT (mpeg2_decoder_t * const decoder, \
2162 motion_t * const motion, \
2163 mpeg2_mc_fct * const * const table) \
2164{ \
2165 int motion_x, motion_y, other_x, other_y; \
2166 unsigned int pos_x, pos_y, xy_half, offset; \
2167 \
2168 (void)table; \
2169 NEEDBITS (bit_buf, bits, bit_ptr); \
2170 motion_x = motion->pmv[0][0] + get_motion_delta (decoder, \
2171 motion->f_code[0]); \
2172 motion_x = bound_motion_vector (motion_x, motion->f_code[0]); \
2173 motion->pmv[1][0] = motion->pmv[0][0] = motion_x; \
2174 NEEDBITS (bit_buf, bits, bit_ptr); \
2175 other_x = ((motion_x + (motion_x > 0)) >> 1) + get_dmv (decoder); \
2176 \
2177 motion_y = motion->pmv[0][1] + get_motion_delta (decoder, \
2178 motion->f_code[1]); \
2179 motion_y = bound_motion_vector (motion_y, motion->f_code[1]); \
2180 motion->pmv[1][1] = motion->pmv[0][1] = motion_y; \
2181 other_y = (((motion_y + (motion_y > 0)) >> 1) + get_dmv (decoder) + \
2182 decoder->dmv_offset); \
2183 \
2184 MOTION (mpeg2_mc.put, motion->ref[0], motion_x, motion_y, 16, 0); \
2185 MOTION (mpeg2_mc.avg, motion->ref[1], other_x, other_y, 16, 0); \
2186} \
2187
2188MOTION_FUNCTIONS (420, MOTION_420, MOTION_FIELD_420, MOTION_DMV_420,
2189 MOTION_ZERO_420)
2190MOTION_FUNCTIONS (422, MOTION_422, MOTION_FIELD_422, MOTION_DMV_422,
2191 MOTION_ZERO_422)
2192MOTION_FUNCTIONS (444, MOTION_444, MOTION_FIELD_444, MOTION_DMV_444,
2193 MOTION_ZERO_444)
2194
2195/* like motion_frame, but parsing without actual motion compensation */
2196static void motion_fr_conceal (mpeg2_decoder_t * const decoder)
2197{
2198 int tmp;
2199
2200 NEEDBITS (bit_buf, bits, bit_ptr);
2201 tmp = (decoder->f_motion.pmv[0][0] +
2202 get_motion_delta (decoder, decoder->f_motion.f_code[0]));
2203 tmp = bound_motion_vector (tmp, decoder->f_motion.f_code[0]);
2204 decoder->f_motion.pmv[1][0] = decoder->f_motion.pmv[0][0] = tmp;
2205
2206 NEEDBITS (bit_buf, bits, bit_ptr);
2207 tmp = (decoder->f_motion.pmv[0][1] +
2208 get_motion_delta (decoder, decoder->f_motion.f_code[1]));
2209 tmp = bound_motion_vector (tmp, decoder->f_motion.f_code[1]);
2210 decoder->f_motion.pmv[1][1] = decoder->f_motion.pmv[0][1] = tmp;
2211
2212 DUMPBITS (bit_buf, bits, 1); /* remove marker_bit */
2213}
2214
2215static void motion_fi_conceal (mpeg2_decoder_t * const decoder)
2216{
2217 int tmp;
2218
2219 NEEDBITS (bit_buf, bits, bit_ptr);
2220 DUMPBITS (bit_buf, bits, 1); /* remove field_select */
2221
2222 tmp = decoder->f_motion.pmv[0][0] +
2223 get_motion_delta (decoder, decoder->f_motion.f_code[0]);
2224 tmp = bound_motion_vector (tmp, decoder->f_motion.f_code[0]);
2225
2226 decoder->f_motion.pmv[1][0] =
2227 decoder->f_motion.pmv[0][0] = tmp;
2228
2229 NEEDBITS (bit_buf, bits, bit_ptr);
2230
2231 tmp = (decoder->f_motion.pmv[0][1] +
2232 get_motion_delta (decoder, decoder->f_motion.f_code[1]));
2233 tmp = bound_motion_vector (tmp, decoder->f_motion.f_code[1]);
2234
2235 decoder->f_motion.pmv[1][1] =
2236 decoder->f_motion.pmv[0][1] = tmp;
2237
2238 DUMPBITS (bit_buf, bits, 1); /* remove marker_bit */
2239}
2240
2241#undef bit_buf
2242#undef bits
2243#undef bit_ptr
2244
2245#define MOTION_CALL(routine, direction) \
2246do { \
2247 if ((direction) & MACROBLOCK_MOTION_FORWARD) \
2248 routine (decoder, &decoder->f_motion, mpeg2_mc.put); \
2249 \
2250 if ((direction) & MACROBLOCK_MOTION_BACKWARD) \
2251 { \
2252 routine (decoder, &decoder->b_motion, \
2253 ((direction) & MACROBLOCK_MOTION_FORWARD ? \
2254 mpeg2_mc.avg : mpeg2_mc.put)); \
2255 } \
2256} while (0)
2257
2258#define NEXT_MACROBLOCK \
2259do { \
2260 decoder->offset += 16; \
2261 \
2262 if (decoder->offset == decoder->width) \
2263 { \
2264 do { /* just so we can use the break statement */ \
2265 if (decoder->convert) \
2266 { \
2267 decoder->convert (decoder->convert_id, decoder->dest, \
2268 decoder->v_offset); \
2269 if (decoder->coding_type == B_TYPE) \
2270 break; \
2271 } \
2272 \
2273 decoder->dest[0] += decoder->slice_stride; \
2274 if (MPEG2_COLOR) \
2275 { \
2276 decoder->dest[1] += decoder->slice_uv_stride; \
2277 decoder->dest[2] += decoder->slice_uv_stride; \
2278 } \
2279 } while (0); \
2280 \
2281 decoder->v_offset += 16; \
2282 \
2283 if (decoder->v_offset > decoder->limit_y) \
2284 return; \
2285 \
2286 decoder->offset = 0; \
2287 } \
2288} while (0)
2289
2290void mpeg2_init_fbuf (mpeg2_decoder_t * decoder,
2291 uint8_t * current_fbuf[MPEG2_COMPONENTS],
2292 uint8_t * forward_fbuf[MPEG2_COMPONENTS],
2293 uint8_t * backward_fbuf[MPEG2_COMPONENTS])
2294{
2295 int offset, stride, height, bottom_field;
2296
2297 stride = decoder->stride_frame;
2298 bottom_field = (decoder->picture_structure == BOTTOM_FIELD);
2299 offset = bottom_field ? stride : 0;
2300 height = decoder->height;
2301
2302 decoder->picture_dest[0] = current_fbuf[0] + offset;
2303#if MPEG2_COLOR
2304 decoder->picture_dest[1] = current_fbuf[1] + (offset >> 1);
2305 decoder->picture_dest[2] = current_fbuf[2] + (offset >> 1);
2306#endif
2307
2308 decoder->f_motion.ref[0][0] = forward_fbuf[0] + offset;
2309#if MPEG2_COLOR
2310 decoder->f_motion.ref[0][1] = forward_fbuf[1] + (offset >> 1);
2311 decoder->f_motion.ref[0][2] = forward_fbuf[2] + (offset >> 1);
2312#endif
2313
2314 decoder->b_motion.ref[0][0] = backward_fbuf[0] + offset;
2315#if MPEG2_COLOR
2316 decoder->b_motion.ref[0][1] = backward_fbuf[1] + (offset >> 1);
2317 decoder->b_motion.ref[0][2] = backward_fbuf[2] + (offset >> 1);
2318#endif
2319
2320 if (decoder->picture_structure != FRAME_PICTURE)
2321 {
2322 decoder->dmv_offset = bottom_field ? 1 : -1;
2323 decoder->f_motion.ref2[0] = decoder->f_motion.ref[bottom_field];
2324 decoder->f_motion.ref2[1] = decoder->f_motion.ref[!bottom_field];
2325 decoder->b_motion.ref2[0] = decoder->b_motion.ref[bottom_field];
2326 decoder->b_motion.ref2[1] = decoder->b_motion.ref[!bottom_field];
2327 offset = stride - offset;
2328
2329 if (decoder->second_field && (decoder->coding_type != B_TYPE))
2330 forward_fbuf = current_fbuf;
2331
2332 decoder->f_motion.ref[1][0] = forward_fbuf[0] + offset;
2333#if MPEG2_COLOR
2334 decoder->f_motion.ref[1][1] = forward_fbuf[1] + (offset >> 1);
2335 decoder->f_motion.ref[1][2] = forward_fbuf[2] + (offset >> 1);
2336#endif
2337 decoder->b_motion.ref[1][0] = backward_fbuf[0] + offset;
2338#if MPEG2_COLOR
2339 decoder->b_motion.ref[1][1] = backward_fbuf[1] + (offset >> 1);
2340 decoder->b_motion.ref[1][2] = backward_fbuf[2] + (offset >> 1);
2341#endif
2342 stride <<= 1;
2343 height >>= 1;
2344 }
2345
2346 decoder->stride = stride;
2347 decoder->slice_stride = 16 * stride;
2348#if MPEG2_COLOR
2349 decoder->uv_stride = stride >> 1;
2350 decoder->slice_uv_stride =
2351 decoder->slice_stride >> (2 - decoder->chroma_format);
2352#endif
2353 decoder->limit_x = 2 * decoder->width - 32;
2354 decoder->limit_y_16 = 2 * height - 32;
2355 decoder->limit_y_8 = 2 * height - 16;
2356 decoder->limit_y = height - 16;
2357
2358 if (decoder->mpeg1)
2359 {
2360 decoder->motion_parser[0] = motion_zero_420;
2361 decoder->motion_parser[MC_FRAME] = motion_mp1;
2362 decoder->motion_parser[4] = motion_reuse_420;
2363 }
2364 else if (decoder->picture_structure == FRAME_PICTURE)
2365 {
2366 if (decoder->chroma_format == 0)
2367 {
2368 decoder->motion_parser[0] = motion_zero_420;
2369 decoder->motion_parser[MC_FIELD] = motion_fr_field_420;
2370 decoder->motion_parser[MC_FRAME] = motion_fr_frame_420;
2371 decoder->motion_parser[MC_DMV] = motion_fr_dmv_420;
2372 decoder->motion_parser[4] = motion_reuse_420;
2373 }
2374 else if (decoder->chroma_format == 1)
2375 {
2376 decoder->motion_parser[0] = motion_zero_422;
2377 decoder->motion_parser[MC_FIELD] = motion_fr_field_422;
2378 decoder->motion_parser[MC_FRAME] = motion_fr_frame_422;
2379 decoder->motion_parser[MC_DMV] = motion_fr_dmv_422;
2380 decoder->motion_parser[4] = motion_reuse_422;
2381 }
2382 else
2383 {
2384 decoder->motion_parser[0] = motion_zero_444;
2385 decoder->motion_parser[MC_FIELD] = motion_fr_field_444;
2386 decoder->motion_parser[MC_FRAME] = motion_fr_frame_444;
2387 decoder->motion_parser[MC_DMV] = motion_fr_dmv_444;
2388 decoder->motion_parser[4] = motion_reuse_444;
2389 }
2390 }
2391 else
2392 {
2393 if (decoder->chroma_format == 0)
2394 {
2395 decoder->motion_parser[0] = motion_zero_420;
2396 decoder->motion_parser[MC_FIELD] = motion_fi_field_420;
2397 decoder->motion_parser[MC_16X8] = motion_fi_16x8_420;
2398 decoder->motion_parser[MC_DMV] = motion_fi_dmv_420;
2399 decoder->motion_parser[4] = motion_reuse_420;
2400 }
2401 else if (decoder->chroma_format == 1)
2402 {
2403 decoder->motion_parser[0] = motion_zero_422;
2404 decoder->motion_parser[MC_FIELD] = motion_fi_field_422;
2405 decoder->motion_parser[MC_16X8] = motion_fi_16x8_422;
2406 decoder->motion_parser[MC_DMV] = motion_fi_dmv_422;
2407 decoder->motion_parser[4] = motion_reuse_422;
2408 }
2409 else
2410 {
2411 decoder->motion_parser[0] = motion_zero_444;
2412 decoder->motion_parser[MC_FIELD] = motion_fi_field_444;
2413 decoder->motion_parser[MC_16X8] = motion_fi_16x8_444;
2414 decoder->motion_parser[MC_DMV] = motion_fi_dmv_444;
2415 decoder->motion_parser[4] = motion_reuse_444;
2416 }
2417 }
2418}
2419
2420static inline int slice_init (mpeg2_decoder_t * const decoder, int code)
2421{
2422#define bit_buf (decoder->bitstream_buf)
2423#define bits (decoder->bitstream_bits)
2424#define bit_ptr (decoder->bitstream_ptr)
2425
2426 int offset;
2427 const MBAtab * mba;
2428
2429#if MPEG2_COLOR
2430 decoder->dc_dct_pred[0] = decoder->dc_dct_pred[1] =
2431 decoder->dc_dct_pred[2] = 16384;
2432#else
2433 decoder->dc_dct_pred[0] = 16384;
2434#endif
2435
2436 decoder->f_motion.pmv[0][0] = decoder->f_motion.pmv[0][1] = 0;
2437 decoder->f_motion.pmv[1][0] = decoder->f_motion.pmv[1][1] = 0;
2438 decoder->b_motion.pmv[0][0] = decoder->b_motion.pmv[0][1] = 0;
2439 decoder->b_motion.pmv[1][0] = decoder->b_motion.pmv[1][1] = 0;
2440
2441 if (decoder->vertical_position_extension)
2442 {
2443 code += UBITS (bit_buf, 3) << 7;
2444 DUMPBITS (bit_buf, bits, 3);
2445 }
2446
2447 decoder->v_offset = (code - 1) * 16;
2448 offset = 0;
2449
2450 if (!(decoder->convert) || decoder->coding_type != B_TYPE)
2451 {
2452 offset = (code - 1) * decoder->slice_stride;
2453 }
2454
2455 decoder->dest[0] = decoder->picture_dest[0] + offset;
2456#if MPEG2_COLOR
2457 offset >>= (2 - decoder->chroma_format);
2458 decoder->dest[1] = decoder->picture_dest[1] + offset;
2459 decoder->dest[2] = decoder->picture_dest[2] + offset;
2460#endif
2461
2462 get_quantizer_scale (decoder);
2463
2464 /* ignore intra_slice and all the extra data */
2465 while (bit_buf & 0x80000000)
2466 {
2467 DUMPBITS (bit_buf, bits, 9);
2468 NEEDBITS (bit_buf, bits, bit_ptr);
2469 }
2470
2471 /* decode initial macroblock address increment */
2472 offset = 0;
2473 while (1)
2474 {
2475 if (bit_buf >= 0x08000000)
2476 {
2477 mba = MBA_5 + (UBITS (bit_buf, 6) - 2);
2478 break;
2479 }
2480 else if (bit_buf >= 0x01800000)
2481 {
2482 mba = MBA_11 + (UBITS (bit_buf, 12) - 24);
2483 break;
2484 }
2485 else
2486 {
2487 switch (UBITS (bit_buf, 12))
2488 {
2489 case 8: /* macroblock_escape */
2490 offset += 33;
2491 DUMPBITS (bit_buf, bits, 11);
2492 NEEDBITS (bit_buf, bits, bit_ptr);
2493 continue;
2494 case 15: /* macroblock_stuffing (MPEG1 only) */
2495 bit_buf &= 0xfffff;
2496 DUMPBITS (bit_buf, bits, 11);
2497 NEEDBITS (bit_buf, bits, bit_ptr);
2498 continue;
2499 default: /* error */
2500 return 1;
2501 }
2502 }
2503 }
2504
2505 DUMPBITS (bit_buf, bits, mba->len + 1);
2506 decoder->offset = (offset + mba->mba) << 4;
2507
2508 while (decoder->offset - decoder->width >= 0)
2509 {
2510 decoder->offset -= decoder->width;
2511
2512 if (!(decoder->convert) || decoder->coding_type != B_TYPE)
2513 {
2514 decoder->dest[0] += decoder->slice_stride;
2515#if MPEG2_COLOR
2516 decoder->dest[1] += decoder->slice_uv_stride;
2517 decoder->dest[2] += decoder->slice_uv_stride;
2518#endif
2519 }
2520
2521 decoder->v_offset += 16;
2522 }
2523
2524 if (decoder->v_offset > decoder->limit_y)
2525 return 1;
2526
2527 return 0;
2528
2529#undef bit_buf
2530#undef bits
2531#undef bit_ptr
2532}
2533
2534void mpeg2_slice (mpeg2_decoder_t * const decoder, const int code,
2535 const uint8_t * const buffer)
2536{
2537#define bit_buf (decoder->bitstream_buf)
2538#define bits (decoder->bitstream_bits)
2539#define bit_ptr (decoder->bitstream_ptr)
2540
2541 bitstream_init (decoder, buffer);
2542
2543 if (slice_init (decoder, code))
2544 return;
2545
2546 while (1)
2547 {
2548 int macroblock_modes;
2549 int mba_inc;
2550 const MBAtab * mba;
2551
2552 NEEDBITS (bit_buf, bits, bit_ptr);
2553
2554 macroblock_modes = get_macroblock_modes (decoder);
2555
2556 /* maybe integrate MACROBLOCK_QUANT test into get_macroblock_modes ? */
2557 if (macroblock_modes & MACROBLOCK_QUANT)
2558 get_quantizer_scale (decoder);
2559
2560 if (macroblock_modes & MACROBLOCK_INTRA)
2561 {
2562 int DCT_offset, DCT_stride;
2563 int offset;
2564 uint8_t * dest_y;
2565
2566 if (decoder->concealment_motion_vectors)
2567 {
2568 if (decoder->picture_structure == FRAME_PICTURE)
2569 motion_fr_conceal (decoder);
2570 else
2571 motion_fi_conceal (decoder);
2572 }
2573 else
2574 {
2575 decoder->f_motion.pmv[0][0] = decoder->f_motion.pmv[0][1] = 0;
2576 decoder->f_motion.pmv[1][0] = decoder->f_motion.pmv[1][1] = 0;
2577 decoder->b_motion.pmv[0][0] = decoder->b_motion.pmv[0][1] = 0;
2578 decoder->b_motion.pmv[1][0] = decoder->b_motion.pmv[1][1] = 0;
2579 }
2580
2581 if (macroblock_modes & DCT_TYPE_INTERLACED)
2582 {
2583 DCT_offset = decoder->stride;
2584 DCT_stride = decoder->stride * 2;
2585 }
2586 else
2587 {
2588 DCT_offset = decoder->stride * 8;
2589 DCT_stride = decoder->stride;
2590 }
2591
2592 offset = decoder->offset;
2593 dest_y = decoder->dest[0] + offset;
2594 slice_intra_DCT (decoder, 0, dest_y, DCT_stride);
2595 slice_intra_DCT (decoder, 0, dest_y + 8, DCT_stride);
2596 slice_intra_DCT (decoder, 0, dest_y + DCT_offset, DCT_stride);
2597 slice_intra_DCT (decoder, 0, dest_y + DCT_offset + 8, DCT_stride);
2598
2599#if MPEG2_COLOR
2600 if (likely (decoder->chroma_format == 0))
2601 {
2602 slice_intra_DCT (decoder, 1, decoder->dest[1] + (offset >> 1),
2603 decoder->uv_stride);
2604 slice_intra_DCT (decoder, 2, decoder->dest[2] + (offset >> 1),
2605 decoder->uv_stride);
2606
2607 if (decoder->coding_type == D_TYPE)
2608 {
2609 NEEDBITS (bit_buf, bits, bit_ptr);
2610 DUMPBITS (bit_buf, bits, 1);
2611 }
2612 }
2613 else if (likely (decoder->chroma_format == 1))
2614 {
2615 uint8_t * dest_u = decoder->dest[1] + (offset >> 1);
2616 uint8_t * dest_v = decoder->dest[2] + (offset >> 1);
2617
2618 DCT_stride >>= 1;
2619 DCT_offset >>= 1;
2620
2621 slice_intra_DCT (decoder, 1, dest_u, DCT_stride);
2622 slice_intra_DCT (decoder, 2, dest_v, DCT_stride);
2623 slice_intra_DCT (decoder, 1, dest_u + DCT_offset, DCT_stride);
2624 slice_intra_DCT (decoder, 2, dest_v + DCT_offset, DCT_stride);
2625 }
2626 else
2627 {
2628 uint8_t * dest_u = decoder->dest[1] + offset;
2629 uint8_t * dest_v = decoder->dest[2] + offset;
2630
2631 slice_intra_DCT (decoder, 1, dest_u, DCT_stride);
2632 slice_intra_DCT (decoder, 2, dest_v, DCT_stride);
2633 slice_intra_DCT (decoder, 1, dest_u + DCT_offset, DCT_stride);
2634 slice_intra_DCT (decoder, 2, dest_v + DCT_offset, DCT_stride);
2635 slice_intra_DCT (decoder, 1, dest_u + 8, DCT_stride);
2636 slice_intra_DCT (decoder, 2, dest_v + 8, DCT_stride);
2637 slice_intra_DCT (decoder, 1, dest_u + DCT_offset + 8,
2638 DCT_stride);
2639 slice_intra_DCT (decoder, 2, dest_v + DCT_offset + 8,
2640 DCT_stride);
2641 }
2642#else
2643 skip_chroma_intra(decoder);
2644#endif /* MPEG2_COLOR */
2645 }
2646 else
2647 {
2648 motion_parser_t * parser;
2649
2650 parser =
2651 decoder->motion_parser[macroblock_modes >> MOTION_TYPE_SHIFT];
2652 MOTION_CALL (parser, macroblock_modes);
2653
2654 if (macroblock_modes & MACROBLOCK_PATTERN)
2655 {
2656 int coded_block_pattern;
2657 int DCT_offset, DCT_stride;
2658
2659 if (macroblock_modes & DCT_TYPE_INTERLACED)
2660 {
2661 DCT_offset = decoder->stride;
2662 DCT_stride = decoder->stride * 2;
2663 }
2664 else
2665 {
2666 DCT_offset = decoder->stride * 8;
2667 DCT_stride = decoder->stride;
2668 }
2669
2670 coded_block_pattern = get_coded_block_pattern (decoder);
2671
2672 if (likely (decoder->chroma_format == 0))
2673 {
2674 int offset = decoder->offset;
2675 uint8_t * dest_y = decoder->dest[0] + offset;
2676
2677 if (coded_block_pattern & 1)
2678 slice_non_intra_DCT (decoder, 0, dest_y, DCT_stride);
2679
2680 if (coded_block_pattern & 2)
2681 slice_non_intra_DCT (decoder, 0, dest_y + 8,
2682 DCT_stride);
2683
2684 if (coded_block_pattern & 4)
2685 slice_non_intra_DCT (decoder, 0, dest_y + DCT_offset,
2686 DCT_stride);
2687
2688 if (coded_block_pattern & 8)
2689 slice_non_intra_DCT (decoder, 0,
2690 dest_y + DCT_offset + 8,
2691 DCT_stride);
2692#if MPEG2_COLOR
2693 if (coded_block_pattern & 16)
2694 slice_non_intra_DCT (decoder, 1,
2695 decoder->dest[1] + (offset >> 1),
2696 decoder->uv_stride);
2697
2698 if (coded_block_pattern & 32)
2699 slice_non_intra_DCT (decoder, 2,
2700 decoder->dest[2] + (offset >> 1),
2701 decoder->uv_stride);
2702#endif /* MPEG2_COLOR */
2703 }
2704 else if (likely (decoder->chroma_format == 1))
2705 {
2706 int offset;
2707 uint8_t * dest_y;
2708
2709 coded_block_pattern |= bit_buf & (3 << 30);
2710 DUMPBITS (bit_buf, bits, 2);
2711
2712 offset = decoder->offset;
2713 dest_y = decoder->dest[0] + offset;
2714
2715 if (coded_block_pattern & 1)
2716 slice_non_intra_DCT (decoder, 0, dest_y, DCT_stride);
2717
2718 if (coded_block_pattern & 2)
2719 slice_non_intra_DCT (decoder, 0, dest_y + 8,
2720 DCT_stride);
2721
2722 if (coded_block_pattern & 4)
2723 slice_non_intra_DCT (decoder, 0, dest_y + DCT_offset,
2724 DCT_stride);
2725
2726 if (coded_block_pattern & 8)
2727 slice_non_intra_DCT (decoder, 0,
2728 dest_y + DCT_offset + 8,
2729 DCT_stride);
2730#if MPEG2_COLOR
2731 DCT_stride >>= 1;
2732 DCT_offset = (DCT_offset + offset) >> 1;
2733
2734 if (coded_block_pattern & 16)
2735 slice_non_intra_DCT (decoder, 1,
2736 decoder->dest[1] + (offset >> 1),
2737 DCT_stride);
2738
2739 if (coded_block_pattern & 32)
2740 slice_non_intra_DCT (decoder, 2,
2741 decoder->dest[2] + (offset >> 1),
2742 DCT_stride);
2743
2744 if (coded_block_pattern & (2 << 30))
2745 slice_non_intra_DCT (decoder, 1,
2746 decoder->dest[1] + DCT_offset,
2747 DCT_stride);
2748
2749 if (coded_block_pattern & (1 << 30))
2750 slice_non_intra_DCT (decoder, 2,
2751 decoder->dest[2] + DCT_offset,
2752 DCT_stride);
2753#endif /* MPEG2_COLOR */
2754 }
2755 else
2756 {
2757 int offset = decoder->offset;
2758 uint8_t * dest_y = decoder->dest[0] + offset;
2759#if MPEG2_COLOR
2760 uint8_t * dest_u = decoder->dest[1] + offset;
2761 uint8_t * dest_v = decoder->dest[2] + offset;
2762#endif
2763 coded_block_pattern |= bit_buf & (63 << 26);
2764 DUMPBITS (bit_buf, bits, 6);
2765
2766 if (coded_block_pattern & 1)
2767 slice_non_intra_DCT (decoder, 0, dest_y, DCT_stride);
2768
2769 if (coded_block_pattern & 2)
2770 slice_non_intra_DCT (decoder, 0, dest_y + 8,
2771 DCT_stride);
2772
2773 if (coded_block_pattern & 4)
2774 slice_non_intra_DCT (decoder, 0, dest_y + DCT_offset,
2775 DCT_stride);
2776
2777 if (coded_block_pattern & 8)
2778 slice_non_intra_DCT (decoder, 0,
2779 dest_y + DCT_offset + 8,
2780 DCT_stride);
2781#if MPEG2_COLOR
2782 if (coded_block_pattern & 16)
2783 slice_non_intra_DCT (decoder, 1, dest_u, DCT_stride);
2784
2785 if (coded_block_pattern & 32)
2786 slice_non_intra_DCT (decoder, 2, dest_v, DCT_stride);
2787
2788 if (coded_block_pattern & (32 << 26))
2789 slice_non_intra_DCT (decoder, 1, dest_u + DCT_offset,
2790 DCT_stride);
2791
2792 if (coded_block_pattern & (16 << 26))
2793 slice_non_intra_DCT (decoder, 2, dest_v + DCT_offset,
2794 DCT_stride);
2795
2796 if (coded_block_pattern & (8 << 26))
2797 slice_non_intra_DCT (decoder, 1, dest_u + 8,
2798 DCT_stride);
2799
2800 if (coded_block_pattern & (4 << 26))
2801 slice_non_intra_DCT (decoder, 2, dest_v + 8,
2802 DCT_stride);
2803
2804 if (coded_block_pattern & (2 << 26))
2805 slice_non_intra_DCT (decoder, 1,
2806 dest_u + DCT_offset + 8,
2807 DCT_stride);
2808
2809 if (coded_block_pattern & (1 << 26))
2810 slice_non_intra_DCT (decoder, 2,
2811 dest_v + DCT_offset + 8,
2812 DCT_stride);
2813#endif /* MPEG2_COLOR */
2814 }
2815#if !MPEG2_COLOR
2816 skip_chroma_non_intra(decoder, coded_block_pattern);
2817#endif
2818 }
2819
2820#if MPEG2_COLOR
2821 decoder->dc_dct_pred[0] = decoder->dc_dct_pred[1] =
2822 decoder->dc_dct_pred[2] = 16384;
2823#else
2824 decoder->dc_dct_pred[0] = 16384;
2825#endif
2826 }
2827
2828 NEXT_MACROBLOCK;
2829
2830 NEEDBITS (bit_buf, bits, bit_ptr);
2831 mba_inc = 0;
2832
2833 while (1)
2834 {
2835 if (bit_buf >= 0x10000000)
2836 {
2837 mba = MBA_5 + (UBITS (bit_buf, 5) - 2);
2838 break;
2839 }
2840 else if (bit_buf >= 0x03000000)
2841 {
2842 mba = MBA_11 + (UBITS (bit_buf, 11) - 24);
2843 break;
2844 }
2845 else
2846 {
2847 switch (UBITS (bit_buf, 11))
2848 {
2849 case 8: /* macroblock_escape */
2850 mba_inc += 33;
2851 /* pass through */
2852 case 15: /* macroblock_stuffing (MPEG1 only) */
2853 DUMPBITS (bit_buf, bits, 11);
2854 NEEDBITS (bit_buf, bits, bit_ptr);
2855 continue;
2856 default: /* end of slice, or error */
2857 return;
2858 }
2859 }
2860 }
2861
2862 DUMPBITS (bit_buf, bits, mba->len);
2863 mba_inc += mba->mba;
2864
2865 if (mba_inc)
2866 {
2867#if MPEG2_COLOR
2868 decoder->dc_dct_pred[0] = decoder->dc_dct_pred[1] =
2869 decoder->dc_dct_pred[2] = 16384;
2870#else
2871 decoder->dc_dct_pred[0] = 16384;
2872#endif
2873 if (decoder->coding_type == P_TYPE)
2874 {
2875 do
2876 {
2877 MOTION_CALL (decoder->motion_parser[0],
2878 MACROBLOCK_MOTION_FORWARD);
2879 NEXT_MACROBLOCK;
2880 }
2881 while (--mba_inc);
2882 }
2883 else
2884 {
2885 do
2886 {
2887 MOTION_CALL (decoder->motion_parser[4], macroblock_modes);
2888 NEXT_MACROBLOCK;
2889 }
2890 while (--mba_inc);
2891 }
2892 }
2893 }
2894
2895#undef bit_buf
2896#undef bits
2897#undef bit_ptr
2898}
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 @@
1/*
2 * vlc.h
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.12
26 */
27
28#define GETWORD(bit_buf, shift, bit_ptr) \
29do { \
30 bit_buf |= ((bit_ptr[0] << 8) | bit_ptr[1]) << (shift); \
31 bit_ptr += 2; \
32} while (0)
33
34static inline void bitstream_init (mpeg2_decoder_t * decoder,
35 const uint8_t * start)
36{
37 decoder->bitstream_buf =
38 (start[0] << 24) | (start[1] << 16) | (start[2] << 8) | start[3];
39 decoder->bitstream_ptr = start + 4;
40 decoder->bitstream_bits = -16;
41}
42
43/* make sure that there are at least 16 valid bits in bit_buf */
44#define NEEDBITS(bit_buf, bits, bit_ptr) \
45do { \
46 if (unlikely (bits > 0)) { \
47 GETWORD (bit_buf, bits, bit_ptr); \
48 bits -= 16; \
49 } \
50} while (0)
51
52/* remove num valid bits from bit_buf */
53#define DUMPBITS(bit_buf, bits, num) \
54do { \
55 bit_buf <<= (num); \
56 bits += (num); \
57} while (0)
58
59/* take num bits from the high part of bit_buf and zero extend them */
60#define UBITS(bit_buf,num) (((uint32_t)(bit_buf)) >> (32 - (num)))
61
62/* take num bits from the high part of bit_buf and sign extend them */
63#define SBITS(bit_buf,num) (((int32_t)(bit_buf)) >> (32 - (num)))
64
65typedef struct {
66 uint8_t modes;
67 uint8_t len;
68} MBtab;
69
70typedef struct {
71 uint8_t delta;
72 uint8_t len;
73} MVtab;
74
75typedef struct {
76 int8_t dmv;
77 uint8_t len;
78} DMVtab;
79
80typedef struct {
81 uint8_t cbp;
82 uint8_t len;
83} CBPtab;
84
85typedef struct {
86 uint8_t size;
87 uint8_t len;
88} DCtab;
89
90typedef struct {
91 uint8_t run;
92 uint8_t level;
93 uint8_t len;
94} DCTtab;
95
96typedef struct {
97 uint8_t mba;
98 uint8_t len;
99} MBAtab;
100
101
102#define INTRA MACROBLOCK_INTRA
103#define QUANT MACROBLOCK_QUANT
104
105static const MBtab MB_I [] ICONST_ATTR = {
106 {INTRA|QUANT, 2}, {INTRA, 1}
107};
108
109#define MC MACROBLOCK_MOTION_FORWARD
110#define CODED MACROBLOCK_PATTERN
111
112static const MBtab MB_P [] ICONST_ATTR = {
113 {INTRA|QUANT, 6}, {CODED|QUANT, 5}, {MC|CODED|QUANT, 5}, {INTRA, 5},
114 {MC, 3}, {MC, 3}, {MC, 3}, {MC, 3},
115 {CODED, 2}, {CODED, 2}, {CODED, 2}, {CODED, 2},
116 {CODED, 2}, {CODED, 2}, {CODED, 2}, {CODED, 2},
117 {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1},
118 {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1},
119 {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1},
120 {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}
121};
122
123#define FWD MACROBLOCK_MOTION_FORWARD
124#define BWD MACROBLOCK_MOTION_BACKWARD
125#define INTER MACROBLOCK_MOTION_FORWARD|MACROBLOCK_MOTION_BACKWARD
126
127static const MBtab MB_B [] ICONST_ATTR = {
128 {0, 6}, {INTRA|QUANT, 6},
129 {BWD|CODED|QUANT, 6}, {FWD|CODED|QUANT, 6},
130 {INTER|CODED|QUANT, 5}, {INTER|CODED|QUANT, 5},
131 {INTRA, 5}, {INTRA, 5},
132 {FWD, 4}, {FWD, 4}, {FWD, 4}, {FWD, 4},
133 {FWD|CODED, 4}, {FWD|CODED, 4}, {FWD|CODED, 4}, {FWD|CODED, 4},
134 {BWD, 3}, {BWD, 3}, {BWD, 3}, {BWD, 3},
135 {BWD, 3}, {BWD, 3}, {BWD, 3}, {BWD, 3},
136 {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3},
137 {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3},
138 {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2},
139 {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2},
140 {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2},
141 {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2},
142 {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2},
143 {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2},
144 {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2},
145 {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}
146};
147
148#undef INTRA
149#undef QUANT
150#undef MC
151#undef CODED
152#undef FWD
153#undef BWD
154#undef INTER
155
156
157static const MVtab MV_4 [] ICONST_ATTR = {
158 { 3, 6}, { 2, 4}, { 1, 3}, { 1, 3}, { 0, 2}, { 0, 2}, { 0, 2}, { 0, 2}
159};
160
161static const MVtab MV_10 [] ICONST_ATTR = {
162 { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10},
163 { 0,10}, { 0,10}, { 0,10}, { 0,10}, {15,10}, {14,10}, {13,10}, {12,10},
164 {11,10}, {10,10}, { 9, 9}, { 9, 9}, { 8, 9}, { 8, 9}, { 7, 9}, { 7, 9},
165 { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7},
166 { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7},
167 { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}
168};
169
170
171static const DMVtab DMV_2 [] ICONST_ATTR = {
172 { 0, 1}, { 0, 1}, { 1, 2}, {-1, 2}
173};
174
175
176static const CBPtab CBP_7 [] ICONST_ATTR = {
177 {0x11, 7}, {0x12, 7}, {0x14, 7}, {0x18, 7},
178 {0x21, 7}, {0x22, 7}, {0x24, 7}, {0x28, 7},
179 {0x3f, 6}, {0x3f, 6}, {0x30, 6}, {0x30, 6},
180 {0x09, 6}, {0x09, 6}, {0x06, 6}, {0x06, 6},
181 {0x1f, 5}, {0x1f, 5}, {0x1f, 5}, {0x1f, 5},
182 {0x10, 5}, {0x10, 5}, {0x10, 5}, {0x10, 5},
183 {0x2f, 5}, {0x2f, 5}, {0x2f, 5}, {0x2f, 5},
184 {0x20, 5}, {0x20, 5}, {0x20, 5}, {0x20, 5},
185 {0x07, 5}, {0x07, 5}, {0x07, 5}, {0x07, 5},
186 {0x0b, 5}, {0x0b, 5}, {0x0b, 5}, {0x0b, 5},
187 {0x0d, 5}, {0x0d, 5}, {0x0d, 5}, {0x0d, 5},
188 {0x0e, 5}, {0x0e, 5}, {0x0e, 5}, {0x0e, 5},
189 {0x05, 5}, {0x05, 5}, {0x05, 5}, {0x05, 5},
190 {0x0a, 5}, {0x0a, 5}, {0x0a, 5}, {0x0a, 5},
191 {0x03, 5}, {0x03, 5}, {0x03, 5}, {0x03, 5},
192 {0x0c, 5}, {0x0c, 5}, {0x0c, 5}, {0x0c, 5},
193 {0x01, 4}, {0x01, 4}, {0x01, 4}, {0x01, 4},
194 {0x01, 4}, {0x01, 4}, {0x01, 4}, {0x01, 4},
195 {0x02, 4}, {0x02, 4}, {0x02, 4}, {0x02, 4},
196 {0x02, 4}, {0x02, 4}, {0x02, 4}, {0x02, 4},
197 {0x04, 4}, {0x04, 4}, {0x04, 4}, {0x04, 4},
198 {0x04, 4}, {0x04, 4}, {0x04, 4}, {0x04, 4},
199 {0x08, 4}, {0x08, 4}, {0x08, 4}, {0x08, 4},
200 {0x08, 4}, {0x08, 4}, {0x08, 4}, {0x08, 4},
201 {0x0f, 3}, {0x0f, 3}, {0x0f, 3}, {0x0f, 3},
202 {0x0f, 3}, {0x0f, 3}, {0x0f, 3}, {0x0f, 3},
203 {0x0f, 3}, {0x0f, 3}, {0x0f, 3}, {0x0f, 3},
204 {0x0f, 3}, {0x0f, 3}, {0x0f, 3}, {0x0f, 3}
205};
206
207static const CBPtab CBP_9 [] ICONST_ATTR = {
208 {0, 9}, {0x00, 9}, {0x39, 9}, {0x36, 9},
209 {0x37, 9}, {0x3b, 9}, {0x3d, 9}, {0x3e, 9},
210 {0x17, 8}, {0x17, 8}, {0x1b, 8}, {0x1b, 8},
211 {0x1d, 8}, {0x1d, 8}, {0x1e, 8}, {0x1e, 8},
212 {0x27, 8}, {0x27, 8}, {0x2b, 8}, {0x2b, 8},
213 {0x2d, 8}, {0x2d, 8}, {0x2e, 8}, {0x2e, 8},
214 {0x19, 8}, {0x19, 8}, {0x16, 8}, {0x16, 8},
215 {0x29, 8}, {0x29, 8}, {0x26, 8}, {0x26, 8},
216 {0x35, 8}, {0x35, 8}, {0x3a, 8}, {0x3a, 8},
217 {0x33, 8}, {0x33, 8}, {0x3c, 8}, {0x3c, 8},
218 {0x15, 8}, {0x15, 8}, {0x1a, 8}, {0x1a, 8},
219 {0x13, 8}, {0x13, 8}, {0x1c, 8}, {0x1c, 8},
220 {0x25, 8}, {0x25, 8}, {0x2a, 8}, {0x2a, 8},
221 {0x23, 8}, {0x23, 8}, {0x2c, 8}, {0x2c, 8},
222 {0x31, 8}, {0x31, 8}, {0x32, 8}, {0x32, 8},
223 {0x34, 8}, {0x34, 8}, {0x38, 8}, {0x38, 8}
224};
225
226
227static const DCtab DC_lum_5 [] ICONST_ATTR = {
228 {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2},
229 {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2},
230 {0, 3}, {0, 3}, {0, 3}, {0, 3}, {3, 3}, {3, 3}, {3, 3}, {3, 3},
231 {4, 3}, {4, 3}, {4, 3}, {4, 3}, {5, 4}, {5, 4}, {6, 5}
232};
233
234static const DCtab DC_chrom_5 [] ICONST_ATTR = {
235 {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2},
236 {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2},
237 {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2},
238 {3, 3}, {3, 3}, {3, 3}, {3, 3}, {4, 4}, {4, 4}, {5, 5}
239};
240
241static const DCtab DC_long [] ICONST_ATTR = {
242 {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, { 6, 5}, { 6, 5},
243 {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, { 6, 5}, { 6, 5},
244 {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, { 7, 6}, { 7, 6},
245 {8, 7}, {8, 7}, {8, 7}, {8, 7}, {9, 8}, {9, 8}, {10, 9}, {11, 9}
246};
247
248
249static const DCTtab DCT_16 [] ICONST_ATTR = {
250 {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0},
251 {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0},
252 {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0},
253 {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0},
254 { 2,18, 0}, { 2,17, 0}, { 2,16, 0}, { 2,15, 0},
255 { 7, 3, 0}, { 17, 2, 0}, { 16, 2, 0}, { 15, 2, 0},
256 { 14, 2, 0}, { 13, 2, 0}, { 12, 2, 0}, { 32, 1, 0},
257 { 31, 1, 0}, { 30, 1, 0}, { 29, 1, 0}, { 28, 1, 0}
258};
259
260static const DCTtab DCT_15 [] ICONST_ATTR = {
261 { 1,40,15}, { 1,39,15}, { 1,38,15}, { 1,37,15},
262 { 1,36,15}, { 1,35,15}, { 1,34,15}, { 1,33,15},
263 { 1,32,15}, { 2,14,15}, { 2,13,15}, { 2,12,15},
264 { 2,11,15}, { 2,10,15}, { 2, 9,15}, { 2, 8,15},
265 { 1,31,14}, { 1,31,14}, { 1,30,14}, { 1,30,14},
266 { 1,29,14}, { 1,29,14}, { 1,28,14}, { 1,28,14},
267 { 1,27,14}, { 1,27,14}, { 1,26,14}, { 1,26,14},
268 { 1,25,14}, { 1,25,14}, { 1,24,14}, { 1,24,14},
269 { 1,23,14}, { 1,23,14}, { 1,22,14}, { 1,22,14},
270 { 1,21,14}, { 1,21,14}, { 1,20,14}, { 1,20,14},
271 { 1,19,14}, { 1,19,14}, { 1,18,14}, { 1,18,14},
272 { 1,17,14}, { 1,17,14}, { 1,16,14}, { 1,16,14}
273};
274
275static const DCTtab DCT_13 [] ICONST_ATTR = {
276 { 11, 2,13}, { 10, 2,13}, { 6, 3,13}, { 4, 4,13},
277 { 3, 5,13}, { 2, 7,13}, { 2, 6,13}, { 1,15,13},
278 { 1,14,13}, { 1,13,13}, { 1,12,13}, { 27, 1,13},
279 { 26, 1,13}, { 25, 1,13}, { 24, 1,13}, { 23, 1,13},
280 { 1,11,12}, { 1,11,12}, { 9, 2,12}, { 9, 2,12},
281 { 5, 3,12}, { 5, 3,12}, { 1,10,12}, { 1,10,12},
282 { 3, 4,12}, { 3, 4,12}, { 8, 2,12}, { 8, 2,12},
283 { 22, 1,12}, { 22, 1,12}, { 21, 1,12}, { 21, 1,12},
284 { 1, 9,12}, { 1, 9,12}, { 20, 1,12}, { 20, 1,12},
285 { 19, 1,12}, { 19, 1,12}, { 2, 5,12}, { 2, 5,12},
286 { 4, 3,12}, { 4, 3,12}, { 1, 8,12}, { 1, 8,12},
287 { 7, 2,12}, { 7, 2,12}, { 18, 1,12}, { 18, 1,12}
288};
289
290static const DCTtab DCT_B14_10 [] ICONST_ATTR = {
291 { 17, 1,10}, { 6, 2,10}, { 1, 7,10}, { 3, 3,10},
292 { 2, 4,10}, { 16, 1,10}, { 15, 1,10}, { 5, 2,10}
293};
294
295static const DCTtab DCT_B14_8 [] ICONST_ATTR = {
296 { 65, 0, 12}, { 65, 0, 12}, { 65, 0, 12}, { 65, 0, 12},
297 { 3, 2, 7}, { 3, 2, 7}, { 10, 1, 7}, { 10, 1, 7},
298 { 1, 4, 7}, { 1, 4, 7}, { 9, 1, 7}, { 9, 1, 7},
299 { 8, 1, 6}, { 8, 1, 6}, { 8, 1, 6}, { 8, 1, 6},
300 { 7, 1, 6}, { 7, 1, 6}, { 7, 1, 6}, { 7, 1, 6},
301 { 2, 2, 6}, { 2, 2, 6}, { 2, 2, 6}, { 2, 2, 6},
302 { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6},
303 { 14, 1, 8}, { 1, 6, 8}, { 13, 1, 8}, { 12, 1, 8},
304 { 4, 2, 8}, { 2, 3, 8}, { 1, 5, 8}, { 11, 1, 8}
305};
306
307static const DCTtab DCT_B14AC_5 [] ICONST_ATTR = {
308 { 1, 3, 5}, { 5, 1, 5}, { 4, 1, 5},
309 { 1, 2, 4}, { 1, 2, 4}, { 3, 1, 4}, { 3, 1, 4},
310 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
311 {129, 0, 2}, {129, 0, 2}, {129, 0, 2}, {129, 0, 2},
312 {129, 0, 2}, {129, 0, 2}, {129, 0, 2}, {129, 0, 2},
313 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
314 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}
315};
316
317static const DCTtab DCT_B14DC_5 [] ICONST_ATTR = {
318 { 1, 3, 5}, { 5, 1, 5}, { 4, 1, 5},
319 { 1, 2, 4}, { 1, 2, 4}, { 3, 1, 4}, { 3, 1, 4},
320 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
321 { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1},
322 { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1},
323 { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1},
324 { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}
325};
326
327static const DCTtab DCT_B15_10 [] ICONST_ATTR = {
328 { 6, 2, 9}, { 6, 2, 9}, { 15, 1, 9}, { 15, 1, 9},
329 { 3, 4,10}, { 17, 1,10}, { 16, 1, 9}, { 16, 1, 9}
330};
331
332static const DCTtab DCT_B15_8 [] ICONST_ATTR = {
333 { 65, 0, 12}, { 65, 0, 12}, { 65, 0, 12}, { 65, 0, 12},
334 { 8, 1, 7}, { 8, 1, 7}, { 9, 1, 7}, { 9, 1, 7},
335 { 7, 1, 7}, { 7, 1, 7}, { 3, 2, 7}, { 3, 2, 7},
336 { 1, 7, 6}, { 1, 7, 6}, { 1, 7, 6}, { 1, 7, 6},
337 { 1, 6, 6}, { 1, 6, 6}, { 1, 6, 6}, { 1, 6, 6},
338 { 5, 1, 6}, { 5, 1, 6}, { 5, 1, 6}, { 5, 1, 6},
339 { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6},
340 { 2, 5, 8}, { 12, 1, 8}, { 1,11, 8}, { 1,10, 8},
341 { 14, 1, 8}, { 13, 1, 8}, { 4, 2, 8}, { 2, 4, 8},
342 { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5},
343 { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5},
344 { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5},
345 { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5},
346 { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5},
347 { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5},
348 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
349 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
350 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
351 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
352 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
353 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
354 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
355 { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3},
356 {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4},
357 {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4},
358 {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4},
359 {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4},
360 { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4},
361 { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4},
362 { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4},
363 { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4},
364 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
365 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
366 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
367 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
368 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
369 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
370 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
371 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
372 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
373 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
374 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
375 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
376 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
377 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
378 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
379 { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2},
380 { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
381 { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
382 { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
383 { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
384 { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
385 { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
386 { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
387 { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3},
388 { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5},
389 { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5},
390 { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5},
391 { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5},
392 { 10, 1, 7}, { 10, 1, 7}, { 2, 3, 7}, { 2, 3, 7},
393 { 11, 1, 7}, { 11, 1, 7}, { 1, 8, 7}, { 1, 8, 7},
394 { 1, 9, 7}, { 1, 9, 7}, { 1,12, 8}, { 1,13, 8},
395 { 3, 3, 8}, { 5, 2, 8}, { 1,14, 8}, { 1,15, 8}
396};
397
398
399static const MBAtab MBA_5 [] ICONST_ATTR = {
400 {6, 5}, {5, 5}, {4, 4}, {4, 4}, {3, 4}, {3, 4},
401 {2, 3}, {2, 3}, {2, 3}, {2, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3},
402 {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1},
403 {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}
404};
405
406static const MBAtab MBA_11 [] ICONST_ATTR = {
407 {32, 11}, {31, 11}, {30, 11}, {29, 11},
408 {28, 11}, {27, 11}, {26, 11}, {25, 11},
409 {24, 11}, {23, 11}, {22, 11}, {21, 11},
410 {20, 10}, {20, 10}, {19, 10}, {19, 10},
411 {18, 10}, {18, 10}, {17, 10}, {17, 10},
412 {16, 10}, {16, 10}, {15, 10}, {15, 10},
413 {14, 8}, {14, 8}, {14, 8}, {14, 8},
414 {14, 8}, {14, 8}, {14, 8}, {14, 8},
415 {13, 8}, {13, 8}, {13, 8}, {13, 8},
416 {13, 8}, {13, 8}, {13, 8}, {13, 8},
417 {12, 8}, {12, 8}, {12, 8}, {12, 8},
418 {12, 8}, {12, 8}, {12, 8}, {12, 8},
419 {11, 8}, {11, 8}, {11, 8}, {11, 8},
420 {11, 8}, {11, 8}, {11, 8}, {11, 8},
421 {10, 8}, {10, 8}, {10, 8}, {10, 8},
422 {10, 8}, {10, 8}, {10, 8}, {10, 8},
423 { 9, 8}, { 9, 8}, { 9, 8}, { 9, 8},
424 { 9, 8}, { 9, 8}, { 9, 8}, { 9, 8},
425 { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7},
426 { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7},
427 { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7},
428 { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7},
429 { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7},
430 { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7},
431 { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7},
432 { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7}
433};
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef MPEG_ALLOC_H
22#define MPEG_ALLOC_H
23
24/* returns the remaining mpeg2 buffer and it's size */
25void * mpeg2_get_buf(size_t *size);
26void *mpeg_malloc(size_t size, mpeg2_alloc_t reason);
27/* Grabs all the buffer available sans margin */
28void *mpeg_malloc_all(size_t *size_out, mpeg2_alloc_t reason);
29/* Initializes the malloc buffer with the given base buffer */
30bool mpeg_alloc_init(unsigned char *buf, size_t mallocsize);
31
32#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Miscellaneous helper API definitions
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "plugin.h"
24#include "mpegplayer.h"
25
26/** Streams **/
27
28/* Initializes the cursor */
29void stream_scan_init(struct stream_scan *sk)
30{
31 dbuf_l2_init(&sk->l2);
32}
33
34/* Ensures direction is -1 or 1 and margin is properly initialized */
35void stream_scan_normalize(struct stream_scan *sk)
36{
37 if (sk->dir >= 0)
38 {
39 sk->dir = SSCAN_FORWARD;
40 sk->margin = sk->len;
41 }
42 else if (sk->dir < 0)
43 {
44 sk->dir = SSCAN_REVERSE;
45 sk->margin = 0;
46 }
47}
48
49/* Moves a scan cursor. If amount is positive, the increment is in the scan
50 * direction, otherwise opposite the scan direction */
51void stream_scan_offset(struct stream_scan *sk, off_t by)
52{
53 off_t bydir = by*sk->dir;
54 sk->pos += bydir;
55 sk->margin -= bydir;
56 sk->len -= by;
57}
58
59/** Time helpers **/
60void ts_to_hms(uint32_t pts, struct hms *hms)
61{
62 hms->frac = pts % TS_SECOND;
63 hms->sec = pts / TS_SECOND;
64 hms->min = hms->sec / 60;
65 hms->hrs = hms->min / 60;
66 hms->sec %= 60;
67 hms->min %= 60;
68}
69
70void hms_format(char *buf, size_t bufsize, struct hms *hms)
71{
72 /* Only display hours if nonzero */
73 if (hms->hrs != 0)
74 {
75 rb->snprintf(buf, bufsize, "%u:%02u:%02u",
76 hms->hrs, hms->min, hms->sec);
77 }
78 else
79 {
80 rb->snprintf(buf, bufsize, "%u:%02u",
81 hms->min, hms->sec);
82 }
83}
84
85/** Maths **/
86uint32_t muldiv_uint32(uint32_t multiplicand,
87 uint32_t multiplier,
88 uint32_t divisor)
89{
90 if (divisor != 0)
91 {
92 uint64_t prod = (uint64_t)multiplier*multiplicand + divisor/2;
93
94 if ((uint32_t)(prod >> 32) < divisor)
95 return (uint32_t)(prod / divisor);
96 }
97 else if (multiplicand == 0 || multiplier == 0)
98 {
99 return 0; /* 0/0 = 0 : yaya */
100 }
101 /* else (> 0) / 0 = UINT32_MAX */
102
103 return UINT32_MAX; /* Saturate */
104}
105
106
107/** Lists **/
108
109/* Does the list have any members? */
110bool list_is_empty(void **list)
111{
112 return *list == NULL;
113}
114
115/* Is the item inserted into a particular list? */
116bool list_is_member(void **list, void *item)
117{
118 return *rb->find_array_ptr(list, item) != NULL;
119}
120
121/* Removes an item from a list - returns true if item was found
122 * and thus removed. */
123bool list_remove_item(void **list, void *item)
124{
125 return rb->remove_array_ptr(list, item) != -1;
126}
127
128/* Adds a list item, insert last, if not already present. */
129void list_add_item(void **list, void *item)
130{
131 void **item_p = rb->find_array_ptr(list, item);
132 if (*item_p == NULL)
133 *item_p = item;
134}
135
136/* Clears the entire list. */
137void list_clear_all(void **list)
138{
139 while (*list != NULL)
140 *list++ = NULL;
141}
142
143/* Enumerate all items in the array, passing each item in turn to the
144 * callback as well as the data value. The current item may be safely
145 * removed. Other changes during enumeration are undefined. The callback
146 * may return 'false' to stop the enumeration early. */
147void list_enum_items(void **list,
148 list_enum_callback_t callback,
149 void* data)
150{
151 for (;;)
152 {
153 void *item = *list;
154
155 if (item == NULL)
156 break;
157
158 if (callback != NULL && !callback(item, data))
159 break;
160
161 if (*list == item)
162 list++; /* Item still there */
163 }
164}
165
166
167/** System events **/
168static long mpeg_sysevent_id;
169
170void mpeg_sysevent_clear(void)
171{
172 mpeg_sysevent_id = 0;
173}
174
175void mpeg_sysevent_set(void)
176{
177 /* Nonzero and won't invoke anything in default event handler */
178 mpeg_sysevent_id = ACTION_STD_CANCEL;
179}
180
181long mpeg_sysevent(void)
182{
183 return mpeg_sysevent_id;
184}
185
186int mpeg_sysevent_callback(int btn,
187 const struct menu_item_ex *menu,
188 struct gui_synclist *this_list)
189{
190 (void) this_list;
191 switch (btn)
192 {
193 case SYS_USB_CONNECTED:
194 case SYS_POWEROFF:
195 case SYS_REBOOT:
196 mpeg_sysevent_id = btn;
197 return ACTION_STD_CANCEL;
198 }
199
200 return btn;
201 (void)menu;
202}
203
204void mpeg_sysevent_handle(void)
205{
206 long id = mpeg_sysevent();
207 if (id != 0)
208 rb->default_event_handler(id);
209}
210
211
212/** Buttons **/
213
214int mpeg_button_get(int timeout)
215{
216 int button;
217
218 mpeg_sysevent_clear();
219 button = timeout == TIMEOUT_BLOCK ? rb->button_get(true) :
220 rb->button_get_w_tmo(timeout);
221
222 /* Produce keyclick */
223 rb->keyclick_click(true, button);
224
225 return mpeg_sysevent_callback(button, NULL, NULL);
226}
227
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Miscellaneous helper API declarations
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef MPEG_MISC_H
24#define MPEG_MISC_H
25
26/* Miscellaneous helpers */
27#ifndef ALIGNED_ATTR
28#define ALIGNED_ATTR(x) __attribute__((aligned(x)))
29#endif
30
31#include "disk_buf.h"
32
33/* Generic states for when things are too simple to care about naming them */
34enum state_enum
35{
36 STATE0 = 0,
37 STATE1,
38 STATE2,
39 STATE3,
40 STATE4,
41 STATE5,
42 STATE6,
43 STATE7,
44 STATE8,
45 STATE9,
46};
47
48/* Macros for comparing memory bytes to a series of constant bytes in an
49 efficient manner - evaluate to true if corresponding bytes match */
50#if defined (CPU_ARM)
51/* ARM must load 32-bit values at addres % 4 == 0 offsets but this data
52 isn't aligned nescessarily, so just byte compare */
53#define CMP_3_CONST(_a, _b) \
54 ({ int _x; \
55 asm volatile ( \
56 "ldrb %[x], [%[a], #0] \n" \
57 "eors %[x], %[x], %[b0] \n" \
58 "ldreqb %[x], [%[a], #1] \n" \
59 "eoreqs %[x], %[x], %[b1] \n" \
60 "ldreqb %[x], [%[a], #2] \n" \
61 "eoreqs %[x], %[x], %[b2] \n" \
62 : [x]"=&r"(_x) \
63 : [a]"r"(_a), \
64 [b0]"i"(((_b) >> 24) & 0xff), \
65 [b1]"i"(((_b) >> 16) & 0xff), \
66 [b2]"i"(((_b) >> 8) & 0xff) \
67 ); \
68 _x == 0; })
69
70#define CMP_4_CONST(_a, _b) \
71 ({ int _x; \
72 asm volatile ( \
73 "ldrb %[x], [%[a], #0] \n" \
74 "eors %[x], %[x], %[b0] \n" \
75 "ldreqb %[x], [%[a], #1] \n" \
76 "eoreqs %[x], %[x], %[b1] \n" \
77 "ldreqb %[x], [%[a], #2] \n" \
78 "eoreqs %[x], %[x], %[b2] \n" \
79 "ldreqb %[x], [%[a], #3] \n" \
80 "eoreqs %[x], %[x], %[b3] \n" \
81 : [x]"=&r"(_x) \
82 : [a]"r"(_a), \
83 [b0]"i"(((_b) >> 24) & 0xff), \
84 [b1]"i"(((_b) >> 16) & 0xff), \
85 [b2]"i"(((_b) >> 8) & 0xff), \
86 [b3]"i"(((_b) ) & 0xff) \
87 ); \
88 _x == 0; })
89
90#elif defined (CPU_COLDFIRE)
91/* Coldfire can just load a 32 bit value at any offset but ASM is not the
92 best way to integrate this with the C code */
93#define CMP_3_CONST(a, b) \
94 (((*(uint32_t *)(a) >> 8) == ((uint32_t)(b) >> 8)))
95
96#define CMP_4_CONST(a, b) \
97 ((*(uint32_t *)(a) == (b)))
98
99#else
100/* Don't know what this is - use bytewise comparisons */
101#define CMP_3_CONST(a, b) \
102 (( ((a)[0] ^ (((b) >> 24) & 0xff)) | \
103 ((a)[1] ^ (((b) >> 16) & 0xff)) | \
104 ((a)[2] ^ (((b) >> 8) & 0xff)) ) == 0)
105
106#define CMP_4_CONST(a, b) \
107 (( ((a)[0] ^ (((b) >> 24) & 0xff)) | \
108 ((a)[1] ^ (((b) >> 16) & 0xff)) | \
109 ((a)[2] ^ (((b) >> 8) & 0xff)) | \
110 ((a)[3] ^ (((b) ) & 0xff)) ) == 0)
111#endif /* CPU_* */
112
113
114/** Streams **/
115
116/* Convert PTS/DTS ticks to our clock ticks */
117#define TS_TO_TICKS(pts) ((uint64_t)CLOCK_RATE*(pts) / TS_SECOND)
118/* Convert our clock ticks to PTS/DTS ticks */
119#define TICKS_TO_TS(ts) ((uint64_t)TS_SECOND*(ts) / CLOCK_RATE)
120/* Convert timecode ticks to our clock ticks */
121#define TC_TO_TICKS(stamp) ((uint64_t)CLOCK_RATE*(stamp) / TC_SECOND)
122/* Convert our clock ticks to timecode ticks */
123#define TICKS_TO_TC(stamp) ((uint64_t)TC_SECOND*(stamp) / CLOCK_RATE)
124/* Convert timecode ticks to timestamp ticks */
125#define TC_TO_TS(stamp) ((stamp) / 600)
126
127/*
128 * S = start position, E = end position
129 *
130 * pos:
131 * initialize to search start position (S)
132 *
133 * len:
134 * initialize to = ABS(S-E)
135 * scanning = remaining bytes in scan direction
136 *
137 * dir:
138 * scan direction; >= 0 == forward, < 0 == reverse
139 *
140 * margin:
141 * amount of data to right of cursor - initialize by stream_scan_normalize
142 *
143 * data:
144 * Extra data used/returned by the function implemented
145 *
146 * Forward scan:
147 * S pos E
148 * | *<-margin->| dir->
149 * | |<--len--->|
150 *
151 * Reverse scan:
152 * E pos S
153 * |<-len->*<-margin->| <-dir
154 * | | |
155 */
156struct stream_scan
157{
158 off_t pos; /* Initial scan position (file offset) */
159 ssize_t len; /* Maximum length of scan */
160 off_t dir; /* Direction - >= 0; forward, < 0 backward */
161 ssize_t margin; /* Used by function to track margin between position and data end */
162 intptr_t data; /* */
163 struct dbuf_l2_cache l2;
164};
165
166#define SSCAN_REVERSE (-1)
167#define SSCAN_FORWARD 1
168
169/* Initializes the cursor */
170void stream_scan_init(struct stream_scan *sk);
171
172/* Ensures direction is -1 or 1 and margin is properly initialized */
173void stream_scan_normalize(struct stream_scan *sk);
174
175/* Moves a scan cursor. If amount is positive, the increment is in the scan
176 * direction, otherwise opposite the scan direction */
177void stream_scan_offset(struct stream_scan *sk, off_t by);
178
179/** Time helpers **/
180struct hms
181{
182 unsigned int hrs;
183 unsigned int min;
184 unsigned int sec;
185 unsigned int frac;
186};
187
188void ts_to_hms(uint32_t ts, struct hms *hms);
189void hms_format(char *buf, size_t bufsize, struct hms *hms);
190
191/** Maths **/
192
193/* Moving average */
194#define AVERAGE(var, x, count) \
195 ({ typeof (count) _c = (count); \
196 ((var) * (_c-1) + (x)) / (_c); })
197
198/* Multiply two unsigned 32-bit integers yielding a 64-bit result and
199 * divide by another unsigned 32-bit integer to yield a 32-bit result.
200 * Rounds to nearest with saturation. */
201uint32_t muldiv_uint32(uint32_t multiplicand,
202 uint32_t multiplier,
203 uint32_t divisor);
204
205
206/** Lists **/
207
208/* Does the list have any members? */
209bool list_is_empty(void **list);
210
211/* Is the item inserted into a particular list? */
212bool list_is_member(void **list, void *item);
213
214/* Removes an item from a list - returns true if item was found
215 * and thus removed. */
216bool list_remove_item(void **list, void *item);
217
218/* Adds a list item, insert last, if not already present. */
219void list_add_item(void **list, void *item);
220
221/* Clears the entire list. */
222void list_clear_all(void **list);
223
224/* Enumerate all items in the array. */
225typedef bool (*list_enum_callback_t)(void *item, void* data);
226
227void list_enum_items(void **list,
228 list_enum_callback_t callback,
229 void *data);
230
231
232/** System events **/
233
234/* Clear event */
235void mpeg_sysevent_clear(void);
236
237/* Set to ACTION_STD_CANCEL */
238void mpeg_sysevent_set(void);
239
240/* Get event code */
241long mpeg_sysevent(void);
242
243/* Call with a system event code and used as menu callback */
244int mpeg_sysevent_callback(int btn, const struct menu_item_ex *menu,
245 struct gui_synclist *this_list);
246
247/* Handle recorded event */
248void mpeg_sysevent_handle(void);
249
250
251/** Buttons **/
252
253/* Get button codes while remembering important events for later
254 * processing; return of ACTION_STD_CANCEL means plugin should
255 * abort and handle the event */
256int mpeg_button_get(int timeout);
257
258#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Parser for MPEG streams
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "plugin.h"
24#include "mpegplayer.h"
25
26struct stream_parser str_parser SHAREDBSS_ATTR;
27
28static void parser_init_state(void)
29{
30 str_parser.last_seek_time = 0;
31 str_parser.format = STREAM_FMT_UNKNOWN;
32 str_parser.start_pts = INVALID_TIMESTAMP;
33 str_parser.end_pts = INVALID_TIMESTAMP;
34 str_parser.flags = 0;
35 str_parser.dims.w = 0;
36 str_parser.dims.h = 0;
37}
38
39/* Place the stream in a state to begin parsing - sync will be performed
40 * first */
41void str_initialize(struct stream *str, off_t pos)
42{
43 /* Initial positions start here */
44 str->hdr.win_left = str->hdr.win_right = pos;
45 /* No packet */
46 str->curr_packet = NULL;
47 /* Pick up parsing from this point in the buffer */
48 str->curr_packet_end = disk_buf_offset2ptr(pos);
49 /* No flags */
50 str->pkt_flags = 0;
51 /* Sync first */
52 str->state = SSTATE_SYNC;
53}
54
55/* Place the stream in an end of data state */
56void str_end_of_stream(struct stream *str)
57{
58 /* Offsets that prevent this stream from being included in the
59 * min left/max right window so that no buffering is triggered on
60 * its behalf. Set right to the min first so a thread reading the
61 * overall window gets doesn't see this as valid no matter what the
62 * file length. */
63 str->hdr.win_right = OFF_T_MIN;
64 str->hdr.win_left = OFF_T_MAX;
65 /* No packets */
66 str->curr_packet = str->curr_packet_end = NULL;
67 /* No flags */
68 str->pkt_flags = 0;
69 /* Fin */
70 str->state = SSTATE_END;
71}
72
73/* Return a timestamp at address p+offset if the marker bits are in tact */
74static inline uint32_t read_pts(uint8_t *p, off_t offset)
75{
76 return TS_CHECK_MARKERS(p, offset) ?
77 TS_FROM_HEADER(p, offset) : INVALID_TIMESTAMP;
78}
79
80static inline bool validate_timestamp(uint32_t ts)
81{
82 return ts >= str_parser.start_pts && ts <= str_parser.end_pts;
83}
84
85/* Find a start code before or after a given position */
86uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code)
87{
88 stream_scan_normalize(sk);
89
90 if (sk->dir < 0)
91 {
92 /* Reverse scan - start with at least the min needed */
93 stream_scan_offset(sk, 4);
94 }
95
96 code &= 0xff; /* Only the low byte matters */
97
98 while (sk->len >= 0 && sk->margin >= 4)
99 {
100 uint8_t *p;
101 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
102 ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p);
103
104 if (pos < 0 || len < 4)
105 break;
106
107 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == code)
108 {
109 return p;
110 }
111
112 stream_scan_offset(sk, 1);
113 }
114
115 return NULL;
116}
117
118/* Find a PES packet header for any stream - return stream to which it
119 * belongs */
120unsigned mpeg_parser_scan_pes(struct stream_scan *sk)
121{
122 stream_scan_normalize(sk);
123
124 if (sk->dir < 0)
125 {
126 /* Reverse scan - start with at least the min needed */
127 stream_scan_offset(sk, 4);
128 }
129
130 while (sk->len >= 0 && sk->margin >= 4)
131 {
132 uint8_t *p;
133 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
134 ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p);
135
136 if (pos < 0 || len < 4)
137 break;
138
139 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
140 {
141 unsigned id = p[3];
142 if (id >= 0xb9)
143 return id; /* PES header */
144 /* else some video stream element */
145 }
146
147 stream_scan_offset(sk, 1);
148 }
149
150 return -1;
151}
152
153/* Return the first SCR found from the scan direction */
154uint32_t mpeg_parser_scan_scr(struct stream_scan *sk)
155{
156 uint8_t *p = mpeg_parser_scan_start_code(sk, MPEG_STREAM_PACK_HEADER);
157
158 if (p != NULL && sk->margin >= 9) /* 9 bytes total required */
159 {
160 sk->data = 9;
161
162 if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */
163 {
164 /* Lookhead p+8 */
165 if (MPEG2_CHECK_PACK_SCR_MARKERS(p, 4))
166 return MPEG2_PACK_HEADER_SCR(p, 4);
167 }
168 else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */
169 {
170 /* Lookahead p+8 */
171 if (TS_CHECK_MARKERS(p, 4))
172 return TS_FROM_HEADER(p, 4);
173 }
174 /* Weird pack header */
175 sk->data = 5;
176 }
177
178 return INVALID_TIMESTAMP;
179}
180
181uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id)
182{
183 stream_scan_normalize(sk);
184
185 if (sk->dir < 0)
186 {
187 /* Reverse scan - start with at least the min needed */
188 stream_scan_offset(sk, 4);
189 }
190
191 while (sk->len >= 0 && sk->margin >= 4)
192 {
193 uint8_t *p;
194 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
195 ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 30, &p);
196
197 if (pos < 0 || len < 4)
198 break;
199
200 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == id)
201 {
202 uint8_t *h = p;
203
204 if (sk->margin < 7)
205 {
206 /* Insufficient data */
207 }
208 else if ((h[6] & 0xc0) == 0x80) /* mpeg2 */
209 {
210 if (sk->margin >= 14 && (h[7] & 0x80) != 0x00)
211 {
212 sk->data = 14;
213 return read_pts(h, 9);
214 }
215 }
216 else /* mpeg1 */
217 {
218 ssize_t l = 6;
219 ssize_t margin = sk->margin;
220
221 /* Skip stuffing_byte */
222 while (margin > 7 && h[l] == 0xff && ++l <= 22)
223 --margin;
224
225 if (margin >= 7)
226 {
227 if ((h[l] & 0xc0) == 0x40)
228 {
229 /* Skip STD_buffer_scale and STD_buffer_size */
230 margin -= 2;
231 l += 2;
232 }
233
234 if (margin >= 5)
235 {
236 /* Header points to the mpeg1 pes header */
237 h += l;
238
239 if ((h[0] & 0xe0) == 0x20)
240 {
241 /* PTS or PTS_DTS indicated */
242 sk->data = (h + 5) - p;
243 return read_pts(h, 0);
244 }
245 }
246 }
247 }
248 /* No PTS present - keep searching for a matching PES header with
249 * one */
250 }
251
252 stream_scan_offset(sk, 1);
253 }
254
255 return INVALID_TIMESTAMP;
256}
257
258static bool init_video_info(void)
259{
260 DEBUGF("Getting movie size\n");
261
262 /* The decoder handles this in order to initialize its knowledge of the
263 * movie parameters making seeking easier */
264 str_send_msg(&video_str, STREAM_RESET, 0);
265 if (str_send_msg(&video_str, VIDEO_GET_SIZE,
266 (intptr_t)&str_parser.dims) != 0)
267 {
268 return true;
269 }
270
271 DEBUGF(" failed\n");
272 return false;
273}
274
275static bool init_times(struct stream *str)
276{
277 struct stream tmp_str;
278 const ssize_t filesize = disk_buf_filesize();
279 const ssize_t max_probe = MIN(512*1024, filesize);
280 bool found_stream;
281
282 /* Simply find the first earliest timestamp - this will be the one
283 * used when streaming anyway */
284 DEBUGF("Finding start_pts: 0x%02x\n", str->id);
285
286 found_stream = false;
287 str->start_pts = INVALID_TIMESTAMP;
288 str->end_pts = INVALID_TIMESTAMP;
289
290 tmp_str.id = str->id;
291 tmp_str.hdr.pos = 0;
292 tmp_str.hdr.limit = max_probe;
293
294 /* Probe for many for the start because some stamps could be anomalous.
295 * Video also can also have things out of order. Just see what it's got.
296 */
297 while (1)
298 {
299 switch (parser_get_next_data(&tmp_str, STREAM_PM_RANDOM_ACCESS))
300 {
301 case STREAM_DATA_END:
302 break;
303 case STREAM_OK:
304 found_stream = true;
305 if (tmp_str.pkt_flags & PKT_HAS_TS)
306 {
307 if (tmp_str.pts < str->start_pts)
308 str->start_pts = tmp_str.pts;
309 }
310 continue;
311 }
312
313 break;
314 }
315
316 if (!found_stream)
317 {
318 DEBUGF(" stream not found:0x%02x\n", str->id);
319 return false;
320 }
321
322 DEBUGF(" start:%u\n", (unsigned)str->start_pts);
323
324 /* Use the decoder thread to perform a synchronized search - no
325 * decoding should take place but just a simple run through timestamps
326 * and durations as the decoder would see them. This should give the
327 * precise time at the end of the last frame for the stream. */
328 DEBUGF("Finding end_pts: 0x%02x\n", str->id);
329
330 str_parser.parms.sd.time = MAX_TIMESTAMP;
331 str_parser.parms.sd.sk.pos = filesize - max_probe;
332 str_parser.parms.sd.sk.len = max_probe;
333 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
334
335 str_send_msg(str, STREAM_RESET, 0);
336
337 if (str_send_msg(str, STREAM_FIND_END_TIME,
338 (intptr_t)&str_parser.parms.sd) == STREAM_PERFECT_MATCH)
339 {
340 str->end_pts = str_parser.parms.sd.time;
341 DEBUGF(" end:%u\n", (unsigned)str->end_pts);
342 }
343
344 return true;
345}
346
347static bool check_times(const struct stream *str)
348{
349 return str->start_pts < str->end_pts &&
350 str->end_pts != INVALID_TIMESTAMP;
351}
352
353/* Return the best-fit file offset of a timestamp in the PES where
354 * timstamp <= time < next timestamp. Will try to return something reasonably
355 * valid if best-fit could not be made. */
356static off_t mpeg_parser_seek_PTS(uint32_t time, unsigned id)
357{
358 ssize_t pos_left = 0;
359 ssize_t pos_right = disk_buf.filesize;
360 ssize_t pos, pos_new;
361 uint32_t time_left = str_parser.start_pts;
362 uint32_t time_right = str_parser.end_pts;
363 uint32_t pts = 0;
364 uint32_t prevpts = 0;
365 enum state_enum state = STATE0;
366 struct stream_scan sk;
367
368 stream_scan_init(&sk);
369
370 /* Initial estimate taken from average bitrate - later interpolations are
371 * taken similarly based on the remaining file interval */
372 pos_new = muldiv_uint32(time - time_left, pos_right - pos_left,
373 time_right - time_left) + pos_left;
374
375 /* return this estimated position if nothing better comes up */
376 pos = pos_new;
377
378 DEBUGF("Seeking stream 0x%02x\n", id);
379 DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n",
380 (unsigned)time_left, (unsigned)time, (unsigned)time_right,
381 (long)pos_left, (long)pos_new, (long)pos_right);
382
383 sk.dir = SSCAN_REVERSE;
384
385 while (state < STATE9)
386 {
387 uint32_t currpts;
388 sk.pos = pos_new;
389 sk.len = (sk.dir < 0) ? pos_new - pos_left : pos_right - pos_new;
390
391 currpts = mpeg_parser_scan_pts(&sk, id);
392
393 if (currpts != INVALID_TIMESTAMP)
394 {
395 ssize_t pos_adj; /* Adjustment to over or under-estimate */
396
397 /* Found a valid timestamp - see were it lies in relation to
398 * target */
399 if (currpts < time)
400 {
401 /* Time at current position is before seek time - move
402 * forward */
403 if (currpts > pts)
404 {
405 /* This is less than the desired time but greater than
406 * the currently seeked one; move the position up */
407 pts = currpts;
408 pos = sk.pos;
409 }
410
411 /* No next timestamp can be sooner */
412 pos_left = sk.pos + sk.data;
413 time_left = currpts;
414
415 if (pos_right <= pos_left)
416 break; /* If the window disappeared - we're done */
417
418 pos_new = muldiv_uint32(time - time_left,
419 pos_right - pos_left,
420 time_right - time_left);
421 /* Point is ahead of us - fudge estimate a bit high */
422 pos_adj = pos_new / 10;
423
424 if (pos_adj > 512*1024)
425 pos_adj = 512*1024;
426
427 pos_new += pos_left + pos_adj;
428
429 if (pos_new >= pos_right)
430 {
431 /* Estimate could push too far */
432 pos_new = pos_right;
433 }
434
435 state = STATE2; /* Last scan was early */
436 sk.dir = SSCAN_REVERSE;
437
438 DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
439 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
440 (unsigned)time_right, (long)pos_left, (long)pos_new,
441 (long)pos_right);
442 }
443 else if (currpts > time)
444 {
445 /* Time at current position is past seek time - move
446 backward */
447 pos_right = sk.pos;
448 time_right = currpts;
449
450 if (pos_right <= pos_left)
451 break; /* If the window disappeared - we're done */
452
453 pos_new = muldiv_uint32(time - time_left,
454 pos_right - pos_left,
455 time_right - time_left);
456 /* Overshot the seek point - fudge estimate a bit low */
457 pos_adj = pos_new / 10;
458
459 if (pos_adj > 512*1024)
460 pos_adj = 512*1024;
461
462 pos_new += pos_left - pos_adj;
463
464 state = STATE3; /* Last scan was late */
465 sk.dir = SSCAN_REVERSE;
466
467 DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
468 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
469 (unsigned)time_right, (long)pos_left, (long)pos_new,
470 (long)pos_right);
471 }
472 else
473 {
474 /* Exact match - it happens */
475 DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
476 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
477 (unsigned)time_right, (long)pos_left, (long)pos_new,
478 (long)pos_right);
479 pts = currpts;
480 pos = sk.pos;
481 state = STATE9;
482 }
483 }
484 else
485 {
486 /* Nothing found */
487
488 switch (state)
489 {
490 case STATE1:
491 /* We already tried the bruteforce scan and failed again - no
492 * more stamps could possibly exist in the interval */
493 DEBUGF("!! no timestamp 2x\n");
494 break;
495 case STATE0:
496 /* Hardly likely except at very beginning - just do L->R scan
497 * to find something */
498 DEBUGF("!! no timestamp on first probe: %ld\n", sk.pos);
499 case STATE2:
500 case STATE3:
501 /* Could just be missing timestamps because the interval is
502 * narrowing down. A large block of data from another stream
503 * may also be in the midst of our chosen points which could
504 * cluster at either extreme end. If anything is there, this
505 * will find it. */
506 pos_new = pos_left;
507 sk.dir = SSCAN_FORWARD;
508 DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
509 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
510 (unsigned)time_right, (long)pos_left, (long)pos_new,
511 (long)pos_right);
512 state = STATE1;
513 break;
514 default:
515 DEBUGF("?? Invalid state: %d\n", state);
516 }
517 }
518
519 /* Same timestamp twice = quit */
520 if (currpts == prevpts)
521 {
522 DEBUGF("!! currpts == prevpts (stop)\n");
523 state = STATE9;
524 }
525
526 prevpts = currpts;
527 }
528
529#if defined(DEBUG) || defined(SIMULATOR)
530 /* The next pts after the seeked-to position should be greater -
531 * most of the time - frames out of presentation order may muck it
532 * up a slight bit */
533 sk.pos = pos + 1;
534 sk.len = disk_buf.filesize;
535 sk.dir = SSCAN_FORWARD;
536
537 uint32_t nextpts = mpeg_parser_scan_pts(&sk, id);
538 DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n",
539 (long)pos, (unsigned)pts, (unsigned)time, (unsigned)nextpts);
540
541 if (pts <= time && time < nextpts)
542 {
543 /* Smile - it worked */
544 DEBUGF(" :) pts<=time<next pts\n");
545 }
546 else
547 {
548 /* See where things ended up */
549 if (pts > time)
550 {
551 /* Hmm */
552 DEBUGF(" :\\ pts>time\n");
553 }
554 if (pts >= nextpts)
555 {
556 /* Weird - probably because of encoded order & tends to be right
557 * anyway if other criteria are met */
558 DEBUGF(" :p pts>=next pts\n");
559 }
560 if (time >= nextpts)
561 {
562 /* Ugh */
563 DEBUGF(" :( time>=nextpts\n");
564 }
565 }
566#endif
567
568 return pos;
569}
570
571static void prepare_audio(uint32_t time)
572{
573 off_t pos;
574
575 if (!str_send_msg(&audio_str, STREAM_NEEDS_SYNC, time))
576 {
577 DEBUGF("Audio was ready\n");
578 return;
579 }
580
581 pos = mpeg_parser_seek_PTS(time, audio_str.id);
582 str_send_msg(&audio_str, STREAM_RESET, 0);
583
584 str_parser.parms.sd.time = time;
585 str_parser.parms.sd.sk.pos = pos;
586 str_parser.parms.sd.sk.len = 1024*1024;
587 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
588
589 str_send_msg(&audio_str, STREAM_SYNC, (intptr_t)&str_parser.parms.sd);
590}
591
592/* This function demuxes the streams and gives the next stream data
593 * pointer.
594 *
595 * STREAM_PM_STREAMING is for operation during playback. If the nescessary
596 * data and worst-case lookahead margin is not available, the stream is
597 * registered for notification when the data becomes available. If parsing
598 * extends beyond the end of the file or the end of stream marker is reached,
599 * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
600 *
601 * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
602 * If the file cache misses for the current position + lookahead, it will be
603 * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
604 * returned.
605 *
606 * The results from one mode may be used as input to the other. Random access
607 * requires cooperation amongst threads to avoid evicting another stream's
608 * data.
609 */
610static int parse_demux(struct stream *str, enum stream_parse_mode type)
611{
612 #define INC_BUF(offset) \
613 ({ off_t _o = (offset); \
614 str->hdr.win_right += _o; \
615 if ((p += _o) >= disk_buf.end) \
616 p -= disk_buf.size; })
617
618 static const int mpeg1_skip_table[16] =
619 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
620
621 uint8_t *p = str->curr_packet_end;
622
623 str->pkt_flags = 0;
624
625 while (1)
626 {
627 uint8_t *header;
628 unsigned id;
629 ssize_t length, bytes;
630
631 switch (type)
632 {
633 case STREAM_PM_STREAMING:
634 /* Has the end been reached already? */
635 switch (str->state)
636 {
637 case SSTATE_PARSE: /* Expected case first if no jumptable */
638 /* Are we at the end of file? */
639 if (str->hdr.win_left < disk_buf.filesize)
640 break;
641 str_end_of_stream(str);
642 return STREAM_DATA_END;
643
644 case SSTATE_SYNC:
645 /* Is sync at the end of file? */
646 if (str->hdr.win_right < disk_buf.filesize)
647 break;
648 str_end_of_stream(str);
649 /* Fall-through */
650 case SSTATE_END:
651 return STREAM_DATA_END;
652 }
653
654 if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD))
655 {
656 /* This data range is not buffered yet - register stream to
657 * be notified when it becomes available. Stream is obliged
658 * to enter a TSTATE_DATA state if it must wait. */
659 int res = str_next_data_not_ready(str);
660
661 if (res != STREAM_OK)
662 return res;
663 }
664 break;
665 /* STREAM_PM_STREAMING: */
666
667 case STREAM_PM_RANDOM_ACCESS:
668 str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET);
669
670 if (str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit ||
671 disk_buf_getbuffer(MIN_BUFAHEAD, &p, NULL, NULL) <= 0)
672 {
673 str_end_of_stream(str);
674 return STREAM_DATA_END;
675 }
676
677 str->state = SSTATE_SYNC;
678 str->hdr.win_left = str->hdr.pos;
679 str->curr_packet = NULL;
680 str->curr_packet_end = p;
681 break;
682 /* STREAM_PM_RANDOM_ACCESS: */
683 }
684
685 if (str->state == SSTATE_SYNC)
686 {
687 /* Scanning for start code */
688 if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
689 {
690 INC_BUF(1);
691 continue;
692 }
693 }
694
695 /* Found a start code - enter parse state */
696 str->state = SSTATE_PARSE;
697
698 /* Pack header, skip it */
699 if (CMP_4_CONST(p, PACK_START_CODE))
700 {
701 /* Max lookahead: 14 */
702 if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */
703 {
704 /* Max delta: 14 + 7 = 21 */
705 /* Skip pack header and any stuffing bytes*/
706 bytes = 14 + (p[13] & 7);
707 }
708 else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */
709 {
710 bytes = 12;
711 }
712 else /* unknown - skip it */
713 {
714 DEBUGF("weird pack header!\n");
715 bytes = 5;
716 }
717
718 INC_BUF(bytes);
719 }
720
721 /* System header, parse and skip it - 6 bytes + size */
722 if (CMP_4_CONST(p, SYSTEM_HEADER_START_CODE))
723 {
724 /* Skip start code */
725 /* Max Delta = 65535 + 6 = 65541 */
726 bytes = 6 + ((p[4] << 8) | p[5]);
727 INC_BUF(bytes);
728 }
729
730 /* Packet header, parse it */
731 if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
732 {
733 /* Problem? Meh...probably not but just a corrupted section.
734 * Try to resync the parser which will probably succeed. */
735 DEBUGF("packet start code prefix not found: 0x%02x\n"
736 " wl:%lu wr:%lu\n"
737 " p:%p cp:%p cpe:%p\n"
738 " dbs:%p dbe:%p dbt:%p\n",
739 str->id, str->hdr.win_left, str->hdr.win_right,
740 p, str->curr_packet, str->curr_packet_end,
741 disk_buf.start, disk_buf.end, disk_buf.tail);
742 str->state = SSTATE_SYNC;
743 INC_BUF(1); /* Next byte - this one's no good */
744 continue;
745 }
746
747 /* We retrieve basic infos */
748 /* Maximum packet length: 6 + 65535 = 65541 */
749 id = p[3];
750 length = ((p[4] << 8) | p[5]) + 6;
751
752 if (id != str->id)
753 {
754 switch (id)
755 {
756 case MPEG_STREAM_PROGRAM_END:
757 /* end of stream */
758 str_end_of_stream(str);
759 DEBUGF("MPEG program end: 0x%02x\n", str->id);
760 return STREAM_DATA_END;
761 case MPEG_STREAM_PACK_HEADER:
762 case MPEG_STREAM_SYSTEM_HEADER:
763 /* These shouldn't be here - no increment or resync
764 * since we'll pick it up above. */
765 continue;
766 default:
767 /* It's not the packet we're looking for, skip it */
768 INC_BUF(length);
769 continue;
770 }
771 }
772
773 /* Ok, it's our packet */
774 header = p;
775
776 if ((header[6] & 0xc0) == 0x80) /* mpeg2 */
777 {
778 /* Max Lookahead: 18 */
779 /* Min length: 9 */
780 /* Max length: 9 + 255 = 264 */
781 length = 9 + header[8];
782
783 /* header points to the mpeg2 pes header */
784 if ((header[7] & 0x80) != 0)
785 {
786 /* header has a pts */
787 uint32_t pts = read_pts(header, 9);
788
789 if (pts != INVALID_TIMESTAMP)
790 {
791 str->pts = pts;
792#if 0
793 /* DTS isn't used for anything since things just get
794 decoded ASAP but keep the code around */
795 if (STREAM_IS_VIDEO(id))
796 {
797 /* Video stream - header may have a dts as well */
798 str->dts = pts;
799
800 if (header[7] & 0x40) != 0x00)
801 {
802 pts = read_pts(header, 14);
803 if (pts != INVALID_TIMESTAMP)
804 str->dts = pts;
805 }
806 }
807#endif
808 str->pkt_flags |= PKT_HAS_TS;
809 }
810 }
811 }
812 else /* mpeg1 */
813 {
814 /* Max lookahead: 24 + 2 + 9 = 35 */
815 /* Max len_skip: 24 + 2 = 26 */
816 /* Min length: 7 */
817 /* Max length: 24 + 2 + 9 = 35 */
818 off_t len_skip;
819 uint8_t * ptsbuf;
820
821 length = 7;
822
823 while (header[length - 1] == 0xff)
824 {
825 if (++length > 23)
826 {
827 DEBUGF("Too much stuffing" );
828 break;
829 }
830 }
831
832 if ((header[length - 1] & 0xc0) == 0x40)
833 length += 2;
834
835 len_skip = length;
836 length += mpeg1_skip_table[header[length - 1] >> 4];
837
838 /* Header points to the mpeg1 pes header */
839 ptsbuf = header + len_skip;
840
841 if ((ptsbuf[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf, -1))
842 {
843 /* header has a pts */
844 uint32_t pts = read_pts(ptsbuf, -1);
845
846 if (pts != INVALID_TIMESTAMP)
847 {
848 str->pts = pts;
849#if 0
850 /* DTS isn't used for anything since things just get
851 decoded ASAP but keep the code around */
852 if (STREAM_IS_VIDEO(id))
853 {
854 /* Video stream - header may have a dts as well */
855 str->dts = pts;
856
857 if (ptsbuf[-1] & 0xf0) == 0x30)
858 {
859 pts = read_pts(ptsbuf, 4);
860
861 if (pts != INVALID_TIMESTAMP)
862 str->dts = pts;
863 }
864 }
865#endif
866 str->pkt_flags |= PKT_HAS_TS;
867 }
868 }
869 }
870
871 p += length;
872 /* Max bytes: 6 + 65535 - 7 = 65534 */
873 bytes = 6 + (header[4] << 8) + header[5] - length;
874
875 str->curr_packet = p;
876 str->curr_packet_end = p + bytes;
877 str->hdr.win_left = str->hdr.win_right + length;
878 str->hdr.win_right = str->hdr.win_left + bytes;
879
880 if (str->hdr.win_right > disk_buf.filesize)
881 {
882 /* No packet that exceeds end of file can be valid */
883 str_end_of_stream(str);
884 return STREAM_DATA_END;
885 }
886
887 return STREAM_OK;
888 } /* end while */
889
890 #undef INC_BUF
891}
892
893/* This simply reads data from the file one page at a time and returns a
894 * pointer to it in the buffer. */
895static int parse_elementary(struct stream *str, enum stream_parse_mode type)
896{
897 uint8_t *p;
898 ssize_t len = 0;
899
900 str->pkt_flags = 0;
901
902 switch (type)
903 {
904 case STREAM_PM_STREAMING:
905 /* Has the end been reached already? */
906 if (str->state == SSTATE_END)
907 return STREAM_DATA_END;
908
909 /* Are we at the end of file? */
910 if (str->hdr.win_left >= disk_buf.filesize)
911 {
912 str_end_of_stream(str);
913 return STREAM_DATA_END;
914 }
915
916 if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD))
917 {
918 /* This data range is not buffered yet - register stream to
919 * be notified when it becomes available. Stream is obliged
920 * to enter a TSTATE_DATA state if it must wait. */
921 int res = str_next_data_not_ready(str);
922
923 if (res != STREAM_OK)
924 return res;
925 }
926
927 len = DISK_BUF_PAGE_SIZE;
928
929 if ((size_t)(str->hdr.win_right + len) > (size_t)disk_buf.filesize)
930 len = disk_buf.filesize - str->hdr.win_right;
931
932 if (len <= 0)
933 {
934 str_end_of_stream(str);
935 return STREAM_DATA_END;
936 }
937
938 p = str->curr_packet_end;
939 if (p >= disk_buf.end)
940 p -= disk_buf.size;
941 break;
942 /* STREAM_PM_STREAMING: */
943
944 case STREAM_PM_RANDOM_ACCESS:
945 str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET);
946 len = disk_buf_getbuffer(DISK_BUF_PAGE_SIZE, &p, NULL, NULL);
947
948 if (len <= 0 || str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit)
949 {
950 str_end_of_stream(str);
951 return STREAM_DATA_END;
952 }
953 break;
954 /* STREAM_PM_RANDOM_ACCESS: */
955 }
956
957 str->state = SSTATE_PARSE;
958 str->curr_packet = p;
959 str->curr_packet_end = p + len;
960 str->hdr.win_left = str->hdr.win_right;
961 str->hdr.win_right = str->hdr.win_left + len;
962
963 return STREAM_OK;
964}
965
966bool parser_prepare_image(uint32_t time)
967{
968 struct stream_scan sk;
969 int tries;
970 int result;
971
972 stream_scan_init(&sk);
973
974 if (!str_send_msg(&video_str, STREAM_NEEDS_SYNC, time))
975 {
976 DEBUGF("Image was ready\n");
977 return true; /* Should already have the image */
978 }
979
980#ifdef HAVE_ADJUSTABLE_CPU_FREQ
981 rb->cpu_boost(true); /* No interference with trigger_cpu_boost */
982#endif
983
984 str_send_msg(&video_str, STREAM_RESET, 0);
985
986 sk.pos = parser_can_seek() ?
987 mpeg_parser_seek_PTS(time, video_str.id) : 0;
988 sk.len = sk.pos;
989 sk.dir = SSCAN_REVERSE;
990
991 tries = 1;
992try_again:
993
994 if (mpeg_parser_scan_start_code(&sk, MPEG_START_GOP))
995 {
996 DEBUGF("GOP found at: %ld\n", sk.pos);
997
998 unsigned id = mpeg_parser_scan_pes(&sk);
999
1000 if (id != video_str.id && sk.pos > 0)
1001 {
1002 /* Not part of our stream */
1003 DEBUGF(" wrong stream: 0x%02x\n", id);
1004 goto try_again;
1005 }
1006
1007 /* This will hit the PES header since it's known to be there */
1008 uint32_t pts = mpeg_parser_scan_pts(&sk, id);
1009
1010 if (pts == INVALID_TIMESTAMP || pts > time)
1011 {
1012 DEBUGF(" wrong timestamp: %u\n", (unsigned)pts);
1013 goto try_again;
1014 }
1015 }
1016
1017 str_parser.parms.sd.time = time;
1018 str_parser.parms.sd.sk.pos = MAX(sk.pos, 0);
1019 str_parser.parms.sd.sk.len = 1024*1024;
1020 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
1021
1022 DEBUGF("thumb pos:%ld len:%ld\n", str_parser.parms.sd.sk.pos,
1023 (long)str_parser.parms.sd.sk.len);
1024
1025 result = str_send_msg(&video_str, STREAM_SYNC,
1026 (intptr_t)&str_parser.parms.sd);
1027
1028 if (result != STREAM_PERFECT_MATCH)
1029 {
1030 /* Two tries should be all that is nescessary to find the exact frame
1031 * if the first GOP actually started later than the timestamp - the
1032 * GOP just prior must then start on or earlier. */
1033 if (++tries <= 2)
1034 goto try_again;
1035 }
1036
1037#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1038 rb->cpu_boost(false);
1039#endif
1040
1041 return result > STREAM_OK;
1042}
1043
1044/* Seek parser to the specified time and return absolute time.
1045 * No actual hard stuff is performed here. That's done when streaming is
1046 * about to begin or something from the current position is requested */
1047uint32_t parser_seek_time(uint32_t time)
1048{
1049 if (!parser_can_seek())
1050 time = 0;
1051 else if (time > str_parser.duration)
1052 time = str_parser.duration;
1053
1054 str_parser.last_seek_time = time + str_parser.start_pts;
1055 return str_parser.last_seek_time;
1056}
1057
1058void parser_prepare_streaming(void)
1059{
1060 struct stream_window sw;
1061
1062 DEBUGF("parser_prepare_streaming\n");
1063
1064 /* Prepare initial video frame */
1065 parser_prepare_image(str_parser.last_seek_time);
1066
1067 /* Sync audio stream */
1068 if (audio_str.start_pts != INVALID_TIMESTAMP)
1069 prepare_audio(str_parser.last_seek_time);
1070
1071 /* Prequeue some data and set buffer window */
1072 if (!stream_get_window(&sw))
1073 sw.left = sw.right = disk_buf.filesize;
1074
1075 DEBUGF(" swl:%ld swr:%ld\n", sw.left, sw.right);
1076
1077 if (sw.right > disk_buf.filesize - 4*MIN_BUFAHEAD)
1078 sw.right = disk_buf.filesize - 4*MIN_BUFAHEAD;
1079
1080 disk_buf_prepare_streaming(sw.left,
1081 sw.right - sw.left + 4*MIN_BUFAHEAD);
1082}
1083
1084int parser_init_stream(void)
1085{
1086 if (disk_buf.in_file < 0)
1087 return STREAM_ERROR;
1088
1089 /* TODO: Actually find which streams are available */
1090 audio_str.id = MPEG_STREAM_AUDIO_FIRST;
1091 video_str.id = MPEG_STREAM_VIDEO_FIRST;
1092
1093 /* Try to pull a video PES - if not found, try video init anyway which
1094 * should succeed if it really is a video-only stream */
1095 video_str.hdr.pos = 0;
1096 video_str.hdr.limit = 256*1024;
1097
1098 if (parse_demux(&video_str, STREAM_PM_RANDOM_ACCESS) == STREAM_OK)
1099 {
1100 /* Found a video packet - assume program stream */
1101 str_parser.format = STREAM_FMT_MPEG_PS;
1102 str_parser.next_data = parse_demux;
1103 }
1104 else
1105 {
1106 /* No PES element found - assume video elementary stream */
1107 str_parser.format = STREAM_FMT_MPV;
1108 str_parser.next_data = parse_elementary;
1109 }
1110
1111 if (!init_video_info())
1112 {
1113 /* Cannot determine video size, etc. */
1114 parser_init_state();
1115 return STREAM_UNSUPPORTED;
1116 }
1117
1118 if (str_parser.format == STREAM_FMT_MPEG_PS)
1119 {
1120 /* Initalize start_pts and end_pts with the length (in 45kHz units) of
1121 * the movie. INVALID_TIMESTAMP if the time could not be determined */
1122 if (!init_times(&video_str) || !check_times(&video_str))
1123 {
1124 /* Must have video at least */
1125 parser_init_state();
1126 return STREAM_UNSUPPORTED;
1127 }
1128
1129 str_parser.flags |= STREAMF_CAN_SEEK;
1130
1131 if (init_times(&audio_str))
1132 {
1133 /* Audio will be part of playback pool */
1134 stream_add_stream(&audio_str);
1135
1136 if (check_times(&audio_str))
1137 {
1138 /* Overall duration is maximum span */
1139 str_parser.start_pts = MIN(audio_str.start_pts, video_str.start_pts);
1140 str_parser.end_pts = MAX(audio_str.end_pts, video_str.end_pts);
1141 }
1142 else
1143 {
1144 /* Bad times on audio - use video times */
1145 str_parser.start_pts = video_str.start_pts;
1146 str_parser.end_pts = video_str.end_pts;
1147
1148 /* Questionable: could use bitrate seek and match video to that */
1149 audio_str.start_pts = video_str.start_pts;
1150 audio_str.end_pts = video_str.end_pts;
1151 }
1152 }
1153 else
1154 {
1155 /* No audio stream - use video only */
1156 str_parser.start_pts = video_str.start_pts;
1157 str_parser.end_pts = video_str.end_pts;
1158 }
1159
1160 str_parser.last_seek_time = str_parser.start_pts;
1161 }
1162 else
1163 {
1164 /* There's no way to handle times on this without a full file
1165 * scan */
1166 audio_str.start_pts = INVALID_TIMESTAMP;
1167 audio_str.end_pts = INVALID_TIMESTAMP;
1168 video_str.start_pts = 0;
1169 video_str.end_pts = INVALID_TIMESTAMP;
1170 str_parser.start_pts = 0;
1171 str_parser.end_pts = INVALID_TIMESTAMP;
1172 }
1173
1174 /* Add video to playback pool */
1175 stream_add_stream(&video_str);
1176
1177 /* Cache duration - it's used very often */
1178 str_parser.duration = str_parser.end_pts - str_parser.start_pts;
1179
1180 DEBUGF("Movie info:\n"
1181 " size:%dx%d\n"
1182 " start:%u\n"
1183 " end:%u\n"
1184 " duration:%u\n",
1185 str_parser.dims.w, str_parser.dims.h,
1186 (unsigned)str_parser.start_pts,
1187 (unsigned)str_parser.end_pts,
1188 (unsigned)str_parser.duration);
1189
1190 return STREAM_OK;
1191}
1192
1193void parser_close_stream(void)
1194{
1195 stream_remove_streams();
1196 parser_init_state();
1197}
1198
1199bool parser_init(void)
1200{
1201 parser_init_state();
1202 return true;
1203}
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 @@
1#include "plugin.h"
2#include "lib/helper.h"
3#include "lib/configfile.h"
4
5#include "mpegplayer.h"
6#include "mpeg_settings.h"
7
8struct mpeg_settings settings;
9
10#define THUMB_DELAY (75*HZ/100)
11
12/* button definitions */
13#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
14 (CONFIG_KEYPAD == IRIVER_H300_PAD)
15#define MPEG_START_TIME_SELECT BUTTON_ON
16#define MPEG_START_TIME_LEFT BUTTON_LEFT
17#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
18#define MPEG_START_TIME_UP BUTTON_UP
19#define MPEG_START_TIME_DOWN BUTTON_DOWN
20#define MPEG_START_TIME_EXIT BUTTON_OFF
21
22#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
23#define MPEG_START_TIME_SELECT BUTTON_PLAY
24#define MPEG_START_TIME_LEFT BUTTON_LEFT
25#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
26#define MPEG_START_TIME_UP BUTTON_UP
27#define MPEG_START_TIME_DOWN BUTTON_DOWN
28#define MPEG_START_TIME_EXIT BUTTON_POWER
29
30#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
31 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
32 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
33#define MPEG_START_TIME_SELECT BUTTON_SELECT
34#define MPEG_START_TIME_LEFT BUTTON_LEFT
35#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
36#define MPEG_START_TIME_UP BUTTON_SCROLL_FWD
37#define MPEG_START_TIME_DOWN BUTTON_SCROLL_BACK
38#define MPEG_START_TIME_EXIT BUTTON_MENU
39
40#elif CONFIG_KEYPAD == GIGABEAT_PAD
41#define MPEG_START_TIME_SELECT BUTTON_SELECT
42#define MPEG_START_TIME_LEFT BUTTON_LEFT
43#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
44#define MPEG_START_TIME_UP BUTTON_UP
45#define MPEG_START_TIME_DOWN BUTTON_DOWN
46#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
47#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
48#define MPEG_START_TIME_EXIT BUTTON_POWER
49
50#define MPEG_START_TIME_RC_SELECT (BUTTON_RC_PLAY | BUTTON_REL)
51#define MPEG_START_TIME_RC_LEFT BUTTON_RC_REW
52#define MPEG_START_TIME_RC_RIGHT BUTTON_RC_FF
53#define MPEG_START_TIME_RC_UP BUTTON_RC_VOL_UP
54#define MPEG_START_TIME_RC_DOWN BUTTON_RC_VOL_DOWN
55#define MPEG_START_TIME_RC_EXIT (BUTTON_RC_PLAY | BUTTON_REPEAT)
56
57#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
58#define MPEG_START_TIME_SELECT BUTTON_SELECT
59#define MPEG_START_TIME_LEFT BUTTON_LEFT
60#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
61#define MPEG_START_TIME_UP BUTTON_UP
62#define MPEG_START_TIME_DOWN BUTTON_DOWN
63#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
64#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
65#define MPEG_START_TIME_EXIT BUTTON_POWER
66
67#define MPEG_START_TIME_RC_SELECT (BUTTON_RC_PLAY | BUTTON_REL)
68#define MPEG_START_TIME_RC_LEFT BUTTON_RC_REW
69#define MPEG_START_TIME_RC_RIGHT BUTTON_RC_FF
70#define MPEG_START_TIME_RC_UP BUTTON_RC_VOL_UP
71#define MPEG_START_TIME_RC_DOWN BUTTON_RC_VOL_DOWN
72#define MPEG_START_TIME_RC_EXIT (BUTTON_RC_PLAY | BUTTON_REPEAT)
73
74#elif CONFIG_KEYPAD == IRIVER_H10_PAD
75#define MPEG_START_TIME_SELECT BUTTON_PLAY
76#define MPEG_START_TIME_LEFT BUTTON_LEFT
77#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
78#define MPEG_START_TIME_UP BUTTON_SCROLL_UP
79#define MPEG_START_TIME_DOWN BUTTON_SCROLL_DOWN
80#define MPEG_START_TIME_EXIT BUTTON_POWER
81
82#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
83#define MPEG_START_TIME_SELECT BUTTON_SELECT
84#define MPEG_START_TIME_LEFT BUTTON_LEFT
85#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
86#define MPEG_START_TIME_UP BUTTON_UP
87#define MPEG_START_TIME_DOWN BUTTON_DOWN
88#define MPEG_START_TIME_LEFT2 BUTTON_SCROLL_BACK
89#define MPEG_START_TIME_RIGHT2 BUTTON_SCROLL_FWD
90#define MPEG_START_TIME_EXIT BUTTON_POWER
91
92#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
93#define MPEG_START_TIME_SELECT BUTTON_SELECT
94#define MPEG_START_TIME_LEFT BUTTON_LEFT
95#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
96#define MPEG_START_TIME_UP BUTTON_UP
97#define MPEG_START_TIME_DOWN BUTTON_DOWN
98#define MPEG_START_TIME_LEFT2 BUTTON_SCROLL_BACK
99#define MPEG_START_TIME_RIGHT2 BUTTON_SCROLL_FWD
100#define MPEG_START_TIME_EXIT (BUTTON_HOME|BUTTON_REPEAT)
101
102#elif (CONFIG_KEYPAD == SANSA_C200_PAD) || \
103(CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
104(CONFIG_KEYPAD == SANSA_M200_PAD)
105#define MPEG_START_TIME_SELECT BUTTON_SELECT
106#define MPEG_START_TIME_LEFT BUTTON_LEFT
107#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
108#define MPEG_START_TIME_UP BUTTON_UP
109#define MPEG_START_TIME_DOWN BUTTON_DOWN
110#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
111#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
112#define MPEG_START_TIME_EXIT BUTTON_POWER
113
114#elif CONFIG_KEYPAD == MROBE500_PAD
115#define MPEG_START_TIME_SELECT BUTTON_RC_HEART
116#define MPEG_START_TIME_LEFT BUTTON_LEFT
117#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
118#define MPEG_START_TIME_UP BUTTON_RC_PLAY
119#define MPEG_START_TIME_DOWN BUTTON_RC_DOWN
120#define MPEG_START_TIME_LEFT2 BUTTON_RC_VOL_UP
121#define MPEG_START_TIME_RIGHT2 BUTTON_RC_VOL_DOWN
122#define MPEG_START_TIME_EXIT BUTTON_POWER
123
124#elif CONFIG_KEYPAD == MROBE100_PAD
125#define MPEG_START_TIME_SELECT BUTTON_SELECT
126#define MPEG_START_TIME_LEFT BUTTON_LEFT
127#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
128#define MPEG_START_TIME_UP BUTTON_UP
129#define MPEG_START_TIME_DOWN BUTTON_DOWN
130#define MPEG_START_TIME_LEFT2 BUTTON_PLAY
131#define MPEG_START_TIME_RIGHT2 BUTTON_MENU
132#define MPEG_START_TIME_EXIT BUTTON_POWER
133
134#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
135#define MPEG_START_TIME_SELECT BUTTON_RC_PLAY
136#define MPEG_START_TIME_LEFT BUTTON_RC_REW
137#define MPEG_START_TIME_RIGHT BUTTON_RC_FF
138#define MPEG_START_TIME_UP BUTTON_RC_VOL_UP
139#define MPEG_START_TIME_DOWN BUTTON_RC_VOL_DOWN
140#define MPEG_START_TIME_EXIT BUTTON_RC_REC
141
142#elif CONFIG_KEYPAD == COWON_D2_PAD
143#define MPEG_START_TIME_EXIT BUTTON_POWER
144
145#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
146#define MPEG_START_TIME_SELECT BUTTON_SELECT
147#define MPEG_START_TIME_LEFT BUTTON_LEFT
148#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
149#define MPEG_START_TIME_UP BUTTON_UP
150#define MPEG_START_TIME_DOWN BUTTON_DOWN
151#define MPEG_START_TIME_LEFT2 BUTTON_PLAY
152#define MPEG_START_TIME_RIGHT2 BUTTON_MENU
153#define MPEG_START_TIME_EXIT BUTTON_BACK
154
155#elif (CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD)
156#define MPEG_START_TIME_SELECT (BUTTON_PLAY|BUTTON_REL)
157#define MPEG_START_TIME_LEFT BUTTON_BACK
158#define MPEG_START_TIME_RIGHT BUTTON_MENU
159#define MPEG_START_TIME_UP BUTTON_UP
160#define MPEG_START_TIME_DOWN BUTTON_DOWN
161#define MPEG_START_TIME_EXIT (BUTTON_PLAY|BUTTON_REPEAT)
162
163#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
164#define MPEG_START_TIME_SELECT BUTTON_SELECT
165#define MPEG_START_TIME_LEFT BUTTON_LEFT
166#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
167#define MPEG_START_TIME_UP BUTTON_UP
168#define MPEG_START_TIME_DOWN BUTTON_DOWN
169#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
170#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
171#define MPEG_START_TIME_EXIT BUTTON_POWER
172
173#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
174#define MPEG_START_TIME_SELECT BUTTON_PLAY
175#define MPEG_START_TIME_LEFT BUTTON_LEFT
176#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
177#define MPEG_START_TIME_UP BUTTON_UP
178#define MPEG_START_TIME_DOWN BUTTON_DOWN
179#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
180#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
181#define MPEG_START_TIME_EXIT BUTTON_POWER
182
183#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
184#define MPEG_START_TIME_SELECT BUTTON_PLAY
185#define MPEG_START_TIME_LEFT BUTTON_PREV
186#define MPEG_START_TIME_RIGHT BUTTON_NEXT
187#define MPEG_START_TIME_UP BUTTON_UP
188#define MPEG_START_TIME_DOWN BUTTON_DOWN
189#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
190#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
191#define MPEG_START_TIME_EXIT BUTTON_POWER
192
193#elif CONFIG_KEYPAD == ONDAVX747_PAD
194#define MPEG_START_TIME_EXIT BUTTON_POWER
195
196#elif CONFIG_KEYPAD == ONDAVX777_PAD
197#define MPEG_START_TIME_EXIT BUTTON_POWER
198
199#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
200 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
201#define MPEG_START_TIME_SELECT BUTTON_PLAY
202#define MPEG_START_TIME_LEFT BUTTON_LEFT
203#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
204#define MPEG_START_TIME_UP BUTTON_UP
205#define MPEG_START_TIME_DOWN BUTTON_DOWN
206#define MPEG_START_TIME_EXIT BUTTON_REW
207
208#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
209#define MPEG_START_TIME_SELECT BUTTON_PLAY
210#define MPEG_START_TIME_LEFT BUTTON_PREV
211#define MPEG_START_TIME_RIGHT BUTTON_NEXT
212#define MPEG_START_TIME_UP BUTTON_UP
213#define MPEG_START_TIME_DOWN BUTTON_DOWN
214#define MPEG_START_TIME_LEFT2 BUTTON_OK
215#define MPEG_START_TIME_RIGHT2 BUTTON_CANCEL
216#define MPEG_START_TIME_EXIT BUTTON_REC
217
218#elif CONFIG_KEYPAD == MPIO_HD200_PAD
219#define MPEG_START_TIME_SELECT BUTTON_FUNC
220#define MPEG_START_TIME_LEFT BUTTON_REW
221#define MPEG_START_TIME_RIGHT BUTTON_FF
222#define MPEG_START_TIME_UP BUTTON_VOL_UP
223#define MPEG_START_TIME_DOWN BUTTON_VOL_DOWN
224#define MPEG_START_TIME_EXIT BUTTON_REC
225
226#elif CONFIG_KEYPAD == MPIO_HD300_PAD
227#define MPEG_START_TIME_SELECT BUTTON_ENTER
228#define MPEG_START_TIME_LEFT BUTTON_REW
229#define MPEG_START_TIME_RIGHT BUTTON_FF
230#define MPEG_START_TIME_UP BUTTON_UP
231#define MPEG_START_TIME_DOWN BUTTON_DOWN
232#define MPEG_START_TIME_EXIT BUTTON_REC
233
234#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
235#define MPEG_START_TIME_SELECT BUTTON_SELECT
236#define MPEG_START_TIME_LEFT BUTTON_LEFT
237#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
238#define MPEG_START_TIME_UP BUTTON_UP
239#define MPEG_START_TIME_DOWN BUTTON_DOWN
240#define MPEG_START_TIME_EXIT BUTTON_POWER
241
242#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
243#define MPEG_START_TIME_SELECT BUTTON_SELECT
244#define MPEG_START_TIME_LEFT BUTTON_LEFT
245#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
246#define MPEG_START_TIME_UP BUTTON_UP
247#define MPEG_START_TIME_DOWN BUTTON_DOWN
248#define MPEG_START_TIME_EXIT BUTTON_POWER
249
250#elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
251#define MPEG_START_TIME_SELECT BUTTON_SELECT
252#define MPEG_START_TIME_LEFT BUTTON_LEFT
253#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
254#define MPEG_START_TIME_UP BUTTON_UP
255#define MPEG_START_TIME_DOWN BUTTON_DOWN
256#define MPEG_START_TIME_EXIT BUTTON_BACK
257
258#elif (CONFIG_KEYPAD == HM60X_PAD) || (CONFIG_KEYPAD == HM801_PAD)
259#define MPEG_START_TIME_SELECT BUTTON_SELECT
260#define MPEG_START_TIME_LEFT BUTTON_LEFT
261#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
262#define MPEG_START_TIME_UP BUTTON_UP
263#define MPEG_START_TIME_DOWN BUTTON_DOWN
264#define MPEG_START_TIME_EXIT BUTTON_POWER
265
266#elif CONFIG_KEYPAD == SONY_NWZ_PAD
267#define MPEG_START_TIME_SELECT BUTTON_PLAY
268#define MPEG_START_TIME_LEFT BUTTON_LEFT
269#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
270#define MPEG_START_TIME_UP BUTTON_UP
271#define MPEG_START_TIME_DOWN BUTTON_DOWN
272#define MPEG_START_TIME_EXIT BUTTON_BACK
273
274#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
275#define MPEG_START_TIME_SELECT BUTTON_SELECT
276#define MPEG_START_TIME_LEFT BUTTON_LEFT
277#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
278#define MPEG_START_TIME_UP BUTTON_UP
279#define MPEG_START_TIME_DOWN BUTTON_DOWN
280#define MPEG_START_TIME_EXIT BUTTON_BACK
281
282#elif CONFIG_KEYPAD == DX50_PAD
283#define MPEG_START_TIME_EXIT BUTTON_POWER
284#define MPEG_START_TIME_SELECT BUTTON_PLAY
285#define MPEG_START_TIME_LEFT BUTTON_LEFT
286#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
287#define MPEG_START_TIME_UP BUTTON_VOL_UP
288#define MPEG_START_TIME_DOWN BUTTON_VOL_DOWN
289
290#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
291#define MPEG_START_TIME_EXIT BUTTON_POWER
292
293#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD
294#define MPEG_START_TIME_SELECT BUTTON_SELECT
295#define MPEG_START_TIME_LEFT BUTTON_LEFT
296#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
297#define MPEG_START_TIME_UP BUTTON_UP
298#define MPEG_START_TIME_DOWN BUTTON_DOWN
299#define MPEG_START_TIME_EXIT BUTTON_POWER
300
301#elif CONFIG_KEYPAD == XDUOO_X3_PAD
302#define MPEG_START_TIME_SELECT BUTTON_PLAY
303#define MPEG_START_TIME_LEFT BUTTON_PREV
304#define MPEG_START_TIME_RIGHT BUTTON_NEXT
305#define MPEG_START_TIME_UP BUTTON_HOME
306#define MPEG_START_TIME_DOWN BUTTON_OPTION
307#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
308#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
309#define MPEG_START_TIME_EXIT BUTTON_POWER
310
311#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
312#define MPEG_START_TIME_SELECT BUTTON_PLAY
313#define MPEG_START_TIME_LEFT BUTTON_PREV
314#define MPEG_START_TIME_RIGHT BUTTON_NEXT
315#define MPEG_START_TIME_UP BUTTON_HOME
316#define MPEG_START_TIME_DOWN BUTTON_OPTION
317#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
318#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
319#define MPEG_START_TIME_EXIT BUTTON_POWER
320
321#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
322#define MPEG_START_TIME_SELECT BUTTON_PLAY
323#define MPEG_START_TIME_LEFT BUTTON_PREV
324#define MPEG_START_TIME_RIGHT BUTTON_NEXT
325#define MPEG_START_TIME_UP BUTTON_HOME
326#define MPEG_START_TIME_DOWN BUTTON_OPTION
327#define MPEG_START_TIME_LEFT2 BUTTON_VOL_UP
328#define MPEG_START_TIME_RIGHT2 BUTTON_VOL_DOWN
329#define MPEG_START_TIME_EXIT BUTTON_POWER
330
331#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
332#define MPEG_START_TIME_SELECT BUTTON_PLAY
333#define MPEG_START_TIME_LEFT BUTTON_HOME
334#define MPEG_START_TIME_RIGHT BUTTON_VOL_DOWN
335#define MPEG_START_TIME_UP BUTTON_PREV
336#define MPEG_START_TIME_DOWN BUTTON_NEXT
337#define MPEG_START_TIME_LEFT2 (BUTTON_POWER + BUTTON_HOME)
338#define MPEG_START_TIME_RIGHT2 (BUTTON_POWER + BUTTON_VOL_DOWN)
339#define MPEG_START_TIME_EXIT BUTTON_POWER
340
341#elif CONFIG_KEYPAD == EROSQ_PAD
342#define MPEG_START_TIME_SELECT BUTTON_PLAY
343#define MPEG_START_TIME_LEFT BUTTON_SCROLL_BACK
344#define MPEG_START_TIME_RIGHT BUTTON_SCROLL_FWD
345#define MPEG_START_TIME_UP BUTTON_PREV
346#define MPEG_START_TIME_DOWN BUTTON_NEXT
347#define MPEG_START_TIME_EXIT BUTTON_POWER
348
349#elif CONFIG_KEYPAD == FIIO_M3K_PAD
350#define MPEG_START_TIME_SELECT BUTTON_SELECT
351#define MPEG_START_TIME_LEFT BUTTON_LEFT
352#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
353#define MPEG_START_TIME_UP BUTTON_UP
354#define MPEG_START_TIME_DOWN BUTTON_DOWN
355#define MPEG_START_TIME_EXIT BUTTON_POWER
356
357#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
358#define MPEG_START_TIME_EXIT BUTTON_POWER
359
360#else
361#error No keymap defined!
362#endif
363
364#ifdef HAVE_TOUCHSCREEN
365#ifndef MPEG_START_TIME_SELECT
366#define MPEG_START_TIME_SELECT BUTTON_CENTER
367#endif
368#ifndef MPEG_START_TIME_LEFT
369#define MPEG_START_TIME_LEFT BUTTON_MIDLEFT
370#endif
371#ifndef MPEG_START_TIME_RIGHT
372#define MPEG_START_TIME_RIGHT BUTTON_MIDRIGHT
373#endif
374#ifndef MPEG_START_TIME_UP
375#define MPEG_START_TIME_UP BUTTON_TOPMIDDLE
376#endif
377#ifndef MPEG_START_TIME_DOWN
378#define MPEG_START_TIME_DOWN BUTTON_BOTTOMMIDDLE
379#endif
380#ifndef MPEG_START_TIME_LEFT2
381#define MPEG_START_TIME_LEFT2 BUTTON_TOPRIGHT
382#endif
383#ifndef MPEG_START_TIME_RIGHT2
384#define MPEG_START_TIME_RIGHT2 BUTTON_TOPLEFT
385#endif
386#ifndef MPEG_START_TIME_EXIT
387#define MPEG_START_TIME_EXIT BUTTON_TOPLEFT
388#endif
389#endif
390
391static struct configdata config[] =
392{
393 {TYPE_INT, 0, 2, { .int_p = &settings.showfps }, "Show FPS", NULL},
394 {TYPE_INT, 0, 2, { .int_p = &settings.limitfps }, "Limit FPS", NULL},
395 {TYPE_INT, 0, 2, { .int_p = &settings.skipframes }, "Skip frames", NULL},
396 {TYPE_INT, 0, INT_MAX, { .int_p = &settings.resume_count }, "Resume count",
397 NULL},
398 {TYPE_INT, 0, MPEG_RESUME_NUM_OPTIONS,
399 { .int_p = &settings.resume_options }, "Resume options", NULL},
400#if MPEG_OPTION_DITHERING_ENABLED
401 {TYPE_INT, 0, INT_MAX, { .int_p = &settings.displayoptions },
402 "Display options", NULL},
403#endif
404 {TYPE_INT, 0, 2, { .int_p = &settings.tone_controls }, "Tone controls",
405 NULL},
406 {TYPE_INT, 0, 2, { .int_p = &settings.channel_modes }, "Channel modes",
407 NULL},
408 {TYPE_INT, 0, 2, { .int_p = &settings.crossfeed }, "Crossfeed", NULL},
409 {TYPE_INT, 0, 2, { .int_p = &settings.equalizer }, "Equalizer", NULL},
410 {TYPE_INT, 0, 2, { .int_p = &settings.dithering }, "Dithering", NULL},
411 {TYPE_INT, 0, 2, { .int_p = &settings.play_mode }, "Play mode", NULL},
412#ifdef HAVE_BACKLIGHT_BRIGHTNESS
413 {TYPE_INT, -1, INT_MAX, { .int_p = &settings.backlight_brightness },
414 "Backlight brightness", NULL},
415#endif
416};
417
418static const struct opt_items noyes[2] = {
419 { STR(LANG_SET_BOOL_NO) },
420 { STR(LANG_SET_BOOL_YES) },
421};
422
423static const struct opt_items singleall[2] = {
424 { STR(LANG_SINGLE) },
425 { STR(LANG_ALL) },
426};
427
428static const struct opt_items globaloff[2] = {
429 { STR(LANG_OFF) },
430 { STR(LANG_USE_SOUND_SETTING) },
431};
432
433static void mpeg_settings(void);
434static bool mpeg_set_option(const char* string,
435 void* variable,
436 enum optiontype type,
437 const struct opt_items* options,
438 int numoptions,
439 void (*function)(int))
440{
441 mpeg_sysevent_clear();
442
443 /* This eats SYS_POWEROFF - :\ */
444 bool usb = rb->set_option(string, variable, type, options, numoptions,
445 function);
446
447 if (usb)
448 mpeg_sysevent_set();
449
450 return usb;
451}
452
453#ifdef HAVE_BACKLIGHT_BRIGHTNESS /* Only used for this atm */
454static bool mpeg_set_int(const char *string, const char *unit,
455 int voice_unit, const int *variable,
456 void (*function)(int), int step,
457 int min,
458 int max,
459 const char* (*formatter)(char*, size_t, int, const char*),
460 int32_t (*get_talk_id)(int, int))
461{
462 mpeg_sysevent_clear();
463
464 bool usb = rb->set_int_ex(string, unit, voice_unit, variable, function,
465 step, min, max, formatter, get_talk_id);
466
467 if (usb)
468 mpeg_sysevent_set();
469
470 return usb;
471}
472
473static int32_t backlight_brightness_getlang(int value, int unit)
474{
475 if (value < 0)
476 return LANG_USE_COMMON_SETTING;
477
478 return TALK_ID(value + MIN_BRIGHTNESS_SETTING, unit);
479}
480
481void mpeg_backlight_update_brightness(int value)
482{
483 if (value >= 0)
484 {
485 value += MIN_BRIGHTNESS_SETTING;
486 backlight_brightness_set(value);
487 }
488 else
489 {
490 backlight_brightness_use_setting();
491 }
492}
493
494static void backlight_brightness_function(int value)
495{
496 mpeg_backlight_update_brightness(value);
497}
498
499static const char* backlight_brightness_formatter(char *buf, size_t length,
500 int value, const char *input)
501{
502 (void)input;
503
504 if (value < 0)
505 return rb->str(LANG_USE_COMMON_SETTING);
506 else
507 rb->snprintf(buf, length, "%d", value + MIN_BRIGHTNESS_SETTING);
508 return buf;
509}
510#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
511
512/* Sync a particular audio setting to global or mpegplayer forced off */
513static void sync_audio_setting(int setting, bool global)
514{
515 switch (setting)
516 {
517 case MPEG_AUDIO_TONE_CONTROLS:
518 #ifdef AUDIOHW_HAVE_BASS
519 rb->sound_set(SOUND_BASS, (global || settings.tone_controls)
520 ? rb->global_settings->bass
521 : rb->sound_default(SOUND_BASS));
522 #endif
523 #ifdef AUDIOHW_HAVE_TREBLE
524 rb->sound_set(SOUND_TREBLE, (global || settings.tone_controls)
525 ? rb->global_settings->treble
526 : rb->sound_default(SOUND_TREBLE));
527 #endif
528
529 #ifdef AUDIOHW_HAVE_EQ
530 for (int band = 0;; band++)
531 {
532 int setting = rb->sound_enum_hw_eq_band_setting(band, AUDIOHW_EQ_GAIN);
533
534 if (setting == -1)
535 break;
536
537 rb->sound_set(setting, (global || settings.tone_controls)
538 ? rb->global_settings->hw_eq_bands[band].gain
539 : rb->sound_default(setting));
540 }
541 #endif /* AUDIOHW_HAVE_EQ */
542 break;
543
544 case MPEG_AUDIO_CHANNEL_MODES:
545 rb->sound_set(SOUND_CHANNELS, (global || settings.channel_modes)
546 ? rb->global_settings->channel_config
547 : SOUND_CHAN_STEREO);
548 break;
549
550 case MPEG_AUDIO_CROSSFEED:
551 rb->dsp_set_crossfeed_type((global || settings.crossfeed) ?
552 rb->global_settings->crossfeed :
553 CROSSFEED_TYPE_NONE);
554 break;
555
556 case MPEG_AUDIO_EQUALIZER:
557 rb->dsp_eq_enable((global || settings.equalizer) ?
558 rb->global_settings->eq_enabled : false);
559 break;
560
561 case MPEG_AUDIO_DITHERING:
562 rb->dsp_dither_enable((global || settings.dithering) ?
563 rb->global_settings->dithering_enabled : false);
564 break;
565 }
566}
567
568/* Sync all audio settings to global or mpegplayer forced off */
569static void sync_audio_settings(bool global)
570{
571 static const int setting_index[] =
572 {
573 MPEG_AUDIO_TONE_CONTROLS,
574 MPEG_AUDIO_CHANNEL_MODES,
575 MPEG_AUDIO_CROSSFEED,
576 MPEG_AUDIO_EQUALIZER,
577 MPEG_AUDIO_DITHERING,
578 };
579 unsigned i;
580
581 for (i = 0; i < ARRAYLEN(setting_index); i++)
582 {
583 sync_audio_setting(setting_index[i], global);
584 }
585}
586
587#ifndef HAVE_LCD_COLOR
588/* Cheapo splash implementation for the grey surface */
589static void grey_splash(int ticks, const unsigned char *fmt, ...)
590{
591 unsigned char buffer[256];
592 int x, y, w, h;
593 int oldfg, oldmode;
594
595 va_list ap;
596 va_start(ap, fmt);
597
598 rb->vsnprintf(buffer, sizeof (buffer), fmt, ap);
599
600 va_end(ap);
601
602 grey_getstringsize(buffer, &w, &h);
603
604 oldfg = grey_get_foreground();
605 oldmode = grey_get_drawmode();
606
607 grey_set_drawmode(DRMODE_FG);
608 grey_set_foreground(GREY_LIGHTGRAY);
609
610 x = (LCD_WIDTH - w) / 2;
611 y = (LCD_HEIGHT - h) / 2;
612
613 grey_fillrect(x - 1, y - 1, w + 2, h + 2);
614
615 grey_set_foreground(GREY_BLACK);
616
617 grey_putsxy(x, y, buffer);
618 grey_drawrect(x - 2, y - 2, w + 4, h + 4);
619
620 grey_set_foreground(oldfg);
621 grey_set_drawmode(oldmode);
622
623 grey_update();
624
625 if (ticks > 0)
626 rb->sleep(ticks);
627}
628#endif /* !HAVE_LCD_COLOR */
629
630static void show_loading(struct vo_rect *rc)
631{
632 int oldmode = mylcd_get_drawmode();
633 mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
634 mylcd_fillrect(rc->l-1, rc->t-1, rc->r - rc->l + 2, rc->b - rc->t + 2);
635 mylcd_set_drawmode(oldmode);
636 mylcd_splash(0, "Loading...");
637}
638
639static void draw_slider(uint32_t range, uint32_t pos, struct vo_rect *rc)
640{
641 #define SLIDER_WIDTH (LCD_WIDTH-SLIDER_LMARGIN-SLIDER_RMARGIN)
642 #define SLIDER_X SLIDER_LMARGIN
643 #define SLIDER_Y (LCD_HEIGHT-SLIDER_HEIGHT-SLIDER_BMARGIN)
644 #define SLIDER_HEIGHT 8
645 #define SLIDER_TEXTMARGIN 1
646 #define SLIDER_LMARGIN 1
647 #define SLIDER_RMARGIN 1
648 #define SLIDER_TMARGIN 1
649 #define SLIDER_BMARGIN 1
650 #define SCREEN_MARGIN 1
651
652 struct hms hms;
653 char str[32];
654 int text_w, text_h, text_y;
655
656 /* Put positition on left */
657 ts_to_hms(pos, &hms);
658 hms_format(str, sizeof(str), &hms);
659 mylcd_getstringsize(str, NULL, &text_h);
660 text_y = SLIDER_Y - SLIDER_TEXTMARGIN - text_h;
661
662 if (rc == NULL)
663 {
664 int oldmode = mylcd_get_drawmode();
665 mylcd_set_drawmode(DRMODE_BG | DRMODE_INVERSEVID);
666 mylcd_fillrect(SLIDER_X, text_y, SLIDER_WIDTH,
667 LCD_HEIGHT - SLIDER_BMARGIN - text_y
668 - SLIDER_TMARGIN);
669 mylcd_set_drawmode(oldmode);
670
671 mylcd_putsxy(SLIDER_X, text_y, str);
672
673 /* Put duration on right */
674 ts_to_hms(range, &hms);
675 hms_format(str, sizeof(str), &hms);
676 mylcd_getstringsize(str, &text_w, NULL);
677
678 mylcd_putsxy(SLIDER_X + SLIDER_WIDTH - text_w, text_y, str);
679
680 /* Draw slider */
681 mylcd_drawrect(SLIDER_X, SLIDER_Y, SLIDER_WIDTH, SLIDER_HEIGHT);
682 mylcd_fillrect(SLIDER_X, SLIDER_Y,
683 muldiv_uint32(pos, SLIDER_WIDTH, range),
684 SLIDER_HEIGHT);
685
686 /* Update screen */
687 mylcd_update_rect(SLIDER_X, text_y - SLIDER_TMARGIN, SLIDER_WIDTH,
688 LCD_HEIGHT - SLIDER_BMARGIN - text_y + SLIDER_TEXTMARGIN);
689 }
690 else
691 {
692 /* Just return slider rectangle */
693 rc->l = SLIDER_X;
694 rc->t = text_y - SLIDER_TMARGIN;
695 rc->r = rc->l + SLIDER_WIDTH;
696 rc->b = rc->t + LCD_HEIGHT - SLIDER_BMARGIN - text_y;
697 }
698}
699
700static bool display_thumb_image(const struct vo_rect *rc)
701{
702 bool retval = true;
703 unsigned ltgray = MYLCD_LIGHTGRAY;
704 unsigned dkgray = MYLCD_DARKGRAY;
705
706 int oldcolor = mylcd_get_foreground();
707
708 if (!stream_display_thumb(rc))
709 {
710 /* Display "No Frame" and erase any border */
711 const char * const str = "No Frame";
712 int x, y, w, h;
713
714 mylcd_getstringsize(str, &w, &h);
715 x = (rc->r + rc->l - w) / 2;
716 y = (rc->b + rc->t - h) / 2;
717 mylcd_putsxy(x, y, str);
718
719 mylcd_update_rect(x, y, w, h);
720
721 ltgray = dkgray = mylcd_get_background();
722 retval = false;
723 }
724
725 /* Draw a raised border around the frame (or erase if no frame) */
726
727 mylcd_set_foreground(ltgray);
728
729 mylcd_hline(rc->l-1, rc->r-1, rc->t-1);
730 mylcd_vline(rc->l-1, rc->t, rc->b-1);
731
732 mylcd_set_foreground(dkgray);
733
734 mylcd_hline(rc->l-1, rc->r, rc->b);
735 mylcd_vline(rc->r, rc->t-1, rc->b);
736
737 mylcd_set_foreground(oldcolor);
738
739 mylcd_update_rect(rc->l-1, rc->t-1, rc->r - rc->l + 2, 1);
740 mylcd_update_rect(rc->l-1, rc->t, 1, rc->b - rc->t);
741 mylcd_update_rect(rc->l-1, rc->b, rc->r - rc->l + 2, 1);
742 mylcd_update_rect(rc->r, rc->t, 1, rc->b - rc->t);
743
744 return retval;
745}
746
747/* Add an amount to the specified time - with saturation */
748static uint32_t increment_time(uint32_t val, int32_t amount, uint32_t range)
749{
750 if (amount < 0)
751 {
752 uint32_t off = -amount;
753 if (range > off && val >= off)
754 val -= off;
755 else
756 val = 0;
757 }
758 else if (amount > 0)
759 {
760 uint32_t off = amount;
761 if (range > off && val <= range - off)
762 val += off;
763 else
764 val = range;
765 }
766
767 return val;
768}
769
770#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
771static void get_start_time_lcd_enable_hook(unsigned short id, void *param)
772{
773 (void)id;
774 (void)param;
775 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_0, 0);
776}
777#endif /* HAVE_LCD_ENABLE */
778
779static int get_start_time(uint32_t duration)
780{
781 int button = 0;
782 int tmo = TIMEOUT_NOBLOCK;
783 uint32_t resume_time = settings.resume_time;
784 struct vo_rect rc_vid, rc_bound;
785 uint32_t aspect_vid, aspect_bound;
786 bool sliding = false;
787
788 enum state_enum slider_state = STATE0;
789
790 mylcd_clear_display();
791 mylcd_update();
792
793#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
794 rb->add_event(LCD_EVENT_ACTIVATION, get_start_time_lcd_enable_hook);
795#endif
796
797 draw_slider(0, 100, &rc_bound);
798 rc_bound.b = rc_bound.t - SLIDER_TMARGIN;
799 rc_bound.t = SCREEN_MARGIN;
800
801 DEBUGF("rc_bound: %d, %d, %d, %d\n", rc_bound.l, rc_bound.t,
802 rc_bound.r, rc_bound.b);
803
804 rc_vid.l = rc_vid.t = 0;
805 if (!stream_vo_get_size((struct vo_ext *)&rc_vid.r))
806 {
807 /* Can't get size - fill whole thing */
808 rc_vid.r = rc_bound.r - rc_bound.l;
809 rc_vid.b = rc_bound.b - rc_bound.t;
810 }
811
812 /* Get aspect ratio of bounding rectangle and video in u16.16 */
813 aspect_bound = ((rc_bound.r - rc_bound.l) << 16) /
814 (rc_bound.b - rc_bound.t);
815
816 DEBUGF("aspect_bound: %u.%02u\n", (unsigned)(aspect_bound >> 16),
817 (unsigned)(100*(aspect_bound & 0xffff) >> 16));
818
819 aspect_vid = (rc_vid.r << 16) / rc_vid.b;
820
821 DEBUGF("aspect_vid: %u.%02u\n", (unsigned)(aspect_vid >> 16),
822 (unsigned)(100*(aspect_vid & 0xffff) >> 16));
823
824 if (aspect_vid >= aspect_bound)
825 {
826 /* Video proportionally wider than or same as bounding rectangle */
827 if (rc_vid.r > rc_bound.r - rc_bound.l)
828 {
829 rc_vid.r = rc_bound.r - rc_bound.l;
830 rc_vid.b = (rc_vid.r << 16) / aspect_vid;
831 }
832 /* else already fits */
833 }
834 else
835 {
836 /* Video proportionally narrower than bounding rectangle */
837 if (rc_vid.b > rc_bound.b - rc_bound.t)
838 {
839 rc_vid.b = rc_bound.b - rc_bound.t;
840 rc_vid.r = (aspect_vid * rc_vid.b) >> 16;
841 }
842 /* else already fits */
843 }
844
845 /* Even width and height >= 2 */
846 rc_vid.r = (rc_vid.r < 2) ? 2 : (rc_vid.r & ~1);
847 rc_vid.b = (rc_vid.b < 2) ? 2 : (rc_vid.b & ~1);
848
849 /* Center display in bounding rectangle */
850 rc_vid.l = ((rc_bound.l + rc_bound.r) - rc_vid.r) / 2;
851 rc_vid.r += rc_vid.l;
852
853 rc_vid.t = ((rc_bound.t + rc_bound.b) - rc_vid.b) / 2;
854 rc_vid.b += rc_vid.t;
855
856 DEBUGF("rc_vid: %d, %d, %d, %d\n", rc_vid.l, rc_vid.t,
857 rc_vid.r, rc_vid.b);
858
859#ifndef HAVE_LCD_COLOR
860 stream_gray_show(true);
861#endif
862
863 while (slider_state < STATE9)
864 {
865 button = mpeg_button_get(tmo);
866
867 switch (button)
868 {
869 case BUTTON_NONE:
870 break;
871
872 /* Coarse (1 minute) control */
873 case MPEG_START_TIME_DOWN:
874 case MPEG_START_TIME_DOWN | BUTTON_REPEAT:
875#ifdef MPEG_START_TIME_RC_DOWN
876 case MPEG_START_TIME_RC_DOWN:
877 case MPEG_START_TIME_RC_DOWN | BUTTON_REPEAT:
878#endif
879 resume_time = increment_time(resume_time, -60*TS_SECOND, duration);
880 slider_state = STATE0;
881 break;
882
883 case MPEG_START_TIME_UP:
884 case MPEG_START_TIME_UP | BUTTON_REPEAT:
885#ifdef MPEG_START_TIME_RC_UP
886 case MPEG_START_TIME_RC_UP:
887 case MPEG_START_TIME_RC_UP | BUTTON_REPEAT:
888#endif
889 resume_time = increment_time(resume_time, 60*TS_SECOND, duration);
890 slider_state = STATE0;
891 break;
892
893 /* Fine (1 second) control */
894 case MPEG_START_TIME_LEFT:
895 case MPEG_START_TIME_LEFT | BUTTON_REPEAT:
896#ifdef MPEG_START_TIME_RC_LEFT
897 case MPEG_START_TIME_RC_LEFT:
898 case MPEG_START_TIME_RC_LEFT | BUTTON_REPEAT:
899#endif
900#ifdef MPEG_START_TIME_LEFT2
901 case MPEG_START_TIME_LEFT2:
902 case MPEG_START_TIME_LEFT2 | BUTTON_REPEAT:
903#endif
904 resume_time = increment_time(resume_time, -TS_SECOND, duration);
905 slider_state = STATE0;
906 break;
907
908 case MPEG_START_TIME_RIGHT:
909 case MPEG_START_TIME_RIGHT | BUTTON_REPEAT:
910#ifdef MPEG_START_TIME_RC_RIGHT
911 case MPEG_START_TIME_RC_RIGHT:
912 case MPEG_START_TIME_RC_RIGHT | BUTTON_REPEAT:
913#endif
914#ifdef MPEG_START_TIME_RIGHT2
915 case MPEG_START_TIME_RIGHT2:
916 case MPEG_START_TIME_RIGHT2 | BUTTON_REPEAT:
917#endif
918 resume_time = increment_time(resume_time, TS_SECOND, duration);
919 slider_state = STATE0;
920 break;
921
922 case MPEG_START_TIME_SELECT:
923#ifdef MPEG_START_TIME_RC_SELECT
924 case MPEG_START_TIME_RC_SELECT:
925#endif
926 settings.resume_time = resume_time;
927 button = MPEG_START_SEEK;
928 slider_state = STATE9;
929 break;
930
931 case MPEG_START_TIME_EXIT:
932#ifdef MPEG_START_TIME_RC_EXIT
933 case MPEG_START_TIME_RC_EXIT:
934#endif
935 button = MPEG_START_EXIT;
936 slider_state = STATE9;
937 break;
938
939 case ACTION_STD_CANCEL:
940 button = MPEG_START_QUIT;
941 slider_state = STATE9;
942 break;
943
944#ifdef HAVE_LCD_ENABLE
945 case LCD_ENABLE_EVENT_0:
946 if (slider_state == STATE2)
947 display_thumb_image(&rc_vid);
948 continue;
949#endif
950
951 default:
952 rb->default_event_handler(button);
953 rb->yield();
954 continue;
955 }
956
957 switch (slider_state)
958 {
959 case STATE0:
960 if (!sliding)
961 {
962 if (rb->global_settings->talk_menu)
963 {
964 rb->talk_disable(true);
965#ifdef PLUGIN_USE_IRAM
966 mpegplayer_iram_restore();
967#endif
968 }
969 trigger_cpu_boost();
970 sliding = true;
971 }
972 stream_seek(resume_time, SEEK_SET);
973 show_loading(&rc_bound);
974 draw_slider(duration, resume_time, NULL);
975 slider_state = STATE1;
976 tmo = THUMB_DELAY;
977 break;
978 case STATE1:
979 display_thumb_image(&rc_vid);
980 slider_state = STATE2;
981 tmo = TIMEOUT_BLOCK;
982 if (sliding)
983 {
984 cancel_cpu_boost();
985 if (rb->global_settings->talk_menu)
986 {
987#ifdef PLUGIN_USE_IRAM
988 mpegplayer_iram_preserve();
989#endif
990 rb->talk_disable(false);
991 talk_val(resume_time / TS_SECOND, UNIT_TIME, false);
992 talk_val(resume_time * 100 / duration, UNIT_PERCENT, true);
993 }
994 sliding = false;
995 }
996 default:
997 break;
998 }
999
1000 rb->yield();
1001 }
1002
1003#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1004 rb->remove_event(LCD_EVENT_ACTIVATION, get_start_time_lcd_enable_hook);
1005#endif
1006#ifndef HAVE_LCD_COLOR
1007 stream_gray_show(false);
1008 grey_clear_display();
1009 grey_update();
1010#endif
1011
1012 cancel_cpu_boost();
1013
1014 return button;
1015}
1016
1017static int show_start_menu(uint32_t duration)
1018{
1019 int selected = 0;
1020 int result = 0;
1021 bool menu_quit = false;
1022
1023 MENUITEM_STRINGLIST(menu, "Mpegplayer Menu", mpeg_sysevent_callback,
1024 ID2P(LANG_RESTART_PLAYBACK),
1025 ID2P(LANG_RESUME_PLAYBACK),
1026 ID2P(LANG_SET_RESUME_TIME),
1027 ID2P(LANG_SETTINGS),
1028 ID2P(LANG_MENU_QUIT));
1029
1030 if (rb->global_settings->talk_menu)
1031 {
1032#ifdef PLUGIN_USE_IRAM
1033 mpegplayer_iram_preserve();
1034#endif
1035 rb->talk_disable(false);
1036 }
1037
1038 rb->button_clear_queue();
1039
1040 while (!menu_quit)
1041 {
1042 mpeg_sysevent_clear();
1043 result = rb->do_menu(&menu, &selected, NULL, false);
1044
1045 switch (result)
1046 {
1047 case MPEG_START_RESTART:
1048 settings.resume_time = 0;
1049 menu_quit = true;
1050 break;
1051
1052 case MPEG_START_RESUME:
1053 menu_quit = true;
1054 break;
1055
1056 case MPEG_START_SEEK:
1057 if (!stream_can_seek())
1058 {
1059 rb->splash(HZ, ID2P(LANG_UNAVAILABLE));
1060 break;
1061 }
1062
1063 result = get_start_time(duration);
1064
1065 if (result != MPEG_START_EXIT)
1066 menu_quit = true;
1067 break;
1068
1069 case MPEG_START_SETTINGS:
1070 mpeg_settings();
1071 break;
1072
1073 default:
1074 result = MPEG_START_QUIT;
1075 menu_quit = true;
1076 break;
1077 }
1078
1079 if (mpeg_sysevent() != 0)
1080 {
1081 result = MPEG_START_QUIT;
1082 menu_quit = true;
1083 }
1084 }
1085
1086 if (rb->global_settings->talk_menu)
1087 {
1088 rb->talk_disable(true);
1089#ifdef PLUGIN_USE_IRAM
1090 mpegplayer_iram_restore();
1091#endif
1092 }
1093
1094 return result;
1095}
1096
1097/* Return the desired resume action */
1098int mpeg_start_menu(uint32_t duration)
1099{
1100 mpeg_sysevent_clear();
1101
1102 switch (settings.resume_options)
1103 {
1104 case MPEG_RESUME_MENU_IF_INCOMPLETE:
1105 if (!stream_can_seek() || settings.resume_time == 0)
1106 {
1107 case MPEG_RESUME_RESTART:
1108 settings.resume_time = 0;
1109 return MPEG_START_RESTART;
1110 }
1111 default:
1112 case MPEG_RESUME_MENU_ALWAYS:
1113 return show_start_menu(duration);
1114 case MPEG_RESUME_ALWAYS:
1115 return MPEG_START_SEEK;
1116 }
1117}
1118
1119int mpeg_menu(void)
1120{
1121 int result;
1122
1123 MENUITEM_STRINGLIST(menu, "Mpegplayer Menu", mpeg_sysevent_callback,
1124 ID2P(LANG_SETTINGS),
1125 ID2P(LANG_RESUME_PLAYBACK),
1126 ID2P(LANG_MENU_QUIT));
1127
1128 if (rb->global_settings->talk_menu)
1129 {
1130#ifdef PLUGIN_USE_IRAM
1131 mpegplayer_iram_preserve();
1132#endif
1133 rb->talk_disable(false);
1134 }
1135
1136 rb->button_clear_queue();
1137
1138 mpeg_sysevent_clear();
1139
1140 result = rb->do_menu(&menu, NULL, NULL, false);
1141
1142 switch (result)
1143 {
1144 case MPEG_MENU_SETTINGS:
1145 mpeg_settings();
1146 break;
1147
1148 case MPEG_MENU_RESUME:
1149 break;
1150
1151 case MPEG_MENU_QUIT:
1152 break;
1153
1154 default:
1155 break;
1156 }
1157
1158 if (mpeg_sysevent() != 0)
1159 result = MPEG_MENU_QUIT;
1160
1161 if (rb->global_settings->talk_menu)
1162 {
1163 rb->talk_disable(true);
1164#ifdef PLUGIN_USE_IRAM
1165 mpegplayer_iram_restore();
1166#endif
1167 }
1168
1169 return result;
1170}
1171
1172static void display_options(void)
1173{
1174 int selected = 0;
1175 int result;
1176 bool menu_quit = false;
1177
1178 MENUITEM_STRINGLIST(menu, "Display Options", mpeg_sysevent_callback,
1179#if MPEG_OPTION_DITHERING_ENABLED
1180 ID2P(LANG_DITHERING),
1181#endif
1182 ID2P(LANG_DISPLAY_FPS),
1183 ID2P(LANG_LIMIT_FPS),
1184 ID2P(LANG_SKIP_FRAMES),
1185#ifdef HAVE_BACKLIGHT_BRIGHTNESS
1186 ID2P(LANG_BACKLIGHT_BRIGHTNESS),
1187#endif
1188 );
1189
1190 rb->button_clear_queue();
1191
1192 while (!menu_quit)
1193 {
1194 mpeg_sysevent_clear();
1195 result = rb->do_menu(&menu, &selected, NULL, false);
1196
1197 switch (result)
1198 {
1199#if MPEG_OPTION_DITHERING_ENABLED
1200 case MPEG_OPTION_DITHERING:
1201 result = (settings.displayoptions & LCD_YUV_DITHER) ? 1 : 0;
1202 mpeg_set_option(rb->str(LANG_DITHERING), &result, INT, noyes, 2, NULL);
1203 settings.displayoptions =
1204 (settings.displayoptions & ~LCD_YUV_DITHER)
1205 | ((result != 0) ? LCD_YUV_DITHER : 0);
1206 rb->lcd_yuv_set_options(settings.displayoptions);
1207 break;
1208#endif /* MPEG_OPTION_DITHERING_ENABLED */
1209
1210 case MPEG_OPTION_DISPLAY_FPS:
1211 mpeg_set_option(rb->str(LANG_DISPLAY_FPS), &settings.showfps, INT,
1212 noyes, 2, NULL);
1213 break;
1214
1215 case MPEG_OPTION_LIMIT_FPS:
1216 mpeg_set_option(rb->str(LANG_LIMIT_FPS), &settings.limitfps, INT,
1217 noyes, 2, NULL);
1218 break;
1219
1220 case MPEG_OPTION_SKIP_FRAMES:
1221 mpeg_set_option(rb->str(LANG_SKIP_FRAMES), &settings.skipframes, INT,
1222 noyes, 2, NULL);
1223 break;
1224
1225#ifdef HAVE_BACKLIGHT_BRIGHTNESS
1226 case MPEG_OPTION_BACKLIGHT_BRIGHTNESS:
1227 result = settings.backlight_brightness;
1228 mpeg_backlight_update_brightness(result);
1229 mpeg_set_int(rb->str(LANG_BACKLIGHT_BRIGHTNESS), NULL, UNIT_INT, &result,
1230 backlight_brightness_function, 1, -1,
1231 MAX_BRIGHTNESS_SETTING - MIN_BRIGHTNESS_SETTING,
1232 backlight_brightness_formatter,
1233 backlight_brightness_getlang);
1234 settings.backlight_brightness = result;
1235 mpeg_backlight_update_brightness(-1);
1236 break;
1237#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
1238
1239 default:
1240 menu_quit = true;
1241 break;
1242 }
1243
1244 if (mpeg_sysevent() != 0)
1245 menu_quit = true;
1246 }
1247}
1248
1249static void audio_options(void)
1250{
1251 int selected = 0;
1252 int result;
1253 bool menu_quit = false;
1254
1255 MENUITEM_STRINGLIST(menu, "Audio Options", mpeg_sysevent_callback,
1256 ID2P(LANG_TONE_CONTROLS),
1257 ID2P(LANG_CHANNEL_CONFIGURATION),
1258 ID2P(LANG_CROSSFEED),
1259 ID2P(LANG_EQUALIZER),
1260 ID2P(LANG_DITHERING));
1261
1262 rb->button_clear_queue();
1263
1264 while (!menu_quit)
1265 {
1266 mpeg_sysevent_clear();
1267 result = rb->do_menu(&menu, &selected, NULL, false);
1268
1269 switch (result)
1270 {
1271 case MPEG_AUDIO_TONE_CONTROLS:
1272 mpeg_set_option(rb->str(LANG_TONE_CONTROLS), &settings.tone_controls, INT,
1273 globaloff, 2, NULL);
1274 sync_audio_setting(result, false);
1275 break;
1276
1277 case MPEG_AUDIO_CHANNEL_MODES:
1278 mpeg_set_option(rb->str(LANG_CHANNEL_CONFIGURATION), &settings.channel_modes,
1279 INT, globaloff, 2, NULL);
1280 sync_audio_setting(result, false);
1281 break;
1282
1283 case MPEG_AUDIO_CROSSFEED:
1284 mpeg_set_option(rb->str(LANG_CROSSFEED), &settings.crossfeed, INT,
1285 globaloff, 2, NULL);
1286 sync_audio_setting(result, false);
1287 break;
1288
1289 case MPEG_AUDIO_EQUALIZER:
1290 mpeg_set_option(rb->str(LANG_EQUALIZER), &settings.equalizer, INT,
1291 globaloff, 2, NULL);
1292 sync_audio_setting(result, false);
1293 break;
1294
1295 case MPEG_AUDIO_DITHERING:
1296 mpeg_set_option(rb->str(LANG_DITHERING), &settings.dithering, INT,
1297 globaloff, 2, NULL);
1298 sync_audio_setting(result, false);
1299 break;
1300
1301 default:
1302 menu_quit = true;
1303 break;
1304 }
1305
1306 if (mpeg_sysevent() != 0)
1307 menu_quit = true;
1308 }
1309}
1310
1311static void resume_options(void)
1312{
1313 static const struct opt_items items[MPEG_RESUME_NUM_OPTIONS] = {
1314 [MPEG_RESUME_MENU_ALWAYS] =
1315 { STR(LANG_FORCE_START_MENU) },
1316 [MPEG_RESUME_MENU_IF_INCOMPLETE] =
1317 { STR(LANG_CONDITIONAL_START_MENU) },
1318 [MPEG_RESUME_ALWAYS] =
1319 { STR(LANG_AUTO_RESUME) },
1320 [MPEG_RESUME_RESTART] =
1321 { STR(LANG_RESTART_PLAYBACK) },
1322 };
1323
1324 mpeg_set_option(rb->str(LANG_MENU_RESUME_OPTIONS), &settings.resume_options,
1325 INT, items, MPEG_RESUME_NUM_OPTIONS, NULL);
1326}
1327
1328static void clear_resume_count(void)
1329{
1330 settings.resume_count = 0;
1331 configfile_save(SETTINGS_FILENAME, config, ARRAYLEN(config),
1332 SETTINGS_VERSION);
1333}
1334
1335static void mpeg_settings(void)
1336{
1337 int selected = 0;
1338 int result;
1339 bool menu_quit = false;
1340
1341 MENUITEM_STRINGLIST(menu, "Settings", mpeg_sysevent_callback,
1342 ID2P(LANG_MENU_DISPLAY_OPTIONS),
1343 ID2P(LANG_MENU_AUDIO_OPTIONS),
1344 ID2P(LANG_MENU_RESUME_OPTIONS),
1345 ID2P(LANG_MENU_PLAY_MODE),
1346 ID2P(LANG_CLEAR_ALL_RESUMES));
1347
1348 rb->button_clear_queue();
1349
1350 while (!menu_quit)
1351 {
1352 mpeg_sysevent_clear();
1353
1354 result = rb->do_menu(&menu, &selected, NULL, false);
1355
1356 switch (result)
1357 {
1358 case MPEG_SETTING_DISPLAY_SETTINGS:
1359 display_options();
1360 break;
1361
1362 case MPEG_SETTING_AUDIO_SETTINGS:
1363 audio_options();
1364 break;
1365
1366 case MPEG_SETTING_ENABLE_START_MENU:
1367 resume_options();
1368 break;
1369
1370 case MPEG_SETTING_PLAY_MODE:
1371 mpeg_set_option(rb->str(LANG_MENU_PLAY_MODE), &settings.play_mode,
1372 INT, singleall, 2, NULL);
1373 break;
1374
1375 case MPEG_SETTING_CLEAR_RESUMES:
1376 clear_resume_count();
1377 break;
1378
1379 default:
1380 menu_quit = true;
1381 break;
1382 }
1383
1384 if (mpeg_sysevent() != 0)
1385 menu_quit = true;
1386 }
1387}
1388
1389void init_settings(const char* filename)
1390{
1391 /* Set the default settings */
1392 settings.showfps = 0; /* Do not show FPS */
1393 settings.limitfps = 1; /* Limit FPS */
1394 settings.skipframes = 1; /* Skip frames */
1395 settings.play_mode = 0; /* Play single video */
1396 settings.resume_options = MPEG_RESUME_MENU_ALWAYS; /* Enable start menu */
1397 settings.resume_count = 0;
1398#ifdef HAVE_BACKLIGHT_BRIGHTNESS
1399 settings.backlight_brightness = -1; /* Use default setting */
1400#endif
1401#if MPEG_OPTION_DITHERING_ENABLED
1402 settings.displayoptions = 0; /* No visual effects */
1403#endif
1404 settings.tone_controls = false;
1405 settings.channel_modes = false;
1406 settings.crossfeed = false;
1407 settings.equalizer = false;
1408 settings.dithering = false;
1409
1410 if (configfile_load(SETTINGS_FILENAME, config, ARRAYLEN(config),
1411 SETTINGS_MIN_VERSION) < 0)
1412 {
1413 /* Generate a new config file with default values */
1414 configfile_save(SETTINGS_FILENAME, config, ARRAYLEN(config),
1415 SETTINGS_VERSION);
1416 }
1417
1418 rb->strlcpy(settings.resume_filename, filename, MAX_PATH);
1419
1420 /* get the resume time for the current mpeg if it exists */
1421 if ((settings.resume_time = configfile_get_value
1422 (SETTINGS_FILENAME, filename)) < 0)
1423 {
1424 settings.resume_time = 0;
1425 }
1426
1427#if MPEG_OPTION_DITHERING_ENABLED
1428 rb->lcd_yuv_set_options(settings.displayoptions);
1429#endif
1430
1431 /* Set our audio options */
1432 sync_audio_settings(false);
1433}
1434
1435void save_settings(void)
1436{
1437 unsigned i;
1438 for (i = 0; i < ARRAYLEN(config); i++)
1439 {
1440 configfile_update_entry(SETTINGS_FILENAME, config[i].name,
1441 *(config[i].int_p));
1442 }
1443
1444 /* If this was a new resume entry then update the total resume count */
1445 if (configfile_update_entry(SETTINGS_FILENAME, settings.resume_filename,
1446 settings.resume_time) == 0)
1447 {
1448 configfile_update_entry(SETTINGS_FILENAME, "Resume count",
1449 ++settings.resume_count);
1450 }
1451
1452 /* Restore audio options */
1453 sync_audio_settings(true);
1454}
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 @@
1
2#include "plugin.h"
3
4#define SETTINGS_VERSION 5
5#define SETTINGS_MIN_VERSION 1
6#define SETTINGS_FILENAME "mpegplayer.cfg"
7
8#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200) || defined(SANSA_C200) \
9 || defined(IRIVER_H10) || defined(COWON_D2) || defined(PHILIPS_HDD1630) \
10 || defined(SANSA_FUZE) || defined(SANSA_E200V2) || defined(SANSA_FUZEV2) \
11 || defined(TOSHIBA_GIGABEAT_S) || defined(PHILIPS_SA9200)
12#define MPEG_OPTION_DITHERING_ENABLED 1
13#endif
14
15#ifndef MPEG_OPTION_DITHERING_ENABLED
16#define MPEG_OPTION_DITHERING_ENABLED 0
17#endif
18
19enum mpeg_option_id
20{
21#if MPEG_OPTION_DITHERING_ENABLED
22 MPEG_OPTION_DITHERING,
23#endif
24 MPEG_OPTION_DISPLAY_FPS,
25 MPEG_OPTION_LIMIT_FPS,
26 MPEG_OPTION_SKIP_FRAMES,
27#ifdef HAVE_BACKLIGHT_BRIGHTNESS
28 MPEG_OPTION_BACKLIGHT_BRIGHTNESS,
29#endif
30};
31
32enum mpeg_audio_option_id
33{
34 MPEG_AUDIO_TONE_CONTROLS,
35 MPEG_AUDIO_CHANNEL_MODES,
36 MPEG_AUDIO_CROSSFEED,
37 MPEG_AUDIO_EQUALIZER,
38 MPEG_AUDIO_DITHERING,
39};
40
41enum mpeg_resume_id
42{
43 MPEG_RESUME_MENU_ALWAYS = 0,
44 MPEG_RESUME_MENU_IF_INCOMPLETE,
45 MPEG_RESUME_RESTART,
46 MPEG_RESUME_ALWAYS,
47 MPEG_RESUME_NUM_OPTIONS,
48};
49
50enum mpeg_start_id
51{
52 MPEG_START_RESTART,
53 MPEG_START_RESUME,
54 MPEG_START_SEEK,
55 MPEG_START_SETTINGS,
56 MPEG_START_QUIT,
57 MPEG_START_EXIT,
58};
59
60enum mpeg_setting_id
61{
62 MPEG_SETTING_DISPLAY_SETTINGS,
63 MPEG_SETTING_AUDIO_SETTINGS,
64 MPEG_SETTING_ENABLE_START_MENU,
65 MPEG_SETTING_PLAY_MODE,
66 MPEG_SETTING_CLEAR_RESUMES,
67};
68
69enum mpeg_menu_id
70{
71 MPEG_MENU_SETTINGS,
72 MPEG_MENU_RESUME,
73 MPEG_MENU_QUIT,
74};
75
76struct mpeg_settings {
77 int showfps; /* flag to display fps */
78 int limitfps; /* flag to limit fps */
79 int skipframes; /* flag to skip frames */
80 int resume_options; /* type of resume action at start */
81 int resume_count; /* total # of resumes in config file */
82 int resume_time; /* resume time for current mpeg (in half minutes) */
83 char resume_filename[MAX_PATH]; /* filename of current mpeg */
84#if MPEG_OPTION_DITHERING_ENABLED
85 int displayoptions;
86#endif
87 int play_mode; /* play single file or all files in directory */
88 /* Audio options - simple on/off specification */
89 int tone_controls;
90 int channel_modes;
91 int crossfeed;
92 int equalizer;
93 int dithering;
94 /* Backlight options */
95#ifdef HAVE_BACKLIGHT_BRIGHTNESS
96 int backlight_brightness;
97#endif
98};
99
100extern struct mpeg_settings settings;
101
102int mpeg_start_menu(uint32_t duration);
103int mpeg_menu(void);
104
105void init_settings(const char* filename);
106void save_settings(void);
107
108#ifdef HAVE_BACKLIGHT_BRIGHTNESS
109void mpeg_backlight_update_brightness(int value);
110#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Stream definitions for MPEG
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef MPEG_STREAM_H
24#define MPEG_STREAM_H
25
26/* Codes for various header byte sequences - MSB represents lowest memory
27 address */
28#define PACKET_START_CODE_PREFIX 0x00000100ul
29#define END_CODE 0x000001b9ul
30#define PACK_START_CODE 0x000001baul
31#define SYSTEM_HEADER_START_CODE 0x000001bbul
32
33/* p = base pointer, b0 - b4 = byte offsets from p */
34/* We only care about the MS 32 bits of the 33 and so the ticks are 45kHz */
35#define TS_FROM_HEADER(p, b0) \
36 ((uint32_t)((((p)[(b0)+0] & 0x0e) << 28) | \
37 (((p)[(b0)+1] ) << 21) | \
38 (((p)[(b0)+2] & 0xfe) << 13) | \
39 (((p)[(b0)+3] ) << 6) | \
40 (((p)[(b0)+4] ) >> 2)))
41
42#define TS_CHECK_MARKERS(p, b0) \
43 (((((p)[(b0)+0] & 0x01) << 2) | \
44 (((p)[(b0)+2] & 0x01) << 1) | \
45 (((p)[(b0)+4] & 0x01) )) == 0x07)
46
47/* Get the SCR in our 45kHz ticks. Ignore the 9-bit extension */
48#define MPEG2_PACK_HEADER_SCR(p, b0) \
49 ((uint32_t)((((p)[(b0)+0] & 0x38) << 26) | \
50 (((p)[(b0)+0] & 0x03) << 27) | \
51 (((p)[(b0)+1] ) << 19) | \
52 (((p)[(b0)+2] & 0xf8) << 11) | \
53 (((p)[(b0)+2] & 0x03) << 12) | \
54 (((p)[(b0)+3] ) << 4) | \
55 (((p)[(b0)+4] ) >> 4)))
56
57#define MPEG2_CHECK_PACK_SCR_MARKERS(ph, b0) \
58 (((((ph)[(b0)+0] & 0x04) ) | \
59 (((ph)[(b0)+2] & 0x04) >> 1) | \
60 (((ph)[(b0)+4] & 0x04) >> 2)) == 0x07)
61
62#define INVALID_TIMESTAMP (~(uint32_t)0)
63#define MAX_TIMESTAMP (INVALID_TIMESTAMP-1)
64#define TS_SECOND (45000) /* Timestamp ticks per second */
65#define TC_SECOND (27000000) /* MPEG timecode ticks per second */
66
67/* These values immediately follow the start code prefix '00 00 01' */
68
69/* Video start codes */
70#define MPEG_START_PICTURE 0x00
71#define MPEG_START_SLICE_FIRST 0x01
72#define MPEG_START_SLICE_LAST 0xaf
73#define MPEG_START_RESERVED_1 0xb0
74#define MPEG_START_RESERVED_2 0xb1
75#define MPEG_START_USER_DATA 0xb2
76#define MPEG_START_SEQUENCE_HEADER 0xb3
77#define MPEG_START_SEQUENCE_ERROR 0xb4
78#define MPEG_START_EXTENSION 0xb5
79#define MPEG_START_RESERVED_3 0xb6
80#define MPEG_START_SEQUENCE_END 0xb7
81#define MPEG_START_GOP 0xb8
82
83/* Stream IDs */
84#define MPEG_STREAM_PROGRAM_END 0xb9
85#define MPEG_STREAM_PACK_HEADER 0xba
86#define MPEG_STREAM_SYSTEM_HEADER 0xbb
87#define MPEG_STREAM_PROGRAM_STREAM_MAP 0xbc
88#define MPEG_STREAM_PRIVATE_1 0xbd
89#define MPEG_STREAM_PADDING 0xbe
90#define MPEG_STREAM_PRIVATE_2 0xbf
91#define MPEG_STREAM_AUDIO_FIRST 0xc0
92#define MPEG_STREAM_AUDIO_LAST 0xcf
93#define MPEG_STREAM_VIDEO_FIRST 0xe0
94#define MPEG_STREAM_VIDEO_LAST 0xef
95#define MPEG_STREAM_ECM 0xf0
96#define MPEG_STREAM_EMM 0xf1
97/* ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A or
98 * ISO/IEC 13818-6_DSMCC_stream */
99#define MPEG_STREAM_MISC_1 0xf2
100/* ISO/IEC_13522_stream */
101#define MPEG_STREAM_MISC_2 0xf3
102/* ITU-T Rec. H.222.1 type A - E */
103#define MPEG_STREAM_MISC_3 0xf4
104#define MPEG_STREAM_MISC_4 0xf5
105#define MPEG_STREAM_MISC_5 0xf6
106#define MPEG_STREAM_MISC_6 0xf7
107#define MPEG_STREAM_MISC_7 0xf8
108#define MPEG_STREAM_ANCILLARY 0xf9
109#define MPEG_STREAM_RESERVED_FIRST 0xfa
110#define MPEG_STREAM_RESERVED_LAST 0xfe
111/* Program stream directory */
112#define MPEG_STREAM_PROGRAM_DIRECTORY 0xff
113
114#define STREAM_IS_AUDIO(s) (((s) & 0xf0) == 0xc0)
115#define STREAM_IS_VIDEO(s) (((s) & 0xf0) == 0xe0)
116
117#define MPEG_MAX_PACKET_SIZE (64*1024+16)
118
119/* Largest MPEG audio frame - MPEG1, Layer II, 384kbps, 32kHz, pad */
120#define MPA_MAX_FRAME_SIZE 1729
121
122#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * mpegplayer main entrypoint and UI implementation
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24/****************************************************************************
25 * NOTES:
26 *
27 * mpegplayer is structured as follows:
28 *
29 * +-->Video Thread-->Video Output-->LCD
30 * |
31 * UI-->Stream Manager-->+-->Audio Thread-->PCM buffer--Audio Device
32 * | | | | (ref. clock)
33 * | | +-->Buffer Thread |
34 * Stream Data | | (clock intf./
35 * Requests | File Cache drift adj.)
36 * | Disk I/O
37 * Stream services
38 * (timing, etc.)
39 *
40 * Thread list:
41 * 1) The main thread - Handles user input, settings, basic playback control
42 * and USB connect.
43 *
44 * 2) Stream Manager thread - Handles playback state, events from streams
45 * such as when a stream is finished, stream commands, PCM state. The
46 * layer in which this thread run also handles arbitration of data
47 * requests between the streams and the disk buffer. The actual specific
48 * transport layer code may get moved out to support multiple container
49 * formats.
50 *
51 * 3) Buffer thread - Buffers data in the background, generates notifications
52 * to streams when their data has been buffered, and watches streams'
53 * progress to keep data available during playback. Handles synchronous
54 * random access requests when the file cache is missed.
55 *
56 * 4) Video thread (running on the COP for PortalPlayer targets) - Decodes
57 * the video stream and renders video frames to the LCD. Handles
58 * miscellaneous video tasks like frame and thumbnail printing.
59 *
60 * 5) Audio thread (running on the main CPU to maintain consistency with the
61 * audio FIQ hander on PP) - Decodes audio frames and places them into
62 * the PCM buffer for rendering by the audio device.
63 *
64 * Streams are neither aware of one another nor care about one another. All
65 * streams shall have their own thread (unless it is _really_ efficient to
66 * have a single thread handle a couple minor streams). All coordination of
67 * the streams is done through the stream manager. The clocking is controlled
68 * by and exposed by the stream manager to other streams and implemented at
69 * the PCM level.
70 *
71 * Notes about MPEG files:
72 *
73 * MPEG System Clock is 27MHz - i.e. 27000000 ticks/second.
74 *
75 * FPS is represented in terms of a frame period - this is always an
76 * integer number of 27MHz ticks.
77 *
78 * e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of
79 * 900900 27MHz ticks.
80 *
81 * In libmpeg2, info->sequence->frame_period contains the frame_period.
82 *
83 * Working with Rockbox's 100Hz tick, the common frame rates would need
84 * to be as follows (1):
85 *
86 * FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz
87 * --------|-----------------------------------------------------------
88 * 10* | 2700000 | 10 | 4410 | 4800
89 * 12* | 2250000 | 8.3333 | 3675 | 4000
90 * 15* | 1800000 | 6.6667 | 2940 | 3200
91 * 23.9760 | 1126125 | 4.170833333 | 1839.3375 | 2002
92 * 24 | 1125000 | 4.166667 | 1837.5 | 2000
93 * 25 | 1080000 | 4 | 1764 | 1920
94 * 29.9700 | 900900 | 3.336667 | 1471,47 | 1601.6
95 * 30 | 900000 | 3.333333 | 1470 | 1600
96 *
97 * *Unofficial framerates
98 *
99 * (1) But we don't really care since the audio clock is used anyway and has
100 * very fine resolution ;-)
101 *****************************************************************************/
102#include "plugin.h"
103#include "mpegplayer.h"
104#include "lib/helper.h"
105#include "mpeg_settings.h"
106#include "video_out.h"
107#include "stream_thread.h"
108#include "stream_mgr.h"
109
110
111/* button definitions */
112#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
113#define MPEG_MENU BUTTON_MODE
114#define MPEG_STOP BUTTON_OFF
115#define MPEG_PAUSE BUTTON_ON
116#define MPEG_VOLDOWN BUTTON_DOWN
117#define MPEG_VOLUP BUTTON_UP
118#define MPEG_RW BUTTON_LEFT
119#define MPEG_FF BUTTON_RIGHT
120
121#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
122 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
123#define MPEG_MENU BUTTON_MENU
124#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
125#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
126#define MPEG_VOLDOWN BUTTON_SCROLL_BACK
127#define MPEG_VOLUP BUTTON_SCROLL_FWD
128#define MPEG_RW BUTTON_LEFT
129#define MPEG_FF BUTTON_RIGHT
130
131#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
132#define MPEG_MENU (BUTTON_REC | BUTTON_REL)
133#define MPEG_STOP BUTTON_POWER
134#define MPEG_PAUSE BUTTON_PLAY
135#define MPEG_VOLDOWN BUTTON_DOWN
136#define MPEG_VOLUP BUTTON_UP
137#define MPEG_RW BUTTON_LEFT
138#define MPEG_FF BUTTON_RIGHT
139
140#elif CONFIG_KEYPAD == GIGABEAT_PAD
141#define MPEG_MENU BUTTON_MENU
142#define MPEG_STOP BUTTON_POWER
143#define MPEG_PAUSE BUTTON_SELECT
144#define MPEG_PAUSE2 BUTTON_A
145#define MPEG_VOLDOWN BUTTON_LEFT
146#define MPEG_VOLUP BUTTON_RIGHT
147#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
148#define MPEG_VOLUP2 BUTTON_VOL_UP
149#define MPEG_RW BUTTON_UP
150#define MPEG_FF BUTTON_DOWN
151
152#define MPEG_RC_MENU BUTTON_RC_DSP
153#define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
154#define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
155#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
156#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
157#define MPEG_RC_RW BUTTON_RC_REW
158#define MPEG_RC_FF BUTTON_RC_FF
159
160#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
161#define MPEG_MENU BUTTON_MENU
162#define MPEG_STOP BUTTON_POWER
163#define MPEG_PAUSE BUTTON_SELECT
164#define MPEG_PAUSE2 BUTTON_PLAY
165#define MPEG_VOLDOWN BUTTON_LEFT
166#define MPEG_VOLUP BUTTON_RIGHT
167#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
168#define MPEG_VOLUP2 BUTTON_VOL_UP
169#define MPEG_RW BUTTON_UP
170#define MPEG_RW2 BUTTON_PREV
171#define MPEG_FF BUTTON_DOWN
172#define MPEG_FF2 BUTTON_NEXT
173#define MPEG_SHOW_OSD BUTTON_BACK
174
175#define MPEG_RC_MENU BUTTON_RC_DSP
176#define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
177#define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
178#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
179#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
180#define MPEG_RC_RW BUTTON_RC_REW
181#define MPEG_RC_FF BUTTON_RC_FF
182
183#elif CONFIG_KEYPAD == IRIVER_H10_PAD
184#define MPEG_MENU BUTTON_LEFT
185#define MPEG_STOP BUTTON_POWER
186#define MPEG_PAUSE BUTTON_PLAY
187#define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
188#define MPEG_VOLUP BUTTON_SCROLL_UP
189#define MPEG_RW BUTTON_REW
190#define MPEG_FF BUTTON_FF
191
192#elif CONFIG_KEYPAD == SANSA_E200_PAD
193#define MPEG_MENU BUTTON_SELECT
194#define MPEG_STOP BUTTON_POWER
195#define MPEG_PAUSE BUTTON_RIGHT
196#define MPEG_VOLDOWN BUTTON_SCROLL_BACK
197#define MPEG_VOLUP BUTTON_SCROLL_FWD
198#define MPEG_RW BUTTON_UP
199#define MPEG_FF BUTTON_DOWN
200
201#elif CONFIG_KEYPAD == SANSA_FUZE_PAD
202#define MPEG_MENU BUTTON_SELECT
203#define MPEG_STOP (BUTTON_HOME|BUTTON_REPEAT)
204#define MPEG_PAUSE BUTTON_UP
205#define MPEG_VOLDOWN BUTTON_SCROLL_BACK
206#define MPEG_VOLUP BUTTON_SCROLL_FWD
207#define MPEG_RW BUTTON_LEFT
208#define MPEG_FF BUTTON_RIGHT
209
210
211#elif CONFIG_KEYPAD == SANSA_C200_PAD || \
212CONFIG_KEYPAD == SANSA_CLIP_PAD || \
213CONFIG_KEYPAD == SANSA_M200_PAD
214#define MPEG_MENU BUTTON_SELECT
215#define MPEG_STOP BUTTON_POWER
216#define MPEG_PAUSE BUTTON_UP
217#define MPEG_VOLDOWN BUTTON_VOL_DOWN
218#define MPEG_VOLUP BUTTON_VOL_UP
219#define MPEG_RW BUTTON_LEFT
220#define MPEG_FF BUTTON_RIGHT
221
222#elif CONFIG_KEYPAD == MROBE500_PAD
223#define MPEG_STOP BUTTON_POWER
224
225#define MPEG_RC_MENU BUTTON_RC_HEART
226#define MPEG_RC_STOP BUTTON_RC_DOWN
227#define MPEG_RC_PAUSE BUTTON_RC_PLAY
228#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
229#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
230#define MPEG_RC_RW BUTTON_RC_REW
231#define MPEG_RC_FF BUTTON_RC_FF
232
233#elif CONFIG_KEYPAD == MROBE100_PAD
234#define MPEG_MENU BUTTON_MENU
235#define MPEG_STOP BUTTON_POWER
236#define MPEG_PAUSE BUTTON_PLAY
237#define MPEG_VOLDOWN BUTTON_DOWN
238#define MPEG_VOLUP BUTTON_UP
239#define MPEG_RW BUTTON_LEFT
240#define MPEG_FF BUTTON_RIGHT
241
242#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
243#define MPEG_MENU BUTTON_RC_MENU
244#define MPEG_STOP BUTTON_RC_REC
245#define MPEG_PAUSE BUTTON_RC_PLAY
246#define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
247#define MPEG_VOLUP BUTTON_RC_VOL_UP
248#define MPEG_RW BUTTON_RC_REW
249#define MPEG_FF BUTTON_RC_FF
250
251#elif CONFIG_KEYPAD == COWON_D2_PAD
252#define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
253//#define MPEG_STOP BUTTON_POWER
254#define MPEG_VOLDOWN BUTTON_MINUS
255#define MPEG_VOLUP BUTTON_PLUS
256
257#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
258#define MPEG_MENU BUTTON_MENU
259#define MPEG_STOP BUTTON_BACK
260#define MPEG_PAUSE BUTTON_PLAY
261#define MPEG_VOLDOWN BUTTON_UP
262#define MPEG_VOLUP BUTTON_DOWN
263#define MPEG_RW BUTTON_LEFT
264#define MPEG_FF BUTTON_RIGHT
265
266#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
267#define MPEG_MENU BUTTON_MENU
268#define MPEG_STOP (BUTTON_PLAY|BUTTON_REPEAT)
269#define MPEG_PAUSE (BUTTON_PLAY|BUTTON_REL)
270#define MPEG_VOLDOWN BUTTON_VOL_DOWN
271#define MPEG_VOLUP BUTTON_VOL_UP
272#define MPEG_RW BUTTON_DOWN
273#define MPEG_FF BUTTON_UP
274
275#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
276#define MPEG_MENU BUTTON_MENU
277#define MPEG_STOP BUTTON_POWER
278#define MPEG_PAUSE BUTTON_SELECT
279#define MPEG_VOLDOWN BUTTON_VOL_DOWN
280#define MPEG_VOLUP BUTTON_VOL_UP
281#define MPEG_RW BUTTON_LEFT
282#define MPEG_FF BUTTON_RIGHT
283
284#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
285#define MPEG_MENU BUTTON_MENU
286#define MPEG_STOP BUTTON_POWER
287#define MPEG_PAUSE BUTTON_PLAY
288#define MPEG_VOLDOWN BUTTON_VOL_DOWN
289#define MPEG_VOLUP BUTTON_VOL_UP
290#define MPEG_RW BUTTON_PREV
291#define MPEG_FF BUTTON_NEXT
292
293#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
294#define MPEG_MENU BUTTON_MENU
295#define MPEG_STOP BUTTON_POWER
296#define MPEG_PAUSE BUTTON_PLAY
297#define MPEG_VOLDOWN BUTTON_VOL_DOWN
298#define MPEG_VOLUP BUTTON_VOL_UP
299#define MPEG_RW BUTTON_UP
300#define MPEG_FF BUTTON_DOWN
301
302#elif CONFIG_KEYPAD == ONDAVX747_PAD
303#define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
304//#define MPEG_STOP BUTTON_POWER
305#define MPEG_VOLDOWN BUTTON_VOL_DOWN
306#define MPEG_VOLUP BUTTON_VOL_UP
307
308#elif CONFIG_KEYPAD == ONDAVX777_PAD
309#define MPEG_MENU BUTTON_POWER
310
311#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
312 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
313#define MPEG_MENU BUTTON_REW
314#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
315#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
316#define MPEG_VOLDOWN BUTTON_DOWN
317#define MPEG_VOLUP BUTTON_UP
318#define MPEG_RW BUTTON_LEFT
319#define MPEG_FF BUTTON_RIGHT
320#define MPEG_SHOW_OSD BUTTON_FFWD
321
322#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
323#define MPEG_MENU BUTTON_MENU
324#define MPEG_STOP BUTTON_REC
325#define MPEG_PAUSE BUTTON_PLAY
326#define MPEG_VOLDOWN BUTTON_DOWN
327#define MPEG_VOLUP BUTTON_UP
328#define MPEG_RW BUTTON_PREV
329#define MPEG_FF BUTTON_NEXT
330
331#elif CONFIG_KEYPAD == MPIO_HD200_PAD
332#define MPEG_MENU BUTTON_FUNC
333#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
334#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
335#define MPEG_VOLDOWN BUTTON_VOL_DOWN
336#define MPEG_VOLUP BUTTON_VOL_UP
337#define MPEG_RW BUTTON_REW
338#define MPEG_FF BUTTON_FF
339
340#elif CONFIG_KEYPAD == MPIO_HD300_PAD
341#define MPEG_MENU BUTTON_MENU
342#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
343#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
344#define MPEG_VOLDOWN BUTTON_DOWN
345#define MPEG_VOLUP BUTTON_UP
346#define MPEG_RW BUTTON_REW
347#define MPEG_FF BUTTON_FF
348
349#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
350#define MPEG_MENU BUTTON_POWER
351#define MPEG_PAUSE (BUTTON_PLAYPAUSE | BUTTON_REL)
352#define MPEG_STOP (BUTTON_PLAYPAUSE | BUTTON_REPEAT)
353#define MPEG_VOLDOWN BUTTON_VOL_DOWN
354#define MPEG_VOLUP BUTTON_VOL_UP
355#define MPEG_RW BUTTON_LEFT
356#define MPEG_FF BUTTON_RIGHT
357
358#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
359#define MPEG_MENU BUTTON_POWER
360#define MPEG_PAUSE (BUTTON_SELECT | BUTTON_REL)
361#define MPEG_STOP (BUTTON_SELECT | BUTTON_REPEAT)
362#define MPEG_VOLDOWN BUTTON_VOL_DOWN
363#define MPEG_VOLUP BUTTON_VOL_UP
364#define MPEG_RW BUTTON_LEFT
365#define MPEG_FF BUTTON_RIGHT
366
367#elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
368#define MPEG_MENU BUTTON_MENU
369#define MPEG_PAUSE BUTTON_SELECT
370#define MPEG_STOP BUTTON_POWER
371#define MPEG_VOLDOWN BUTTON_DOWN
372#define MPEG_VOLUP BUTTON_UP
373#define MPEG_RW BUTTON_LEFT
374#define MPEG_FF BUTTON_RIGHT
375
376#elif CONFIG_KEYPAD == HM60X_PAD
377#define MPEG_MENU BUTTON_POWER
378#define MPEG_PAUSE BUTTON_SELECT
379#define MPEG_STOP (BUTTON_SELECT | BUTTON_POWER)
380#define MPEG_VOLDOWN (BUTTON_POWER | BUTTON_DOWN)
381#define MPEG_VOLUP (BUTTON_POWER | BUTTON_UP)
382#define MPEG_RW BUTTON_LEFT
383#define MPEG_FF BUTTON_RIGHT
384
385#elif CONFIG_KEYPAD == HM801_PAD
386#define MPEG_MENU BUTTON_POWER
387#define MPEG_PAUSE BUTTON_PLAY
388#define MPEG_STOP (BUTTON_POWER | BUTTON_PLAY)
389#define MPEG_VOLDOWN (BUTTON_POWER | BUTTON_DOWN)
390#define MPEG_VOLUP (BUTTON_POWER | BUTTON_UP)
391#define MPEG_RW BUTTON_PREV
392#define MPEG_FF BUTTON_NEXT
393
394#elif CONFIG_KEYPAD == SONY_NWZ_PAD
395#define MPEG_MENU BUTTON_BACK
396#define MPEG_PAUSE BUTTON_PLAY
397#define MPEG_STOP BUTTON_POWER
398#define MPEG_VOLDOWN BUTTON_UP
399#define MPEG_VOLUP BUTTON_DOWN
400#define MPEG_RW BUTTON_LEFT
401#define MPEG_FF BUTTON_RIGHT
402
403#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
404#define MPEG_MENU BUTTON_MENU
405#define MPEG_PAUSE BUTTON_PLAYPAUSE
406#define MPEG_STOP BUTTON_BACK
407#define MPEG_VOLDOWN BUTTON_DOWN
408#define MPEG_VOLUP BUTTON_UP
409#define MPEG_RW BUTTON_LEFT
410#define MPEG_FF BUTTON_RIGHT
411
412#elif CONFIG_KEYPAD == DX50_PAD
413#define MPEG_MENU BUTTON_POWER
414#define MPEG_VOLDOWN BUTTON_VOL_DOWN
415#define MPEG_VOLUP BUTTON_VOL_UP
416#define MPEG_RW BUTTON_LEFT
417#define MPEG_FF BUTTON_RIGHT
418#define MPEG_PAUSE BUTTON_PLAY
419#define MPEG_STOP (BUTTON_PLAY|BUTTON_REPEAT)
420
421#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
422#define MPEG_MENU BUTTON_POWER
423#define MPEG_PAUSE BUTTON_MENU
424#define MPEG_STOP (BUTTON_MENU|BUTTON_REPEAT)
425
426#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD
427#define MPEG_MENU BUTTON_POWER
428#define MPEG_PAUSE BUTTON_SELECT
429#define MPEG_STOP BUTTON_DOWN
430#define MPEG_VOLDOWN BUTTON_VOLDOWN
431#define MPEG_VOLUP BUTTON_VOLUP
432#define MPEG_RW BUTTON_LEFT
433#define MPEG_FF BUTTON_RIGHT
434
435#elif CONFIG_KEYPAD == XDUOO_X3_PAD
436#define MPEG_MENU BUTTON_PLAY
437#define MPEG_STOP BUTTON_POWER
438#define MPEG_PAUSE BUTTON_HOME
439#define MPEG_VOLDOWN BUTTON_VOL_DOWN
440#define MPEG_VOLUP BUTTON_VOL_UP
441#define MPEG_RW BUTTON_PREV
442#define MPEG_FF BUTTON_NEXT
443
444#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
445#define MPEG_MENU BUTTON_PLAY
446#define MPEG_STOP BUTTON_POWER
447#define MPEG_PAUSE BUTTON_HOME
448#define MPEG_VOLDOWN BUTTON_VOL_DOWN
449#define MPEG_VOLUP BUTTON_VOL_UP
450#define MPEG_RW BUTTON_PREV
451#define MPEG_FF BUTTON_NEXT
452
453#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
454#define MPEG_MENU BUTTON_PLAY
455#define MPEG_STOP BUTTON_POWER
456#define MPEG_PAUSE BUTTON_HOME
457#define MPEG_VOLDOWN BUTTON_VOL_DOWN
458#define MPEG_VOLUP BUTTON_VOL_UP
459#define MPEG_RW BUTTON_PREV
460#define MPEG_FF BUTTON_NEXT
461
462#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
463#define MPEG_MENU BUTTON_PLAY
464#define MPEG_STOP BUTTON_POWER
465#define MPEG_PAUSE BUTTON_HOME
466#define MPEG_VOLDOWN BUTTON_VOL_DOWN
467#define MPEG_VOLUP BUTTON_VOL_UP
468#define MPEG_RW BUTTON_PREV
469#define MPEG_FF BUTTON_NEXT
470
471#elif CONFIG_KEYPAD == EROSQ_PAD
472#define MPEG_MENU BUTTON_MENU
473#define MPEG_STOP BUTTON_POWER
474#define MPEG_PAUSE BUTTON_PLAY
475#define MPEG_VOLDOWN BUTTON_VOL_DOWN
476#define MPEG_VOLUP BUTTON_VOL_UP
477#define MPEG_RW BUTTON_PREV
478#define MPEG_FF BUTTON_NEXT
479
480#elif CONFIG_KEYPAD == FIIO_M3K_PAD
481#define MPEG_MENU BUTTON_MENU
482#define MPEG_STOP BUTTON_POWER
483#define MPEG_PAUSE BUTTON_PLAY
484#define MPEG_VOLDOWN BUTTON_VOL_DOWN
485#define MPEG_VOLUP BUTTON_VOL_UP
486#define MPEG_RW BUTTON_LEFT
487#define MPEG_FF BUTTON_RIGHT
488
489#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
490/* use touchscreen */
491
492#else
493#error No keymap defined!
494#endif
495
496#ifdef HAVE_TOUCHSCREEN
497#ifndef MPEG_MENU
498#define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
499#endif
500#ifndef MPEG_STOP
501#define MPEG_STOP BUTTON_TOPLEFT
502#endif
503#ifndef MPEG_PAUSE
504#define MPEG_PAUSE BUTTON_CENTER
505#endif
506#ifndef MPEG_VOLDOWN
507#define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
508#endif
509#ifndef MPEG_VOLUP
510#define MPEG_VOLUP BUTTON_TOPMIDDLE
511#endif
512#ifndef MPEG_RW
513#define MPEG_RW BUTTON_MIDLEFT
514#endif
515#ifndef MPEG_FF
516#define MPEG_FF BUTTON_MIDRIGHT
517#endif
518#endif
519
520/* One thing we can do here for targets with remotes is having a display
521 * always on the remote instead of always forcing a popup on the main display */
522
523#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
524 /* 3% of 30min file == 54s step size */
525#define MIN_FF_REWIND_STEP (TS_SECOND/2)
526#define OSD_MIN_UPDATE_INTERVAL (HZ/2)
527#define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */
528
529enum video_action
530{
531 VIDEO_STOP = 0,
532 VIDEO_PREV,
533 VIDEO_NEXT,
534 VIDEO_ACTION_MANUAL = 0x8000, /* Flag that says user did it */
535};
536
537/* OSD status - same order as icon array */
538enum osd_status_enum
539{
540 OSD_STATUS_STOPPED = 0,
541 OSD_STATUS_PAUSED,
542 OSD_STATUS_PLAYING,
543 OSD_STATUS_FF,
544 OSD_STATUS_RW,
545 OSD_STATUS_COUNT,
546 OSD_STATUS_MASK = 0x7
547};
548
549enum osd_bits
550{
551 OSD_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
552 /* Refresh the... */
553 OSD_REFRESH_VOLUME = 0x0001, /* ...volume display */
554 OSD_REFRESH_TIME = 0x0002, /* ...time display+progress */
555 OSD_REFRESH_STATUS = 0x0004, /* ...playback status icon */
556 OSD_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
557 OSD_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
558 OSD_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
559 OSD_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
560 OSD_SHOW = 0x4000, /* OR bitflag - show the OSD */
561#ifdef HAVE_HEADPHONE_DETECTION
562 OSD_HP_PAUSE = 0x2000, /* OR bitflag - headphones caused pause */
563#endif
564 OSD_HIDE = 0x0000, /* hide the OSD (aid readability) */
565 OSD_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
566};
567
568/* Status icons selected according to font height */
569extern const unsigned char mpegplayer_status_icons_8x8x1[];
570extern const unsigned char mpegplayer_status_icons_12x12x1[];
571extern const unsigned char mpegplayer_status_icons_16x16x1[];
572
573/* Main border areas that contain OSD elements */
574#define OSD_BDR_L 2
575#define OSD_BDR_T 2
576#define OSD_BDR_R 2
577#define OSD_BDR_B 2
578
579struct osd
580{
581 long hide_tick;
582 long show_for;
583 long print_tick;
584 long print_delay;
585 long resume_tick;
586 long resume_delay;
587 long next_auto_refresh;
588 int x;
589 int y;
590 int width;
591 int height;
592 unsigned fgcolor;
593 unsigned bgcolor;
594 unsigned prog_fillcolor;
595 struct vo_rect update_rect;
596 struct vo_rect prog_rect;
597 struct vo_rect time_rect;
598 struct vo_rect dur_rect;
599 struct vo_rect vol_rect;
600 const unsigned char *icons;
601 struct vo_rect stat_rect;
602 int status;
603 uint32_t curr_time;
604 unsigned auto_refresh;
605 unsigned flags;
606 int font;
607};
608
609struct fps
610{
611 /* FPS Display */
612 struct vo_rect rect; /* OSD coordinates */
613 int pf_x; /* Screen coordinates */
614 int pf_y;
615 int pf_width;
616 int pf_height;
617 long update_tick; /* When to next update FPS reading */
618 #define FPS_FORMAT "%d.%02d"
619 #define FPS_DIMSTR "999.99" /* For establishing rect size */
620 #define FPS_BUFSIZE sizeof("999.99")
621};
622
623static struct osd osd;
624static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */
625
626#ifdef LCD_PORTRAIT
627static fb_data* get_framebuffer(void)
628{
629 struct viewport *vp_main = *(rb->screens[SCREEN_MAIN]->current_viewport);
630 return vp_main->buffer->fb_ptr;
631}
632#endif
633
634static void osd_show(unsigned show);
635
636#ifdef LCD_LANDSCAPE
637 #define __X (x + osd.x)
638 #define __Y (y + osd.y)
639 #define __W width
640 #define __H height
641#else
642 #define __X (LCD_WIDTH - (y + osd.y) - height)
643 #define __Y (x + osd.x)
644 #define __W height
645 #define __H width
646#endif
647
648#ifdef HAVE_LCD_COLOR
649/* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
650static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
651{
652 int r1 = RGB_UNPACK_RED(c1);
653 int g1 = RGB_UNPACK_GREEN(c1);
654 int b1 = RGB_UNPACK_BLUE(c1);
655
656 int r2 = RGB_UNPACK_RED(c2);
657 int g2 = RGB_UNPACK_GREEN(c2);
658 int b2 = RGB_UNPACK_BLUE(c2);
659
660 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
661 amount*(g2 - g1) / 255 + g1,
662 amount*(b2 - b1) / 255 + b1);
663}
664#endif
665
666#ifdef PLUGIN_USE_IRAM
667/* IRAM preserving mechanism to enable talking menus */
668static char *iram_saved_copy;
669extern char iramstart[], iramend[];
670
671static void iram_saving_init(void)
672{
673#ifndef SIMULATOR
674 size_t size;
675 iram_saved_copy = (char *)rb->plugin_get_buffer(&size);
676
677 if (size >= (size_t)(iramend-iramstart))
678 iram_saved_copy += size - (size_t)(iramend - iramstart);
679 else
680#endif
681 iram_saved_copy = NULL;
682
683 return;
684}
685
686void mpegplayer_iram_preserve(void)
687{
688 if (iram_saved_copy)
689 {
690 rb->memcpy(iram_saved_copy, iramstart, iramend-iramstart);
691#ifdef HAVE_CPUCACHE_INVALIDATE
692 /* make the icache (if it exists) up to date with the new code */
693 rb->cpucache_invalidate();
694#endif /* HAVE_CPUCACHE_INVALIDATE */
695 }
696 return;
697}
698
699void mpegplayer_iram_restore(void)
700{
701 if (iram_saved_copy)
702 {
703 rb->audio_hard_stop();
704 rb->memcpy(iramstart, iram_saved_copy, iramend-iramstart);
705#ifdef HAVE_CPUCACHE_INVALIDATE
706 /* make the icache (if it exists) up to date with the new code */
707 rb->cpucache_invalidate();
708#endif /* HAVE_CPUCACHE_INVALIDATE */
709 }
710 return;
711}
712#endif
713
714/* Drawing functions that operate rotated on LCD_PORTRAIT displays -
715 * most are just wrappers of lcd_* functions with transforms applied.
716 * The origin is the upper-left corner of the OSD area */
717static void draw_update_rect(int x, int y, int width, int height)
718{
719 mylcd_update_rect(__X, __Y, __W, __H);
720}
721
722static void draw_clear_area(int x, int y, int width, int height)
723{
724#ifdef HAVE_LCD_COLOR
725 rb->screen_clear_area(rb->screens[SCREEN_MAIN], __X, __Y, __W, __H);
726#else
727 int oldmode = grey_get_drawmode();
728 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
729 grey_fillrect(__X, __Y, __W, __H);
730 grey_set_drawmode(oldmode);
731#endif
732}
733
734static void draw_clear_area_rect(const struct vo_rect *rc)
735{
736 draw_clear_area(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t);
737}
738
739static void draw_fillrect(int x, int y, int width, int height)
740{
741 mylcd_fillrect(__X, __Y, __W, __H);
742}
743
744static void draw_hline(int x1, int x2, int y)
745{
746#ifdef LCD_LANDSCAPE
747 mylcd_hline(x1 + osd.x, x2 + osd.x, y + osd.y);
748#else
749 y = LCD_WIDTH - (y + osd.y) - 1;
750 mylcd_vline(y, x1 + osd.x, x2 + osd.x);
751#endif
752}
753
754static void draw_vline(int x, int y1, int y2)
755{
756#ifdef LCD_LANDSCAPE
757 mylcd_vline(x + osd.x, y1 + osd.y, y2 + osd.y);
758#else
759 y1 = LCD_WIDTH - (y1 + osd.y) - 1;
760 y2 = LCD_WIDTH - (y2 + osd.y) - 1;
761 mylcd_hline(y1, y2, x + osd.x);
762#endif
763}
764
765static void draw_scrollbar_draw(int x, int y, int width, int height,
766 uint32_t min, uint32_t max, uint32_t val)
767{
768 unsigned oldfg = mylcd_get_foreground();
769
770 draw_hline(x + 1, x + width - 2, y);
771 draw_hline(x + 1, x + width - 2, y + height - 1);
772 draw_vline(x, y + 1, y + height - 2);
773 draw_vline(x + width - 1, y + 1, y + height - 2);
774
775 val = muldiv_uint32(width - 2, val, max - min);
776 val = MIN(val, (uint32_t)(width - 2));
777
778 draw_fillrect(x + 1, y + 1, val, height - 2);
779
780 mylcd_set_foreground(osd.prog_fillcolor);
781
782 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
783
784 mylcd_set_foreground(oldfg);
785}
786
787static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
788 int max, int val)
789{
790 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
791 min, max, val);
792}
793
794static void draw_setfont(int font)
795{
796 osd.font = font;
797 mylcd_setfont(font);
798}
799
800#ifdef LCD_PORTRAIT
801/* Portrait displays need rotated text rendering */
802
803/* Limited function that only renders in DRMODE_FG and uses absolute screen
804 * coordinates */
805static void draw_oriented_mono_bitmap_part(const unsigned char *src,
806 int src_x, int src_y,
807 int stride, int x, int y,
808 int width, int height)
809{
810 const unsigned char *src_end;
811 fb_data *dst, *dst_end;
812 unsigned fg_pattern;
813
814 if (x + width > SCREEN_WIDTH)
815 width = SCREEN_WIDTH - x; /* Clip right */
816 if (x < 0)
817 width += x, x = 0; /* Clip left */
818 if (width <= 0)
819 return; /* nothing left to do */
820
821 if (y + height > SCREEN_HEIGHT)
822 height = SCREEN_HEIGHT - y; /* Clip bottom */
823 if (y < 0)
824 height += y, y = 0; /* Clip top */
825 if (height <= 0)
826 return; /* nothing left to do */
827
828 fg_pattern = rb->lcd_get_foreground();
829 /*bg_pattern =*/ rb->lcd_get_background();
830
831 src += stride * (src_y >> 3) + src_x; /* move starting point */
832 src_y &= 7;
833 src_end = src + width;
834
835 dst = get_framebuffer() + (LCD_WIDTH - y) + x*LCD_WIDTH;
836 do
837 {
838 const unsigned char *src_col = src++;
839 unsigned data = *src_col >> src_y;
840 int numbits = 8 - src_y;
841
842 fb_data *dst_col = dst;
843 dst_end = dst_col - height;
844 dst += LCD_WIDTH;
845
846 do
847 {
848 dst_col--;
849
850 if (data & 1)
851 *dst_col = FB_SCALARPACK(fg_pattern);
852#if 0
853 else
854 *dst_col = bg_pattern;
855#endif
856 data >>= 1;
857 if (--numbits == 0) {
858 src_col += stride;
859 data = *src_col;
860 numbits = 8;
861 }
862 }
863 while (dst_col > dst_end);
864 }
865 while (src < src_end);
866}
867
868
869#define ALPHA_COLOR_FONT_DEPTH 2
870#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
871#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
872#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
873#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
874#ifdef CPU_ARM
875#define BLEND_INIT do {} while (0)
876#define BLEND_FINISH do {} while(0)
877#define BLEND_START(acc, color, alpha) \
878 asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha))
879#define BLEND_CONT(acc, color, alpha) \
880 asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
881#define BLEND_OUT(acc) do {} while (0)
882#elif defined(CPU_COLDFIRE)
883#define ALPHA_BITMAP_READ_WORDS
884#define BLEND_INIT \
885 unsigned long _macsr = coldfire_get_macsr(); \
886 coldfire_set_macsr(EMAC_UNSIGNED)
887#define BLEND_FINISH \
888 coldfire_set_macsr(_macsr)
889#define BLEND_START(acc, color, alpha) \
890 asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
891#define BLEND_CONT BLEND_START
892#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
893#else
894#define BLEND_INIT do {} while (0)
895#define BLEND_FINISH do {} while(0)
896#define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha))
897#define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha))
898#define BLEND_OUT(acc) do {} while (0)
899#endif
900
901/* Blend the given two colors */
902static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
903{
904#if LCD_DEPTH == 16
905 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
906#if (LCD_PIXELFORMAT == RGB565SWAPPED)
907 c1 = swap16(c1);
908 c2 = swap16(c2);
909#endif
910 unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f;
911 unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
912 unsigned p;
913 BLEND_START(p, c1l, a);
914 BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
915 BLEND_OUT(p);
916 p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
917 p |= (p >> 16);
918#if (LCD_PIXELFORMAT == RGB565SWAPPED)
919 return swap16(p);
920#else
921 return p;
922#endif
923
924#else /* LCD_DEPTH == 24 */
925 unsigned s = c1;
926 unsigned d = c2;
927 unsigned s1 = s & 0xff00ff;
928 unsigned d1 = d & 0xff00ff;
929 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
930 d1 = (d1 + ((s1 - d1) * a >> ALPHA_COLOR_LOOKUP_SHIFT)) & 0xff00ff;
931 s &= 0xff00;
932 d &= 0xff00;
933 d = (d + ((s - d) * a >> ALPHA_COLOR_LOOKUP_SHIFT)) & 0xff00;
934
935 return d1 | d;
936#endif
937}
938
939static void draw_oriented_alpha_bitmap_part(const unsigned char *src,
940 int src_x, int src_y,
941 int stride, int x, int y,
942 int width, int height)
943{
944 fb_data *dst, *dst_start;
945 unsigned fg_pattern;
946
947 if (x + width > SCREEN_WIDTH)
948 width = SCREEN_WIDTH - x; /* Clip right */
949 if (x < 0)
950 width += x, x = 0; /* Clip left */
951 if (width <= 0)
952 return; /* nothing left to do */
953
954 if (y + height > SCREEN_HEIGHT)
955 height = SCREEN_HEIGHT - y; /* Clip bottom */
956 if (y < 0)
957 height += y, y = 0; /* Clip top */
958 if (height <= 0)
959 return; /* nothing left to do */
960
961 /* initialize blending */
962 BLEND_INIT;
963
964 fg_pattern = rb->lcd_get_foreground();
965 /*bg_pattern=*/ rb->lcd_get_background();
966
967 dst_start = get_framebuffer() + (LCD_WIDTH - y - 1) + x*LCD_WIDTH;
968 int col, row = height;
969 unsigned data, pixels;
970 unsigned skip_end = (stride - width);
971 unsigned skip_start = src_y * stride + src_x;
972
973#ifdef ALPHA_BITMAP_READ_WORDS
974 uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
975 skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
976 src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
977 data = letoh32(*src_w++);
978#else
979 src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
980 data = *src;
981#endif
982 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
983 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
984#ifdef ALPHA_BITMAP_READ_WORDS
985 pixels = 8 - pixels;
986#endif
987
988 do
989 {
990 col = width;
991 dst = dst_start--;
992#ifdef ALPHA_BITMAP_READ_WORDS
993#define UPDATE_SRC_ALPHA do { \
994 if (--pixels) \
995 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
996 else \
997 { \
998 data = letoh32(*src_w++); \
999 pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
1000 } \
1001 } while (0)
1002#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
1003#define UPDATE_SRC_ALPHA do { \
1004 if (pixels ^= 1) \
1005 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
1006 else \
1007 data = *(++src); \
1008 } while (0)
1009#else
1010#define UPDATE_SRC_ALPHA do { \
1011 if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
1012 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
1013 else \
1014 data = *(++src); \
1015 } while (0)
1016#endif
1017 do
1018 {
1019 unsigned color = blend_two_colors(FB_UNPACK_SCALAR_LCD(*dst), fg_pattern,
1020 data & ALPHA_COLOR_LOOKUP_SIZE );
1021 *dst= FB_SCALARPACK(color);
1022 dst += LCD_WIDTH;
1023 UPDATE_SRC_ALPHA;
1024 }
1025 while (--col);
1026#ifdef ALPHA_BITMAP_READ_WORDS
1027 if (skip_end < pixels)
1028 {
1029 pixels -= skip_end;
1030 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
1031 } else {
1032 pixels = skip_end - pixels;
1033 src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
1034 pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
1035 data = letoh32(*src_w++);
1036 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
1037 pixels = 8 - pixels;
1038 }
1039#else
1040 if (skip_end)
1041 {
1042 pixels += skip_end;
1043 if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
1044 {
1045 src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
1046 pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
1047 data = *src;
1048 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
1049 } else
1050 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
1051 }
1052#endif
1053 } while (--row);
1054}
1055
1056static void draw_putsxy_oriented(int x, int y, const char *str)
1057{
1058 unsigned short ch;
1059 unsigned short *ucs;
1060 int ofs = MIN(x, 0);
1061 struct font* pf = rb->font_get(osd.font);
1062
1063 ucs = rb->bidi_l2v(str, 1);
1064
1065 x += osd.x;
1066 y += osd.y;
1067
1068 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
1069 {
1070 int width;
1071 const unsigned char *bits;
1072
1073 /* get proportional width and glyph bits */
1074 width = rb->font_get_width(pf, ch);
1075
1076 if (ofs > width) {
1077 ofs -= width;
1078 continue;
1079 }
1080
1081 bits = rb->font_get_bits(pf, ch);
1082
1083 if (pf->depth)
1084 draw_oriented_alpha_bitmap_part(bits, ofs, 0, width, x, y,
1085 width - ofs, pf->height);
1086 else
1087 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
1088 width - ofs, pf->height);
1089
1090 x += width - ofs;
1091 ofs = 0;
1092 }
1093}
1094#else
1095static void draw_oriented_mono_bitmap_part(const unsigned char *src,
1096 int src_x, int src_y,
1097 int stride, int x, int y,
1098 int width, int height)
1099{
1100 int mode = mylcd_get_drawmode();
1101 mylcd_set_drawmode(DRMODE_FG);
1102 mylcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height);
1103 mylcd_set_drawmode(mode);
1104}
1105
1106static void draw_putsxy_oriented(int x, int y, const char *str)
1107{
1108 int mode = mylcd_get_drawmode();
1109 mylcd_set_drawmode(DRMODE_FG);
1110 mylcd_putsxy(x + osd.x, y + osd.y, str);
1111 mylcd_set_drawmode(mode);
1112}
1113#endif /* LCD_PORTRAIT */
1114
1115/** FPS Display **/
1116
1117/* Post-frame callback (on video thread) - update the FPS rectangle from the
1118 * framebuffer */
1119static void fps_post_frame_callback(void)
1120{
1121 vo_lock();
1122 mylcd_update_rect(fps.pf_x, fps.pf_y,
1123 fps.pf_width, fps.pf_height);
1124 vo_unlock();
1125}
1126
1127/* Set up to have the callback only update the intersection of the video
1128 * rectangle and the FPS text rectangle - if they don't intersect, then
1129 * the callback is set to NULL */
1130static void fps_update_post_frame_callback(void)
1131{
1132 void (*cb)(void) = NULL;
1133
1134 if (settings.showfps) {
1135 struct vo_rect cliprect;
1136
1137 if (stream_vo_get_clip(&cliprect)) {
1138 /* Oriented screen coordinates -> OSD coordinates */
1139 vo_rect_offset(&cliprect, -osd.x, -osd.y);
1140
1141 if (vo_rect_intersect(&cliprect, &cliprect, &fps.rect)) {
1142 int x = cliprect.l;
1143 int y = cliprect.t;
1144 int width = cliprect.r - cliprect.l;
1145 int height = cliprect.b - cliprect.t;
1146
1147 /* OSD coordinates -> framebuffer coordinates */
1148 fps.pf_x = __X;
1149 fps.pf_y = __Y;
1150 fps.pf_width = __W;
1151 fps.pf_height = __H;
1152
1153 cb = fps_post_frame_callback;
1154 }
1155 }
1156 }
1157
1158 stream_set_callback(VIDEO_SET_POST_FRAME_CALLBACK, cb);
1159}
1160
1161/* Refresh the FPS display */
1162static void fps_refresh(void)
1163{
1164 char str[FPS_BUFSIZE];
1165 struct video_output_stats stats;
1166 int w, h, sw;
1167 long tick;
1168
1169 tick = *rb->current_tick;
1170
1171 if (TIME_BEFORE(tick, fps.update_tick))
1172 return;
1173
1174 fps.update_tick = tick + FPS_UPDATE_INTERVAL;
1175
1176 stream_video_stats(&stats);
1177
1178 rb->snprintf(str, FPS_BUFSIZE, FPS_FORMAT,
1179 stats.fps / 100, stats.fps % 100);
1180
1181 w = fps.rect.r - fps.rect.l;
1182 h = fps.rect.b - fps.rect.t;
1183
1184 draw_clear_area(fps.rect.l, fps.rect.t, w, h);
1185 mylcd_getstringsize(str, &sw, NULL);
1186 draw_putsxy_oriented(fps.rect.r - sw, fps.rect.t, str);
1187
1188 vo_lock();
1189 draw_update_rect(fps.rect.l, fps.rect.t, w, h);
1190 vo_unlock();
1191}
1192
1193/* Initialize the FPS display */
1194static void fps_init(void)
1195{
1196 fps.update_tick = *rb->current_tick;
1197 fps.rect.l = fps.rect.t = 0;
1198 mylcd_getstringsize(FPS_DIMSTR, &fps.rect.r, &fps.rect.b);
1199 vo_rect_offset(&fps.rect, -osd.x, -osd.y);
1200 fps_update_post_frame_callback();
1201}
1202
1203/** OSD **/
1204
1205#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1206/* So we can refresh the overlay */
1207static void osd_lcd_enable_hook(unsigned short id, void* param)
1208{
1209 (void)id;
1210 (void)param;
1211 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
1212}
1213#endif
1214
1215static void osdbacklight_hw_on_video_mode(bool video_on)
1216{
1217 if (video_on) {
1218#ifdef HAVE_BACKLIGHT
1219 /* Turn off backlight timeout */
1220 backlight_ignore_timeout();
1221#endif
1222#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1223 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
1224#endif
1225 } else {
1226#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1227 rb->add_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
1228#endif
1229#ifdef HAVE_BACKLIGHT
1230 /* Revert to user's backlight settings */
1231 backlight_use_settings();
1232#endif
1233 }
1234}
1235
1236#ifdef HAVE_BACKLIGHT_BRIGHTNESS
1237static void osd_backlight_brightness_video_mode(bool video_on)
1238{
1239 if (settings.backlight_brightness < 0)
1240 return;
1241
1242 mpeg_backlight_update_brightness(
1243 video_on ? settings.backlight_brightness : -1);
1244}
1245#else
1246#define osd_backlight_brightness_video_mode(video_on)
1247#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
1248
1249static void osd_text_init(void)
1250{
1251 struct hms hms;
1252 char buf[32];
1253 int phys;
1254 int spc_width;
1255
1256 draw_setfont(FONT_UI);
1257
1258 osd.x = 0;
1259 osd.width = SCREEN_WIDTH;
1260
1261 vo_rect_clear(&osd.time_rect);
1262 vo_rect_clear(&osd.stat_rect);
1263 vo_rect_clear(&osd.prog_rect);
1264 vo_rect_clear(&osd.vol_rect);
1265
1266 ts_to_hms(stream_get_duration(), &hms);
1267 hms_format(buf, sizeof (buf), &hms);
1268 mylcd_getstringsize(buf, &osd.time_rect.r, &osd.time_rect.b);
1269
1270 /* Choose well-sized bitmap images relative to font height */
1271 if (osd.time_rect.b < 12) {
1272 osd.icons = mpegplayer_status_icons_8x8x1;
1273 osd.stat_rect.r = osd.stat_rect.b = 8;
1274 } else if (osd.time_rect.b < 16) {
1275 osd.icons = mpegplayer_status_icons_12x12x1;
1276 osd.stat_rect.r = osd.stat_rect.b = 12;
1277 } else {
1278 osd.icons = mpegplayer_status_icons_16x16x1;
1279 osd.stat_rect.r = osd.stat_rect.b = 16;
1280 }
1281
1282 if (osd.stat_rect.b < osd.time_rect.b) {
1283 vo_rect_offset(&osd.stat_rect, 0,
1284 (osd.time_rect.b - osd.stat_rect.b) / 2 + OSD_BDR_T);
1285 vo_rect_offset(&osd.time_rect, OSD_BDR_L, OSD_BDR_T);
1286 } else {
1287 vo_rect_offset(&osd.time_rect, OSD_BDR_L,
1288 osd.stat_rect.b - osd.time_rect.b + OSD_BDR_T);
1289 vo_rect_offset(&osd.stat_rect, 0, OSD_BDR_T);
1290 }
1291
1292 osd.dur_rect = osd.time_rect;
1293
1294 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
1295 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
1296 rb->sound_unit(SOUND_VOLUME));
1297
1298 mylcd_getstringsize(" ", &spc_width, NULL);
1299 mylcd_getstringsize(buf, &osd.vol_rect.r, &osd.vol_rect.b);
1300
1301 osd.prog_rect.r = SCREEN_WIDTH - OSD_BDR_L - spc_width -
1302 osd.vol_rect.r - OSD_BDR_R;
1303 osd.prog_rect.b = 3*osd.stat_rect.b / 4;
1304 vo_rect_offset(&osd.prog_rect, osd.time_rect.l,
1305 osd.time_rect.b);
1306
1307 vo_rect_offset(&osd.stat_rect,
1308 (osd.prog_rect.r + osd.prog_rect.l - osd.stat_rect.r) / 2,
1309 0);
1310
1311 vo_rect_offset(&osd.dur_rect,
1312 osd.prog_rect.r - osd.dur_rect.r, 0);
1313
1314 vo_rect_offset(&osd.vol_rect, osd.prog_rect.r + spc_width,
1315 (osd.prog_rect.b + osd.prog_rect.t - osd.vol_rect.b) / 2);
1316
1317 osd.height = OSD_BDR_T + MAX(osd.prog_rect.b, osd.vol_rect.b) -
1318 MIN(osd.time_rect.t, osd.stat_rect.t) + OSD_BDR_B;
1319
1320#ifdef HAVE_LCD_COLOR
1321 osd.height = ALIGN_UP(osd.height, 2);
1322#endif
1323 osd.y = SCREEN_HEIGHT - osd.height;
1324
1325 draw_setfont(FONT_SYSFIXED);
1326}
1327
1328static void osd_init(void)
1329{
1330 osd.flags = 0;
1331 osd.show_for = HZ*4;
1332 osd.print_delay = 75*HZ/100;
1333 osd.resume_delay = HZ/2;
1334#ifdef HAVE_LCD_COLOR
1335 osd.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
1336 osd.fgcolor = LCD_WHITE;
1337 osd.prog_fillcolor = LCD_BLACK;
1338#else
1339 osd.bgcolor = GREY_LIGHTGRAY;
1340 osd.fgcolor = GREY_BLACK;
1341 osd.prog_fillcolor = GREY_WHITE;
1342#endif
1343 osd.curr_time = 0;
1344 osd.status = OSD_STATUS_STOPPED;
1345 osd.auto_refresh = OSD_REFRESH_TIME;
1346 osd.next_auto_refresh = *rb->current_tick;
1347 osd_text_init();
1348 fps_init();
1349}
1350
1351#ifdef HAVE_HEADPHONE_DETECTION
1352static void osd_set_hp_pause_flag(bool set)
1353{
1354 if (set)
1355 osd.flags |= OSD_HP_PAUSE;
1356 else
1357 osd.flags &= ~OSD_HP_PAUSE;
1358}
1359#else
1360#define osd_set_hp_pause_flag(set)
1361#endif /* HAVE_HEADPHONE_DETECTION */
1362
1363static void osd_schedule_refresh(unsigned refresh)
1364{
1365 long tick = *rb->current_tick;
1366
1367 if (refresh & OSD_REFRESH_VIDEO)
1368 osd.print_tick = tick + osd.print_delay;
1369
1370 if (refresh & OSD_REFRESH_RESUME)
1371 osd.resume_tick = tick + osd.resume_delay;
1372
1373 osd.auto_refresh |= refresh;
1374}
1375
1376static void osd_cancel_refresh(unsigned refresh)
1377{
1378 osd.auto_refresh &= ~refresh;
1379}
1380
1381/* Refresh the background area */
1382static void osd_refresh_background(void)
1383{
1384 char buf[32];
1385 struct hms hms;
1386
1387 unsigned bg = mylcd_get_background();
1388 mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
1389
1390#ifdef HAVE_LCD_COLOR
1391 /* Draw a "raised" area for our graphics */
1392 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 192));
1393 draw_hline(0, osd.width, 0);
1394
1395 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 80));
1396 draw_hline(0, osd.width, 1);
1397
1398 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 48));
1399 draw_hline(0, osd.width, osd.height-2);
1400
1401 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 128));
1402 draw_hline(0, osd.width, osd.height-1);
1403
1404 mylcd_set_background(bg);
1405 draw_clear_area(0, 2, osd.width, osd.height - 4);
1406#else
1407 /* Give contrast with the main background */
1408 mylcd_set_background(MYLCD_WHITE);
1409 draw_hline(0, osd.width, 0);
1410
1411 mylcd_set_background(MYLCD_DARKGRAY);
1412 draw_hline(0, osd.width, osd.height-1);
1413
1414 mylcd_set_background(bg);
1415 draw_clear_area(0, 1, osd.width, osd.height - 2);
1416#endif
1417
1418 vo_rect_set_ext(&osd.update_rect, 0, 0, osd.width, osd.height);
1419 mylcd_set_drawmode(DRMODE_SOLID);
1420
1421 if (stream_get_duration() != INVALID_TIMESTAMP) {
1422 /* Draw the movie duration */
1423 ts_to_hms(stream_get_duration(), &hms);
1424 hms_format(buf, sizeof (buf), &hms);
1425 draw_putsxy_oriented(osd.dur_rect.l, osd.dur_rect.t, buf);
1426 }
1427 /* else don't know the duration */
1428}
1429
1430/* Refresh the current time display + the progress bar */
1431static void osd_refresh_time(void)
1432{
1433 char buf[32];
1434 struct hms hms;
1435
1436 uint32_t duration = stream_get_duration();
1437
1438 draw_scrollbar_draw_rect(&osd.prog_rect, 0, duration,
1439 osd.curr_time);
1440
1441 ts_to_hms(osd.curr_time, &hms);
1442 hms_format(buf, sizeof (buf), &hms);
1443
1444 draw_clear_area_rect(&osd.time_rect);
1445 draw_putsxy_oriented(osd.time_rect.l, osd.time_rect.t, buf);
1446
1447 vo_rect_union(&osd.update_rect, &osd.update_rect,
1448 &osd.prog_rect);
1449 vo_rect_union(&osd.update_rect, &osd.update_rect,
1450 &osd.time_rect);
1451}
1452
1453/* Refresh the volume display area */
1454static void osd_refresh_volume(void)
1455{
1456 char buf[32];
1457 int width;
1458
1459 int volume = rb->global_settings->volume;
1460 rb->snprintf(buf, sizeof (buf), "%d%s",
1461 rb->sound_val2phys(SOUND_VOLUME, volume),
1462 rb->sound_unit(SOUND_VOLUME));
1463 mylcd_getstringsize(buf, &width, NULL);
1464
1465 /* Right-justified */
1466 draw_clear_area_rect(&osd.vol_rect);
1467 draw_putsxy_oriented(osd.vol_rect.r - width, osd.vol_rect.t, buf);
1468
1469 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.vol_rect);
1470}
1471
1472/* Refresh the status icon */
1473static void osd_refresh_status(void)
1474{
1475 int icon_size = osd.stat_rect.r - osd.stat_rect.l;
1476
1477 draw_clear_area_rect(&osd.stat_rect);
1478
1479#ifdef HAVE_LCD_COLOR
1480 /* Draw status icon with a drop shadow */
1481 unsigned oldfg = mylcd_get_foreground();
1482 int i = 1;
1483
1484 mylcd_set_foreground(draw_blendcolor(mylcd_get_background(),
1485 MYLCD_BLACK, 96));
1486
1487 while (1)
1488 {
1489 draw_oriented_mono_bitmap_part(osd.icons,
1490 icon_size*osd.status,
1491 0,
1492 icon_size*OSD_STATUS_COUNT,
1493 osd.stat_rect.l + osd.x + i,
1494 osd.stat_rect.t + osd.y + i,
1495 icon_size, icon_size);
1496
1497 if (--i < 0)
1498 break;
1499
1500 mylcd_set_foreground(oldfg);
1501 }
1502
1503 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
1504#else
1505 draw_oriented_mono_bitmap_part(osd.icons,
1506 icon_size*osd.status,
1507 0,
1508 icon_size*OSD_STATUS_COUNT,
1509 osd.stat_rect.l + osd.x,
1510 osd.stat_rect.t + osd.y,
1511 icon_size, icon_size);
1512 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
1513#endif
1514}
1515
1516/* Update the current status which determines which icon is displayed */
1517static bool osd_update_status(void)
1518{
1519 int status;
1520
1521 switch (stream_status())
1522 {
1523 default:
1524 status = OSD_STATUS_STOPPED;
1525 break;
1526 case STREAM_PAUSED:
1527 /* If paused with a pending resume, coerce it to OSD_STATUS_PLAYING */
1528 status = (osd.auto_refresh & OSD_REFRESH_RESUME) ?
1529 OSD_STATUS_PLAYING : OSD_STATUS_PAUSED;
1530 break;
1531 case STREAM_PLAYING:
1532 status = OSD_STATUS_PLAYING;
1533 break;
1534 }
1535
1536 if (status != osd.status) {
1537 /* A refresh is needed */
1538 osd.status = status;
1539 return true;
1540 }
1541
1542 return false;
1543}
1544
1545/* Update the current time that will be displayed */
1546static void osd_update_time(void)
1547{
1548 uint32_t start;
1549 osd.curr_time = stream_get_seek_time(&start);
1550 osd.curr_time -= start;
1551}
1552
1553/* Refresh various parts of the OSD - showing it if it is hidden */
1554static void osd_refresh(int hint)
1555{
1556 long tick;
1557 unsigned oldbg, oldfg;
1558
1559 tick = *rb->current_tick;
1560
1561 if (settings.showfps)
1562 fps_refresh();
1563
1564 if (hint == OSD_REFRESH_DEFAULT) {
1565 /* The default which forces no updates */
1566
1567 /* Make sure Rockbox doesn't turn off the player because of
1568 too little activity */
1569 if (osd.status == OSD_STATUS_PLAYING)
1570 rb->reset_poweroff_timer();
1571
1572 /* Redraw the current or possibly extract a new video frame */
1573 if ((osd.auto_refresh & OSD_REFRESH_VIDEO) &&
1574 TIME_AFTER(tick, osd.print_tick)) {
1575 osd.auto_refresh &= ~OSD_REFRESH_VIDEO;
1576 stream_draw_frame(false);
1577 }
1578
1579 /* Restart playback if the timout was reached */
1580 if ((osd.auto_refresh & OSD_REFRESH_RESUME) &&
1581 TIME_AFTER(tick, osd.resume_tick)) {
1582 osd.auto_refresh &= ~(OSD_REFRESH_RESUME | OSD_REFRESH_VIDEO);
1583 stream_resume();
1584 }
1585
1586 /* If not visible, return */
1587 if (!(osd.flags & OSD_SHOW))
1588 return;
1589
1590 /* Hide if the visibility duration was reached */
1591 if (TIME_AFTER(tick, osd.hide_tick)) {
1592 osd_show(OSD_HIDE);
1593 return;
1594 }
1595 } else {
1596 /* A forced update of some region */
1597
1598 /* Show if currently invisible */
1599 if (!(osd.flags & OSD_SHOW)) {
1600 /* Avoid call back into this function - it will be drawn */
1601 osd_show(OSD_SHOW | OSD_NODRAW);
1602 hint = OSD_REFRESH_ALL;
1603 }
1604
1605 /* Move back timeouts for frame print and hide */
1606 osd.print_tick = tick + osd.print_delay;
1607 osd.hide_tick = tick + osd.show_for;
1608 }
1609
1610 if (TIME_AFTER(tick, osd.next_auto_refresh)) {
1611 /* Refresh whatever graphical elements are due automatically */
1612 osd.next_auto_refresh = tick + OSD_MIN_UPDATE_INTERVAL;
1613
1614 if (osd.auto_refresh & OSD_REFRESH_STATUS) {
1615 if (osd_update_status())
1616 hint |= OSD_REFRESH_STATUS;
1617 }
1618
1619 if (osd.auto_refresh & OSD_REFRESH_TIME) {
1620 osd_update_time();
1621 hint |= OSD_REFRESH_TIME;
1622 }
1623 }
1624
1625 if (hint == 0)
1626 return; /* No drawing needed */
1627
1628 /* Set basic drawing params that are used. Elements that perform variations
1629 * will restore them. */
1630 oldfg = mylcd_get_foreground();
1631 oldbg = mylcd_get_background();
1632
1633 draw_setfont(FONT_UI);
1634 mylcd_set_foreground(osd.fgcolor);
1635 mylcd_set_background(osd.bgcolor);
1636
1637 vo_rect_clear(&osd.update_rect);
1638
1639 if (hint & OSD_REFRESH_BACKGROUND) {
1640 osd_refresh_background();
1641 hint |= OSD_REFRESH_ALL; /* Requires a redraw of everything */
1642 }
1643
1644 if (hint & OSD_REFRESH_TIME) {
1645 osd_refresh_time();
1646 }
1647
1648 if (hint & OSD_REFRESH_VOLUME) {
1649 osd_refresh_volume();
1650 }
1651
1652 if (hint & OSD_REFRESH_STATUS) {
1653 osd_refresh_status();
1654 }
1655
1656 /* Go back to defaults */
1657 draw_setfont(FONT_SYSFIXED);
1658 mylcd_set_foreground(oldfg);
1659 mylcd_set_background(oldbg);
1660
1661 /* Update the dirty rectangle */
1662 vo_lock();
1663
1664 draw_update_rect(osd.update_rect.l,
1665 osd.update_rect.t,
1666 osd.update_rect.r - osd.update_rect.l,
1667 osd.update_rect.b - osd.update_rect.t);
1668
1669 vo_unlock();
1670}
1671
1672/* Show/Hide the OSD */
1673static void osd_show(unsigned show)
1674{
1675 if (((show ^ osd.flags) & OSD_SHOW) == 0)
1676 {
1677 if (show & OSD_SHOW) {
1678 osd.hide_tick = *rb->current_tick + osd.show_for;
1679 }
1680 return;
1681 }
1682
1683 if (show & OSD_SHOW) {
1684 /* Clip away the part of video that is covered */
1685 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, osd.y };
1686
1687 osd.flags |= OSD_SHOW;
1688
1689 if (osd.status != OSD_STATUS_PLAYING) {
1690 /* Not playing - set brightness to mpegplayer setting */
1691 osd_backlight_brightness_video_mode(true);
1692 }
1693
1694 stream_vo_set_clip(&rc);
1695
1696 if (!(show & OSD_NODRAW))
1697 osd_refresh(OSD_REFRESH_ALL);
1698 } else {
1699 /* Uncover clipped video area and redraw it */
1700 osd.flags &= ~OSD_SHOW;
1701
1702 draw_clear_area(0, 0, osd.width, osd.height);
1703
1704 if (!(show & OSD_NODRAW)) {
1705 vo_lock();
1706 draw_update_rect(0, 0, osd.width, osd.height);
1707 vo_unlock();
1708
1709 stream_vo_set_clip(NULL);
1710 stream_draw_frame(false);
1711 } else {
1712 stream_vo_set_clip(NULL);
1713 }
1714
1715 if (osd.status != OSD_STATUS_PLAYING) {
1716 /* Not playing - restore backlight brightness */
1717 osd_backlight_brightness_video_mode(false);
1718 }
1719 }
1720}
1721
1722/* Set the current status - update screen if specified */
1723static void osd_set_status(int status)
1724{
1725 bool draw = (status & OSD_NODRAW) == 0;
1726
1727 status &= OSD_STATUS_MASK;
1728
1729 if (osd.status != status) {
1730
1731 osd.status = status;
1732
1733 if (draw)
1734 osd_refresh(OSD_REFRESH_STATUS);
1735 }
1736}
1737
1738/* Get the current status value */
1739static int osd_get_status(void)
1740{
1741 return osd.status & OSD_STATUS_MASK;
1742}
1743
1744/* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;)
1745 * Returns last button code
1746 */
1747static int osd_ff_rw(int btn, unsigned refresh, uint32_t *new_time)
1748{
1749 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1750 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
1751 uint32_t start;
1752 uint32_t time = stream_get_seek_time(&start);
1753 const uint32_t duration = stream_get_duration();
1754 unsigned int max_step = 0;
1755 uint32_t ff_rw_count = 0;
1756 unsigned status = osd.status;
1757 int new_btn;
1758
1759 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME |
1760 OSD_REFRESH_TIME);
1761
1762 time -= start; /* Absolute clock => stream-relative */
1763
1764 switch (btn)
1765 {
1766 case MPEG_FF:
1767#ifdef MPEG_FF2
1768 case MPEG_FF2:
1769#endif
1770#ifdef MPEG_RC_FF
1771 case MPEG_RC_FF:
1772#endif
1773 osd_set_status(OSD_STATUS_FF);
1774 new_btn = btn | BUTTON_REPEAT; /* simplify code below */
1775 break;
1776 case MPEG_RW:
1777#ifdef MPEG_RW2
1778 case MPEG_RW2:
1779#endif
1780#ifdef MPEG_RC_RW
1781 case MPEG_RC_RW:
1782#endif
1783 osd_set_status(OSD_STATUS_RW);
1784 new_btn = btn | BUTTON_REPEAT; /* simplify code below */
1785 break;
1786 default:
1787 new_btn = BUTTON_NONE; /* Fail tests below but still do proper exit */
1788 }
1789
1790 while (1)
1791 {
1792 stream_keep_disk_active();
1793
1794 if (new_btn == (btn | BUTTON_REPEAT)) {
1795 if (osd.status == OSD_STATUS_FF) {
1796 /* fast forwarding, calc max step relative to end */
1797 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1798 FF_REWIND_MAX_PERCENT, 100);
1799 } else {
1800 /* rewinding, calc max step relative to start */
1801 max_step = muldiv_uint32(time - ff_rw_count,
1802 FF_REWIND_MAX_PERCENT, 100);
1803 }
1804
1805 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1806
1807 if (step > max_step)
1808 step = max_step;
1809
1810 ff_rw_count += step;
1811
1812 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1813 step += step >> ff_rw_accel;
1814
1815 if (osd.status == OSD_STATUS_FF) {
1816 if (duration - time <= ff_rw_count)
1817 ff_rw_count = duration - time;
1818
1819 osd.curr_time = time + ff_rw_count;
1820 } else {
1821 if (time <= ff_rw_count)
1822 ff_rw_count = time;
1823
1824 osd.curr_time = time - ff_rw_count;
1825 }
1826
1827 osd_refresh(OSD_REFRESH_TIME);
1828
1829 new_btn = mpeg_button_get(TIMEOUT_BLOCK);
1830 }
1831 else {
1832 if (new_btn == (btn | BUTTON_REL)) {
1833 if (osd.status == OSD_STATUS_FF)
1834 time += ff_rw_count;
1835 else if (osd.status == OSD_STATUS_RW)
1836 time -= ff_rw_count;
1837 }
1838
1839 *new_time = time;
1840
1841 osd_schedule_refresh(refresh);
1842 osd_set_status(status);
1843 osd_schedule_refresh(OSD_REFRESH_TIME);
1844
1845 return new_btn;
1846 }
1847 }
1848}
1849
1850/* Return adjusted STREAM_* status */
1851static int osd_stream_status(void)
1852{
1853 int status = stream_status();
1854
1855 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1856 if (status == STREAM_PAUSED) {
1857 if (osd.auto_refresh & OSD_REFRESH_RESUME)
1858 status = STREAM_PLAYING;
1859 }
1860
1861 return status;
1862}
1863
1864/* Change the current audio volume by a specified amount */
1865static void osd_set_volume(int delta)
1866{
1867 int vol = rb->global_settings->volume;
1868 int limit;
1869
1870 vol += delta;
1871
1872 if (delta < 0) {
1873 /* Volume down - clip to lower limit */
1874 limit = rb->sound_min(SOUND_VOLUME);
1875 if (vol < limit)
1876 vol = limit;
1877 } else {
1878 /* Volume up - clip to upper limit */
1879 limit = rb->sound_max(SOUND_VOLUME);
1880 if (vol > limit)
1881 vol = limit;
1882 }
1883
1884 /* Sync the global settings */
1885 if (vol != rb->global_settings->volume) {
1886 rb->sound_set(SOUND_VOLUME, vol);
1887 rb->global_settings->volume = vol;
1888 }
1889
1890 /* Update the volume display */
1891 osd_refresh(OSD_REFRESH_VOLUME);
1892}
1893
1894/* Begin playback at the specified time */
1895static int osd_play(uint32_t time)
1896{
1897 int retval;
1898
1899 osd_set_hp_pause_flag(false);
1900 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1901
1902 retval = stream_seek(time, SEEK_SET);
1903
1904 if (retval >= STREAM_OK) {
1905 osdbacklight_hw_on_video_mode(true);
1906 osd_backlight_brightness_video_mode(true);
1907 stream_show_vo(true);
1908
1909 retval = stream_play();
1910
1911 if (retval >= STREAM_OK)
1912 osd_set_status(OSD_STATUS_PLAYING | OSD_NODRAW);
1913 }
1914
1915 return retval;
1916}
1917
1918/* Halt playback - pause engine and return logical state */
1919static int osd_halt(void)
1920{
1921 int status = stream_pause();
1922
1923 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1924 if (status == STREAM_PAUSED) {
1925 if (osd_get_status() == OSD_STATUS_PLAYING)
1926 status = STREAM_PLAYING;
1927 }
1928
1929 /* Cancel some auto refreshes - caller will restart them if desired */
1930 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1931
1932 /* No backlight fiddling here - callers does the right thing */
1933
1934 return status;
1935}
1936
1937/* Pause playback if playing */
1938static int osd_pause(void)
1939{
1940 unsigned refresh = osd.auto_refresh;
1941 int status = osd_halt();
1942
1943 osd_set_hp_pause_flag(false);
1944
1945 if (status == STREAM_PLAYING && (refresh & OSD_REFRESH_RESUME)) {
1946 /* Resume pending - change to a still video frame update */
1947 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1948 }
1949
1950 osd_set_status(OSD_STATUS_PAUSED);
1951
1952 osdbacklight_hw_on_video_mode(false);
1953 /* Leave brightness alone and restore it when OSD is hidden */
1954
1955 if (stream_can_seek() && rb->global_settings->pause_rewind) {
1956 stream_seek(-rb->global_settings->pause_rewind*TS_SECOND,
1957 SEEK_CUR);
1958 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1959 /* Update time display now */
1960 osd_update_time();
1961 osd_refresh(OSD_REFRESH_TIME);
1962 }
1963
1964 return status;
1965}
1966
1967/* Resume playback if halted or paused */
1968static void osd_resume(void)
1969{
1970 /* Cancel video and resume auto refresh - the resyc when starting
1971 * playback will perform those tasks */
1972 osd_set_hp_pause_flag(false);
1973 osdbacklight_hw_on_video_mode(true);
1974 osd_backlight_brightness_video_mode(true);
1975 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1976 osd_set_status(OSD_STATUS_PLAYING);
1977 stream_resume();
1978}
1979
1980/* Stop playback - remember the resume point if not closed */
1981static void osd_stop(void)
1982{
1983 uint32_t resume_time;
1984
1985 osd_set_hp_pause_flag(false);
1986 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1987 osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW);
1988 osd_show(OSD_HIDE);
1989
1990 stream_stop();
1991
1992 resume_time = stream_get_resume_time();
1993
1994 if (resume_time != INVALID_TIMESTAMP)
1995 settings.resume_time = resume_time;
1996
1997 osdbacklight_hw_on_video_mode(false);
1998 osd_backlight_brightness_video_mode(false);
1999}
2000
2001/* Perform a seek by button if seeking is possible for this stream.
2002 *
2003 * A delay will be inserted before restarting in case the user decides to
2004 * seek again soon after.
2005 *
2006 * Returns last button code
2007 */
2008static int osd_seek_btn(int btn)
2009{
2010 int status;
2011 unsigned refresh = 0;
2012 uint32_t time;
2013
2014 if (!stream_can_seek())
2015 return true;
2016
2017 /* Halt playback - not strictly necessary but nice when doing
2018 * buttons */
2019 status = osd_halt();
2020
2021 if (status == STREAM_STOPPED)
2022 return true;
2023
2024 osd_show(OSD_SHOW);
2025
2026 /* Obtain a new playback point according to the buttons */
2027 if (status == STREAM_PLAYING)
2028 refresh = OSD_REFRESH_RESUME; /* delay resume if playing */
2029 else
2030 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
2031
2032 btn = osd_ff_rw(btn, refresh, &time);
2033
2034 /* Tell engine to resume at that time */
2035 stream_seek(time, SEEK_SET);
2036
2037 return btn;
2038}
2039
2040/* Perform a seek by time if seeking is possible for this stream
2041 *
2042 * If playing, the seeking is immediate, otherise a delay is added to showing
2043 * a still if paused in case the user does another seek soon after.
2044 *
2045 * If seeking isn't possible, a time of zero performs a skip to the
2046 * beginning.
2047 */
2048static void osd_seek_time(uint32_t time)
2049{
2050 int status;
2051 unsigned refresh = 0;
2052
2053 if (!stream_can_seek() && time != 0)
2054 return;
2055
2056 stream_wait_status();
2057 status = osd_stream_status();
2058
2059 if (status == STREAM_STOPPED)
2060 return;
2061
2062 if (status == STREAM_PLAYING) /* merely preserve resume */
2063 refresh = osd.auto_refresh & OSD_REFRESH_RESUME;
2064 else
2065 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
2066
2067 /* Cancel print or resume if pending */
2068 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
2069
2070 /* Tell engine to seek to the given time - no state change */
2071 stream_seek(time, SEEK_SET);
2072
2073 osd_update_time();
2074 osd_refresh(OSD_REFRESH_TIME);
2075 osd_schedule_refresh(refresh);
2076}
2077
2078/* Has this file one of the supported extensions? */
2079static bool is_videofile(const char* file)
2080{
2081 static const char * const extensions[] =
2082 {
2083 /* Should match apps/plugins/viewers.config */
2084 "mpg", "mpeg", "mpv", "m2v"
2085 };
2086
2087 const char* ext = rb->strrchr(file, '.');
2088 int i;
2089
2090 if (!ext)
2091 return false;
2092
2093 for (i = ARRAYLEN(extensions) - 1; i >= 0; i--)
2094 {
2095 if (!rb->strcasecmp(ext + 1, extensions[i]))
2096 break;
2097 }
2098
2099 return i >= 0;
2100}
2101
2102/* deliver the next/previous video file in the current directory.
2103 returns false if there is none. */
2104static bool get_videofile(int direction, char* videofile, size_t bufsize)
2105{
2106 struct tree_context *tree = rb->tree_get_context();
2107 struct entry *dircache = rb->tree_get_entries(tree);
2108 int i, step, end, found = 0;
2109 char *videoname = rb->strrchr(videofile, '/') + 1;
2110 size_t rest = bufsize - (videoname - videofile) - 1;
2111
2112 if (direction == VIDEO_NEXT) {
2113 i = 0;
2114 step = 1;
2115 end = tree->filesindir;
2116 } else {
2117 i = tree->filesindir-1;
2118 step = -1;
2119 end = -1;
2120 }
2121 for (; i != end; i += step)
2122 {
2123 const char* name = dircache[i].name;
2124 if (!rb->strcmp(name, videoname)) {
2125 found = 1;
2126 continue;
2127 }
2128 if (found && rb->strlen(name) <= rest &&
2129 !(dircache[i].attr & ATTR_DIRECTORY) && is_videofile(name))
2130 {
2131 rb->strcpy(videoname, name);
2132 return true;
2133 }
2134 }
2135
2136 return false;
2137}
2138
2139#ifdef HAVE_HEADPHONE_DETECTION
2140/* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
2141static void osd_handle_phone_plug(bool inserted)
2142{
2143 if (rb->global_settings->unplug_mode == 0)
2144 return;
2145
2146 /* Wait for any incomplete state transition to complete first */
2147 stream_wait_status();
2148
2149 int status = osd_stream_status();
2150
2151 if (inserted) {
2152 if (rb->global_settings->unplug_mode > 1) {
2153 if (status == STREAM_PAUSED &&
2154 (osd.flags & OSD_HP_PAUSE)) {
2155 osd_resume();
2156 }
2157 }
2158 } else {
2159 if (status == STREAM_PLAYING) {
2160 osd_pause();
2161
2162 osd_set_hp_pause_flag(true);
2163 }
2164 }
2165}
2166#endif
2167
2168static int button_loop(void)
2169{
2170 int next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
2171
2172 rb->lcd_setfont(FONT_SYSFIXED);
2173#ifdef HAVE_LCD_COLOR
2174 rb->lcd_set_foreground(LCD_WHITE);
2175 rb->lcd_set_background(LCD_BLACK);
2176#endif
2177 rb->lcd_clear_display();
2178 rb->lcd_update();
2179
2180#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2181 rb->lcd_set_mode(LCD_MODE_YUV);
2182#endif
2183
2184 osd_init();
2185
2186 /* Start playback at the specified starting time */
2187 if (osd_play(settings.resume_time) < STREAM_OK) {
2188 rb->splash(HZ*2, "Playback failed");
2189 return VIDEO_STOP;
2190 }
2191
2192 /* Gently poll the video player for EOS and handle UI */
2193 while (stream_status() != STREAM_STOPPED)
2194 {
2195 int button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL/2);
2196
2197 switch (button)
2198 {
2199 case BUTTON_NONE:
2200 {
2201 osd_refresh(OSD_REFRESH_DEFAULT);
2202 continue;
2203 } /* BUTTON_NONE: */
2204
2205#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
2206 case LCD_ENABLE_EVENT_1:
2207 {
2208 /* Draw the current frame if prepared already */
2209 stream_draw_frame(true);
2210 break;
2211 } /* LCD_ENABLE_EVENT_1: */
2212#endif
2213
2214 case MPEG_VOLUP:
2215 case MPEG_VOLUP|BUTTON_REPEAT:
2216#ifdef MPEG_VOLUP2
2217 case MPEG_VOLUP2:
2218 case MPEG_VOLUP2|BUTTON_REPEAT:
2219#endif
2220#ifdef MPEG_RC_VOLUP
2221 case MPEG_RC_VOLUP:
2222 case MPEG_RC_VOLUP|BUTTON_REPEAT:
2223#endif
2224 {
2225 osd_set_volume(+1);
2226 break;
2227 } /* MPEG_VOLUP*: */
2228
2229 case MPEG_VOLDOWN:
2230 case MPEG_VOLDOWN|BUTTON_REPEAT:
2231#ifdef MPEG_VOLDOWN2
2232 case MPEG_VOLDOWN2:
2233 case MPEG_VOLDOWN2|BUTTON_REPEAT:
2234#endif
2235#ifdef MPEG_RC_VOLDOWN
2236 case MPEG_RC_VOLDOWN:
2237 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
2238#endif
2239 {
2240 osd_set_volume(-1);
2241 break;
2242 } /* MPEG_VOLDOWN*: */
2243
2244 case MPEG_MENU:
2245#ifdef MPEG_RC_MENU
2246 case MPEG_RC_MENU:
2247#endif
2248 {
2249 int state = osd_halt(); /* save previous state */
2250 int result;
2251
2252 /* Hide video output */
2253 osd_show(OSD_HIDE | OSD_NODRAW);
2254 stream_show_vo(false);
2255 osd_backlight_brightness_video_mode(false);
2256
2257#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2258 rb->lcd_set_mode(LCD_MODE_RGB565);
2259#endif
2260
2261 result = mpeg_menu();
2262
2263 next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
2264
2265 fps_update_post_frame_callback();
2266
2267 /* The menu can change the font, so restore */
2268 rb->lcd_setfont(FONT_SYSFIXED);
2269#ifdef HAVE_LCD_COLOR
2270 rb->lcd_set_foreground(LCD_WHITE);
2271 rb->lcd_set_background(LCD_BLACK);
2272#endif
2273 rb->lcd_clear_display();
2274 rb->lcd_update();
2275
2276 switch (result)
2277 {
2278 case MPEG_MENU_QUIT:
2279 next_action = VIDEO_STOP;
2280 osd_stop();
2281 break;
2282
2283 default:
2284#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2285 rb->lcd_set_mode(LCD_MODE_YUV);
2286#endif
2287 /* If not stopped, show video again */
2288 if (state != STREAM_STOPPED) {
2289 osd_show(OSD_SHOW);
2290 stream_show_vo(true);
2291 }
2292
2293 /* If stream was playing, restart it */
2294 if (state == STREAM_PLAYING) {
2295 osd_resume();
2296 }
2297 break;
2298 }
2299 break;
2300 } /* MPEG_MENU: */
2301
2302#ifdef MPEG_SHOW_OSD
2303 case MPEG_SHOW_OSD:
2304 case MPEG_SHOW_OSD | BUTTON_REPEAT:
2305 /* Show if not visible */
2306 osd_show(OSD_SHOW);
2307 /* Make sure it refreshes */
2308 osd_refresh(OSD_REFRESH_DEFAULT);
2309 break;
2310#endif
2311
2312 case MPEG_STOP:
2313#ifdef MPEG_RC_STOP
2314 case MPEG_RC_STOP:
2315#endif
2316 case ACTION_STD_CANCEL:
2317 {
2318 cancel_playback:
2319 next_action = VIDEO_STOP;
2320 osd_stop();
2321 break;
2322 } /* MPEG_STOP: */
2323
2324 case MPEG_PAUSE:
2325#ifdef MPEG_PAUSE2
2326 case MPEG_PAUSE2:
2327#endif
2328#ifdef MPEG_RC_PAUSE
2329 case MPEG_RC_PAUSE:
2330#endif
2331 {
2332 int status = osd_stream_status();
2333
2334 if (status == STREAM_PLAYING) {
2335 /* Playing => Paused */
2336 osd_pause();
2337 }
2338 else if (status == STREAM_PAUSED) {
2339 /* Paused => Playing */
2340 osd_resume();
2341 }
2342
2343 break;
2344 } /* MPEG_PAUSE*: */
2345
2346 case MPEG_RW:
2347#ifdef MPEG_RW2
2348 case MPEG_RW2:
2349#endif
2350#ifdef MPEG_RC_RW
2351 case MPEG_RC_RW:
2352#endif
2353 {
2354 int old_button = button;
2355
2356 /* If button has been released: skip to next/previous file */
2357 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
2358
2359 if ((old_button | BUTTON_REL) == button) {
2360 /* Check current playback position */
2361 osd_update_time();
2362
2363 if (settings.play_mode == 0 || osd.curr_time >= 3*TS_SECOND) {
2364 /* Start the current video from the beginning */
2365 osd_seek_time(0*TS_SECOND);
2366 }
2367 else {
2368 /* Release within 3 seconds of start: skip to previous
2369 * file */
2370 osd_stop();
2371 next_action = VIDEO_PREV | VIDEO_ACTION_MANUAL;
2372 }
2373 }
2374 else if ((button & ~BUTTON_REPEAT) == old_button) {
2375 button = osd_seek_btn(old_button);
2376 }
2377
2378 if (button == ACTION_STD_CANCEL)
2379 goto cancel_playback; /* jump to stop handling above */
2380
2381 rb->default_event_handler(button);
2382 break;
2383 } /* MPEG_RW: */
2384
2385 case MPEG_FF:
2386#ifdef MPEG_FF2
2387 case MPEG_FF2:
2388#endif
2389#ifdef MPEG_RC_FF
2390 case MPEG_RC_FF:
2391#endif
2392 {
2393 int old_button = button;
2394
2395 if (settings.play_mode != 0)
2396 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
2397
2398 if ((old_button | BUTTON_REL) == button) {
2399 /* If button has been released: skip to next file */
2400 osd_stop();
2401 next_action = VIDEO_NEXT | VIDEO_ACTION_MANUAL;
2402 }
2403 else if ((button & ~BUTTON_REPEAT) == old_button) {
2404 button = osd_seek_btn(old_button);
2405 }
2406
2407 if (button == ACTION_STD_CANCEL)
2408 goto cancel_playback; /* jump to stop handling above */
2409
2410 rb->default_event_handler(button);
2411 break;
2412 } /* MPEG_FF: */
2413
2414#ifdef HAVE_HEADPHONE_DETECTION
2415 case SYS_PHONE_PLUGGED:
2416 case SYS_PHONE_UNPLUGGED:
2417 {
2418 osd_handle_phone_plug(button == SYS_PHONE_PLUGGED);
2419 break;
2420 } /* SYS_PHONE_*: */
2421#endif
2422
2423 default:
2424 {
2425 osd_refresh(OSD_REFRESH_DEFAULT);
2426 rb->default_event_handler(button);
2427 break;
2428 } /* default: */
2429 }
2430
2431 rb->yield();
2432 } /* end while */
2433
2434 osd_stop();
2435
2436#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
2437 /* Be sure hook is removed before exiting since the stop will put it
2438 * back because of the backlight restore. */
2439 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
2440#endif
2441
2442 rb->lcd_setfont(FONT_UI);
2443
2444 return next_action;
2445}
2446
2447enum plugin_status plugin_start(const void* parameter)
2448{
2449 static char videofile[MAX_PATH];
2450 int status = PLUGIN_OK; /* assume success */
2451 bool quit = false;
2452
2453#if defined(PLUGIN_USE_IRAM) && !defined(SIMULATOR)
2454 bool preserved_talk_state;
2455#endif
2456
2457 if (parameter == NULL) {
2458 /* No file = GTFO */
2459 rb->splash(HZ*2, "No File");
2460 return PLUGIN_ERROR;
2461 }
2462
2463 /* Disable all talking before initializing IRAM */
2464 rb->talk_disable(true);
2465
2466#ifdef PLUGIN_USE_IRAM
2467 iram_saving_init();
2468
2469#ifndef SIMULATOR
2470 preserved_talk_state = rb->global_settings->talk_menu;
2471 if (!iram_saved_copy)
2472 rb->global_settings->talk_menu = false;
2473#endif
2474#endif
2475
2476#ifdef HAVE_LCD_COLOR
2477 rb->lcd_set_backdrop(NULL);
2478 rb->lcd_set_foreground(LCD_WHITE);
2479 rb->lcd_set_background(LCD_BLACK);
2480#endif
2481
2482 rb->lcd_clear_display();
2483 rb->lcd_update();
2484
2485 rb->strcpy(videofile, (const char*) parameter);
2486
2487 if (stream_init() < STREAM_OK) {
2488 /* Fatal because this should not fail */
2489 DEBUGF("Could not initialize streams\n");
2490 status = PLUGIN_ERROR;
2491 } else {
2492 int next_action = VIDEO_STOP;
2493 bool get_videofile_says = true;
2494
2495 while (!quit)
2496 {
2497 init_settings(videofile);
2498
2499 int result = stream_open(videofile);
2500 bool manual_skip = false;
2501
2502 if (result >= STREAM_OK) {
2503 /* start menu */
2504 rb->lcd_clear_display();
2505 rb->lcd_update();
2506 result = mpeg_start_menu(stream_get_duration());
2507
2508 next_action = VIDEO_STOP;
2509 if (result != MPEG_START_QUIT) {
2510 /* Enter button loop and process UI */
2511 next_action = button_loop();
2512 manual_skip = next_action & VIDEO_ACTION_MANUAL;
2513 next_action &= ~VIDEO_ACTION_MANUAL;
2514 }
2515
2516 stream_close();
2517
2518 rb->lcd_clear_display();
2519 rb->lcd_update();
2520
2521 save_settings();
2522 } else {
2523 /* Problem with file; display message about it - not
2524 * considered a plugin error */
2525 long tick;
2526 const char *errstring;
2527
2528 DEBUGF("Could not open %s\n", videofile);
2529 switch (result)
2530 {
2531 case STREAM_UNSUPPORTED:
2532 errstring = "Unsupported format";
2533 break;
2534 default:
2535 errstring = "Error opening file: %d";
2536 }
2537
2538 tick = *rb->current_tick + HZ*2;
2539
2540 rb->splashf(0, errstring, result);
2541
2542 /* Be sure it doesn't get stuck in an unbreakable loop of bad
2543 * files, just in case! Otherwise, keep searching in the
2544 * chosen direction until a good one is found. */
2545 while (!quit && TIME_BEFORE(*rb->current_tick, tick))
2546 {
2547 int button = mpeg_button_get(HZ*2);
2548
2549 switch (button)
2550 {
2551 case MPEG_STOP:
2552 case ACTION_STD_CANCEL:
2553 /* Abort the search and exit */
2554 next_action = VIDEO_STOP;
2555 quit = true;
2556 break;
2557
2558 case BUTTON_NONE:
2559 if (settings.play_mode != 0) {
2560 if (next_action == VIDEO_STOP) {
2561 /* Default to next file */
2562 next_action = VIDEO_NEXT;
2563 }
2564 else if (next_action == VIDEO_PREV &&
2565 !get_videofile_says) {
2566 /* Was first file already; avoid endlessly
2567 * retrying it */
2568 next_action = VIDEO_STOP;
2569 }
2570 }
2571 break;
2572
2573 default:
2574 rb->default_event_handler(button);
2575 } /* switch */
2576 } /* while */
2577 }
2578
2579 /* return value of button_loop says, what's next */
2580 switch (next_action)
2581 {
2582 case VIDEO_NEXT:
2583 {
2584 get_videofile_says = get_videofile(VIDEO_NEXT, videofile,
2585 sizeof(videofile));
2586 /* quit after finished the last videofile */
2587 quit = !get_videofile_says;
2588
2589 if (manual_skip)
2590 {
2591 rb->system_sound_play(get_videofile_says ?
2592 SOUND_TRACK_SKIP : SOUND_TRACK_NO_MORE);
2593 }
2594
2595 break;
2596 }
2597 case VIDEO_PREV:
2598 {
2599 get_videofile_says = get_videofile(VIDEO_PREV, videofile,
2600 sizeof(videofile));
2601 /* if there is no previous file, play the same videofile */
2602
2603 if (manual_skip)
2604 {
2605 rb->system_sound_play(get_videofile_says ?
2606 SOUND_TRACK_SKIP : SOUND_TRACK_NO_MORE);
2607 }
2608
2609 break;
2610 }
2611 case VIDEO_STOP:
2612 {
2613 quit = true;
2614 break;
2615 }
2616 }
2617 } /* while */
2618 }
2619
2620#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2621 rb->lcd_set_mode(LCD_MODE_RGB565);
2622#endif
2623
2624 stream_exit();
2625
2626#if defined(PLUGIN_USE_IRAM) && !defined(SIMULATOR)
2627 if (!iram_saved_copy)
2628 rb->global_settings->talk_menu = preserved_talk_state;
2629#endif
2630
2631 rb->talk_disable(false);
2632
2633 /* Actually handle delayed processing of system events of interest
2634 * that were captured in other button loops */
2635 mpeg_sysevent_handle();
2636
2637 return status;
2638}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Main mpegplayer config header.
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef MPEGPLAYER_H
24#define MPEGPLAYER_H
25
26#ifdef HAVE_SCHEDULER_BOOSTCTRL
27#define trigger_cpu_boost rb->trigger_cpu_boost
28#define cancel_cpu_boost rb->cancel_cpu_boost
29#endif
30/* #else function-like empty macros are defined in the headers */
31
32/* Should be enough for now */
33#define MPEGPLAYER_MAX_STREAMS 4
34
35/* Memory allotments for various subsystems */
36#define MIN_MEMMARGIN (4*1024)
37
38/** Video thread **/
39#define LIBMPEG2_ALLOC_SIZE (2*1024*1024)
40
41/** MPEG audio buffer **/
42#define AUDIOBUF_GUARD_SIZE (MPA_MAX_FRAME_SIZE + 2*MAD_BUFFER_GUARD)
43#define AUDIOBUF_SIZE (64*1024)
44#define AUDIOBUF_ALLOC_SIZE (AUDIOBUF_SIZE+AUDIOBUF_GUARD_SIZE)
45
46/** PCM buffer **/
47#define CLOCK_RATE 44100 /* Our clock rate in ticks/second (samplerate) */
48
49/* Define this as "1" to have a test tone instead of silence clip */
50#define SILENCE_TEST_TONE 0
51
52/* NOTE: Sizes make no frame header allowance when considering duration */
53#define PCMOUT_BUFSIZE (CLOCK_RATE/2*4) /* 1/2s */
54#define PCMOUT_GUARD_SIZE (PCMOUT_BUFSIZE) /* guarantee contiguous sizes */
55#define PCMOUT_ALLOC_SIZE (PCMOUT_BUFSIZE + PCMOUT_GUARD_SIZE)
56 /* Start pcm playback @ 25% full */
57#define PCMOUT_PLAY_WM (PCMOUT_BUFSIZE/4)
58#define PCMOUT_LOW_WM (0)
59
60/** disk buffer **/
61#define DISK_BUF_LOW_WATERMARK (1024*1024)
62/* 65535+6 is required since each PES has a 6 byte header with a 16 bit
63 * packet length field */
64#define DISK_GUARDBUF_SIZE ALIGN_UP(65535+6, 4)
65
66#ifdef HAVE_LCD_COLOR
67#define mylcd_splash rb->splash
68#else
69#include "lib/grey.h"
70#define mylcd_splash grey_splash
71#endif
72
73#include "lib/mylcd.h"
74
75#include "libmpeg2/mpeg2.h"
76#include "video_out.h"
77#include "mpeg_stream.h"
78#include "mpeg_misc.h"
79#include "mpeg_alloc.h"
80#include "stream_thread.h"
81#include "parser.h"
82#include "pcm_output.h"
83#include "disk_buf.h"
84#include "stream_mgr.h"
85
86#define LCD_ENABLE_EVENT_0 MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0)
87#define LCD_ENABLE_EVENT_1 MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 1)
88
89#ifdef PLUGIN_USE_IRAM
90/* IRAM preserving mechanism to enable talking menus */
91extern void mpegplayer_iram_preserve(void);
92extern void mpegplayer_iram_restore(void);
93#endif
94
95#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 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10MPEGSRCDIR := $(APPSDIR)/plugins/mpegplayer
11MPEGBUILDDIR := $(BUILDDIR)/apps/plugins/mpegplayer
12
13ROCKS += $(MPEGBUILDDIR)/mpegplayer.rock
14
15MPEG_SRC := $(call preprocess, $(MPEGSRCDIR)/SOURCES)
16MPEG_OBJ := $(call c2obj, $(MPEG_SRC))
17
18# add source files to OTHER_SRC to get automatic dependencies
19OTHER_SRC += $(MPEG_SRC)
20
21# Set '-fgnu89-inline' if supported (GCCVER >= 4.1.3, GCCNUM > 401)
22ifeq ($(shell expr $(GCCNUM) \> 401),1)
23 MPEGCFLAGS = $(PLUGINFLAGS) -fgnu89-inline
24else
25 MPEGCFLAGS = $(PLUGINFLAGS)
26endif
27
28$(MPEGBUILDDIR)/mpegplayer.rock: $(MPEG_OBJ) $(CODECDIR)/libmad-mpeg.a
29
30$(MPEGBUILDDIR)/%.o: $(MPEGSRCDIR)/%.c $(MPEGSRCDIR)/mpegplayer.make
31 $(SILENT)mkdir -p $(dir $@)
32 $(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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * AV parser inteface declarations
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef PARSER_H
24#define PARSER_H
25
26enum stream_formats
27{
28 STREAM_FMT_UNKNOWN = -1,
29 STREAM_FMT_MPEG_TS, /* MPEG transport stream */
30 STREAM_FMT_MPEG_PS, /* MPEG program stream */
31 STREAM_FMT_MPV, /* MPEG Video only (1 or 2) */
32 STREAM_FMT_MPA, /* MPEG Audio only */
33};
34
35/* Structure used by a thread that handles a single demuxed data stream and
36 * receives commands from the stream manager */
37enum stream_parse_states
38{
39 /* Stream is... */
40 SSTATE_SYNC, /* synchronizing by trying to find a start code */
41 SSTATE_PARSE, /* parsing the stream looking for packets */
42 SSTATE_END, /* at the end of data */
43};
44
45enum stream_parse_mode
46{
47 STREAM_PM_STREAMING = 0, /* Next packet when streaming */
48 STREAM_PM_RANDOM_ACCESS, /* Random-access parsing */
49};
50
51enum stream_parser_flags
52{
53 STREAMF_CAN_SEEK = 0x1, /* Seeking possible for this stream */
54};
55
56struct stream_parser
57{
58 /* Common generic parser data */
59 enum stream_formats format; /* Stream format */
60 uint32_t start_pts; /* The movie start time as represented by
61 the first audio PTS tag in the
62 stream converted to half minutes */
63 uint32_t end_pts; /* The movie end time as represented by
64 the maximum audio PTS tag in the
65 stream converted to half minutes */
66 uint32_t duration; /* Duration in PTS units */
67 unsigned flags; /* Various attributes set at init */
68 struct vo_ext dims; /* Movie dimensions in pixels */
69 uint32_t last_seek_time;
70 int (*next_data)(struct stream *str, enum stream_parse_mode type);
71 union /* A place for reusable no-cache parameters */
72 {
73 struct str_sync_data sd;
74 } parms;
75};
76
77extern struct stream_parser str_parser;
78
79/* MPEG parsing */
80uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code);
81unsigned mpeg_parser_scan_pes(struct stream_scan *sk);
82uint32_t mpeg_parser_scan_scr(struct stream_scan *sk);
83uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id);
84off_t mpeg_stream_stream_seek_PTS(uint32_t time, int id);
85
86/* General parsing */
87bool parser_init(void);
88void str_initialize(struct stream *str, off_t pos);
89bool parser_prepare_image(uint32_t time);
90bool parser_get_video_size(struct vo_ext *sz);
91int parser_init_stream(void);
92void parser_close_stream(void);
93static inline bool parser_can_seek(void)
94 { return str_parser.flags & STREAMF_CAN_SEEK; }
95uint32_t parser_seek_time(uint32_t time);
96void parser_prepare_streaming(void);
97void str_end_of_stream(struct stream *str);
98
99static inline int parser_get_next_data(struct stream *str,
100 enum stream_parse_mode type)
101 { return str_parser.next_data(str, type); }
102
103#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * PCM output buffer definitions
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "plugin.h"
24#include "mpegplayer.h"
25
26/* PCM channel we're using */
27#define MPEG_PCM_CHANNEL PCM_MIXER_CHAN_PLAYBACK
28
29/* Pointers */
30
31/* Start of buffer */
32static struct pcm_frame_header * ALIGNED_ATTR(4) pcm_buffer;
33/* End of buffer (not guard) */
34static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_end;
35/* Read pointer */
36static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_head IBSS_ATTR;
37/* Write pointer */
38static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR;
39
40/* Bytes */
41static ssize_t pcmbuf_curr_size IBSS_ATTR; /* Size of currently playing frame */
42static ssize_t pcmbuf_read IBSS_ATTR; /* Number of bytes read by DMA */
43static ssize_t pcmbuf_written IBSS_ATTR; /* Number of bytes written by source */
44static ssize_t pcmbuf_threshold IBSS_ATTR; /* Non-silence threshold */
45
46/* Clock */
47static uint32_t clock_start IBSS_ATTR; /* Clock at playback start */
48static uint32_t volatile clock_tick IBSS_ATTR; /* Our base clock */
49static uint32_t volatile clock_time IBSS_ATTR; /* Timestamp adjusted */
50
51static int pcm_skipped = 0;
52static int pcm_underruns = 0;
53
54static unsigned int old_sampr = 0;
55
56/* Small silence clip. ~5.80ms @ 44.1kHz */
57static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 };
58
59/* Delete all buffer contents */
60static void pcm_reset_buffer(void)
61{
62 pcmbuf_threshold = PCMOUT_PLAY_WM;
63 pcmbuf_curr_size = pcmbuf_read = pcmbuf_written = 0;
64 pcmbuf_head = pcmbuf_tail = pcm_buffer;
65 pcm_skipped = pcm_underruns = 0;
66}
67
68/* Advance a PCM buffer pointer by size bytes circularly */
69static inline void pcm_advance_buffer(struct pcm_frame_header **p,
70 size_t size)
71{
72 *p = SKIPBYTES(*p, size);
73 if (*p >= pcmbuf_end)
74 *p = SKIPBYTES(*p, -PCMOUT_BUFSIZE);
75}
76
77/* Return physical space used */
78static inline ssize_t pcm_output_bytes_used(void)
79{
80 return pcmbuf_written - pcmbuf_read; /* wrap-safe */
81}
82
83/* Return physical space free */
84static inline ssize_t pcm_output_bytes_free(void)
85{
86 return PCMOUT_BUFSIZE - pcm_output_bytes_used();
87}
88
89/* Audio DMA handler */
90static void get_more(const void **start, size_t *size)
91{
92 ssize_t sz;
93
94 /* Free-up the last frame played frame if any */
95 pcmbuf_read += pcmbuf_curr_size;
96 pcmbuf_curr_size = 0;
97
98 sz = pcm_output_bytes_used();
99
100 if (sz > pcmbuf_threshold)
101 {
102 pcmbuf_threshold = PCMOUT_LOW_WM;
103
104 while (1)
105 {
106 uint32_t time = pcmbuf_head->time;
107 int32_t offset = time - clock_time;
108
109 sz = pcmbuf_head->size;
110
111 if (sz < (ssize_t)(PCM_HDR_SIZE + 4) ||
112 (sz & 3) != 0)
113 {
114 /* Just show a warning about this - will never happen
115 * without a corrupted buffer */
116 DEBUGF("get_more: invalid size (%ld)\n", (long)sz);
117 }
118
119 if (offset < -100*CLOCK_RATE/1000)
120 {
121 /* Frame more than 100ms late - drop it */
122 pcm_advance_buffer(&pcmbuf_head, sz);
123 pcmbuf_read += sz;
124 pcm_skipped++;
125 if (pcm_output_bytes_used() > 0)
126 continue;
127
128 /* Ran out so revert to default watermark */
129 pcmbuf_threshold = PCMOUT_PLAY_WM;
130 pcm_underruns++;
131 }
132 else if (offset < 100*CLOCK_RATE/1000)
133 {
134 /* Frame less than 100ms early - play it */
135 struct pcm_frame_header *head = pcmbuf_head;
136
137 pcm_advance_buffer(&pcmbuf_head, sz);
138 pcmbuf_curr_size = sz;
139
140 sz -= PCM_HDR_SIZE;
141
142 /* Audio is time master - keep clock synchronized */
143 clock_time = time + (sz >> 2);
144
145 /* Update base clock */
146 clock_tick += sz >> 2;
147
148 *start = head->data;
149 *size = sz;
150 return;
151 }
152 /* Frame will be dropped - play silence clip */
153 break;
154 }
155 }
156 else
157 {
158 /* Ran out so revert to default watermark */
159 if (pcmbuf_threshold == PCMOUT_LOW_WM)
160 pcm_underruns++;
161
162 pcmbuf_threshold = PCMOUT_PLAY_WM;
163 }
164
165 /* Keep clock going at all times */
166 clock_time += sizeof (silence) / 4;
167 clock_tick += sizeof (silence) / 4;
168
169 *start = silence;
170 *size = sizeof (silence);
171
172 if (sz < 0)
173 pcmbuf_read = pcmbuf_written;
174}
175
176/** Public interface **/
177
178/* Return a buffer pointer if at least size bytes are available and if so,
179 * give the actual free space */
180void * pcm_output_get_buffer(ssize_t *size)
181{
182 ssize_t sz = *size;
183 ssize_t free = pcm_output_bytes_free() - PCM_HDR_SIZE;
184
185 if (sz >= 0 && free >= sz)
186 {
187 *size = free; /* return actual free space (- header) */
188 return pcmbuf_tail->data;
189 }
190
191 /* Leave *size alone so caller doesn't have to reinit */
192 return NULL;
193}
194
195/* Commit the buffer returned by pcm_ouput_get_buffer; timestamp is PCM
196 * clock time units, not video format time units */
197bool pcm_output_commit_data(ssize_t size, uint32_t timestamp)
198{
199 if (size <= 0 || (size & 3))
200 return false; /* invalid */
201
202 size += PCM_HDR_SIZE;
203
204 if (size > pcm_output_bytes_free())
205 return false; /* too big */
206
207 pcmbuf_tail->size = size;
208 pcmbuf_tail->time = timestamp;
209
210 pcm_advance_buffer(&pcmbuf_tail, size);
211 pcmbuf_written += size;
212
213 return true;
214}
215
216/* Returns 'true' if the buffer is completely empty */
217bool pcm_output_empty(void)
218{
219 return pcm_output_bytes_used() <= 0;
220}
221
222/* Flushes the buffer - clock keeps counting */
223void pcm_output_flush(void)
224{
225 rb->pcm_play_lock();
226
227 enum channel_status status = rb->mixer_channel_status(MPEG_PCM_CHANNEL);
228
229 /* Stop PCM to clear current buffer */
230 if (status != CHANNEL_STOPPED)
231 rb->mixer_channel_stop(MPEG_PCM_CHANNEL);
232
233 rb->pcm_play_unlock();
234
235 pcm_reset_buffer();
236
237 /* Restart if playing state was current */
238 if (status == CHANNEL_PLAYING)
239 rb->mixer_channel_play_data(MPEG_PCM_CHANNEL,
240 get_more, NULL, 0);
241}
242
243/* Seek the reference clock to the specified time - next audio data ready to
244 go to DMA should be on the buffer with the same time index or else the PCM
245 buffer should be empty */
246void pcm_output_set_clock(uint32_t time)
247{
248 rb->pcm_play_lock();
249
250 clock_start = time;
251 clock_tick = time;
252 clock_time = time;
253
254 rb->pcm_play_unlock();
255}
256
257/* Return the clock as synchronized by audio frame timestamps */
258uint32_t pcm_output_get_clock(void)
259{
260 uint32_t time, rem;
261
262 /* Reread if data race detected - rem will be 0 if driver hasn't yet
263 * updated to the new buffer size. Also be sure pcm state doesn't
264 * cause indefinite loop.
265 *
266 * FYI: NOT scrutinized for rd/wr reordering on different cores. */
267 do
268 {
269 time = clock_time;
270 rem = rb->mixer_channel_get_bytes_waiting(MPEG_PCM_CHANNEL) >> 2;
271 }
272 while (UNLIKELY(time != clock_time ||
273 (rem == 0 &&
274 rb->mixer_channel_status(MPEG_PCM_CHANNEL) == CHANNEL_PLAYING))
275 );
276
277 return time - rem;
278
279}
280
281/* Return the raw clock as counted from the last pcm_output_set_clock
282 * call */
283uint32_t pcm_output_get_ticks(uint32_t *start)
284{
285 uint32_t tick, rem;
286
287 /* Same procedure as pcm_output_get_clock */
288 do
289 {
290 tick = clock_tick;
291 rem = rb->mixer_channel_get_bytes_waiting(MPEG_PCM_CHANNEL) >> 2;
292 }
293 while (UNLIKELY(tick != clock_tick ||
294 (rem == 0 &&
295 rb->mixer_channel_status(MPEG_PCM_CHANNEL) == CHANNEL_PLAYING))
296 );
297
298 if (start)
299 *start = clock_start;
300
301 return tick - rem;
302}
303
304/* Pauses/Starts pcm playback - and the clock */
305void pcm_output_play_pause(bool play)
306{
307 rb->pcm_play_lock();
308
309 if (rb->mixer_channel_status(MPEG_PCM_CHANNEL) != CHANNEL_STOPPED)
310 {
311 rb->mixer_channel_play_pause(MPEG_PCM_CHANNEL, play);
312 rb->pcm_play_unlock();
313 }
314 else
315 {
316 rb->pcm_play_unlock();
317
318 if (play)
319 {
320 rb->mixer_channel_set_amplitude(MPEG_PCM_CHANNEL, MIX_AMP_UNITY);
321 rb->mixer_channel_play_data(MPEG_PCM_CHANNEL,
322 get_more, NULL, 0);
323 }
324 }
325}
326
327/* Stops all playback and resets the clock */
328void pcm_output_stop(void)
329{
330 rb->pcm_play_lock();
331
332 if (rb->mixer_channel_status(MPEG_PCM_CHANNEL) != CHANNEL_STOPPED)
333 rb->mixer_channel_stop(MPEG_PCM_CHANNEL);
334
335 rb->pcm_play_unlock();
336
337 pcm_output_flush();
338 pcm_output_set_clock(0);
339}
340
341/* Drains any data if the start threshold hasn't been reached */
342void pcm_output_drain(void)
343{
344 rb->pcm_play_lock();
345 pcmbuf_threshold = PCMOUT_LOW_WM;
346 rb->pcm_play_unlock();
347}
348
349bool pcm_output_init(void)
350{
351 pcm_buffer = mpeg_malloc(PCMOUT_ALLOC_SIZE, MPEG_ALLOC_PCMOUT);
352 if (pcm_buffer == NULL)
353 return false;
354
355 pcmbuf_end = SKIPBYTES(pcm_buffer, PCMOUT_BUFSIZE);
356
357 pcm_reset_buffer();
358
359#if INPUT_SRC_CAPS != 0
360 /* Select playback */
361 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
362 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
363#endif
364
365#if SILENCE_TEST_TONE
366 /* Make the silence clip a square wave */
367 const int16_t silence_amp = INT16_MAX / 16;
368 unsigned i;
369
370 for (i = 0; i < ARRAYLEN(silence); i += 2)
371 {
372 if (i < ARRAYLEN(silence)/2)
373 {
374 silence[i] = silence_amp;
375 silence[i+1] = silence_amp;
376 }
377 else
378 {
379 silence[i] = -silence_amp;
380 silence[i+1] = -silence_amp;
381 }
382 }
383#endif
384
385 old_sampr = rb->mixer_get_frequency();
386 rb->mixer_set_frequency(CLOCK_RATE);
387 rb->pcmbuf_fade(false, true);
388 return true;
389}
390
391void pcm_output_exit(void)
392{
393 rb->pcmbuf_fade(false, false);
394 if (old_sampr != 0)
395 rb->mixer_set_frequency(old_sampr);
396}
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * PCM output buffer declarations
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef PCM_OUTPUT_H
24#define PCM_OUTPUT_H
25
26#define PCM_HDR_SIZE (sizeof (struct pcm_frame_header))
27struct pcm_frame_header /* Header added to pcm data every time a decoded
28 audio frame is sent out */
29{
30 uint32_t size; /* size of this frame - including header */
31 uint32_t time; /* timestamp for this frame in audio ticks */
32 unsigned char data[]; /* open array of audio data */
33} ALIGNED_ATTR(4);
34
35bool pcm_output_init(void);
36void pcm_output_exit(void);
37void pcm_output_flush(void);
38void pcm_output_set_clock(uint32_t time);
39uint32_t pcm_output_get_clock(void);
40uint32_t pcm_output_get_ticks(uint32_t *start);
41void pcm_output_play_pause(bool play);
42void pcm_output_stop(void);
43void pcm_output_drain(void);
44void * pcm_output_get_buffer(ssize_t *size);
45bool pcm_output_commit_data(ssize_t size, uint32_t timestamp);
46bool pcm_output_empty(void);
47
48#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * AV stream manager implementation
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "plugin.h"
24#include "mpegplayer.h"
25#include "lib/grey.h"
26#include "mpeg_settings.h"
27
28#ifndef HAVE_LCD_COLOR
29GREY_INFO_STRUCT_IRAM
30#endif
31
32static struct event_queue stream_mgr_queue SHAREDBSS_ATTR;
33static struct queue_sender_list stream_mgr_queue_send SHAREDBSS_ATTR;
34static uint32_t stream_mgr_thread_stack[DEFAULT_STACK_SIZE*2/sizeof(uint32_t)];
35
36struct stream_mgr stream_mgr SHAREDBSS_ATTR;
37
38/* Forward decs */
39static int stream_on_close(void);
40
41struct str_broadcast_data
42{
43 long cmd; /* Command to send to stream */
44 intptr_t data; /* Data to send with command */
45};
46
47static inline void stream_mgr_lock(void)
48{
49 rb->mutex_lock(&stream_mgr.str_mtx);
50}
51
52static inline void stream_mgr_unlock(void)
53{
54 rb->mutex_unlock(&stream_mgr.str_mtx);
55}
56
57static inline void actl_lock(void)
58{
59 rb->mutex_lock(&stream_mgr.actl_mtx);
60}
61
62static inline void actl_unlock(void)
63{
64 rb->mutex_unlock(&stream_mgr.actl_mtx);
65}
66
67static inline void stream_mgr_post_msg(long id, intptr_t data)
68{
69 rb->queue_post(stream_mgr.q, id, data);
70}
71
72static inline intptr_t stream_mgr_send_msg(long id, intptr_t data)
73{
74 return rb->queue_send(stream_mgr.q, id, data);
75}
76
77static inline void stream_mgr_reply_msg(intptr_t retval)
78{
79 rb->queue_reply(stream_mgr.q, retval);
80}
81
82int str_next_data_not_ready(struct stream *str)
83{
84 /* Save the current window since it actually might be ready by the time
85 * the registration is received by buffering. */
86 off_t win_right = str->hdr.win_right;
87
88 if (str->hdr.win_right < disk_buf.filesize - MIN_BUFAHEAD &&
89 disk_buf.filesize > MIN_BUFAHEAD)
90 {
91 /* Set right edge to where probing left off + the minimum margin */
92 str->hdr.win_right += MIN_BUFAHEAD;
93 }
94 else
95 {
96 /* Request would be passed the end of the file */
97 str->hdr.win_right = disk_buf.filesize;
98 }
99
100 switch (disk_buf_send_msg(DISK_BUF_DATA_NOTIFY, (intptr_t)str))
101 {
102 case DISK_BUF_NOTIFY_OK:
103 /* Was ready - restore window and process */
104 str->hdr.win_right = win_right;
105 return STREAM_OK;
106
107 case DISK_BUF_NOTIFY_ERROR:
108 /* Error - quit parsing */
109 str_end_of_stream(str);
110 return STREAM_DATA_END;
111
112 default:
113 /* Not ready - go wait for notification from buffering. */
114 str->pkt_flags = 0;
115 return STREAM_DATA_NOT_READY;
116 }
117}
118
119void str_data_notify_received(struct stream *str)
120{
121 /* Normalize win_right back to the packet length */
122 if (str->state == SSTATE_END)
123 return;
124
125 if (str->curr_packet == NULL)
126 {
127 /* Nothing was yet parsed since init */
128 str->hdr.win_right = str->hdr.win_left;
129 }
130 else
131 {
132 /* Restore window based upon current packet */
133 str->hdr.win_right = str->hdr.win_left +
134 (str->curr_packet_end - str->curr_packet);
135 }
136}
137
138/* Set stream manager to a "no-file" state */
139static void stream_mgr_init_state(void)
140{
141 stream_mgr.filename = NULL;
142 stream_mgr.resume_time = INVALID_TIMESTAMP;
143 stream_mgr.seeked = false;
144}
145
146/* Add a stream to the playback pool */
147void stream_add_stream(struct stream *str)
148{
149 actl_lock();
150
151 list_remove_item(stream_mgr.strl, str);
152 list_add_item(stream_mgr.strl, str);
153
154 actl_unlock();
155}
156
157/* Callback for various list-moving operations */
158static bool strl_enum_callback(struct stream *str, void *data)
159{
160 actl_lock();
161
162 list_remove_item(stream_mgr.strl, str);
163
164 if (*(int*)data == 1)
165 list_add_item(stream_mgr.actl, str);
166
167 actl_unlock();
168
169 return true;
170}
171
172/* Clear all streams from active and playback pools */
173void stream_remove_streams(void)
174{
175 int add_item = 0;
176 list_enum_items(stream_mgr.strl,
177 (list_enum_callback_t)strl_enum_callback, (void *)&add_item);
178}
179
180/* Move the playback pool to the active list */
181void move_strl_to_actl(void)
182{
183 int add_item = 1;
184 list_enum_items(stream_mgr.strl,
185 (list_enum_callback_t)strl_enum_callback, (void *)&add_item);
186}
187
188/* Remove a stream from the active list and return it to the pool */
189static bool actl_stream_remove(struct stream *str)
190{
191 bool retval;
192
193 actl_lock();
194
195 retval = list_remove_item(stream_mgr.actl, str);
196
197 if (retval)
198 list_add_item(stream_mgr.strl, str);
199
200 actl_unlock();
201
202 return retval;
203}
204
205/* Broadcast a message to all active streams */
206static bool actl_stream_broadcast_callback(struct stream *str,
207 struct str_broadcast_data *sbd)
208{
209 switch (sbd->cmd)
210 {
211 case STREAM_PLAY:
212 case STREAM_PAUSE:
213 break;
214
215 case STREAM_STOP:
216 if (sbd->data != 0)
217 {
218 actl_lock();
219
220 list_remove_item(stream_mgr.actl, str);
221 list_add_item(stream_mgr.strl, str);
222
223 actl_unlock();
224 sbd->data = 0;
225 }
226 break;
227
228 default:
229 return false;
230 }
231
232 str_send_msg(str, sbd->cmd, sbd->data);
233 return true;
234}
235
236static void actl_stream_broadcast(int cmd, intptr_t data)
237{
238 struct str_broadcast_data sbd;
239 sbd.cmd = cmd;
240 sbd.data = data;
241 list_enum_items(stream_mgr.actl,
242 (list_enum_callback_t)actl_stream_broadcast_callback,
243 (void*)&sbd);
244}
245
246/* Set the current base clock */
247static void set_stream_clock(uint32_t time)
248{
249 /* Fudge: Start clock 100ms early to allow for some filling time */
250 if (time > 100*TS_SECOND/1000)
251 time -= 100*TS_SECOND/1000;
252 else
253 time = 0;
254
255 pcm_output_set_clock(TS_TO_TICKS(time));
256}
257
258static void stream_start_playback(uint32_t time, bool fill_buffer)
259{
260 if (stream_mgr.seeked)
261 {
262 /* Clear any seeked status */
263 stream_mgr.seeked = false;
264
265 /* Flush old PCM data */
266 pcm_output_flush();
267
268 /* Set the master clock */
269 set_stream_clock(time);
270
271 /* Make sure streams are back in active pool */
272 move_strl_to_actl();
273
274 /* Prepare the parser and associated streams */
275 parser_prepare_streaming();
276 }
277
278 /* Start buffer which optional force fill */
279 disk_buf_send_msg(STREAM_PLAY, fill_buffer);
280
281 /* Tell each stream to start - may generate end of stream signals
282 * now - we'll handle this when finished */
283 actl_stream_broadcast(STREAM_PLAY, 0);
284
285 /* Actually start the clock */
286 pcm_output_play_pause(true);
287}
288
289/* Return the play time relative to the specified play time */
290static uint32_t time_from_whence(uint32_t time, int whence)
291{
292 int64_t currtime;
293 uint32_t start;
294
295 switch (whence)
296 {
297 case SEEK_SET:
298 /* Set the current time (time = unsigned offset from 0) */
299 if (time > str_parser.duration)
300 time = str_parser.duration;
301 break;
302 case SEEK_CUR:
303 /* Seek forward or backward from the current time
304 * (time = signed offset from current) */
305 currtime = stream_get_seek_time(&start);
306 currtime -= start;
307 currtime += (int32_t)time;
308
309 if (currtime < 0)
310 currtime = 0;
311 else if ((uint64_t)currtime > str_parser.duration)
312 currtime = str_parser.duration;
313
314 time = (uint32_t)currtime;
315 break;
316 case SEEK_END:
317 /* Seek from the end (time = unsigned offset from end) */
318 if (time > str_parser.duration)
319 time = str_parser.duration;
320 time = str_parser.duration - time;
321 break;
322 }
323
324 return time;
325}
326
327/* Handle seeking details if playing or paused */
328static uint32_t stream_seek_intl(uint32_t time, int whence,
329 int status, bool *was_buffering)
330{
331 if (status != STREAM_STOPPED)
332 {
333 bool wb;
334
335 /* Place streams in a non-running state - keep them on actl if
336 * still there */
337 actl_stream_broadcast(STREAM_PAUSE, 0);
338
339 /* Stop all buffering or else risk clobbering random-access data */
340 wb = disk_buf_send_msg(STREAM_STOP, 0);
341
342 if (was_buffering != NULL)
343 *was_buffering = wb;
344 }
345
346 time = time_from_whence(time, whence);
347
348 stream_mgr.seeked = true;
349
350 return parser_seek_time(time);
351}
352
353/* Store the resume time at the last seek/current clock point */
354static void stream_remember_resume_time(void)
355{
356 /* Assume invalidity */
357 stream_mgr.resume_time = 0;
358
359 if (stream_can_seek())
360 {
361 /* Read the current stream time or the last seeked position */
362 uint32_t start;
363 uint32_t time = stream_get_seek_time(&start);
364
365 if (time >= str_parser.start_pts && time <= str_parser.end_pts)
366 {
367 /* Save the current stream time */
368 stream_mgr.resume_time = time - start;
369 }
370 }
371}
372
373/* Handle STREAM_OPEN */
374void stream_on_open(const char *filename)
375{
376 int err = STREAM_ERROR;
377
378 stream_mgr_lock();
379
380 trigger_cpu_boost();
381
382 /* Open the video file */
383 if (disk_buf_open(filename) >= 0)
384 {
385 /* Initialize the parser */
386 err = parser_init_stream();
387
388 if (err >= STREAM_OK)
389 {
390 /* File ok - save the opened filename */
391 stream_mgr.filename = filename;
392 }
393 }
394
395 /* If error - cleanup */
396 if (err < STREAM_OK)
397 stream_on_close();
398
399 cancel_cpu_boost();
400
401 stream_mgr_unlock();
402
403 stream_mgr_reply_msg(err);
404}
405
406/* Handler STREAM_PLAY */
407static void stream_on_play(void)
408{
409 int status = stream_mgr.status;
410
411 stream_mgr_lock();
412
413 if (status == STREAM_STOPPED)
414 {
415 uint32_t start;
416
417 /* We just say we're playing now */
418 stream_mgr.status = STREAM_PLAYING;
419
420 /* Reply with previous state */
421 stream_mgr_reply_msg(status);
422
423 trigger_cpu_boost();
424
425 /* Seek to initial position and set clock to that time */
426
427 /* Save the resume time */
428 stream_remember_resume_time();
429
430 /* Prepare seek to start point */
431 start = stream_seek_intl(stream_mgr.resume_time, SEEK_SET,
432 STREAM_STOPPED, NULL);
433
434 /* Sync and start - force buffer fill */
435 stream_start_playback(start, true);
436 }
437 else
438 {
439 /* Reply with previous state */
440 stream_mgr_reply_msg(status);
441 }
442
443 stream_mgr_unlock();
444}
445
446/* Handle STREAM_PAUSE */
447static void stream_on_pause(void)
448{
449 int status = stream_mgr.status;
450
451 stream_mgr_lock();
452
453 /* Reply with previous state */
454 stream_mgr_reply_msg(status);
455
456 if (status == STREAM_PLAYING)
457 {
458 /* Pause the clock */
459 pcm_output_play_pause(false);
460
461 /* Pause each active stream */
462 actl_stream_broadcast(STREAM_PAUSE, 0);
463
464 /* Pause the disk buffer - buffer may continue filling */
465 disk_buf_send_msg(STREAM_PAUSE, false);
466
467 /* Unboost the CPU */
468 cancel_cpu_boost();
469
470 /* Offically paused */
471 stream_mgr.status = STREAM_PAUSED;
472 }
473
474 stream_mgr_unlock();
475}
476
477/* Handle STREAM_RESUME */
478static void stream_on_resume(void)
479{
480 int status = stream_mgr.status;
481
482 stream_mgr_lock();
483
484 /* Reply with previous state */
485 stream_mgr_reply_msg(status);
486
487 if (status == STREAM_PAUSED)
488 {
489 /* Boost the CPU */
490 trigger_cpu_boost();
491
492 /* Sync and start - no force buffering */
493 stream_start_playback(str_parser.last_seek_time, false);
494
495 /* Officially playing */
496 stream_mgr.status = STREAM_PLAYING;
497 }
498
499 stream_mgr_unlock();
500}
501
502/* Handle STREAM_STOP */
503static void stream_on_stop(bool reply)
504{
505 int status = stream_mgr.status;
506
507 stream_mgr_lock();
508
509 if (reply)
510 stream_mgr_reply_msg(status);
511
512 if (status != STREAM_STOPPED)
513 {
514 /* Pause the clock */
515 pcm_output_play_pause(false);
516
517 /* Update the resume time info */
518 stream_remember_resume_time();
519
520 /* Not stopped = paused or playing */
521 stream_mgr.seeked = false;
522
523 /* Stop buffering */
524 disk_buf_send_msg(STREAM_STOP, 0);
525
526 /* Clear any still-active streams and remove from actl */
527 actl_stream_broadcast(STREAM_STOP, 1);
528
529 /* Stop PCM output (and clock) */
530 pcm_output_stop();
531
532 /* Cancel our processor boost */
533 cancel_cpu_boost();
534
535 stream_mgr.status = STREAM_STOPPED;
536 }
537
538 stream_mgr_unlock();
539}
540
541/* Handle STREAM_SEEK */
542static void stream_on_seek(struct stream_seek_data *skd)
543{
544 uint32_t time = skd->time;
545 int whence = skd->whence;
546
547 switch (whence)
548 {
549 case SEEK_SET:
550 case SEEK_CUR:
551 case SEEK_END:
552 if (stream_mgr.filename == NULL)
553 break;
554
555 /* Keep things spinning if already doing so */
556 stream_keep_disk_active();
557
558 /* Have data - reply in order to acquire lock */
559 stream_mgr_reply_msg(STREAM_OK);
560
561 stream_mgr_lock();
562
563 /* Either seeking must be possible or a full rewind must be done */
564 if (stream_can_seek() || time_from_whence(time, whence) == 0)
565 {
566 bool buffer = false;
567
568 if (stream_mgr.status == STREAM_PLAYING)
569 {
570 /* Keep clock from advancing while seeking */
571 pcm_output_play_pause(false);
572 }
573
574 time = stream_seek_intl(time, whence, stream_mgr.status, &buffer);
575 stream_remember_resume_time();
576
577 if (stream_mgr.status == STREAM_PLAYING)
578 {
579 /* Sync and restart - no force buffering */
580 stream_start_playback(time, buffer);
581 }
582 }
583
584 stream_mgr_unlock();
585 return;
586 }
587
588 /* Invalid parameter or no file */
589 stream_mgr_reply_msg(STREAM_ERROR);
590}
591
592/* Handle STREAM_CLOSE */
593static int stream_on_close(void)
594{
595 int status = STREAM_STOPPED;
596
597 stream_mgr_lock();
598
599 /* Any open file that was accepted for playback? */
600 if (stream_mgr.filename != NULL)
601 {
602 /* Yes - hide video */
603 stream_show_vo(false);
604 /* Stop any playback */
605 status = stream_mgr.status;
606 stream_on_stop(false);
607 /* Tell parser file is finished */
608 parser_close_stream();
609 /* Reinitialize manager */
610 stream_mgr_init_state();
611 }
612
613 /* Let disk buffer reset itself - file might be open even if no good */
614 disk_buf_close();
615
616 stream_mgr_unlock();
617
618 return status;
619}
620
621/* Handle STREAM_EV_COMPLETE */
622static void stream_on_ev_complete(struct stream *str)
623{
624 stream_mgr_lock();
625
626 /* Stream is active? */
627 if (actl_stream_remove(str))
628 {
629 /* No - remove this stream from the active list */
630 DEBUGF(" finished: 0x%02x\n", str->id);
631 if (list_is_empty(stream_mgr.actl))
632 {
633 /* All streams have acked - stop playback */
634 stream_on_stop(false);
635 stream_mgr.resume_time = 0; /* Played to end - no resume */
636 }
637 else
638 {
639 /* Stream is done - stop it and place back in pool */
640 str_send_msg(str, STREAM_STOP, 1);
641 }
642 }
643
644 stream_mgr_unlock();
645}
646
647/* Callback for stream to notify about events internal to them */
648void stream_generate_event(struct stream *str, long id, intptr_t data)
649{
650 if (str == NULL)
651 return;
652
653 switch (id)
654 {
655 case STREAM_EV_COMPLETE:
656 /* The last stream has ended */
657 stream_mgr_post_msg(STREAM_EV_COMPLETE, (intptr_t)str);
658 break;
659 }
660
661 (void)data;
662}
663
664/* Clear any particular notification for which a stream registered */
665void stream_clear_notify(struct stream *str, int for_msg)
666{
667 switch (for_msg)
668 {
669 case DISK_BUF_DATA_NOTIFY:
670 disk_buf_send_msg(DISK_BUF_CLEAR_DATA_NOTIFY, (intptr_t)str);
671 break;
672 }
673}
674
675/* Special handling for certain messages since they involve multiple
676 * operations behind the scenes */
677static intptr_t send_video_msg(long id, intptr_t data)
678{
679 intptr_t retval = 0;
680
681 if (video_str.thread != 0 && disk_buf.in_file >= 0)
682 {
683
684 switch (id)
685 {
686 case VIDEO_DISPLAY_SHOW:
687 if (data != 0 && disk_buf_status() == STREAM_STOPPED)
688 { /* Only prepare image if showing and not playing */
689 parser_prepare_image(str_parser.last_seek_time);
690 }
691 break;
692
693 case VIDEO_PRINT_FRAME:
694 if (data)
695 break;
696 case VIDEO_PRINT_THUMBNAIL:
697 if (disk_buf_status() != STREAM_STOPPED)
698 break; /* Prepare image if not playing */
699
700 /* Ignore return and try video thread anyway */
701 parser_prepare_image(str_parser.last_seek_time);
702
703 /* Image ready - pass message to video thread */
704 break;
705 }
706
707 retval = str_send_msg(&video_str, id, data);
708 }
709
710 return retval;
711}
712
713/* Show/hide the video output */
714bool stream_show_vo(bool show)
715{
716 bool vis;
717 stream_mgr_lock();
718
719 vis = send_video_msg(VIDEO_DISPLAY_SHOW, show);
720#ifndef HAVE_LCD_COLOR
721 grey_show(show);
722#endif
723 stream_mgr_unlock();
724
725 return vis;
726}
727
728/* Query the visibility of video output */
729bool stream_vo_is_visible(void)
730{
731 bool vis;
732 stream_mgr_lock();
733 vis = send_video_msg(VIDEO_DISPLAY_IS_VISIBLE, 0);
734 stream_mgr_unlock();
735 return vis;
736}
737
738/* Return the video dimensions */
739bool stream_vo_get_size(struct vo_ext *sz)
740{
741 bool retval = false;
742
743 stream_mgr_lock();
744
745 if (str_parser.dims.w > 0 && str_parser.dims.h > 0)
746 {
747 *sz = str_parser.dims;
748 retval = true;
749 }
750
751 stream_mgr_unlock();
752
753 return retval;
754}
755
756void stream_vo_set_clip(const struct vo_rect *rc)
757{
758 stream_mgr_lock();
759
760 if (rc)
761 {
762 stream_mgr.parms.rc = *rc;
763 rc = &stream_mgr.parms.rc;
764 }
765
766 send_video_msg(VIDEO_SET_CLIP_RECT, (intptr_t)rc);
767
768 stream_mgr_unlock();
769}
770
771bool stream_vo_get_clip(struct vo_rect *rc)
772{
773 bool retval;
774
775 if (!rc)
776 return false;
777
778 stream_mgr_lock();
779
780 retval = send_video_msg(VIDEO_GET_CLIP_RECT,
781 (intptr_t)&stream_mgr.parms.rc);
782
783 *rc = stream_mgr.parms.rc;
784
785 stream_mgr_unlock();
786
787 return retval;
788}
789
790#ifndef HAVE_LCD_COLOR
791/* Show/hide the gray video overlay (independently of vo visibility). */
792void stream_gray_show(bool show)
793{
794 stream_mgr_lock();
795
796 grey_show(show);
797
798 stream_mgr_unlock();
799}
800
801#endif /* !HAVE_LCD_COLOR */
802
803/* Display a thumbnail at the last seek point */
804bool stream_display_thumb(const struct vo_rect *rc)
805{
806 bool retval;
807
808 if (rc == NULL)
809 return false;
810
811 stream_mgr_lock();
812
813 stream_mgr.parms.rc = *rc;
814 retval = send_video_msg(VIDEO_PRINT_THUMBNAIL,
815 (intptr_t)&stream_mgr.parms.rc);
816
817 stream_mgr_unlock();
818
819 return retval;
820}
821
822bool stream_draw_frame(bool no_prepare)
823{
824 bool retval;
825 stream_mgr_lock();
826
827 retval = send_video_msg(VIDEO_PRINT_FRAME, no_prepare);
828
829 stream_mgr_unlock();
830
831 return retval;
832}
833
834bool stream_set_callback(long id, void *fn)
835{
836 bool retval = false;
837
838 stream_mgr_lock();
839
840 switch (id)
841 {
842 case VIDEO_SET_POST_FRAME_CALLBACK:
843 retval = send_video_msg(id, (intptr_t)fn);
844 }
845
846 stream_mgr_unlock();
847
848 return retval;
849}
850
851/* Return the time playback should resume if interrupted */
852uint32_t stream_get_resume_time(void)
853{
854 uint32_t resume_time;
855
856 /* A stop request is async and replies before setting this - must lock */
857 stream_mgr_lock();
858
859 resume_time = stream_mgr.resume_time;
860
861 stream_mgr_unlock();
862
863 return resume_time;
864}
865
866uint32_t stream_get_seek_time(uint32_t *start)
867{
868 uint32_t time;
869
870 stream_mgr_lock();
871
872 if (stream_mgr.seeked)
873 {
874 time = str_parser.last_seek_time;
875 }
876 else
877 {
878 time = TICKS_TO_TS(pcm_output_get_clock());
879
880 /* Clock can be start early so keep in range */
881 if (time < str_parser.start_pts)
882 time = str_parser.start_pts;
883 }
884
885 if (start != NULL)
886 *start = str_parser.start_pts;
887
888 stream_mgr_unlock();
889
890 return time;
891}
892
893/* Wait for a state transistion to complete */
894void stream_wait_status(void)
895{
896 stream_mgr_lock();
897 stream_mgr_unlock();
898}
899
900/* Returns the smallest file window that includes all active streams'
901 * windows */
902static bool stream_get_window_callback(struct stream *str,
903 struct stream_window *sw)
904{
905 off_t swl = str->hdr.win_left;
906 off_t swr = str->hdr.win_right;
907
908 if (swl < sw->left)
909 sw->left = swl;
910
911 if (swr > sw->right)
912 sw->right = swr;
913
914 return true;
915}
916
917bool stream_get_window(struct stream_window *sw)
918{
919 if (sw == NULL)
920 return false;
921
922 sw->left = LONG_MAX;
923 sw->right = LONG_MIN;
924
925 actl_lock();
926 list_enum_items(stream_mgr.actl,
927 (list_enum_callback_t)stream_get_window_callback,
928 (void*)sw);
929 actl_unlock();
930
931 return sw->left <= sw->right;
932}
933
934/* Playback control thread */
935static void stream_mgr_thread(void)
936{
937 struct queue_event ev;
938
939 while (1)
940 {
941 rb->queue_wait(stream_mgr.q, &ev);
942
943 switch (ev.id)
944 {
945 case STREAM_OPEN:
946 stream_on_open((const char *)ev.data);
947 break;
948
949 case STREAM_CLOSE:
950 stream_on_close();
951 break;
952
953 case STREAM_PLAY:
954 stream_on_play();
955 break;
956
957 case STREAM_PAUSE:
958 if (ev.data)
959 stream_on_resume();
960 else
961 stream_on_pause();
962 break;
963
964 case STREAM_STOP:
965 stream_on_stop(true);
966 break;
967
968 case STREAM_SEEK:
969 stream_on_seek((struct stream_seek_data *)ev.data);
970 break;
971
972 case STREAM_EV_COMPLETE:
973 stream_on_ev_complete((struct stream *)ev.data);
974 break;
975
976 case STREAM_QUIT:
977 if (stream_mgr.status != STREAM_STOPPED)
978 stream_on_stop(false);
979 return;
980 }
981 }
982}
983
984/* Stream command interface APIs */
985
986/* Opens a new file */
987int stream_open(const char *filename)
988{
989 if (stream_mgr.thread != 0)
990 return stream_mgr_send_msg(STREAM_OPEN, (intptr_t)filename);
991 return STREAM_ERROR;
992}
993
994/* Plays the current file starting at time 'start' */
995int stream_play(void)
996{
997 if (stream_mgr.thread != 0)
998 return stream_mgr_send_msg(STREAM_PLAY, 0);
999 return STREAM_ERROR;
1000}
1001
1002/* Pauses playback if playing */
1003int stream_pause(void)
1004{
1005 if (stream_mgr.thread != 0)
1006 return stream_mgr_send_msg(STREAM_PAUSE, false);
1007 return STREAM_ERROR;
1008}
1009
1010/* Resumes playback if paused */
1011int stream_resume(void)
1012{
1013 if (stream_mgr.thread != 0)
1014 return stream_mgr_send_msg(STREAM_PAUSE, true);
1015 return STREAM_ERROR;
1016}
1017
1018/* Stops playback if not stopped */
1019int stream_stop(void)
1020{
1021 if (stream_mgr.thread != 0)
1022 return stream_mgr_send_msg(STREAM_STOP, 0);
1023 return STREAM_ERROR;
1024}
1025
1026/* Seeks playback time to/by the specified time */
1027int stream_seek(uint32_t time, int whence)
1028{
1029 int ret;
1030
1031 if (stream_mgr.thread == 0)
1032 return STREAM_ERROR;
1033
1034 stream_mgr_lock();
1035
1036 stream_mgr.parms.skd.time = time;
1037 stream_mgr.parms.skd.whence = whence;
1038
1039 ret = stream_mgr_send_msg(STREAM_SEEK, (intptr_t)&stream_mgr.parms.skd);
1040
1041 stream_mgr_unlock();
1042
1043 return ret;
1044}
1045
1046/* Closes the current file */
1047int stream_close(void)
1048{
1049 if (stream_mgr.thread != 0)
1050 return stream_mgr_send_msg(STREAM_CLOSE, 0);
1051 return STREAM_ERROR;
1052}
1053
1054/* Initializes the playback engine */
1055int stream_init(void)
1056{
1057 void *mem;
1058 size_t memsize;
1059
1060 stream_mgr.status = STREAM_STOPPED;
1061 stream_mgr_init_state();
1062
1063 /* Initialize our window to the outside world first */
1064 rb->mutex_init(&stream_mgr.str_mtx);
1065 rb->mutex_init(&stream_mgr.actl_mtx);
1066
1067 stream_mgr.q = &stream_mgr_queue;
1068 rb->queue_init(stream_mgr.q, false);
1069
1070 /* sets audiosize and returns buffer pointer */
1071 mem = rb->plugin_get_audio_buffer(&memsize);
1072
1073 /* Initialize non-allocator blocks first */
1074#ifndef HAVE_LCD_COLOR
1075 long greysize;
1076
1077 /* Greylib init handles all necessary cache alignment */
1078 if (!grey_init(mem, memsize, GREY_BUFFERED|GREY_ON_COP,
1079 LCD_WIDTH, LCD_HEIGHT, &greysize))
1080 {
1081 rb->splash(HZ, "greylib init failed!");
1082 return STREAM_ERROR;
1083 }
1084
1085 mem += greysize;
1086 memsize -= greysize;
1087
1088 grey_clear_display();
1089#endif /* !HAVE_LCD_COLOR */
1090
1091 stream_mgr.thread = rb->create_thread(stream_mgr_thread,
1092 stream_mgr_thread_stack, sizeof(stream_mgr_thread_stack),
1093 0, "mpgstream_mgr" IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU));
1094
1095 rb->queue_enable_queue_send(stream_mgr.q, &stream_mgr_queue_send,
1096 stream_mgr.thread);
1097
1098 if (stream_mgr.thread == 0)
1099 {
1100 rb->splash(HZ, "Could not create stream manager thread!");
1101 return STREAM_ERROR;
1102 }
1103
1104 /* Wait for thread to initialize */
1105 stream_mgr_send_msg(STREAM_NULL, 0);
1106
1107 /* Initialise our malloc buffer */
1108 if (!mpeg_alloc_init(mem, memsize))
1109 {
1110 rb->splash(HZ, "Out of memory in stream_init");
1111 }
1112 /* These inits use the allocator */
1113 else if (!pcm_output_init())
1114 {
1115 rb->splash(HZ, "Could not initialize PCM!");
1116 }
1117 else if (!audio_thread_init())
1118 {
1119 rb->splash(HZ, "Cannot create audio thread!");
1120 }
1121 else if (!video_thread_init())
1122 {
1123 rb->splash(HZ, "Cannot create video thread!");
1124 }
1125 /* Disk buffer takes max allotment of what's left so it must be last */
1126 else if (!disk_buf_init())
1127 {
1128 rb->splash(HZ, "Cannot create buffering thread!");
1129 }
1130 else if (!parser_init())
1131 {
1132 rb->splash(HZ, "Parser init failed!");
1133 }
1134 else
1135 {
1136 return STREAM_OK;
1137 }
1138
1139 return STREAM_ERROR;
1140}
1141
1142/* Cleans everything up */
1143void stream_exit(void)
1144{
1145 stream_close();
1146
1147 /* Stop the threads and wait for them to terminate */
1148 video_thread_exit();
1149 audio_thread_exit();
1150 disk_buf_exit();
1151 pcm_output_exit();
1152
1153 if (stream_mgr.thread != 0)
1154 {
1155 stream_mgr_post_msg(STREAM_QUIT, 0);
1156 rb->thread_wait(stream_mgr.thread);
1157 stream_mgr.thread = 0;
1158 }
1159
1160#ifndef HAVE_LCD_COLOR
1161 grey_release();
1162#endif
1163}
diff --git a/apps/plugins/mpegplayer/stream_mgr.h b/apps/plugins/mpegplayer/stream_mgr.h
new file mode 100644
index 0000000000..7dba9acc09
--- /dev/null
+++ b/apps/plugins/mpegplayer/stream_mgr.h
@@ -0,0 +1,168 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * AV stream manager decalarations
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef STREAM_MGR_H
24#define STREAM_MGR_H
25
26/* Basic media control interface - this handles state changes and stream
27 * coordination with assistance from the parser */
28struct stream_mgr
29{
30 unsigned int thread; /* Playback control thread */
31 struct event_queue *q; /* event queue for control thread */
32 const char *filename; /* Current filename */
33 uint32_t resume_time; /* The stream tick where playback was
34 stopped (or started) */
35 bool seeked; /* A seek happened and things must be
36 resynced */
37 int status; /* Current playback status */
38 void *strl[MPEGPLAYER_MAX_STREAMS+1]; /* List of available streams */
39 void *actl[MPEGPLAYER_MAX_STREAMS+1]; /* List of active streams */
40 struct mutex str_mtx; /* Main stream manager mutex */
41 struct mutex actl_mtx; /* Lock for current-streams list */
42 union /* A place for reusable non-cacheable parameters */
43 {
44 struct vo_rect rc;
45 struct stream_seek_data skd;
46 } parms;
47};
48
49extern struct stream_mgr stream_mgr SHAREDBSS_ATTR;
50
51struct stream_window
52{
53 off_t left, right;
54};
55
56/** Interface for use by streams and other internal objects **/
57bool stream_get_window(struct stream_window *sw);
58void stream_clear_notify(struct stream *str, int for_msg);
59int str_next_data_not_ready(struct stream *str);
60/* Called by a stream to say it got its buffering notification */
61void str_data_notify_received(struct stream *str);
62void stream_add_stream(struct stream *str);
63void stream_remove_streams(void);
64
65enum stream_events
66{
67 __STREAM_EV_FIRST = STREAM_MESSAGE_LAST-1,
68 STREAM_EV_COMPLETE,
69};
70
71void stream_generate_event(struct stream *str, long id, intptr_t data);
72
73/** Main control functions **/
74
75/* Initialize the playback engine */
76int stream_init(void);
77
78/* Close the playback engine */
79void stream_exit(void);
80
81/* Open a new file */
82int stream_open(const char *filename);
83
84/* Close the current file */
85int stream_close(void);
86
87/* Plays from the current seekpoint if stopped */
88int stream_play(void);
89
90/* Pauses playback if playing */
91int stream_pause(void);
92
93/* Resumes playback if paused */
94int stream_resume(void);
95
96/* Stops all streaming activity if playing or paused */
97int stream_stop(void);
98
99/* Point stream at a particular time.
100 * whence = one of SEEK_SET, SEEK_CUR, SEEK_END */
101int stream_seek(uint32_t time, int whence);
102
103/* Show/Hide the video image at the current seekpoint */
104bool stream_show_vo(bool show);
105
106/* Set the visible section of video */
107void stream_vo_set_clip(const struct vo_rect *rc);
108
109/* Return current visible section of video */
110bool stream_vo_get_clip(struct vo_rect *rc);
111
112#ifndef HAVE_LCD_COLOR
113void stream_gray_show(bool show);
114#endif
115
116/* Display thumbnail of the current seekpoint */
117bool stream_display_thumb(const struct vo_rect *rc);
118
119/* Draw the frame at the current position */
120bool stream_draw_frame(bool no_prepare);
121
122/* Return video dimensions */
123bool stream_vo_get_size(struct vo_ext *sz);
124
125/* Returns the resume time in timestamp ticks */
126uint32_t stream_get_resume_time(void);
127
128/* Returns stream_get_time if no seek is pending or else the
129 last time give to seek */
130uint32_t stream_get_seek_time(uint32_t *start);
131
132/* Return the absolute stream time in clock ticks - adjusted by
133 * master clock stream via audio timestamps */
134static inline uint32_t stream_get_time(void)
135 { return pcm_output_get_clock(); }
136
137/* Return the absolute clock time in clock ticks - unadjusted */
138static inline uint32_t stream_get_ticks(uint32_t *start)
139 { return pcm_output_get_ticks(start); }
140
141/* Returns the current playback status */
142static inline int stream_status(void)
143 { return stream_mgr.status; }
144
145/* Wait for a state transistion to complete */
146void stream_wait_status(void);
147
148/* Returns the playback length of the stream */
149static inline uint32_t stream_get_duration(void)
150 { return str_parser.duration; }
151
152static inline bool stream_can_seek(void)
153 { return parser_can_seek(); }
154
155static inline void stream_video_stats(struct video_output_stats *s)
156 { video_thread_get_stats(s); }
157
158bool stream_set_callback(long id, void * fn);
159
160/* Keep the disk spinning (for seeking and browsing) */
161static inline void stream_keep_disk_active(void)
162{
163#ifdef HAVE_DISK_STORAGE
164 rb->storage_spin();
165#endif
166 }
167
168#endif /* STREAM_MGR_H */
diff --git a/apps/plugins/mpegplayer/stream_thread.h b/apps/plugins/mpegplayer/stream_thread.h
new file mode 100644
index 0000000000..dfa6e8c9a1
--- /dev/null
+++ b/apps/plugins/mpegplayer/stream_thread.h
@@ -0,0 +1,201 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Declarations for stream-specific threading
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef STREAM_THREAD_H
24#define STREAM_THREAD_H
25
26#define PKT_HAS_TS 0x1
27
28/* Stream header which is the minimum to receive asynchronous buffering
29 * notifications.
30 * Layed-out to allow streaming access after random-access parsing */
31struct stream_hdr
32{
33 struct event_queue *q; /* Communication queue - separate to allow it
34 to be placed in another section */
35 off_t win_left; /* Left position within data stream */
36 union
37 {
38 off_t win_right; /* Right position within data stream */
39 off_t pos; /* Start/current position for random-access read */
40 };
41 off_t limit; /* Limit for random-access read */
42};
43
44struct stream
45{
46 struct stream_hdr hdr; /* Base stream data */
47 unsigned int thread; /* Stream's thread */
48 uint8_t* curr_packet; /* Current stream packet beginning */
49 uint8_t* curr_packet_end; /* Current stream packet end */
50 int state; /* State machine parsing mode */
51 uint32_t start_pts; /* First timestamp for stream */
52 uint32_t end_pts; /* Last timestamp for stream */
53 uint32_t pts; /* Last presentation timestamp */
54 uint32_t pkt_flags; /* PKT_* flags */
55 unsigned id; /* Stream identifier */
56};
57
58#define STR_FROM_HDR(sh) ((struct stream *)(sh))
59
60/* Make sure there there is always enough data buffered ahead for
61 * the worst possible case - regardless of whether a valid stream
62 * would actually produce that */
63#define MIN_BUFAHEAD (21+65535+6+65535+6) /* 131103 */
64
65/* States that a stream's thread assumes internally */
66enum thread_states
67{
68 /* Stream thread... */
69 TSTATE_INIT = 0, /* is initialized and primed */
70 TSTATE_DATA, /* is awaiting data to be available */
71 TSTATE_BUFFERING, /* is buffering data */
72 TSTATE_EOS, /* has hit the end of data */
73 TSTATE_DECODE, /* is in a decoding state */
74 TSTATE_RENDER, /* is in a rendering state */
75 TSTATE_RENDER_WAIT, /* is waiting to render */
76};
77
78/* Commands that streams respond to */
79enum stream_message
80{
81 STREAM_NULL = 0, /* A NULL message for whatever reason -
82 usually ignored */
83 STREAM_PLAY, /* Start playback at current position */
84 STREAM_PAUSE, /* Stop playing and await further commands */
85 STREAM_RESET, /* Reset the stream for a discontinuity */
86 STREAM_STOP, /* Stop stream - requires a reset later */
87 STREAM_SEEK, /* Seek the current stream to a new location */
88 STREAM_OPEN, /* Open a new file */
89 STREAM_CLOSE, /* Close the current file */
90 STREAM_QUIT, /* Exit the stream and thread */
91 STREAM_NEEDS_SYNC, /* Need to sync before stream decoding? */
92 STREAM_SYNC, /* Sync to the specified time from some key point */
93 STREAM_FIND_END_TIME, /* Get the exact end time of an elementary
94 * stream - ie. time just after last frame is finished */
95 /* Disk buffer */
96 STREAM_DISK_BUF_FIRST,
97 DISK_BUF_DATA_NOTIFY = STREAM_DISK_BUF_FIRST,
98 DISK_BUF_CLEAR_DATA_NOTIFY, /* Cancel pending data notification */
99 DISK_BUF_CACHE_RANGE, /* Cache a range of the file in the buffer */
100 /* Audio stream */
101 STREAM_AUDIO_FIRST,
102 /* Video stream */
103 STREAM_VIDEO_FIRST,
104 VIDEO_DISPLAY_SHOW = STREAM_VIDEO_FIRST, /* Show/hide video output */
105 VIDEO_DISPLAY_IS_VISIBLE, /* Is the video output visible? */
106 VIDEO_GET_SIZE, /* Get the video dimensions */
107 VIDEO_PRINT_FRAME, /* Print the frame at the current position */
108 VIDEO_PRINT_THUMBNAIL, /* Print a thumbnail of the current position */
109 VIDEO_SET_CLIP_RECT, /* Set the visible video area */
110 VIDEO_GET_CLIP_RECT, /* Return the visible video area */
111 VIDEO_SET_POST_FRAME_CALLBACK, /* Set a callback after frame is drawn */
112 STREAM_MESSAGE_LAST,
113};
114
115/* Data parameter for STREAM_SEEK */
116struct stream_seek_data
117{
118 uint32_t time; /* Time to seek to/by */
119 int whence; /* Specification of relationship to current position/file */
120};
121
122/* Data parameter for STREAM_SYNC */
123struct str_sync_data
124{
125 uint32_t time; /* Time to sync to */
126 struct stream_scan sk; /* Specification of start/limits/direction */
127};
128
129/* Stream status codes - not eqivalent to thread states */
130enum stream_status
131{
132 /* Stream status is... */
133 STREAM_DATA_END = -4, /* Stream has ended */
134 STREAM_DATA_NOT_READY = -3, /* Data was not available yet */
135 STREAM_UNSUPPORTED = -2, /* Format is unsupported */
136 STREAM_ERROR = -1, /* some kind of error - quit it or reset it */
137 STREAM_OK = 0, /* General inequality for success >= is OK, < error */
138 STREAM_STOPPED = 0, /* stopped and awaiting commands - send STREAM_INIT */
139 STREAM_PLAYING, /* playing and rendering its data */
140 STREAM_PAUSED, /* paused and awaiting commands */
141 /* Other status codes (> STREAM_OK) */
142 STREAM_MATCH, /* A good match was found */
143 STREAM_PERFECT_MATCH, /* Exactly what was wanted was found or
144 no better match is possible */
145 STREAM_NOT_FOUND, /* Match not found */
146};
147
148/* Clip time to range for a particular stream */
149static inline uint32_t clip_time(struct stream *str, uint32_t time)
150{
151 if (time < str->start_pts)
152 time = str->start_pts;
153 else if (time >= str->end_pts)
154 time = str->end_pts;
155
156 return time;
157}
158
159extern struct stream video_str IBSS_ATTR;
160extern struct stream audio_str IBSS_ATTR;
161
162bool video_thread_init(void);
163void video_thread_exit(void);
164
165struct video_output_stats
166{
167 int num_drawn; /* Number of frames drawn since reset */
168 int num_skipped; /* Number of frames skipped since reset */
169 int fps; /* fps rate in 100ths of a frame per second */
170};
171
172void video_thread_get_stats(struct video_output_stats *s);
173
174bool audio_thread_init(void);
175void audio_thread_exit(void);
176
177
178/* Some queue function wrappers to keep things clean-ish */
179
180/* For stream use only */
181static inline bool str_have_msg(struct stream *str)
182 { return !rb->queue_empty(str->hdr.q); }
183
184static inline void str_get_msg(struct stream *str, struct queue_event *ev)
185 { rb->queue_wait(str->hdr.q, ev); }
186
187static inline void str_get_msg_w_tmo(struct stream *str, struct queue_event *ev,
188 int timeout)
189 { rb->queue_wait_w_tmo(str->hdr.q, ev, timeout); }
190
191static inline void str_reply_msg(struct stream *str, intptr_t reply)
192 { rb->queue_reply(str->hdr.q, reply); }
193
194/* Public use */
195static inline intptr_t str_send_msg(struct stream *str, long id, intptr_t data)
196 { return rb->queue_send(str->hdr.q, id, data); }
197
198static inline void str_post_msg(struct stream *str, long id, intptr_t data)
199 { rb->queue_post(str->hdr.q, id, data); }
200
201#endif /* STREAM_THREAD_H */
diff --git a/apps/plugins/mpegplayer/video_out.h b/apps/plugins/mpegplayer/video_out.h
new file mode 100644
index 0000000000..2a3364c382
--- /dev/null
+++ b/apps/plugins/mpegplayer/video_out.h
@@ -0,0 +1,102 @@
1/*
2 * video_out.h
3 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
4 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
5 *
6 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
7 * See http://libmpeg2.sourceforge.net/ for updates.
8 *
9 * mpeg2dec is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * mpeg2dec is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id$
24 * libmpeg2 sync history:
25 * 2008-07-01 - CVS revision 1.22
26 */
27
28#ifndef VIDEO_OUT_H
29#define VIDEO_OUT_H
30
31#if LCD_WIDTH >= LCD_HEIGHT
32#define SCREEN_WIDTH LCD_WIDTH
33#define SCREEN_HEIGHT LCD_HEIGHT
34#define LCD_LANDSCAPE
35#else /* Assume the screen is rotated on portrait LCDs */
36#define SCREEN_WIDTH LCD_HEIGHT
37#define SCREEN_HEIGHT LCD_WIDTH
38#define LCD_PORTRAIT
39#endif
40
41/* Structure to hold width and height values */
42struct vo_ext
43{
44 int w, h;
45};
46
47/* Structure that defines a rectangle by its edges */
48struct vo_rect
49{
50 int l, t, r, b;
51};
52
53void vo_draw_frame (uint8_t * const * buf);
54bool vo_draw_frame_thumb (uint8_t * const * buf,
55 const struct vo_rect *rc);
56bool vo_init (void);
57bool vo_show (bool show);
58bool vo_is_visible(void);
59void vo_setup (const mpeg2_sequence_t * sequence);
60void vo_set_clip_rect(const struct vo_rect *rc);
61bool vo_get_clip_rect(struct vo_rect *rc);
62void vo_dimensions(struct vo_ext *sz);
63void vo_cleanup (void);
64void vo_set_post_draw_callback(void (*cb)(void));
65
66#if NUM_CORES > 1
67void vo_lock(void);
68void vo_unlock(void);
69#else
70static inline void vo_lock(void) {}
71static inline void vo_unlock(void) {}
72#endif
73
74/* Sets all coordinates of a vo_rect to 0 */
75void vo_rect_clear(struct vo_rect *rc);
76/* Returns true if left >= right or top >= bottom */
77bool vo_rect_empty(const struct vo_rect *rc);
78/* Initializes a vo_rect using upper-left corner and extents */
79void vo_rect_set_ext(struct vo_rect *rc, int x, int y,
80 int width, int height);
81/* Query if two rectangles intersect
82 * If either are empty returns false */
83bool vo_rects_intersect(const struct vo_rect *rc1,
84 const struct vo_rect *rc2);
85
86/* Intersect two rectangles
87 * Resulting rectangle is placed in rc_dst.
88 * rc_dst is set to empty if they don't intersect.
89 * Empty source rectangles do not intersect any rectangle.
90 * rc_dst may be the same structure as rc1 or rc2.
91 * Returns true if the resulting rectangle is not empty. */
92bool vo_rect_intersect(struct vo_rect *rc_dst,
93 const struct vo_rect *rc1,
94 const struct vo_rect *rc2);
95
96bool vo_rect_union(struct vo_rect *rc_dst,
97 const struct vo_rect *rc1,
98 const struct vo_rect *rc2);
99
100void vo_rect_offset(struct vo_rect *rc, int dx, int dy);
101
102#endif /* VIDEO_OUT_H */
diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c
new file mode 100644
index 0000000000..331383843b
--- /dev/null
+++ b/apps/plugins/mpegplayer/video_out_rockbox.c
@@ -0,0 +1,576 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * mpegplayer video output routines
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "libmpeg2/mpeg2dec_config.h"
22
23#include "plugin.h"
24#include "mpegplayer.h"
25
26#define VO_NON_NULL_RECT 0x1
27#define VO_VISIBLE 0x2
28
29struct vo_data
30{
31 int image_width;
32 int image_height;
33 int image_chroma_x;
34 int image_chroma_y;
35 int display_width;
36 int display_height;
37 int output_x;
38 int output_y;
39 int output_width;
40 int output_height;
41 unsigned flags;
42 struct vo_rect rc_vid;
43 struct vo_rect rc_clip;
44 void (*post_draw_callback)(void);
45};
46
47#if NUM_CORES > 1
48/* Cache aligned and padded to avoid clobbering other processors' cacheable
49 * data */
50static union {
51 uint8_t __vo_data[CACHEALIGN_UP(sizeof(struct vo_data))];
52 struct vo_data vo;
53} vo_raw CACHEALIGN_ATTR;
54#define vo vo_raw.vo
55#else
56static struct vo_data vo;
57#endif
58
59#if NUM_CORES > 1
60static struct mutex vo_mtx SHAREDBSS_ATTR;
61#endif
62
63static inline void video_lock_init(void)
64{
65#if NUM_CORES > 1
66 rb->mutex_init(&vo_mtx);
67#endif
68}
69
70static inline void video_lock(void)
71{
72#if NUM_CORES > 1
73 rb->mutex_lock(&vo_mtx);
74#endif
75}
76
77static inline void video_unlock(void)
78{
79#if NUM_CORES > 1
80 rb->mutex_unlock(&vo_mtx);
81#endif
82}
83
84
85/* Draw a black rectangle if no video frame is available */
86static void vo_draw_black(struct vo_rect *rc)
87{
88 int foreground;
89 int x, y, w, h;
90
91 video_lock();
92
93 foreground = mylcd_get_foreground();
94
95 mylcd_set_foreground(MYLCD_BLACK);
96
97 if (rc)
98 {
99 x = rc->l;
100 y = rc->t;
101 w = rc->r - rc->l;
102 h = rc->b - rc->t;
103 }
104 else
105 {
106#if LCD_WIDTH >= LCD_HEIGHT
107 x = vo.output_x;
108 y = vo.output_y;
109 w = vo.output_width;
110 h = vo.output_height;
111#else
112 x = LCD_WIDTH - vo.output_height - vo.output_y;
113 y = vo.output_x;
114 w = vo.output_height;
115 h = vo.output_width;
116#endif
117 }
118
119 mylcd_fillrect(x, y, w, h);
120 mylcd_update_rect(x, y, w, h);
121
122 mylcd_set_foreground(foreground);
123
124 video_unlock();
125}
126
127static inline void yuv_blit(uint8_t * const * buf, int src_x, int src_y,
128 int stride, int x, int y, int width, int height)
129{
130 video_lock();
131
132#ifdef HAVE_LCD_COLOR
133 rb->lcd_blit_yuv(buf, src_x, src_y, stride, x, y , width, height);
134#else
135 grey_ub_gray_bitmap_part(buf[0], src_x, src_y, stride, x, y, width, height);
136#endif
137
138 video_unlock();
139}
140
141void vo_draw_frame(uint8_t * const * buf)
142{
143 if ((vo.flags & (VO_NON_NULL_RECT | VO_VISIBLE)) !=
144 (VO_NON_NULL_RECT | VO_VISIBLE))
145 {
146 /* Frame is hidden - either by being set invisible or is clipped
147 * away - copout */
148 DEBUGF("vo hidden\n");
149 }
150 else if (buf == NULL)
151 {
152 /* No frame exists - draw black */
153 vo_draw_black(NULL);
154 DEBUGF("vo no frame\n");
155 }
156 else
157 {
158 yuv_blit(buf, 0, 0, vo.image_width,
159 vo.output_x, vo.output_y, vo.output_width,
160 vo.output_height);
161 }
162
163 if (vo.post_draw_callback)
164 vo.post_draw_callback();
165}
166
167static inline void vo_rect_clear_inl(struct vo_rect *rc)
168{
169 rc->l = rc->t = rc->r = rc->b = 0;
170}
171
172static inline bool vo_rect_empty_inl(const struct vo_rect *rc)
173{
174 return rc == NULL || rc->l >= rc->r || rc->t >= rc->b;
175}
176
177static inline bool vo_rects_intersect_inl(const struct vo_rect *rc1,
178 const struct vo_rect *rc2)
179{
180 return !vo_rect_empty_inl(rc1) &&
181 !vo_rect_empty_inl(rc2) &&
182 rc1->l < rc2->r && rc1->r > rc2->l &&
183 rc1->t < rc2->b && rc1->b > rc2->t;
184}
185
186/* Sets all coordinates of a vo_rect to 0 */
187void vo_rect_clear(struct vo_rect *rc)
188{
189 vo_rect_clear_inl(rc);
190}
191
192/* Returns true if left >= right or top >= bottom */
193bool vo_rect_empty(const struct vo_rect *rc)
194{
195 return vo_rect_empty_inl(rc);
196}
197
198/* Initializes a vo_rect using upper-left corner and extents */
199void vo_rect_set_ext(struct vo_rect *rc, int x, int y,
200 int width, int height)
201{
202 rc->l = x;
203 rc->t = y;
204 rc->r = x + width;
205 rc->b = y + height;
206}
207
208/* Query if two rectangles intersect */
209bool vo_rects_intersect(const struct vo_rect *rc1,
210 const struct vo_rect *rc2)
211{
212 return vo_rects_intersect_inl(rc1, rc2);
213}
214
215/* Intersect two rectangles, placing the result in rc_dst */
216bool vo_rect_intersect(struct vo_rect *rc_dst,
217 const struct vo_rect *rc1,
218 const struct vo_rect *rc2)
219{
220 if (rc_dst != NULL)
221 {
222 if (vo_rects_intersect_inl(rc1, rc2))
223 {
224 rc_dst->l = MAX(rc1->l, rc2->l);
225 rc_dst->r = MIN(rc1->r, rc2->r);
226 rc_dst->t = MAX(rc1->t, rc2->t);
227 rc_dst->b = MIN(rc1->b, rc2->b);
228 return true;
229 }
230
231 vo_rect_clear_inl(rc_dst);
232 }
233
234 return false;
235}
236
237bool vo_rect_union(struct vo_rect *rc_dst,
238 const struct vo_rect *rc1,
239 const struct vo_rect *rc2)
240{
241 if (rc_dst != NULL)
242 {
243 if (!vo_rect_empty_inl(rc1))
244 {
245 if (!vo_rect_empty_inl(rc2))
246 {
247 rc_dst->l = MIN(rc1->l, rc2->l);
248 rc_dst->t = MIN(rc1->t, rc2->t);
249 rc_dst->r = MAX(rc1->r, rc2->r);
250 rc_dst->b = MAX(rc1->b, rc2->b);
251 }
252 else
253 {
254 *rc_dst = *rc1;
255 }
256
257 return true;
258 }
259 else if (!vo_rect_empty_inl(rc2))
260 {
261 *rc_dst = *rc2;
262 return true;
263 }
264
265 vo_rect_clear_inl(rc_dst);
266 }
267
268 return false;
269}
270
271void vo_rect_offset(struct vo_rect *rc, int dx, int dy)
272{
273 rc->l += dx;
274 rc->t += dy;
275 rc->r += dx;
276 rc->b += dy;
277}
278
279/* Shink or stretch each axis - rotate counter-clockwise to retain upright
280 * orientation on rotated displays (they rotate clockwise) */
281void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride,
282 int src_w, int src_h, int dst_w, int dst_h)
283{
284 uint8_t *dst_end = dst + dst_w*dst_h;
285
286#if LCD_WIDTH >= LCD_HEIGHT
287 int src_w2 = src_w*2; /* 2x dimensions (for rounding before division) */
288 int dst_w2 = dst_w*2;
289 int src_h2 = src_h*2;
290 int dst_h2 = dst_h*2;
291 int qw = src_w2 / dst_w2; /* src-dst width ratio quotient */
292 int rw = src_w2 - qw*dst_w2; /* src-dst width ratio remainder */
293 int qh = src_h2 / dst_h2; /* src-dst height ratio quotient */
294 int rh = src_h2 - qh*dst_h2; /* src-dst height ratio remainder */
295 int dw = dst_w; /* Width error accumulator */
296 int dh = dst_h; /* Height error accumulator */
297#else
298 int src_w2 = src_w*2;
299 int dst_w2 = dst_h*2;
300 int src_h2 = src_h*2;
301 int dst_h2 = dst_w*2;
302 int qw = src_h2 / dst_w2;
303 int rw = src_h2 - qw*dst_w2;
304 int qh = src_w2 / dst_h2;
305 int rh = src_w2 - qh*dst_h2;
306 int dw = dst_h;
307 int dh = dst_w;
308
309 src += src_w - 1;
310#endif
311
312 while (1)
313 {
314 const uint8_t *s = src;
315#if LCD_WIDTH >= LCD_HEIGHT
316 uint8_t * const dst_line_end = dst + dst_w;
317#else
318 uint8_t * const dst_line_end = dst + dst_h;
319#endif
320 while (1)
321 {
322 *dst++ = *s;
323
324 if (dst >= dst_line_end)
325 {
326 dw = dst_w;
327 break;
328 }
329
330#if LCD_WIDTH >= LCD_HEIGHT
331 s += qw;
332#else
333 s += qw*stride;
334#endif
335 dw += rw;
336
337 if (dw >= dst_w2)
338 {
339 dw -= dst_w2;
340#if LCD_WIDTH >= LCD_HEIGHT
341 s++;
342#else
343 s += stride;
344#endif
345 }
346 }
347
348 if (dst >= dst_end)
349 break;
350#if LCD_WIDTH >= LCD_HEIGHT
351 src += qh*stride;
352#else
353 src -= qh;
354#endif
355 dh += rh;
356
357 if (dh >= dst_h2)
358 {
359 dh -= dst_h2;
360#if LCD_WIDTH >= LCD_HEIGHT
361 src += stride;
362#else
363 src--;
364#endif
365 }
366 }
367}
368
369bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc)
370{
371 void *mem;
372 size_t bufsize = 0;
373 uint8_t *yuv[3];
374 struct vo_rect thumb_rc;
375 int thumb_width, thumb_height;
376#ifdef HAVE_LCD_COLOR
377 int thumb_uv_width, thumb_uv_height;
378#endif
379
380 /* Obtain rectangle as clipped to the screen */
381 vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT);
382 if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc))
383 return true;
384
385 if (buf == NULL)
386 goto no_thumb_exit;
387
388 DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t,
389 thumb_rc.r, thumb_rc.b);
390
391 thumb_width = rc->r - rc->l;
392 thumb_height = rc->b - rc->t;
393#ifdef HAVE_LCD_COLOR
394 thumb_uv_width = thumb_width / 2;
395 thumb_uv_height = thumb_height / 2;
396
397 DEBUGF("thumb: w: %d h: %d uvw: %d uvh: %d\n", thumb_width,
398 thumb_height, thumb_uv_width, thumb_uv_height);
399#else
400 DEBUGF("thumb: w: %d h: %d\n", thumb_width, thumb_height);
401#endif
402
403 /* Use remaining mpeg2 buffer as temp space */
404 mem = mpeg2_get_buf(&bufsize);
405
406 if (bufsize < (size_t)(thumb_width*thumb_height)
407#ifdef HAVE_LCD_COLOR
408 + 2u*(thumb_uv_width * thumb_uv_height)
409#endif
410 )
411 {
412 DEBUGF("thumb: insufficient buffer\n");
413 goto no_thumb_exit;
414 }
415
416 yuv[0] = mem;
417 stretch_image_plane(buf[0], yuv[0], vo.image_width,
418 vo.display_width, vo.display_height,
419 thumb_width, thumb_height);
420
421#ifdef HAVE_LCD_COLOR
422 yuv[1] = yuv[0] + thumb_width*thumb_height;
423 yuv[2] = yuv[1] + thumb_uv_width*thumb_uv_height;
424
425 stretch_image_plane(buf[1], yuv[1], vo.image_width / 2,
426 vo.display_width / 2, vo.display_height / 2,
427 thumb_uv_width, thumb_uv_height);
428
429 stretch_image_plane(buf[2], yuv[2], vo.image_width / 2,
430 vo.display_width / 2, vo.display_height / 2,
431 thumb_uv_width, thumb_uv_height);
432#endif
433
434#if LCD_WIDTH >= LCD_HEIGHT
435 yuv_blit(yuv, 0, 0, thumb_width,
436 thumb_rc.l, thumb_rc.t,
437 thumb_rc.r - thumb_rc.l,
438 thumb_rc.b - thumb_rc.t);
439#else
440 yuv_blit(yuv, 0, 0, thumb_height,
441 thumb_rc.t, thumb_rc.l,
442 thumb_rc.b - thumb_rc.t,
443 thumb_rc.r - thumb_rc.l);
444#endif /* LCD_WIDTH >= LCD_HEIGHT */
445
446 return true;
447
448no_thumb_exit:
449 vo_draw_black(&thumb_rc);
450 return false;
451}
452
453void vo_setup(const mpeg2_sequence_t * sequence)
454{
455 vo.image_width = sequence->width;
456 vo.image_height = sequence->height;
457 vo.display_width = sequence->display_width;
458 vo.display_height = sequence->display_height;
459
460 DEBUGF("vo_setup - w:%d h:%d\n", vo.display_width, vo.display_height);
461
462 vo.image_chroma_x = vo.image_width / sequence->chroma_width;
463 vo.image_chroma_y = vo.image_height / sequence->chroma_height;
464
465 if (sequence->display_width >= SCREEN_WIDTH)
466 {
467 vo.rc_vid.l = 0;
468 vo.rc_vid.r = SCREEN_WIDTH;
469 }
470 else
471 {
472 vo.rc_vid.l = (SCREEN_WIDTH - sequence->display_width) / 2;
473#ifdef HAVE_LCD_COLOR
474 vo.rc_vid.l &= ~1;
475#endif
476 vo.rc_vid.r = vo.rc_vid.l + sequence->display_width;
477 }
478
479 if (sequence->display_height >= SCREEN_HEIGHT)
480 {
481 vo.rc_vid.t = 0;
482 vo.rc_vid.b = SCREEN_HEIGHT;
483 }
484 else
485 {
486 vo.rc_vid.t = (SCREEN_HEIGHT - sequence->display_height) / 2;
487#ifdef HAVE_LCD_COLOR
488 vo.rc_vid.t &= ~1;
489#endif
490 vo.rc_vid.b = vo.rc_vid.t + sequence->display_height;
491 }
492
493 vo_set_clip_rect(&vo.rc_clip);
494}
495
496void vo_dimensions(struct vo_ext *sz)
497{
498 sz->w = vo.display_width;
499 sz->h = vo.display_height;
500}
501
502bool vo_init(void)
503{
504 vo.flags = 0;
505 vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
506 video_lock_init();
507 return true;
508}
509
510bool vo_show(bool show)
511{
512 bool vis = vo.flags & VO_VISIBLE;
513
514 if (show)
515 vo.flags |= VO_VISIBLE;
516 else
517 vo.flags &= ~VO_VISIBLE;
518
519 return vis;
520}
521
522bool vo_is_visible(void)
523{
524 return vo.flags & VO_VISIBLE;
525}
526
527void vo_cleanup(void)
528{
529 vo.flags = 0;
530}
531
532void vo_set_clip_rect(const struct vo_rect *rc)
533{
534 struct vo_rect rc_out;
535
536 if (rc == NULL)
537 vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
538 else
539 vo.rc_clip = *rc;
540
541 if (!vo_rect_intersect(&rc_out, &vo.rc_vid, &vo.rc_clip))
542 vo.flags &= ~VO_NON_NULL_RECT;
543 else
544 vo.flags |= VO_NON_NULL_RECT;
545
546 vo.output_x = rc_out.l;
547 vo.output_y = rc_out.t;
548 vo.output_width = rc_out.r - rc_out.l;
549 vo.output_height = rc_out.b - rc_out.t;
550}
551
552bool vo_get_clip_rect(struct vo_rect *rc)
553{
554 rc->l = vo.output_x;
555 rc->t = vo.output_y;
556 rc->r = rc->l + vo.output_width;
557 rc->b = rc->t + vo.output_height;
558 return (vo.flags & VO_NON_NULL_RECT) != 0;
559}
560
561void vo_set_post_draw_callback(void (*cb)(void))
562{
563 vo.post_draw_callback = cb;
564}
565
566#if NUM_CORES > 1
567void vo_lock(void)
568{
569 video_lock();
570}
571
572void vo_unlock(void)
573{
574 video_unlock();
575}
576#endif
diff --git a/apps/plugins/mpegplayer/video_thread.c b/apps/plugins/mpegplayer/video_thread.c
new file mode 100644
index 0000000000..392cc6179f
--- /dev/null
+++ b/apps/plugins/mpegplayer/video_thread.c
@@ -0,0 +1,1059 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * mpegplayer video thread implementation
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "plugin.h"
24#include "mpegplayer.h"
25#include "libmpeg2/mpeg2dec_config.h"
26#include "lib/grey.h"
27#include "video_out.h"
28#include "mpeg_settings.h"
29
30/** Video stream and thread **/
31
32/* Video thread data passed around to its various functions */
33struct video_thread_data
34{
35 /* Stream data */
36 mpeg2dec_t *mpeg2dec; /* Our video decoder */
37 const mpeg2_info_t *info; /* Info about video stream */
38 int state; /* Thread state */
39 int status; /* Media status */
40 struct queue_event ev;/* Our event queue to receive commands */
41 /* Operational info */
42 uint32_t stream_time; /* Current time from beginning of stream */
43 uint32_t goal_time; /* Scheduled time of current frame */
44 int32_t remain_time; /* T-minus value to frame_time (-:early, +:late) */
45 int skip_ref_pics; /* Severe skipping - wait for I-frame */
46 int skip_level; /* Number of frames still to skip */
47 int num_picture; /* Number of picture headers read */
48 int num_intra; /* Number of I-picture headers read */
49 int group_est; /* Estmated number remaining as of last I */
50 long last_render; /* Last time a frame was drawn */
51 /* Sync info */
52 uint32_t frame_time; /* Current due time of frame (unadjusted) */
53 uint32_t frame_period; /* Frame period in clock ticks */
54 int num_ref_pics; /* Number of I and P frames since sync/skip */
55 int syncf_perfect; /* Last sync fit result */
56};
57
58/* Number drawn since reset */
59static int video_num_drawn SHAREDBSS_ATTR;
60/* Number skipped since reset */
61static int video_num_skipped SHAREDBSS_ATTR;
62
63/* TODO: Check if 4KB is appropriate - it works for my test streams,
64 so maybe we can reduce it. */
65#define VIDEO_STACKSIZE (4*1024)
66static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR;
67static struct event_queue video_str_queue SHAREDBSS_ATTR;
68static struct queue_sender_list video_str_queue_send SHAREDBSS_ATTR;
69struct stream video_str IBSS_ATTR;
70
71#define DEFAULT_GOP_SIZE INT_MAX /* no I/P skips until it learns */
72#define DROP_THRESHOLD (100*TS_SECOND/1000)
73#define MAX_EARLINESS (120*TS_SECOND/1000)
74
75#if defined(DEBUG) || defined(SIMULATOR)
76static unsigned char pic_coding_type_char(unsigned type)
77{
78 switch (type)
79 {
80 case PIC_FLAG_CODING_TYPE_I:
81 return 'I'; /* Intra-coded */
82 case PIC_FLAG_CODING_TYPE_P:
83 return 'P'; /* Forward-predicted */
84 case PIC_FLAG_CODING_TYPE_B:
85 return 'B'; /* Bidirectionally-predicted */
86 case PIC_FLAG_CODING_TYPE_D:
87 return 'D'; /* DC-coded */
88 default:
89 return '?'; /* Say what? */
90 }
91}
92#endif /* defined(DEBUG) || defined(SIMULATOR) */
93
94/* Multi-use:
95 * 1) Find the sequence header and initialize video out
96 * 2) Find the end of the final frame
97 */
98static int video_str_scan(struct video_thread_data *td,
99 struct str_sync_data *sd)
100{
101 int retval = STREAM_ERROR;
102 uint32_t time = INVALID_TIMESTAMP;
103 uint32_t period = 0;
104 struct stream tmp_str;
105
106 tmp_str.id = video_str.id;
107 tmp_str.hdr.pos = sd->sk.pos;
108 tmp_str.hdr.limit = sd->sk.pos + sd->sk.len;
109
110 /* Fully reset if obtaining size for a new stream */
111 mpeg2_reset(td->mpeg2dec, td->ev.id == VIDEO_GET_SIZE);
112 mpeg2_skip(td->mpeg2dec, 1);
113
114 while (1)
115 {
116 mpeg2_state_t mp2state = mpeg2_parse(td->mpeg2dec);
117 rb->yield();
118
119 switch (mp2state)
120 {
121 case STATE_BUFFER:
122 switch (parser_get_next_data(&tmp_str, STREAM_PM_RANDOM_ACCESS))
123 {
124 case STREAM_DATA_END:
125 DEBUGF("video_stream_scan:STREAM_DATA_END\n");
126 goto scan_finished;
127
128 case STREAM_OK:
129 if (tmp_str.pkt_flags & PKT_HAS_TS)
130 mpeg2_tag_picture(td->mpeg2dec, tmp_str.pts, 0);
131
132 mpeg2_buffer(td->mpeg2dec, tmp_str.curr_packet,
133 tmp_str.curr_packet_end);
134 td->info = mpeg2_info(td->mpeg2dec);
135 break;
136 }
137 break;
138
139 case STATE_SEQUENCE:
140 DEBUGF("video_stream_scan:STATE_SEQUENCE\n");
141 vo_setup(td->info->sequence);
142
143 if (td->ev.id == VIDEO_GET_SIZE)
144 {
145 retval = STREAM_OK;
146 goto scan_finished;
147 }
148 break;
149
150 case STATE_SLICE:
151 case STATE_END:
152 case STATE_INVALID_END:
153 {
154 if (td->info->display_picture == NULL)
155 break;
156
157 switch (td->ev.id)
158 {
159 case STREAM_SYNC:
160 retval = STREAM_OK;
161 goto scan_finished;
162
163 case STREAM_FIND_END_TIME:
164 if (td->info->display_picture->flags & PIC_FLAG_TAGS)
165 time = td->info->display_picture->tag;
166 else if (time != INVALID_TIMESTAMP)
167 time += period;
168
169 period = TC_TO_TS(td->info->sequence->frame_period);
170 break;
171 }
172
173 break;
174 }
175
176 default:
177 break;
178 }
179 }
180
181scan_finished:
182
183 if (td->ev.id == STREAM_FIND_END_TIME)
184 {
185 if (time != INVALID_TIMESTAMP)
186 {
187 sd->time = time + period;
188 retval = STREAM_PERFECT_MATCH;
189 }
190 else
191 {
192 retval = STREAM_NOT_FOUND;
193 }
194 }
195
196 mpeg2_skip(td->mpeg2dec, 0);
197 return retval;
198}
199
200static bool init_sequence(struct video_thread_data *td)
201{
202 struct str_sync_data sd;
203
204 sd.time = 0; /* Ignored */
205 sd.sk.pos = 0;
206 sd.sk.len = 1024*1024;
207 sd.sk.dir = SSCAN_FORWARD;
208
209 return video_str_scan(td, &sd) == STREAM_OK;
210}
211
212static bool check_needs_sync(struct video_thread_data *td, uint32_t time)
213{
214 uint32_t end_time;
215
216 DEBUGF("check_needs_sync:\n");
217 if (td->info == NULL || td->info->display_fbuf == NULL)
218 {
219 DEBUGF(" no fbuf\n");
220 return true;
221 }
222
223 if (td->syncf_perfect == 0)
224 {
225 DEBUGF(" no frame\n");
226 return true;
227 }
228
229 time = clip_time(&video_str, time);
230 end_time = td->frame_time + td->frame_period;
231
232 DEBUGF(" sft:%u t:%u sfte:%u\n", (unsigned)td->frame_time,
233 (unsigned)time, (unsigned)end_time);
234
235 if (time < td->frame_time)
236 return true;
237
238 if (time >= end_time)
239 return time < video_str.end_pts || end_time < video_str.end_pts;
240
241 return false;
242}
243
244/* Do any needed decoding/slide up to the specified time */
245static int sync_decoder(struct video_thread_data *td,
246 struct str_sync_data *sd)
247{
248 int retval = STREAM_ERROR;
249 uint32_t time = clip_time(&video_str, sd->time);
250
251 td->syncf_perfect = 0;
252 td->frame_time = 0;
253 td->frame_period = 0;
254 td->num_ref_pics = 0;
255
256 /* Sometimes theres no sequence headers nearby and libmpeg2 may have reset
257 * fully at some point */
258 if ((td->info == NULL || td->info->sequence == NULL) && !init_sequence(td))
259 {
260 DEBUGF("sync_decoder=>init_sequence failed\n");
261 goto sync_finished;
262 }
263
264 video_str.hdr.pos = sd->sk.pos;
265 video_str.hdr.limit = sd->sk.pos + sd->sk.len;
266 mpeg2_reset(td->mpeg2dec, false);
267 mpeg2_skip(td->mpeg2dec, 1);
268
269 while (1)
270 {
271 mpeg2_state_t mp2state = mpeg2_parse(td->mpeg2dec);
272
273 switch (mp2state)
274 {
275 case STATE_BUFFER:
276 switch (parser_get_next_data(&video_str, STREAM_PM_RANDOM_ACCESS))
277 {
278 case STREAM_DATA_END:
279 DEBUGF("sync_decoder:STR_DATA_END\n");
280 if (td->info && td->info->display_picture &&
281 !(td->info->display_picture->flags & PIC_FLAG_SKIP))
282 {
283 /* No frame matching the time was found up to the end of
284 * the stream - consider a perfect match since no better
285 * can be made */
286 retval = STREAM_PERFECT_MATCH;
287 td->syncf_perfect = 1;
288 }
289 goto sync_finished;
290
291 case STREAM_OK:
292 if (video_str.pkt_flags & PKT_HAS_TS)
293 mpeg2_tag_picture(td->mpeg2dec, video_str.pts, 0);
294 mpeg2_buffer(td->mpeg2dec, video_str.curr_packet,
295 video_str.curr_packet_end);
296 td->info = mpeg2_info(td->mpeg2dec);
297 break;
298 }
299 break;
300
301 case STATE_SEQUENCE:
302 DEBUGF(" STATE_SEQUENCE\n");
303 vo_setup(td->info->sequence);
304 break;
305
306 case STATE_GOP:
307 DEBUGF(" STATE_GOP: (%s)\n",
308 (td->info->gop->flags & GOP_FLAG_CLOSED_GOP) ?
309 "closed" : "open");
310 break;
311
312 case STATE_PICTURE:
313 {
314 int type = td->info->current_picture->flags
315 & PIC_MASK_CODING_TYPE;
316
317 switch (type)
318 {
319 case PIC_FLAG_CODING_TYPE_I:
320 /* I-frame; start decoding */
321 mpeg2_skip(td->mpeg2dec, 0);
322 td->num_ref_pics++;
323 break;
324
325 case PIC_FLAG_CODING_TYPE_P:
326 /* P-frames don't count without I-frames */
327 if (td->num_ref_pics > 0)
328 td->num_ref_pics++;
329 break;
330 }
331
332 if (td->info->current_picture->flags & PIC_FLAG_TAGS)
333 {
334 DEBUGF(" STATE_PICTURE (%c): %u\n", pic_coding_type_char(type),
335 (unsigned)td->info->current_picture->tag);
336 }
337 else
338 {
339 DEBUGF(" STATE_PICTURE (%c): -\n", pic_coding_type_char(type));
340 }
341
342 break;
343 }
344
345 case STATE_SLICE:
346 case STATE_END:
347 case STATE_INVALID_END:
348 {
349 uint32_t end_time;
350
351 if (td->info->display_picture == NULL)
352 {
353 DEBUGF(" td->info->display_picture == NULL\n");
354 break; /* No picture */
355 }
356
357 int type = td->info->display_picture->flags
358 & PIC_MASK_CODING_TYPE;
359
360 if (td->info->display_picture->flags & PIC_FLAG_TAGS)
361 {
362 td->frame_time = td->info->display_picture->tag;
363 DEBUGF(" frame tagged:%u (%c%s)\n", (unsigned)td->frame_time,
364 pic_coding_type_char(type),
365 (td->info->display_picture->flags & PIC_FLAG_SKIP) ?
366 " skipped" : "");
367 }
368 else
369 {
370 td->frame_time += td->frame_period;
371 DEBUGF(" add frame_period:%u (%c%s)\n", (unsigned)td->frame_time,
372 pic_coding_type_char(type),
373 (td->info->display_picture->flags & PIC_FLAG_SKIP) ?
374 " skipped" : "");
375 }
376
377 td->frame_period = TC_TO_TS(td->info->sequence->frame_period);
378 end_time = td->frame_time + td->frame_period;
379
380 DEBUGF(" ft:%u t:%u fe:%u (%c%s)",
381 (unsigned)td->frame_time,
382 (unsigned)time,
383 (unsigned)end_time,
384 pic_coding_type_char(type),
385 (td->info->display_picture->flags & PIC_FLAG_SKIP) ?
386 " skipped" : "");
387
388 if (end_time <= time && end_time < video_str.end_pts)
389 {
390 /* Still too early and have not hit at EOS */
391 DEBUGF(" too early\n");
392 break;
393 }
394 else if (!(td->info->display_picture->flags & PIC_FLAG_SKIP))
395 {
396 /* One perfect point if dependent frames were decoded */
397 switch (type)
398 {
399 case PIC_FLAG_CODING_TYPE_B:
400 if (td->num_ref_pics > 1)
401 {
402 case PIC_FLAG_CODING_TYPE_P:
403 if (td->num_ref_pics > 0)
404 {
405 case PIC_FLAG_CODING_TYPE_I:
406 td->syncf_perfect = 1;
407 break;
408 }
409 }
410 }
411
412 if ((td->frame_time <= time && time < end_time) ||
413 end_time >= video_str.end_pts)
414 {
415 /* One perfect point for matching time goal */
416 DEBUGF(" ft<=t<fe\n");
417 td->syncf_perfect++;
418 }
419 else
420 {
421 DEBUGF(" ft>t\n");
422 }
423
424 /* Two or more perfect points = perfect match - yay! */
425 retval = (td->syncf_perfect >= 2) ?
426 STREAM_PERFECT_MATCH : STREAM_MATCH;
427 }
428 else
429 {
430 /* Too late, no I-Frame yet */
431 DEBUGF("\n");
432 }
433
434 goto sync_finished;
435 }
436
437 default:
438 break;
439 }
440
441 rb->yield();
442 } /* end while */
443
444sync_finished:
445 mpeg2_skip(td->mpeg2dec, 0);
446 return retval;
447}
448
449static bool frame_print_handler(struct video_thread_data *td)
450{
451 bool retval;
452 uint8_t * const * buf = NULL;
453
454 if (td->info != NULL && td->info->display_fbuf != NULL &&
455 td->syncf_perfect > 0)
456 buf = td->info->display_fbuf->buf;
457
458 if (td->ev.id == VIDEO_PRINT_THUMBNAIL)
459 {
460 /* Print a thumbnail of whatever was last decoded - scale and
461 * position to fill the specified rectangle */
462 retval = vo_draw_frame_thumb(buf, (struct vo_rect *)td->ev.data);
463 }
464 else
465 {
466 /* Print the last frame decoded */
467 vo_draw_frame(buf);
468 retval = buf != NULL;
469 }
470
471 return retval;
472}
473
474/* This only returns to play or quit */
475static void video_thread_msg(struct video_thread_data *td)
476{
477 while (1)
478 {
479 intptr_t reply = 0;
480
481 switch (td->ev.id)
482 {
483 case STREAM_PLAY:
484 td->status = STREAM_PLAYING;
485
486 switch (td->state)
487 {
488 case TSTATE_INIT:
489 /* Begin decoding state */
490 td->state = TSTATE_DECODE;
491 /* */
492 case TSTATE_DECODE:
493 if (td->syncf_perfect <= 0)
494 break;
495 /* There should be a frame already, just draw it */
496 td->goal_time = td->frame_time;
497 td->state = TSTATE_RENDER_WAIT;
498 /* */
499 case TSTATE_RENDER_WAIT:
500 /* Settings may have changed to nonlimited - just draw
501 * what was previously being waited for */
502 td->stream_time = TICKS_TO_TS(stream_get_time());
503 if (!settings.limitfps)
504 td->state = TSTATE_RENDER;
505 /* */
506 case TSTATE_RENDER:
507 break;
508
509 case TSTATE_EOS:
510 /* At end of stream - no playback possible so fire the
511 * completion event */
512 stream_generate_event(&video_str, STREAM_EV_COMPLETE, 0);
513 break;
514 }
515
516 reply = td->state != TSTATE_EOS;
517 break;
518
519 case STREAM_PAUSE:
520 td->status = STREAM_PAUSED;
521 reply = td->state != TSTATE_EOS;
522 break;
523
524 case STREAM_STOP:
525 if (td->state == TSTATE_DATA)
526 stream_clear_notify(&video_str, DISK_BUF_DATA_NOTIFY);
527
528 td->status = STREAM_STOPPED;
529 td->state = TSTATE_EOS;
530 reply = true;
531 break;
532
533 case VIDEO_DISPLAY_IS_VISIBLE:
534 reply = vo_is_visible();
535 break;
536
537 case VIDEO_DISPLAY_SHOW:
538 /* Show video and draw the last frame we had if any or reveal the
539 * underlying framebuffer if hiding */
540 reply = vo_show(!!td->ev.data);
541
542#ifdef HAVE_LCD_COLOR
543 /* Match graylib behavior as much as possible */
544 if (!td->ev.data == !reply)
545 break;
546
547 if (td->ev.data)
548 {
549 frame_print_handler(td);
550 }
551 else
552 {
553 IF_COP(rb->commit_discard_dcache());
554 vo_lock();
555 rb->lcd_update();
556 vo_unlock();
557 }
558#endif
559 break;
560
561 case STREAM_RESET:
562 if (td->state == TSTATE_DATA)
563 stream_clear_notify(&video_str, DISK_BUF_DATA_NOTIFY);
564
565 td->state = TSTATE_INIT;
566 td->status = STREAM_STOPPED;
567
568 /* Reset operational info but not sync info */
569 td->stream_time = UINT32_MAX;
570 td->goal_time = 0;
571 td->remain_time = 0;
572 td->skip_ref_pics = 0;
573 td->skip_level = 0;
574 td->num_picture = 0;
575 td->num_intra = 0;
576 td->group_est = DEFAULT_GOP_SIZE;
577 td->last_render = *rb->current_tick - HZ;
578 video_num_drawn = 0;
579 video_num_skipped = 0;
580
581 reply = true;
582 break;
583
584 case STREAM_NEEDS_SYNC:
585 reply = check_needs_sync(td, td->ev.data);
586 break;
587
588 case STREAM_SYNC:
589 if (td->state == TSTATE_INIT)
590 reply = sync_decoder(td, (struct str_sync_data *)td->ev.data);
591 break;
592
593 case DISK_BUF_DATA_NOTIFY:
594 /* Our bun is done */
595 if (td->state != TSTATE_DATA)
596 break;
597
598 td->state = TSTATE_DECODE;
599 str_data_notify_received(&video_str);
600 break;
601
602 case VIDEO_PRINT_FRAME:
603 case VIDEO_PRINT_THUMBNAIL:
604 reply = frame_print_handler(td);
605 break;
606
607 case VIDEO_SET_CLIP_RECT:
608 vo_set_clip_rect((const struct vo_rect *)td->ev.data);
609 break;
610
611 case VIDEO_GET_CLIP_RECT:
612 reply = vo_get_clip_rect((struct vo_rect *)td->ev.data);
613 break;
614
615 case VIDEO_GET_SIZE:
616 {
617 if (td->state != TSTATE_INIT)
618 break; /* Can only use after a reset was issued */
619
620 /* This will reset the decoder in full for this particular event */
621 if (init_sequence(td))
622 {
623 reply = true;
624 vo_dimensions((struct vo_ext *)td->ev.data);
625 }
626 break;
627 }
628
629 case STREAM_FIND_END_TIME:
630 if (td->state != TSTATE_INIT)
631 {
632 reply = STREAM_ERROR;
633 break;
634 }
635
636 reply = video_str_scan(td, (struct str_sync_data *)td->ev.data);
637 break;
638
639 case VIDEO_SET_POST_FRAME_CALLBACK:
640 vo_set_post_draw_callback((void (*)(void))td->ev.data);
641 reply = true;
642 break;
643
644 case STREAM_QUIT:
645 /* Time to go - make thread exit */
646 td->state = TSTATE_EOS;
647 return;
648 }
649
650 str_reply_msg(&video_str, reply);
651
652 if (td->status == STREAM_PLAYING)
653 {
654 switch (td->state)
655 {
656 case TSTATE_DECODE:
657 case TSTATE_RENDER:
658 case TSTATE_RENDER_WAIT:
659 /* These return when in playing state */
660 return;
661 }
662 }
663
664 str_get_msg(&video_str, &td->ev);
665 }
666}
667
668static void video_thread(void)
669{
670 struct video_thread_data td;
671
672 memset(&td, 0, sizeof (td));
673 td.mpeg2dec = mpeg2_init();
674 td.status = STREAM_STOPPED;
675 td.state = TSTATE_EOS;
676
677 if (td.mpeg2dec == NULL)
678 {
679 td.status = STREAM_ERROR;
680 /* Loop and wait for quit message */
681 while (1)
682 {
683 str_get_msg(&video_str, &td.ev);
684 if (td.ev.id == STREAM_QUIT)
685 return;
686 str_reply_msg(&video_str, STREAM_ERROR);
687 }
688 }
689
690 vo_init();
691
692 goto message_wait;
693
694 while (1)
695 {
696 mpeg2_state_t mp2state;
697 td.state = TSTATE_DECODE;
698
699 /* Check for any pending messages and process them */
700 if (str_have_msg(&video_str))
701 {
702 message_wait:
703 /* Wait for a message to be queued */
704 str_get_msg(&video_str, &td.ev);
705
706 message_process:
707 /* Process a message already dequeued */
708 video_thread_msg(&td);
709
710 switch (td.state)
711 {
712 /* These states are the only ones that should return */
713 case TSTATE_DECODE: goto picture_decode;
714 case TSTATE_RENDER: goto picture_draw;
715 case TSTATE_RENDER_WAIT: goto picture_wait;
716 /* Anything else is interpreted as an exit */
717 default: goto video_exit;
718 }
719 }
720
721 picture_decode:
722 mp2state = mpeg2_parse (td.mpeg2dec);
723
724 switch (mp2state)
725 {
726 case STATE_BUFFER:
727 /* Request next packet data */
728 switch (parser_get_next_data(&video_str, STREAM_PM_STREAMING))
729 {
730 case STREAM_DATA_NOT_READY:
731 /* Wait for data to be buffered */
732 td.state = TSTATE_DATA;
733 goto message_wait;
734
735 case STREAM_DATA_END:
736 /* No more data. */
737 td.state = TSTATE_EOS;
738 if (td.status == STREAM_PLAYING)
739 stream_generate_event(&video_str, STREAM_EV_COMPLETE, 0);
740 goto message_wait;
741
742 case STREAM_OK:
743 if (video_str.pkt_flags & PKT_HAS_TS)
744 mpeg2_tag_picture(td.mpeg2dec, video_str.pts, 0);
745
746 mpeg2_buffer(td.mpeg2dec, video_str.curr_packet,
747 video_str.curr_packet_end);
748 td.info = mpeg2_info(td.mpeg2dec);
749 break;
750 }
751 break;
752
753 case STATE_SEQUENCE:
754 /* New video sequence, inform output of any changes */
755 vo_setup(td.info->sequence);
756 break;
757
758 case STATE_PICTURE:
759 {
760 /* This is not in presentation order - do our best anyway */
761 int skip = td.skip_ref_pics;
762
763 /* Frame type: I/P/B/D */
764 switch (td.info->current_picture->flags & PIC_MASK_CODING_TYPE)
765 {
766 case PIC_FLAG_CODING_TYPE_I:
767 if (++td.num_intra >= 2)
768 td.group_est = td.num_picture / (td.num_intra - 1);
769
770 /* Things are extremely late and all frames will be
771 dropped until the next key frame */
772 if (td.skip_level > 0 && td.skip_level >= td.group_est)
773 {
774 td.skip_level--; /* skip frame */
775 skip = td.skip_ref_pics = 1; /* wait for I-frame */
776 td.num_ref_pics = 0;
777 }
778 else if (skip != 0)
779 {
780 skip = td.skip_ref_pics = 0; /* now, decode */
781 td.num_ref_pics = 1;
782 }
783 break;
784
785 case PIC_FLAG_CODING_TYPE_P:
786 if (skip == 0)
787 {
788 td.num_ref_pics++;
789
790 /* If skip_level at least the estimated number of frames
791 left in I-I span, skip until next I-frame */
792 if (td.group_est > 0 && td.skip_level >= td.group_est)
793 {
794 skip = td.skip_ref_pics = 1; /* wait for I-frame */
795 td.num_ref_pics = 0;
796 }
797 }
798
799 if (skip != 0)
800 td.skip_level--;
801 break;
802
803 case PIC_FLAG_CODING_TYPE_B:
804 /* We want to drop something, so this B-frame won't even be
805 decoded. Drawing can happen on the next frame if so desired
806 so long as the B-frames were not dependent upon those from
807 a previous open GOP where the needed reference frames were
808 skipped */
809 if (td.skip_level > 0 || td.num_ref_pics < 2)
810 {
811 skip = 1;
812 td.skip_level--;
813 }
814 break;
815
816 default:
817 skip = 1;
818 break;
819 }
820
821 if (td.num_intra > 0)
822 td.num_picture++;
823
824 td.group_est--;
825
826 mpeg2_skip(td.mpeg2dec, skip);
827 break;
828 }
829
830 case STATE_SLICE:
831 case STATE_END:
832 case STATE_INVALID_END:
833 {
834 int32_t offset; /* Tick adjustment to keep sync */
835
836 if (td.info->display_fbuf == NULL)
837 break; /* No picture */
838
839 /* Get presentation times in audio samples - quite accurate
840 enough - add previous frame duration if not stamped */
841 if (td.info->display_picture->flags & PIC_FLAG_TAGS)
842 td.frame_time = td.info->display_picture->tag;
843 else
844 td.frame_time += td.frame_period;
845
846 td.frame_period = TC_TO_TS(td.info->sequence->frame_period);
847
848 if (!settings.limitfps)
849 {
850 /* No limiting => no dropping or waiting - draw this frame */
851 td.remain_time = 0;
852 td.skip_level = 0;
853 td.syncf_perfect = 1; /* have frame */
854 goto picture_draw;
855 }
856
857 td.goal_time = td.frame_time;
858 td.stream_time = TICKS_TO_TS(stream_get_time());
859
860 /* How early/late are we? > 0 = late, < 0 early */
861 offset = td.stream_time - td.goal_time;
862
863 if (offset >= 0)
864 {
865 /* Late or on-time */
866 if (td.remain_time < 0)
867 td.remain_time = 0; /* now, late */
868
869 offset = AVERAGE(td.remain_time, offset, 4);
870 td.remain_time = offset;
871 }
872 else
873 {
874 /* Early */
875 if (td.remain_time >= 0)
876 td.remain_time = 0; /* now, early */
877 else if (offset > td.remain_time)
878 td.remain_time = MAX(offset, -MAX_EARLINESS); /* less early */
879 else if (td.remain_time != 0)
880 td.remain_time = AVERAGE(td.remain_time, 0, 8); /* earlier/same */
881 /* else there's been no frame drop */
882
883 offset = -td.remain_time;
884 }
885
886 /* Skip anything not decoded */
887 if (td.info->display_picture->flags & PIC_FLAG_SKIP)
888 goto picture_skip;
889
890 td.syncf_perfect = 1; /* have frame (assume so from now on) */
891
892 /* Keep goal_time >= 0 */
893 if ((uint32_t)offset > td.goal_time)
894 offset = td.goal_time;
895
896 td.goal_time -= offset;
897
898 if (!settings.skipframes)
899 {
900 /* No skipping - just wait if we're early and correct for
901 lateness as much as possible. */
902 td.skip_level = 0;
903 goto picture_wait;
904 }
905
906 /** Possibly skip this frame **/
907
908 /* Frameskipping has the following order of preference:
909 *
910 * Frame Type Who Notes/Rationale
911 * B decoder arbitrarily drop - no decode or draw
912 * Any renderer arbitrarily drop - I/P unless B decoded
913 * P decoder must wait for I-frame
914 * I decoder must wait for I-frame
915 *
916 * If a frame can be drawn and it has been at least 1/2 second,
917 * the image will be updated no matter how late it is just to
918 * avoid looking stuck.
919 */
920 if (td.skip_level > 0 &&
921 TIME_BEFORE(*rb->current_tick, td.last_render + HZ/2))
922 {
923 /* Frame skip was set previously but either there wasn't anything
924 dropped yet or not dropped enough. So we quit at least rendering
925 the actual frame to avoid further increase of a/v-drift. */
926 td.skip_level--;
927 goto picture_skip;
928 }
929
930 /* At this point a frame _will_ be drawn - a skip may happen on
931 the next however */
932
933 /* Calculate number of frames to drop/skip - allow brief periods
934 of lateness before producing skips */
935 td.skip_level = 0;
936 if (td.remain_time > 0 && (uint32_t)offset > DROP_THRESHOLD)
937 {
938 td.skip_level = (offset - DROP_THRESHOLD + td.frame_period)
939 / td.frame_period;
940 }
941
942 picture_wait:
943 td.state = TSTATE_RENDER_WAIT;
944
945 /* Wait until time catches up */
946 while (1)
947 {
948 int32_t twait = td.goal_time - td.stream_time;
949 /* Watch for messages while waiting for the frame time */
950
951 if (twait <= 0)
952 break;
953
954 if (twait > TS_SECOND/HZ)
955 {
956 /* Several ticks to wait - do some sleeping */
957 int timeout = (twait - HZ) / (TS_SECOND/HZ);
958 str_get_msg_w_tmo(&video_str, &td.ev, MAX(timeout, 1));
959 if (td.ev.id != SYS_TIMEOUT)
960 goto message_process;
961 }
962 else
963 {
964 /* Just a little left - spin and be accurate */
965 rb->yield();
966 if (str_have_msg(&video_str))
967 goto message_wait;
968 }
969
970 td.stream_time = TICKS_TO_TS(stream_get_time());
971 }
972
973 picture_draw:
974 /* Record last frame time */
975 td.last_render = *rb->current_tick;
976
977 vo_draw_frame(td.info->display_fbuf->buf);
978 video_num_drawn++;
979 break;
980
981 picture_skip:
982 if (td.remain_time <= DROP_THRESHOLD)
983 {
984 td.skip_level = 0;
985 if (td.remain_time <= 0)
986 td.remain_time = INT32_MIN;
987 }
988
989 video_num_skipped++;
990 break;
991 }
992
993 default:
994 break;
995 }
996
997 rb->yield();
998 } /* end while */
999
1000video_exit:
1001 vo_cleanup();
1002 mpeg2_close(td.mpeg2dec);
1003}
1004
1005/* Initializes the video thread */
1006bool video_thread_init(void)
1007{
1008 intptr_t rep;
1009
1010 IF_COP(rb->commit_dcache());
1011
1012 video_str.hdr.q = &video_str_queue;
1013 rb->queue_init(video_str.hdr.q, false);
1014
1015 /* We put the video thread on another processor for multi-core targets. */
1016 video_str.thread = rb->create_thread(
1017 video_thread, video_stack, VIDEO_STACKSIZE, 0,
1018 "mpgvideo" IF_PRIO(,PRIORITY_PLAYBACK) IF_COP(, COP));
1019
1020 rb->queue_enable_queue_send(video_str.hdr.q, &video_str_queue_send,
1021 video_str.thread);
1022
1023 if (video_str.thread == 0)
1024 return false;
1025
1026 /* Wait for thread to initialize */
1027 rep = str_send_msg(&video_str, STREAM_NULL, 0);
1028 IF_COP(rb->commit_discard_dcache());
1029
1030 return rep == 0; /* Normally STREAM_NULL should be ignored */
1031}
1032
1033/* Terminates the video thread */
1034void video_thread_exit(void)
1035{
1036 if (video_str.thread != 0)
1037 {
1038 str_post_msg(&video_str, STREAM_QUIT, 0);
1039 rb->thread_wait(video_str.thread);
1040 IF_COP(rb->commit_discard_dcache());
1041 video_str.thread = 0;
1042 }
1043}
1044
1045
1046/** Misc **/
1047void video_thread_get_stats(struct video_output_stats *s)
1048{
1049 uint32_t start;
1050 uint32_t now = stream_get_ticks(&start);
1051 s->num_drawn = video_num_drawn;
1052 s->num_skipped = video_num_skipped;
1053
1054 s->fps = 0;
1055
1056 if (now > start)
1057 s->fps = muldiv_uint32(CLOCK_RATE*100, s->num_drawn, now - start);
1058}
1059
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 26c1543fdd..8d266966d0 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -71,6 +71,10 @@ wav,viewers/wavview,10
71wav,viewers/wav2wv,- 71wav,viewers/wav2wv,-
72wav,viewers/mp3_encoder,- 72wav,viewers/mp3_encoder,-
73wav,viewers/test_codec,- 73wav,viewers/test_codec,-
74mpg,viewers/mpegplayer,4
75mpeg,viewers/mpegplayer,4
76mpv,viewers/mpegplayer,4
77m2v,viewers/mpegplayer,4
74iriver,viewers/iriver_flash,3 78iriver,viewers/iriver_flash,3
75tap,viewers/zxbox,12 79tap,viewers/zxbox,12
76opx,viewers/open_plugins,- 80opx,viewers/open_plugins,-
diff --git a/docs/MAINTAINERS b/docs/MAINTAINERS
index 2771adb7ad..143e6fa2b4 100644
--- a/docs/MAINTAINERS
+++ b/docs/MAINTAINERS
@@ -189,6 +189,7 @@ Plugins
189:minesweeper: Antoine Cellerier 189:minesweeper: Antoine Cellerier
190:mosaique: 190:mosaique:
191:mp3_encoder: 191:mp3_encoder:
192:mpegplayer: Michael Sevakis
192:nim: 193:nim:
193:oscilloscope: Jens Arnold 194:oscilloscope: Jens Arnold
194:pacbox: Dave Chapman 195:pacbox: Dave Chapman
diff --git a/lib/rbcodec/codecs/libmad/libmad.make b/lib/rbcodec/codecs/libmad/libmad.make
index 479dd54773..6c50e1d201 100644
--- a/lib/rbcodec/codecs/libmad/libmad.make
+++ b/lib/rbcodec/codecs/libmad/libmad.make
@@ -7,9 +7,16 @@
7# $Id$ 7# $Id$
8# 8#
9 9
10# we need to build two different mad libraries
11# (one for codec, one for mpegplayer)
12# so a little trickery is necessary
13
10MADFLAGS = $(CODECFLAGS) -I$(RBCODECLIB_DIR)/codecs/libmad 14MADFLAGS = $(CODECFLAGS) -I$(RBCODECLIB_DIR)/codecs/libmad
11MADFLAGS += -UDEBUG -DNDEBUG -DHAVE_LIMITS_H -DHAVE_ASSERT_H 15MADFLAGS += -UDEBUG -DNDEBUG -DHAVE_LIMITS_H -DHAVE_ASSERT_H
12 16
17# MPEGplayer
18MPEGMADFLAGS = $(MADFLAGS) -DMPEGPLAYER
19
13# libmad 20# libmad
14MADLIB := $(CODECDIR)/libmad.a 21MADLIB := $(CODECDIR)/libmad.a
15MADLIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/libmad/SOURCES) 22MADLIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/libmad/SOURCES)
@@ -20,8 +27,26 @@ $(MADLIB): $(MADLIB_OBJ)
20 $(SILENT)$(shell rm -f $@) 27 $(SILENT)$(shell rm -f $@)
21 $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null 28 $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
22 29
30# libmad-mpeg
31MPEGMADLIB := $(CODECDIR)/libmad-mpeg.a
32MPEGMADLIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/libmad/SOURCES)
33MPEGMADLIB_OBJ := $(addsuffix .o,$(basename $(subst $(RBCODECLIB_DIR)/codecs/libmad,$(RBCODEC_BLD)/codecs/libmad-mpeg,$(MPEGMADLIB_SRC))))
34
35$(MPEGMADLIB): $(MPEGMADLIB_OBJ)
36 $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
37
23# pattern rules 38# pattern rules
24 39
40$(CODECDIR)/libmad-mpeg/%.o : $(RBCODECLIB_DIR)/codecs/libmad/%.c
41 $(SILENT)mkdir -p $(dir $@)
42 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<)) \
43 $(CC) $(MPEGMADFLAGS) -c $< -o $@
44
45$(CODECDIR)/libmad-mpeg/%.o : $(RBCODECLIB_DIR)/codecs/libmad/%.S
46 $(SILENT)mkdir -p $(dir $@)
47 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<)) \
48 $(CC) $(MPEGMADFLAGS) -c $< -o $@
49
25$(CODECDIR)/libmad/%.o: $(RBCODECLIB_DIR)/codecs/libmad/%.c 50$(CODECDIR)/libmad/%.o: $(RBCODECLIB_DIR)/codecs/libmad/%.c
26 $(SILENT)mkdir -p $(dir $@) 51 $(SILENT)mkdir -p $(dir $@)
27 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<)) \ 52 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<)) \
diff --git a/lib/rbcodec/codecs/libmad/mad_iram.h b/lib/rbcodec/codecs/libmad/mad_iram.h
index 5a315c0381..ac0b64cca9 100644
--- a/lib/rbcodec/codecs/libmad/mad_iram.h
+++ b/lib/rbcodec/codecs/libmad/mad_iram.h
@@ -35,9 +35,10 @@
35#define ICODE_ATTR_MPA_SYNTH 35#define ICODE_ATTR_MPA_SYNTH
36#define ICONST_ATTR_MPA_HUFFMAN 36#define ICONST_ATTR_MPA_HUFFMAN
37#else 37#else
38/* Code performs slower in IRAM on PP502x 38/* Code performs slower in IRAM on PP502x and there is no space in
39 S3C2440 doesn't have any IRAM available for codecs */ 39 mpegplayer on the PP5002. S3C2440 doesn't have any IRAM available for
40#if defined(CPU_PP502x) 40 codecs */
41#if defined(CPU_PP502x) || (CONFIG_CPU == PP5002 && defined(MPEGPLAYER))
41#define ICODE_SECTION_MPA_ARM .text 42#define ICODE_SECTION_MPA_ARM .text
42#define ICODE_ATTR_MPA_SYNTH 43#define ICODE_ATTR_MPA_SYNTH
43#else 44#else
diff --git a/lib/rbcodec/codecs/mpa.c b/lib/rbcodec/codecs/mpa.c
index db33f17c3b..d6bcc04910 100644
--- a/lib/rbcodec/codecs/mpa.c
+++ b/lib/rbcodec/codecs/mpa.c
@@ -26,7 +26,7 @@
26 26
27CODEC_HEADER 27CODEC_HEADER
28 28
29#if NUM_CORES > 1 29#if NUM_CORES > 1 && !defined(MPEGPLAYER)
30#define MPA_SYNTH_ON_COP 30#define MPA_SYNTH_ON_COP
31#endif 31#endif
32 32
diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex
index 73cc1a2ada..3348b1fce5 100644
--- a/manual/plugins/main.tex
+++ b/manual/plugins/main.tex
@@ -201,6 +201,8 @@ option from the \setting{Context Menu} (see \reference{ref:Contextmenu}).}
201\nopt{lowmem}{\input{plugins/midiplay.tex}} 201\nopt{lowmem}{\input{plugins/midiplay.tex}}
202\nopt{lowmem}{\input{plugins/mikmod.tex}} 202\nopt{lowmem}{\input{plugins/mikmod.tex}}
203 203
204\nopt{lowmem}{\input{plugins/mpegplayer.tex}}
205
204\input{plugins/mp3_encoder.tex} 206\input{plugins/mp3_encoder.tex}
205 207
206\opt{iriverh300,iriverh100,SANSA_FUZE_PAD,SANSA_E200_PAD,IPOD_4G_PAD,IPOD_3G_PAD% 208\opt{iriverh300,iriverh100,SANSA_FUZE_PAD,SANSA_E200_PAD,IPOD_4G_PAD,IPOD_3G_PAD%
diff --git a/manual/plugins/mpegplayer.tex b/manual/plugins/mpegplayer.tex
new file mode 100644
index 0000000000..9fa438dabe
--- /dev/null
+++ b/manual/plugins/mpegplayer.tex
@@ -0,0 +1,119 @@
1% $Id$ %
2\subsection{MPEG Player}
3The Mpeg Player is a video player plugin capable of playing back MPEG-1 and
4MPEG-2 video streams with MPEG audio multiplexed into \fname{.mpg} files.
5
6To play a video file, you just select it in the Rockbox \setting{File Browser}.
7If your file does not have the \fname{.mpg} extension but is encoded in the
8supported format, you will need to use the \setting{Open With...} context menu
9option and choose \setting{mpegplayer}.
10
11\begin{btnmap}
12 \opt{GIGABEAT_S_PAD}{\ButtonSelect{} or \ButtonPlay}
13 \opt{GIGABEAT_PAD}{\ButtonSelect{} or \ButtonA}
14 \nopt{GIGABEAT_S_PAD,GIGABEAT_PAD}{\ActionWpsPlay}
15 \opt{HAVEREMOTEKEYMAP}{& }
16 & Pause / Resume\\
17 \ActionWpsStop
18 \opt{HAVEREMOTEKEYMAP}{& }
19 & Stop\\
20 \nopt{GIGABEAT_S_PAD,GIGABEAT_PAD}{\ActionWpsVolUp{} / \ActionWpsVolDown}
21 \opt{GIGABEAT_S_PAD,GIGABEAT_PAD}{\ButtonLeft{} or \ButtonVolDown{} /
22 \ButtonRight{} or \ButtonVolUp}
23 \opt{HAVEREMOTEKEYMAP}{& }
24 & Adjust volume up / down\\
25 \nopt{GIGABEAT_S_PAD,GIGABEAT_PAD,SAMSUNG_YH92X_PAD,SAMSUNG_YH820_PAD}{%
26 \ActionWpsSkipPrev{} / \ActionWpsSkipNext}
27 \opt{GIGABEAT_S_PAD,GIGABEAT_PAD}{\ButtonUp{} / \ButtonDown}
28 \opt{SAMSUNG_YH92X_PAD,SAMSUNG_YH820_PAD}{\ButtonLeft{} / \ButtonRight}
29 \opt{HAVEREMOTEKEYMAP}{& }
30 & Rewind / Fast Forward\\
31 \opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonMode}
32 \opt{IPOD_4G_PAD,IPOD_3G_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,MROBE100_PAD,PBELL_VIBE500_PAD}
33 {\ButtonMenu}
34 \opt{IAUDIO_X5_PAD}{\ButtonRec}
35 \opt{IRIVER_H10_PAD}{\ButtonRew}
36 \opt{SAMSUNG_YH92X_PAD,SAMSUNG_YH820_PAD}{\ButtonRew}
37 \opt{SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,SANSA_CLIP_PAD}{\ButtonSelect}
38 \opt{HAVEREMOTEKEYMAP}{& }
39 & Open the MPEG Player menu\\
40\end{btnmap}
41
42When a video file is selected, the Start Menu will be displayed, unless it is
43disabled via the \setting{Resume Options} (see below). In the latter case the video
44will start playing immediately.
45
46Start Menu
47
48\begin{description}
49\item[Play from beginning] Resume information is discarded and the video plays
50 from the start.
51\item[Resume at: mm:ss] Resume video playback at stored resume time mm:ss
52 (start of the video if no resume time is found).
53\item[Set start time] A preview screen is presented consisting of a
54 thumbnail preview and a progress bar where the user can select a start time
55 by `seeking' through the video. The video playback is started by pressing
56 the select button.
57\item[Settings] Open \setting{Settings} submenu -- see below.
58\item[Quit mpegplayer] Exit the plugin.
59\end{description}
60
61Main Menu
62
63\begin{description}
64\item[Settings] Open \setting{Settings} submenu -- see below.
65\item[Resume playback] Return to playback screen.
66\item[Quit mpegplayer] Exit the plugin.
67\end{description}
68
69Settings Menu
70
71\begin{description}
72\item[Display Options] Open \setting{Display Options} submenu -- see below.
73\item[Audio Options] Open \setting{Audio Options} submenu -- see below.
74\item[Resume Options] (default: Start menu) Enable/disable the start menu.
75\item[Play Mode] (default: Single) Set to \setting{All} to play multiple
76 \fname{.mpg} files in the directory continuously.
77\item[Clear all resumes: x] Discard all x resume points.
78\end{description}
79
80Display Options Menu
81
82\begin{description}
83\item[Dithering] (default: off) Prevent banding effects in gradients by blending
84 of colours. (only available on Sansa e200, Sansa c200 and Gigabeat F/X)
85\item[Display FPS] (default: off) This option displays (once a second -- if your
86 video is full-screen this means it will get overwritten by the video and
87 appear to flash once per second) the average number of frames decoded per
88 second, the total number of frames skipped (see the Skip Frames option),
89 the current time (in 100~Hz ticks) and the time the current frame is due to
90 be displayed.
91\item[Limit FPS] (default: on) With this option disabled, mpegplayer will
92 display the video as fast as it can. Useful for benchmarking.
93\item[Skip frames] (default: on) This option causes mpegplayer to attempt to
94 maintain realtime playback by skipping the display of frames -- but these
95 frames are still decoded. Disabling this option can cause loss of A/V sync.
96\opt{backlight_brightness}{
97 \item[Backlight Brightness] (default: Use setting) Choose brightness to use
98 during video playback. Set to \setting{Use setting} to use the Brightness
99 setting.
100}
101\end{description}
102
103Audio Options Menu
104
105\begin{description}
106\item[Tone Controls] (default: force off) Use the bass and treble control
107 settings or force them off.
108\item[Channel Modes] (default: force off) Use the channel configuration setting
109 or force Stereo mode.
110\item[Crossfeed] (default: force off) Use the Crossfeed setting or force
111 crossfeed off.
112\item[Equalizer] (default: force off) Use the Equalizer setting or force the
113 equalizer off.
114\item[Dithering] (default: force off) Use the Dithering setting or force
115 audio dithering off.
116\end{description}
117
118See this page in the Rockbox wiki for information on how to encode your videos
119to the supported format. \wikilink{PluginMpegplayer}