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
= OFF_T_MIN
;
64 str
->hdr
.win_left
= OFF_T_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_l2(&sk
->l2
, 4, &p
);
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_l2(&sk
->l2
, 4, &p
);
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_l2(&sk
->l2
, 30, &p
);
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 (margin
> 7 && h
[l
] == 0xff && ++l
<= 22)
227 if ((h
[l
] & 0xc0) == 0x40)
229 /* Skip STD_buffer_scale and STD_buffer_size */
236 /* Header points to the mpeg1 pes header */
239 if ((h
[0] & 0xe0) == 0x20)
241 /* PTS or PTS_DTS indicated */
242 sk
->data
= (h
+ 5) - p
;
243 return read_pts(h
, 0);
248 /* No PTS present - keep searching for a matching PES header with
252 stream_scan_offset(sk
, 1);
255 return INVALID_TIMESTAMP
;
258 static bool init_video_info(void)
260 DEBUGF("Getting movie size\n");
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)
275 static void init_times(struct stream
*str
)
278 struct stream tmp_str
;
279 const ssize_t filesize
= disk_buf_filesize();
280 const ssize_t max_probe
= MIN(512*1024, filesize
);
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
);
286 tmp_str
.id
= str
->id
;
288 tmp_str
.hdr
.limit
= max_probe
;
290 str
->start_pts
= INVALID_TIMESTAMP
;
292 /* Probe many for video because of B-frames */
293 for (i
= STREAM_IS_VIDEO(str
->id
) ? 5 : 1; i
> 0;)
295 switch (parser_get_next_data(&tmp_str
, STREAM_PM_RANDOM_ACCESS
))
297 case STREAM_DATA_END
:
300 if (tmp_str
.pkt_flags
& PKT_HAS_TS
)
302 if (tmp_str
.pts
< str
->start_pts
)
303 str
->start_pts
= tmp_str
.pts
;
304 i
--; /* Decrement timestamp counter */
312 DEBUGF(" start:%u\n", (unsigned)str
->start_pts
);
314 /* Use the decoder thread to perform a synchronized search - no
315 * decoding should take place but just a simple run through timestamps
316 * and durations as the decoder would see them. This should give the
317 * precise time at the end of the last frame for the stream. */
318 DEBUGF("Finding end_pts: 0x%02x\n", str
->id
);
320 str
->end_pts
= INVALID_TIMESTAMP
;
322 if (str
->start_pts
!= INVALID_TIMESTAMP
)
324 str_parser
.parms
.sd
.time
= MAX_TIMESTAMP
;
325 str_parser
.parms
.sd
.sk
.pos
= filesize
- max_probe
;
326 str_parser
.parms
.sd
.sk
.len
= max_probe
;
327 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
329 str_send_msg(str
, STREAM_RESET
, 0);
331 if (str_send_msg(str
, STREAM_FIND_END_TIME
,
332 (intptr_t)&str_parser
.parms
.sd
) == STREAM_PERFECT_MATCH
)
334 str
->end_pts
= str_parser
.parms
.sd
.time
;
335 DEBUGF(" end:%u\n", (unsigned)str
->end_pts
);
339 /* End must be greater than start. If the start PTS is found, the end PTS
340 * must be valid too. If the start PTS was invalid, then the end will never
341 * be scanned above. */
342 if (str
->start_pts
>= str
->end_pts
|| str
->end_pts
== INVALID_TIMESTAMP
)
344 str
->start_pts
= INVALID_TIMESTAMP
;
345 str
->end_pts
= INVALID_TIMESTAMP
;
349 /* Return the best-fit file offset of a timestamp in the PES where
350 * timstamp <= time < next timestamp. Will try to return something reasonably
351 * valid if best-fit could not be made. */
352 static off_t
mpeg_parser_seek_PTS(uint32_t time
, unsigned id
)
354 ssize_t pos_left
= 0;
355 ssize_t pos_right
= disk_buf
.filesize
;
356 ssize_t pos
, pos_new
;
357 uint32_t time_left
= str_parser
.start_pts
;
358 uint32_t time_right
= str_parser
.end_pts
;
360 uint32_t prevpts
= 0;
361 enum state_enum state
= STATE0
;
362 struct stream_scan sk
;
364 stream_scan_init(&sk
);
366 /* Initial estimate taken from average bitrate - later interpolations are
367 * taken similarly based on the remaining file interval */
368 pos_new
= muldiv_uint32(time
- time_left
, pos_right
- pos_left
,
369 time_right
- time_left
) + pos_left
;
371 /* return this estimated position if nothing better comes up */
374 DEBUGF("Seeking stream 0x%02x\n", id
);
375 DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n",
376 (unsigned)time_left
, (unsigned)time
, (unsigned)time_right
,
377 (long)pos_left
, (long)pos_new
, (long)pos_right
);
379 sk
.dir
= SSCAN_REVERSE
;
381 while (state
< STATE9
)
385 sk
.len
= (sk
.dir
< 0) ? pos_new
- pos_left
: pos_right
- pos_new
;
387 currpts
= mpeg_parser_scan_pts(&sk
, id
);
389 if (currpts
!= INVALID_TIMESTAMP
)
391 ssize_t pos_adj
; /* Adjustment to over or under-estimate */
393 /* Found a valid timestamp - see were it lies in relation to
397 /* Time at current position is before seek time - move
401 /* This is less than the desired time but greater than
402 * the currently seeked one; move the position up */
407 /* No next timestamp can be sooner */
408 pos_left
= sk
.pos
+ sk
.data
;
411 if (pos_right
<= pos_left
)
412 break; /* If the window disappeared - we're done */
414 pos_new
= muldiv_uint32(time
- time_left
,
415 pos_right
- pos_left
,
416 time_right
- time_left
);
417 /* Point is ahead of us - fudge estimate a bit high */
418 pos_adj
= pos_new
/ 10;
420 if (pos_adj
> 512*1024)
423 pos_new
+= pos_left
+ pos_adj
;
425 if (pos_new
>= pos_right
)
427 /* Estimate could push too far */
431 state
= STATE2
; /* Last scan was early */
432 sk
.dir
= SSCAN_REVERSE
;
434 DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
435 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
436 (unsigned)time_right
, (long)pos_left
, (long)pos_new
,
439 else if (currpts
> time
)
441 /* Time at current position is past seek time - move
444 time_right
= currpts
;
446 if (pos_right
<= pos_left
)
447 break; /* If the window disappeared - we're done */
449 pos_new
= muldiv_uint32(time
- time_left
,
450 pos_right
- pos_left
,
451 time_right
- time_left
);
452 /* Overshot the seek point - fudge estimate a bit low */
453 pos_adj
= pos_new
/ 10;
455 if (pos_adj
> 512*1024)
458 pos_new
+= pos_left
- pos_adj
;
460 state
= STATE3
; /* Last scan was late */
461 sk
.dir
= SSCAN_REVERSE
;
463 DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
464 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
465 (unsigned)time_right
, (long)pos_left
, (long)pos_new
,
470 /* Exact match - it happens */
471 DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
472 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
473 (unsigned)time_right
, (long)pos_left
, (long)pos_new
,
487 /* We already tried the bruteforce scan and failed again - no
488 * more stamps could possibly exist in the interval */
489 DEBUGF("!! no timestamp 2x\n");
492 /* Hardly likely except at very beginning - just do L->R scan
493 * to find something */
494 DEBUGF("!! no timestamp on first probe: %ld\n", sk
.pos
);
497 /* Could just be missing timestamps because the interval is
498 * narrowing down. A large block of data from another stream
499 * may also be in the midst of our chosen points which could
500 * cluster at either extreme end. If anything is there, this
503 sk
.dir
= SSCAN_FORWARD
;
504 DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
505 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
506 (unsigned)time_right
, (long)pos_left
, (long)pos_new
,
511 DEBUGF("?? Invalid state: %d\n", state
);
515 /* Same timestamp twice = quit */
516 if (currpts
== prevpts
)
518 DEBUGF("!! currpts == prevpts (stop)\n");
525 #if defined(DEBUG) || defined(SIMULATOR)
526 /* The next pts after the seeked-to position should be greater -
527 * most of the time - frames out of presentation order may muck it
530 sk
.len
= disk_buf
.filesize
;
531 sk
.dir
= SSCAN_FORWARD
;
533 uint32_t nextpts
= mpeg_parser_scan_pts(&sk
, id
);
534 DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n",
535 (long)pos
, (unsigned)pts
, (unsigned)time
, (unsigned)nextpts
);
537 if (pts
<= time
&& time
< nextpts
)
539 /* Smile - it worked */
540 DEBUGF(" :) pts<=time<next pts\n");
544 /* See where things ended up */
548 DEBUGF(" :\\ pts>time\n");
552 /* Weird - probably because of encoded order & tends to be right
553 * anyway if other criteria are met */
554 DEBUGF(" :p pts>=next pts\n");
559 DEBUGF(" :( time>=nextpts\n");
567 static bool prepare_image(uint32_t time
)
569 struct stream_scan sk
;
573 stream_scan_init(&sk
);
575 if (!str_send_msg(&video_str
, STREAM_NEEDS_SYNC
, time
))
577 DEBUGF("Image was ready\n");
578 return true; /* Should already have the image */
581 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
582 rb
->cpu_boost(true); /* No interference with trigger_cpu_boost */
585 str_send_msg(&video_str
, STREAM_RESET
, 0);
587 sk
.pos
= parser_can_seek() ?
588 mpeg_parser_seek_PTS(time
, video_str
.id
) : 0;
590 sk
.dir
= SSCAN_REVERSE
;
595 if (mpeg_parser_scan_start_code(&sk
, MPEG_START_GOP
))
597 DEBUGF("GOP found at: %ld\n", sk
.pos
);
599 unsigned id
= mpeg_parser_scan_pes(&sk
);
601 if (id
!= video_str
.id
&& sk
.pos
> 0)
603 /* Not part of our stream */
604 DEBUGF(" wrong stream: 0x%02x\n", id
);
608 /* This will hit the PES header since it's known to be there */
609 uint32_t pts
= mpeg_parser_scan_pts(&sk
, id
);
611 if (pts
== INVALID_TIMESTAMP
|| pts
> time
)
613 DEBUGF(" wrong timestamp: %u\n", (unsigned)pts
);
618 str_parser
.parms
.sd
.time
= time
;
619 str_parser
.parms
.sd
.sk
.pos
= MAX(sk
.pos
, 0);
620 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
621 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
623 DEBUGF("thumb pos:%ld len:%ld\n", str_parser
.parms
.sd
.sk
.pos
,
624 (long)str_parser
.parms
.sd
.sk
.len
);
626 result
= str_send_msg(&video_str
, STREAM_SYNC
,
627 (intptr_t)&str_parser
.parms
.sd
);
629 if (result
!= STREAM_PERFECT_MATCH
)
631 /* Two tries should be all that is nescessary to find the exact frame
632 * if the first GOP actually started later than the timestamp - the
633 * GOP just prior must then start on or earlier. */
638 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
639 rb
->cpu_boost(false);
642 return result
> STREAM_OK
;
645 static void prepare_audio(uint32_t time
)
649 if (!str_send_msg(&audio_str
, STREAM_NEEDS_SYNC
, time
))
651 DEBUGF("Audio was ready\n");
655 pos
= mpeg_parser_seek_PTS(time
, audio_str
.id
);
656 str_send_msg(&audio_str
, STREAM_RESET
, 0);
658 str_parser
.parms
.sd
.time
= time
;
659 str_parser
.parms
.sd
.sk
.pos
= pos
;
660 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
661 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
663 str_send_msg(&audio_str
, STREAM_SYNC
, (intptr_t)&str_parser
.parms
.sd
);
666 /* This function demuxes the streams and gives the next stream data
669 * STREAM_PM_STREAMING is for operation during playback. If the nescessary
670 * data and worst-case lookahead margin is not available, the stream is
671 * registered for notification when the data becomes available. If parsing
672 * extends beyond the end of the file or the end of stream marker is reached,
673 * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
675 * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
676 * If the file cache misses for the current position + lookahead, it will be
677 * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
680 * The results from one mode may be used as input to the other. Random access
681 * requires cooperation amongst threads to avoid evicting another stream's
684 static int parse_demux(struct stream
*str
, enum stream_parse_mode type
)
686 #define INC_BUF(offset) \
687 ({ off_t _o = (offset); \
688 str->hdr.win_right += _o; \
689 if ((p += _o) >= disk_buf.end) \
690 p -= disk_buf.size; })
692 static const int mpeg1_skip_table
[16] =
693 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
695 uint8_t *p
= str
->curr_packet_end
;
703 ssize_t length
, bytes
;
707 case STREAM_PM_STREAMING
:
708 /* Has the end been reached already? */
711 case SSTATE_PARSE
: /* Expected case first if no jumptable */
712 /* Are we at the end of file? */
713 if (str
->hdr
.win_left
< disk_buf
.filesize
)
715 str_end_of_stream(str
);
716 return STREAM_DATA_END
;
719 /* Is sync at the end of file? */
720 if (str
->hdr
.win_right
< disk_buf
.filesize
)
722 str_end_of_stream(str
);
725 return STREAM_DATA_END
;
728 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
730 /* This data range is not buffered yet - register stream to
731 * be notified when it becomes available. Stream is obliged
732 * to enter a TSTATE_DATA state if it must wait. */
733 int res
= str_next_data_not_ready(str
);
735 if (res
!= STREAM_OK
)
739 /* STREAM_PM_STREAMING: */
741 case STREAM_PM_RANDOM_ACCESS
:
742 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
744 if (str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
||
745 disk_buf_getbuffer(MIN_BUFAHEAD
, &p
, NULL
, NULL
) <= 0)
747 str_end_of_stream(str
);
748 return STREAM_DATA_END
;
751 str
->state
= SSTATE_SYNC
;
752 str
->hdr
.win_left
= str
->hdr
.pos
;
753 str
->curr_packet
= NULL
;
754 str
->curr_packet_end
= p
;
756 /* STREAM_PM_RANDOM_ACCESS: */
759 if (str
->state
== SSTATE_SYNC
)
761 /* Scanning for start code */
762 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
769 /* Found a start code - enter parse state */
770 str
->state
= SSTATE_PARSE
;
772 /* Pack header, skip it */
773 if (CMP_4_CONST(p
, PACK_START_CODE
))
775 /* Max lookahead: 14 */
776 if ((p
[4] & 0xc0) == 0x40) /* mpeg-2 */
778 /* Max delta: 14 + 7 = 21 */
779 /* Skip pack header and any stuffing bytes*/
780 bytes
= 14 + (p
[13] & 7);
782 else if ((p
[4] & 0xf0) == 0x20) /* mpeg-1 */
786 else /* unknown - skip it */
788 DEBUGF("weird pack header!\n");
795 /* System header, parse and skip it - 6 bytes + size */
796 if (CMP_4_CONST(p
, SYSTEM_HEADER_START_CODE
))
798 /* Skip start code */
799 /* Max Delta = 65535 + 6 = 65541 */
800 bytes
= 6 + ((p
[4] << 8) | p
[5]);
804 /* Packet header, parse it */
805 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
807 /* Problem? Meh...probably not but just a corrupted section.
808 * Try to resync the parser which will probably succeed. */
809 DEBUGF("packet start code prefix not found: 0x%02x\n"
811 " p:%p cp:%p cpe:%p\n"
812 " dbs:%p dbe:%p dbt:%p\n",
813 str
->id
, str
->hdr
.win_left
, str
->hdr
.win_right
,
814 p
, str
->curr_packet
, str
->curr_packet_end
,
815 disk_buf
.start
, disk_buf
.end
, disk_buf
.tail
);
816 str
->state
= SSTATE_SYNC
;
817 INC_BUF(1); /* Next byte - this one's no good */
821 /* We retrieve basic infos */
822 /* Maximum packet length: 6 + 65535 = 65541 */
824 length
= ((p
[4] << 8) | p
[5]) + 6;
830 case MPEG_STREAM_PROGRAM_END
:
832 str_end_of_stream(str
);
833 DEBUGF("MPEG program end: 0x%02x\n", str
->id
);
834 return STREAM_DATA_END
;
835 case MPEG_STREAM_PACK_HEADER
:
836 case MPEG_STREAM_SYSTEM_HEADER
:
837 /* These shouldn't be here - no increment or resync
838 * since we'll pick it up above. */
841 /* It's not the packet we're looking for, skip it */
847 /* Ok, it's our packet */
850 if ((header
[6] & 0xc0) == 0x80) /* mpeg2 */
852 /* Max Lookahead: 18 */
854 /* Max length: 9 + 255 = 264 */
855 length
= 9 + header
[8];
857 /* header points to the mpeg2 pes header */
858 if ((header
[7] & 0x80) != 0)
860 /* header has a pts */
861 uint32_t pts
= read_pts(header
, 9);
863 if (pts
!= INVALID_TIMESTAMP
)
867 /* DTS isn't used for anything since things just get
868 decoded ASAP but keep the code around */
869 if (STREAM_IS_VIDEO(id
))
871 /* Video stream - header may have a dts as well */
874 if (header
[7] & 0x40) != 0x00)
876 pts
= read_pts(header
, 14);
877 if (pts
!= INVALID_TIMESTAMP
)
882 str
->pkt_flags
|= PKT_HAS_TS
;
888 /* Max lookahead: 24 + 2 + 9 = 35 */
889 /* Max len_skip: 24 + 2 = 26 */
891 /* Max length: 24 + 2 + 9 = 35 */
897 while (header
[length
- 1] == 0xff)
901 DEBUGF("Too much stuffing" );
906 if ((header
[length
- 1] & 0xc0) == 0x40)
910 length
+= mpeg1_skip_table
[header
[length
- 1] >> 4];
912 /* Header points to the mpeg1 pes header */
913 ptsbuf
= header
+ len_skip
;
915 if ((ptsbuf
[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf
, -1))
917 /* header has a pts */
918 uint32_t pts
= read_pts(ptsbuf
, -1);
920 if (pts
!= INVALID_TIMESTAMP
)
924 /* DTS isn't used for anything since things just get
925 decoded ASAP but keep the code around */
926 if (STREAM_IS_VIDEO(id
))
928 /* Video stream - header may have a dts as well */
931 if (ptsbuf
[-1] & 0xf0) == 0x30)
933 pts
= read_pts(ptsbuf
, 4);
935 if (pts
!= INVALID_TIMESTAMP
)
940 str
->pkt_flags
|= PKT_HAS_TS
;
946 /* Max bytes: 6 + 65535 - 7 = 65534 */
947 bytes
= 6 + (header
[4] << 8) + header
[5] - length
;
949 str
->curr_packet
= p
;
950 str
->curr_packet_end
= p
+ bytes
;
951 str
->hdr
.win_left
= str
->hdr
.win_right
+ length
;
952 str
->hdr
.win_right
= str
->hdr
.win_left
+ bytes
;
954 if (str
->hdr
.win_right
> disk_buf
.filesize
)
956 /* No packet that exceeds end of file can be valid */
957 str_end_of_stream(str
);
958 return STREAM_DATA_END
;
967 /* This simply reads data from the file one page at a time and returns a
968 * pointer to it in the buffer. */
969 static int parse_elementary(struct stream
*str
, enum stream_parse_mode type
)
978 case STREAM_PM_STREAMING
:
979 /* Has the end been reached already? */
980 if (str
->state
== SSTATE_END
)
981 return STREAM_DATA_END
;
983 /* Are we at the end of file? */
984 if (str
->hdr
.win_left
>= disk_buf
.filesize
)
986 str_end_of_stream(str
);
987 return STREAM_DATA_END
;
990 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
992 /* This data range is not buffered yet - register stream to
993 * be notified when it becomes available. Stream is obliged
994 * to enter a TSTATE_DATA state if it must wait. */
995 int res
= str_next_data_not_ready(str
);
997 if (res
!= STREAM_OK
)
1001 len
= DISK_BUF_PAGE_SIZE
;
1003 if ((size_t)(str
->hdr
.win_right
+ len
) > (size_t)disk_buf
.filesize
)
1004 len
= disk_buf
.filesize
- str
->hdr
.win_right
;
1008 str_end_of_stream(str
);
1009 return STREAM_DATA_END
;
1012 p
= str
->curr_packet_end
;
1013 if (p
>= disk_buf
.end
)
1016 /* STREAM_PM_STREAMING: */
1018 case STREAM_PM_RANDOM_ACCESS
:
1019 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
1020 len
= disk_buf_getbuffer(DISK_BUF_PAGE_SIZE
, &p
, NULL
, NULL
);
1022 if (len
<= 0 || str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
)
1024 str_end_of_stream(str
);
1025 return STREAM_DATA_END
;
1028 /* STREAM_PM_RANDOM_ACCESS: */
1031 str
->state
= SSTATE_PARSE
;
1032 str
->curr_packet
= p
;
1033 str
->curr_packet_end
= p
+ len
;
1034 str
->hdr
.win_left
= str
->hdr
.win_right
;
1035 str
->hdr
.win_right
= str
->hdr
.win_left
+ len
;
1040 intptr_t parser_send_video_msg(long id
, intptr_t data
)
1042 intptr_t retval
= 0;
1044 if (video_str
.thread
!= 0 && disk_buf
.in_file
>= 0)
1046 /* Hook certain messages since they involve multiple operations
1047 * behind the scenes */
1050 case VIDEO_DISPLAY_SHOW
:
1051 if (data
!= 0 && disk_buf_status() == STREAM_STOPPED
)
1052 { /* Only prepare image if showing and not playing */
1053 prepare_image(str_parser
.last_seek_time
);
1057 case VIDEO_PRINT_FRAME
:
1060 case VIDEO_PRINT_THUMBNAIL
:
1061 if (disk_buf_status() != STREAM_STOPPED
)
1062 break; /* Prepare image if not playing */
1064 if (!prepare_image(str_parser
.last_seek_time
))
1065 return false; /* Preparation failed */
1067 /* Image ready - pass message to video thread */
1071 retval
= str_send_msg(&video_str
, id
, data
);
1077 /* Seek parser to the specified time and return absolute time.
1078 * No actual hard stuff is performed here. That's done when streaming is
1079 * about to begin or something from the current position is requested */
1080 uint32_t parser_seek_time(uint32_t time
)
1082 if (!parser_can_seek())
1084 else if (time
> str_parser
.duration
)
1085 time
= str_parser
.duration
;
1087 str_parser
.last_seek_time
= time
+ str_parser
.start_pts
;
1088 return str_parser
.last_seek_time
;
1091 void parser_prepare_streaming(void)
1093 struct stream_window sw
;
1095 DEBUGF("parser_prepare_streaming\n");
1097 /* Prepare initial video frame */
1098 prepare_image(str_parser
.last_seek_time
);
1100 /* Sync audio stream */
1101 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1102 prepare_audio(str_parser
.last_seek_time
);
1104 /* Prequeue some data and set buffer window */
1105 if (!stream_get_window(&sw
))
1106 sw
.left
= sw
.right
= disk_buf
.filesize
;
1108 DEBUGF(" swl:%ld swr:%ld\n", sw
.left
, sw
.right
);
1110 if (sw
.right
> disk_buf
.filesize
- 4*MIN_BUFAHEAD
)
1111 sw
.right
= disk_buf
.filesize
- 4*MIN_BUFAHEAD
;
1113 disk_buf_prepare_streaming(sw
.left
,
1114 sw
.right
- sw
.left
+ 4*MIN_BUFAHEAD
);
1117 int parser_init_stream(void)
1119 if (disk_buf
.in_file
< 0)
1120 return STREAM_ERROR
;
1122 /* TODO: Actually find which streams are available */
1123 audio_str
.id
= MPEG_STREAM_AUDIO_FIRST
;
1124 video_str
.id
= MPEG_STREAM_VIDEO_FIRST
;
1126 /* Try to pull a video PES - if not found, try video init anyway which
1127 * should succeed if it really is a video-only stream */
1128 video_str
.hdr
.pos
= 0;
1129 video_str
.hdr
.limit
= 256*1024;
1131 if (parse_demux(&video_str
, STREAM_PM_RANDOM_ACCESS
) == STREAM_OK
)
1133 /* Found a video packet - assume program stream */
1134 str_parser
.format
= STREAM_FMT_MPEG_PS
;
1135 str_parser
.next_data
= parse_demux
;
1139 /* No PES element found - assume video elementary stream */
1140 str_parser
.format
= STREAM_FMT_MPV
;
1141 str_parser
.next_data
= parse_elementary
;
1144 if (!init_video_info())
1146 /* Cannot determine video size, etc. */
1147 return STREAM_UNSUPPORTED
;
1150 if (str_parser
.format
== STREAM_FMT_MPEG_PS
)
1152 /* Initalize start_pts and end_pts with the length (in 45kHz units) of
1153 * the movie. INVALID_TIMESTAMP if the time could not be determined */
1154 init_times(&audio_str
);
1155 init_times(&video_str
);
1157 if (video_str
.start_pts
== INVALID_TIMESTAMP
)
1159 /* Must have video at least */
1160 return STREAM_UNSUPPORTED
;
1163 str_parser
.flags
|= STREAMF_CAN_SEEK
;
1165 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1167 /* Overall duration is maximum span */
1168 str_parser
.start_pts
= MIN(audio_str
.start_pts
, video_str
.start_pts
);
1169 str_parser
.end_pts
= MAX(audio_str
.end_pts
, video_str
.end_pts
);
1171 /* Audio will be part of playback pool */
1172 stream_add_stream(&audio_str
);
1176 /* No audio stream - use video only */
1177 str_parser
.start_pts
= video_str
.start_pts
;
1178 str_parser
.end_pts
= video_str
.end_pts
;
1181 str_parser
.last_seek_time
= str_parser
.start_pts
;
1185 /* There's no way to handle times on this without a full file
1187 audio_str
.start_pts
= INVALID_TIMESTAMP
;
1188 audio_str
.end_pts
= INVALID_TIMESTAMP
;
1189 video_str
.start_pts
= 0;
1190 video_str
.end_pts
= INVALID_TIMESTAMP
;
1191 str_parser
.start_pts
= 0;
1192 str_parser
.end_pts
= INVALID_TIMESTAMP
;
1195 /* Add video to playback pool */
1196 stream_add_stream(&video_str
);
1198 /* Cache duration - it's used very often */
1199 str_parser
.duration
= str_parser
.end_pts
- str_parser
.start_pts
;
1201 DEBUGF("Movie info:\n"
1206 str_parser
.dims
.w
, str_parser
.dims
.h
,
1207 (unsigned)str_parser
.start_pts
,
1208 (unsigned)str_parser
.end_pts
,
1209 (unsigned)str_parser
.duration
);
1214 void parser_close_stream(void)
1216 stream_remove_streams();
1217 parser_init_state();
1220 bool parser_init(void)
1222 parser_init_state();