summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2007-01-01 12:59:32 +0000
committerDave Chapman <dave@dchapman.com>2007-01-01 12:59:32 +0000
commit4b801b2d3a45db8df38d02b3c634b0b257290a9b (patch)
treeb7fbacd310a053d1fb9e69533ec7ee8cf8c10431
parent718ffdeadaea1c03d562201990ae699a87df526a (diff)
downloadrockbox-4b801b2d3a45db8df38d02b3c634b0b257290a9b.tar.gz
rockbox-4b801b2d3a45db8df38d02b3c634b0b257290a9b.zip
Initial implementation of audio support (44.1KHz only, mp2 or mp3, mono/stereo, any bitrate) and .mpg file (MPEG program stream) parsing for mpegplayer - .m2v files are no longer supported. .mpg parser based on patch #6366 from Mathieu Favreaux. Currently limited to only playing files smaller than the available buffer RAM (it will play longer files, but never refills the buffer when it runs empty). There is also no a/v sync implemented, and still no seeking support. Coldfire (iriver H3x0 and iaudio X5) users can use the optimisations provided in patch #5995 to increase the framerate, and PortalPlayer (ipods and iriver H10) users will want to use kernel_on_cop_6.diff from FS#5755 which enables mpegplayer to run the video thread on the second CPU core - video on the second core with audio on the first core runs at the same speed as the old mpegplayer did with no audio.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11877 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/plugin.c1
-rw-r--r--apps/plugin.h7
-rw-r--r--apps/plugins/mpegplayer/Makefile6
-rw-r--r--apps/plugins/mpegplayer/alloc.c43
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c734
-rw-r--r--apps/plugins/viewers.config2
-rw-r--r--docs/CREDITS1
7 files changed, 715 insertions, 79 deletions
diff --git a/apps/plugin.c b/apps/plugin.c
index 9739df4661..2fbfe346b3 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -486,6 +486,7 @@ static const struct plugin_api rockbox_api = {
486 sound_default, 486 sound_default,
487 pcm_record_more, 487 pcm_record_more,
488#endif 488#endif
489 create_thread_on_core,
489}; 490};
490 491
491int plugin_load(const char* plugin, void* parameter) 492int plugin_load(const char* plugin, void* parameter)
diff --git a/apps/plugin.h b/apps/plugin.h
index 6fad78edd7..6e9a1316d5 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -110,7 +110,7 @@
110#define PLUGIN_MAGIC 0x526F634B /* RocK */ 110#define PLUGIN_MAGIC 0x526F634B /* RocK */
111 111
112/* increase this every time the api struct changes */ 112/* increase this every time the api struct changes */
113#define PLUGIN_API_VERSION 39 113#define PLUGIN_API_VERSION 40
114 114
115/* update this to latest version if a change to the api struct breaks 115/* update this to latest version if a change to the api struct breaks
116 backwards compatibility (and please take the opportunity to sort in any 116 backwards compatibility (and please take the opportunity to sort in any
@@ -602,6 +602,11 @@ struct plugin_api {
602 int (*sound_default)(int setting); 602 int (*sound_default)(int setting);
603 void (*pcm_record_more)(void *start, size_t size); 603 void (*pcm_record_more)(void *start, size_t size);
604#endif 604#endif
605
606 struct thread_entry*(*create_thread_on_core)(
607 unsigned int core, void (*function)(void),
608 void* stack, int stack_size,
609 const char *name IF_PRIO(, int priority));
605}; 610};
606 611
607/* plugin header */ 612/* plugin header */
diff --git a/apps/plugins/mpegplayer/Makefile b/apps/plugins/mpegplayer/Makefile
index fd19a6da9c..f31f00f26c 100644
--- a/apps/plugins/mpegplayer/Makefile
+++ b/apps/plugins/mpegplayer/Makefile
@@ -34,7 +34,7 @@ all: $(OUTPUT)
34 34
35ifndef SIMVER 35ifndef SIMVER
36$(OBJDIR)/mpegplayer.elf: $(OBJS) $(LINKFILE) 36$(OBJDIR)/mpegplayer.elf: $(OBJS) $(LINKFILE)
37 $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lgcc \ 37 $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lmad -lgcc\
38 -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/mpegplayer.map 38 -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/mpegplayer.map
39 39
40$(OUTPUT): $(OBJDIR)/mpegplayer.elf 40$(OUTPUT): $(OBJDIR)/mpegplayer.elf
@@ -46,7 +46,7 @@ ifeq ($(SIMVER), x11)
46# This is the X11 simulator version 46# This is the X11 simulator version
47 47
48$(OUTPUT): $(OBJS) 48$(OUTPUT): $(OBJS)
49 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin -o $@ 49 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin -lmad -o $@
50ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) 50ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN)
51# 'x' must be kept or you'll have "Win32 error 5" 51# 'x' must be kept or you'll have "Win32 error 5"
52# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 52# $ fgrep 5 /usr/include/w32api/winerror.h | head -1
@@ -61,7 +61,7 @@ ifeq ($(SIMVER), sdl)
61# This is the SDL simulator version 61# This is the SDL simulator version
62 62
63$(OUTPUT): $(OBJS) 63$(OUTPUT): $(OBJS)
64 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin -o $@ 64 $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin -lmad -o $@
65ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) 65ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN)
66# 'x' must be kept or you'll have "Win32 error 5" 66# 'x' must be kept or you'll have "Win32 error 5"
67# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 67# $ fgrep 5 /usr/include/w32api/winerror.h | head -1
diff --git a/apps/plugins/mpegplayer/alloc.c b/apps/plugins/mpegplayer/alloc.c
index 0a3568ae5b..d406947a58 100644
--- a/apps/plugins/mpegplayer/alloc.c
+++ b/apps/plugins/mpegplayer/alloc.c
@@ -46,6 +46,7 @@ void * mpeg2_malloc (unsigned size, mpeg2_alloc_t reason)
46 46
47 (void)reason; 47 (void)reason;
48 48
49 DEBUGF("mpeg2_malloc(%d,%d)\n",size,reason);
49 if (mem_ptr + (long)size > bufsize) { 50 if (mem_ptr + (long)size > bufsize) {
50 DEBUGF("OUT OF MEMORY\n"); 51 DEBUGF("OUT OF MEMORY\n");
51 return NULL; 52 return NULL;
@@ -74,3 +75,45 @@ void *memcpy(void *dest, const void *src, size_t n) {
74 75
75 return dest; 76 return dest;
76} 77}
78
79
80/* The following are expected by libmad */
81void* codec_malloc(size_t size)
82{
83 return mpeg2_malloc(size,-3);
84}
85
86void* codec_calloc(size_t nmemb, size_t size)
87{
88 void* ptr;
89
90 ptr = mpeg2_malloc(nmemb*size,-3);
91
92 if (ptr)
93 rb->memset(ptr,0,size);
94
95 return ptr;
96}
97
98void codec_free(void* ptr) {
99 (void)ptr;
100}
101
102void *memmove(void *dest, const void *src, size_t n)
103{
104 return rb->memmove(dest,src,n);
105}
106
107void *memset(void *s, int c, size_t n)
108{
109 return rb->memset(s,c,n);
110}
111
112void abort(void)
113{
114 rb->lcd_putsxy(0,0,"ABORT!");
115 rb->lcd_update();
116
117 while (1);
118 /* Let's hope this is never called */
119}
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index 8a839eb17b..d046e1e01a 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -1,8 +1,13 @@
1 /* 1/*
2 * mpegplayer.c - based on mpeg2dec.c 2 * mpegplayer.c - based on :
3 * - mpeg2dec.c
4 * - m2psd.c (http://www.brouhaha.com/~eric/software/m2psd/)
3 * 5 *
4 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> 6 * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
5 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> 7 * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
8 *
9 * m2psd: MPEG 2 Program Stream Demultiplexer
10 * Copyright (C) 2003 Eric Smith <eric@brouhaha.com>
6 * 11 *
7 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. 12 * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
8 * See http://libmpeg2.sourceforge.net/ for updates. 13 * See http://libmpeg2.sourceforge.net/ for updates.
@@ -22,6 +27,79 @@
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */ 28 */
24 29
30/*
31
32NOTES:
33
34mpegplayer is structured as follows:
35
361) Video thread (running on the COP for PortalPlayer targets).
372) Audio thread (running on the main CPU to maintain consistency with
38 the audio FIQ hander on PP).
393) The main thread which takes care of buffering.
40
41Using the main thread for buffering wastes the 8KB main stack which is
42in IRAM. However, 8KB is not enough for the audio thread to run (it
43needs somewhere between 8KB and 9KB), so we create a new thread in
44order to`give it a larger stack.
45
46We use 4.5KB of the main stack for a libmad buffer (making use of
47otherwise unused IRAM). There is also the possiblity of stealing the
48main Rockbox codec thread's 9KB of IRAM stack and using that for
49mpegplayer's audio thread - but we should only implement that if we
50can put the IRAM to good use.
51
52The button loop (and hence pause/resume, main menu and, in the future,
53seeking) is placed in the audio thread. This keeps it on the main CPU
54in PP targets and also allows buffering to continue in the background
55whilst the main thread is filling the buffer.
56
57A/V sync is not yet implemented but is planned to be achieved by
58syncing the master clock with the audio, and then (as is currently
59implemented), syncing video with the master clock. This can happen in
60the audio thread, along with resyncing after pause.
61
62Seeking should probably happen in the main thread, as that's where the
63buffering happens.
64
65On PortalPlayer targets, the main CPU is not being fully utilised -
66the bottleneck is the video decoding on the COP. One way to improve
67that might be to move the rendering of the frames (i.e. the
68lcd_yuv_blit() call) from the COP back to the main CPU. Ideas and
69patches for that are welcome!
70
71Notes about MPEG files:
72
73MPEG System Clock is 27MHz - i.e. 27000000 ticks/second.
74
75FPS is represented in terms of a frame period - this is always an
76integer number of 27MHz ticks.
77
78e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of
79900900 27MHz ticks.
80
81In libmpeg2, info->sequence->frame_period contains the frame_period.
82
83Working with Rockbox's 100Hz tick, the common frame rates would need
84to be as follows:
85
86FPS | 27Mhz | 100Hz
87--------|----------------
8810* | 2700000 | 10
8912* | 2250000 | 8.3333
9015* | 1800000 | 6.6667
9123.9760 | 1126125 | 4.170833333
9224 | 1125000 | 4.166667
9325 | 1080000 | 4
9429.9700 | 900900 | 3.336667
9530 | 900000 | 3.333333
96
97
98*Unofficial framerates
99
100*/
101
102
25#include "mpeg2dec_config.h" 103#include "mpeg2dec_config.h"
26 104
27#include "plugin.h" 105#include "plugin.h"
@@ -29,15 +107,11 @@
29#include "mpeg2.h" 107#include "mpeg2.h"
30#include "mpeg_settings.h" 108#include "mpeg_settings.h"
31#include "video_out.h" 109#include "video_out.h"
110#include "../../codecs/libmad/mad.h"
32 111
33PLUGIN_HEADER 112PLUGIN_HEADER
34PLUGIN_IRAM_DECLARE 113PLUGIN_IRAM_DECLARE
35 114
36struct plugin_api* rb;
37
38static mpeg2dec_t * mpeg2dec;
39static int total_offset = 0;
40
41/* button definitions */ 115/* button definitions */
42#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) 116#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
43#define MPEG_MENU BUTTON_MODE 117#define MPEG_MENU BUTTON_MODE
@@ -73,35 +147,81 @@ static int total_offset = 0;
73#error MPEGPLAYER: Unsupported keypad 147#error MPEGPLAYER: Unsupported keypad
74#endif 148#endif
75 149
76static int tick_enabled = 0; 150struct plugin_api* rb;
151
152static mpeg2dec_t * mpeg2dec;
153static int total_offset = 0;
154
155/* Streams */
156typedef struct
157{
158 uint8_t* curr_packet; /* Current stream packet beginning */
159 uint8_t* curr_packet_end; /* Current stream packet end */
160
161 uint8_t* next_packet; /* Next stream packet beginning */
162
163 int id;
164} Stream;
165
166static Stream audio_str, video_str;
167
168static uint8_t *disk_buf, *disk_buf_end;
169
170/* Events */
171static struct event_queue msg_queue IBSS_ATTR;
172
173#define MSG_BUFFER_NEARLY_EMPTY 1
174#define MSG_EXIT_REQUESTED 2
175
176/* Threads */
177static struct thread_entry* audiothread_id;
178static struct thread_entry* videothread_id;
179
180/* Status */
181#define STREAM_PLAYING 0
182#define STREAM_DONE 1
183#define STREAM_PAUSING 2
184#define PLEASE_STOP 3
185#define PLEASE_PAUSE 4
186
187int audiostatus IBSS_ATTR;
188int videostatus IBSS_ATTR;
189
190/* Various buffers */
191/* TODO: Can we reduce the PCM buffer size? */
192#define PCMBUFFER_SIZE (512*1024)
193#define AUDIOBUFFER_SIZE (32*1024)
194#define LIBMPEG2BUFFER_SIZE (2*1024*1024)
195
196static int tick_enabled IBSS_ATTR = 0;
77 197
78#define MPEG_CURRENT_TICK ((unsigned int)((*rb->current_tick - tick_offset))) 198#define MPEG_CURRENT_TICK ((unsigned int)((*rb->current_tick - tick_offset)))
79 199
80/* The value to subtract from current_tick to get the current mpeg tick */ 200/* The value to subtract from current_tick to get the current mpeg tick */
81static int tick_offset; 201static int tick_offset IBSS_ATTR;
82 202
83/* The last tick - i.e. the time to reset the tick_offset to when unpausing */ 203/* The last tick - i.e. the time to reset the tick_offset to when unpausing */
84static int last_tick; 204static int last_tick IBSS_ATTR;
85 205
86void start_timer(void) 206static void start_timer(void)
87{ 207{
88 last_tick = 0; 208 last_tick = 0;
89 tick_offset = *rb->current_tick; 209 tick_offset = *rb->current_tick;
90} 210}
91 211
92void unpause_timer(void) 212static void unpause_timer(void)
93{ 213{
94 tick_offset = *rb->current_tick - last_tick; 214 tick_offset = *rb->current_tick - last_tick;
95} 215}
96 216
97void pause_timer(void) 217static void pause_timer(void)
98{ 218{
99 /* Save the current MPEG tick */ 219 /* Save the current MPEG tick */
100 last_tick = *rb->current_tick - tick_offset; 220 last_tick = *rb->current_tick - tick_offset;
101} 221}
102 222
103 223
104static bool button_loop(void) 224static void button_loop(void)
105{ 225{
106 bool result; 226 bool result;
107 int button = rb->button_get(false); 227 int button = rb->button_get(false);
@@ -109,28 +229,53 @@ static bool button_loop(void)
109 switch (button) 229 switch (button)
110 { 230 {
111 case MPEG_MENU: 231 case MPEG_MENU:
232 videostatus=PLEASE_PAUSE;
233 rb->pcm_play_pause(false);
112 pause_timer(); 234 pause_timer();
113 235
236 /* Wait for video thread to stop */
237 while (videostatus == PLEASE_PAUSE) { rb->sleep(HZ/25); }
114 result = mpeg_menu(); 238 result = mpeg_menu();
115 239
240 /* The menu can change the font, so restore */
241 rb->lcd_setfont(FONT_SYSFIXED);
242
116 unpause_timer(); 243 unpause_timer();
117 244
118 return result; 245 if (result) {
246 audiostatus = PLEASE_STOP;
247 videostatus = PLEASE_STOP;
248 } else {
249 videostatus = STREAM_PLAYING;
250 rb->pcm_play_pause(true);
251 }
252 break;
119 253
120 case MPEG_STOP: 254 case MPEG_STOP:
121 return true; 255 audiostatus = PLEASE_STOP;
256 videostatus = PLEASE_STOP;
257 break;
122 258
123 case MPEG_PAUSE: 259 case MPEG_PAUSE:
260 videostatus=PLEASE_PAUSE;
261 rb->pcm_play_pause(false);
124 pause_timer(); /* Freeze time */ 262 pause_timer(); /* Freeze time */
263
125 button = BUTTON_NONE; 264 button = BUTTON_NONE;
126#ifdef HAVE_ADJUSTABLE_CPU_FREQ 265#ifdef HAVE_ADJUSTABLE_CPU_FREQ
127 rb->cpu_boost(false); 266 rb->cpu_boost(false);
128#endif 267#endif
129 do { 268 do {
130 button = rb->button_get(true); 269 button = rb->button_get(true);
131 if (button == MPEG_STOP) 270 if (button == MPEG_STOP) {
132 return true; 271 audiostatus = PLEASE_STOP;
272 videostatus = PLEASE_STOP;
273 return;
274 }
133 } while (button != MPEG_PAUSE); 275 } while (button != MPEG_PAUSE);
276
277 videostatus = STREAM_PLAYING;
278 rb->pcm_play_pause(true);
134#ifdef HAVE_ADJUSTABLE_CPU_FREQ 279#ifdef HAVE_ADJUSTABLE_CPU_FREQ
135 rb->cpu_boost(true); 280 rb->cpu_boost(true);
136#endif 281#endif
@@ -138,59 +283,414 @@ static bool button_loop(void)
138 break; 283 break;
139 284
140 default: 285 default:
141 if(rb->default_event_handler(button) == SYS_USB_CONNECTED) 286 if(rb->default_event_handler(button) == SYS_USB_CONNECTED) {
142 return true; 287 audiostatus = PLEASE_STOP;
288 videostatus = PLEASE_STOP;
289 }
143 } 290 }
144 return false;
145} 291}
146 292
147/* 293/* libmad related functions/definitions */
294#define INPUT_CHUNK_SIZE 8192
148 295
149NOTES: 296struct mad_stream stream IBSS_ATTR;
297struct mad_frame frame IBSS_ATTR;
298struct mad_synth synth IBSS_ATTR;
150 299
151MPEG System Clock is 27MHz - i.e. 27000000 ticks/second. 300unsigned char mad_main_data[MAD_BUFFER_MDLEN]; /* 2567 bytes */
152 301
153FPS is represented in terms of a frame period - this is always an 302static void init_mad(void* mad_frame_overlap)
154integer number of 27MHz ticks. 303{
304 rb->memset(&stream, 0, sizeof(struct mad_stream));
305 rb->memset(&frame, 0, sizeof(struct mad_frame));
306 rb->memset(&synth, 0, sizeof(struct mad_synth));
155 307
156e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of 308 mad_stream_init(&stream);
157900900 27MHz ticks. 309 mad_frame_init(&frame);
310 mad_synth_init(&synth);
158 311
159In libmpeg2, info->sequence->frame_period contains the frame_period. 312 /* We do this so libmad doesn't try to call codec_calloc() */
313 frame.overlap = mad_frame_overlap;
160 314
161Working with Rockbox's 100Hz tick, the common frame rates would need 315 rb->memset(mad_main_data, 0, sizeof(mad_main_data));
162to be as follows: 316 stream.main_data = &mad_main_data;
317}
163 318
164FPS | 27Mhz | 100Hz 319/* MPEG related headers */
165--------|---------------- 320uint8_t packet_start_code_prefix [3] = { 0x00, 0x00, 0x01 };
16610* | 2700000 | 10 321uint8_t end_code [4] = { 0x00, 0x00, 0x01, 0xb9 };
16712* | 2250000 | 8.3333 322uint8_t pack_start_code [4] = { 0x00, 0x00, 0x01, 0xba };
16815* | 1800000 | 6.6667 323uint8_t system_header_start_code [4] = { 0x00, 0x00, 0x01, 0xbb };
16923.9760 | 1126125 | 4.170833333
17024 | 1125000 | 4.166667
17125 | 1080000 | 4
17229.9700 | 900900 | 3.336667
17330 | 900000 | 3.333333
174 324
325/* This function demux the streams and give the next stream data pointer */
326static void get_next_data( Stream* str )
327{
328 uint8_t *p;
329 uint8_t *header;
330 int stream;
331
332 static int mpeg1_skip_table[16] = {
333 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
334 };
335
336 if( str->curr_packet_end == NULL )
337 {
338 while( (p = disk_buf) == NULL )
339 {
340 rb->sleep(100);
341 }
342 }else{
343 p = str->curr_packet_end;
344 }
345
346 for( ;; )
347 {
348 int length, bytes;
349
350 /* Pack header, skip it */
351 if( rb->memcmp (p, pack_start_code, sizeof (pack_start_code)) == 0 )
352 {
353 if ((p[4] & 0xc0) == 0x40) { /* mpeg-2 */
354 p += 14 + (p[13] & 7);
355 } else if ((p[4] & 0xf0) == 0x20) { /* mpeg-1 */
356 p += 12;
357 } else {
358 rb->splash( 30, true, "Weird Pack header!" );
359 p += 5;
360 }
361 /*rb->splash( 30, true, "Pack header" );*/
362 }
175 363
176*Unofficial framerates 364 /* System header, parse and skip it */
365 if( rb->memcmp (p, system_header_start_code, sizeof (system_header_start_code)) == 0 )
366 {
367 int header_length;
177 368
178*/ 369 p += 4; /*skip start code*/
370 header_length = (*(p++)) << 8;
371 header_length += *(p++);
179 372
180static uint64_t eta; 373 p += header_length;
374 /*rb->splash( 30, true, "System header" );*/
375 }
376
377 /* Packet header, parse it */
378 if( rb->memcmp (p, packet_start_code_prefix, sizeof (packet_start_code_prefix)) != 0 )
379 {
380 /* Problem */
381 //rb->splash( HZ*3, true, "missing packet start code prefix : %X%X at %X", *p, *(p+2), p-disk_buf );
382 str->curr_packet_end = str->curr_packet = NULL;
383 return;
384 //++p;
385 break;
386 }
387
388 /* We retrieve basic infos */
389 stream = *(p+3);
390 length = (*(p+4)) << 8;
391 length += *(p+5);
392
393 /*rb->splash( 100, true, "Stream : %X", stream );*/
394 if (stream != str->id)
395 {
396 /* End of stream ? */
397 if( stream == 0xB9 )
398 {
399 str->curr_packet_end = str->curr_packet = NULL;
400 return;
401 }
402
403 /* It's not the packet we're looking for, skip it */
404 p += length+6;
405 continue;
406 }
407
408 /* Ok, it's our packet */
409 str->curr_packet_end = p + length+6;
410 header = p;
411 if ((header[6] & 0xc0) == 0x80) { /* mpeg2 */
412
413 length = 9 + header[8];
414 /* header points to the mpeg2 pes header */
415 if (header[7] & 0x80) {
416 uint32_t pts, dts;
417
418 pts = (((header[9] >> 1) << 30) |
419 (header[10] << 22) | ((header[11] >> 1) << 15) |
420 (header[12] << 7) | (header[13] >> 1));
421 dts = (!(header[7] & 0x40) ? pts :
422 ((uint32_t)(((header[14] >> 1) << 30) |
423 (header[15] << 22) |
424 ((header[16] >> 1) << 15) |
425 (header[17] << 7) | (header[18] >> 1))));
426
427 if( stream >= 0xe0 )
428 mpeg2_tag_picture (mpeg2dec, pts, dts);
429 }
430 } else { /* mpeg1 */
431 int len_skip;
432 uint8_t * ptsbuf;
433
434 length = 7;
435 while (header[length - 1] == 0xff)
436 {
437 length++;
438 if (length > 23)
439 {
440 rb->splash( 30, true, "Too much stuffing" );
441 break;
442 }
443 }
444 if ((header[length - 1] & 0xc0) == 0x40)
445 {
446 length += 2;
447 }
448 len_skip = length;
449 length += mpeg1_skip_table[header[length - 1] >> 4];
450
451 /* header points to the mpeg1 pes header */
452 ptsbuf = header + len_skip;
453 if ((ptsbuf[-1] & 0xe0) == 0x20)
454 {
455 uint32_t pts, dts;
456
457 pts = (((ptsbuf[-1] >> 1) << 30) |
458 (ptsbuf[0] << 22) | ((ptsbuf[1] >> 1) << 15) |
459 (ptsbuf[2] << 7) | (ptsbuf[3] >> 1));
460 dts = (((ptsbuf[-1] & 0xf0) != 0x30) ? pts :
461 ((uint32_t)(((ptsbuf[4] >> 1) << 30) |
462 (ptsbuf[5] << 22) | ((ptsbuf[6] >> 1) << 15) |
463 (ptsbuf[7] << 7) | (ptsbuf[18] >> 1))));
464 if( stream >= 0xe0 )
465 mpeg2_tag_picture (mpeg2dec, pts, dts);
466 }
467 }
468 p += length;
469 bytes = 6 + (header[4] << 8) + header[5] - length;
470 if (bytes > 0) {
471 /*str->curr_packet_end = p+bytes;*/
472 str->curr_packet = p;
473 return;
474 }
475
476 if( str->curr_packet_end > disk_buf_end )
477 {
478 /* We should ask for buffering here */
479 str->curr_packet_end = str->curr_packet = NULL;
480 return;
481 }
482
483 break;
484 }
485}
486
487uint8_t* mpa_buffer;
488size_t mpa_buffer_size;
489
490static volatile int madpcm_playing;
491static volatile int16_t* pcm_buffer;
492static volatile size_t pcm_buffer_size;
493
494static volatile size_t pcmbuf_len;
495static volatile int16_t* pcmbuf_end;
496static volatile int16_t* pcmbuf_head;
497static volatile int16_t* pcmbuf_tail;
498
499static void init_pcmbuf(void)
500{
501 pcmbuf_head = pcm_buffer;
502 pcmbuf_len = 0;
503 pcmbuf_tail = pcm_buffer;
504 pcmbuf_end = pcm_buffer + pcm_buffer_size / sizeof(int16_t);
505 madpcm_playing = 0;
506}
507
508static void get_more(unsigned char** start, size_t* size)
509{
510 if (pcmbuf_len < 32*1024) {
511 *start = NULL;
512 *size = 0;
513 madpcm_playing = 0;
514 } else {
515 *start = (unsigned char*)(pcmbuf_tail);
516 *size = 32*1024;
517 pcmbuf_tail += (32*1024)/sizeof(int16_t);
518 pcmbuf_len -= 32*1024;
519 if (pcmbuf_tail >= pcmbuf_end) { pcmbuf_tail = pcm_buffer; }
520 }
521}
522
523int line;
524
525static void mad_decode(void)
526{
527 int32_t* left;
528 int32_t* right;
529 int i;
530 size_t n = 0;
531 size_t len;
532 int file_end = 0; /* A count of the errors in each frame */
533 int framelength;
534
535 init_pcmbuf();
536
537 /* This is the decoding loop. */
538 for (;;) {
539 button_loop();
540
541 if (audiostatus == PLEASE_STOP) {
542 goto done;
543 }
544
545 if (n < 1000) { /* TODO: What is the maximum size of an MPEG audio frame? */
546 get_next_data( &audio_str );
547 if (audio_str.curr_packet == NULL) {
548 goto done;
549 }
550 len = audio_str.curr_packet_end - audio_str.curr_packet;
551 if (n + len > mpa_buffer_size) {
552 rb->splash( 30, true, "Audio buffer overflow" );
553 audiostatus=STREAM_DONE;
554 /* Wait to be killed */
555 for (;;) { rb->sleep(HZ); }
556 }
557 rb->memcpy(mpa_buffer+n,audio_str.curr_packet,len);
558 n += len;
559 }
560
561 /* Lock buffers */
562 if (stream.error == 0) {
563 mad_stream_buffer(&stream, mpa_buffer, n);
564 }
565
566 if (mad_frame_decode(&frame, &stream)) {
567 if (stream.error == MAD_FLAG_INCOMPLETE
568 || stream.error == MAD_ERROR_BUFLEN) {
569 /* This makes the codec support partially corrupted files */
570 if (file_end == 30)
571 break;
572
573#if 0
574 /* The mpa.c version: */
575 if (stream.next_frame)
576 inputbuffer = stream.next_frame;
577 else
578 inputbuffer++;
579#endif
580
581 stream.error = 0;
582 file_end++;
583 continue;
584 } else if (MAD_RECOVERABLE(stream.error)) {
585 continue;
586 } else {
587 /* Some other unrecoverable error */
588 break;
589 }
590 break;
591 }
592
593 file_end = 0;
594
595 mad_synth_frame(&synth, &frame);
596
597 /* TODO: Don't memmove so much... */
598 if (stream.next_frame) {
599 len = stream.next_frame - mpa_buffer;
600 rb->memmove(mpa_buffer,stream.next_frame,n-len);
601 n -= len;
602 } else {
603 /* What to do here? */
604 goto done;
605 }
606#if 0
607 /* The mpa.c version: */
608 if (stream.next_frame)
609 inputbuffer = stream.next_frame;
610 else
611 inputbuffer = inputbuffer_end;
612#endif
613
614 framelength = synth.pcm.length;
615
616 if (framelength > 0) {
617 /* Leave at least 32KB free (this will be the currently playing chunk) */
618 while (pcmbuf_len + framelength*4 + 32*1024 > pcm_buffer_size) { rb->yield(); }
619
620 if (MAD_NCHANNELS(&frame.header) == 2) {
621 left = &synth.pcm.samples[0][0];
622 right = &synth.pcm.samples[1][0];
623 for (i = 0 ; i < framelength; i++) {
624 *(pcmbuf_head++) = *(left++) >> 13;
625 *(pcmbuf_head++) = *(right++) >> 13;
626 if (pcmbuf_head >= pcmbuf_end) { pcmbuf_head = pcm_buffer; }
627 }
628 } else { /* mono */
629 int16_t sample;
630 left = &synth.pcm.samples[0][0];
631 for (i = 0 ; i < framelength; i++) {
632 sample = *(left++) >> 13;
633 *(pcmbuf_head++) = sample;
634 *(pcmbuf_head++) = sample;
635 if (pcmbuf_head >= pcmbuf_end) { pcmbuf_head = pcm_buffer; }
636 }
637 }
638
639 /* TODO: Disable interrupts for Coldfire? */
640
641 /* pcmbuf_len is also modified by the FIQ handler (in
642 get_more), so we disable the FIQ TODO: Add sempahore so we
643 don't change whilst get_more is running. */
644
645#ifdef CPU_ARM
646 disable_fiq();
647#endif
648 pcmbuf_len += framelength*4;
649#ifdef CPU_ARM
650 enable_fiq();
651#endif
652 if ((!madpcm_playing) && (pcmbuf_len > 64*1024)) {
653 madpcm_playing = 1;
654 rb->pcm_play_data(get_more,NULL,0);
655 }
656 }
657 rb->yield();
658 }
659
660done:
661 rb->pcm_play_stop();
662 audiostatus=STREAM_DONE;
663
664 for (;;) { rb->sleep(HZ); }
665}
666
667/* End of libmad stuff */
668
669static uint64_t eta IBSS_ATTR;
181 670
182static bool decode_mpeg2 (uint8_t * current, uint8_t * end) 671/* TODO: Running in the main thread, libmad needs 8.25KB of stack.
672 The codec thread uses a 9KB stack. So we can probable reduce this a
673 little, but leave at 9KB for now to be safe. */
674#define AUDIO_STACKSIZE (9*1024)
675uint32_t audio_stack[AUDIO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR;
676
677/* TODO: Check if 4KB is appropriate - it works for my test streams,
678 so maybe we can reduce it. */
679#define VIDEO_STACKSIZE (4*1024)
680static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR;
681
682static void decode_mpeg2 (void)
183{ 683{
184 const mpeg2_info_t * info; 684 const mpeg2_info_t * info;
185 mpeg2_state_t state; 685 mpeg2_state_t state;
186 char str[80]; 686 char str[80];
187 static int skipped = 0; 687 static int skipped = 0;
688 int skipcount = 0;
188 static int frame = 0; 689 static int frame = 0;
189 static int starttick = 0; 690 static int starttick = 0;
190 static int lasttick; 691 static int lasttick;
191 unsigned int eta2; 692 unsigned int eta2;
192 unsigned int x; 693 unsigned int x;
193
194 int fps; 694 int fps;
195 695
196 if (starttick == 0) { 696 if (starttick == 0) {
@@ -198,18 +698,35 @@ static bool decode_mpeg2 (uint8_t * current, uint8_t * end)
198 lasttick=starttick; 698 lasttick=starttick;
199 } 699 }
200 700
201 mpeg2_buffer (mpeg2dec, current, end); 701 /* Request the first packet data */
202 total_offset += end - current; 702 get_next_data( &video_str );
703 mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end);
704 total_offset += video_str.curr_packet_end - video_str.curr_packet;
203 705
204 info = mpeg2_info (mpeg2dec); 706 info = mpeg2_info (mpeg2dec);
205 while (1) { 707 while (1) {
206 if (button_loop()) 708 if (videostatus == PLEASE_STOP) {
207 return true; 709 goto done;
710 } else if (videostatus == PLEASE_PAUSE) {
711 videostatus = STREAM_PAUSING;
712 while (videostatus == STREAM_PAUSING) { rb->sleep(HZ/10); }
713 }
208 state = mpeg2_parse (mpeg2dec); 714 state = mpeg2_parse (mpeg2dec);
715 rb->yield();
209 716
210 switch (state) { 717 switch (state) {
211 case STATE_BUFFER: 718 case STATE_BUFFER:
212 return false; 719 /* Request next packet data */
720 get_next_data( &video_str );
721 mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end);
722 total_offset += video_str.curr_packet_end - video_str.curr_packet;
723 info = mpeg2_info (mpeg2dec);
724 if (video_str.curr_packet == NULL) {
725 /* No more data. */
726 goto done;
727 }
728 continue;
729
213 case STATE_SEQUENCE: 730 case STATE_SEQUENCE:
214 vo_setup(info->sequence->width, 731 vo_setup(info->sequence->width,
215 info->sequence->height, 732 info->sequence->height,
@@ -244,10 +761,12 @@ static bool decode_mpeg2 (uint8_t * current, uint8_t * end)
244 /* If we are more than 1/20 second behind schedule (and 761 /* If we are more than 1/20 second behind schedule (and
245 more than 1/20 second into the decoding), skip frame */ 762 more than 1/20 second into the decoding), skip frame */
246 if (settings.skipframes && (x > HZ/20) && 763 if (settings.skipframes && (x > HZ/20) &&
247 (eta2 < (x - (HZ/20)))) { 764 (eta2 < (x - (HZ/20))) && (skipcount < 10)) {
248 skipped++; 765 skipped++;
766 skipcount++;
249 } else { 767 } else {
250 vo_draw_frame(info->display_fbuf->buf); 768 vo_draw_frame(info->display_fbuf->buf);
769 skipcount = 0;
251 } 770 }
252 771
253 /* Calculate fps */ 772 /* Calculate fps */
@@ -269,53 +788,68 @@ static bool decode_mpeg2 (uint8_t * current, uint8_t * end)
269 788
270 rb->yield(); 789 rb->yield();
271 } 790 }
272}
273
274static void es_loop (int in_file, uint8_t* buffer, size_t buffer_size)
275{
276 uint8_t * end;
277
278 if (buffer==NULL)
279 return;
280 791
281 eta = 0; 792done:
282 do { 793 videostatus = STREAM_DONE;
283 rb->splash(0,true,"Buffering..."); 794 for (;;) { rb->sleep(HZ); }
284 save_settings(); /* Save settings (if they have changed) */
285 end = buffer + rb->read (in_file, buffer, buffer_size);
286 if (decode_mpeg2 (buffer, end))
287 break;
288 } while (end == buffer + buffer_size);
289} 795}
290 796
291enum plugin_status plugin_start(struct plugin_api* api, void* parameter) 797enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
292{ 798{
293 (void)parameter;
294 void* audiobuf; 799 void* audiobuf;
295 int audiosize; 800 int audiosize;
296 int in_file; 801 int in_file;
297 uint8_t* buffer; 802 uint8_t* buffer;
298 size_t buffer_size; 803 size_t buffer_size;
299 804
805 /* We define this here so it is on the main stack (in IRAM) */
806 mad_fixed_t mad_frame_overlap[2][32][18]; /* 4608 bytes */
807
300 /* This also stops audio playback - so we do it before using IRAM */ 808 /* This also stops audio playback - so we do it before using IRAM */
301 audiobuf = api->plugin_get_audio_buffer(&audiosize); 809 audiobuf = api->plugin_get_audio_buffer(&audiosize);
302 810
303 PLUGIN_IRAM_INIT(api) 811 PLUGIN_IRAM_INIT(api)
304 rb = api; 812 rb = api;
305 813
814 /* Set disk pointers to NULL */
815 disk_buf_end = disk_buf = NULL;
816
817 /* Stream construction */
818 /* We take the first stream of each (audio and video) */
819 /* TODO : Search for these in the file first */
820 audio_str.curr_packet_end = audio_str.curr_packet = audio_str.next_packet = NULL;
821 video_str = audio_str;
822 video_str.id = 0xe0;
823 audio_str.id = 0xc0;
824
306 /* Initialise our malloc buffer */ 825 /* Initialise our malloc buffer */
307 mpeg2_alloc_init(audiobuf,audiosize); 826 mpeg2_alloc_init(audiobuf,audiosize);
308 827
309 /* Grab most of the buffer for the compressed video - leave 2MB */ 828 /* Grab most of the buffer for the compressed video - leave 4MB for mpeg audio, and 512KB for PCM audio data */
310 buffer_size = audiosize - 2*1024*1024; 829 buffer_size = audiosize - (PCMBUFFER_SIZE+AUDIOBUFFER_SIZE+LIBMPEG2BUFFER_SIZE);
311 buffer = mpeg2_malloc(buffer_size,0); 830 DEBUGF("audiosize=%d, buffer_size=%d\n",audiosize,buffer_size);
831 buffer = mpeg2_malloc(buffer_size,-1);
312 832
313 if (buffer == NULL) 833 if (buffer == NULL)
314 return PLUGIN_ERROR; 834 return PLUGIN_ERROR;
315 835
316 rb->lcd_set_backdrop(NULL); 836 mpa_buffer_size = AUDIOBUFFER_SIZE;
837 mpa_buffer = mpeg2_malloc(mpa_buffer_size,-2);
838
839 if (mpa_buffer == NULL)
840 return PLUGIN_ERROR;
841
842 /* Use 0.5MB of the remaining 4MB for audio data */
843 pcm_buffer_size = PCMBUFFER_SIZE;
844 pcm_buffer = mpeg2_malloc(pcm_buffer_size,-2);
845
846 if (pcm_buffer == NULL)
847 return PLUGIN_ERROR;
848
849 /* The remaining buffer is for use by libmpeg2 */
317 850
318#ifdef HAVE_LCD_COLOR 851#ifdef HAVE_LCD_COLOR
852 rb->lcd_set_backdrop(NULL);
319 rb->lcd_set_foreground(LCD_WHITE); 853 rb->lcd_set_foreground(LCD_WHITE);
320 rb->lcd_set_background(LCD_BLACK); 854 rb->lcd_set_background(LCD_BLACK);
321#endif 855#endif
@@ -326,6 +860,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
326 return PLUGIN_ERROR; 860 return PLUGIN_ERROR;
327 } 861 }
328 862
863 /* Open the video file */
329 in_file = rb->open((char*)parameter,O_RDONLY); 864 in_file = rb->open((char*)parameter,O_RDONLY);
330 865
331 if (in_file < 0) { 866 if (in_file < 0) {
@@ -337,6 +872,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
337 872
338 mpeg2dec = mpeg2_init (); 873 mpeg2dec = mpeg2_init ();
339 874
875 rb->queue_init( &msg_queue, false ); /* Msg queue init */
876
340 if (mpeg2dec == NULL) 877 if (mpeg2dec == NULL)
341 return PLUGIN_ERROR; 878 return PLUGIN_ERROR;
342 879
@@ -353,10 +890,57 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
353 rb->cpu_boost(true); 890 rb->cpu_boost(true);
354#endif 891#endif
355 892
356 es_loop (in_file, buffer, buffer_size); 893 /* Initialise libmad */
894 rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap));
895 init_mad(mad_frame_overlap);
896
897 eta = 0;
898
899 rb->splash(0,true,"Buffering...");
900
901 disk_buf_end = buffer + rb->read (in_file, buffer, buffer_size);
902 disk_buf = buffer;
903
904 rb->lcd_setfont(FONT_SYSFIXED);
905
906 audiostatus = STREAM_PLAYING;
907 videostatus = STREAM_PLAYING;
908
909 /* We put the video thread on the second processor for multi-core targets. */
910#if NUM_CORES > 1
911 if ((videothread_id = rb->create_thread_on_core(COP,decode_mpeg2,
912#else
913 if ((videothread_id = rb->create_thread(decode_mpeg2,
914#endif
915 (uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo" IF_PRIO(,PRIORITY_PLAYBACK))) == NULL)
916 {
917 rb->splash(HZ,true,"Cannot create video thread!");
918 return PLUGIN_ERROR;
919 }
920
921 if ((audiothread_id = rb->create_thread(mad_decode,
922 (uint8_t*)audio_stack,AUDIO_STACKSIZE,"mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK))) == NULL)
923 {
924 rb->splash(HZ,true,"Cannot create audio thread!");
925 return PLUGIN_ERROR;
926 }
927
928 /* Wait until both threads have finished their work */
929 while ((audiostatus != STREAM_DONE) || (videostatus != STREAM_DONE)) {
930 rb->sleep(HZ/10);
931 }
932
933 rb->remove_thread(audiothread_id);
934 rb->remove_thread(videothread_id);
935 rb->yield(); /* Is this needed? */
936
937 rb->lcd_clear_display();
938 rb->lcd_update();
357 939
358 mpeg2_close (mpeg2dec); 940 mpeg2_close (mpeg2dec);
359 941
942 rb->queue_delete( &msg_queue );
943
360 rb->close (in_file); 944 rb->close (in_file);
361 945
362#ifdef HAVE_ADJUSTABLE_CPU_FREQ 946#ifdef HAVE_ADJUSTABLE_CPU_FREQ
@@ -365,6 +949,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
365 949
366 save_settings(); /* Save settings (if they have changed) */ 950 save_settings(); /* Save settings (if they have changed) */
367 951
952 rb->lcd_setfont(FONT_UI);
953
368#ifdef CONFIG_BACKLIGHT 954#ifdef CONFIG_BACKLIGHT
369 /* reset backlight settings */ 955 /* reset backlight settings */
370 rb->backlight_set_timeout(rb->global_settings->backlight_timeout); 956 rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 35ad7088e4..345b1653d7 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -21,7 +21,7 @@ wav,viewers/wav2wv, 00 00 00 00 00 00
21wav,viewers/mp3_encoder, 00 00 00 00 00 00 21wav,viewers/mp3_encoder, 00 00 00 00 00 00
22wav,viewers/wavplay,60 7F 05 35 3F 00 22wav,viewers/wavplay,60 7F 05 35 3F 00
23bmp,rocks/rockpaint, 01 10 01 10 01 10 23bmp,rocks/rockpaint, 01 10 01 10 01 10
24m2v,viewers/mpegplayer,5D 7F 5D 7F 5D 7F 24mpg,viewers/mpegplayer,5D 7F 5D 7F 5D 7F
25iriver,viewers/iriver_flash,2A 7F 41 41 7F 2A 25iriver,viewers/iriver_flash,2A 7F 41 41 7F 2A
26tap,viewers/zxbox,66 52 4A 66 52 4A 26tap,viewers/zxbox,66 52 4A 66 52 4A
27sna,viewers/zxbox,66 52 4A 66 52 4A 27sna,viewers/zxbox,66 52 4A 66 52 4A
diff --git a/docs/CREDITS b/docs/CREDITS
index 43a53bd36c..df511b57f4 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -260,3 +260,4 @@ Chris Taylor
260Tobias Langhoff 260Tobias Langhoff
261Steve Gotthardt 261Steve Gotthardt
262Greg White 262Greg White
263Mattieu Favréaux