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 bool init_times(struct stream
*str
)
277 struct stream tmp_str
;
278 const ssize_t filesize
= disk_buf_filesize();
279 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 found_stream
= false;
287 str
->start_pts
= INVALID_TIMESTAMP
;
288 str
->end_pts
= INVALID_TIMESTAMP
;
290 tmp_str
.id
= str
->id
;
292 tmp_str
.hdr
.limit
= max_probe
;
294 /* Probe for many for the start because some stamps could be anomalous.
295 * Video also can also have things out of order. Just see what it's got.
299 switch (parser_get_next_data(&tmp_str
, STREAM_PM_RANDOM_ACCESS
))
301 case STREAM_DATA_END
:
305 if (tmp_str
.pkt_flags
& PKT_HAS_TS
)
307 if (tmp_str
.pts
< str
->start_pts
)
308 str
->start_pts
= tmp_str
.pts
;
318 DEBUGF(" stream not found:0x%02x\n", str
->id
);
322 DEBUGF(" start:%u\n", (unsigned)str
->start_pts
);
324 /* Use the decoder thread to perform a synchronized search - no
325 * decoding should take place but just a simple run through timestamps
326 * and durations as the decoder would see them. This should give the
327 * precise time at the end of the last frame for the stream. */
328 DEBUGF("Finding end_pts: 0x%02x\n", str
->id
);
330 str_parser
.parms
.sd
.time
= MAX_TIMESTAMP
;
331 str_parser
.parms
.sd
.sk
.pos
= filesize
- max_probe
;
332 str_parser
.parms
.sd
.sk
.len
= max_probe
;
333 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
335 str_send_msg(str
, STREAM_RESET
, 0);
337 if (str_send_msg(str
, STREAM_FIND_END_TIME
,
338 (intptr_t)&str_parser
.parms
.sd
) == STREAM_PERFECT_MATCH
)
340 str
->end_pts
= str_parser
.parms
.sd
.time
;
341 DEBUGF(" end:%u\n", (unsigned)str
->end_pts
);
347 static bool check_times(const struct stream
*str
)
349 return str
->start_pts
< str
->end_pts
&&
350 str
->end_pts
!= INVALID_TIMESTAMP
;
353 /* Return the best-fit file offset of a timestamp in the PES where
354 * timstamp <= time < next timestamp. Will try to return something reasonably
355 * valid if best-fit could not be made. */
356 static off_t
mpeg_parser_seek_PTS(uint32_t time
, unsigned id
)
358 ssize_t pos_left
= 0;
359 ssize_t pos_right
= disk_buf
.filesize
;
360 ssize_t pos
, pos_new
;
361 uint32_t time_left
= str_parser
.start_pts
;
362 uint32_t time_right
= str_parser
.end_pts
;
364 uint32_t prevpts
= 0;
365 enum state_enum state
= STATE0
;
366 struct stream_scan sk
;
368 stream_scan_init(&sk
);
370 /* Initial estimate taken from average bitrate - later interpolations are
371 * taken similarly based on the remaining file interval */
372 pos_new
= muldiv_uint32(time
- time_left
, pos_right
- pos_left
,
373 time_right
- time_left
) + pos_left
;
375 /* return this estimated position if nothing better comes up */
378 DEBUGF("Seeking stream 0x%02x\n", id
);
379 DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n",
380 (unsigned)time_left
, (unsigned)time
, (unsigned)time_right
,
381 (long)pos_left
, (long)pos_new
, (long)pos_right
);
383 sk
.dir
= SSCAN_REVERSE
;
385 while (state
< STATE9
)
389 sk
.len
= (sk
.dir
< 0) ? pos_new
- pos_left
: pos_right
- pos_new
;
391 currpts
= mpeg_parser_scan_pts(&sk
, id
);
393 if (currpts
!= INVALID_TIMESTAMP
)
395 ssize_t pos_adj
; /* Adjustment to over or under-estimate */
397 /* Found a valid timestamp - see were it lies in relation to
401 /* Time at current position is before seek time - move
405 /* This is less than the desired time but greater than
406 * the currently seeked one; move the position up */
411 /* No next timestamp can be sooner */
412 pos_left
= sk
.pos
+ sk
.data
;
415 if (pos_right
<= pos_left
)
416 break; /* If the window disappeared - we're done */
418 pos_new
= muldiv_uint32(time
- time_left
,
419 pos_right
- pos_left
,
420 time_right
- time_left
);
421 /* Point is ahead of us - fudge estimate a bit high */
422 pos_adj
= pos_new
/ 10;
424 if (pos_adj
> 512*1024)
427 pos_new
+= pos_left
+ pos_adj
;
429 if (pos_new
>= pos_right
)
431 /* Estimate could push too far */
435 state
= STATE2
; /* Last scan was early */
436 sk
.dir
= SSCAN_REVERSE
;
438 DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
439 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
440 (unsigned)time_right
, (long)pos_left
, (long)pos_new
,
443 else if (currpts
> time
)
445 /* Time at current position is past seek time - move
448 time_right
= currpts
;
450 if (pos_right
<= pos_left
)
451 break; /* If the window disappeared - we're done */
453 pos_new
= muldiv_uint32(time
- time_left
,
454 pos_right
- pos_left
,
455 time_right
- time_left
);
456 /* Overshot the seek point - fudge estimate a bit low */
457 pos_adj
= pos_new
/ 10;
459 if (pos_adj
> 512*1024)
462 pos_new
+= pos_left
- pos_adj
;
464 state
= STATE3
; /* Last scan was late */
465 sk
.dir
= SSCAN_REVERSE
;
467 DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
468 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
469 (unsigned)time_right
, (long)pos_left
, (long)pos_new
,
474 /* Exact match - it happens */
475 DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
476 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
477 (unsigned)time_right
, (long)pos_left
, (long)pos_new
,
491 /* We already tried the bruteforce scan and failed again - no
492 * more stamps could possibly exist in the interval */
493 DEBUGF("!! no timestamp 2x\n");
496 /* Hardly likely except at very beginning - just do L->R scan
497 * to find something */
498 DEBUGF("!! no timestamp on first probe: %ld\n", sk
.pos
);
501 /* Could just be missing timestamps because the interval is
502 * narrowing down. A large block of data from another stream
503 * may also be in the midst of our chosen points which could
504 * cluster at either extreme end. If anything is there, this
507 sk
.dir
= SSCAN_FORWARD
;
508 DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
509 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
510 (unsigned)time_right
, (long)pos_left
, (long)pos_new
,
515 DEBUGF("?? Invalid state: %d\n", state
);
519 /* Same timestamp twice = quit */
520 if (currpts
== prevpts
)
522 DEBUGF("!! currpts == prevpts (stop)\n");
529 #if defined(DEBUG) || defined(SIMULATOR)
530 /* The next pts after the seeked-to position should be greater -
531 * most of the time - frames out of presentation order may muck it
534 sk
.len
= disk_buf
.filesize
;
535 sk
.dir
= SSCAN_FORWARD
;
537 uint32_t nextpts
= mpeg_parser_scan_pts(&sk
, id
);
538 DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n",
539 (long)pos
, (unsigned)pts
, (unsigned)time
, (unsigned)nextpts
);
541 if (pts
<= time
&& time
< nextpts
)
543 /* Smile - it worked */
544 DEBUGF(" :) pts<=time<next pts\n");
548 /* See where things ended up */
552 DEBUGF(" :\\ pts>time\n");
556 /* Weird - probably because of encoded order & tends to be right
557 * anyway if other criteria are met */
558 DEBUGF(" :p pts>=next pts\n");
563 DEBUGF(" :( time>=nextpts\n");
571 static void prepare_audio(uint32_t time
)
575 if (!str_send_msg(&audio_str
, STREAM_NEEDS_SYNC
, time
))
577 DEBUGF("Audio was ready\n");
581 pos
= mpeg_parser_seek_PTS(time
, audio_str
.id
);
582 str_send_msg(&audio_str
, STREAM_RESET
, 0);
584 str_parser
.parms
.sd
.time
= time
;
585 str_parser
.parms
.sd
.sk
.pos
= pos
;
586 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
587 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
589 str_send_msg(&audio_str
, STREAM_SYNC
, (intptr_t)&str_parser
.parms
.sd
);
592 /* This function demuxes the streams and gives the next stream data
595 * STREAM_PM_STREAMING is for operation during playback. If the nescessary
596 * data and worst-case lookahead margin is not available, the stream is
597 * registered for notification when the data becomes available. If parsing
598 * extends beyond the end of the file or the end of stream marker is reached,
599 * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
601 * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
602 * If the file cache misses for the current position + lookahead, it will be
603 * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
606 * The results from one mode may be used as input to the other. Random access
607 * requires cooperation amongst threads to avoid evicting another stream's
610 static int parse_demux(struct stream
*str
, enum stream_parse_mode type
)
612 #define INC_BUF(offset) \
613 ({ off_t _o = (offset); \
614 str->hdr.win_right += _o; \
615 if ((p += _o) >= disk_buf.end) \
616 p -= disk_buf.size; })
618 static const int mpeg1_skip_table
[16] =
619 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
621 uint8_t *p
= str
->curr_packet_end
;
629 ssize_t length
, bytes
;
633 case STREAM_PM_STREAMING
:
634 /* Has the end been reached already? */
637 case SSTATE_PARSE
: /* Expected case first if no jumptable */
638 /* Are we at the end of file? */
639 if (str
->hdr
.win_left
< disk_buf
.filesize
)
641 str_end_of_stream(str
);
642 return STREAM_DATA_END
;
645 /* Is sync at the end of file? */
646 if (str
->hdr
.win_right
< disk_buf
.filesize
)
648 str_end_of_stream(str
);
651 return STREAM_DATA_END
;
654 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
656 /* This data range is not buffered yet - register stream to
657 * be notified when it becomes available. Stream is obliged
658 * to enter a TSTATE_DATA state if it must wait. */
659 int res
= str_next_data_not_ready(str
);
661 if (res
!= STREAM_OK
)
665 /* STREAM_PM_STREAMING: */
667 case STREAM_PM_RANDOM_ACCESS
:
668 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
670 if (str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
||
671 disk_buf_getbuffer(MIN_BUFAHEAD
, &p
, NULL
, NULL
) <= 0)
673 str_end_of_stream(str
);
674 return STREAM_DATA_END
;
677 str
->state
= SSTATE_SYNC
;
678 str
->hdr
.win_left
= str
->hdr
.pos
;
679 str
->curr_packet
= NULL
;
680 str
->curr_packet_end
= p
;
682 /* STREAM_PM_RANDOM_ACCESS: */
685 if (str
->state
== SSTATE_SYNC
)
687 /* Scanning for start code */
688 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
695 /* Found a start code - enter parse state */
696 str
->state
= SSTATE_PARSE
;
698 /* Pack header, skip it */
699 if (CMP_4_CONST(p
, PACK_START_CODE
))
701 /* Max lookahead: 14 */
702 if ((p
[4] & 0xc0) == 0x40) /* mpeg-2 */
704 /* Max delta: 14 + 7 = 21 */
705 /* Skip pack header and any stuffing bytes*/
706 bytes
= 14 + (p
[13] & 7);
708 else if ((p
[4] & 0xf0) == 0x20) /* mpeg-1 */
712 else /* unknown - skip it */
714 DEBUGF("weird pack header!\n");
721 /* System header, parse and skip it - 6 bytes + size */
722 if (CMP_4_CONST(p
, SYSTEM_HEADER_START_CODE
))
724 /* Skip start code */
725 /* Max Delta = 65535 + 6 = 65541 */
726 bytes
= 6 + ((p
[4] << 8) | p
[5]);
730 /* Packet header, parse it */
731 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
733 /* Problem? Meh...probably not but just a corrupted section.
734 * Try to resync the parser which will probably succeed. */
735 DEBUGF("packet start code prefix not found: 0x%02x\n"
737 " p:%p cp:%p cpe:%p\n"
738 " dbs:%p dbe:%p dbt:%p\n",
739 str
->id
, str
->hdr
.win_left
, str
->hdr
.win_right
,
740 p
, str
->curr_packet
, str
->curr_packet_end
,
741 disk_buf
.start
, disk_buf
.end
, disk_buf
.tail
);
742 str
->state
= SSTATE_SYNC
;
743 INC_BUF(1); /* Next byte - this one's no good */
747 /* We retrieve basic infos */
748 /* Maximum packet length: 6 + 65535 = 65541 */
750 length
= ((p
[4] << 8) | p
[5]) + 6;
756 case MPEG_STREAM_PROGRAM_END
:
758 str_end_of_stream(str
);
759 DEBUGF("MPEG program end: 0x%02x\n", str
->id
);
760 return STREAM_DATA_END
;
761 case MPEG_STREAM_PACK_HEADER
:
762 case MPEG_STREAM_SYSTEM_HEADER
:
763 /* These shouldn't be here - no increment or resync
764 * since we'll pick it up above. */
767 /* It's not the packet we're looking for, skip it */
773 /* Ok, it's our packet */
776 if ((header
[6] & 0xc0) == 0x80) /* mpeg2 */
778 /* Max Lookahead: 18 */
780 /* Max length: 9 + 255 = 264 */
781 length
= 9 + header
[8];
783 /* header points to the mpeg2 pes header */
784 if ((header
[7] & 0x80) != 0)
786 /* header has a pts */
787 uint32_t pts
= read_pts(header
, 9);
789 if (pts
!= INVALID_TIMESTAMP
)
793 /* DTS isn't used for anything since things just get
794 decoded ASAP but keep the code around */
795 if (STREAM_IS_VIDEO(id
))
797 /* Video stream - header may have a dts as well */
800 if (header
[7] & 0x40) != 0x00)
802 pts
= read_pts(header
, 14);
803 if (pts
!= INVALID_TIMESTAMP
)
808 str
->pkt_flags
|= PKT_HAS_TS
;
814 /* Max lookahead: 24 + 2 + 9 = 35 */
815 /* Max len_skip: 24 + 2 = 26 */
817 /* Max length: 24 + 2 + 9 = 35 */
823 while (header
[length
- 1] == 0xff)
827 DEBUGF("Too much stuffing" );
832 if ((header
[length
- 1] & 0xc0) == 0x40)
836 length
+= mpeg1_skip_table
[header
[length
- 1] >> 4];
838 /* Header points to the mpeg1 pes header */
839 ptsbuf
= header
+ len_skip
;
841 if ((ptsbuf
[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf
, -1))
843 /* header has a pts */
844 uint32_t pts
= read_pts(ptsbuf
, -1);
846 if (pts
!= INVALID_TIMESTAMP
)
850 /* DTS isn't used for anything since things just get
851 decoded ASAP but keep the code around */
852 if (STREAM_IS_VIDEO(id
))
854 /* Video stream - header may have a dts as well */
857 if (ptsbuf
[-1] & 0xf0) == 0x30)
859 pts
= read_pts(ptsbuf
, 4);
861 if (pts
!= INVALID_TIMESTAMP
)
866 str
->pkt_flags
|= PKT_HAS_TS
;
872 /* Max bytes: 6 + 65535 - 7 = 65534 */
873 bytes
= 6 + (header
[4] << 8) + header
[5] - length
;
875 str
->curr_packet
= p
;
876 str
->curr_packet_end
= p
+ bytes
;
877 str
->hdr
.win_left
= str
->hdr
.win_right
+ length
;
878 str
->hdr
.win_right
= str
->hdr
.win_left
+ bytes
;
880 if (str
->hdr
.win_right
> disk_buf
.filesize
)
882 /* No packet that exceeds end of file can be valid */
883 str_end_of_stream(str
);
884 return STREAM_DATA_END
;
893 /* This simply reads data from the file one page at a time and returns a
894 * pointer to it in the buffer. */
895 static int parse_elementary(struct stream
*str
, enum stream_parse_mode type
)
904 case STREAM_PM_STREAMING
:
905 /* Has the end been reached already? */
906 if (str
->state
== SSTATE_END
)
907 return STREAM_DATA_END
;
909 /* Are we at the end of file? */
910 if (str
->hdr
.win_left
>= disk_buf
.filesize
)
912 str_end_of_stream(str
);
913 return STREAM_DATA_END
;
916 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
918 /* This data range is not buffered yet - register stream to
919 * be notified when it becomes available. Stream is obliged
920 * to enter a TSTATE_DATA state if it must wait. */
921 int res
= str_next_data_not_ready(str
);
923 if (res
!= STREAM_OK
)
927 len
= DISK_BUF_PAGE_SIZE
;
929 if ((size_t)(str
->hdr
.win_right
+ len
) > (size_t)disk_buf
.filesize
)
930 len
= disk_buf
.filesize
- str
->hdr
.win_right
;
934 str_end_of_stream(str
);
935 return STREAM_DATA_END
;
938 p
= str
->curr_packet_end
;
939 if (p
>= disk_buf
.end
)
942 /* STREAM_PM_STREAMING: */
944 case STREAM_PM_RANDOM_ACCESS
:
945 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
946 len
= disk_buf_getbuffer(DISK_BUF_PAGE_SIZE
, &p
, NULL
, NULL
);
948 if (len
<= 0 || str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
)
950 str_end_of_stream(str
);
951 return STREAM_DATA_END
;
954 /* STREAM_PM_RANDOM_ACCESS: */
957 str
->state
= SSTATE_PARSE
;
958 str
->curr_packet
= p
;
959 str
->curr_packet_end
= p
+ len
;
960 str
->hdr
.win_left
= str
->hdr
.win_right
;
961 str
->hdr
.win_right
= str
->hdr
.win_left
+ len
;
966 bool parser_prepare_image(uint32_t time
)
968 struct stream_scan sk
;
972 stream_scan_init(&sk
);
974 if (!str_send_msg(&video_str
, STREAM_NEEDS_SYNC
, time
))
976 DEBUGF("Image was ready\n");
977 return true; /* Should already have the image */
980 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
981 rb
->cpu_boost(true); /* No interference with trigger_cpu_boost */
984 str_send_msg(&video_str
, STREAM_RESET
, 0);
986 sk
.pos
= parser_can_seek() ?
987 mpeg_parser_seek_PTS(time
, video_str
.id
) : 0;
989 sk
.dir
= SSCAN_REVERSE
;
994 if (mpeg_parser_scan_start_code(&sk
, MPEG_START_GOP
))
996 DEBUGF("GOP found at: %ld\n", sk
.pos
);
998 unsigned id
= mpeg_parser_scan_pes(&sk
);
1000 if (id
!= video_str
.id
&& sk
.pos
> 0)
1002 /* Not part of our stream */
1003 DEBUGF(" wrong stream: 0x%02x\n", id
);
1007 /* This will hit the PES header since it's known to be there */
1008 uint32_t pts
= mpeg_parser_scan_pts(&sk
, id
);
1010 if (pts
== INVALID_TIMESTAMP
|| pts
> time
)
1012 DEBUGF(" wrong timestamp: %u\n", (unsigned)pts
);
1017 str_parser
.parms
.sd
.time
= time
;
1018 str_parser
.parms
.sd
.sk
.pos
= MAX(sk
.pos
, 0);
1019 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
1020 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
1022 DEBUGF("thumb pos:%ld len:%ld\n", str_parser
.parms
.sd
.sk
.pos
,
1023 (long)str_parser
.parms
.sd
.sk
.len
);
1025 result
= str_send_msg(&video_str
, STREAM_SYNC
,
1026 (intptr_t)&str_parser
.parms
.sd
);
1028 if (result
!= STREAM_PERFECT_MATCH
)
1030 /* Two tries should be all that is nescessary to find the exact frame
1031 * if the first GOP actually started later than the timestamp - the
1032 * GOP just prior must then start on or earlier. */
1037 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1038 rb
->cpu_boost(false);
1041 return result
> STREAM_OK
;
1044 /* Seek parser to the specified time and return absolute time.
1045 * No actual hard stuff is performed here. That's done when streaming is
1046 * about to begin or something from the current position is requested */
1047 uint32_t parser_seek_time(uint32_t time
)
1049 if (!parser_can_seek())
1051 else if (time
> str_parser
.duration
)
1052 time
= str_parser
.duration
;
1054 str_parser
.last_seek_time
= time
+ str_parser
.start_pts
;
1055 return str_parser
.last_seek_time
;
1058 void parser_prepare_streaming(void)
1060 struct stream_window sw
;
1062 DEBUGF("parser_prepare_streaming\n");
1064 /* Prepare initial video frame */
1065 parser_prepare_image(str_parser
.last_seek_time
);
1067 /* Sync audio stream */
1068 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1069 prepare_audio(str_parser
.last_seek_time
);
1071 /* Prequeue some data and set buffer window */
1072 if (!stream_get_window(&sw
))
1073 sw
.left
= sw
.right
= disk_buf
.filesize
;
1075 DEBUGF(" swl:%ld swr:%ld\n", sw
.left
, sw
.right
);
1077 if (sw
.right
> disk_buf
.filesize
- 4*MIN_BUFAHEAD
)
1078 sw
.right
= disk_buf
.filesize
- 4*MIN_BUFAHEAD
;
1080 disk_buf_prepare_streaming(sw
.left
,
1081 sw
.right
- sw
.left
+ 4*MIN_BUFAHEAD
);
1084 int parser_init_stream(void)
1086 if (disk_buf
.in_file
< 0)
1087 return STREAM_ERROR
;
1089 /* TODO: Actually find which streams are available */
1090 audio_str
.id
= MPEG_STREAM_AUDIO_FIRST
;
1091 video_str
.id
= MPEG_STREAM_VIDEO_FIRST
;
1093 /* Try to pull a video PES - if not found, try video init anyway which
1094 * should succeed if it really is a video-only stream */
1095 video_str
.hdr
.pos
= 0;
1096 video_str
.hdr
.limit
= 256*1024;
1098 if (parse_demux(&video_str
, STREAM_PM_RANDOM_ACCESS
) == STREAM_OK
)
1100 /* Found a video packet - assume program stream */
1101 str_parser
.format
= STREAM_FMT_MPEG_PS
;
1102 str_parser
.next_data
= parse_demux
;
1106 /* No PES element found - assume video elementary stream */
1107 str_parser
.format
= STREAM_FMT_MPV
;
1108 str_parser
.next_data
= parse_elementary
;
1111 if (!init_video_info())
1113 /* Cannot determine video size, etc. */
1114 parser_init_state();
1115 return STREAM_UNSUPPORTED
;
1118 if (str_parser
.format
== STREAM_FMT_MPEG_PS
)
1120 /* Initalize start_pts and end_pts with the length (in 45kHz units) of
1121 * the movie. INVALID_TIMESTAMP if the time could not be determined */
1122 if (!init_times(&video_str
) || !check_times(&video_str
))
1124 /* Must have video at least */
1125 parser_init_state();
1126 return STREAM_UNSUPPORTED
;
1129 str_parser
.flags
|= STREAMF_CAN_SEEK
;
1131 if (init_times(&audio_str
))
1133 /* Audio will be part of playback pool */
1134 stream_add_stream(&audio_str
);
1136 if (check_times(&audio_str
))
1138 /* Overall duration is maximum span */
1139 str_parser
.start_pts
= MIN(audio_str
.start_pts
, video_str
.start_pts
);
1140 str_parser
.end_pts
= MAX(audio_str
.end_pts
, video_str
.end_pts
);
1144 /* Bad times on audio - use video times */
1145 str_parser
.start_pts
= video_str
.start_pts
;
1146 str_parser
.end_pts
= video_str
.end_pts
;
1148 /* Questionable: could use bitrate seek and match video to that */
1149 audio_str
.start_pts
= video_str
.start_pts
;
1150 audio_str
.end_pts
= video_str
.end_pts
;
1155 /* No audio stream - use video only */
1156 str_parser
.start_pts
= video_str
.start_pts
;
1157 str_parser
.end_pts
= video_str
.end_pts
;
1160 str_parser
.last_seek_time
= str_parser
.start_pts
;
1164 /* There's no way to handle times on this without a full file
1166 audio_str
.start_pts
= INVALID_TIMESTAMP
;
1167 audio_str
.end_pts
= INVALID_TIMESTAMP
;
1168 video_str
.start_pts
= 0;
1169 video_str
.end_pts
= INVALID_TIMESTAMP
;
1170 str_parser
.start_pts
= 0;
1171 str_parser
.end_pts
= INVALID_TIMESTAMP
;
1174 /* Add video to playback pool */
1175 stream_add_stream(&video_str
);
1177 /* Cache duration - it's used very often */
1178 str_parser
.duration
= str_parser
.end_pts
- str_parser
.start_pts
;
1180 DEBUGF("Movie info:\n"
1185 str_parser
.dims
.w
, str_parser
.dims
.h
,
1186 (unsigned)str_parser
.start_pts
,
1187 (unsigned)str_parser
.end_pts
,
1188 (unsigned)str_parser
.duration
);
1193 void parser_close_stream(void)
1195 stream_remove_streams();
1196 parser_init_state();
1199 bool parser_init(void)
1201 parser_init_state();