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:%zd pn:%zd pr:%zd\n",
370 (unsigned)time_left
, (unsigned)time
, (unsigned)time_right
,
371 pos_left
, pos_new
, 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:%zd pn:%zd pr:%zd\n",
429 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
430 (unsigned)time_right
, pos_left
, pos_new
, pos_right
);
432 else if (currpts
> time
)
434 /* Time at current position is past seek time - move
437 time_right
= currpts
;
439 if (pos_right
<= pos_left
)
440 break; /* If the window disappeared - we're done */
442 pos_new
= muldiv_uint32(time
- time_left
,
443 pos_right
- pos_left
,
444 time_right
- time_left
);
445 /* Overshot the seek point - fudge estimate a bit low */
446 pos_adj
= pos_new
/ 10;
448 if (pos_adj
> 512*1024)
451 pos_new
+= pos_left
- pos_adj
;
453 state
= STATE3
; /* Last scan was late */
454 sk
.dir
= SSCAN_REVERSE
;
456 DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%zd pn:%zd pr:%zd\n",
457 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
458 (unsigned)time_right
, pos_left
, pos_new
, pos_right
);
462 /* Exact match - it happens */
463 DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%zd pn:%zd pr:%zd\n",
464 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
465 (unsigned)time_right
, pos_left
, pos_new
, pos_right
);
478 /* We already tried the bruteforce scan and failed again - no
479 * more stamps could possibly exist in the interval */
480 DEBUGF("!! no timestamp 2x\n");
483 /* Hardly likely except at very beginning - just do L->R scan
484 * to find something */
485 DEBUGF("!! no timestamp on first probe: %ld\n", sk
.pos
);
488 /* Could just be missing timestamps because the interval is
489 * narrowing down. A large block of data from another stream
490 * may also be in the midst of our chosen points which could
491 * cluster at either extreme end. If anything is there, this
494 sk
.dir
= SSCAN_FORWARD
;
495 DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%zd pn:%zd pr:%zd\n",
496 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
497 (unsigned)time_right
, pos_left
, pos_new
, pos_right
);
501 DEBUGF("?? Invalid state: %d\n", state
);
505 /* Same timestamp twice = quit */
506 if (currpts
== prevpts
)
508 DEBUGF("!! currpts == prevpts (stop)\n");
515 #if defined(DEBUG) || defined(SIMULATOR)
516 /* The next pts after the seeked-to position should be greater -
517 * most of the time - frames out of presentation order may muck it
520 sk
.len
= disk_buf
.filesize
;
521 sk
.dir
= SSCAN_FORWARD
;
523 uint32_t nextpts
= mpeg_parser_scan_pts(&sk
, id
);
524 DEBUGF("Seek pos:%zd pts:%u t:%u next pts:%u \n",
525 pos
, (unsigned)pts
, (unsigned)time
, (unsigned)nextpts
);
527 if (pts
<= time
&& time
< nextpts
)
529 /* Smile - it worked */
530 DEBUGF(" :) pts<=time<next pts\n");
534 /* See where things ended up */
538 DEBUGF(" :\\ pts>time\n");
542 /* Weird - probably because of encoded order & tends to be right
543 * anyway if other criteria are met */
544 DEBUGF(" :p pts>=next pts\n");
549 DEBUGF(" :( time>=nextpts\n");
557 static bool prepare_image(uint32_t time
)
559 struct stream_scan sk
;
563 if (!str_send_msg(&video_str
, STREAM_NEEDS_SYNC
, time
))
565 DEBUGF("Image was ready\n");
566 return true; /* Should already have the image */
569 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
570 rb
->cpu_boost(true); /* No interference with trigger_cpu_boost */
573 str_send_msg(&video_str
, STREAM_RESET
, 0);
575 sk
.pos
= parser_can_seek() ?
576 mpeg_parser_seek_PTS(time
, video_str
.id
) : 0;
578 sk
.dir
= SSCAN_REVERSE
;
583 if (mpeg_parser_scan_start_code(&sk
, MPEG_START_GOP
))
585 DEBUGF("GOP found at: %ld\n", sk
.pos
);
587 unsigned id
= mpeg_parser_scan_pes(&sk
);
589 if (id
!= video_str
.id
&& sk
.pos
> 0)
591 /* Not part of our stream */
592 DEBUGF(" wrong stream: 0x%02x\n", id
);
596 /* This will hit the PES header since it's known to be there */
597 uint32_t pts
= mpeg_parser_scan_pts(&sk
, id
);
599 if (pts
== INVALID_TIMESTAMP
|| pts
> time
)
601 DEBUGF(" wrong timestamp: %u\n", (unsigned)pts
);
606 str_parser
.parms
.sd
.time
= time
;
607 str_parser
.parms
.sd
.sk
.pos
= MAX(sk
.pos
, 0);
608 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
609 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
611 DEBUGF("thumb pos:%ld len:%zd\n", str_parser
.parms
.sd
.sk
.pos
,
612 str_parser
.parms
.sd
.sk
.len
);
614 result
= str_send_msg(&video_str
, STREAM_SYNC
,
615 (intptr_t)&str_parser
.parms
.sd
);
617 if (result
!= STREAM_PERFECT_MATCH
)
619 /* Two tries should be all that is nescessary to find the exact frame
620 * if the first GOP actually started later than the timestamp - the
621 * GOP just prior must then start on or earlier. */
626 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
627 rb
->cpu_boost(false);
630 return result
> STREAM_OK
;
633 static void prepare_audio(uint32_t time
)
637 if (!str_send_msg(&audio_str
, STREAM_NEEDS_SYNC
, time
))
639 DEBUGF("Audio was ready\n");
643 pos
= mpeg_parser_seek_PTS(time
, audio_str
.id
);
644 str_send_msg(&audio_str
, STREAM_RESET
, 0);
646 str_parser
.parms
.sd
.time
= time
;
647 str_parser
.parms
.sd
.sk
.pos
= pos
;
648 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
649 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
651 str_send_msg(&audio_str
, STREAM_SYNC
, (intptr_t)&str_parser
.parms
.sd
);
654 /* This function demuxes the streams and gives the next stream data
657 * STREAM_PM_STREAMING is for operation during playback. If the nescessary
658 * data and worst-case lookahead margin is not available, the stream is
659 * registered for notification when the data becomes available. If parsing
660 * extends beyond the end of the file or the end of stream marker is reached,
661 * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
663 * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
664 * If the file cache misses for the current position + lookahead, it will be
665 * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
668 * The results from one mode may be used as input to the other. Random access
669 * requires cooperation amongst threads to avoid evicting another stream's
672 static int parse_demux(struct stream
*str
, enum stream_parse_mode type
)
674 #define INC_BUF(offset) \
675 ({ off_t _o = (offset); \
676 str->hdr.win_right += _o; \
677 if ((p += _o) >= disk_buf.end) \
678 p -= disk_buf.size; })
680 static const int mpeg1_skip_table
[16] =
681 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
683 uint8_t *p
= str
->curr_packet_end
;
691 ssize_t length
, bytes
;
695 case STREAM_PM_STREAMING
:
696 /* Has the end been reached already? */
699 case SSTATE_PARSE
: /* Expected case first if no jumptable */
700 /* Are we at the end of file? */
701 if (str
->hdr
.win_left
< disk_buf
.filesize
)
703 str_end_of_stream(str
);
704 return STREAM_DATA_END
;
707 /* Is sync at the end of file? */
708 if (str
->hdr
.win_right
< disk_buf
.filesize
)
710 str_end_of_stream(str
);
713 return STREAM_DATA_END
;
716 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
718 /* This data range is not buffered yet - register stream to
719 * be notified when it becomes available. Stream is obliged
720 * to enter a TSTATE_DATA state if it must wait. */
721 int res
= str_next_data_not_ready(str
);
723 if (res
!= STREAM_OK
)
727 /* STREAM_PM_STREAMING: */
729 case STREAM_PM_RANDOM_ACCESS
:
730 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
732 if (str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
||
733 disk_buf_getbuffer(MIN_BUFAHEAD
, &p
, NULL
, NULL
) <= 0)
735 str_end_of_stream(str
);
736 return STREAM_DATA_END
;
739 str
->state
= SSTATE_SYNC
;
740 str
->hdr
.win_left
= str
->hdr
.pos
;
741 str
->curr_packet
= NULL
;
742 str
->curr_packet_end
= p
;
744 /* STREAM_PM_RANDOM_ACCESS: */
747 if (str
->state
== SSTATE_SYNC
)
749 /* Scanning for start code */
750 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
757 /* Found a start code - enter parse state */
758 str
->state
= SSTATE_PARSE
;
760 /* Pack header, skip it */
761 if (CMP_4_CONST(p
, PACK_START_CODE
))
763 /* Max lookahead: 14 */
764 if ((p
[4] & 0xc0) == 0x40) /* mpeg-2 */
766 /* Max delta: 14 + 7 = 21 */
767 /* Skip pack header and any stuffing bytes*/
768 bytes
= 14 + (p
[13] & 7);
770 else if ((p
[4] & 0xf0) == 0x20) /* mpeg-1 */
774 else /* unknown - skip it */
776 DEBUGF("weird pack header!\n");
783 /* System header, parse and skip it - 6 bytes + size */
784 if (CMP_4_CONST(p
, SYSTEM_HEADER_START_CODE
))
786 /* Skip start code */
787 /* Max Delta = 65535 + 6 = 65541 */
788 bytes
= 6 + ((p
[4] << 8) | p
[5]);
792 /* Packet header, parse it */
793 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
795 /* Problem? Meh...probably not but just a corrupted section.
796 * Try to resync the parser which will probably succeed. */
797 DEBUGF("packet start code prefix not found: 0x%02x\n"
799 " p:%p cp:%p cpe:%p\n"
800 " dbs:%p dbe:%p dbt:%p\n",
801 str
->id
, str
->hdr
.win_left
, str
->hdr
.win_right
,
802 p
, str
->curr_packet
, str
->curr_packet_end
,
803 disk_buf
.start
, disk_buf
.end
, disk_buf
.tail
);
804 str
->state
= SSTATE_SYNC
;
805 INC_BUF(1); /* Next byte - this one's no good */
809 /* We retrieve basic infos */
810 /* Maximum packet length: 6 + 65535 = 65541 */
812 length
= ((p
[4] << 8) | p
[5]) + 6;
818 case MPEG_STREAM_PROGRAM_END
:
820 str_end_of_stream(str
);
821 DEBUGF("MPEG program end: 0x%02x\n", str
->id
);
822 return STREAM_DATA_END
;
823 case MPEG_STREAM_PACK_HEADER
:
824 case MPEG_STREAM_SYSTEM_HEADER
:
825 /* These shouldn't be here - no increment or resync
826 * since we'll pick it up above. */
829 /* It's not the packet we're looking for, skip it */
835 /* Ok, it's our packet */
838 if ((header
[6] & 0xc0) == 0x80) /* mpeg2 */
840 /* Max Lookahead: 18 */
842 /* Max length: 9 + 255 = 264 */
843 length
= 9 + header
[8];
845 /* header points to the mpeg2 pes header */
846 if ((header
[7] & 0x80) != 0)
848 /* header has a pts */
849 uint32_t pts
= read_pts(header
, 9);
851 if (pts
!= INVALID_TIMESTAMP
)
855 /* DTS isn't used for anything since things just get
856 decoded ASAP but keep the code around */
857 if (STREAM_IS_VIDEO(id
))
859 /* Video stream - header may have a dts as well */
862 if (header
[7] & 0x40) != 0x00)
864 pts
= read_pts(header
, 14);
865 if (pts
!= INVALID_TIMESTAMP
)
870 str
->pkt_flags
|= PKT_HAS_TS
;
876 /* Max lookahead: 24 + 2 + 9 = 35 */
877 /* Max len_skip: 24 + 2 = 26 */
879 /* Max length: 24 + 2 + 9 = 35 */
885 while (header
[length
- 1] == 0xff)
889 DEBUGF("Too much stuffing" );
894 if ((header
[length
- 1] & 0xc0) == 0x40)
898 length
+= mpeg1_skip_table
[header
[length
- 1] >> 4];
900 /* Header points to the mpeg1 pes header */
901 ptsbuf
= header
+ len_skip
;
903 if ((ptsbuf
[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf
, -1))
905 /* header has a pts */
906 uint32_t pts
= read_pts(ptsbuf
, -1);
908 if (pts
!= INVALID_TIMESTAMP
)
912 /* DTS isn't used for anything since things just get
913 decoded ASAP but keep the code around */
914 if (STREAM_IS_VIDEO(id
))
916 /* Video stream - header may have a dts as well */
919 if (ptsbuf
[-1] & 0xf0) == 0x30)
921 pts
= read_pts(ptsbuf
, 4);
923 if (pts
!= INVALID_TIMESTAMP
)
928 str
->pkt_flags
|= PKT_HAS_TS
;
934 /* Max bytes: 6 + 65535 - 7 = 65534 */
935 bytes
= 6 + (header
[4] << 8) + header
[5] - length
;
937 str
->curr_packet
= p
;
938 str
->curr_packet_end
= p
+ bytes
;
939 str
->hdr
.win_left
= str
->hdr
.win_right
+ length
;
940 str
->hdr
.win_right
= str
->hdr
.win_left
+ bytes
;
942 if (str
->hdr
.win_right
> disk_buf
.filesize
)
944 /* No packet that exceeds end of file can be valid */
945 str_end_of_stream(str
);
946 return STREAM_DATA_END
;
955 /* This simply reads data from the file one page at a time and returns a
956 * pointer to it in the buffer. */
957 static int parse_elementary(struct stream
*str
, enum stream_parse_mode type
)
966 case STREAM_PM_STREAMING
:
967 /* Has the end been reached already? */
968 if (str
->state
== SSTATE_END
)
969 return STREAM_DATA_END
;
971 /* Are we at the end of file? */
972 if (str
->hdr
.win_left
>= disk_buf
.filesize
)
974 str_end_of_stream(str
);
975 return STREAM_DATA_END
;
978 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
980 /* This data range is not buffered yet - register stream to
981 * be notified when it becomes available. Stream is obliged
982 * to enter a TSTATE_DATA state if it must wait. */
983 int res
= str_next_data_not_ready(str
);
985 if (res
!= STREAM_OK
)
989 len
= DISK_BUF_PAGE_SIZE
;
991 if ((size_t)(str
->hdr
.win_right
+ len
) > (size_t)disk_buf
.filesize
)
992 len
= disk_buf
.filesize
- str
->hdr
.win_right
;
996 str_end_of_stream(str
);
997 return STREAM_DATA_END
;
1000 p
= str
->curr_packet_end
;
1001 if (p
>= disk_buf
.end
)
1004 /* STREAM_PM_STREAMING: */
1006 case STREAM_PM_RANDOM_ACCESS
:
1007 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
1008 len
= disk_buf_getbuffer(DISK_BUF_PAGE_SIZE
, &p
, NULL
, NULL
);
1010 if (len
<= 0 || str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
)
1012 str_end_of_stream(str
);
1013 return STREAM_DATA_END
;
1016 /* STREAM_PM_RANDOM_ACCESS: */
1019 str
->state
= SSTATE_PARSE
;
1020 str
->curr_packet
= p
;
1021 str
->curr_packet_end
= p
+ len
;
1022 str
->hdr
.win_left
= str
->hdr
.win_right
;
1023 str
->hdr
.win_right
= str
->hdr
.win_left
+ len
;
1028 intptr_t parser_send_video_msg(long id
, intptr_t data
)
1030 intptr_t retval
= 0;
1032 if (video_str
.thread
!= 0 && disk_buf
.in_file
>= 0)
1034 /* Hook certain messages since they involve multiple operations
1035 * behind the scenes */
1038 case VIDEO_DISPLAY_SHOW
:
1039 if (data
!= 0 && disk_buf_status() == STREAM_STOPPED
)
1040 { /* Only prepare image if showing and not playing */
1041 prepare_image(str_parser
.last_seek_time
);
1045 case VIDEO_PRINT_FRAME
:
1048 case VIDEO_PRINT_THUMBNAIL
:
1049 if (disk_buf_status() != STREAM_STOPPED
)
1050 break; /* Prepare image if not playing */
1052 if (!prepare_image(str_parser
.last_seek_time
))
1053 return false; /* Preparation failed */
1055 /* Image ready - pass message to video thread */
1059 retval
= str_send_msg(&video_str
, id
, data
);
1065 /* Seek parser to the specified time and return absolute time.
1066 * No actual hard stuff is performed here. That's done when streaming is
1067 * about to begin or something from the current position is requested */
1068 uint32_t parser_seek_time(uint32_t time
)
1070 if (!parser_can_seek())
1072 else if (time
> str_parser
.duration
)
1073 time
= str_parser
.duration
;
1075 str_parser
.last_seek_time
= time
+ str_parser
.start_pts
;
1076 return str_parser
.last_seek_time
;
1079 void parser_prepare_streaming(void)
1081 struct stream_window sw
;
1083 DEBUGF("parser_prepare_streaming\n");
1085 /* Prepare initial video frame */
1086 prepare_image(str_parser
.last_seek_time
);
1088 /* Sync audio stream */
1089 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1090 prepare_audio(str_parser
.last_seek_time
);
1092 /* Prequeue some data and set buffer window */
1093 if (!stream_get_window(&sw
))
1094 sw
.left
= sw
.right
= disk_buf
.filesize
;
1096 DEBUGF(" swl:%ld swr:%ld\n", sw
.left
, sw
.right
);
1098 if (sw
.right
> disk_buf
.filesize
- 4*MIN_BUFAHEAD
)
1099 sw
.right
= disk_buf
.filesize
- 4*MIN_BUFAHEAD
;
1101 disk_buf_prepare_streaming(sw
.left
,
1102 sw
.right
- sw
.left
+ 4*MIN_BUFAHEAD
);
1105 int parser_init_stream(void)
1107 if (disk_buf
.in_file
< 0)
1108 return STREAM_ERROR
;
1110 /* TODO: Actually find which streams are available */
1111 audio_str
.id
= MPEG_STREAM_AUDIO_FIRST
;
1112 video_str
.id
= MPEG_STREAM_VIDEO_FIRST
;
1114 /* Try to pull a video PES - if not found, try video init anyway which
1115 * should succeed if it really is a video-only stream */
1116 video_str
.hdr
.pos
= 0;
1117 video_str
.hdr
.limit
= 256*1024;
1119 if (parse_demux(&video_str
, STREAM_PM_RANDOM_ACCESS
) == STREAM_OK
)
1121 /* Found a video packet - assume program stream */
1122 str_parser
.format
= STREAM_FMT_MPEG_PS
;
1123 str_parser
.next_data
= parse_demux
;
1127 /* No PES element found - assume video elementary stream */
1128 str_parser
.format
= STREAM_FMT_MPV
;
1129 str_parser
.next_data
= parse_elementary
;
1132 if (!init_video_info())
1134 /* Cannot determine video size, etc. */
1135 return STREAM_UNSUPPORTED
;
1138 if (str_parser
.format
== STREAM_FMT_MPEG_PS
)
1140 /* Initalize start_pts and end_pts with the length (in 45kHz units) of
1141 * the movie. INVALID_TIMESTAMP if the time could not be determined */
1142 init_times(&audio_str
);
1143 init_times(&video_str
);
1145 if (video_str
.start_pts
== INVALID_TIMESTAMP
)
1147 /* Must have video at least */
1148 return STREAM_UNSUPPORTED
;
1151 str_parser
.flags
|= STREAMF_CAN_SEEK
;
1153 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1155 /* Overall duration is maximum span */
1156 str_parser
.start_pts
= MIN(audio_str
.start_pts
, video_str
.start_pts
);
1157 str_parser
.end_pts
= MAX(audio_str
.end_pts
, video_str
.end_pts
);
1159 /* Audio will be part of playback pool */
1160 stream_add_stream(&audio_str
);
1164 /* No audio stream - use video only */
1165 str_parser
.start_pts
= video_str
.start_pts
;
1166 str_parser
.end_pts
= video_str
.end_pts
;
1169 str_parser
.last_seek_time
= str_parser
.start_pts
;
1173 /* There's no way to handle times on this without a full file
1175 audio_str
.start_pts
= INVALID_TIMESTAMP
;
1176 audio_str
.end_pts
= INVALID_TIMESTAMP
;
1177 video_str
.start_pts
= 0;
1178 video_str
.end_pts
= INVALID_TIMESTAMP
;
1179 str_parser
.start_pts
= 0;
1180 str_parser
.end_pts
= INVALID_TIMESTAMP
;
1183 /* Add video to playback pool */
1184 stream_add_stream(&video_str
);
1186 /* Cache duration - it's used very often */
1187 str_parser
.duration
= str_parser
.end_pts
- str_parser
.start_pts
;
1189 DEBUGF("Movie info:\n"
1194 str_parser
.dims
.w
, str_parser
.dims
.h
,
1195 (unsigned)str_parser
.start_pts
,
1196 (unsigned)str_parser
.end_pts
,
1197 (unsigned)str_parser
.duration
);
1202 void parser_close_stream(void)
1204 stream_remove_streams();
1205 parser_init_state();
1208 bool parser_init(void)
1210 parser_init_state();