From d25d24812e8120c0eb133a412287ac030eb185c9 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Wed, 4 Aug 2021 09:49:56 -0400 Subject: RFC: Get rid of mpegplayer plugin It might have made sense once upon a time, but in today's world... Change-Id: I5d638e6f7a2308c50ab12bd901338f02cf426aae --- apps/plugins/mpegplayer/mpeg_parser.c | 1203 --------------------------------- 1 file changed, 1203 deletions(-) delete mode 100644 apps/plugins/mpegplayer/mpeg_parser.c (limited to 'apps/plugins/mpegplayer/mpeg_parser.c') diff --git a/apps/plugins/mpegplayer/mpeg_parser.c b/apps/plugins/mpegplayer/mpeg_parser.c deleted file mode 100644 index cc57b0c43c..0000000000 --- a/apps/plugins/mpegplayer/mpeg_parser.c +++ /dev/null @@ -1,1203 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Parser for MPEG streams - * - * Copyright (c) 2007 Michael Sevakis - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "plugin.h" -#include "mpegplayer.h" - -struct stream_parser str_parser SHAREDBSS_ATTR; - -static void parser_init_state(void) -{ - str_parser.last_seek_time = 0; - str_parser.format = STREAM_FMT_UNKNOWN; - str_parser.start_pts = INVALID_TIMESTAMP; - str_parser.end_pts = INVALID_TIMESTAMP; - str_parser.flags = 0; - str_parser.dims.w = 0; - str_parser.dims.h = 0; -} - -/* Place the stream in a state to begin parsing - sync will be performed - * first */ -void str_initialize(struct stream *str, off_t pos) -{ - /* Initial positions start here */ - str->hdr.win_left = str->hdr.win_right = pos; - /* No packet */ - str->curr_packet = NULL; - /* Pick up parsing from this point in the buffer */ - str->curr_packet_end = disk_buf_offset2ptr(pos); - /* No flags */ - str->pkt_flags = 0; - /* Sync first */ - str->state = SSTATE_SYNC; -} - -/* Place the stream in an end of data state */ -void str_end_of_stream(struct stream *str) -{ - /* Offsets that prevent this stream from being included in the - * min left/max right window so that no buffering is triggered on - * its behalf. Set right to the min first so a thread reading the - * overall window gets doesn't see this as valid no matter what the - * file length. */ - str->hdr.win_right = OFF_T_MIN; - str->hdr.win_left = OFF_T_MAX; - /* No packets */ - str->curr_packet = str->curr_packet_end = NULL; - /* No flags */ - str->pkt_flags = 0; - /* Fin */ - str->state = SSTATE_END; -} - -/* Return a timestamp at address p+offset if the marker bits are in tact */ -static inline uint32_t read_pts(uint8_t *p, off_t offset) -{ - return TS_CHECK_MARKERS(p, offset) ? - TS_FROM_HEADER(p, offset) : INVALID_TIMESTAMP; -} - -static inline bool validate_timestamp(uint32_t ts) -{ - return ts >= str_parser.start_pts && ts <= str_parser.end_pts; -} - -/* Find a start code before or after a given position */ -uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code) -{ - stream_scan_normalize(sk); - - if (sk->dir < 0) - { - /* Reverse scan - start with at least the min needed */ - stream_scan_offset(sk, 4); - } - - code &= 0xff; /* Only the low byte matters */ - - while (sk->len >= 0 && sk->margin >= 4) - { - uint8_t *p; - off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); - ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p); - - if (pos < 0 || len < 4) - break; - - if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == code) - { - return p; - } - - stream_scan_offset(sk, 1); - } - - return NULL; -} - -/* Find a PES packet header for any stream - return stream to which it - * belongs */ -unsigned mpeg_parser_scan_pes(struct stream_scan *sk) -{ - stream_scan_normalize(sk); - - if (sk->dir < 0) - { - /* Reverse scan - start with at least the min needed */ - stream_scan_offset(sk, 4); - } - - while (sk->len >= 0 && sk->margin >= 4) - { - uint8_t *p; - off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); - ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p); - - if (pos < 0 || len < 4) - break; - - if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) - { - unsigned id = p[3]; - if (id >= 0xb9) - return id; /* PES header */ - /* else some video stream element */ - } - - stream_scan_offset(sk, 1); - } - - return -1; -} - -/* Return the first SCR found from the scan direction */ -uint32_t mpeg_parser_scan_scr(struct stream_scan *sk) -{ - uint8_t *p = mpeg_parser_scan_start_code(sk, MPEG_STREAM_PACK_HEADER); - - if (p != NULL && sk->margin >= 9) /* 9 bytes total required */ - { - sk->data = 9; - - if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */ - { - /* Lookhead p+8 */ - if (MPEG2_CHECK_PACK_SCR_MARKERS(p, 4)) - return MPEG2_PACK_HEADER_SCR(p, 4); - } - else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */ - { - /* Lookahead p+8 */ - if (TS_CHECK_MARKERS(p, 4)) - return TS_FROM_HEADER(p, 4); - } - /* Weird pack header */ - sk->data = 5; - } - - return INVALID_TIMESTAMP; -} - -uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id) -{ - stream_scan_normalize(sk); - - if (sk->dir < 0) - { - /* Reverse scan - start with at least the min needed */ - stream_scan_offset(sk, 4); - } - - while (sk->len >= 0 && sk->margin >= 4) - { - uint8_t *p; - off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); - ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 30, &p); - - if (pos < 0 || len < 4) - break; - - if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == id) - { - uint8_t *h = p; - - if (sk->margin < 7) - { - /* Insufficient data */ - } - else if ((h[6] & 0xc0) == 0x80) /* mpeg2 */ - { - if (sk->margin >= 14 && (h[7] & 0x80) != 0x00) - { - sk->data = 14; - return read_pts(h, 9); - } - } - else /* mpeg1 */ - { - ssize_t l = 6; - ssize_t margin = sk->margin; - - /* Skip stuffing_byte */ - while (margin > 7 && h[l] == 0xff && ++l <= 22) - --margin; - - if (margin >= 7) - { - if ((h[l] & 0xc0) == 0x40) - { - /* Skip STD_buffer_scale and STD_buffer_size */ - margin -= 2; - l += 2; - } - - if (margin >= 5) - { - /* Header points to the mpeg1 pes header */ - h += l; - - if ((h[0] & 0xe0) == 0x20) - { - /* PTS or PTS_DTS indicated */ - sk->data = (h + 5) - p; - return read_pts(h, 0); - } - } - } - } - /* No PTS present - keep searching for a matching PES header with - * one */ - } - - stream_scan_offset(sk, 1); - } - - return INVALID_TIMESTAMP; -} - -static bool init_video_info(void) -{ - DEBUGF("Getting movie size\n"); - - /* The decoder handles this in order to initialize its knowledge of the - * movie parameters making seeking easier */ - str_send_msg(&video_str, STREAM_RESET, 0); - if (str_send_msg(&video_str, VIDEO_GET_SIZE, - (intptr_t)&str_parser.dims) != 0) - { - return true; - } - - DEBUGF(" failed\n"); - return false; -} - -static bool init_times(struct stream *str) -{ - struct stream tmp_str; - const ssize_t filesize = disk_buf_filesize(); - const ssize_t max_probe = MIN(512*1024, filesize); - bool found_stream; - - /* Simply find the first earliest timestamp - this will be the one - * used when streaming anyway */ - DEBUGF("Finding start_pts: 0x%02x\n", str->id); - - found_stream = false; - str->start_pts = INVALID_TIMESTAMP; - str->end_pts = INVALID_TIMESTAMP; - - tmp_str.id = str->id; - tmp_str.hdr.pos = 0; - tmp_str.hdr.limit = max_probe; - - /* Probe for many for the start because some stamps could be anomalous. - * Video also can also have things out of order. Just see what it's got. - */ - while (1) - { - switch (parser_get_next_data(&tmp_str, STREAM_PM_RANDOM_ACCESS)) - { - case STREAM_DATA_END: - break; - case STREAM_OK: - found_stream = true; - if (tmp_str.pkt_flags & PKT_HAS_TS) - { - if (tmp_str.pts < str->start_pts) - str->start_pts = tmp_str.pts; - } - continue; - } - - break; - } - - if (!found_stream) - { - DEBUGF(" stream not found:0x%02x\n", str->id); - return false; - } - - DEBUGF(" start:%u\n", (unsigned)str->start_pts); - - /* Use the decoder thread to perform a synchronized search - no - * decoding should take place but just a simple run through timestamps - * and durations as the decoder would see them. This should give the - * precise time at the end of the last frame for the stream. */ - DEBUGF("Finding end_pts: 0x%02x\n", str->id); - - str_parser.parms.sd.time = MAX_TIMESTAMP; - str_parser.parms.sd.sk.pos = filesize - max_probe; - str_parser.parms.sd.sk.len = max_probe; - str_parser.parms.sd.sk.dir = SSCAN_FORWARD; - - str_send_msg(str, STREAM_RESET, 0); - - if (str_send_msg(str, STREAM_FIND_END_TIME, - (intptr_t)&str_parser.parms.sd) == STREAM_PERFECT_MATCH) - { - str->end_pts = str_parser.parms.sd.time; - DEBUGF(" end:%u\n", (unsigned)str->end_pts); - } - - return true; -} - -static bool check_times(const struct stream *str) -{ - return str->start_pts < str->end_pts && - str->end_pts != INVALID_TIMESTAMP; -} - -/* Return the best-fit file offset of a timestamp in the PES where - * timstamp <= time < next timestamp. Will try to return something reasonably - * valid if best-fit could not be made. */ -static off_t mpeg_parser_seek_PTS(uint32_t time, unsigned id) -{ - ssize_t pos_left = 0; - ssize_t pos_right = disk_buf.filesize; - ssize_t pos, pos_new; - uint32_t time_left = str_parser.start_pts; - uint32_t time_right = str_parser.end_pts; - uint32_t pts = 0; - uint32_t prevpts = 0; - enum state_enum state = STATE0; - struct stream_scan sk; - - stream_scan_init(&sk); - - /* Initial estimate taken from average bitrate - later interpolations are - * taken similarly based on the remaining file interval */ - pos_new = muldiv_uint32(time - time_left, pos_right - pos_left, - time_right - time_left) + pos_left; - - /* return this estimated position if nothing better comes up */ - pos = pos_new; - - DEBUGF("Seeking stream 0x%02x\n", id); - DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n", - (unsigned)time_left, (unsigned)time, (unsigned)time_right, - (long)pos_left, (long)pos_new, (long)pos_right); - - sk.dir = SSCAN_REVERSE; - - while (state < STATE9) - { - uint32_t currpts; - sk.pos = pos_new; - sk.len = (sk.dir < 0) ? pos_new - pos_left : pos_right - pos_new; - - currpts = mpeg_parser_scan_pts(&sk, id); - - if (currpts != INVALID_TIMESTAMP) - { - ssize_t pos_adj; /* Adjustment to over or under-estimate */ - - /* Found a valid timestamp - see were it lies in relation to - * target */ - if (currpts < time) - { - /* Time at current position is before seek time - move - * forward */ - if (currpts > pts) - { - /* This is less than the desired time but greater than - * the currently seeked one; move the position up */ - pts = currpts; - pos = sk.pos; - } - - /* No next timestamp can be sooner */ - pos_left = sk.pos + sk.data; - time_left = currpts; - - if (pos_right <= pos_left) - break; /* If the window disappeared - we're done */ - - pos_new = muldiv_uint32(time - time_left, - pos_right - pos_left, - time_right - time_left); - /* Point is ahead of us - fudge estimate a bit high */ - pos_adj = pos_new / 10; - - if (pos_adj > 512*1024) - pos_adj = 512*1024; - - pos_new += pos_left + pos_adj; - - if (pos_new >= pos_right) - { - /* Estimate could push too far */ - pos_new = pos_right; - } - - state = STATE2; /* Last scan was early */ - sk.dir = SSCAN_REVERSE; - - DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n", - (unsigned)time_left, (unsigned)time, (unsigned)currpts, - (unsigned)time_right, (long)pos_left, (long)pos_new, - (long)pos_right); - } - else if (currpts > time) - { - /* Time at current position is past seek time - move - backward */ - pos_right = sk.pos; - time_right = currpts; - - if (pos_right <= pos_left) - break; /* If the window disappeared - we're done */ - - pos_new = muldiv_uint32(time - time_left, - pos_right - pos_left, - time_right - time_left); - /* Overshot the seek point - fudge estimate a bit low */ - pos_adj = pos_new / 10; - - if (pos_adj > 512*1024) - pos_adj = 512*1024; - - pos_new += pos_left - pos_adj; - - state = STATE3; /* Last scan was late */ - sk.dir = SSCAN_REVERSE; - - DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n", - (unsigned)time_left, (unsigned)time, (unsigned)currpts, - (unsigned)time_right, (long)pos_left, (long)pos_new, - (long)pos_right); - } - else - { - /* Exact match - it happens */ - DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n", - (unsigned)time_left, (unsigned)time, (unsigned)currpts, - (unsigned)time_right, (long)pos_left, (long)pos_new, - (long)pos_right); - pts = currpts; - pos = sk.pos; - state = STATE9; - } - } - else - { - /* Nothing found */ - - switch (state) - { - case STATE1: - /* We already tried the bruteforce scan and failed again - no - * more stamps could possibly exist in the interval */ - DEBUGF("!! no timestamp 2x\n"); - break; - case STATE0: - /* Hardly likely except at very beginning - just do L->R scan - * to find something */ - DEBUGF("!! no timestamp on first probe: %ld\n", sk.pos); - case STATE2: - case STATE3: - /* Could just be missing timestamps because the interval is - * narrowing down. A large block of data from another stream - * may also be in the midst of our chosen points which could - * cluster at either extreme end. If anything is there, this - * will find it. */ - pos_new = pos_left; - sk.dir = SSCAN_FORWARD; - DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n", - (unsigned)time_left, (unsigned)time, (unsigned)currpts, - (unsigned)time_right, (long)pos_left, (long)pos_new, - (long)pos_right); - state = STATE1; - break; - default: - DEBUGF("?? Invalid state: %d\n", state); - } - } - - /* Same timestamp twice = quit */ - if (currpts == prevpts) - { - DEBUGF("!! currpts == prevpts (stop)\n"); - state = STATE9; - } - - prevpts = currpts; - } - -#if defined(DEBUG) || defined(SIMULATOR) - /* The next pts after the seeked-to position should be greater - - * most of the time - frames out of presentation order may muck it - * up a slight bit */ - sk.pos = pos + 1; - sk.len = disk_buf.filesize; - sk.dir = SSCAN_FORWARD; - - uint32_t nextpts = mpeg_parser_scan_pts(&sk, id); - DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n", - (long)pos, (unsigned)pts, (unsigned)time, (unsigned)nextpts); - - if (pts <= time && time < nextpts) - { - /* Smile - it worked */ - DEBUGF(" :) pts<=time time) - { - /* Hmm */ - DEBUGF(" :\\ pts>time\n"); - } - if (pts >= nextpts) - { - /* Weird - probably because of encoded order & tends to be right - * anyway if other criteria are met */ - DEBUGF(" :p pts>=next pts\n"); - } - if (time >= nextpts) - { - /* Ugh */ - DEBUGF(" :( time>=nextpts\n"); - } - } -#endif - - return pos; -} - -static void prepare_audio(uint32_t time) -{ - off_t pos; - - if (!str_send_msg(&audio_str, STREAM_NEEDS_SYNC, time)) - { - DEBUGF("Audio was ready\n"); - return; - } - - pos = mpeg_parser_seek_PTS(time, audio_str.id); - str_send_msg(&audio_str, STREAM_RESET, 0); - - str_parser.parms.sd.time = time; - str_parser.parms.sd.sk.pos = pos; - str_parser.parms.sd.sk.len = 1024*1024; - str_parser.parms.sd.sk.dir = SSCAN_FORWARD; - - str_send_msg(&audio_str, STREAM_SYNC, (intptr_t)&str_parser.parms.sd); -} - -/* This function demuxes the streams and gives the next stream data - * pointer. - * - * STREAM_PM_STREAMING is for operation during playback. If the nescessary - * data and worst-case lookahead margin is not available, the stream is - * registered for notification when the data becomes available. If parsing - * extends beyond the end of the file or the end of stream marker is reached, - * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS. - * - * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking. - * If the file cache misses for the current position + lookahead, it will be - * loaded from disk. When the specified limit is reached, STREAM_DATA_END is - * returned. - * - * The results from one mode may be used as input to the other. Random access - * requires cooperation amongst threads to avoid evicting another stream's - * data. - */ -static int parse_demux(struct stream *str, enum stream_parse_mode type) -{ - #define INC_BUF(offset) \ - ({ off_t _o = (offset); \ - str->hdr.win_right += _o; \ - if ((p += _o) >= disk_buf.end) \ - p -= disk_buf.size; }) - - static const int mpeg1_skip_table[16] = - { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - uint8_t *p = str->curr_packet_end; - - str->pkt_flags = 0; - - while (1) - { - uint8_t *header; - unsigned id; - ssize_t length, bytes; - - switch (type) - { - case STREAM_PM_STREAMING: - /* Has the end been reached already? */ - switch (str->state) - { - case SSTATE_PARSE: /* Expected case first if no jumptable */ - /* Are we at the end of file? */ - if (str->hdr.win_left < disk_buf.filesize) - break; - str_end_of_stream(str); - return STREAM_DATA_END; - - case SSTATE_SYNC: - /* Is sync at the end of file? */ - if (str->hdr.win_right < disk_buf.filesize) - break; - str_end_of_stream(str); - /* Fall-through */ - case SSTATE_END: - return STREAM_DATA_END; - } - - if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD)) - { - /* This data range is not buffered yet - register stream to - * be notified when it becomes available. Stream is obliged - * to enter a TSTATE_DATA state if it must wait. */ - int res = str_next_data_not_ready(str); - - if (res != STREAM_OK) - return res; - } - break; - /* STREAM_PM_STREAMING: */ - - case STREAM_PM_RANDOM_ACCESS: - str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET); - - if (str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit || - disk_buf_getbuffer(MIN_BUFAHEAD, &p, NULL, NULL) <= 0) - { - str_end_of_stream(str); - return STREAM_DATA_END; - } - - str->state = SSTATE_SYNC; - str->hdr.win_left = str->hdr.pos; - str->curr_packet = NULL; - str->curr_packet_end = p; - break; - /* STREAM_PM_RANDOM_ACCESS: */ - } - - if (str->state == SSTATE_SYNC) - { - /* Scanning for start code */ - if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) - { - INC_BUF(1); - continue; - } - } - - /* Found a start code - enter parse state */ - str->state = SSTATE_PARSE; - - /* Pack header, skip it */ - if (CMP_4_CONST(p, PACK_START_CODE)) - { - /* Max lookahead: 14 */ - if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */ - { - /* Max delta: 14 + 7 = 21 */ - /* Skip pack header and any stuffing bytes*/ - bytes = 14 + (p[13] & 7); - } - else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */ - { - bytes = 12; - } - else /* unknown - skip it */ - { - DEBUGF("weird pack header!\n"); - bytes = 5; - } - - INC_BUF(bytes); - } - - /* System header, parse and skip it - 6 bytes + size */ - if (CMP_4_CONST(p, SYSTEM_HEADER_START_CODE)) - { - /* Skip start code */ - /* Max Delta = 65535 + 6 = 65541 */ - bytes = 6 + ((p[4] << 8) | p[5]); - INC_BUF(bytes); - } - - /* Packet header, parse it */ - if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) - { - /* Problem? Meh...probably not but just a corrupted section. - * Try to resync the parser which will probably succeed. */ - DEBUGF("packet start code prefix not found: 0x%02x\n" - " wl:%lu wr:%lu\n" - " p:%p cp:%p cpe:%p\n" - " dbs:%p dbe:%p dbt:%p\n", - str->id, str->hdr.win_left, str->hdr.win_right, - p, str->curr_packet, str->curr_packet_end, - disk_buf.start, disk_buf.end, disk_buf.tail); - str->state = SSTATE_SYNC; - INC_BUF(1); /* Next byte - this one's no good */ - continue; - } - - /* We retrieve basic infos */ - /* Maximum packet length: 6 + 65535 = 65541 */ - id = p[3]; - length = ((p[4] << 8) | p[5]) + 6; - - if (id != str->id) - { - switch (id) - { - case MPEG_STREAM_PROGRAM_END: - /* end of stream */ - str_end_of_stream(str); - DEBUGF("MPEG program end: 0x%02x\n", str->id); - return STREAM_DATA_END; - case MPEG_STREAM_PACK_HEADER: - case MPEG_STREAM_SYSTEM_HEADER: - /* These shouldn't be here - no increment or resync - * since we'll pick it up above. */ - continue; - default: - /* It's not the packet we're looking for, skip it */ - INC_BUF(length); - continue; - } - } - - /* Ok, it's our packet */ - header = p; - - if ((header[6] & 0xc0) == 0x80) /* mpeg2 */ - { - /* Max Lookahead: 18 */ - /* Min length: 9 */ - /* Max length: 9 + 255 = 264 */ - length = 9 + header[8]; - - /* header points to the mpeg2 pes header */ - if ((header[7] & 0x80) != 0) - { - /* header has a pts */ - uint32_t pts = read_pts(header, 9); - - if (pts != INVALID_TIMESTAMP) - { - str->pts = pts; -#if 0 - /* DTS isn't used for anything since things just get - decoded ASAP but keep the code around */ - if (STREAM_IS_VIDEO(id)) - { - /* Video stream - header may have a dts as well */ - str->dts = pts; - - if (header[7] & 0x40) != 0x00) - { - pts = read_pts(header, 14); - if (pts != INVALID_TIMESTAMP) - str->dts = pts; - } - } -#endif - str->pkt_flags |= PKT_HAS_TS; - } - } - } - else /* mpeg1 */ - { - /* Max lookahead: 24 + 2 + 9 = 35 */ - /* Max len_skip: 24 + 2 = 26 */ - /* Min length: 7 */ - /* Max length: 24 + 2 + 9 = 35 */ - off_t len_skip; - uint8_t * ptsbuf; - - length = 7; - - while (header[length - 1] == 0xff) - { - if (++length > 23) - { - DEBUGF("Too much stuffing" ); - break; - } - } - - if ((header[length - 1] & 0xc0) == 0x40) - length += 2; - - len_skip = length; - length += mpeg1_skip_table[header[length - 1] >> 4]; - - /* Header points to the mpeg1 pes header */ - ptsbuf = header + len_skip; - - if ((ptsbuf[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf, -1)) - { - /* header has a pts */ - uint32_t pts = read_pts(ptsbuf, -1); - - if (pts != INVALID_TIMESTAMP) - { - str->pts = pts; -#if 0 - /* DTS isn't used for anything since things just get - decoded ASAP but keep the code around */ - if (STREAM_IS_VIDEO(id)) - { - /* Video stream - header may have a dts as well */ - str->dts = pts; - - if (ptsbuf[-1] & 0xf0) == 0x30) - { - pts = read_pts(ptsbuf, 4); - - if (pts != INVALID_TIMESTAMP) - str->dts = pts; - } - } -#endif - str->pkt_flags |= PKT_HAS_TS; - } - } - } - - p += length; - /* Max bytes: 6 + 65535 - 7 = 65534 */ - bytes = 6 + (header[4] << 8) + header[5] - length; - - str->curr_packet = p; - str->curr_packet_end = p + bytes; - str->hdr.win_left = str->hdr.win_right + length; - str->hdr.win_right = str->hdr.win_left + bytes; - - if (str->hdr.win_right > disk_buf.filesize) - { - /* No packet that exceeds end of file can be valid */ - str_end_of_stream(str); - return STREAM_DATA_END; - } - - return STREAM_OK; - } /* end while */ - - #undef INC_BUF -} - -/* This simply reads data from the file one page at a time and returns a - * pointer to it in the buffer. */ -static int parse_elementary(struct stream *str, enum stream_parse_mode type) -{ - uint8_t *p; - ssize_t len = 0; - - str->pkt_flags = 0; - - switch (type) - { - case STREAM_PM_STREAMING: - /* Has the end been reached already? */ - if (str->state == SSTATE_END) - return STREAM_DATA_END; - - /* Are we at the end of file? */ - if (str->hdr.win_left >= disk_buf.filesize) - { - str_end_of_stream(str); - return STREAM_DATA_END; - } - - if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD)) - { - /* This data range is not buffered yet - register stream to - * be notified when it becomes available. Stream is obliged - * to enter a TSTATE_DATA state if it must wait. */ - int res = str_next_data_not_ready(str); - - if (res != STREAM_OK) - return res; - } - - len = DISK_BUF_PAGE_SIZE; - - if ((size_t)(str->hdr.win_right + len) > (size_t)disk_buf.filesize) - len = disk_buf.filesize - str->hdr.win_right; - - if (len <= 0) - { - str_end_of_stream(str); - return STREAM_DATA_END; - } - - p = str->curr_packet_end; - if (p >= disk_buf.end) - p -= disk_buf.size; - break; - /* STREAM_PM_STREAMING: */ - - case STREAM_PM_RANDOM_ACCESS: - str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET); - len = disk_buf_getbuffer(DISK_BUF_PAGE_SIZE, &p, NULL, NULL); - - if (len <= 0 || str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit) - { - str_end_of_stream(str); - return STREAM_DATA_END; - } - break; - /* STREAM_PM_RANDOM_ACCESS: */ - } - - str->state = SSTATE_PARSE; - str->curr_packet = p; - str->curr_packet_end = p + len; - str->hdr.win_left = str->hdr.win_right; - str->hdr.win_right = str->hdr.win_left + len; - - return STREAM_OK; -} - -bool parser_prepare_image(uint32_t time) -{ - struct stream_scan sk; - int tries; - int result; - - stream_scan_init(&sk); - - if (!str_send_msg(&video_str, STREAM_NEEDS_SYNC, time)) - { - DEBUGF("Image was ready\n"); - return true; /* Should already have the image */ - } - -#ifdef HAVE_ADJUSTABLE_CPU_FREQ - rb->cpu_boost(true); /* No interference with trigger_cpu_boost */ -#endif - - str_send_msg(&video_str, STREAM_RESET, 0); - - sk.pos = parser_can_seek() ? - mpeg_parser_seek_PTS(time, video_str.id) : 0; - sk.len = sk.pos; - sk.dir = SSCAN_REVERSE; - - tries = 1; -try_again: - - if (mpeg_parser_scan_start_code(&sk, MPEG_START_GOP)) - { - DEBUGF("GOP found at: %ld\n", sk.pos); - - unsigned id = mpeg_parser_scan_pes(&sk); - - if (id != video_str.id && sk.pos > 0) - { - /* Not part of our stream */ - DEBUGF(" wrong stream: 0x%02x\n", id); - goto try_again; - } - - /* This will hit the PES header since it's known to be there */ - uint32_t pts = mpeg_parser_scan_pts(&sk, id); - - if (pts == INVALID_TIMESTAMP || pts > time) - { - DEBUGF(" wrong timestamp: %u\n", (unsigned)pts); - goto try_again; - } - } - - str_parser.parms.sd.time = time; - str_parser.parms.sd.sk.pos = MAX(sk.pos, 0); - str_parser.parms.sd.sk.len = 1024*1024; - str_parser.parms.sd.sk.dir = SSCAN_FORWARD; - - DEBUGF("thumb pos:%ld len:%ld\n", str_parser.parms.sd.sk.pos, - (long)str_parser.parms.sd.sk.len); - - result = str_send_msg(&video_str, STREAM_SYNC, - (intptr_t)&str_parser.parms.sd); - - if (result != STREAM_PERFECT_MATCH) - { - /* Two tries should be all that is nescessary to find the exact frame - * if the first GOP actually started later than the timestamp - the - * GOP just prior must then start on or earlier. */ - if (++tries <= 2) - goto try_again; - } - -#ifdef HAVE_ADJUSTABLE_CPU_FREQ - rb->cpu_boost(false); -#endif - - return result > STREAM_OK; -} - -/* Seek parser to the specified time and return absolute time. - * No actual hard stuff is performed here. That's done when streaming is - * about to begin or something from the current position is requested */ -uint32_t parser_seek_time(uint32_t time) -{ - if (!parser_can_seek()) - time = 0; - else if (time > str_parser.duration) - time = str_parser.duration; - - str_parser.last_seek_time = time + str_parser.start_pts; - return str_parser.last_seek_time; -} - -void parser_prepare_streaming(void) -{ - struct stream_window sw; - - DEBUGF("parser_prepare_streaming\n"); - - /* Prepare initial video frame */ - parser_prepare_image(str_parser.last_seek_time); - - /* Sync audio stream */ - if (audio_str.start_pts != INVALID_TIMESTAMP) - prepare_audio(str_parser.last_seek_time); - - /* Prequeue some data and set buffer window */ - if (!stream_get_window(&sw)) - sw.left = sw.right = disk_buf.filesize; - - DEBUGF(" swl:%ld swr:%ld\n", sw.left, sw.right); - - if (sw.right > disk_buf.filesize - 4*MIN_BUFAHEAD) - sw.right = disk_buf.filesize - 4*MIN_BUFAHEAD; - - disk_buf_prepare_streaming(sw.left, - sw.right - sw.left + 4*MIN_BUFAHEAD); -} - -int parser_init_stream(void) -{ - if (disk_buf.in_file < 0) - return STREAM_ERROR; - - /* TODO: Actually find which streams are available */ - audio_str.id = MPEG_STREAM_AUDIO_FIRST; - video_str.id = MPEG_STREAM_VIDEO_FIRST; - - /* Try to pull a video PES - if not found, try video init anyway which - * should succeed if it really is a video-only stream */ - video_str.hdr.pos = 0; - video_str.hdr.limit = 256*1024; - - if (parse_demux(&video_str, STREAM_PM_RANDOM_ACCESS) == STREAM_OK) - { - /* Found a video packet - assume program stream */ - str_parser.format = STREAM_FMT_MPEG_PS; - str_parser.next_data = parse_demux; - } - else - { - /* No PES element found - assume video elementary stream */ - str_parser.format = STREAM_FMT_MPV; - str_parser.next_data = parse_elementary; - } - - if (!init_video_info()) - { - /* Cannot determine video size, etc. */ - parser_init_state(); - return STREAM_UNSUPPORTED; - } - - if (str_parser.format == STREAM_FMT_MPEG_PS) - { - /* Initalize start_pts and end_pts with the length (in 45kHz units) of - * the movie. INVALID_TIMESTAMP if the time could not be determined */ - if (!init_times(&video_str) || !check_times(&video_str)) - { - /* Must have video at least */ - parser_init_state(); - return STREAM_UNSUPPORTED; - } - - str_parser.flags |= STREAMF_CAN_SEEK; - - if (init_times(&audio_str)) - { - /* Audio will be part of playback pool */ - stream_add_stream(&audio_str); - - if (check_times(&audio_str)) - { - /* Overall duration is maximum span */ - str_parser.start_pts = MIN(audio_str.start_pts, video_str.start_pts); - str_parser.end_pts = MAX(audio_str.end_pts, video_str.end_pts); - } - else - { - /* Bad times on audio - use video times */ - str_parser.start_pts = video_str.start_pts; - str_parser.end_pts = video_str.end_pts; - - /* Questionable: could use bitrate seek and match video to that */ - audio_str.start_pts = video_str.start_pts; - audio_str.end_pts = video_str.end_pts; - } - } - else - { - /* No audio stream - use video only */ - str_parser.start_pts = video_str.start_pts; - str_parser.end_pts = video_str.end_pts; - } - - str_parser.last_seek_time = str_parser.start_pts; - } - else - { - /* There's no way to handle times on this without a full file - * scan */ - audio_str.start_pts = INVALID_TIMESTAMP; - audio_str.end_pts = INVALID_TIMESTAMP; - video_str.start_pts = 0; - video_str.end_pts = INVALID_TIMESTAMP; - str_parser.start_pts = 0; - str_parser.end_pts = INVALID_TIMESTAMP; - } - - /* Add video to playback pool */ - stream_add_stream(&video_str); - - /* Cache duration - it's used very often */ - str_parser.duration = str_parser.end_pts - str_parser.start_pts; - - DEBUGF("Movie info:\n" - " size:%dx%d\n" - " start:%u\n" - " end:%u\n" - " duration:%u\n", - str_parser.dims.w, str_parser.dims.h, - (unsigned)str_parser.start_pts, - (unsigned)str_parser.end_pts, - (unsigned)str_parser.duration); - - return STREAM_OK; -} - -void parser_close_stream(void) -{ - stream_remove_streams(); - parser_init_state(); -} - -bool parser_init(void) -{ - parser_init_state(); - return true; -} -- cgit v1.2.3