diff options
Diffstat (limited to 'apps/plugins/mpegplayer')
-rw-r--r-- | apps/plugins/mpegplayer/Makefile | 6 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/alloc.c | 43 | ||||
-rw-r--r-- | apps/plugins/mpegplayer/mpegplayer.c | 734 |
3 files changed, 706 insertions, 77 deletions
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 | ||
35 | ifndef SIMVER | 35 | ifndef 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 $@ |
50 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | 50 | ifeq ($(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 $@ |
65 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | 65 | ifeq ($(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 */ | ||
81 | void* codec_malloc(size_t size) | ||
82 | { | ||
83 | return mpeg2_malloc(size,-3); | ||
84 | } | ||
85 | |||
86 | void* 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 | |||
98 | void codec_free(void* ptr) { | ||
99 | (void)ptr; | ||
100 | } | ||
101 | |||
102 | void *memmove(void *dest, const void *src, size_t n) | ||
103 | { | ||
104 | return rb->memmove(dest,src,n); | ||
105 | } | ||
106 | |||
107 | void *memset(void *s, int c, size_t n) | ||
108 | { | ||
109 | return rb->memset(s,c,n); | ||
110 | } | ||
111 | |||
112 | void 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 | |||
32 | NOTES: | ||
33 | |||
34 | mpegplayer is structured as follows: | ||
35 | |||
36 | 1) Video thread (running on the COP for PortalPlayer targets). | ||
37 | 2) Audio thread (running on the main CPU to maintain consistency with | ||
38 | the audio FIQ hander on PP). | ||
39 | 3) The main thread which takes care of buffering. | ||
40 | |||
41 | Using the main thread for buffering wastes the 8KB main stack which is | ||
42 | in IRAM. However, 8KB is not enough for the audio thread to run (it | ||
43 | needs somewhere between 8KB and 9KB), so we create a new thread in | ||
44 | order to`give it a larger stack. | ||
45 | |||
46 | We use 4.5KB of the main stack for a libmad buffer (making use of | ||
47 | otherwise unused IRAM). There is also the possiblity of stealing the | ||
48 | main Rockbox codec thread's 9KB of IRAM stack and using that for | ||
49 | mpegplayer's audio thread - but we should only implement that if we | ||
50 | can put the IRAM to good use. | ||
51 | |||
52 | The button loop (and hence pause/resume, main menu and, in the future, | ||
53 | seeking) is placed in the audio thread. This keeps it on the main CPU | ||
54 | in PP targets and also allows buffering to continue in the background | ||
55 | whilst the main thread is filling the buffer. | ||
56 | |||
57 | A/V sync is not yet implemented but is planned to be achieved by | ||
58 | syncing the master clock with the audio, and then (as is currently | ||
59 | implemented), syncing video with the master clock. This can happen in | ||
60 | the audio thread, along with resyncing after pause. | ||
61 | |||
62 | Seeking should probably happen in the main thread, as that's where the | ||
63 | buffering happens. | ||
64 | |||
65 | On PortalPlayer targets, the main CPU is not being fully utilised - | ||
66 | the bottleneck is the video decoding on the COP. One way to improve | ||
67 | that might be to move the rendering of the frames (i.e. the | ||
68 | lcd_yuv_blit() call) from the COP back to the main CPU. Ideas and | ||
69 | patches for that are welcome! | ||
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: | ||
85 | |||
86 | FPS | 27Mhz | 100Hz | ||
87 | --------|---------------- | ||
88 | 10* | 2700000 | 10 | ||
89 | 12* | 2250000 | 8.3333 | ||
90 | 15* | 1800000 | 6.6667 | ||
91 | 23.9760 | 1126125 | 4.170833333 | ||
92 | 24 | 1125000 | 4.166667 | ||
93 | 25 | 1080000 | 4 | ||
94 | 29.9700 | 900900 | 3.336667 | ||
95 | 30 | 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 | ||
33 | PLUGIN_HEADER | 112 | PLUGIN_HEADER |
34 | PLUGIN_IRAM_DECLARE | 113 | PLUGIN_IRAM_DECLARE |
35 | 114 | ||
36 | struct plugin_api* rb; | ||
37 | |||
38 | static mpeg2dec_t * mpeg2dec; | ||
39 | static 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 | ||
76 | static int tick_enabled = 0; | 150 | struct plugin_api* rb; |
151 | |||
152 | static mpeg2dec_t * mpeg2dec; | ||
153 | static int total_offset = 0; | ||
154 | |||
155 | /* Streams */ | ||
156 | typedef 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 | |||
166 | static Stream audio_str, video_str; | ||
167 | |||
168 | static uint8_t *disk_buf, *disk_buf_end; | ||
169 | |||
170 | /* Events */ | ||
171 | static struct event_queue msg_queue IBSS_ATTR; | ||
172 | |||
173 | #define MSG_BUFFER_NEARLY_EMPTY 1 | ||
174 | #define MSG_EXIT_REQUESTED 2 | ||
175 | |||
176 | /* Threads */ | ||
177 | static struct thread_entry* audiothread_id; | ||
178 | static 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 | |||
187 | int audiostatus IBSS_ATTR; | ||
188 | int 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 | |||
196 | static 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 */ |
81 | static int tick_offset; | 201 | static 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 */ |
84 | static int last_tick; | 204 | static int last_tick IBSS_ATTR; |
85 | 205 | ||
86 | void start_timer(void) | 206 | static 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 | ||
92 | void unpause_timer(void) | 212 | static 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 | ||
97 | void pause_timer(void) | 217 | static 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 | ||
104 | static bool button_loop(void) | 224 | static 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 | ||
149 | NOTES: | 296 | struct mad_stream stream IBSS_ATTR; |
297 | struct mad_frame frame IBSS_ATTR; | ||
298 | struct mad_synth synth IBSS_ATTR; | ||
150 | 299 | ||
151 | MPEG System Clock is 27MHz - i.e. 27000000 ticks/second. | 300 | unsigned char mad_main_data[MAD_BUFFER_MDLEN]; /* 2567 bytes */ |
152 | 301 | ||
153 | FPS is represented in terms of a frame period - this is always an | 302 | static void init_mad(void* mad_frame_overlap) |
154 | integer 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 | ||
156 | e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of | 308 | mad_stream_init(&stream); |
157 | 900900 27MHz ticks. | 309 | mad_frame_init(&frame); |
310 | mad_synth_init(&synth); | ||
158 | 311 | ||
159 | In 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 | ||
161 | Working with Rockbox's 100Hz tick, the common frame rates would need | 315 | rb->memset(mad_main_data, 0, sizeof(mad_main_data)); |
162 | to be as follows: | 316 | stream.main_data = &mad_main_data; |
317 | } | ||
163 | 318 | ||
164 | FPS | 27Mhz | 100Hz | 319 | /* MPEG related headers */ |
165 | --------|---------------- | 320 | uint8_t packet_start_code_prefix [3] = { 0x00, 0x00, 0x01 }; |
166 | 10* | 2700000 | 10 | 321 | uint8_t end_code [4] = { 0x00, 0x00, 0x01, 0xb9 }; |
167 | 12* | 2250000 | 8.3333 | 322 | uint8_t pack_start_code [4] = { 0x00, 0x00, 0x01, 0xba }; |
168 | 15* | 1800000 | 6.6667 | 323 | uint8_t system_header_start_code [4] = { 0x00, 0x00, 0x01, 0xbb }; |
169 | 23.9760 | 1126125 | 4.170833333 | ||
170 | 24 | 1125000 | 4.166667 | ||
171 | 25 | 1080000 | 4 | ||
172 | 29.9700 | 900900 | 3.336667 | ||
173 | 30 | 900000 | 3.333333 | ||
174 | 324 | ||
325 | /* This function demux the streams and give the next stream data pointer */ | ||
326 | static 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 | ||
180 | static 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 | |||
487 | uint8_t* mpa_buffer; | ||
488 | size_t mpa_buffer_size; | ||
489 | |||
490 | static volatile int madpcm_playing; | ||
491 | static volatile int16_t* pcm_buffer; | ||
492 | static volatile size_t pcm_buffer_size; | ||
493 | |||
494 | static volatile size_t pcmbuf_len; | ||
495 | static volatile int16_t* pcmbuf_end; | ||
496 | static volatile int16_t* pcmbuf_head; | ||
497 | static volatile int16_t* pcmbuf_tail; | ||
498 | |||
499 | static 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 | |||
508 | static 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 | |||
523 | int line; | ||
524 | |||
525 | static 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 | |||
660 | done: | ||
661 | rb->pcm_play_stop(); | ||
662 | audiostatus=STREAM_DONE; | ||
663 | |||
664 | for (;;) { rb->sleep(HZ); } | ||
665 | } | ||
666 | |||
667 | /* End of libmad stuff */ | ||
668 | |||
669 | static uint64_t eta IBSS_ATTR; | ||
181 | 670 | ||
182 | static 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) | ||
675 | uint32_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) | ||
680 | static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR; | ||
681 | |||
682 | static 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 | |||
274 | static 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; | 792 | done: |
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 | ||
291 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | 797 | enum 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); |