summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer/video_thread.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-12-29 19:46:35 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-12-29 19:46:35 +0000
commita222f27c4a17ed8f9809cda7861fe5f23d4cc0c1 (patch)
treed393a23d83549f99772bb156e59ffb88725148b6 /apps/plugins/mpegplayer/video_thread.c
parent1d0f6b90ff43776e55b4b9f062c9bea3f99aa768 (diff)
downloadrockbox-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.c1040
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 */
31struct 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)
58static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR;
59static struct event_queue video_str_queue NOCACHEBSS_ATTR;
60static struct queue_sender_list video_str_queue_send NOCACHEBSS_ATTR;
61struct stream video_str IBSS_ATTR;
62
63static 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)
84static 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 */
106static 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
188scan_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
207static 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
219static 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 */
252static 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
446sync_finished:
447 mpeg2_skip(td->mpeg2dec, 0);
448 return retval;
449}
450
451/* This only returns to play or quit */
452static 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
647static 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 */
998bool 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 */
1024void 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}