summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer/mpeg_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/mpegplayer/mpeg_parser.c')
-rw-r--r--apps/plugins/mpegplayer/mpeg_parser.c1203
1 files changed, 1203 insertions, 0 deletions
diff --git a/apps/plugins/mpegplayer/mpeg_parser.c b/apps/plugins/mpegplayer/mpeg_parser.c
new file mode 100644
index 0000000000..cc57b0c43c
--- /dev/null
+++ b/apps/plugins/mpegplayer/mpeg_parser.c
@@ -0,0 +1,1203 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Parser for MPEG streams
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
26struct stream_parser str_parser SHAREDBSS_ATTR;
27
28static void parser_init_state(void)
29{
30 str_parser.last_seek_time = 0;
31 str_parser.format = STREAM_FMT_UNKNOWN;
32 str_parser.start_pts = INVALID_TIMESTAMP;
33 str_parser.end_pts = INVALID_TIMESTAMP;
34 str_parser.flags = 0;
35 str_parser.dims.w = 0;
36 str_parser.dims.h = 0;
37}
38
39/* Place the stream in a state to begin parsing - sync will be performed
40 * first */
41void str_initialize(struct stream *str, off_t pos)
42{
43 /* Initial positions start here */
44 str->hdr.win_left = str->hdr.win_right = pos;
45 /* No packet */
46 str->curr_packet = NULL;
47 /* Pick up parsing from this point in the buffer */
48 str->curr_packet_end = disk_buf_offset2ptr(pos);
49 /* No flags */
50 str->pkt_flags = 0;
51 /* Sync first */
52 str->state = SSTATE_SYNC;
53}
54
55/* Place the stream in an end of data state */
56void str_end_of_stream(struct stream *str)
57{
58 /* Offsets that prevent this stream from being included in the
59 * min left/max right window so that no buffering is triggered on
60 * its behalf. Set right to the min first so a thread reading the
61 * overall window gets doesn't see this as valid no matter what the
62 * file length. */
63 str->hdr.win_right = OFF_T_MIN;
64 str->hdr.win_left = OFF_T_MAX;
65 /* No packets */
66 str->curr_packet = str->curr_packet_end = NULL;
67 /* No flags */
68 str->pkt_flags = 0;
69 /* Fin */
70 str->state = SSTATE_END;
71}
72
73/* Return a timestamp at address p+offset if the marker bits are in tact */
74static inline uint32_t read_pts(uint8_t *p, off_t offset)
75{
76 return TS_CHECK_MARKERS(p, offset) ?
77 TS_FROM_HEADER(p, offset) : INVALID_TIMESTAMP;
78}
79
80static inline bool validate_timestamp(uint32_t ts)
81{
82 return ts >= str_parser.start_pts && ts <= str_parser.end_pts;
83}
84
85/* Find a start code before or after a given position */
86uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code)
87{
88 stream_scan_normalize(sk);
89
90 if (sk->dir < 0)
91 {
92 /* Reverse scan - start with at least the min needed */
93 stream_scan_offset(sk, 4);
94 }
95
96 code &= 0xff; /* Only the low byte matters */
97
98 while (sk->len >= 0 && sk->margin >= 4)
99 {
100 uint8_t *p;
101 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
102 ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p);
103
104 if (pos < 0 || len < 4)
105 break;
106
107 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == code)
108 {
109 return p;
110 }
111
112 stream_scan_offset(sk, 1);
113 }
114
115 return NULL;
116}
117
118/* Find a PES packet header for any stream - return stream to which it
119 * belongs */
120unsigned mpeg_parser_scan_pes(struct stream_scan *sk)
121{
122 stream_scan_normalize(sk);
123
124 if (sk->dir < 0)
125 {
126 /* Reverse scan - start with at least the min needed */
127 stream_scan_offset(sk, 4);
128 }
129
130 while (sk->len >= 0 && sk->margin >= 4)
131 {
132 uint8_t *p;
133 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
134 ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p);
135
136 if (pos < 0 || len < 4)
137 break;
138
139 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
140 {
141 unsigned id = p[3];
142 if (id >= 0xb9)
143 return id; /* PES header */
144 /* else some video stream element */
145 }
146
147 stream_scan_offset(sk, 1);
148 }
149
150 return -1;
151}
152
153/* Return the first SCR found from the scan direction */
154uint32_t mpeg_parser_scan_scr(struct stream_scan *sk)
155{
156 uint8_t *p = mpeg_parser_scan_start_code(sk, MPEG_STREAM_PACK_HEADER);
157
158 if (p != NULL && sk->margin >= 9) /* 9 bytes total required */
159 {
160 sk->data = 9;
161
162 if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */
163 {
164 /* Lookhead p+8 */
165 if (MPEG2_CHECK_PACK_SCR_MARKERS(p, 4))
166 return MPEG2_PACK_HEADER_SCR(p, 4);
167 }
168 else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */
169 {
170 /* Lookahead p+8 */
171 if (TS_CHECK_MARKERS(p, 4))
172 return TS_FROM_HEADER(p, 4);
173 }
174 /* Weird pack header */
175 sk->data = 5;
176 }
177
178 return INVALID_TIMESTAMP;
179}
180
181uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id)
182{
183 stream_scan_normalize(sk);
184
185 if (sk->dir < 0)
186 {
187 /* Reverse scan - start with at least the min needed */
188 stream_scan_offset(sk, 4);
189 }
190
191 while (sk->len >= 0 && sk->margin >= 4)
192 {
193 uint8_t *p;
194 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
195 ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 30, &p);
196
197 if (pos < 0 || len < 4)
198 break;
199
200 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == id)
201 {
202 uint8_t *h = p;
203
204 if (sk->margin < 7)
205 {
206 /* Insufficient data */
207 }
208 else if ((h[6] & 0xc0) == 0x80) /* mpeg2 */
209 {
210 if (sk->margin >= 14 && (h[7] & 0x80) != 0x00)
211 {
212 sk->data = 14;
213 return read_pts(h, 9);
214 }
215 }
216 else /* mpeg1 */
217 {
218 ssize_t l = 6;
219 ssize_t margin = sk->margin;
220
221 /* Skip stuffing_byte */
222 while (margin > 7 && h[l] == 0xff && ++l <= 22)
223 --margin;
224
225 if (margin >= 7)
226 {
227 if ((h[l] & 0xc0) == 0x40)
228 {
229 /* Skip STD_buffer_scale and STD_buffer_size */
230 margin -= 2;
231 l += 2;
232 }
233
234 if (margin >= 5)
235 {
236 /* Header points to the mpeg1 pes header */
237 h += l;
238
239 if ((h[0] & 0xe0) == 0x20)
240 {
241 /* PTS or PTS_DTS indicated */
242 sk->data = (h + 5) - p;
243 return read_pts(h, 0);
244 }
245 }
246 }
247 }
248 /* No PTS present - keep searching for a matching PES header with
249 * one */
250 }
251
252 stream_scan_offset(sk, 1);
253 }
254
255 return INVALID_TIMESTAMP;
256}
257
258static bool init_video_info(void)
259{
260 DEBUGF("Getting movie size\n");
261
262 /* The decoder handles this in order to initialize its knowledge of the
263 * movie parameters making seeking easier */
264 str_send_msg(&video_str, STREAM_RESET, 0);
265 if (str_send_msg(&video_str, VIDEO_GET_SIZE,
266 (intptr_t)&str_parser.dims) != 0)
267 {
268 return true;
269 }
270
271 DEBUGF(" failed\n");
272 return false;
273}
274
275static bool init_times(struct stream *str)
276{
277 struct stream tmp_str;
278 const ssize_t filesize = disk_buf_filesize();
279 const ssize_t max_probe = MIN(512*1024, filesize);
280 bool found_stream;
281
282 /* Simply find the first earliest timestamp - this will be the one
283 * used when streaming anyway */
284 DEBUGF("Finding start_pts: 0x%02x\n", str->id);
285
286 found_stream = false;
287 str->start_pts = INVALID_TIMESTAMP;
288 str->end_pts = INVALID_TIMESTAMP;
289
290 tmp_str.id = str->id;
291 tmp_str.hdr.pos = 0;
292 tmp_str.hdr.limit = max_probe;
293
294 /* Probe for many for the start because some stamps could be anomalous.
295 * Video also can also have things out of order. Just see what it's got.
296 */
297 while (1)
298 {
299 switch (parser_get_next_data(&tmp_str, STREAM_PM_RANDOM_ACCESS))
300 {
301 case STREAM_DATA_END:
302 break;
303 case STREAM_OK:
304 found_stream = true;
305 if (tmp_str.pkt_flags & PKT_HAS_TS)
306 {
307 if (tmp_str.pts < str->start_pts)
308 str->start_pts = tmp_str.pts;
309 }
310 continue;
311 }
312
313 break;
314 }
315
316 if (!found_stream)
317 {
318 DEBUGF(" stream not found:0x%02x\n", str->id);
319 return false;
320 }
321
322 DEBUGF(" start:%u\n", (unsigned)str->start_pts);
323
324 /* Use the decoder thread to perform a synchronized search - no
325 * decoding should take place but just a simple run through timestamps
326 * and durations as the decoder would see them. This should give the
327 * precise time at the end of the last frame for the stream. */
328 DEBUGF("Finding end_pts: 0x%02x\n", str->id);
329
330 str_parser.parms.sd.time = MAX_TIMESTAMP;
331 str_parser.parms.sd.sk.pos = filesize - max_probe;
332 str_parser.parms.sd.sk.len = max_probe;
333 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
334
335 str_send_msg(str, STREAM_RESET, 0);
336
337 if (str_send_msg(str, STREAM_FIND_END_TIME,
338 (intptr_t)&str_parser.parms.sd) == STREAM_PERFECT_MATCH)
339 {
340 str->end_pts = str_parser.parms.sd.time;
341 DEBUGF(" end:%u\n", (unsigned)str->end_pts);
342 }
343
344 return true;
345}
346
347static bool check_times(const struct stream *str)
348{
349 return str->start_pts < str->end_pts &&
350 str->end_pts != INVALID_TIMESTAMP;
351}
352
353/* Return the best-fit file offset of a timestamp in the PES where
354 * timstamp <= time < next timestamp. Will try to return something reasonably
355 * valid if best-fit could not be made. */
356static off_t mpeg_parser_seek_PTS(uint32_t time, unsigned id)
357{
358 ssize_t pos_left = 0;
359 ssize_t pos_right = disk_buf.filesize;
360 ssize_t pos, pos_new;
361 uint32_t time_left = str_parser.start_pts;
362 uint32_t time_right = str_parser.end_pts;
363 uint32_t pts = 0;
364 uint32_t prevpts = 0;
365 enum state_enum state = STATE0;
366 struct stream_scan sk;
367
368 stream_scan_init(&sk);
369
370 /* Initial estimate taken from average bitrate - later interpolations are
371 * taken similarly based on the remaining file interval */
372 pos_new = muldiv_uint32(time - time_left, pos_right - pos_left,
373 time_right - time_left) + pos_left;
374
375 /* return this estimated position if nothing better comes up */
376 pos = pos_new;
377
378 DEBUGF("Seeking stream 0x%02x\n", id);
379 DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n",
380 (unsigned)time_left, (unsigned)time, (unsigned)time_right,
381 (long)pos_left, (long)pos_new, (long)pos_right);
382
383 sk.dir = SSCAN_REVERSE;
384
385 while (state < STATE9)
386 {
387 uint32_t currpts;
388 sk.pos = pos_new;
389 sk.len = (sk.dir < 0) ? pos_new - pos_left : pos_right - pos_new;
390
391 currpts = mpeg_parser_scan_pts(&sk, id);
392
393 if (currpts != INVALID_TIMESTAMP)
394 {
395 ssize_t pos_adj; /* Adjustment to over or under-estimate */
396
397 /* Found a valid timestamp - see were it lies in relation to
398 * target */
399 if (currpts < time)
400 {
401 /* Time at current position is before seek time - move
402 * forward */
403 if (currpts > pts)
404 {
405 /* This is less than the desired time but greater than
406 * the currently seeked one; move the position up */
407 pts = currpts;
408 pos = sk.pos;
409 }
410
411 /* No next timestamp can be sooner */
412 pos_left = sk.pos + sk.data;
413 time_left = currpts;
414
415 if (pos_right <= pos_left)
416 break; /* If the window disappeared - we're done */
417
418 pos_new = muldiv_uint32(time - time_left,
419 pos_right - pos_left,
420 time_right - time_left);
421 /* Point is ahead of us - fudge estimate a bit high */
422 pos_adj = pos_new / 10;
423
424 if (pos_adj > 512*1024)
425 pos_adj = 512*1024;
426
427 pos_new += pos_left + pos_adj;
428
429 if (pos_new >= pos_right)
430 {
431 /* Estimate could push too far */
432 pos_new = pos_right;
433 }
434
435 state = STATE2; /* Last scan was early */
436 sk.dir = SSCAN_REVERSE;
437
438 DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
439 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
440 (unsigned)time_right, (long)pos_left, (long)pos_new,
441 (long)pos_right);
442 }
443 else if (currpts > time)
444 {
445 /* Time at current position is past seek time - move
446 backward */
447 pos_right = sk.pos;
448 time_right = currpts;
449
450 if (pos_right <= pos_left)
451 break; /* If the window disappeared - we're done */
452
453 pos_new = muldiv_uint32(time - time_left,
454 pos_right - pos_left,
455 time_right - time_left);
456 /* Overshot the seek point - fudge estimate a bit low */
457 pos_adj = pos_new / 10;
458
459 if (pos_adj > 512*1024)
460 pos_adj = 512*1024;
461
462 pos_new += pos_left - pos_adj;
463
464 state = STATE3; /* Last scan was late */
465 sk.dir = SSCAN_REVERSE;
466
467 DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
468 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
469 (unsigned)time_right, (long)pos_left, (long)pos_new,
470 (long)pos_right);
471 }
472 else
473 {
474 /* Exact match - it happens */
475 DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
476 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
477 (unsigned)time_right, (long)pos_left, (long)pos_new,
478 (long)pos_right);
479 pts = currpts;
480 pos = sk.pos;
481 state = STATE9;
482 }
483 }
484 else
485 {
486 /* Nothing found */
487
488 switch (state)
489 {
490 case STATE1:
491 /* We already tried the bruteforce scan and failed again - no
492 * more stamps could possibly exist in the interval */
493 DEBUGF("!! no timestamp 2x\n");
494 break;
495 case STATE0:
496 /* Hardly likely except at very beginning - just do L->R scan
497 * to find something */
498 DEBUGF("!! no timestamp on first probe: %ld\n", sk.pos);
499 case STATE2:
500 case STATE3:
501 /* Could just be missing timestamps because the interval is
502 * narrowing down. A large block of data from another stream
503 * may also be in the midst of our chosen points which could
504 * cluster at either extreme end. If anything is there, this
505 * will find it. */
506 pos_new = pos_left;
507 sk.dir = SSCAN_FORWARD;
508 DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
509 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
510 (unsigned)time_right, (long)pos_left, (long)pos_new,
511 (long)pos_right);
512 state = STATE1;
513 break;
514 default:
515 DEBUGF("?? Invalid state: %d\n", state);
516 }
517 }
518
519 /* Same timestamp twice = quit */
520 if (currpts == prevpts)
521 {
522 DEBUGF("!! currpts == prevpts (stop)\n");
523 state = STATE9;
524 }
525
526 prevpts = currpts;
527 }
528
529#if defined(DEBUG) || defined(SIMULATOR)
530 /* The next pts after the seeked-to position should be greater -
531 * most of the time - frames out of presentation order may muck it
532 * up a slight bit */
533 sk.pos = pos + 1;
534 sk.len = disk_buf.filesize;
535 sk.dir = SSCAN_FORWARD;
536
537 uint32_t nextpts = mpeg_parser_scan_pts(&sk, id);
538 DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n",
539 (long)pos, (unsigned)pts, (unsigned)time, (unsigned)nextpts);
540
541 if (pts <= time && time < nextpts)
542 {
543 /* Smile - it worked */
544 DEBUGF(" :) pts<=time<next pts\n");
545 }
546 else
547 {
548 /* See where things ended up */
549 if (pts > time)
550 {
551 /* Hmm */
552 DEBUGF(" :\\ pts>time\n");
553 }
554 if (pts >= nextpts)
555 {
556 /* Weird - probably because of encoded order & tends to be right
557 * anyway if other criteria are met */
558 DEBUGF(" :p pts>=next pts\n");
559 }
560 if (time >= nextpts)
561 {
562 /* Ugh */
563 DEBUGF(" :( time>=nextpts\n");
564 }
565 }
566#endif
567
568 return pos;
569}
570
571static void prepare_audio(uint32_t time)
572{
573 off_t pos;
574
575 if (!str_send_msg(&audio_str, STREAM_NEEDS_SYNC, time))
576 {
577 DEBUGF("Audio was ready\n");
578 return;
579 }
580
581 pos = mpeg_parser_seek_PTS(time, audio_str.id);
582 str_send_msg(&audio_str, STREAM_RESET, 0);
583
584 str_parser.parms.sd.time = time;
585 str_parser.parms.sd.sk.pos = pos;
586 str_parser.parms.sd.sk.len = 1024*1024;
587 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
588
589 str_send_msg(&audio_str, STREAM_SYNC, (intptr_t)&str_parser.parms.sd);
590}
591
592/* This function demuxes the streams and gives the next stream data
593 * pointer.
594 *
595 * STREAM_PM_STREAMING is for operation during playback. If the nescessary
596 * data and worst-case lookahead margin is not available, the stream is
597 * registered for notification when the data becomes available. If parsing
598 * extends beyond the end of the file or the end of stream marker is reached,
599 * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
600 *
601 * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
602 * If the file cache misses for the current position + lookahead, it will be
603 * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
604 * returned.
605 *
606 * The results from one mode may be used as input to the other. Random access
607 * requires cooperation amongst threads to avoid evicting another stream's
608 * data.
609 */
610static int parse_demux(struct stream *str, enum stream_parse_mode type)
611{
612 #define INC_BUF(offset) \
613 ({ off_t _o = (offset); \
614 str->hdr.win_right += _o; \
615 if ((p += _o) >= disk_buf.end) \
616 p -= disk_buf.size; })
617
618 static const int mpeg1_skip_table[16] =
619 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
620
621 uint8_t *p = str->curr_packet_end;
622
623 str->pkt_flags = 0;
624
625 while (1)
626 {
627 uint8_t *header;
628 unsigned id;
629 ssize_t length, bytes;
630
631 switch (type)
632 {
633 case STREAM_PM_STREAMING:
634 /* Has the end been reached already? */
635 switch (str->state)
636 {
637 case SSTATE_PARSE: /* Expected case first if no jumptable */
638 /* Are we at the end of file? */
639 if (str->hdr.win_left < disk_buf.filesize)
640 break;
641 str_end_of_stream(str);
642 return STREAM_DATA_END;
643
644 case SSTATE_SYNC:
645 /* Is sync at the end of file? */
646 if (str->hdr.win_right < disk_buf.filesize)
647 break;
648 str_end_of_stream(str);
649 /* Fall-through */
650 case SSTATE_END:
651 return STREAM_DATA_END;
652 }
653
654 if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD))
655 {
656 /* This data range is not buffered yet - register stream to
657 * be notified when it becomes available. Stream is obliged
658 * to enter a TSTATE_DATA state if it must wait. */
659 int res = str_next_data_not_ready(str);
660
661 if (res != STREAM_OK)
662 return res;
663 }
664 break;
665 /* STREAM_PM_STREAMING: */
666
667 case STREAM_PM_RANDOM_ACCESS:
668 str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET);
669
670 if (str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit ||
671 disk_buf_getbuffer(MIN_BUFAHEAD, &p, NULL, NULL) <= 0)
672 {
673 str_end_of_stream(str);
674 return STREAM_DATA_END;
675 }
676
677 str->state = SSTATE_SYNC;
678 str->hdr.win_left = str->hdr.pos;
679 str->curr_packet = NULL;
680 str->curr_packet_end = p;
681 break;
682 /* STREAM_PM_RANDOM_ACCESS: */
683 }
684
685 if (str->state == SSTATE_SYNC)
686 {
687 /* Scanning for start code */
688 if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
689 {
690 INC_BUF(1);
691 continue;
692 }
693 }
694
695 /* Found a start code - enter parse state */
696 str->state = SSTATE_PARSE;
697
698 /* Pack header, skip it */
699 if (CMP_4_CONST(p, PACK_START_CODE))
700 {
701 /* Max lookahead: 14 */
702 if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */
703 {
704 /* Max delta: 14 + 7 = 21 */
705 /* Skip pack header and any stuffing bytes*/
706 bytes = 14 + (p[13] & 7);
707 }
708 else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */
709 {
710 bytes = 12;
711 }
712 else /* unknown - skip it */
713 {
714 DEBUGF("weird pack header!\n");
715 bytes = 5;
716 }
717
718 INC_BUF(bytes);
719 }
720
721 /* System header, parse and skip it - 6 bytes + size */
722 if (CMP_4_CONST(p, SYSTEM_HEADER_START_CODE))
723 {
724 /* Skip start code */
725 /* Max Delta = 65535 + 6 = 65541 */
726 bytes = 6 + ((p[4] << 8) | p[5]);
727 INC_BUF(bytes);
728 }
729
730 /* Packet header, parse it */
731 if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
732 {
733 /* Problem? Meh...probably not but just a corrupted section.
734 * Try to resync the parser which will probably succeed. */
735 DEBUGF("packet start code prefix not found: 0x%02x\n"
736 " wl:%lu wr:%lu\n"
737 " p:%p cp:%p cpe:%p\n"
738 " dbs:%p dbe:%p dbt:%p\n",
739 str->id, str->hdr.win_left, str->hdr.win_right,
740 p, str->curr_packet, str->curr_packet_end,
741 disk_buf.start, disk_buf.end, disk_buf.tail);
742 str->state = SSTATE_SYNC;
743 INC_BUF(1); /* Next byte - this one's no good */
744 continue;
745 }
746
747 /* We retrieve basic infos */
748 /* Maximum packet length: 6 + 65535 = 65541 */
749 id = p[3];
750 length = ((p[4] << 8) | p[5]) + 6;
751
752 if (id != str->id)
753 {
754 switch (id)
755 {
756 case MPEG_STREAM_PROGRAM_END:
757 /* end of stream */
758 str_end_of_stream(str);
759 DEBUGF("MPEG program end: 0x%02x\n", str->id);
760 return STREAM_DATA_END;
761 case MPEG_STREAM_PACK_HEADER:
762 case MPEG_STREAM_SYSTEM_HEADER:
763 /* These shouldn't be here - no increment or resync
764 * since we'll pick it up above. */
765 continue;
766 default:
767 /* It's not the packet we're looking for, skip it */
768 INC_BUF(length);
769 continue;
770 }
771 }
772
773 /* Ok, it's our packet */
774 header = p;
775
776 if ((header[6] & 0xc0) == 0x80) /* mpeg2 */
777 {
778 /* Max Lookahead: 18 */
779 /* Min length: 9 */
780 /* Max length: 9 + 255 = 264 */
781 length = 9 + header[8];
782
783 /* header points to the mpeg2 pes header */
784 if ((header[7] & 0x80) != 0)
785 {
786 /* header has a pts */
787 uint32_t pts = read_pts(header, 9);
788
789 if (pts != INVALID_TIMESTAMP)
790 {
791 str->pts = pts;
792#if 0
793 /* DTS isn't used for anything since things just get
794 decoded ASAP but keep the code around */
795 if (STREAM_IS_VIDEO(id))
796 {
797 /* Video stream - header may have a dts as well */
798 str->dts = pts;
799
800 if (header[7] & 0x40) != 0x00)
801 {
802 pts = read_pts(header, 14);
803 if (pts != INVALID_TIMESTAMP)
804 str->dts = pts;
805 }
806 }
807#endif
808 str->pkt_flags |= PKT_HAS_TS;
809 }
810 }
811 }
812 else /* mpeg1 */
813 {
814 /* Max lookahead: 24 + 2 + 9 = 35 */
815 /* Max len_skip: 24 + 2 = 26 */
816 /* Min length: 7 */
817 /* Max length: 24 + 2 + 9 = 35 */
818 off_t len_skip;
819 uint8_t * ptsbuf;
820
821 length = 7;
822
823 while (header[length - 1] == 0xff)
824 {
825 if (++length > 23)
826 {
827 DEBUGF("Too much stuffing" );
828 break;
829 }
830 }
831
832 if ((header[length - 1] & 0xc0) == 0x40)
833 length += 2;
834
835 len_skip = length;
836 length += mpeg1_skip_table[header[length - 1] >> 4];
837
838 /* Header points to the mpeg1 pes header */
839 ptsbuf = header + len_skip;
840
841 if ((ptsbuf[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf, -1))
842 {
843 /* header has a pts */
844 uint32_t pts = read_pts(ptsbuf, -1);
845
846 if (pts != INVALID_TIMESTAMP)
847 {
848 str->pts = pts;
849#if 0
850 /* DTS isn't used for anything since things just get
851 decoded ASAP but keep the code around */
852 if (STREAM_IS_VIDEO(id))
853 {
854 /* Video stream - header may have a dts as well */
855 str->dts = pts;
856
857 if (ptsbuf[-1] & 0xf0) == 0x30)
858 {
859 pts = read_pts(ptsbuf, 4);
860
861 if (pts != INVALID_TIMESTAMP)
862 str->dts = pts;
863 }
864 }
865#endif
866 str->pkt_flags |= PKT_HAS_TS;
867 }
868 }
869 }
870
871 p += length;
872 /* Max bytes: 6 + 65535 - 7 = 65534 */
873 bytes = 6 + (header[4] << 8) + header[5] - length;
874
875 str->curr_packet = p;
876 str->curr_packet_end = p + bytes;
877 str->hdr.win_left = str->hdr.win_right + length;
878 str->hdr.win_right = str->hdr.win_left + bytes;
879
880 if (str->hdr.win_right > disk_buf.filesize)
881 {
882 /* No packet that exceeds end of file can be valid */
883 str_end_of_stream(str);
884 return STREAM_DATA_END;
885 }
886
887 return STREAM_OK;
888 } /* end while */
889
890 #undef INC_BUF
891}
892
893/* This simply reads data from the file one page at a time and returns a
894 * pointer to it in the buffer. */
895static int parse_elementary(struct stream *str, enum stream_parse_mode type)
896{
897 uint8_t *p;
898 ssize_t len = 0;
899
900 str->pkt_flags = 0;
901
902 switch (type)
903 {
904 case STREAM_PM_STREAMING:
905 /* Has the end been reached already? */
906 if (str->state == SSTATE_END)
907 return STREAM_DATA_END;
908
909 /* Are we at the end of file? */
910 if (str->hdr.win_left >= disk_buf.filesize)
911 {
912 str_end_of_stream(str);
913 return STREAM_DATA_END;
914 }
915
916 if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD))
917 {
918 /* This data range is not buffered yet - register stream to
919 * be notified when it becomes available. Stream is obliged
920 * to enter a TSTATE_DATA state if it must wait. */
921 int res = str_next_data_not_ready(str);
922
923 if (res != STREAM_OK)
924 return res;
925 }
926
927 len = DISK_BUF_PAGE_SIZE;
928
929 if ((size_t)(str->hdr.win_right + len) > (size_t)disk_buf.filesize)
930 len = disk_buf.filesize - str->hdr.win_right;
931
932 if (len <= 0)
933 {
934 str_end_of_stream(str);
935 return STREAM_DATA_END;
936 }
937
938 p = str->curr_packet_end;
939 if (p >= disk_buf.end)
940 p -= disk_buf.size;
941 break;
942 /* STREAM_PM_STREAMING: */
943
944 case STREAM_PM_RANDOM_ACCESS:
945 str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET);
946 len = disk_buf_getbuffer(DISK_BUF_PAGE_SIZE, &p, NULL, NULL);
947
948 if (len <= 0 || str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit)
949 {
950 str_end_of_stream(str);
951 return STREAM_DATA_END;
952 }
953 break;
954 /* STREAM_PM_RANDOM_ACCESS: */
955 }
956
957 str->state = SSTATE_PARSE;
958 str->curr_packet = p;
959 str->curr_packet_end = p + len;
960 str->hdr.win_left = str->hdr.win_right;
961 str->hdr.win_right = str->hdr.win_left + len;
962
963 return STREAM_OK;
964}
965
966bool parser_prepare_image(uint32_t time)
967{
968 struct stream_scan sk;
969 int tries;
970 int result;
971
972 stream_scan_init(&sk);
973
974 if (!str_send_msg(&video_str, STREAM_NEEDS_SYNC, time))
975 {
976 DEBUGF("Image was ready\n");
977 return true; /* Should already have the image */
978 }
979
980#ifdef HAVE_ADJUSTABLE_CPU_FREQ
981 rb->cpu_boost(true); /* No interference with trigger_cpu_boost */
982#endif
983
984 str_send_msg(&video_str, STREAM_RESET, 0);
985
986 sk.pos = parser_can_seek() ?
987 mpeg_parser_seek_PTS(time, video_str.id) : 0;
988 sk.len = sk.pos;
989 sk.dir = SSCAN_REVERSE;
990
991 tries = 1;
992try_again:
993
994 if (mpeg_parser_scan_start_code(&sk, MPEG_START_GOP))
995 {
996 DEBUGF("GOP found at: %ld\n", sk.pos);
997
998 unsigned id = mpeg_parser_scan_pes(&sk);
999
1000 if (id != video_str.id && sk.pos > 0)
1001 {
1002 /* Not part of our stream */
1003 DEBUGF(" wrong stream: 0x%02x\n", id);
1004 goto try_again;
1005 }
1006
1007 /* This will hit the PES header since it's known to be there */
1008 uint32_t pts = mpeg_parser_scan_pts(&sk, id);
1009
1010 if (pts == INVALID_TIMESTAMP || pts > time)
1011 {
1012 DEBUGF(" wrong timestamp: %u\n", (unsigned)pts);
1013 goto try_again;
1014 }
1015 }
1016
1017 str_parser.parms.sd.time = time;
1018 str_parser.parms.sd.sk.pos = MAX(sk.pos, 0);
1019 str_parser.parms.sd.sk.len = 1024*1024;
1020 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
1021
1022 DEBUGF("thumb pos:%ld len:%ld\n", str_parser.parms.sd.sk.pos,
1023 (long)str_parser.parms.sd.sk.len);
1024
1025 result = str_send_msg(&video_str, STREAM_SYNC,
1026 (intptr_t)&str_parser.parms.sd);
1027
1028 if (result != STREAM_PERFECT_MATCH)
1029 {
1030 /* Two tries should be all that is nescessary to find the exact frame
1031 * if the first GOP actually started later than the timestamp - the
1032 * GOP just prior must then start on or earlier. */
1033 if (++tries <= 2)
1034 goto try_again;
1035 }
1036
1037#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1038 rb->cpu_boost(false);
1039#endif
1040
1041 return result > STREAM_OK;
1042}
1043
1044/* Seek parser to the specified time and return absolute time.
1045 * No actual hard stuff is performed here. That's done when streaming is
1046 * about to begin or something from the current position is requested */
1047uint32_t parser_seek_time(uint32_t time)
1048{
1049 if (!parser_can_seek())
1050 time = 0;
1051 else if (time > str_parser.duration)
1052 time = str_parser.duration;
1053
1054 str_parser.last_seek_time = time + str_parser.start_pts;
1055 return str_parser.last_seek_time;
1056}
1057
1058void parser_prepare_streaming(void)
1059{
1060 struct stream_window sw;
1061
1062 DEBUGF("parser_prepare_streaming\n");
1063
1064 /* Prepare initial video frame */
1065 parser_prepare_image(str_parser.last_seek_time);
1066
1067 /* Sync audio stream */
1068 if (audio_str.start_pts != INVALID_TIMESTAMP)
1069 prepare_audio(str_parser.last_seek_time);
1070
1071 /* Prequeue some data and set buffer window */
1072 if (!stream_get_window(&sw))
1073 sw.left = sw.right = disk_buf.filesize;
1074
1075 DEBUGF(" swl:%ld swr:%ld\n", sw.left, sw.right);
1076
1077 if (sw.right > disk_buf.filesize - 4*MIN_BUFAHEAD)
1078 sw.right = disk_buf.filesize - 4*MIN_BUFAHEAD;
1079
1080 disk_buf_prepare_streaming(sw.left,
1081 sw.right - sw.left + 4*MIN_BUFAHEAD);
1082}
1083
1084int parser_init_stream(void)
1085{
1086 if (disk_buf.in_file < 0)
1087 return STREAM_ERROR;
1088
1089 /* TODO: Actually find which streams are available */
1090 audio_str.id = MPEG_STREAM_AUDIO_FIRST;
1091 video_str.id = MPEG_STREAM_VIDEO_FIRST;
1092
1093 /* Try to pull a video PES - if not found, try video init anyway which
1094 * should succeed if it really is a video-only stream */
1095 video_str.hdr.pos = 0;
1096 video_str.hdr.limit = 256*1024;
1097
1098 if (parse_demux(&video_str, STREAM_PM_RANDOM_ACCESS) == STREAM_OK)
1099 {
1100 /* Found a video packet - assume program stream */
1101 str_parser.format = STREAM_FMT_MPEG_PS;
1102 str_parser.next_data = parse_demux;
1103 }
1104 else
1105 {
1106 /* No PES element found - assume video elementary stream */
1107 str_parser.format = STREAM_FMT_MPV;
1108 str_parser.next_data = parse_elementary;
1109 }
1110
1111 if (!init_video_info())
1112 {
1113 /* Cannot determine video size, etc. */
1114 parser_init_state();
1115 return STREAM_UNSUPPORTED;
1116 }
1117
1118 if (str_parser.format == STREAM_FMT_MPEG_PS)
1119 {
1120 /* Initalize start_pts and end_pts with the length (in 45kHz units) of
1121 * the movie. INVALID_TIMESTAMP if the time could not be determined */
1122 if (!init_times(&video_str) || !check_times(&video_str))
1123 {
1124 /* Must have video at least */
1125 parser_init_state();
1126 return STREAM_UNSUPPORTED;
1127 }
1128
1129 str_parser.flags |= STREAMF_CAN_SEEK;
1130
1131 if (init_times(&audio_str))
1132 {
1133 /* Audio will be part of playback pool */
1134 stream_add_stream(&audio_str);
1135
1136 if (check_times(&audio_str))
1137 {
1138 /* Overall duration is maximum span */
1139 str_parser.start_pts = MIN(audio_str.start_pts, video_str.start_pts);
1140 str_parser.end_pts = MAX(audio_str.end_pts, video_str.end_pts);
1141 }
1142 else
1143 {
1144 /* Bad times on audio - use video times */
1145 str_parser.start_pts = video_str.start_pts;
1146 str_parser.end_pts = video_str.end_pts;
1147
1148 /* Questionable: could use bitrate seek and match video to that */
1149 audio_str.start_pts = video_str.start_pts;
1150 audio_str.end_pts = video_str.end_pts;
1151 }
1152 }
1153 else
1154 {
1155 /* No audio stream - use video only */
1156 str_parser.start_pts = video_str.start_pts;
1157 str_parser.end_pts = video_str.end_pts;
1158 }
1159
1160 str_parser.last_seek_time = str_parser.start_pts;
1161 }
1162 else
1163 {
1164 /* There's no way to handle times on this without a full file
1165 * scan */
1166 audio_str.start_pts = INVALID_TIMESTAMP;
1167 audio_str.end_pts = INVALID_TIMESTAMP;
1168 video_str.start_pts = 0;
1169 video_str.end_pts = INVALID_TIMESTAMP;
1170 str_parser.start_pts = 0;
1171 str_parser.end_pts = INVALID_TIMESTAMP;
1172 }
1173
1174 /* Add video to playback pool */
1175 stream_add_stream(&video_str);
1176
1177 /* Cache duration - it's used very often */
1178 str_parser.duration = str_parser.end_pts - str_parser.start_pts;
1179
1180 DEBUGF("Movie info:\n"
1181 " size:%dx%d\n"
1182 " start:%u\n"
1183 " end:%u\n"
1184 " duration:%u\n",
1185 str_parser.dims.w, str_parser.dims.h,
1186 (unsigned)str_parser.start_pts,
1187 (unsigned)str_parser.end_pts,
1188 (unsigned)str_parser.duration);
1189
1190 return STREAM_OK;
1191}
1192
1193void parser_close_stream(void)
1194{
1195 stream_remove_streams();
1196 parser_init_state();
1197}
1198
1199bool parser_init(void)
1200{
1201 parser_init_state();
1202 return true;
1203}