Fix yellow: missed a cast
[kugel-rb.git] / apps / plugins / mpegplayer / mpeg_parser.c
blob714f38ac09233793ff7afa7e84a7f507d448cbb5
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Parser for MPEG streams
12 * Copyright (c) 2007 Michael Sevakis
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "plugin.h"
24 #include "mpegplayer.h"
26 struct stream_parser str_parser SHAREDBSS_ATTR;
28 static void parser_init_state(void)
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;
39 /* Place the stream in a state to begin parsing - sync will be performed
40 * first */
41 void str_initialize(struct stream *str, off_t pos)
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;
55 /* Place the stream in an end of data state */
56 void str_end_of_stream(struct stream *str)
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 = LONG_MIN;
64 str->hdr.win_left = LONG_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;
73 /* Return a timestamp at address p+offset if the marker bits are in tact */
74 static inline uint32_t read_pts(uint8_t *p, off_t offset)
76 return TS_CHECK_MARKERS(p, offset) ?
77 TS_FROM_HEADER(p, offset) : INVALID_TIMESTAMP;
80 static inline bool validate_timestamp(uint32_t ts)
82 return ts >= str_parser.start_pts && ts <= str_parser.end_pts;
85 /* Find a start code before or after a given position */
86 uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code)
88 stream_scan_normalize(sk);
90 if (sk->dir < 0)
92 /* Reverse scan - start with at least the min needed */
93 stream_scan_offset(sk, 4);
96 code &= 0xff; /* Only the low byte matters */
98 while (sk->len >= 0 && sk->margin >= 4)
100 uint8_t *p;
101 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
102 ssize_t len = disk_buf_getbuffer(4, &p, NULL, NULL);
104 if (pos < 0 || len < 4)
105 break;
107 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == code)
109 return p;
112 stream_scan_offset(sk, 1);
115 return NULL;
118 /* Find a PES packet header for any stream - return stream to which it
119 * belongs */
120 unsigned mpeg_parser_scan_pes(struct stream_scan *sk)
122 stream_scan_normalize(sk);
124 if (sk->dir < 0)
126 /* Reverse scan - start with at least the min needed */
127 stream_scan_offset(sk, 4);
130 while (sk->len >= 0 && sk->margin >= 4)
132 uint8_t *p;
133 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
134 ssize_t len = disk_buf_getbuffer(4, &p, NULL, NULL);
136 if (pos < 0 || len < 4)
137 break;
139 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
141 unsigned id = p[3];
142 if (id >= 0xb9)
143 return id; /* PES header */
144 /* else some video stream element */
147 stream_scan_offset(sk, 1);
150 return -1;
153 /* Return the first SCR found from the scan direction */
154 uint32_t mpeg_parser_scan_scr(struct stream_scan *sk)
156 uint8_t *p = mpeg_parser_scan_start_code(sk, MPEG_STREAM_PACK_HEADER);
158 if (p != NULL && sk->margin >= 9) /* 9 bytes total required */
160 sk->data = 9;
162 if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */
164 /* Lookhead p+8 */
165 if (MPEG2_CHECK_PACK_SCR_MARKERS(p, 4))
166 return MPEG2_PACK_HEADER_SCR(p, 4);
168 else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */
170 /* Lookahead p+8 */
171 if (TS_CHECK_MARKERS(p, 4))
172 return TS_FROM_HEADER(p, 4);
174 /* Weird pack header */
175 sk->data = 5;
178 return INVALID_TIMESTAMP;
181 uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id)
183 stream_scan_normalize(sk);
185 if (sk->dir < 0)
187 /* Reverse scan - start with at least the min needed */
188 stream_scan_offset(sk, 4);
191 while (sk->len >= 0 && sk->margin >= 4)
193 uint8_t *p;
194 off_t pos = disk_buf_lseek(sk->pos, SEEK_SET);
195 ssize_t len = disk_buf_getbuffer(35, &p, NULL, NULL);
197 if (pos < 0 || len < 4)
198 break;
200 if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == id)
202 uint8_t *h = p;
204 if (sk->margin < 6)
206 /* Insufficient data */
208 else if ((h[6] & 0xc0) == 0x80) /* mpeg2 */
210 if (sk->margin >= 14 && (h[7] & 0x80) != 0x00)
212 sk->data = 14;
213 return read_pts(h, 9);
216 else /* mpeg1 */
218 ssize_t l = 7;
219 ssize_t margin = sk->margin;
221 /* Skip stuffing_byte */
222 while (h[l - 1] == 0xff && ++l <= 23)
223 --margin;
225 if ((h[l - 1] & 0xc0) == 0x40)
227 /* Skip STD_buffer_scale and STD_buffer_size */
228 margin -= 2;
229 l += 2;
232 if (margin >= 4)
234 /* header points to the mpeg1 pes header */
235 h += l;
237 if ((h[-1] & 0xe0) == 0x20)
239 sk->data = (h + 4) - p;
240 return read_pts(h, -1);
244 /* No PTS present - keep searching for a matching PES header with
245 * one */
248 stream_scan_offset(sk, 1);
251 return INVALID_TIMESTAMP;
254 static bool init_video_info(void)
256 DEBUGF("Getting movie size\n");
258 /* The decoder handles this in order to initialize its knowledge of the
259 * movie parameters making seeking easier */
260 str_send_msg(&video_str, STREAM_RESET, 0);
261 if (str_send_msg(&video_str, VIDEO_GET_SIZE,
262 (intptr_t)&str_parser.dims) != 0)
264 return true;
267 DEBUGF(" failed\n");
268 return false;
271 static void init_times(struct stream *str)
273 int i;
274 struct stream tmp_str;
275 const ssize_t filesize = disk_buf_filesize();
276 const ssize_t max_probe = MIN(512*1024, filesize);
278 /* Simply find the first earliest timestamp - this will be the one
279 * used when streaming anyway */
280 DEBUGF("Finding start_pts: 0x%02x\n", str->id);
282 tmp_str.id = str->id;
283 tmp_str.hdr.pos = 0;
284 tmp_str.hdr.limit = max_probe;
286 str->start_pts = INVALID_TIMESTAMP;
288 /* Probe many for video because of B-frames */
289 for (i = STREAM_IS_VIDEO(str->id) ? 5 : 1; i > 0;)
291 switch (parser_get_next_data(&tmp_str, STREAM_PM_RANDOM_ACCESS))
293 case STREAM_DATA_END:
294 break;
295 case STREAM_OK:
296 if (tmp_str.pkt_flags & PKT_HAS_TS)
298 if (tmp_str.pts < str->start_pts)
299 str->start_pts = tmp_str.pts;
300 i--; /* Decrement timestamp counter */
302 continue;
305 break;
308 DEBUGF(" start:%u\n", (unsigned)str->start_pts);
310 /* Use the decoder thread to perform a synchronized search - no
311 * decoding should take place but just a simple run through timestamps
312 * and durations as the decoder would see them. This should give the
313 * precise time at the end of the last frame for the stream. */
314 DEBUGF("Finding end_pts: 0x%02x\n", str->id);
316 str->end_pts = INVALID_TIMESTAMP;
318 if (str->start_pts != INVALID_TIMESTAMP)
320 str_parser.parms.sd.time = MAX_TIMESTAMP;
321 str_parser.parms.sd.sk.pos = filesize - max_probe;
322 str_parser.parms.sd.sk.len = max_probe;
323 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
325 str_send_msg(str, STREAM_RESET, 0);
327 if (str_send_msg(str, STREAM_FIND_END_TIME,
328 (intptr_t)&str_parser.parms.sd) == STREAM_PERFECT_MATCH)
330 str->end_pts = str_parser.parms.sd.time;
331 DEBUGF(" end:%u\n", (unsigned)str->end_pts);
335 /* End must be greater than start. If the start PTS is found, the end PTS
336 * must be valid too. If the start PTS was invalid, then the end will never
337 * be scanned above. */
338 if (str->start_pts >= str->end_pts || str->end_pts == INVALID_TIMESTAMP)
340 str->start_pts = INVALID_TIMESTAMP;
341 str->end_pts = INVALID_TIMESTAMP;
345 /* Return the best-fit file offset of a timestamp in the PES where
346 * timstamp <= time < next timestamp. Will try to return something reasonably
347 * valid if best-fit could not be made. */
348 static off_t mpeg_parser_seek_PTS(uint32_t time, unsigned id)
350 ssize_t pos_left = 0;
351 ssize_t pos_right = disk_buf.filesize;
352 ssize_t pos, pos_new;
353 uint32_t time_left = str_parser.start_pts;
354 uint32_t time_right = str_parser.end_pts;
355 uint32_t pts = 0;
356 uint32_t prevpts = 0;
357 enum state_enum state = STATE0;
358 struct stream_scan sk;
360 /* Initial estimate taken from average bitrate - later interpolations are
361 * taken similarly based on the remaining file interval */
362 pos_new = muldiv_uint32(time - time_left, pos_right - pos_left,
363 time_right - time_left) + pos_left;
365 /* return this estimated position if nothing better comes up */
366 pos = pos_new;
368 DEBUGF("Seeking stream 0x%02x\n", id);
369 DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n",
370 (unsigned)time_left, (unsigned)time, (unsigned)time_right,
371 (long)pos_left, (long)pos_new, (long)pos_right);
373 sk.dir = SSCAN_REVERSE;
375 while (state < STATE9)
377 uint32_t currpts;
378 sk.pos = pos_new;
379 sk.len = (sk.dir < 0) ? pos_new - pos_left : pos_right - pos_new;
381 currpts = mpeg_parser_scan_pts(&sk, id);
383 if (currpts != INVALID_TIMESTAMP)
385 ssize_t pos_adj; /* Adjustment to over or under-estimate */
387 /* Found a valid timestamp - see were it lies in relation to
388 * target */
389 if (currpts < time)
391 /* Time at current position is before seek time - move
392 * forward */
393 if (currpts > pts)
395 /* This is less than the desired time but greater than
396 * the currently seeked one; move the position up */
397 pts = currpts;
398 pos = sk.pos;
401 /* No next timestamp can be sooner */
402 pos_left = sk.pos + sk.data;
403 time_left = currpts;
405 if (pos_right <= pos_left)
406 break; /* If the window disappeared - we're done */
408 pos_new = muldiv_uint32(time - time_left,
409 pos_right - pos_left,
410 time_right - time_left);
411 /* Point is ahead of us - fudge estimate a bit high */
412 pos_adj = pos_new / 10;
414 if (pos_adj > 512*1024)
415 pos_adj = 512*1024;
417 pos_new += pos_left + pos_adj;
419 if (pos_new >= pos_right)
421 /* Estimate could push too far */
422 pos_new = pos_right;
425 state = STATE2; /* Last scan was early */
426 sk.dir = SSCAN_REVERSE;
428 DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
429 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
430 (unsigned)time_right, (long)pos_left, (long)pos_new,
431 (long)pos_right);
433 else if (currpts > time)
435 /* Time at current position is past seek time - move
436 backward */
437 pos_right = sk.pos;
438 time_right = currpts;
440 if (pos_right <= pos_left)
441 break; /* If the window disappeared - we're done */
443 pos_new = muldiv_uint32(time - time_left,
444 pos_right - pos_left,
445 time_right - time_left);
446 /* Overshot the seek point - fudge estimate a bit low */
447 pos_adj = pos_new / 10;
449 if (pos_adj > 512*1024)
450 pos_adj = 512*1024;
452 pos_new += pos_left - pos_adj;
454 state = STATE3; /* Last scan was late */
455 sk.dir = SSCAN_REVERSE;
457 DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
458 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
459 (unsigned)time_right, (long)pos_left, (long)pos_new,
460 (long)pos_right);
462 else
464 /* Exact match - it happens */
465 DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
466 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
467 (unsigned)time_right, (long)pos_left, (long)pos_new,
468 (long)pos_right);
469 pts = currpts;
470 pos = sk.pos;
471 state = STATE9;
474 else
476 /* Nothing found */
478 switch (state)
480 case STATE1:
481 /* We already tried the bruteforce scan and failed again - no
482 * more stamps could possibly exist in the interval */
483 DEBUGF("!! no timestamp 2x\n");
484 break;
485 case STATE0:
486 /* Hardly likely except at very beginning - just do L->R scan
487 * to find something */
488 DEBUGF("!! no timestamp on first probe: %ld\n", sk.pos);
489 case STATE2:
490 case STATE3:
491 /* Could just be missing timestamps because the interval is
492 * narrowing down. A large block of data from another stream
493 * may also be in the midst of our chosen points which could
494 * cluster at either extreme end. If anything is there, this
495 * will find it. */
496 pos_new = pos_left;
497 sk.dir = SSCAN_FORWARD;
498 DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
499 (unsigned)time_left, (unsigned)time, (unsigned)currpts,
500 (unsigned)time_right, (long)pos_left, (long)pos_new,
501 (long)pos_right);
502 state = STATE1;
503 break;
504 default:
505 DEBUGF("?? Invalid state: %d\n", state);
509 /* Same timestamp twice = quit */
510 if (currpts == prevpts)
512 DEBUGF("!! currpts == prevpts (stop)\n");
513 state = STATE9;
516 prevpts = currpts;
519 #if defined(DEBUG) || defined(SIMULATOR)
520 /* The next pts after the seeked-to position should be greater -
521 * most of the time - frames out of presentation order may muck it
522 * up a slight bit */
523 sk.pos = pos + 1;
524 sk.len = disk_buf.filesize;
525 sk.dir = SSCAN_FORWARD;
527 uint32_t nextpts = mpeg_parser_scan_pts(&sk, id);
528 DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n",
529 (long)pos, (unsigned)pts, (unsigned)time, (unsigned)nextpts);
531 if (pts <= time && time < nextpts)
533 /* Smile - it worked */
534 DEBUGF(" :) pts<=time<next pts\n");
536 else
538 /* See where things ended up */
539 if (pts > time)
541 /* Hmm */
542 DEBUGF(" :\\ pts>time\n");
544 if (pts >= nextpts)
546 /* Weird - probably because of encoded order & tends to be right
547 * anyway if other criteria are met */
548 DEBUGF(" :p pts>=next pts\n");
550 if (time >= nextpts)
552 /* Ugh */
553 DEBUGF(" :( time>=nextpts\n");
556 #endif
558 return pos;
561 static bool prepare_image(uint32_t time)
563 struct stream_scan sk;
564 int tries;
565 int result;
567 if (!str_send_msg(&video_str, STREAM_NEEDS_SYNC, time))
569 DEBUGF("Image was ready\n");
570 return true; /* Should already have the image */
573 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
574 rb->cpu_boost(true); /* No interference with trigger_cpu_boost */
575 #endif
577 str_send_msg(&video_str, STREAM_RESET, 0);
579 sk.pos = parser_can_seek() ?
580 mpeg_parser_seek_PTS(time, video_str.id) : 0;
581 sk.len = sk.pos;
582 sk.dir = SSCAN_REVERSE;
584 tries = 1;
585 try_again:
587 if (mpeg_parser_scan_start_code(&sk, MPEG_START_GOP))
589 DEBUGF("GOP found at: %ld\n", sk.pos);
591 unsigned id = mpeg_parser_scan_pes(&sk);
593 if (id != video_str.id && sk.pos > 0)
595 /* Not part of our stream */
596 DEBUGF(" wrong stream: 0x%02x\n", id);
597 goto try_again;
600 /* This will hit the PES header since it's known to be there */
601 uint32_t pts = mpeg_parser_scan_pts(&sk, id);
603 if (pts == INVALID_TIMESTAMP || pts > time)
605 DEBUGF(" wrong timestamp: %u\n", (unsigned)pts);
606 goto try_again;
610 str_parser.parms.sd.time = time;
611 str_parser.parms.sd.sk.pos = MAX(sk.pos, 0);
612 str_parser.parms.sd.sk.len = 1024*1024;
613 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
615 DEBUGF("thumb pos:%ld len:%ld\n", str_parser.parms.sd.sk.pos,
616 (long)str_parser.parms.sd.sk.len);
618 result = str_send_msg(&video_str, STREAM_SYNC,
619 (intptr_t)&str_parser.parms.sd);
621 if (result != STREAM_PERFECT_MATCH)
623 /* Two tries should be all that is nescessary to find the exact frame
624 * if the first GOP actually started later than the timestamp - the
625 * GOP just prior must then start on or earlier. */
626 if (++tries <= 2)
627 goto try_again;
630 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
631 rb->cpu_boost(false);
632 #endif
634 return result > STREAM_OK;
637 static void prepare_audio(uint32_t time)
639 off_t pos;
641 if (!str_send_msg(&audio_str, STREAM_NEEDS_SYNC, time))
643 DEBUGF("Audio was ready\n");
644 return;
647 pos = mpeg_parser_seek_PTS(time, audio_str.id);
648 str_send_msg(&audio_str, STREAM_RESET, 0);
650 str_parser.parms.sd.time = time;
651 str_parser.parms.sd.sk.pos = pos;
652 str_parser.parms.sd.sk.len = 1024*1024;
653 str_parser.parms.sd.sk.dir = SSCAN_FORWARD;
655 str_send_msg(&audio_str, STREAM_SYNC, (intptr_t)&str_parser.parms.sd);
658 /* This function demuxes the streams and gives the next stream data
659 * pointer.
661 * STREAM_PM_STREAMING is for operation during playback. If the nescessary
662 * data and worst-case lookahead margin is not available, the stream is
663 * registered for notification when the data becomes available. If parsing
664 * extends beyond the end of the file or the end of stream marker is reached,
665 * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
667 * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
668 * If the file cache misses for the current position + lookahead, it will be
669 * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
670 * returned.
672 * The results from one mode may be used as input to the other. Random access
673 * requires cooperation amongst threads to avoid evicting another stream's
674 * data.
676 static int parse_demux(struct stream *str, enum stream_parse_mode type)
678 #define INC_BUF(offset) \
679 ({ off_t _o = (offset); \
680 str->hdr.win_right += _o; \
681 if ((p += _o) >= disk_buf.end) \
682 p -= disk_buf.size; })
684 static const int mpeg1_skip_table[16] =
685 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
687 uint8_t *p = str->curr_packet_end;
689 str->pkt_flags = 0;
691 while (1)
693 uint8_t *header;
694 unsigned id;
695 ssize_t length, bytes;
697 switch (type)
699 case STREAM_PM_STREAMING:
700 /* Has the end been reached already? */
701 switch (str->state)
703 case SSTATE_PARSE: /* Expected case first if no jumptable */
704 /* Are we at the end of file? */
705 if (str->hdr.win_left < disk_buf.filesize)
706 break;
707 str_end_of_stream(str);
708 return STREAM_DATA_END;
710 case SSTATE_SYNC:
711 /* Is sync at the end of file? */
712 if (str->hdr.win_right < disk_buf.filesize)
713 break;
714 str_end_of_stream(str);
715 /* Fall-through */
716 case SSTATE_END:
717 return STREAM_DATA_END;
720 if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD))
722 /* This data range is not buffered yet - register stream to
723 * be notified when it becomes available. Stream is obliged
724 * to enter a TSTATE_DATA state if it must wait. */
725 int res = str_next_data_not_ready(str);
727 if (res != STREAM_OK)
728 return res;
730 break;
731 /* STREAM_PM_STREAMING: */
733 case STREAM_PM_RANDOM_ACCESS:
734 str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET);
736 if (str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit ||
737 disk_buf_getbuffer(MIN_BUFAHEAD, &p, NULL, NULL) <= 0)
739 str_end_of_stream(str);
740 return STREAM_DATA_END;
743 str->state = SSTATE_SYNC;
744 str->hdr.win_left = str->hdr.pos;
745 str->curr_packet = NULL;
746 str->curr_packet_end = p;
747 break;
748 /* STREAM_PM_RANDOM_ACCESS: */
751 if (str->state == SSTATE_SYNC)
753 /* Scanning for start code */
754 if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
756 INC_BUF(1);
757 continue;
761 /* Found a start code - enter parse state */
762 str->state = SSTATE_PARSE;
764 /* Pack header, skip it */
765 if (CMP_4_CONST(p, PACK_START_CODE))
767 /* Max lookahead: 14 */
768 if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */
770 /* Max delta: 14 + 7 = 21 */
771 /* Skip pack header and any stuffing bytes*/
772 bytes = 14 + (p[13] & 7);
774 else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */
776 bytes = 12;
778 else /* unknown - skip it */
780 DEBUGF("weird pack header!\n");
781 bytes = 5;
784 INC_BUF(bytes);
787 /* System header, parse and skip it - 6 bytes + size */
788 if (CMP_4_CONST(p, SYSTEM_HEADER_START_CODE))
790 /* Skip start code */
791 /* Max Delta = 65535 + 6 = 65541 */
792 bytes = 6 + ((p[4] << 8) | p[5]);
793 INC_BUF(bytes);
796 /* Packet header, parse it */
797 if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
799 /* Problem? Meh...probably not but just a corrupted section.
800 * Try to resync the parser which will probably succeed. */
801 DEBUGF("packet start code prefix not found: 0x%02x\n"
802 " wl:%lu wr:%lu\n"
803 " p:%p cp:%p cpe:%p\n"
804 " dbs:%p dbe:%p dbt:%p\n",
805 str->id, str->hdr.win_left, str->hdr.win_right,
806 p, str->curr_packet, str->curr_packet_end,
807 disk_buf.start, disk_buf.end, disk_buf.tail);
808 str->state = SSTATE_SYNC;
809 INC_BUF(1); /* Next byte - this one's no good */
810 continue;
813 /* We retrieve basic infos */
814 /* Maximum packet length: 6 + 65535 = 65541 */
815 id = p[3];
816 length = ((p[4] << 8) | p[5]) + 6;
818 if (id != str->id)
820 switch (id)
822 case MPEG_STREAM_PROGRAM_END:
823 /* end of stream */
824 str_end_of_stream(str);
825 DEBUGF("MPEG program end: 0x%02x\n", str->id);
826 return STREAM_DATA_END;
827 case MPEG_STREAM_PACK_HEADER:
828 case MPEG_STREAM_SYSTEM_HEADER:
829 /* These shouldn't be here - no increment or resync
830 * since we'll pick it up above. */
831 continue;
832 default:
833 /* It's not the packet we're looking for, skip it */
834 INC_BUF(length);
835 continue;
839 /* Ok, it's our packet */
840 header = p;
842 if ((header[6] & 0xc0) == 0x80) /* mpeg2 */
844 /* Max Lookahead: 18 */
845 /* Min length: 9 */
846 /* Max length: 9 + 255 = 264 */
847 length = 9 + header[8];
849 /* header points to the mpeg2 pes header */
850 if ((header[7] & 0x80) != 0)
852 /* header has a pts */
853 uint32_t pts = read_pts(header, 9);
855 if (pts != INVALID_TIMESTAMP)
857 str->pts = pts;
858 #if 0
859 /* DTS isn't used for anything since things just get
860 decoded ASAP but keep the code around */
861 if (STREAM_IS_VIDEO(id))
863 /* Video stream - header may have a dts as well */
864 str->dts = pts;
866 if (header[7] & 0x40) != 0x00)
868 pts = read_pts(header, 14);
869 if (pts != INVALID_TIMESTAMP)
870 str->dts = pts;
873 #endif
874 str->pkt_flags |= PKT_HAS_TS;
878 else /* mpeg1 */
880 /* Max lookahead: 24 + 2 + 9 = 35 */
881 /* Max len_skip: 24 + 2 = 26 */
882 /* Min length: 7 */
883 /* Max length: 24 + 2 + 9 = 35 */
884 off_t len_skip;
885 uint8_t * ptsbuf;
887 length = 7;
889 while (header[length - 1] == 0xff)
891 if (++length > 23)
893 DEBUGF("Too much stuffing" );
894 break;
898 if ((header[length - 1] & 0xc0) == 0x40)
899 length += 2;
901 len_skip = length;
902 length += mpeg1_skip_table[header[length - 1] >> 4];
904 /* Header points to the mpeg1 pes header */
905 ptsbuf = header + len_skip;
907 if ((ptsbuf[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf, -1))
909 /* header has a pts */
910 uint32_t pts = read_pts(ptsbuf, -1);
912 if (pts != INVALID_TIMESTAMP)
914 str->pts = pts;
915 #if 0
916 /* DTS isn't used for anything since things just get
917 decoded ASAP but keep the code around */
918 if (STREAM_IS_VIDEO(id))
920 /* Video stream - header may have a dts as well */
921 str->dts = pts;
923 if (ptsbuf[-1] & 0xf0) == 0x30)
925 pts = read_pts(ptsbuf, 4);
927 if (pts != INVALID_TIMESTAMP)
928 str->dts = pts;
931 #endif
932 str->pkt_flags |= PKT_HAS_TS;
937 p += length;
938 /* Max bytes: 6 + 65535 - 7 = 65534 */
939 bytes = 6 + (header[4] << 8) + header[5] - length;
941 str->curr_packet = p;
942 str->curr_packet_end = p + bytes;
943 str->hdr.win_left = str->hdr.win_right + length;
944 str->hdr.win_right = str->hdr.win_left + bytes;
946 if (str->hdr.win_right > disk_buf.filesize)
948 /* No packet that exceeds end of file can be valid */
949 str_end_of_stream(str);
950 return STREAM_DATA_END;
953 return STREAM_OK;
954 } /* end while */
956 #undef INC_BUF
959 /* This simply reads data from the file one page at a time and returns a
960 * pointer to it in the buffer. */
961 static int parse_elementary(struct stream *str, enum stream_parse_mode type)
963 uint8_t *p;
964 ssize_t len = 0;
966 str->pkt_flags = 0;
968 switch (type)
970 case STREAM_PM_STREAMING:
971 /* Has the end been reached already? */
972 if (str->state == SSTATE_END)
973 return STREAM_DATA_END;
975 /* Are we at the end of file? */
976 if (str->hdr.win_left >= disk_buf.filesize)
978 str_end_of_stream(str);
979 return STREAM_DATA_END;
982 if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD))
984 /* This data range is not buffered yet - register stream to
985 * be notified when it becomes available. Stream is obliged
986 * to enter a TSTATE_DATA state if it must wait. */
987 int res = str_next_data_not_ready(str);
989 if (res != STREAM_OK)
990 return res;
993 len = DISK_BUF_PAGE_SIZE;
995 if ((size_t)(str->hdr.win_right + len) > (size_t)disk_buf.filesize)
996 len = disk_buf.filesize - str->hdr.win_right;
998 if (len <= 0)
1000 str_end_of_stream(str);
1001 return STREAM_DATA_END;
1004 p = str->curr_packet_end;
1005 if (p >= disk_buf.end)
1006 p -= disk_buf.size;
1007 break;
1008 /* STREAM_PM_STREAMING: */
1010 case STREAM_PM_RANDOM_ACCESS:
1011 str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET);
1012 len = disk_buf_getbuffer(DISK_BUF_PAGE_SIZE, &p, NULL, NULL);
1014 if (len <= 0 || str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit)
1016 str_end_of_stream(str);
1017 return STREAM_DATA_END;
1019 break;
1020 /* STREAM_PM_RANDOM_ACCESS: */
1023 str->state = SSTATE_PARSE;
1024 str->curr_packet = p;
1025 str->curr_packet_end = p + len;
1026 str->hdr.win_left = str->hdr.win_right;
1027 str->hdr.win_right = str->hdr.win_left + len;
1029 return STREAM_OK;
1032 intptr_t parser_send_video_msg(long id, intptr_t data)
1034 intptr_t retval = 0;
1036 if (video_str.thread != 0 && disk_buf.in_file >= 0)
1038 /* Hook certain messages since they involve multiple operations
1039 * behind the scenes */
1040 switch (id)
1042 case VIDEO_DISPLAY_SHOW:
1043 if (data != 0 && disk_buf_status() == STREAM_STOPPED)
1044 { /* Only prepare image if showing and not playing */
1045 prepare_image(str_parser.last_seek_time);
1047 break;
1049 case VIDEO_PRINT_FRAME:
1050 if (data)
1051 break;
1052 case VIDEO_PRINT_THUMBNAIL:
1053 if (disk_buf_status() != STREAM_STOPPED)
1054 break; /* Prepare image if not playing */
1056 if (!prepare_image(str_parser.last_seek_time))
1057 return false; /* Preparation failed */
1059 /* Image ready - pass message to video thread */
1060 break;
1063 retval = str_send_msg(&video_str, id, data);
1066 return retval;
1069 /* Seek parser to the specified time and return absolute time.
1070 * No actual hard stuff is performed here. That's done when streaming is
1071 * about to begin or something from the current position is requested */
1072 uint32_t parser_seek_time(uint32_t time)
1074 if (!parser_can_seek())
1075 time = 0;
1076 else if (time > str_parser.duration)
1077 time = str_parser.duration;
1079 str_parser.last_seek_time = time + str_parser.start_pts;
1080 return str_parser.last_seek_time;
1083 void parser_prepare_streaming(void)
1085 struct stream_window sw;
1087 DEBUGF("parser_prepare_streaming\n");
1089 /* Prepare initial video frame */
1090 prepare_image(str_parser.last_seek_time);
1092 /* Sync audio stream */
1093 if (audio_str.start_pts != INVALID_TIMESTAMP)
1094 prepare_audio(str_parser.last_seek_time);
1096 /* Prequeue some data and set buffer window */
1097 if (!stream_get_window(&sw))
1098 sw.left = sw.right = disk_buf.filesize;
1100 DEBUGF(" swl:%ld swr:%ld\n", sw.left, sw.right);
1102 if (sw.right > disk_buf.filesize - 4*MIN_BUFAHEAD)
1103 sw.right = disk_buf.filesize - 4*MIN_BUFAHEAD;
1105 disk_buf_prepare_streaming(sw.left,
1106 sw.right - sw.left + 4*MIN_BUFAHEAD);
1109 int parser_init_stream(void)
1111 if (disk_buf.in_file < 0)
1112 return STREAM_ERROR;
1114 /* TODO: Actually find which streams are available */
1115 audio_str.id = MPEG_STREAM_AUDIO_FIRST;
1116 video_str.id = MPEG_STREAM_VIDEO_FIRST;
1118 /* Try to pull a video PES - if not found, try video init anyway which
1119 * should succeed if it really is a video-only stream */
1120 video_str.hdr.pos = 0;
1121 video_str.hdr.limit = 256*1024;
1123 if (parse_demux(&video_str, STREAM_PM_RANDOM_ACCESS) == STREAM_OK)
1125 /* Found a video packet - assume program stream */
1126 str_parser.format = STREAM_FMT_MPEG_PS;
1127 str_parser.next_data = parse_demux;
1129 else
1131 /* No PES element found - assume video elementary stream */
1132 str_parser.format = STREAM_FMT_MPV;
1133 str_parser.next_data = parse_elementary;
1136 if (!init_video_info())
1138 /* Cannot determine video size, etc. */
1139 return STREAM_UNSUPPORTED;
1142 if (str_parser.format == STREAM_FMT_MPEG_PS)
1144 /* Initalize start_pts and end_pts with the length (in 45kHz units) of
1145 * the movie. INVALID_TIMESTAMP if the time could not be determined */
1146 init_times(&audio_str);
1147 init_times(&video_str);
1149 if (video_str.start_pts == INVALID_TIMESTAMP)
1151 /* Must have video at least */
1152 return STREAM_UNSUPPORTED;
1155 str_parser.flags |= STREAMF_CAN_SEEK;
1157 if (audio_str.start_pts != INVALID_TIMESTAMP)
1159 /* Overall duration is maximum span */
1160 str_parser.start_pts = MIN(audio_str.start_pts, video_str.start_pts);
1161 str_parser.end_pts = MAX(audio_str.end_pts, video_str.end_pts);
1163 /* Audio will be part of playback pool */
1164 stream_add_stream(&audio_str);
1166 else
1168 /* No audio stream - use video only */
1169 str_parser.start_pts = video_str.start_pts;
1170 str_parser.end_pts = video_str.end_pts;
1173 str_parser.last_seek_time = str_parser.start_pts;
1175 else
1177 /* There's no way to handle times on this without a full file
1178 * scan */
1179 audio_str.start_pts = INVALID_TIMESTAMP;
1180 audio_str.end_pts = INVALID_TIMESTAMP;
1181 video_str.start_pts = 0;
1182 video_str.end_pts = INVALID_TIMESTAMP;
1183 str_parser.start_pts = 0;
1184 str_parser.end_pts = INVALID_TIMESTAMP;
1187 /* Add video to playback pool */
1188 stream_add_stream(&video_str);
1190 /* Cache duration - it's used very often */
1191 str_parser.duration = str_parser.end_pts - str_parser.start_pts;
1193 DEBUGF("Movie info:\n"
1194 " size:%dx%d\n"
1195 " start:%u\n"
1196 " end:%u\n"
1197 " duration:%u\n",
1198 str_parser.dims.w, str_parser.dims.h,
1199 (unsigned)str_parser.start_pts,
1200 (unsigned)str_parser.end_pts,
1201 (unsigned)str_parser.duration);
1203 return STREAM_OK;
1206 void parser_close_stream(void)
1208 stream_remove_streams();
1209 parser_init_state();
1212 bool parser_init(void)
1214 parser_init_state();
1215 return true;