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 void prepare_audio(uint32_t time
)
571 if (!str_send_msg(&audio_str
, STREAM_NEEDS_SYNC
, time
))
573 DEBUGF("Audio was ready\n");
577 pos
= mpeg_parser_seek_PTS(time
, audio_str
.id
);
578 str_send_msg(&audio_str
, STREAM_RESET
, 0);
580 str_parser
.parms
.sd
.time
= time
;
581 str_parser
.parms
.sd
.sk
.pos
= pos
;
582 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
583 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
585 str_send_msg(&audio_str
, STREAM_SYNC
, (intptr_t)&str_parser
.parms
.sd
);
588 /* This function demuxes the streams and gives the next stream data
591 * STREAM_PM_STREAMING is for operation during playback. If the nescessary
592 * data and worst-case lookahead margin is not available, the stream is
593 * registered for notification when the data becomes available. If parsing
594 * extends beyond the end of the file or the end of stream marker is reached,
595 * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
597 * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
598 * If the file cache misses for the current position + lookahead, it will be
599 * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
602 * The results from one mode may be used as input to the other. Random access
603 * requires cooperation amongst threads to avoid evicting another stream's
606 static int parse_demux(struct stream
*str
, enum stream_parse_mode type
)
608 #define INC_BUF(offset) \
609 ({ off_t _o = (offset); \
610 str->hdr.win_right += _o; \
611 if ((p += _o) >= disk_buf.end) \
612 p -= disk_buf.size; })
614 static const int mpeg1_skip_table
[16] =
615 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
617 uint8_t *p
= str
->curr_packet_end
;
625 ssize_t length
, bytes
;
629 case STREAM_PM_STREAMING
:
630 /* Has the end been reached already? */
633 case SSTATE_PARSE
: /* Expected case first if no jumptable */
634 /* Are we at the end of file? */
635 if (str
->hdr
.win_left
< disk_buf
.filesize
)
637 str_end_of_stream(str
);
638 return STREAM_DATA_END
;
641 /* Is sync at the end of file? */
642 if (str
->hdr
.win_right
< disk_buf
.filesize
)
644 str_end_of_stream(str
);
647 return STREAM_DATA_END
;
650 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
652 /* This data range is not buffered yet - register stream to
653 * be notified when it becomes available. Stream is obliged
654 * to enter a TSTATE_DATA state if it must wait. */
655 int res
= str_next_data_not_ready(str
);
657 if (res
!= STREAM_OK
)
661 /* STREAM_PM_STREAMING: */
663 case STREAM_PM_RANDOM_ACCESS
:
664 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
666 if (str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
||
667 disk_buf_getbuffer(MIN_BUFAHEAD
, &p
, NULL
, NULL
) <= 0)
669 str_end_of_stream(str
);
670 return STREAM_DATA_END
;
673 str
->state
= SSTATE_SYNC
;
674 str
->hdr
.win_left
= str
->hdr
.pos
;
675 str
->curr_packet
= NULL
;
676 str
->curr_packet_end
= p
;
678 /* STREAM_PM_RANDOM_ACCESS: */
681 if (str
->state
== SSTATE_SYNC
)
683 /* Scanning for start code */
684 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
691 /* Found a start code - enter parse state */
692 str
->state
= SSTATE_PARSE
;
694 /* Pack header, skip it */
695 if (CMP_4_CONST(p
, PACK_START_CODE
))
697 /* Max lookahead: 14 */
698 if ((p
[4] & 0xc0) == 0x40) /* mpeg-2 */
700 /* Max delta: 14 + 7 = 21 */
701 /* Skip pack header and any stuffing bytes*/
702 bytes
= 14 + (p
[13] & 7);
704 else if ((p
[4] & 0xf0) == 0x20) /* mpeg-1 */
708 else /* unknown - skip it */
710 DEBUGF("weird pack header!\n");
717 /* System header, parse and skip it - 6 bytes + size */
718 if (CMP_4_CONST(p
, SYSTEM_HEADER_START_CODE
))
720 /* Skip start code */
721 /* Max Delta = 65535 + 6 = 65541 */
722 bytes
= 6 + ((p
[4] << 8) | p
[5]);
726 /* Packet header, parse it */
727 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
729 /* Problem? Meh...probably not but just a corrupted section.
730 * Try to resync the parser which will probably succeed. */
731 DEBUGF("packet start code prefix not found: 0x%02x\n"
733 " p:%p cp:%p cpe:%p\n"
734 " dbs:%p dbe:%p dbt:%p\n",
735 str
->id
, str
->hdr
.win_left
, str
->hdr
.win_right
,
736 p
, str
->curr_packet
, str
->curr_packet_end
,
737 disk_buf
.start
, disk_buf
.end
, disk_buf
.tail
);
738 str
->state
= SSTATE_SYNC
;
739 INC_BUF(1); /* Next byte - this one's no good */
743 /* We retrieve basic infos */
744 /* Maximum packet length: 6 + 65535 = 65541 */
746 length
= ((p
[4] << 8) | p
[5]) + 6;
752 case MPEG_STREAM_PROGRAM_END
:
754 str_end_of_stream(str
);
755 DEBUGF("MPEG program end: 0x%02x\n", str
->id
);
756 return STREAM_DATA_END
;
757 case MPEG_STREAM_PACK_HEADER
:
758 case MPEG_STREAM_SYSTEM_HEADER
:
759 /* These shouldn't be here - no increment or resync
760 * since we'll pick it up above. */
763 /* It's not the packet we're looking for, skip it */
769 /* Ok, it's our packet */
772 if ((header
[6] & 0xc0) == 0x80) /* mpeg2 */
774 /* Max Lookahead: 18 */
776 /* Max length: 9 + 255 = 264 */
777 length
= 9 + header
[8];
779 /* header points to the mpeg2 pes header */
780 if ((header
[7] & 0x80) != 0)
782 /* header has a pts */
783 uint32_t pts
= read_pts(header
, 9);
785 if (pts
!= INVALID_TIMESTAMP
)
789 /* DTS isn't used for anything since things just get
790 decoded ASAP but keep the code around */
791 if (STREAM_IS_VIDEO(id
))
793 /* Video stream - header may have a dts as well */
796 if (header
[7] & 0x40) != 0x00)
798 pts
= read_pts(header
, 14);
799 if (pts
!= INVALID_TIMESTAMP
)
804 str
->pkt_flags
|= PKT_HAS_TS
;
810 /* Max lookahead: 24 + 2 + 9 = 35 */
811 /* Max len_skip: 24 + 2 = 26 */
813 /* Max length: 24 + 2 + 9 = 35 */
819 while (header
[length
- 1] == 0xff)
823 DEBUGF("Too much stuffing" );
828 if ((header
[length
- 1] & 0xc0) == 0x40)
832 length
+= mpeg1_skip_table
[header
[length
- 1] >> 4];
834 /* Header points to the mpeg1 pes header */
835 ptsbuf
= header
+ len_skip
;
837 if ((ptsbuf
[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf
, -1))
839 /* header has a pts */
840 uint32_t pts
= read_pts(ptsbuf
, -1);
842 if (pts
!= INVALID_TIMESTAMP
)
846 /* DTS isn't used for anything since things just get
847 decoded ASAP but keep the code around */
848 if (STREAM_IS_VIDEO(id
))
850 /* Video stream - header may have a dts as well */
853 if (ptsbuf
[-1] & 0xf0) == 0x30)
855 pts
= read_pts(ptsbuf
, 4);
857 if (pts
!= INVALID_TIMESTAMP
)
862 str
->pkt_flags
|= PKT_HAS_TS
;
868 /* Max bytes: 6 + 65535 - 7 = 65534 */
869 bytes
= 6 + (header
[4] << 8) + header
[5] - length
;
871 str
->curr_packet
= p
;
872 str
->curr_packet_end
= p
+ bytes
;
873 str
->hdr
.win_left
= str
->hdr
.win_right
+ length
;
874 str
->hdr
.win_right
= str
->hdr
.win_left
+ bytes
;
876 if (str
->hdr
.win_right
> disk_buf
.filesize
)
878 /* No packet that exceeds end of file can be valid */
879 str_end_of_stream(str
);
880 return STREAM_DATA_END
;
889 /* This simply reads data from the file one page at a time and returns a
890 * pointer to it in the buffer. */
891 static int parse_elementary(struct stream
*str
, enum stream_parse_mode type
)
900 case STREAM_PM_STREAMING
:
901 /* Has the end been reached already? */
902 if (str
->state
== SSTATE_END
)
903 return STREAM_DATA_END
;
905 /* Are we at the end of file? */
906 if (str
->hdr
.win_left
>= disk_buf
.filesize
)
908 str_end_of_stream(str
);
909 return STREAM_DATA_END
;
912 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
914 /* This data range is not buffered yet - register stream to
915 * be notified when it becomes available. Stream is obliged
916 * to enter a TSTATE_DATA state if it must wait. */
917 int res
= str_next_data_not_ready(str
);
919 if (res
!= STREAM_OK
)
923 len
= DISK_BUF_PAGE_SIZE
;
925 if ((size_t)(str
->hdr
.win_right
+ len
) > (size_t)disk_buf
.filesize
)
926 len
= disk_buf
.filesize
- str
->hdr
.win_right
;
930 str_end_of_stream(str
);
931 return STREAM_DATA_END
;
934 p
= str
->curr_packet_end
;
935 if (p
>= disk_buf
.end
)
938 /* STREAM_PM_STREAMING: */
940 case STREAM_PM_RANDOM_ACCESS
:
941 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
942 len
= disk_buf_getbuffer(DISK_BUF_PAGE_SIZE
, &p
, NULL
, NULL
);
944 if (len
<= 0 || str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
)
946 str_end_of_stream(str
);
947 return STREAM_DATA_END
;
950 /* STREAM_PM_RANDOM_ACCESS: */
953 str
->state
= SSTATE_PARSE
;
954 str
->curr_packet
= p
;
955 str
->curr_packet_end
= p
+ len
;
956 str
->hdr
.win_left
= str
->hdr
.win_right
;
957 str
->hdr
.win_right
= str
->hdr
.win_left
+ len
;
962 bool parser_prepare_image(uint32_t time
)
964 struct stream_scan sk
;
968 stream_scan_init(&sk
);
970 if (!str_send_msg(&video_str
, STREAM_NEEDS_SYNC
, time
))
972 DEBUGF("Image was ready\n");
973 return true; /* Should already have the image */
976 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
977 rb
->cpu_boost(true); /* No interference with trigger_cpu_boost */
980 str_send_msg(&video_str
, STREAM_RESET
, 0);
982 sk
.pos
= parser_can_seek() ?
983 mpeg_parser_seek_PTS(time
, video_str
.id
) : 0;
985 sk
.dir
= SSCAN_REVERSE
;
990 if (mpeg_parser_scan_start_code(&sk
, MPEG_START_GOP
))
992 DEBUGF("GOP found at: %ld\n", sk
.pos
);
994 unsigned id
= mpeg_parser_scan_pes(&sk
);
996 if (id
!= video_str
.id
&& sk
.pos
> 0)
998 /* Not part of our stream */
999 DEBUGF(" wrong stream: 0x%02x\n", id
);
1003 /* This will hit the PES header since it's known to be there */
1004 uint32_t pts
= mpeg_parser_scan_pts(&sk
, id
);
1006 if (pts
== INVALID_TIMESTAMP
|| pts
> time
)
1008 DEBUGF(" wrong timestamp: %u\n", (unsigned)pts
);
1013 str_parser
.parms
.sd
.time
= time
;
1014 str_parser
.parms
.sd
.sk
.pos
= MAX(sk
.pos
, 0);
1015 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
1016 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
1018 DEBUGF("thumb pos:%ld len:%ld\n", str_parser
.parms
.sd
.sk
.pos
,
1019 (long)str_parser
.parms
.sd
.sk
.len
);
1021 result
= str_send_msg(&video_str
, STREAM_SYNC
,
1022 (intptr_t)&str_parser
.parms
.sd
);
1024 if (result
!= STREAM_PERFECT_MATCH
)
1026 /* Two tries should be all that is nescessary to find the exact frame
1027 * if the first GOP actually started later than the timestamp - the
1028 * GOP just prior must then start on or earlier. */
1033 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1034 rb
->cpu_boost(false);
1037 return result
> STREAM_OK
;
1040 /* Seek parser to the specified time and return absolute time.
1041 * No actual hard stuff is performed here. That's done when streaming is
1042 * about to begin or something from the current position is requested */
1043 uint32_t parser_seek_time(uint32_t time
)
1045 if (!parser_can_seek())
1047 else if (time
> str_parser
.duration
)
1048 time
= str_parser
.duration
;
1050 str_parser
.last_seek_time
= time
+ str_parser
.start_pts
;
1051 return str_parser
.last_seek_time
;
1054 void parser_prepare_streaming(void)
1056 struct stream_window sw
;
1058 DEBUGF("parser_prepare_streaming\n");
1060 /* Prepare initial video frame */
1061 parser_prepare_image(str_parser
.last_seek_time
);
1063 /* Sync audio stream */
1064 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1065 prepare_audio(str_parser
.last_seek_time
);
1067 /* Prequeue some data and set buffer window */
1068 if (!stream_get_window(&sw
))
1069 sw
.left
= sw
.right
= disk_buf
.filesize
;
1071 DEBUGF(" swl:%ld swr:%ld\n", sw
.left
, sw
.right
);
1073 if (sw
.right
> disk_buf
.filesize
- 4*MIN_BUFAHEAD
)
1074 sw
.right
= disk_buf
.filesize
- 4*MIN_BUFAHEAD
;
1076 disk_buf_prepare_streaming(sw
.left
,
1077 sw
.right
- sw
.left
+ 4*MIN_BUFAHEAD
);
1080 int parser_init_stream(void)
1082 if (disk_buf
.in_file
< 0)
1083 return STREAM_ERROR
;
1085 /* TODO: Actually find which streams are available */
1086 audio_str
.id
= MPEG_STREAM_AUDIO_FIRST
;
1087 video_str
.id
= MPEG_STREAM_VIDEO_FIRST
;
1089 /* Try to pull a video PES - if not found, try video init anyway which
1090 * should succeed if it really is a video-only stream */
1091 video_str
.hdr
.pos
= 0;
1092 video_str
.hdr
.limit
= 256*1024;
1094 if (parse_demux(&video_str
, STREAM_PM_RANDOM_ACCESS
) == STREAM_OK
)
1096 /* Found a video packet - assume program stream */
1097 str_parser
.format
= STREAM_FMT_MPEG_PS
;
1098 str_parser
.next_data
= parse_demux
;
1102 /* No PES element found - assume video elementary stream */
1103 str_parser
.format
= STREAM_FMT_MPV
;
1104 str_parser
.next_data
= parse_elementary
;
1107 if (!init_video_info())
1109 /* Cannot determine video size, etc. */
1110 parser_init_state();
1111 return STREAM_UNSUPPORTED
;
1114 if (str_parser
.format
== STREAM_FMT_MPEG_PS
)
1116 /* Initalize start_pts and end_pts with the length (in 45kHz units) of
1117 * the movie. INVALID_TIMESTAMP if the time could not be determined */
1118 init_times(&audio_str
);
1119 init_times(&video_str
);
1121 if (video_str
.start_pts
== INVALID_TIMESTAMP
)
1123 /* Must have video at least */
1124 parser_init_state();
1125 return STREAM_UNSUPPORTED
;
1128 str_parser
.flags
|= STREAMF_CAN_SEEK
;
1130 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1132 /* Overall duration is maximum span */
1133 str_parser
.start_pts
= MIN(audio_str
.start_pts
, video_str
.start_pts
);
1134 str_parser
.end_pts
= MAX(audio_str
.end_pts
, video_str
.end_pts
);
1136 /* Audio will be part of playback pool */
1137 stream_add_stream(&audio_str
);
1141 /* No audio stream - use video only */
1142 str_parser
.start_pts
= video_str
.start_pts
;
1143 str_parser
.end_pts
= video_str
.end_pts
;
1146 str_parser
.last_seek_time
= str_parser
.start_pts
;
1150 /* There's no way to handle times on this without a full file
1152 audio_str
.start_pts
= INVALID_TIMESTAMP
;
1153 audio_str
.end_pts
= INVALID_TIMESTAMP
;
1154 video_str
.start_pts
= 0;
1155 video_str
.end_pts
= INVALID_TIMESTAMP
;
1156 str_parser
.start_pts
= 0;
1157 str_parser
.end_pts
= INVALID_TIMESTAMP
;
1160 /* Add video to playback pool */
1161 stream_add_stream(&video_str
);
1163 /* Cache duration - it's used very often */
1164 str_parser
.duration
= str_parser
.end_pts
- str_parser
.start_pts
;
1166 DEBUGF("Movie info:\n"
1171 str_parser
.dims
.w
, str_parser
.dims
.h
,
1172 (unsigned)str_parser
.start_pts
,
1173 (unsigned)str_parser
.end_pts
,
1174 (unsigned)str_parser
.duration
);
1179 void parser_close_stream(void)
1181 stream_remove_streams();
1182 parser_init_state();
1185 bool parser_init(void)
1187 parser_init_state();