diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-12-29 19:46:35 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-12-29 19:46:35 +0000 |
commit | a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1 (patch) | |
tree | d393a23d83549f99772bb156e59ffb88725148b6 /apps/plugins/mpegplayer/video_thread.c | |
parent | 1d0f6b90ff43776e55b4b9f062c9bea3f99aa768 (diff) | |
download | rockbox-a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1.tar.gz rockbox-a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1.zip |
mpegplayer: Make playback engine fully seekable and frame-accurate and split into logical parts. Be sure to have all current features work. Actual UI for seeking will be added soon. Recommended GOP size is about 15-30 frames depending on target or seeking can be slow with really long GOPs (nature of MPEG video). More refined encoding recommendations for a particular player should be posted soon.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15977 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/mpegplayer/video_thread.c')
-rw-r--r-- | apps/plugins/mpegplayer/video_thread.c | 1040 |
1 files changed, 1040 insertions, 0 deletions
diff --git a/apps/plugins/mpegplayer/video_thread.c b/apps/plugins/mpegplayer/video_thread.c new file mode 100644 index 0000000000..e69089d734 --- /dev/null +++ b/apps/plugins/mpegplayer/video_thread.c | |||
@@ -0,0 +1,1040 @@ | |||
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 | * All files in this archive are subject to the GNU General Public License. | ||
15 | * See the file COPYING in the source tree root for full license agreement. | ||
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 "plugin.h" | ||
22 | #include "mpegplayer.h" | ||
23 | #include "mpeg2dec_config.h" | ||
24 | #include "gray.h" | ||
25 | #include "video_out.h" | ||
26 | #include "mpeg_settings.h" | ||
27 | |||
28 | /** Video stream and thread **/ | ||
29 | |||
30 | /* Video thread data passed around to its various functions */ | ||
31 | struct video_thread_data | ||
32 | { | ||
33 | mpeg2dec_t *mpeg2dec; /* Our video decoder */ | ||
34 | const mpeg2_info_t *info; /* Info about video stream */ | ||
35 | int state; /* Thread state */ | ||
36 | int status; /* Media status */ | ||
37 | struct queue_event ev; /* Our event queue to receive commands */ | ||
38 | int num_drawn; /* Number of frames drawn since reset */ | ||
39 | int num_skipped; /* Number of frames skipped since reset */ | ||
40 | uint32_t curr_time; /* Current due time of frame */ | ||
41 | uint32_t period; /* Frame period in clock ticks */ | ||
42 | uint32_t eta_stream; /* Current time of stream */ | ||
43 | uint32_t eta_video; /* Time that frame has been scheduled for */ | ||
44 | int32_t eta_early; /* How early has the frame been decoded? */ | ||
45 | int32_t eta_late; /* How late has the frame been decoded? */ | ||
46 | int frame_drop_level; /* Drop severity */ | ||
47 | int skip_level; /* Skip severity */ | ||
48 | long last_showfps; /* Last time the FPS display was updated */ | ||
49 | long last_render; /* Last time a frame was drawn */ | ||
50 | int syncf_perfect; /* Last sync fit result */ | ||
51 | uint32_t syncf_time; /* PTS of last synced frame */ | ||
52 | uint32_t syncf_period; /* TS duration of last synced frame */ | ||
53 | }; | ||
54 | |||
55 | /* TODO: Check if 4KB is appropriate - it works for my test streams, | ||
56 | so maybe we can reduce it. */ | ||
57 | #define VIDEO_STACKSIZE (4*1024) | ||
58 | static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR; | ||
59 | static struct event_queue video_str_queue NOCACHEBSS_ATTR; | ||
60 | static struct queue_sender_list video_str_queue_send NOCACHEBSS_ATTR; | ||
61 | struct stream video_str IBSS_ATTR; | ||
62 | |||
63 | static void draw_fps(struct video_thread_data *td) | ||
64 | { | ||
65 | uint32_t start; | ||
66 | uint32_t clock_ticks = stream_get_ticks(&start); | ||
67 | int fps = 0; | ||
68 | char str[80]; | ||
69 | |||
70 | clock_ticks -= start; | ||
71 | if (clock_ticks != 0) | ||
72 | fps = muldiv_uint32(CLOCK_RATE*100, td->num_drawn, clock_ticks); | ||
73 | |||
74 | rb->snprintf(str, sizeof(str), "%d.%02d %d %d ", | ||
75 | fps / 100, fps % 100, td->num_skipped, | ||
76 | td->info->display_picture->temporal_reference); | ||
77 | rb->lcd_putsxy(0, 0, str); | ||
78 | rb->lcd_update_rect(0, 0, LCD_WIDTH, 8); | ||
79 | |||
80 | td->last_showfps = *rb->current_tick; | ||
81 | } | ||
82 | |||
83 | #if defined(DEBUG) || defined(SIMULATOR) | ||
84 | static unsigned char pic_coding_type_char(unsigned type) | ||
85 | { | ||
86 | switch (type) | ||
87 | { | ||
88 | case PIC_FLAG_CODING_TYPE_I: | ||
89 | return 'I'; /* Intra-coded */ | ||
90 | case PIC_FLAG_CODING_TYPE_P: | ||
91 | return 'P'; /* Forward-predicted */ | ||
92 | case PIC_FLAG_CODING_TYPE_B: | ||
93 | return 'B'; /* Bidirectionally-predicted */ | ||
94 | case PIC_FLAG_CODING_TYPE_D: | ||
95 | return 'D'; /* DC-coded */ | ||
96 | default: | ||
97 | return '?'; /* Say what? */ | ||
98 | } | ||
99 | } | ||
100 | #endif /* defined(DEBUG) || defined(SIMULATOR) */ | ||
101 | |||
102 | /* Multi-use: | ||
103 | * 1) Find the sequence header and initialize video out | ||
104 | * 2) Find the end of the final frame | ||
105 | */ | ||
106 | static int video_str_scan(struct video_thread_data *td, | ||
107 | struct str_sync_data *sd) | ||
108 | { | ||
109 | int retval = STREAM_ERROR; | ||
110 | uint32_t time = INVALID_TIMESTAMP; | ||
111 | uint32_t period = 0; | ||
112 | struct stream tmp_str; | ||
113 | |||
114 | tmp_str.id = video_str.id; | ||
115 | tmp_str.hdr.pos = sd->sk.pos; | ||
116 | tmp_str.hdr.limit = sd->sk.pos + sd->sk.len; | ||
117 | |||
118 | mpeg2_reset(td->mpeg2dec, false); | ||
119 | mpeg2_skip(td->mpeg2dec, 1); | ||
120 | |||
121 | while (1) | ||
122 | { | ||
123 | mpeg2_state_t mp2state = mpeg2_parse(td->mpeg2dec); | ||
124 | rb->yield(); | ||
125 | |||
126 | switch (mp2state) | ||
127 | { | ||
128 | case STATE_BUFFER: | ||
129 | switch (parser_get_next_data(&tmp_str, STREAM_PM_RANDOM_ACCESS)) | ||
130 | { | ||
131 | case STREAM_DATA_END: | ||
132 | DEBUGF("video_stream_scan:STREAM_DATA_END\n"); | ||
133 | goto scan_finished; | ||
134 | |||
135 | case STREAM_OK: | ||
136 | if (tmp_str.pkt_flags & PKT_HAS_TS) | ||
137 | mpeg2_tag_picture(td->mpeg2dec, tmp_str.pts, 0); | ||
138 | |||
139 | mpeg2_buffer(td->mpeg2dec, tmp_str.curr_packet, | ||
140 | tmp_str.curr_packet_end); | ||
141 | td->info = mpeg2_info(td->mpeg2dec); | ||
142 | break; | ||
143 | } | ||
144 | break; | ||
145 | |||
146 | case STATE_SEQUENCE: | ||
147 | DEBUGF("video_stream_scan:STATE_SEQUENCE\n"); | ||
148 | vo_setup(td->info->sequence); | ||
149 | |||
150 | if (td->ev.id == VIDEO_GET_SIZE) | ||
151 | { | ||
152 | retval = STREAM_OK; | ||
153 | goto scan_finished; | ||
154 | } | ||
155 | break; | ||
156 | |||
157 | case STATE_SLICE: | ||
158 | case STATE_END: | ||
159 | case STATE_INVALID_END: | ||
160 | { | ||
161 | if (td->info->display_picture == NULL) | ||
162 | break; | ||
163 | |||
164 | switch (td->ev.id) | ||
165 | { | ||
166 | case STREAM_SYNC: | ||
167 | retval = STREAM_OK; | ||
168 | goto scan_finished; | ||
169 | |||
170 | case STREAM_FIND_END_TIME: | ||
171 | if (td->info->display_picture->flags & PIC_FLAG_TAGS) | ||
172 | time = td->info->display_picture->tag; | ||
173 | else if (time != INVALID_TIMESTAMP) | ||
174 | time += period; | ||
175 | |||
176 | period = TC_TO_TS(td->info->sequence->frame_period); | ||
177 | break; | ||
178 | } | ||
179 | |||
180 | break; | ||
181 | } | ||
182 | |||
183 | default: | ||
184 | break; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | scan_finished: | ||
189 | |||
190 | if (td->ev.id == STREAM_FIND_END_TIME) | ||
191 | { | ||
192 | if (time != INVALID_TIMESTAMP) | ||
193 | { | ||
194 | sd->time = time + period; | ||
195 | retval = STREAM_PERFECT_MATCH; | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | retval = STREAM_NOT_FOUND; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | mpeg2_skip(td->mpeg2dec, 0); | ||
204 | return retval; | ||
205 | } | ||
206 | |||
207 | static bool init_sequence(struct video_thread_data *td) | ||
208 | { | ||
209 | struct str_sync_data sd; | ||
210 | |||
211 | sd.time = 0; /* Ignored */ | ||
212 | sd.sk.pos = 0; | ||
213 | sd.sk.len = 1024*1024; | ||
214 | sd.sk.dir = SSCAN_FORWARD; | ||
215 | |||
216 | return video_str_scan(td, &sd) == STREAM_OK; | ||
217 | } | ||
218 | |||
219 | static bool check_needs_sync(struct video_thread_data *td, uint32_t time) | ||
220 | { | ||
221 | uint32_t syncf_end; | ||
222 | |||
223 | DEBUGF("check_needs_sync:\n"); | ||
224 | if (td->info == NULL || td->info->display_fbuf == NULL) | ||
225 | { | ||
226 | DEBUGF(" no fbuf\n"); | ||
227 | return true; | ||
228 | } | ||
229 | |||
230 | if (td->syncf_perfect == 0) | ||
231 | { | ||
232 | DEBUGF(" no frame\n"); | ||
233 | return true; | ||
234 | } | ||
235 | |||
236 | time = clip_time(&video_str, time); | ||
237 | syncf_end = td->syncf_time + td->syncf_period; | ||
238 | |||
239 | DEBUGF(" sft:%u t:%u sfte:%u\n", (unsigned)td->syncf_time, | ||
240 | (unsigned)time, (unsigned)syncf_end); | ||
241 | |||
242 | if (time < td->syncf_time) | ||
243 | return true; | ||
244 | |||
245 | if (time >= syncf_end) | ||
246 | return time < video_str.end_pts || syncf_end < video_str.end_pts; | ||
247 | |||
248 | return false; | ||
249 | } | ||
250 | |||
251 | /* Do any needed decoding/slide up to the specified time */ | ||
252 | static int sync_decoder(struct video_thread_data *td, | ||
253 | struct str_sync_data *sd) | ||
254 | { | ||
255 | int retval = STREAM_ERROR; | ||
256 | int ipic = 0, ppic = 0; | ||
257 | uint32_t time = clip_time(&video_str, sd->time); | ||
258 | |||
259 | td->syncf_perfect = 0; | ||
260 | td->syncf_time = 0; | ||
261 | td->syncf_period = 0; | ||
262 | td->curr_time = 0; | ||
263 | td->period = 0; | ||
264 | |||
265 | /* Sometimes theres no sequence headers nearby and libmpeg2 may have reset | ||
266 | * fully at some point */ | ||
267 | if ((td->info == NULL || td->info->sequence == NULL) && !init_sequence(td)) | ||
268 | { | ||
269 | DEBUGF("sync_decoder=>init_sequence failed\n"); | ||
270 | goto sync_finished; | ||
271 | } | ||
272 | |||
273 | video_str.hdr.pos = sd->sk.pos; | ||
274 | video_str.hdr.limit = sd->sk.pos + sd->sk.len; | ||
275 | mpeg2_reset(td->mpeg2dec, false); | ||
276 | mpeg2_skip(td->mpeg2dec, 1); | ||
277 | |||
278 | while (1) | ||
279 | { | ||
280 | mpeg2_state_t mp2state = mpeg2_parse(td->mpeg2dec); | ||
281 | rb->yield(); | ||
282 | |||
283 | switch (mp2state) | ||
284 | { | ||
285 | case STATE_BUFFER: | ||
286 | switch (parser_get_next_data(&video_str, STREAM_PM_RANDOM_ACCESS)) | ||
287 | { | ||
288 | case STREAM_DATA_END: | ||
289 | DEBUGF("sync_decoder:STR_DATA_END\n"); | ||
290 | if (td->info && td->info->display_picture && | ||
291 | !(td->info->display_picture->flags & PIC_FLAG_SKIP)) | ||
292 | { | ||
293 | /* No frame matching the time was found up to the end of | ||
294 | * the stream - consider a perfect match since no better | ||
295 | * can be made */ | ||
296 | retval = STREAM_PERFECT_MATCH; | ||
297 | td->syncf_perfect = 1; | ||
298 | } | ||
299 | goto sync_finished; | ||
300 | |||
301 | case STREAM_OK: | ||
302 | if (video_str.pkt_flags & PKT_HAS_TS) | ||
303 | mpeg2_tag_picture(td->mpeg2dec, video_str.pts, 0); | ||
304 | |||
305 | mpeg2_buffer(td->mpeg2dec, video_str.curr_packet, | ||
306 | video_str.curr_packet_end); | ||
307 | td->info = mpeg2_info(td->mpeg2dec); | ||
308 | break; | ||
309 | } | ||
310 | break; | ||
311 | |||
312 | case STATE_SEQUENCE: | ||
313 | DEBUGF(" STATE_SEQUENCE\n"); | ||
314 | vo_setup(td->info->sequence); | ||
315 | break; | ||
316 | |||
317 | case STATE_GOP: | ||
318 | DEBUGF(" STATE_GOP: (%s)\n", | ||
319 | (td->info->gop->flags & GOP_FLAG_CLOSED_GOP) ? | ||
320 | "closed" : "open"); | ||
321 | break; | ||
322 | |||
323 | case STATE_PICTURE: | ||
324 | { | ||
325 | int type = td->info->current_picture->flags | ||
326 | & PIC_MASK_CODING_TYPE; | ||
327 | |||
328 | switch (type) | ||
329 | { | ||
330 | case PIC_FLAG_CODING_TYPE_I: | ||
331 | /* I-frame; start decoding */ | ||
332 | mpeg2_skip(td->mpeg2dec, 0); | ||
333 | ipic = 1; | ||
334 | break; | ||
335 | case PIC_FLAG_CODING_TYPE_P: | ||
336 | /* P-frames don't count without I-frames */ | ||
337 | ppic = ipic; | ||
338 | break; | ||
339 | } | ||
340 | |||
341 | if (td->info->current_picture->flags & PIC_FLAG_TAGS) | ||
342 | { | ||
343 | DEBUGF(" STATE_PICTURE (%c): %u\n", pic_coding_type_char(type), | ||
344 | (unsigned)td->info->current_picture->tag); | ||
345 | } | ||
346 | else | ||
347 | { | ||
348 | DEBUGF(" STATE_PICTURE (%c): -\n", pic_coding_type_char(type)); | ||
349 | } | ||
350 | |||
351 | break; | ||
352 | } | ||
353 | |||
354 | case STATE_SLICE: | ||
355 | case STATE_END: | ||
356 | case STATE_INVALID_END: | ||
357 | { | ||
358 | uint32_t syncf_end; | ||
359 | |||
360 | if (td->info->display_picture == NULL) | ||
361 | { | ||
362 | DEBUGF(" td->info->display_picture == NULL\n"); | ||
363 | break; /* No picture */ | ||
364 | } | ||
365 | |||
366 | int type = td->info->display_picture->flags | ||
367 | & PIC_MASK_CODING_TYPE; | ||
368 | |||
369 | if (td->info->display_picture->flags & PIC_FLAG_TAGS) | ||
370 | { | ||
371 | td->syncf_time = td->info->display_picture->tag; | ||
372 | DEBUGF(" frame tagged:%u (%c%s)\n", (unsigned)td->syncf_time, | ||
373 | pic_coding_type_char(type), | ||
374 | (td->info->display_picture->flags & PIC_FLAG_SKIP) ? | ||
375 | " skipped" : ""); | ||
376 | } | ||
377 | else | ||
378 | { | ||
379 | td->syncf_time += td->syncf_period; | ||
380 | DEBUGF(" add period:%u (%c%s)\n", (unsigned)td->syncf_time, | ||
381 | pic_coding_type_char(type), | ||
382 | (td->info->display_picture->flags & PIC_FLAG_SKIP) ? | ||
383 | " skipped" : ""); | ||
384 | } | ||
385 | |||
386 | td->syncf_period = TC_TO_TS(td->info->sequence->frame_period); | ||
387 | syncf_end = td->syncf_time + td->syncf_period; | ||
388 | |||
389 | DEBUGF(" ft:%u t:%u fe:%u (%c%s)", | ||
390 | (unsigned)td->syncf_time, | ||
391 | (unsigned)time, | ||
392 | (unsigned)(td->syncf_time + td->syncf_period), | ||
393 | pic_coding_type_char(type), | ||
394 | (td->info->display_picture->flags & PIC_FLAG_SKIP) ? | ||
395 | " skipped" : ""); | ||
396 | |||
397 | td->curr_time = TS_TO_TICKS(td->syncf_time); | ||
398 | td->period = TS_TO_TICKS(td->syncf_period); | ||
399 | |||
400 | if (syncf_end <= time && syncf_end < video_str.end_pts) | ||
401 | { | ||
402 | /* Still too early and have not hit at EOS */ | ||
403 | DEBUGF(" too early\n"); | ||
404 | break; | ||
405 | } | ||
406 | else if (!(td->info->display_picture->flags & PIC_FLAG_SKIP)) | ||
407 | { | ||
408 | /* One perfect point if dependent frames were decoded */ | ||
409 | td->syncf_perfect = ipic; | ||
410 | |||
411 | if (type == PIC_FLAG_CODING_TYPE_B) | ||
412 | td->syncf_perfect &= ppic; | ||
413 | |||
414 | if ((td->syncf_time <= time && time < syncf_end) || | ||
415 | syncf_end >= video_str.end_pts) | ||
416 | { | ||
417 | /* One perfect point for matching time goal */ | ||
418 | DEBUGF(" ft<=t<fe\n"); | ||
419 | td->syncf_perfect++; | ||
420 | } | ||
421 | else | ||
422 | { | ||
423 | DEBUGF(" ft>t\n"); | ||
424 | } | ||
425 | |||
426 | /* Two or more perfect points = perfect match - yay! */ | ||
427 | retval = (td->syncf_perfect >= 2) ? | ||
428 | STREAM_PERFECT_MATCH : STREAM_MATCH; | ||
429 | } | ||
430 | else | ||
431 | { | ||
432 | /* Too late, no I-Frame yet */ | ||
433 | DEBUGF("\n"); | ||
434 | } | ||
435 | |||
436 | goto sync_finished; | ||
437 | } | ||
438 | |||
439 | default: | ||
440 | break; | ||
441 | } | ||
442 | |||
443 | rb->yield(); | ||
444 | } /* end while */ | ||
445 | |||
446 | sync_finished: | ||
447 | mpeg2_skip(td->mpeg2dec, 0); | ||
448 | return retval; | ||
449 | } | ||
450 | |||
451 | /* This only returns to play or quit */ | ||
452 | static void video_thread_msg(struct video_thread_data *td) | ||
453 | { | ||
454 | while (1) | ||
455 | { | ||
456 | intptr_t reply = 0; | ||
457 | |||
458 | switch (td->ev.id) | ||
459 | { | ||
460 | case STREAM_PLAY: | ||
461 | td->status = STREAM_PLAYING; | ||
462 | |||
463 | switch (td->state) | ||
464 | { | ||
465 | case TSTATE_RENDER_WAIT: | ||
466 | /* Settings may have changed to nonlimited - just draw | ||
467 | * what was previously being waited for */ | ||
468 | if (!settings.limitfps) | ||
469 | td->state = TSTATE_RENDER; | ||
470 | case TSTATE_DECODE: | ||
471 | case TSTATE_RENDER: | ||
472 | break; | ||
473 | |||
474 | case TSTATE_INIT: | ||
475 | /* Begin decoding state */ | ||
476 | td->state = TSTATE_DECODE; | ||
477 | break; | ||
478 | |||
479 | case TSTATE_EOS: | ||
480 | /* At end of stream - no playback possible so fire the | ||
481 | * completion event */ | ||
482 | stream_generate_event(&video_str, STREAM_EV_COMPLETE, 0); | ||
483 | break; | ||
484 | } | ||
485 | |||
486 | reply = td->state != TSTATE_EOS; | ||
487 | break; | ||
488 | |||
489 | case STREAM_PAUSE: | ||
490 | td->status = STREAM_PAUSED; | ||
491 | reply = td->state != TSTATE_EOS; | ||
492 | break; | ||
493 | |||
494 | case STREAM_STOP: | ||
495 | if (td->state == TSTATE_DATA) | ||
496 | stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY); | ||
497 | |||
498 | td->status = STREAM_STOPPED; | ||
499 | td->state = TSTATE_EOS; | ||
500 | reply = true; | ||
501 | break; | ||
502 | |||
503 | case VIDEO_DISPLAY_IS_VISIBLE: | ||
504 | reply = vo_is_visible(); | ||
505 | break; | ||
506 | |||
507 | case VIDEO_DISPLAY_SHOW: | ||
508 | /* Show video and draw the last frame we had if any or reveal the | ||
509 | * underlying framebuffer if hiding */ | ||
510 | reply = vo_show(!!td->ev.data); | ||
511 | |||
512 | #ifdef HAVE_LCD_COLOR | ||
513 | /* Match graylib behavior as much as possible */ | ||
514 | if (!td->ev.data == !reply) | ||
515 | break; | ||
516 | |||
517 | if (td->ev.data) | ||
518 | { | ||
519 | if (td->info != NULL && td->info->display_fbuf != NULL) | ||
520 | vo_draw_frame(td->info->display_fbuf->buf); | ||
521 | } | ||
522 | else | ||
523 | { | ||
524 | IF_COP(invalidate_icache()); | ||
525 | rb->lcd_update(); | ||
526 | } | ||
527 | #else | ||
528 | GRAY_FLUSH_ICACHE(); | ||
529 | #endif | ||
530 | break; | ||
531 | |||
532 | case STREAM_RESET: | ||
533 | if (td->state == TSTATE_DATA) | ||
534 | stream_clear_notify(&audio_str, DISK_BUF_DATA_NOTIFY); | ||
535 | |||
536 | td->state = TSTATE_INIT; | ||
537 | td->status = STREAM_STOPPED; | ||
538 | |||
539 | /* Reset operational info but not sync info */ | ||
540 | td->eta_stream = UINT32_MAX; | ||
541 | td->eta_video = 0; | ||
542 | td->eta_early = 0; | ||
543 | td->eta_late = 0; | ||
544 | td->frame_drop_level = 0; | ||
545 | td->skip_level = 0; | ||
546 | td->num_drawn = 0; | ||
547 | td->num_skipped = 0; | ||
548 | td->last_showfps = *rb->current_tick - HZ; | ||
549 | td->last_render = td->last_showfps; | ||
550 | |||
551 | reply = true; | ||
552 | break; | ||
553 | |||
554 | case STREAM_NEEDS_SYNC: | ||
555 | reply = check_needs_sync(td, td->ev.data); | ||
556 | break; | ||
557 | |||
558 | case STREAM_SYNC: | ||
559 | if (td->state == TSTATE_INIT) | ||
560 | reply = sync_decoder(td, (struct str_sync_data *)td->ev.data); | ||
561 | break; | ||
562 | |||
563 | case DISK_BUF_DATA_NOTIFY: | ||
564 | /* Our bun is done */ | ||
565 | if (td->state != TSTATE_DATA) | ||
566 | break; | ||
567 | |||
568 | td->state = TSTATE_DECODE; | ||
569 | str_data_notify_received(&video_str); | ||
570 | break; | ||
571 | |||
572 | case VIDEO_PRINT_THUMBNAIL: | ||
573 | /* Print a thumbnail of whatever was last decoded - scale and | ||
574 | * position to fill the specified rectangle */ | ||
575 | if (td->info != NULL && td->info->display_fbuf != NULL) | ||
576 | { | ||
577 | vo_draw_frame_thumb(td->info->display_fbuf->buf, | ||
578 | (struct vo_rect *)td->ev.data); | ||
579 | reply = true; | ||
580 | } | ||
581 | break; | ||
582 | |||
583 | case VIDEO_PRINT_FRAME: | ||
584 | /* Print the last frame decoded */ | ||
585 | if (td->info != NULL && td->info->display_fbuf != NULL) | ||
586 | { | ||
587 | vo_draw_frame(td->info->display_fbuf->buf); | ||
588 | reply = true; | ||
589 | } | ||
590 | break; | ||
591 | |||
592 | case VIDEO_GET_SIZE: | ||
593 | { | ||
594 | if (td->state != TSTATE_INIT) | ||
595 | break; | ||
596 | |||
597 | if (init_sequence(td)) | ||
598 | { | ||
599 | reply = true; | ||
600 | vo_dimensions((struct vo_ext *)td->ev.data); | ||
601 | } | ||
602 | break; | ||
603 | } | ||
604 | |||
605 | case STREAM_FIND_END_TIME: | ||
606 | if (td->state != TSTATE_INIT) | ||
607 | { | ||
608 | reply = STREAM_ERROR; | ||
609 | break; | ||
610 | } | ||
611 | |||
612 | reply = video_str_scan(td, (struct str_sync_data *)td->ev.data); | ||
613 | break; | ||
614 | |||
615 | #ifdef GRAY_CACHE_MAINT | ||
616 | case VIDEO_GRAY_CACHEOP: | ||
617 | td->ev.data ? | ||
618 | GRAY_INVALIDATE_ICACHE() : | ||
619 | GRAY_FLUSH_ICACHE(); | ||
620 | break; | ||
621 | #endif | ||
622 | |||
623 | case STREAM_QUIT: | ||
624 | /* Time to go - make thread exit */ | ||
625 | td->state = TSTATE_EOS; | ||
626 | return; | ||
627 | } | ||
628 | |||
629 | str_reply_msg(&video_str, reply); | ||
630 | |||
631 | if (td->status == STREAM_PLAYING) | ||
632 | { | ||
633 | switch (td->state) | ||
634 | { | ||
635 | case TSTATE_DECODE: | ||
636 | case TSTATE_RENDER: | ||
637 | case TSTATE_RENDER_WAIT: | ||
638 | /* These return when in playing state */ | ||
639 | return; | ||
640 | } | ||
641 | } | ||
642 | |||
643 | str_get_msg(&video_str, &td->ev); | ||
644 | } | ||
645 | } | ||
646 | |||
647 | static void video_thread(void) | ||
648 | { | ||
649 | struct video_thread_data td; | ||
650 | |||
651 | td.status = STREAM_STOPPED; | ||
652 | td.state = TSTATE_EOS; | ||
653 | td.mpeg2dec = mpeg2_init(); | ||
654 | td.info = NULL; | ||
655 | td.syncf_perfect = 0; | ||
656 | td.syncf_time = 0; | ||
657 | td.syncf_period = 0; | ||
658 | |||
659 | if (td.mpeg2dec == NULL) | ||
660 | { | ||
661 | td.status = STREAM_ERROR; | ||
662 | /* Loop and wait for quit message */ | ||
663 | while (1) | ||
664 | { | ||
665 | str_get_msg(&video_str, &td.ev); | ||
666 | if (td.ev.id == STREAM_QUIT) | ||
667 | return; | ||
668 | str_reply_msg(&video_str, STREAM_ERROR); | ||
669 | } | ||
670 | } | ||
671 | |||
672 | vo_init(); | ||
673 | |||
674 | goto message_wait; | ||
675 | |||
676 | while (1) | ||
677 | { | ||
678 | mpeg2_state_t mp2state; | ||
679 | td.state = TSTATE_DECODE; | ||
680 | |||
681 | /* Check for any pending messages and process them */ | ||
682 | if (str_have_msg(&video_str)) | ||
683 | { | ||
684 | message_wait: | ||
685 | /* Wait for a message to be queued */ | ||
686 | str_get_msg(&video_str, &td.ev); | ||
687 | |||
688 | message_process: | ||
689 | /* Process a message already dequeued */ | ||
690 | video_thread_msg(&td); | ||
691 | |||
692 | switch (td.state) | ||
693 | { | ||
694 | /* These states are the only ones that should return */ | ||
695 | case TSTATE_DECODE: goto picture_decode; | ||
696 | case TSTATE_RENDER: goto picture_draw; | ||
697 | case TSTATE_RENDER_WAIT: goto picture_wait; | ||
698 | /* Anything else is interpreted as an exit */ | ||
699 | default: | ||
700 | vo_cleanup(); | ||
701 | mpeg2_close(td.mpeg2dec); | ||
702 | return; | ||
703 | } | ||
704 | } | ||
705 | |||
706 | picture_decode: | ||
707 | mp2state = mpeg2_parse (td.mpeg2dec); | ||
708 | rb->yield(); | ||
709 | |||
710 | switch (mp2state) | ||
711 | { | ||
712 | case STATE_BUFFER: | ||
713 | /* Request next packet data */ | ||
714 | switch (parser_get_next_data(&video_str, STREAM_PM_STREAMING)) | ||
715 | { | ||
716 | case STREAM_DATA_NOT_READY: | ||
717 | /* Wait for data to be buffered */ | ||
718 | td.state = TSTATE_DATA; | ||
719 | goto message_wait; | ||
720 | |||
721 | case STREAM_DATA_END: | ||
722 | /* No more data. */ | ||
723 | td.state = TSTATE_EOS; | ||
724 | if (td.status == STREAM_PLAYING) | ||
725 | stream_generate_event(&video_str, STREAM_EV_COMPLETE, 0); | ||
726 | goto message_wait; | ||
727 | |||
728 | case STREAM_OK: | ||
729 | if (video_str.pkt_flags & PKT_HAS_TS) | ||
730 | mpeg2_tag_picture(td.mpeg2dec, video_str.pts, 0); | ||
731 | |||
732 | mpeg2_buffer(td.mpeg2dec, video_str.curr_packet, | ||
733 | video_str.curr_packet_end); | ||
734 | td.info = mpeg2_info(td.mpeg2dec); | ||
735 | break; | ||
736 | } | ||
737 | break; | ||
738 | |||
739 | case STATE_SEQUENCE: | ||
740 | /* New video sequence, inform output of any changes */ | ||
741 | vo_setup(td.info->sequence); | ||
742 | break; | ||
743 | |||
744 | case STATE_PICTURE: | ||
745 | { | ||
746 | int skip = 0; /* Assume no skip */ | ||
747 | |||
748 | if (td.frame_drop_level >= 1 || td.skip_level > 0) | ||
749 | { | ||
750 | /* A frame will be dropped in the decoder */ | ||
751 | |||
752 | /* Frame type: I/P/B/D */ | ||
753 | int type = td.info->current_picture->flags | ||
754 | & PIC_MASK_CODING_TYPE; | ||
755 | |||
756 | switch (type) | ||
757 | { | ||
758 | case PIC_FLAG_CODING_TYPE_I: | ||
759 | case PIC_FLAG_CODING_TYPE_D: | ||
760 | /* Level 5: Things are extremely late and all frames will | ||
761 | be dropped until the next key frame */ | ||
762 | if (td.frame_drop_level >= 1) | ||
763 | td.frame_drop_level = 0; /* Key frame - reset drop level */ | ||
764 | if (td.skip_level >= 5) | ||
765 | { | ||
766 | td.frame_drop_level = 1; | ||
767 | td.skip_level = 0; /* reset */ | ||
768 | } | ||
769 | break; | ||
770 | case PIC_FLAG_CODING_TYPE_P: | ||
771 | /* Level 4: Things are very late and all frames will be | ||
772 | dropped until the next key frame */ | ||
773 | if (td.skip_level >= 4) | ||
774 | { | ||
775 | td.frame_drop_level = 1; | ||
776 | td.skip_level = 0; /* reset */ | ||
777 | } | ||
778 | break; | ||
779 | case PIC_FLAG_CODING_TYPE_B: | ||
780 | /* We want to drop something, so this B frame won't even | ||
781 | be decoded. Drawing can happen on the next frame if so | ||
782 | desired. Bring the level down as skips are done. */ | ||
783 | skip = 1; | ||
784 | if (td.skip_level > 0) | ||
785 | td.skip_level--; | ||
786 | } | ||
787 | |||
788 | skip |= td.frame_drop_level; | ||
789 | } | ||
790 | |||
791 | mpeg2_skip(td.mpeg2dec, skip); | ||
792 | break; | ||
793 | } | ||
794 | |||
795 | case STATE_SLICE: | ||
796 | case STATE_END: | ||
797 | case STATE_INVALID_END: | ||
798 | { | ||
799 | int32_t offset; /* Tick adjustment to keep sync */ | ||
800 | |||
801 | /* draw current picture */ | ||
802 | if (td.info->display_fbuf == NULL) | ||
803 | break; /* No picture */ | ||
804 | |||
805 | /* Get presentation times in audio samples - quite accurate | ||
806 | enough - add previous frame duration if not stamped */ | ||
807 | td.curr_time = (td.info->display_picture->flags & PIC_FLAG_TAGS) ? | ||
808 | TS_TO_TICKS(td.info->display_picture->tag) : | ||
809 | (td.curr_time + td.period); | ||
810 | |||
811 | td.period = TC_TO_TICKS(td.info->sequence->frame_period); | ||
812 | |||
813 | /* No limiting => no dropping - draw this frame */ | ||
814 | if (!settings.limitfps) | ||
815 | { | ||
816 | goto picture_draw; | ||
817 | } | ||
818 | |||
819 | td.eta_video = td.curr_time; | ||
820 | td.eta_stream = stream_get_time(); | ||
821 | |||
822 | /* How early/late are we? > 0 = late, < 0 early */ | ||
823 | offset = td.eta_stream - td.eta_video; | ||
824 | |||
825 | if (!settings.skipframes) | ||
826 | { | ||
827 | /* Make no effort to determine whether this frame should be | ||
828 | drawn or not since no action can be taken to correct the | ||
829 | situation. We'll just wait if we're early and correct for | ||
830 | lateness as much as possible. */ | ||
831 | if (offset < 0) | ||
832 | offset = 0; | ||
833 | |||
834 | td.eta_late = AVERAGE(td.eta_late, offset, 4); | ||
835 | offset = td.eta_late; | ||
836 | |||
837 | if ((uint32_t)offset > td.eta_video) | ||
838 | offset = td.eta_video; | ||
839 | |||
840 | td.eta_video -= offset; | ||
841 | goto picture_wait; | ||
842 | } | ||
843 | |||
844 | /** Possibly skip this frame **/ | ||
845 | |||
846 | /* Frameskipping has the following order of preference: | ||
847 | * | ||
848 | * Frame Type Who Notes/Rationale | ||
849 | * B decoder arbitrarily drop - no decode or draw | ||
850 | * Any renderer arbitrarily drop - will be I/D/P | ||
851 | * P decoder must wait for I/D-frame - choppy | ||
852 | * I/D decoder must wait for I/D-frame - choppy | ||
853 | * | ||
854 | * If a frame can be drawn and it has been at least 1/2 second, | ||
855 | * the image will be updated no matter how late it is just to | ||
856 | * avoid looking stuck. | ||
857 | */ | ||
858 | |||
859 | /* If we're late, set the eta to play the frame early so | ||
860 | we may catch up. If early, especially because of a drop, | ||
861 | mitigate a "snap" by moving back gradually. */ | ||
862 | if (offset >= 0) /* late or on time */ | ||
863 | { | ||
864 | td.eta_early = 0; /* Not early now :( */ | ||
865 | |||
866 | td.eta_late = AVERAGE(td.eta_late, offset, 4); | ||
867 | offset = td.eta_late; | ||
868 | |||
869 | if ((uint32_t)offset > td.eta_video) | ||
870 | offset = td.eta_video; | ||
871 | |||
872 | td.eta_video -= offset; | ||
873 | } | ||
874 | else | ||
875 | { | ||
876 | td.eta_late = 0; /* Not late now :) */ | ||
877 | |||
878 | if (offset > td.eta_early) | ||
879 | { | ||
880 | /* Just dropped a frame and we're now early or we're | ||
881 | coming back from being early */ | ||
882 | td.eta_early = offset; | ||
883 | if ((uint32_t)-offset > td.eta_video) | ||
884 | offset = -td.eta_video; | ||
885 | |||
886 | td.eta_video += offset; | ||
887 | } | ||
888 | else | ||
889 | { | ||
890 | /* Just early with an offset, do exponential drift back */ | ||
891 | if (td.eta_early != 0) | ||
892 | { | ||
893 | td.eta_early = AVERAGE(td.eta_early, 0, 8); | ||
894 | td.eta_video = ((uint32_t)-td.eta_early > td.eta_video) ? | ||
895 | 0 : (td.eta_video + td.eta_early); | ||
896 | } | ||
897 | |||
898 | offset = td.eta_early; | ||
899 | } | ||
900 | } | ||
901 | |||
902 | if (td.info->display_picture->flags & PIC_FLAG_SKIP) | ||
903 | { | ||
904 | /* This frame was set to skip so skip it after having updated | ||
905 | timing information */ | ||
906 | td.num_skipped++; | ||
907 | td.eta_early = INT32_MIN; | ||
908 | goto picture_skip; | ||
909 | } | ||
910 | |||
911 | if (td.skip_level == 3 && | ||
912 | TIME_BEFORE(*rb->current_tick, td.last_render + HZ/2)) | ||
913 | { | ||
914 | /* Render drop was set previously but nothing was dropped in the | ||
915 | decoder or it's been to long since drawing the last frame. */ | ||
916 | td.skip_level = 0; | ||
917 | td.num_skipped++; | ||
918 | td.eta_early = INT32_MIN; | ||
919 | goto picture_skip; | ||
920 | } | ||
921 | |||
922 | /* At this point a frame _will_ be drawn - a skip may happen on | ||
923 | the next however */ | ||
924 | td.skip_level = 0; | ||
925 | |||
926 | if (offset > CLOCK_RATE*110/1000) | ||
927 | { | ||
928 | /* Decide which skip level is needed in order to catch up */ | ||
929 | |||
930 | /* TODO: Calculate this rather than if...else - this is rather | ||
931 | exponential though */ | ||
932 | if (offset > CLOCK_RATE*367/1000) | ||
933 | td.skip_level = 5; /* Decoder skip: I/D */ | ||
934 | if (offset > CLOCK_RATE*233/1000) | ||
935 | td.skip_level = 4; /* Decoder skip: P */ | ||
936 | else if (offset > CLOCK_RATE*167/1000) | ||
937 | td.skip_level = 3; /* Render skip */ | ||
938 | else if (offset > CLOCK_RATE*133/1000) | ||
939 | td.skip_level = 2; /* Decoder skip: B */ | ||
940 | else | ||
941 | td.skip_level = 1; /* Decoder skip: B */ | ||
942 | } | ||
943 | |||
944 | picture_wait: | ||
945 | td.state = TSTATE_RENDER_WAIT; | ||
946 | |||
947 | /* Wait until time catches up */ | ||
948 | while (td.eta_video > td.eta_stream) | ||
949 | { | ||
950 | /* Watch for messages while waiting for the frame time */ | ||
951 | int32_t eta_remaining = td.eta_video - td.eta_stream; | ||
952 | if (eta_remaining > CLOCK_RATE/HZ) | ||
953 | { | ||
954 | /* Several ticks to wait - do some sleeping */ | ||
955 | int timeout = (eta_remaining - HZ) / (CLOCK_RATE/HZ); | ||
956 | str_get_msg_w_tmo(&video_str, &td.ev, MAX(timeout, 1)); | ||
957 | if (td.ev.id != SYS_TIMEOUT) | ||
958 | goto message_process; | ||
959 | } | ||
960 | else | ||
961 | { | ||
962 | /* Just a little left - spin and be accurate */ | ||
963 | rb->priority_yield(); | ||
964 | if (str_have_msg(&video_str)) | ||
965 | goto message_wait; | ||
966 | } | ||
967 | |||
968 | td.eta_stream = stream_get_time(); | ||
969 | } | ||
970 | |||
971 | picture_draw: | ||
972 | /* Record last frame time */ | ||
973 | td.last_render = *rb->current_tick; | ||
974 | vo_draw_frame(td.info->display_fbuf->buf); | ||
975 | td.num_drawn++; | ||
976 | |||
977 | picture_skip: | ||
978 | if (!settings.showfps) | ||
979 | break; | ||
980 | |||
981 | if (TIME_BEFORE(*rb->current_tick, td.last_showfps + HZ)) | ||
982 | break; | ||
983 | |||
984 | /* Calculate and display fps */ | ||
985 | draw_fps(&td); | ||
986 | break; | ||
987 | } | ||
988 | |||
989 | default: | ||
990 | break; | ||
991 | } | ||
992 | |||
993 | rb->yield(); | ||
994 | } /* end while */ | ||
995 | } | ||
996 | |||
997 | /* Initializes the video thread */ | ||
998 | bool video_thread_init(void) | ||
999 | { | ||
1000 | intptr_t rep; | ||
1001 | |||
1002 | IF_COP(flush_icache()); | ||
1003 | |||
1004 | video_str.hdr.q = &video_str_queue; | ||
1005 | rb->queue_init(video_str.hdr.q, false); | ||
1006 | rb->queue_enable_queue_send(video_str.hdr.q, &video_str_queue_send); | ||
1007 | |||
1008 | /* We put the video thread on another processor for multi-core targets. */ | ||
1009 | video_str.thread = rb->create_thread( | ||
1010 | video_thread, video_stack, VIDEO_STACKSIZE, 0, | ||
1011 | "mpgvideo" IF_PRIO(,PRIORITY_PLAYBACK) IF_COP(, COP)); | ||
1012 | |||
1013 | if (video_str.thread == NULL) | ||
1014 | return false; | ||
1015 | |||
1016 | /* Wait for thread to initialize */ | ||
1017 | rep = str_send_msg(&video_str, STREAM_NULL, 0); | ||
1018 | IF_COP(invalidate_icache()); | ||
1019 | |||
1020 | return rep == 0; /* Normally STREAM_NULL should be ignored */ | ||
1021 | } | ||
1022 | |||
1023 | /* Terminates the video thread */ | ||
1024 | void video_thread_exit(void) | ||
1025 | { | ||
1026 | if (video_str.thread != NULL) | ||
1027 | { | ||
1028 | str_post_msg(&video_str, STREAM_QUIT, 0); | ||
1029 | rb->thread_wait(video_str.thread); | ||
1030 | IF_COP(invalidate_icache()); | ||
1031 | video_str.thread = NULL; | ||
1032 | } | ||
1033 | else | ||
1034 | { | ||
1035 | /* Some things were done before thread creation */ | ||
1036 | #ifndef HAVE_LCD_COLOR | ||
1037 | gray_release(); | ||
1038 | #endif | ||
1039 | } | ||
1040 | } | ||