diff options
Diffstat (limited to 'src/input/vcd/vcdplayer.c')
-rw-r--r-- | src/input/vcd/vcdplayer.c | 1040 |
1 files changed, 1040 insertions, 0 deletions
diff --git a/src/input/vcd/vcdplayer.c b/src/input/vcd/vcdplayer.c new file mode 100644 index 000000000..c3f9069da --- /dev/null +++ b/src/input/vcd/vcdplayer.c @@ -0,0 +1,1040 @@ +/* + $Id: vcdplayer.c,v 1.1 2003/10/13 11:47:11 f1rmb Exp $ + + Copyright (C) 2002,2003 Rocky Bernstein <rocky@panix.com> + + 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 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* Standard includes */ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <time.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#include <errno.h> + +#ifdef HAVE_VCDNAV +#include <libvcd/files.h> +#include <cdio/iso9660.h> +#else +#include "libvcd/files.h" +#include "cdio/iso9660.h" +#endif + +#include "vcdplayer.h" +#include "vcdio.h" + +#define LOG_ERR(this, s, args...) \ + if (this != NULL && this->log_err != NULL) \ + this->log_err("%s: "s, __func__ , ##args) + +unsigned long int vcdplayer_debug = 0; + +static void _vcdplayer_set_origin(vcdplayer_input_t *this); + +/*! + Return true if playback control (PBC) is on +*/ +bool +vcdplayer_pbc_is_on(const vcdplayer_input_t *this) +{ + return VCDINFO_INVALID_ENTRY != this->cur_lid; +} + +/* Given an itemid, return the size for the object (via information + previously stored when opening the vcd). */ +static size_t +_vcdplayer_get_item_size(vcdplayer_input_t *this, vcdinfo_itemid_t itemid) +{ + switch (itemid.type) { + case VCDINFO_ITEM_TYPE_ENTRY: + return this->entry[itemid.num].size; + break; + case VCDINFO_ITEM_TYPE_SEGMENT: + return this->segment[itemid.num].size; + break; + case VCDINFO_ITEM_TYPE_TRACK: + return this->track[itemid.num-1].size; + break; + case VCDINFO_ITEM_TYPE_LID: + /* Play list number (LID) */ + return 0; + break; + case VCDINFO_ITEM_TYPE_NOTFOUND: + case VCDINFO_ITEM_TYPE_SPAREID2: + default: + LOG_ERR(this, "%s %d\n", _("bad item type"), itemid.type); + return 0; + } +} + +#define add_format_str_info(val) \ + { \ + const char *str = val; \ + unsigned int len; \ + if (val != NULL) { \ + len=strlen(str); \ + if (len != 0) { \ + strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \ + tp += len; \ + } \ + saw_control_prefix = false; \ + } \ + } + +#define add_format_num_info(val, fmt) \ + { \ + char num_str[10]; \ + unsigned int len; \ + sprintf(num_str, fmt, val); \ + len=strlen(num_str); \ + if (len != 0) { \ + strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \ + tp += len; \ + } \ + saw_control_prefix = false; \ + } + +/*! + Take a format string and expand escape sequences, that is sequences that + begin with %, with information from the current VCD. + The expanded string is returned. Here is a list of escape sequences: + + %A : The album information + %C : The VCD volume count - the number of CD's in the collection. + %c : The VCD volume num - the number of the CD in the collection. + %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD + %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT... + %L : The playlist ID prefixed with " LID" if it exists + %N : The current number of the above - a decimal number + %P : The publisher ID + %p : The preparer ID + %S : If we are in a segment (menu), the kind of segment + %T : The track number + %V : The volume set ID + %v : The volume ID + A number between 1 and the volume count. + %% : a % +*/ +char * +vcdplayer_format_str(vcdplayer_input_t *this, const char format_str[]) +{ +#define TEMP_STR_SIZE 256 +#define TEMP_STR_LEN (TEMP_STR_SIZE-1) + static char temp_str[TEMP_STR_SIZE]; + size_t i; + char * tp = temp_str; + bool saw_control_prefix = false; + size_t format_len = strlen(format_str); + vcdinfo_obj_t *obj = this->vcd; + + bzero(temp_str, TEMP_STR_SIZE); + + for (i=0; i<format_len; i++) { + + if (!saw_control_prefix && format_str[i] != '%') { + *tp++ = format_str[i]; + saw_control_prefix = false; + continue; + } + + switch(format_str[i]) { + case '%': + if (saw_control_prefix) { + *tp++ = '%'; + } + saw_control_prefix = !saw_control_prefix; + break; + case 'A': + add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(obj), + MAX_ALBUM_LEN)); + break; + + case 'c': + add_format_num_info(vcdinfo_get_volume_num(obj), "%d"); + break; + + case 'C': + add_format_num_info(vcdinfo_get_volume_count(obj), "%d"); + break; + + case 'F': + add_format_str_info(vcdinfo_get_format_version_str(obj)); + break; + + case 'I': + { + switch (this->play_item.type) { + case VCDINFO_ITEM_TYPE_TRACK: + strncat(tp, "Track", TEMP_STR_LEN-(tp-temp_str)); + tp += strlen("Track"); + break; + case VCDINFO_ITEM_TYPE_ENTRY: + strncat(tp, "Entry", TEMP_STR_LEN-(tp-temp_str)); + tp += strlen("Entry"); + break; + case VCDINFO_ITEM_TYPE_SEGMENT: + strncat(tp, "Segment", TEMP_STR_LEN-(tp-temp_str)); + tp += strlen("Segment"); + break; + case VCDINFO_ITEM_TYPE_LID: + strncat(tp, "List ID", TEMP_STR_LEN-(tp-temp_str)); + tp += strlen("List ID"); + break; + case VCDINFO_ITEM_TYPE_SPAREID2: + strncat(tp, "Navigation", TEMP_STR_LEN-(tp-temp_str)); + tp += strlen("Navigation"); + break; + default: + /* What to do? */ + ; + } + saw_control_prefix = false; + } + break; + + case 'L': + if (vcdplayer_pbc_is_on(this)) { + char num_str[10]; + sprintf(num_str, " List ID %d", this->cur_lid); + strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); + tp += strlen(num_str); + } + saw_control_prefix = false; + break; + + case 'N': + add_format_num_info(this->play_item.num, "%d"); + break; + + case 'p': + add_format_str_info(vcdinfo_get_preparer_id(obj)); + break; + + case 'P': + add_format_str_info(vcdinfo_get_publisher_id(obj)); + break; + + case 'S': + if ( VCDINFO_ITEM_TYPE_SEGMENT==this->play_item.type ) { + char seg_type_str[10]; + + sprintf(seg_type_str, " %s", + vcdinfo_video_type2str(obj, this->play_item.num)); + strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str)); + tp += strlen(seg_type_str); + } + saw_control_prefix = false; + break; + + case 'T': + add_format_num_info(this->cur_track, "%d"); + break; + + case 'V': + add_format_str_info(vcdinfo_get_volumeset_id(obj)); + break; + + case 'v': + add_format_str_info(vcdinfo_get_volume_id(obj)); + break; + + default: + *tp++ = '%'; + *tp++ = format_str[i]; + saw_control_prefix = false; + } + } + return strdup(temp_str); +} + +static void +_vcdplayer_update_entry(vcdinfo_obj_t *obj, uint16_t ofs, uint16_t *entry, + const char *label) +{ + if ( ofs == VCDINFO_INVALID_OFFSET ) { + *entry = VCDINFO_INVALID_ENTRY; + } else { + vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(obj, ofs); + if (off_t != NULL) { + *entry = off_t->lid; + dbg_print(INPUT_DBG_PBC, "%s: %d\n", label, off_t->lid); + } else + *entry = VCDINFO_INVALID_ENTRY; + } +} + +/*! + Update next/prev/return/default navigation buttons (via this->cur_lid). + Update size of play-item (via this->play_item). +*/ +void +vcdplayer_update_nav(vcdplayer_input_t *this) +{ + int play_item = this->play_item.num; + vcdinfo_obj_t *obj = this->vcd; + + int min_entry = 1; + int max_entry = 0; + + if (vcdplayer_pbc_is_on(this)) { + + vcdinfo_lid_get_pxd(obj, &(this->pxd), this->cur_lid); + + switch (this->pxd.descriptor_type) { + case PSD_TYPE_SELECTION_LIST: + case PSD_TYPE_EXT_SELECTION_LIST: + if (this->pxd.psd == NULL) return; + _vcdplayer_update_entry(obj, vcdinf_psd_get_prev_offset(this->pxd.psd), + &(this->prev_entry), "prev"); + + _vcdplayer_update_entry(obj, vcdinf_psd_get_next_offset(this->pxd.psd), + &(this->next_entry), "next"); + + _vcdplayer_update_entry(obj, vcdinf_psd_get_return_offset(this->pxd.psd), + &(this->return_entry), "return"); + + _vcdplayer_update_entry(obj, + vcdinfo_get_default_offset(obj, this->cur_lid), + &(this->default_entry), "default"); + break; + case PSD_TYPE_PLAY_LIST: + if (this->pxd.pld == NULL) return; + _vcdplayer_update_entry(obj, vcdinf_pld_get_prev_offset(this->pxd.pld), + &(this->prev_entry), "prev"); + + _vcdplayer_update_entry(obj, vcdinf_pld_get_next_offset(this->pxd.pld), + &(this->next_entry), "next"); + + _vcdplayer_update_entry(obj, vcdinf_pld_get_return_offset(this->pxd.pld), + &(this->return_entry), "return"); + this->default_entry = VCDINFO_INVALID_ENTRY; + break; + case PSD_TYPE_END_LIST: + this->origin_lsn = this->cur_lsn = this->end_lsn = VCDINFO_NULL_LSN; + /* Fall through */ + case PSD_TYPE_COMMAND_LIST: + this->next_entry = this->prev_entry = this->return_entry = + this->default_entry = VCDINFO_INVALID_ENTRY; + break; + } + + this->update_title(); + return; + } + + /* PBC is not on. Set up for simplified next, prev, and return. */ + + switch (this->play_item.type) { + case VCDINFO_ITEM_TYPE_ENTRY: + case VCDINFO_ITEM_TYPE_SEGMENT: + case VCDINFO_ITEM_TYPE_TRACK: + + switch (this->play_item.type) { + case VCDINFO_ITEM_TYPE_ENTRY: + max_entry = this->num_entries; + min_entry = 0; /* Can remove when Entries start at 1. */ + this->cur_track = vcdinfo_get_track(obj, play_item); + this->track_lsn = vcdinfo_get_track_lsn(obj, this->cur_track); + break; + case VCDINFO_ITEM_TYPE_SEGMENT: + max_entry = this->num_segments; + this->cur_track = VCDINFO_INVALID_TRACK; + + break; + case VCDINFO_ITEM_TYPE_TRACK: + max_entry = this->num_tracks; + this->cur_track = this->play_item.num; + this->track_lsn = vcdinfo_get_track_lsn(obj, this->cur_track); + break; + default: ; /* Handle exceptional cases below */ + } + + _vcdplayer_set_origin(this); + /* Set next, prev, return and default to simple and hopefully + useful values. + */ + if (play_item+1 >= max_entry) + this->next_entry = VCDINFO_INVALID_ENTRY; + else + this->next_entry = play_item+1; + + if (play_item-1 >= min_entry) + this->prev_entry = play_item-1; + else + this->prev_entry = VCDINFO_INVALID_ENTRY; + + this->default_entry = play_item; + this->return_entry = min_entry; + break; + + case VCDINFO_ITEM_TYPE_LID: + { + /* Should have handled above. */ + break; + } + default: ; + } + this->update_title(); +} + +/*! + Set reading to play an entire track. +*/ +static void +_vcdplayer_set_track(vcdplayer_input_t *this, unsigned int track_num) +{ + if (track_num < 1 || track_num > this->num_tracks) + return; + else { + vcdinfo_obj_t *obj = this->vcd; + vcdinfo_itemid_t itemid; + + itemid.num = track_num; + itemid.type = VCDINFO_ITEM_TYPE_TRACK; + this->in_still = 0; + this->cur_lsn = vcdinfo_get_track_lsn(obj, track_num); + this->play_item = itemid; + this->cur_track = track_num; + this->track_lsn = this->cur_lsn; + + _vcdplayer_set_origin(this); + + dbg_print(INPUT_DBG_LSN, "LSN: %u\n", this->cur_lsn); + } +} + +/*! + Set reading to play an entry +*/ +static void +_vcdplayer_set_entry(vcdplayer_input_t *this, unsigned int num) +{ + vcdinfo_obj_t *obj = this->vcd; + unsigned int num_entries = vcdinfo_get_num_entries(obj); + + if (num >= num_entries) { + LOG_ERR(this, "%s %d\n", _("bad entry number"), num); + return; + } else { + vcdinfo_itemid_t itemid; + + itemid.num = num; + itemid.type = VCDINFO_ITEM_TYPE_ENTRY; + this->in_still = 0; + this->cur_lsn = vcdinfo_get_entry_lsn(obj, num); + this->play_item = itemid; + this->cur_track = vcdinfo_get_track(obj, num); + this->track_lsn = vcdinfo_get_track_lsn(obj, this->cur_track); + this->track_end_lsn = this->track_lsn + + this->track[this->cur_track-1].size; + + _vcdplayer_set_origin(this); + + dbg_print(INPUT_DBG_LSN, "LSN: %u, track_end LSN: %u\n", + this->cur_lsn, this->track_end_lsn); + } +} + +/*! + Set reading to play an segment (e.g. still frame) +*/ +static void +_vcdplayer_set_segment(vcdplayer_input_t *this, unsigned int num) +{ + vcdinfo_obj_t *obj = this->vcd; + segnum_t num_segs = vcdinfo_get_num_segments(obj); + + if (num >= num_segs) { + LOG_ERR(this, "%s %d\n", _("bad segment number"), num); + return; + } else { + vcdinfo_itemid_t itemid; + + this->cur_lsn = vcdinfo_get_seg_lsn(obj, num); + this->cur_track = 0; + + if (VCDINFO_NULL_LSN==this->cur_lsn) { + LOG_ERR(this, "%s %d\n", + _("Error in getting current segment number"), num); + return; + } + + itemid.num = num; + itemid.type = VCDINFO_ITEM_TYPE_SEGMENT; + this->play_item = itemid; + + _vcdplayer_set_origin(this); + + dbg_print(INPUT_DBG_LSN, "LSN: %u\n", this->cur_lsn); + } +} + +/* Play entry. */ +/* Play a single item. */ +static void +vcdplayer_play_single_item(vcdplayer_input_t *this, vcdinfo_itemid_t itemid) +{ + vcdinfo_obj_t *obj = this->vcd; + + dbg_print(INPUT_DBG_CALL, "called itemid.num: %d, itemid.type: %d\n", + itemid.num, itemid.type); + + this->in_still = 0; + + switch (itemid.type) { + case VCDINFO_ITEM_TYPE_SEGMENT: + { + vcdinfo_video_segment_type_t segtype + = vcdinfo_get_video_type(obj, itemid.num); + segnum_t num_segs = vcdinfo_get_num_segments(obj); + + dbg_print(INPUT_DBG_PBC, "%s (%d), itemid.num: %d\n", + vcdinfo_video_type2str(obj, itemid.num), + (int) segtype, itemid.num); + + if (itemid.num >= num_segs) return; + _vcdplayer_set_segment(this, itemid.num); + + switch (segtype) + { + case VCDINFO_FILES_VIDEO_NTSC_STILL: + case VCDINFO_FILES_VIDEO_NTSC_STILL2: + case VCDINFO_FILES_VIDEO_PAL_STILL: + case VCDINFO_FILES_VIDEO_PAL_STILL2: + this->in_still = -5; + break; + default: + this->in_still = 0; + } + + break; + } + + case VCDINFO_ITEM_TYPE_TRACK: + dbg_print(INPUT_DBG_PBC, "track %d\n", itemid.num); + if (itemid.num < 1 || itemid.num > this->num_tracks) return; + _vcdplayer_set_track(this, itemid.num); + break; + + case VCDINFO_ITEM_TYPE_ENTRY: + { + unsigned int num_entries = vcdinfo_get_num_entries(obj); + dbg_print(INPUT_DBG_PBC, "entry %d\n", itemid.num); + if (itemid.num >= num_entries) return; + _vcdplayer_set_entry(this, itemid.num); + break; + } + + case VCDINFO_ITEM_TYPE_LID: + LOG_ERR(this, "%s\n", _("Should have converted this above")); + break; + + case VCDINFO_ITEM_TYPE_NOTFOUND: + dbg_print(INPUT_DBG_PBC, "play nothing\n"); + this->cur_lsn = this->end_lsn; + return; + + default: + LOG_ERR(this, "item type %d not implemented.\n", itemid.type); + return; + } + + this->play_item = itemid; + + vcdplayer_update_nav(this); + + /* Some players like xine, have a fifo queue of audio and video buffers + that need to be flushed when playing a new selection. */ + /* if (this->flush_buffers) + this->flush_buffers(); */ + +} + +/* + Get the next play-item in the list given in the LIDs. Note play-item + here refers to list of play-items for a single LID It shouldn't be + confused with a user's list of favorite things to play or the + "next" field of a LID which moves us to a different LID. + */ +static bool +_vcdplayer_inc_play_item(vcdplayer_input_t *this) +{ + int noi; + + dbg_print(INPUT_DBG_CALL, "called pli: %d\n", this->pdi); + + if ( NULL == this || NULL == this->pxd.pld ) return false; + + noi = vcdinf_pld_get_noi(this->pxd.pld); + + if ( noi <= 0 ) return false; + + /* Handle delays like autowait or wait here? */ + + this->pdi++; + + if ( this->pdi < 0 || this->pdi >= noi ) return false; + + else { + uint16_t trans_itemid_num=vcdinf_pld_get_play_item(this->pxd.pld, + this->pdi); + vcdinfo_itemid_t trans_itemid; + + if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false; + + vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid); + dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s\n", + this->pdi, vcdinfo_pin2str (trans_itemid_num)); + vcdplayer_play_single_item(this, trans_itemid); + return true; + } +} + +void +vcdplayer_play(vcdplayer_input_t *this, vcdinfo_itemid_t itemid) +{ + dbg_print(INPUT_DBG_CALL, "called itemid.num: %d itemid.type: %d\n", + itemid.num, itemid.type); + + if (!vcdplayer_pbc_is_on(this)) { + vcdplayer_play_single_item(this, itemid); + } else { + /* PBC on - Itemid.num is LID. */ + + vcdinfo_obj_t *obj = this->vcd; + + if (obj == NULL) return; + + this->cur_lid = itemid.num; + vcdinfo_lid_get_pxd(obj, &(this->pxd), itemid.num); + + switch (this->pxd.descriptor_type) { + + case PSD_TYPE_SELECTION_LIST: + case PSD_TYPE_EXT_SELECTION_LIST: { + vcdinfo_itemid_t trans_itemid; + uint16_t trans_itemid_num; + + if (this->pxd.psd == NULL) return; + trans_itemid_num = vcdinf_psd_get_itemid(this->pxd.psd); + vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid); + this->loop_count = 1; + this->loop_item = trans_itemid; + vcdplayer_play_single_item(this, trans_itemid); + break; + } + + case PSD_TYPE_PLAY_LIST: { + if (this->pxd.pld == NULL) return; + this->pdi = -1; + _vcdplayer_inc_play_item(this); + break; + } + + case PSD_TYPE_END_LIST: + case PSD_TYPE_COMMAND_LIST: + + default: + ; + } + } +} + +/* + Set's start origin and size for subsequent seeks. + input: this->cur_lsn, this->play_item + changed: this->origin_lsn, this->end_lsn +*/ +static void +_vcdplayer_set_origin(vcdplayer_input_t *this) +{ + size_t size = _vcdplayer_get_item_size(this, this->play_item); + + this->end_lsn = this->cur_lsn + size; + this->origin_lsn = this->cur_lsn; + + dbg_print((INPUT_DBG_CALL|INPUT_DBG_LSN), "end LSN: %u\n", this->end_lsn); +} + +#define RETURN_NULL_BLOCK \ + memset (buf, 0, M2F2_SECTOR_SIZE); \ + buf[0] = 0; buf[1] = 0; buf[2] = 0x01; \ + return READ_BLOCK + +#define RETURN_NULL_STILL \ + memset (buf, 0, M2F2_SECTOR_SIZE); \ + buf[0] = 0; buf[1] = 0; buf[2] = 0x01; \ + return READ_STILL_FRAME + +#define SLEEP_1_SEC_AND_HANDLE_EVENTS \ + if (this->handle_events()) goto skip_next_play; \ + this->sleep(250000); \ + if (this->handle_events()) goto skip_next_play; \ + this->sleep(250000); \ + if (this->handle_events()) goto skip_next_play; \ + this->sleep(250000); \ + if (this->handle_events()) goto skip_next_play; \ + this->sleep(250000); +/* if (this->in_still) this->force_redisplay(); */ + + +/* Handles PBC navigation when reaching the end of a play item. */ +static vcdplayer_read_status_t +vcdplayer_pbc_nav (vcdplayer_input_t *this, uint8_t *buf) +{ + /* We are in playback control. */ + vcdinfo_itemid_t itemid; + + if (0 != this->in_still && this->in_still != -5) { + SLEEP_1_SEC_AND_HANDLE_EVENTS; + if (this->in_still > 0) this->in_still--; + return READ_STILL_FRAME; + } + + /* The end of an entry is really the end of the associated + sequence (or track). */ + + if ( (VCDINFO_ITEM_TYPE_ENTRY == this->play_item.type) && + (this->cur_lsn < this->track_end_lsn) ) { + /* Set up to just continue to the next entry */ + this->play_item.num++; + dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), + "continuing into next entry: %u\n", this->play_item.num); + vcdplayer_play_single_item(this, this->play_item); + this->update_title(); + goto skip_next_play; + } + + switch (this->pxd.descriptor_type) { + case PSD_TYPE_END_LIST: + return READ_END; + break; + case PSD_TYPE_PLAY_LIST: { + int wait_time = vcdinf_get_wait_time(this->pxd.pld); + + dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d\n", wait_time); + + if (_vcdplayer_inc_play_item(this)) + goto skip_next_play; + + /* Handle any wait time given. */ + if (-5 == this->in_still) { + if (wait_time != 0) { + this->in_still = wait_time - 1; + SLEEP_1_SEC_AND_HANDLE_EVENTS ; + return READ_STILL_FRAME; + } + } + break; + } + case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */ + case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */ + { + int wait_time = vcdinf_get_timeout_time(this->pxd.psd); + uint16_t timeout_offs = vcdinf_get_timeout_offset(this->pxd.psd); + uint16_t max_loop = vcdinf_get_loop_count(this->pxd.psd); + vcdinfo_offset_t *offset_timeout_LID = + vcdinfo_get_offset_t(this->vcd, timeout_offs); + + dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d\n", + wait_time, this->loop_count, max_loop); + + /* Handle any wait time given */ + if (-5 == this->in_still) { + this->in_still = wait_time - 1; + SLEEP_1_SEC_AND_HANDLE_EVENTS ; + return READ_STILL_FRAME; + } + + /* Handle any looping given. */ + if ( max_loop == 0 || this->loop_count < max_loop ) { + this->loop_count++; + if (this->loop_count == 0x7f) this->loop_count = 0; + vcdplayer_play_single_item(this, this->loop_item); + if (this->in_still) this->force_redisplay(); + goto skip_next_play; + } + + /* Looping finished and wait finished. Move to timeout + entry or next entry, or handle still. */ + + if (NULL != offset_timeout_LID) { + /* Handle timeout_LID */ + itemid.num = offset_timeout_LID->lid; + itemid.type = VCDINFO_ITEM_TYPE_LID; + dbg_print(INPUT_DBG_PBC, "timeout to: %d\n", itemid.num); + vcdplayer_play(this, itemid); + goto skip_next_play; + } else { + int num_selections = vcdinf_get_num_selections(this->pxd.psd); + if (num_selections > 0) { + /* Pick a random selection. */ + unsigned int bsn=vcdinf_get_bsn(this->pxd.psd); + int rand_selection=bsn + + (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0)); + lid_t rand_lid=vcdplayer_selection2lid (this, rand_selection); + itemid.num = rand_lid; + itemid.type = VCDINFO_ITEM_TYPE_LID; + dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d\n", + rand_selection - bsn, rand_lid); + vcdplayer_play(this, itemid); + goto skip_next_play; + } else if (this->in_still) { + /* Hack: Just go back and do still again */ + SLEEP_1_SEC_AND_HANDLE_EVENTS ; + RETURN_NULL_STILL ; + } + } + + break; + } + case VCDINFO_ITEM_TYPE_NOTFOUND: + LOG_ERR(this, "NOTFOUND in PBC -- not supposed to happen\n"); + break; + case VCDINFO_ITEM_TYPE_SPAREID2: + LOG_ERR(this, "SPAREID2 in PBC -- not supposed to happen\n"); + break; + case VCDINFO_ITEM_TYPE_LID: + LOG_ERR(this, "LID in PBC -- not supposed to happen\n"); + break; + + default: + ; + } + /* FIXME: Should handle autowait ... */ + itemid.num = this->next_entry; + itemid.type = VCDINFO_ITEM_TYPE_LID; + vcdplayer_play(this, itemid); + skip_next_play: ; + return READ_BLOCK; +} + +/* Handles navigation when NOT in PBC reaching the end of a play item. + The navigations rules here we are sort of made up, but the intent + is to do something that's probably right or helpful. +*/ +static vcdplayer_read_status_t +vcdplayer_non_pbc_nav (vcdplayer_input_t *this, uint8_t *buf) +{ + /* Not in playback control. Do we advance automatically or stop? */ + switch (this->play_item.type) { + case VCDINFO_ITEM_TYPE_TRACK: + case VCDINFO_ITEM_TYPE_ENTRY: + if (this->autoadvance && this->next_entry != VCDINFO_INVALID_ENTRY) { + this->play_item.num=this->next_entry; + vcdplayer_update_nav(this); + } else + return READ_END; + break; + case VCDINFO_ITEM_TYPE_SPAREID2: + printf("SPAREID2\n"); + if (this->in_still) { + RETURN_NULL_STILL ; + /* Hack: Just go back and do still again */ + /*this->force_redisplay(); + this->cur_lsn = this->origin_lsn;*/ + } + return READ_END; + + case VCDINFO_ITEM_TYPE_NOTFOUND: + LOG_ERR(this, "NOTFOUND outside PBC -- not supposed to happen\n"); + if (this->in_still) { + RETURN_NULL_STILL ; + /* Hack: Just go back and do still again */ + /*this->force_redisplay(); + this->cur_lsn = this->origin_lsn;*/ + } else + return READ_END; + break; + + case VCDINFO_ITEM_TYPE_LID: + LOG_ERR(this, "LID outside PBC -- not supposed to happen\n"); + if (this->in_still) { + RETURN_NULL_STILL ; + /* Hack: Just go back and do still again */ + /* this->force_redisplay(); + this->cur_lsn = this->origin_lsn; */ + } else + return READ_END; + break; + + case VCDINFO_ITEM_TYPE_SEGMENT: + if (this->in_still) { + /* Hack: Just go back and do still again */ + RETURN_NULL_STILL ; + } + return READ_END; + } + return READ_BLOCK; +} + + +/*! + Read nlen bytes into buf and return the status back. + + This routine is a bit complicated because on reaching the end of + a track or entry we may automatically advance to the item, or + interpret the next item in the playback-control list. +*/ +vcdplayer_read_status_t +vcdplayer_read (vcdplayer_input_t *this, uint8_t *buf, const off_t nlen) +{ + + this->handle_events (); + + if ( this->cur_lsn >= this->end_lsn ) { + vcdplayer_read_status_t read_status; + + /* We've run off of the end of this entry. Do we continue or stop? */ + dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), + "end reached, cur: %u, end: %u\n", this->cur_lsn, this->end_lsn); + + handle_item_continuation: + read_status = vcdplayer_pbc_is_on(this) + ? vcdplayer_pbc_nav(this, buf) + : vcdplayer_non_pbc_nav(this, buf); + + if (READ_BLOCK != read_status) return read_status; + } + + /* Read the next block. + + Important note: we probably speed things up by removing "data" + and the memcpy to it by extending vcd_image_source_read_mode2 + to allow a mode to do what's below in addition to its + "raw" and "block" mode. It also would probably improve the modularity + a little bit as well. + */ + + { + CdIo *img = vcdinfo_get_cd_image(this->vcd); + typedef struct { + uint8_t subheader [8]; + uint8_t data [M2F2_SECTOR_SIZE]; + } vcdsector_t; + vcdsector_t vcd_sector; + + do { + dbg_print(INPUT_DBG_LSN, "LSN: %u\n", this->cur_lsn); + if (cdio_read_mode2_sector(img, &vcd_sector, this->cur_lsn, true)!=0) { + dbg_print(INPUT_DBG_LSN, "read error\n"); + return READ_ERROR; + } + this->cur_lsn++; + + if ( this->cur_lsn >= this->end_lsn ) { + /* We've run off of the end of this entry. Do we continue or stop? */ + dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), + "end reached in reading, cur: %u, end: %u\n", + this->cur_lsn, this->end_lsn); + break; + } + + /* Check header ID for a padding sector and simply discard + these. It is alleged that VCD's put these in to keep the + bitrate constant. + */ + } while((vcd_sector.subheader[2]&~0x01)==0x60); + + if ( this->cur_lsn >= this->end_lsn ) + /* We've run off of the end of this entry. Do we continue or stop? */ + goto handle_item_continuation; + + memcpy (buf, vcd_sector.data, M2F2_SECTOR_SIZE); + return READ_BLOCK; + } +} + +/* Do if needed */ +void +vcdplayer_send_button_update(vcdplayer_input_t *this, const int mode) +{ + /* dbg_print(INPUT_DBG_CALL, "Called\n"); */ + return; +} + +lid_t +vcdplayer_selection2lid (vcdplayer_input_t *this, int entry_num) +{ + /* FIXME: Some of this probably gets moved to vcdinfo. */ + /* Convert selection number to lid and then entry number...*/ + unsigned int offset; + unsigned int bsn=vcdinf_get_bsn(this->pxd.psd); + vcdinfo_obj_t *obj = this->vcd; + + dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), + "Called lid %u, entry_num %d bsn %d\n", this->cur_lid, + entry_num, bsn); + + if ( (entry_num - bsn + 1) > 0) { + offset = vcdinfo_lid_get_offset(obj, this->cur_lid, entry_num-bsn+1); + } else { + LOG_ERR(this, "Selection number %u too small. bsn %u\n", + entry_num, bsn); + return VCDINFO_INVALID_LID; + } + + if (offset != VCDINFO_INVALID_OFFSET) { + vcdinfo_offset_t *ofs; + int old = entry_num; + + switch (offset) { + case PSD_OFS_DISABLED: + LOG_ERR(this, "Selection %u disabled\n", entry_num); + return VCDINFO_INVALID_LID; + case PSD_OFS_MULTI_DEF: + LOG_ERR(this, "Selection %u multi_def\n", entry_num); + return VCDINFO_INVALID_LID; + case PSD_OFS_MULTI_DEF_NO_NUM: + LOG_ERR(this, "Selection %u multi_def_no_num\n", entry_num); + return VCDINFO_INVALID_LID; + default: ; + } + + ofs = vcdinfo_get_offset_t(obj, offset); + + if (NULL == ofs) { + LOG_ERR(this, "error in vcdinfo_get_offset\n"); + return -1; + } + dbg_print(INPUT_DBG_PBC, + "entry %u turned into selection lid %u\n", + old, ofs->lid); + return ofs->lid; + + } else { + LOG_ERR(this, "invalid or unset entry %u\n", entry_num); + return VCDINFO_INVALID_LID; + } +} + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ |