1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
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
;
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
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
;
46 str
->curr_packet
= NULL
;
47 /* Pick up parsing from this point in the buffer */
48 str
->curr_packet_end
= disk_buf_offset2ptr(pos
);
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
63 str
->hdr
.win_right
= LONG_MIN
;
64 str
->hdr
.win_left
= LONG_MAX
;
66 str
->curr_packet
= str
->curr_packet_end
= NULL
;
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
);
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)
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)
107 if (CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
) && p
[3] == code
)
112 stream_scan_offset(sk
, 1);
118 /* Find a PES packet header for any stream - return stream to which it
120 unsigned mpeg_parser_scan_pes(struct stream_scan
*sk
)
122 stream_scan_normalize(sk
);
126 /* Reverse scan - start with at least the min needed */
127 stream_scan_offset(sk
, 4);
130 while (sk
->len
>= 0 && sk
->margin
>= 4)
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)
139 if (CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
143 return id
; /* PES header */
144 /* else some video stream element */
147 stream_scan_offset(sk
, 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 */
162 if ((p
[4] & 0xc0) == 0x40) /* mpeg-2 */
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 */
171 if (TS_CHECK_MARKERS(p
, 4))
172 return TS_FROM_HEADER(p
, 4);
174 /* Weird pack header */
178 return INVALID_TIMESTAMP
;
181 uint32_t mpeg_parser_scan_pts(struct stream_scan
*sk
, unsigned id
)
183 stream_scan_normalize(sk
);
187 /* Reverse scan - start with at least the min needed */
188 stream_scan_offset(sk
, 4);
191 while (sk
->len
>= 0 && sk
->margin
>= 4)
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)
200 if (CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
) && p
[3] == id
)
206 /* Insufficient data */
208 else if ((h
[6] & 0xc0) == 0x80) /* mpeg2 */
210 if (sk
->margin
>= 14 && (h
[7] & 0x80) != 0x00)
213 return read_pts(h
, 9);
219 ssize_t margin
= sk
->margin
;
221 /* Skip stuffing_byte */
222 while (h
[l
- 1] == 0xff && ++l
<= 23)
225 if ((h
[l
- 1] & 0xc0) == 0x40)
227 /* Skip STD_buffer_scale and STD_buffer_size */
234 /* header points to the mpeg1 pes header */
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
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)
271 static void init_times(struct stream
*str
)
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
;
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
:
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 */
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
;
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 */
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
)
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
391 /* Time at current position is before seek time - move
395 /* This is less than the desired time but greater than
396 * the currently seeked one; move the position up */
401 /* No next timestamp can be sooner */
402 pos_left
= sk
.pos
+ sk
.data
;
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)
417 pos_new
+= pos_left
+ pos_adj
;
419 if (pos_new
>= pos_right
)
421 /* Estimate could push too far */
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
,
433 else if (currpts
> time
)
435 /* Time at current position is past seek time - move
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)
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
,
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
,
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");
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
);
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
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
,
505 DEBUGF("?? Invalid state: %d\n", state
);
509 /* Same timestamp twice = quit */
510 if (currpts
== prevpts
)
512 DEBUGF("!! currpts == prevpts (stop)\n");
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
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");
538 /* See where things ended up */
542 DEBUGF(" :\\ pts>time\n");
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");
553 DEBUGF(" :( time>=nextpts\n");
561 static bool prepare_image(uint32_t time
)
563 struct stream_scan sk
;
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 */
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;
582 sk
.dir
= SSCAN_REVERSE
;
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
);
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
);
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. */
630 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
631 rb
->cpu_boost(false);
634 return result
> STREAM_OK
;
637 static void prepare_audio(uint32_t time
)
641 if (!str_send_msg(&audio_str
, STREAM_NEEDS_SYNC
, time
))
643 DEBUGF("Audio was ready\n");
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
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
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
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
;
695 ssize_t length
, bytes
;
699 case STREAM_PM_STREAMING
:
700 /* Has the end been reached already? */
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
)
707 str_end_of_stream(str
);
708 return STREAM_DATA_END
;
711 /* Is sync at the end of file? */
712 if (str
->hdr
.win_right
< disk_buf
.filesize
)
714 str_end_of_stream(str
);
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
)
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
;
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
))
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 */
778 else /* unknown - skip it */
780 DEBUGF("weird pack header!\n");
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]);
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"
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 */
813 /* We retrieve basic infos */
814 /* Maximum packet length: 6 + 65535 = 65541 */
816 length
= ((p
[4] << 8) | p
[5]) + 6;
822 case MPEG_STREAM_PROGRAM_END
:
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. */
833 /* It's not the packet we're looking for, skip it */
839 /* Ok, it's our packet */
842 if ((header
[6] & 0xc0) == 0x80) /* mpeg2 */
844 /* Max Lookahead: 18 */
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
)
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 */
866 if (header
[7] & 0x40) != 0x00)
868 pts
= read_pts(header
, 14);
869 if (pts
!= INVALID_TIMESTAMP
)
874 str
->pkt_flags
|= PKT_HAS_TS
;
880 /* Max lookahead: 24 + 2 + 9 = 35 */
881 /* Max len_skip: 24 + 2 = 26 */
883 /* Max length: 24 + 2 + 9 = 35 */
889 while (header
[length
- 1] == 0xff)
893 DEBUGF("Too much stuffing" );
898 if ((header
[length
- 1] & 0xc0) == 0x40)
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
)
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 */
923 if (ptsbuf
[-1] & 0xf0) == 0x30)
925 pts
= read_pts(ptsbuf
, 4);
927 if (pts
!= INVALID_TIMESTAMP
)
932 str
->pkt_flags
|= PKT_HAS_TS
;
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
;
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
)
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
)
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
;
1000 str_end_of_stream(str
);
1001 return STREAM_DATA_END
;
1004 p
= str
->curr_packet_end
;
1005 if (p
>= disk_buf
.end
)
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
;
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
;
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 */
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
);
1049 case VIDEO_PRINT_FRAME
:
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 */
1063 retval
= str_send_msg(&video_str
, id
, data
);
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())
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
;
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
);
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
;
1177 /* There's no way to handle times on this without a full file
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"
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
);
1206 void parser_close_stream(void)
1208 stream_remove_streams();
1209 parser_init_state();
1212 bool parser_init(void)
1214 parser_init_state();