1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Parser for MPEG streams
12 * Copyright (c) 2007 Michael Sevakis
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "mpegplayer.h"
24 struct stream_parser str_parser NOCACHEBSS_ATTR
;
26 static void parser_init_state(void)
28 str_parser
.last_seek_time
= 0;
29 str_parser
.format
= STREAM_FMT_UNKNOWN
;
30 str_parser
.start_pts
= INVALID_TIMESTAMP
;
31 str_parser
.end_pts
= INVALID_TIMESTAMP
;
33 str_parser
.dims
.w
= 0;
34 str_parser
.dims
.h
= 0;
37 /* Place the stream in a state to begin parsing - sync will be performed
39 void str_initialize(struct stream
*str
, off_t pos
)
41 /* Initial positions start here */
42 str
->hdr
.win_left
= str
->hdr
.win_right
= pos
;
44 str
->curr_packet
= NULL
;
45 /* Pick up parsing from this point in the buffer */
46 str
->curr_packet_end
= disk_buf_offset2ptr(pos
);
50 str
->state
= SSTATE_SYNC
;
53 /* Place the stream in an end of data state */
54 void str_end_of_stream(struct stream
*str
)
56 /* Offsets that prevent this stream from being included in the
57 * min left/max right window so that no buffering is triggered on
58 * its behalf. Set right to the min first so a thread reading the
59 * overall window gets doesn't see this as valid no matter what the
61 str
->hdr
.win_right
= LONG_MIN
;
62 str
->hdr
.win_left
= LONG_MAX
;
64 str
->curr_packet
= str
->curr_packet_end
= NULL
;
68 str
->state
= SSTATE_END
;
71 /* Return a timestamp at address p+offset if the marker bits are in tact */
72 static inline uint32_t read_pts(uint8_t *p
, off_t offset
)
74 return TS_CHECK_MARKERS(p
, offset
) ?
75 TS_FROM_HEADER(p
, offset
) : INVALID_TIMESTAMP
;
78 static inline bool validate_timestamp(uint32_t ts
)
80 return ts
>= str_parser
.start_pts
&& ts
<= str_parser
.end_pts
;
83 /* Find a start code before or after a given position */
84 uint8_t * mpeg_parser_scan_start_code(struct stream_scan
*sk
, uint32_t code
)
86 stream_scan_normalize(sk
);
90 /* Reverse scan - start with at least the min needed */
91 stream_scan_offset(sk
, 4);
94 code
&= 0xff; /* Only the low byte matters */
96 while (sk
->len
>= 0 && sk
->margin
>= 4)
99 off_t pos
= disk_buf_lseek(sk
->pos
, SEEK_SET
);
100 ssize_t len
= disk_buf_getbuffer(4, &p
, NULL
, NULL
);
102 if (pos
< 0 || len
< 4)
105 if (CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
) && p
[3] == code
)
110 stream_scan_offset(sk
, 1);
116 /* Find a PES packet header for any stream - return stream to which it
118 unsigned mpeg_parser_scan_pes(struct stream_scan
*sk
)
120 stream_scan_normalize(sk
);
124 /* Reverse scan - start with at least the min needed */
125 stream_scan_offset(sk
, 4);
128 while (sk
->len
>= 0 && sk
->margin
>= 4)
131 off_t pos
= disk_buf_lseek(sk
->pos
, SEEK_SET
);
132 ssize_t len
= disk_buf_getbuffer(4, &p
, NULL
, NULL
);
134 if (pos
< 0 || len
< 4)
137 if (CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
141 return id
; /* PES header */
142 /* else some video stream element */
145 stream_scan_offset(sk
, 1);
151 /* Return the first SCR found from the scan direction */
152 uint32_t mpeg_parser_scan_scr(struct stream_scan
*sk
)
154 uint8_t *p
= mpeg_parser_scan_start_code(sk
, MPEG_STREAM_PACK_HEADER
);
156 if (p
!= NULL
&& sk
->margin
>= 9) /* 9 bytes total required */
160 if ((p
[4] & 0xc0) == 0x40) /* mpeg-2 */
163 if (MPEG2_CHECK_PACK_SCR_MARKERS(p
, 4))
164 return MPEG2_PACK_HEADER_SCR(p
, 4);
166 else if ((p
[4] & 0xf0) == 0x20) /* mpeg-1 */
169 if (TS_CHECK_MARKERS(p
, 4))
170 return TS_FROM_HEADER(p
, 4);
172 /* Weird pack header */
176 return INVALID_TIMESTAMP
;
179 uint32_t mpeg_parser_scan_pts(struct stream_scan
*sk
, unsigned id
)
181 stream_scan_normalize(sk
);
185 /* Reverse scan - start with at least the min needed */
186 stream_scan_offset(sk
, 4);
189 while (sk
->len
>= 0 && sk
->margin
>= 4)
192 off_t pos
= disk_buf_lseek(sk
->pos
, SEEK_SET
);
193 ssize_t len
= disk_buf_getbuffer(35, &p
, NULL
, NULL
);
195 if (pos
< 0 || len
< 4)
198 if (CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
) && p
[3] == id
)
204 /* Insufficient data */
206 else if ((h
[6] & 0xc0) == 0x80) /* mpeg2 */
208 if (sk
->margin
>= 14 && (h
[7] & 0x80) != 0x00)
211 return read_pts(h
, 9);
217 ssize_t margin
= sk
->margin
;
219 /* Skip stuffing_byte */
220 while (h
[l
- 1] == 0xff && ++l
<= 23)
223 if ((h
[l
- 1] & 0xc0) == 0x40)
225 /* Skip STD_buffer_scale and STD_buffer_size */
232 /* header points to the mpeg1 pes header */
235 if ((h
[-1] & 0xe0) == 0x20)
237 sk
->data
= (h
+ 4) - p
;
238 return read_pts(h
, -1);
242 /* No PTS present - keep searching for a matching PES header with
246 stream_scan_offset(sk
, 1);
249 return INVALID_TIMESTAMP
;
252 static bool init_video_info(void)
254 DEBUGF("Getting movie size\n");
256 /* The decoder handles this in order to initialize its knowledge of the
257 * movie parameters making seeking easier */
258 str_send_msg(&video_str
, STREAM_RESET
, 0);
259 if (str_send_msg(&video_str
, VIDEO_GET_SIZE
,
260 (intptr_t)&str_parser
.dims
) != 0)
269 static void init_times(struct stream
*str
)
272 struct stream tmp_str
;
273 const ssize_t filesize
= disk_buf_filesize();
274 const ssize_t max_probe
= MIN(512*1024, filesize
);
276 /* Simply find the first earliest timestamp - this will be the one
277 * used when streaming anyway */
278 DEBUGF("Finding start_pts: 0x%02x\n", str
->id
);
280 tmp_str
.id
= str
->id
;
282 tmp_str
.hdr
.limit
= max_probe
;
284 str
->start_pts
= INVALID_TIMESTAMP
;
286 /* Probe many for video because of B-frames */
287 for (i
= STREAM_IS_VIDEO(str
->id
) ? 5 : 1; i
> 0;)
289 switch (parser_get_next_data(&tmp_str
, STREAM_PM_RANDOM_ACCESS
))
291 case STREAM_DATA_END
:
294 if (tmp_str
.pkt_flags
& PKT_HAS_TS
)
296 if (tmp_str
.pts
< str
->start_pts
)
297 str
->start_pts
= tmp_str
.pts
;
298 i
--; /* Decrement timestamp counter */
306 DEBUGF(" start:%u\n", (unsigned)str
->start_pts
);
308 /* Use the decoder thread to perform a synchronized search - no
309 * decoding should take place but just a simple run through timestamps
310 * and durations as the decoder would see them. This should give the
311 * precise time at the end of the last frame for the stream. */
312 DEBUGF("Finding end_pts: 0x%02x\n", str
->id
);
314 str
->end_pts
= INVALID_TIMESTAMP
;
316 if (str
->start_pts
!= INVALID_TIMESTAMP
)
318 str_parser
.parms
.sd
.time
= MAX_TIMESTAMP
;
319 str_parser
.parms
.sd
.sk
.pos
= filesize
- max_probe
;
320 str_parser
.parms
.sd
.sk
.len
= max_probe
;
321 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
323 str_send_msg(str
, STREAM_RESET
, 0);
325 if (str_send_msg(str
, STREAM_FIND_END_TIME
,
326 (intptr_t)&str_parser
.parms
.sd
) == STREAM_PERFECT_MATCH
)
328 str
->end_pts
= str_parser
.parms
.sd
.time
;
329 DEBUGF(" end:%u\n", (unsigned)str
->end_pts
);
333 /* End must be greater than start */
334 if (str
->start_pts
>= str
->end_pts
)
336 str
->start_pts
= INVALID_TIMESTAMP
;
337 str
->end_pts
= INVALID_TIMESTAMP
;
341 /* Return the best-fit file offset of a timestamp in the PES where
342 * timstamp <= time < next timestamp. Will try to return something reasonably
343 * valid if best-fit could not be made. */
344 static off_t
mpeg_parser_seek_PTS(uint32_t time
, unsigned id
)
346 ssize_t pos_left
= 0;
347 ssize_t pos_right
= disk_buf
.filesize
;
348 ssize_t pos
, pos_new
;
349 uint32_t time_left
= str_parser
.start_pts
;
350 uint32_t time_right
= str_parser
.end_pts
;
352 uint32_t prevpts
= 0;
353 enum state_enum state
= state0
;
354 struct stream_scan sk
;
356 /* Initial estimate taken from average bitrate - later interpolations are
357 * taken similarly based on the remaining file interval */
358 pos_new
= muldiv_uint32(time
- time_left
, pos_right
- pos_left
,
359 time_right
- time_left
) + pos_left
;
361 /* return this estimated position if nothing better comes up */
364 DEBUGF("Seeking stream 0x%02x\n", id
);
365 DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n",
366 (unsigned)time_left
, (unsigned)time
, (unsigned)time_right
,
367 pos_left
, pos_new
, pos_right
);
369 sk
.dir
= SSCAN_REVERSE
;
371 while (state
< state9
)
375 sk
.len
= (sk
.dir
< 0) ? pos_new
- pos_left
: pos_right
- pos_new
;
377 currpts
= mpeg_parser_scan_pts(&sk
, id
);
379 if (currpts
!= INVALID_TIMESTAMP
)
381 /* Found a valid timestamp - see were it lies in relation to
385 /* Time at current position is before seek time - move
389 /* This is less than the desired time but greater than
390 * the currently seeked one; move the position up */
395 /* No next timestamp can be sooner */
396 pos_left
= sk
.pos
+ sk
.data
;
399 if (pos_right
<= pos_left
)
400 break; /* If the window disappeared - we're done */
402 pos_new
= muldiv_uint32(time
- time_left
,
403 pos_right
- pos_left
,
404 time_right
- time_left
) + pos_left
;
405 /* Point is ahead of us - fudge estimate a bit high */
406 pos_new
= muldiv_uint32(11, pos_new
- pos_left
, 10)
409 if (pos_new
>= pos_right
)
411 /* Estimate could push too far */
415 state
= state2
; /* Last scan was early */
416 sk
.dir
= SSCAN_REVERSE
;
418 DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
419 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
420 (unsigned)time_right
, pos_left
, pos_new
, pos_right
);
422 else if (currpts
> time
)
424 /* Time at current position is past seek time - move
427 time_right
= currpts
;
429 if (pos_right
<= pos_left
)
430 break; /* If the window disappeared - we're done */
432 pos_new
= muldiv_uint32(time
- time_left
,
433 pos_right
- pos_left
,
434 time_right
- time_left
) + pos_left
;
435 /* Overshot the seek point - fudge estimate a bit low */
436 pos_new
= muldiv_uint32(9, pos_new
- pos_left
, 10) + pos_left
;
438 state
= state3
; /* Last scan was late */
439 sk
.dir
= SSCAN_REVERSE
;
441 DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
442 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
443 (unsigned)time_right
, pos_left
, pos_new
, pos_right
);
447 /* Exact match - it happens */
448 DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
449 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
450 (unsigned)time_right
, pos_left
, pos_new
, pos_right
);
463 /* We already tried the bruteforce scan and failed again - no
464 * more stamps could possibly exist in the interval */
465 DEBUGF("!! no timestamp 2x\n");
468 /* Hardly likely except at very beginning - just do L->R scan
469 * to find something */
470 DEBUGF("!! no timestamp on first probe: %ld\n", sk
.pos
);
473 /* Could just be missing timestamps because the interval is
474 * narrowing down. A large block of data from another stream
475 * may also be in the midst of our chosen points which could
476 * cluster at either extreme end. If anything is there, this
479 sk
.dir
= SSCAN_FORWARD
;
480 DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n",
481 (unsigned)time_left
, (unsigned)time
, (unsigned)currpts
,
482 (unsigned)time_right
, pos_left
, pos_new
, pos_right
);
486 DEBUGF("?? Invalid state: %d\n", state
);
490 /* Same timestamp twice = quit */
491 if (currpts
== prevpts
)
493 DEBUGF("!! currpts == prevpts (stop)\n");
500 #if defined(DEBUG) || defined(SIMULATOR)
501 /* The next pts after the seeked-to position should be greater -
502 * most of the time - frames out of presentation order may muck it
505 sk
.len
= disk_buf
.filesize
;
506 sk
.dir
= SSCAN_FORWARD
;
508 uint32_t nextpts
= mpeg_parser_scan_pts(&sk
, id
);
509 DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n",
510 pos
, (unsigned)pts
, (unsigned)time
, (unsigned)nextpts
);
512 if (pts
<= time
&& time
< nextpts
)
514 /* Smile - it worked */
515 DEBUGF(" :) pts<=time<next pts\n");
519 /* See where things ended up */
523 DEBUGF(" :\\ pts>time\n");
527 /* Weird - probably because of encoded order & tends to be right
528 * anyway if other criteria are met */
529 DEBUGF(" :p pts>=next pts\n");
534 DEBUGF(" :( time>=nextpts\n");
542 static bool prepare_image(uint32_t time
)
544 struct stream_scan sk
;
548 if (!str_send_msg(&video_str
, STREAM_NEEDS_SYNC
, time
))
550 DEBUGF("Image was ready\n");
551 return true; /* Should already have the image */
554 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
555 rb
->cpu_boost(true); /* No interference with trigger_cpu_boost */
558 str_send_msg(&video_str
, STREAM_RESET
, 0);
560 sk
.pos
= parser_can_seek() ?
561 mpeg_parser_seek_PTS(time
, video_str
.id
) : 0;
563 sk
.dir
= SSCAN_REVERSE
;
568 if (mpeg_parser_scan_start_code(&sk
, MPEG_START_GOP
))
570 DEBUGF("GOP found at: %ld\n", sk
.pos
);
572 unsigned id
= mpeg_parser_scan_pes(&sk
);
574 if (id
!= video_str
.id
&& sk
.pos
> 0)
576 /* Not part of our stream */
577 DEBUGF(" wrong stream: 0x%02x\n", id
);
581 /* This will hit the PES header since it's known to be there */
582 uint32_t pts
= mpeg_parser_scan_pts(&sk
, id
);
584 if (pts
== INVALID_TIMESTAMP
|| pts
> time
)
586 DEBUGF(" wrong timestamp: %u\n", (unsigned)pts
);
591 str_parser
.parms
.sd
.time
= time
;
592 str_parser
.parms
.sd
.sk
.pos
= MAX(sk
.pos
, 0);
593 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
594 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
596 DEBUGF("thumb pos:%ld len:%ld\n", str_parser
.parms
.sd
.sk
.pos
,
597 str_parser
.parms
.sd
.sk
.len
);
599 result
= str_send_msg(&video_str
, STREAM_SYNC
,
600 (intptr_t)&str_parser
.parms
.sd
);
602 if (result
!= STREAM_PERFECT_MATCH
)
604 /* Two tries should be all that is nescessary to find the exact frame
605 * if the first GOP actually started later than the timestamp - the
606 * GOP just prior must then start on or earlier. */
611 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
612 rb
->cpu_boost(false);
615 return result
> STREAM_OK
;
618 static void prepare_audio(uint32_t time
)
622 if (!str_send_msg(&audio_str
, STREAM_NEEDS_SYNC
, time
))
624 DEBUGF("Audio was ready\n");
628 pos
= mpeg_parser_seek_PTS(time
, audio_str
.id
);
629 str_send_msg(&audio_str
, STREAM_RESET
, 0);
631 str_parser
.parms
.sd
.time
= time
;
632 str_parser
.parms
.sd
.sk
.pos
= pos
;
633 str_parser
.parms
.sd
.sk
.len
= 1024*1024;
634 str_parser
.parms
.sd
.sk
.dir
= SSCAN_FORWARD
;
636 str_send_msg(&audio_str
, STREAM_SYNC
, (intptr_t)&str_parser
.parms
.sd
);
639 /* This function demuxes the streams and gives the next stream data
642 * STREAM_PM_STREAMING is for operation during playback. If the nescessary
643 * data and worst-case lookahead margin is not available, the stream is
644 * registered for notification when the data becomes available. If parsing
645 * extends beyond the end of the file or the end of stream marker is reached,
646 * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS.
648 * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking.
649 * If the file cache misses for the current position + lookahead, it will be
650 * loaded from disk. When the specified limit is reached, STREAM_DATA_END is
653 * The results from one mode may be used as input to the other. Random access
654 * requires cooperation amongst threads to avoid evicting another stream's
657 static int parse_demux(struct stream
*str
, enum stream_parse_mode type
)
659 #define INC_BUF(offset) \
660 ({ off_t _o = (offset); \
661 str->hdr.win_right += _o; \
662 if ((p += _o) >= disk_buf.end) \
663 p -= disk_buf.size; })
665 static const int mpeg1_skip_table
[16] =
666 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
668 uint8_t *p
= str
->curr_packet_end
;
676 ssize_t length
, bytes
;
680 case STREAM_PM_STREAMING
:
681 /* Has the end been reached already? */
682 if (str
->state
== SSTATE_END
)
683 return STREAM_DATA_END
;
685 /* Are we at the end of file? */
686 if (str
->hdr
.win_left
>= disk_buf
.filesize
)
688 str_end_of_stream(str
);
689 return STREAM_DATA_END
;
692 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
694 /* This data range is not buffered yet - register stream to
695 * be notified when it becomes available. Stream is obliged
696 * to enter a TSTATE_DATA state if it must wait. */
697 int res
= str_next_data_not_ready(str
);
699 if (res
!= STREAM_OK
)
703 /* STREAM_PM_STREAMING: */
705 case STREAM_PM_RANDOM_ACCESS
:
706 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
708 if (str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
||
709 disk_buf_getbuffer(MIN_BUFAHEAD
, &p
, NULL
, NULL
) <= 0)
711 str_end_of_stream(str
);
712 return STREAM_DATA_END
;
715 str
->state
= SSTATE_SYNC
;
716 str
->hdr
.win_left
= str
->hdr
.pos
;
717 str
->curr_packet
= NULL
;
718 str
->curr_packet_end
= p
;
720 /* STREAM_PM_RANDOM_ACCESS: */
723 if (str
->state
== SSTATE_SYNC
)
725 /* Scanning for start code */
726 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
733 /* Found a start code - enter parse state */
734 str
->state
= SSTATE_PARSE
;
736 /* Pack header, skip it */
737 if (CMP_4_CONST(p
, PACK_START_CODE
))
739 /* Max lookahead: 14 */
740 if ((p
[4] & 0xc0) == 0x40) /* mpeg-2 */
742 /* Max delta: 14 + 7 = 21 */
743 /* Skip pack header and any stuffing bytes*/
744 bytes
= 14 + (p
[13] & 7);
746 else if ((p
[4] & 0xf0) == 0x20) /* mpeg-1 */
750 else /* unknown - skip it */
752 DEBUGF("weird pack header!\n");
759 /* System header, parse and skip it - 6 bytes + size */
760 if (CMP_4_CONST(p
, SYSTEM_HEADER_START_CODE
))
762 /* Skip start code */
763 /* Max Delta = 65535 + 6 = 65541 */
764 bytes
= 6 + ((p
[4] << 8) | p
[5]);
768 /* Packet header, parse it */
769 if (!CMP_3_CONST(p
, PACKET_START_CODE_PREFIX
))
771 /* Problem? Meh...probably not but just a corrupted section.
772 * Try to resync the parser which will probably succeed. */
773 DEBUGF("packet start code prefix not found: 0x%02x\n"
775 " p:%p cp:%p cpe:%p\n"
776 " dbs:%p dbe:%p dbt:%p\n",
777 str
->id
, str
->hdr
.win_left
, str
->hdr
.win_right
,
778 p
, str
->curr_packet
, str
->curr_packet_end
,
779 disk_buf
.start
, disk_buf
.end
, disk_buf
.tail
);
780 str
->state
= SSTATE_SYNC
;
781 INC_BUF(1); /* Next byte - this one's no good */
785 /* We retrieve basic infos */
786 /* Maximum packet length: 6 + 65535 = 65541 */
788 length
= ((p
[4] << 8) | p
[5]) + 6;
794 case MPEG_STREAM_PROGRAM_END
:
796 str_end_of_stream(str
);
797 DEBUGF("MPEG program end: 0x%02x\n", str
->id
);
798 return STREAM_DATA_END
;
799 case MPEG_STREAM_PACK_HEADER
:
800 case MPEG_STREAM_SYSTEM_HEADER
:
801 /* These shouldn't be here - no increment or resync
802 * since we'll pick it up above. */
805 /* It's not the packet we're looking for, skip it */
811 /* Ok, it's our packet */
814 if ((header
[6] & 0xc0) == 0x80) /* mpeg2 */
816 /* Max Lookahead: 18 */
818 /* Max length: 9 + 255 = 264 */
819 length
= 9 + header
[8];
821 /* header points to the mpeg2 pes header */
822 if ((header
[7] & 0x80) != 0)
824 /* header has a pts */
825 uint32_t pts
= read_pts(header
, 9);
827 if (pts
!= INVALID_TIMESTAMP
)
831 /* DTS isn't used for anything since things just get
832 decoded ASAP but keep the code around */
833 if (STREAM_IS_VIDEO(id
))
835 /* Video stream - header may have a dts as well */
838 if (header
[7] & 0x40) != 0x00)
840 pts
= read_pts(header
, 14);
841 if (pts
!= INVALID_TIMESTAMP
)
846 str
->pkt_flags
|= PKT_HAS_TS
;
852 /* Max lookahead: 24 + 2 + 9 = 35 */
853 /* Max len_skip: 24 + 2 = 26 */
855 /* Max length: 24 + 2 + 9 = 35 */
861 while (header
[length
- 1] == 0xff)
865 DEBUGF("Too much stuffing" );
870 if ((header
[length
- 1] & 0xc0) == 0x40)
874 length
+= mpeg1_skip_table
[header
[length
- 1] >> 4];
876 /* Header points to the mpeg1 pes header */
877 ptsbuf
= header
+ len_skip
;
879 if ((ptsbuf
[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf
, -1))
881 /* header has a pts */
882 uint32_t pts
= read_pts(ptsbuf
, -1);
884 if (pts
!= INVALID_TIMESTAMP
)
888 /* DTS isn't used for anything since things just get
889 decoded ASAP but keep the code around */
890 if (STREAM_IS_VIDEO(id
))
892 /* Video stream - header may have a dts as well */
895 if (ptsbuf
[-1] & 0xf0) == 0x30)
897 pts
= read_pts(ptsbuf
, 4);
899 if (pts
!= INVALID_TIMESTAMP
)
904 str
->pkt_flags
|= PKT_HAS_TS
;
910 /* Max bytes: 6 + 65535 - 7 = 65534 */
911 bytes
= 6 + (header
[4] << 8) + header
[5] - length
;
913 str
->curr_packet
= p
;
914 str
->curr_packet_end
= p
+ bytes
;
915 str
->hdr
.win_left
= str
->hdr
.win_right
+ length
;
916 str
->hdr
.win_right
= str
->hdr
.win_left
+ bytes
;
918 if (str
->hdr
.win_right
> disk_buf
.filesize
)
920 /* No packet that exceeds end of file can be valid */
921 str_end_of_stream(str
);
922 return STREAM_DATA_END
;
931 /* This simply reads data from the file one page at a time and returns a
932 * pointer to it in the buffer. */
933 static int parse_elementary(struct stream
*str
, enum stream_parse_mode type
)
942 case STREAM_PM_STREAMING
:
943 /* Has the end been reached already? */
944 if (str
->state
== SSTATE_END
)
945 return STREAM_DATA_END
;
947 /* Are we at the end of file? */
948 if (str
->hdr
.win_left
>= disk_buf
.filesize
)
950 str_end_of_stream(str
);
951 return STREAM_DATA_END
;
954 if (!disk_buf_is_data_ready(&str
->hdr
, MIN_BUFAHEAD
))
956 /* This data range is not buffered yet - register stream to
957 * be notified when it becomes available. Stream is obliged
958 * to enter a TSTATE_DATA state if it must wait. */
959 int res
= str_next_data_not_ready(str
);
961 if (res
!= STREAM_OK
)
965 len
= DISK_BUF_PAGE_SIZE
;
967 if ((size_t)(str
->hdr
.win_right
+ len
) > (size_t)disk_buf
.filesize
)
968 len
= disk_buf
.filesize
- str
->hdr
.win_right
;
972 str_end_of_stream(str
);
973 return STREAM_DATA_END
;
976 p
= str
->curr_packet_end
;
978 /* STREAM_PM_STREAMING: */
980 case STREAM_PM_RANDOM_ACCESS
:
981 str
->hdr
.pos
= disk_buf_lseek(str
->hdr
.pos
, SEEK_SET
);
982 len
= disk_buf_getbuffer(DISK_BUF_PAGE_SIZE
, &p
, NULL
, NULL
);
984 if (len
<= 0 || str
->hdr
.pos
< 0 || str
->hdr
.pos
>= str
->hdr
.limit
)
986 str_end_of_stream(str
);
987 return STREAM_DATA_END
;
990 /* STREAM_PM_RANDOM_ACCESS: */
993 str
->state
= SSTATE_PARSE
;
994 str
->curr_packet
= p
;
995 str
->curr_packet_end
= p
+ len
;
996 str
->hdr
.win_left
= str
->hdr
.win_right
;
997 str
->hdr
.win_right
= str
->hdr
.win_left
+ len
;
1002 intptr_t parser_send_video_msg(long id
, intptr_t data
)
1004 intptr_t retval
= 0;
1006 if (video_str
.thread
!= NULL
&& disk_buf
.in_file
>= 0)
1008 /* Hook certain messages since they involve multiple operations
1009 * behind the scenes */
1012 case VIDEO_DISPLAY_SHOW
:
1013 if (data
!= 0 && stream_status() != STREAM_PLAYING
)
1014 { /* Only prepare image if showing and not playing */
1015 prepare_image(str_parser
.last_seek_time
);
1019 case VIDEO_PRINT_FRAME
:
1020 case VIDEO_PRINT_THUMBNAIL
:
1021 if (stream_status() == STREAM_PLAYING
)
1022 break; /* Prepare image if not playing */
1024 if (!prepare_image(str_parser
.last_seek_time
))
1025 return false; /* Preparation failed */
1027 /* Image ready - pass message to video thread */
1031 retval
= str_send_msg(&video_str
, id
, data
);
1037 /* Seek parser to the specified time and return absolute time.
1038 * No actual hard stuff is performed here. That's done when streaming is
1039 * about to begin or something from the current position is requested */
1040 uint32_t parser_seek_time(uint32_t time
)
1042 if (!parser_can_seek())
1044 else if (time
> str_parser
.duration
)
1045 time
= str_parser
.duration
;
1047 str_parser
.last_seek_time
= time
+ str_parser
.start_pts
;
1048 return str_parser
.last_seek_time
;
1051 void parser_prepare_streaming(void)
1053 struct stream_window sw
;
1055 DEBUGF("parser_prepare_streaming\n");
1057 /* Prepare initial video frame */
1058 prepare_image(str_parser
.last_seek_time
);
1060 /* Sync audio stream */
1061 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1062 prepare_audio(str_parser
.last_seek_time
);
1064 /* Prequeue some data and set buffer window */
1065 if (!stream_get_window(&sw
))
1066 sw
.left
= sw
.right
= disk_buf
.filesize
;
1068 DEBUGF(" swl:%ld swr:%ld\n", sw
.left
, sw
.right
);
1070 if (sw
.right
> disk_buf
.filesize
- 4*MIN_BUFAHEAD
)
1071 sw
.right
= disk_buf
.filesize
- 4*MIN_BUFAHEAD
;
1073 disk_buf_prepare_streaming(sw
.left
,
1074 sw
.right
- sw
.left
+ 4*MIN_BUFAHEAD
);
1077 int parser_init_stream(void)
1079 if (disk_buf
.in_file
< 0)
1080 return STREAM_ERROR
;
1082 /* TODO: Actually find which streams are available */
1083 audio_str
.id
= MPEG_STREAM_AUDIO_FIRST
;
1084 video_str
.id
= MPEG_STREAM_VIDEO_FIRST
;
1086 /* Try to pull a video PES - if not found, try video init anyway which
1087 * should succeed if it really is a video-only stream */
1088 video_str
.hdr
.pos
= 0;
1089 video_str
.hdr
.limit
= 256*1024;
1091 if (parse_demux(&video_str
, STREAM_PM_RANDOM_ACCESS
) == STREAM_OK
)
1093 /* Found a video packet - assume transport stream */
1094 str_parser
.format
= STREAM_FMT_MPEG_TS
;
1095 str_parser
.next_data
= parse_demux
;
1099 /* No PES element found - assume video elementary stream */
1100 str_parser
.format
= STREAM_FMT_MPV
;
1101 str_parser
.next_data
= parse_elementary
;
1104 if (!init_video_info())
1106 /* Cannot determine video size, etc. */
1107 return STREAM_UNSUPPORTED
;
1110 if (str_parser
.format
== STREAM_FMT_MPEG_TS
)
1112 /* Initalize start_pts and end_pts with the length (in 45kHz units) of
1113 * the movie. INVALID_TIMESTAMP if the time could not be determined */
1114 init_times(&audio_str
);
1115 init_times(&video_str
);
1117 if (video_str
.start_pts
== INVALID_TIMESTAMP
)
1119 /* Must have video at least */
1120 return STREAM_UNSUPPORTED
;
1123 str_parser
.flags
|= STREAMF_CAN_SEEK
;
1125 if (audio_str
.start_pts
!= INVALID_TIMESTAMP
)
1127 /* Overall duration is maximum span */
1128 str_parser
.start_pts
= MIN(audio_str
.start_pts
, video_str
.start_pts
);
1129 str_parser
.end_pts
= MAX(audio_str
.end_pts
, video_str
.end_pts
);
1131 /* Audio will be part of playback pool */
1132 stream_add_stream(&audio_str
);
1136 /* No audio stream - use video only */
1137 str_parser
.start_pts
= video_str
.start_pts
;
1138 str_parser
.end_pts
= video_str
.end_pts
;
1143 /* There's no way to handle times on this without a full file
1145 audio_str
.start_pts
= INVALID_TIMESTAMP
;
1146 audio_str
.end_pts
= INVALID_TIMESTAMP
;
1147 video_str
.start_pts
= 0;
1148 video_str
.end_pts
= INVALID_TIMESTAMP
;
1149 str_parser
.start_pts
= 0;
1150 str_parser
.end_pts
= INVALID_TIMESTAMP
;
1153 /* Add video to playback pool */
1154 stream_add_stream(&video_str
);
1156 /* Cache duration - it's used very often */
1157 str_parser
.duration
= str_parser
.end_pts
- str_parser
.start_pts
;
1159 DEBUGF("Movie info:\n"
1164 str_parser
.dims
.w
, str_parser
.dims
.h
,
1165 (unsigned)str_parser
.start_pts
,
1166 (unsigned)str_parser
.end_pts
,
1167 (unsigned)str_parser
.duration
);
1172 void parser_close_stream(void)
1174 stream_remove_streams();
1175 parser_init_state();
1178 bool parser_init(void)
1180 parser_init_state();