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