/*! Play item assocated with the "prev" selection. Return false if there was some problem. */ bool vcdplayer_play_prev( access_t * p_access ) { vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys; vcdinfo_obj_t *p_vcdinfo = p_vcdplayer->vcd; vcdinfo_itemid_t itemid; dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), "current: %d" , p_vcdplayer->play_item.num); itemid = p_vcdplayer->play_item; if (vcdplayer_pbc_is_on(p_vcdplayer)) { vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid); switch (p_vcdplayer->pxd.descriptor_type) { case PSD_TYPE_SELECTION_LIST: case PSD_TYPE_EXT_SELECTION_LIST: if (p_vcdplayer->pxd.psd == NULL) return false; vcdplayer_update_entry(p_access, vcdinf_psd_get_prev_offset(p_vcdplayer->pxd.psd), &itemid.num, "prev"); itemid.type = VCDINFO_ITEM_TYPE_LID; break; case PSD_TYPE_PLAY_LIST: if (p_vcdplayer->pxd.pld == NULL) return false; vcdplayer_update_entry(p_access, vcdinf_pld_get_prev_offset(p_vcdplayer->pxd.pld), &itemid.num, "prev"); itemid.type = VCDINFO_ITEM_TYPE_LID; break; case PSD_TYPE_END_LIST: case PSD_TYPE_COMMAND_LIST: LOG_WARN( "There is no PBC 'prev' selection here" ); return false; } } else { /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */ int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type) ? 0 : 1; if (p_vcdplayer->play_item.num > min_entry) { itemid.num = p_vcdplayer->play_item.num-1; } else { LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" ); return false; } } /** ??? p_vcdplayer->update_title(); ***/ vcdplayer_play( p_access, itemid ); return VLC_SUCCESS; }
/*! Play item assocated with the "prev" selection. Return VLC_FALSE if there was some problem. */ vlc_bool_t vcdplayer_play_prev( input_thread_t * p_input ) { thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data; vcdinfo_obj_t *obj = p_vcd->vcd; vcdinfo_itemid_t itemid; dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), "current: %d" , p_vcd->play_item.num); itemid.type = p_vcd->play_item.type; if (vcdplayer_pbc_is_on(p_vcd)) { vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid); switch (p_vcd->pxd.descriptor_type) { case PSD_TYPE_SELECTION_LIST: case PSD_TYPE_EXT_SELECTION_LIST: if (p_vcd->pxd.psd == NULL) return VLC_FALSE; vcdplayer_update_entry( p_input, vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), &itemid.num, "prev"); itemid.type = VCDINFO_ITEM_TYPE_LID; break; case PSD_TYPE_PLAY_LIST: if (p_vcd->pxd.pld == NULL) return VLC_FALSE; vcdplayer_update_entry( p_input, vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), &itemid.num, "prev"); itemid.type = VCDINFO_ITEM_TYPE_LID; break; case PSD_TYPE_END_LIST: case PSD_TYPE_COMMAND_LIST: LOG_WARN( "There is no PBC 'prev' selection here" ); return VLC_FALSE; } } else { /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */ int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) ? 0 : 1; if (p_vcd->play_item.num > min_entry) { itemid.num = p_vcd->play_item.num-1; } else { LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" ); return VLC_FALSE; } } /** ??? p_vcd->update_title(); ***/ return VLC_SUCCESS == VCDPlay( p_input, itemid ); }
void vcdplayer_play(access_t *p_access, vcdinfo_itemid_t itemid) { vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys; dbg_print(INPUT_DBG_CALL, "called itemid.num: %d itemid.type: %d", itemid.num, itemid.type); if (!vcdplayer_pbc_is_on(p_vcdplayer)) { vcdplayer_play_single_item(p_access, itemid); } else { /* PBC on - Itemid.num is LID. */ vcdinfo_obj_t *p_vcdinfo = p_vcdplayer->vcd; if (p_vcdinfo == NULL) return; p_vcdplayer->i_lid = itemid.num; vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), itemid.num); switch (p_vcdplayer->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 (p_vcdplayer->pxd.psd == NULL) return; trans_itemid_num = vcdinf_psd_get_itemid(p_vcdplayer->pxd.psd); vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid); p_vcdplayer->i_loop = 1; p_vcdplayer->loop_item = trans_itemid; vcdplayer_play_single_item(p_access, trans_itemid); break; } case PSD_TYPE_PLAY_LIST: { if (p_vcdplayer->pxd.pld == NULL) return; p_vcdplayer->pdi = -1; vcdplayer_inc_play_item(p_access); break; } case PSD_TYPE_END_LIST: case PSD_TYPE_COMMAND_LIST: default: ; } } }
/*! 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 %M : MRL %N : The current number of the %I - 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 % */ static char * VCDFormatStr(vcdplayer_t *p_vcdplayer, const char *format_str, const char *mrl, const vcdinfo_itemid_t *itemid) { #define TEMP_STR_SIZE 256 char temp_str[TEMP_STR_SIZE]; char *tp = temp_str; const char *te = tp+TEMP_STR_SIZE-1; bool saw_control_prefix = false; memset(temp_str, 0, TEMP_STR_SIZE); for (; *format_str && tp<te; ++format_str) { if (!saw_control_prefix && *format_str != '%') { *tp++ = *format_str; saw_control_prefix = false; continue; } switch(*format_str) { case '%': if (saw_control_prefix) { *tp++ = '%'; } saw_control_prefix = !saw_control_prefix; break; case 'A': tp += snprintf(tp,te-tp,"%s", vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcdplayer->vcd), MAX_ALBUM_LEN)); break; case 'c': tp += snprintf(tp,te-tp,"%d",vcdinfo_get_volume_num(p_vcdplayer->vcd)); break; case 'C': tp += snprintf(tp,te-tp,"%d",vcdinfo_get_volume_count(p_vcdplayer->vcd)); break; case 'F': tp += snprintf(tp,te-tp,"%s", vcdinfo_get_format_version_str(p_vcdplayer->vcd)); break; case 'I': { switch (itemid->type) { case VCDINFO_ITEM_TYPE_TRACK: tp += snprintf(tp,te-tp,"%s",_("Track")); break; case VCDINFO_ITEM_TYPE_ENTRY: tp += snprintf(tp,te-tp,"%s",_("Entry")); break; case VCDINFO_ITEM_TYPE_SEGMENT: tp += snprintf(tp,te-tp,"%s",_("Segment")); break; case VCDINFO_ITEM_TYPE_LID: tp += snprintf(tp,te-tp,"%s",_("List ID")); break; case VCDINFO_ITEM_TYPE_SPAREID2: tp += snprintf(tp,te-tp,"%s",_("Navigation")); break; default: /* What to do? */ ; } saw_control_prefix = false; } break; case 'L': if (vcdplayer_pbc_is_on(p_vcdplayer)) tp += snprintf(tp,te-tp,"%s %d",_("List ID"),p_vcdplayer->i_lid); saw_control_prefix = false; break; case 'M': tp += snprintf(tp,te-tp,"%s",mrl); break; case 'N': tp += snprintf(tp,te-tp,"%d",itemid->num); break; case 'p': tp += snprintf(tp,te-tp,"%s",vcdinfo_get_preparer_id(p_vcdplayer->vcd)); break; case 'P': tp += snprintf(tp,te-tp,"%s",vcdinfo_get_publisher_id(p_vcdplayer->vcd)); break; case 'S': if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) { tp += snprintf(tp,te-tp," %s", vcdinfo_video_type2str(p_vcdplayer->vcd, itemid->num)); } saw_control_prefix = false; break; case 'T': tp += snprintf(tp,te-tp,"%d",p_vcdplayer->i_track); break; case 'V': tp += snprintf(tp,te-tp,"%s",vcdinfo_get_volumeset_id(p_vcdplayer->vcd)); break; case 'v': tp += snprintf(tp,te-tp,"%s",vcdinfo_get_volume_id(p_vcdplayer->vcd)); break; default: *tp++ = '%'; if(tp<te) *tp++ = *format_str; saw_control_prefix = false; } } return strdup(temp_str); }
/*! Play item assocated with the "next" selection. Return false if there was some problem. */ bool vcdplayer_play_next( access_t * p_access ) { vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys; vcdinfo_obj_t *p_vcdinfo; vcdinfo_itemid_t itemid; if (!p_vcdplayer) return false; dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), "current: %d" , p_vcdplayer->play_item.num); p_vcdinfo = p_vcdplayer->vcd; itemid = p_vcdplayer->play_item; if (vcdplayer_pbc_is_on(p_vcdplayer)) { vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid); switch (p_vcdplayer->pxd.descriptor_type) { case PSD_TYPE_SELECTION_LIST: case PSD_TYPE_EXT_SELECTION_LIST: if (p_vcdplayer->pxd.psd == NULL) return false; vcdplayer_update_entry(p_access, vcdinf_psd_get_next_offset(p_vcdplayer->pxd.psd), &itemid.num, "next"); itemid.type = VCDINFO_ITEM_TYPE_LID; break; case PSD_TYPE_PLAY_LIST: if (p_vcdplayer->pxd.pld == NULL) return false; vcdplayer_update_entry(p_access, vcdinf_pld_get_next_offset(p_vcdplayer->pxd.pld), &itemid.num, "next"); itemid.type = VCDINFO_ITEM_TYPE_LID; break; case PSD_TYPE_END_LIST: case PSD_TYPE_COMMAND_LIST: LOG_WARN( "There is no PBC 'next' selection here" ); return false; } } else { /* PBC is not on. "Next" selection is play_item.num+1 if possible. */ int max_entry = 0; switch (p_vcdplayer->play_item.type) { case VCDINFO_ITEM_TYPE_ENTRY: case VCDINFO_ITEM_TYPE_SEGMENT: case VCDINFO_ITEM_TYPE_TRACK: switch (p_vcdplayer->play_item.type) { case VCDINFO_ITEM_TYPE_ENTRY: max_entry = p_vcdplayer->i_entries; break; case VCDINFO_ITEM_TYPE_SEGMENT: max_entry = p_vcdplayer->i_segments; break; case VCDINFO_ITEM_TYPE_TRACK: max_entry = p_vcdplayer->i_tracks; break; default: ; /* Handle exceptional cases below */ } if (p_vcdplayer->play_item.num+1 < max_entry) { itemid.num = p_vcdplayer->play_item.num+1; } else { LOG_WARN( "At the end - non-PBC 'next' not possible here" ); return false; } break; case VCDINFO_ITEM_TYPE_LID: /* Should have handled above. */ LOG_WARN( "Internal inconsistency - should not have gotten here." ); return false; default: return false; } } /** ??? p_vcdplayer->update_title(); ***/ vcdplayer_play( p_access, itemid ); return VLC_SUCCESS; }
/*! Play item assocated with the "default" selection. Return false if there was some problem. */ bool vcdplayer_play_default( access_t * p_access ) { vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys; vcdinfo_itemid_t itemid; if (!p_vcdplayer) { dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), "null p_vcdplayer" ); return VLC_EGENERIC; } dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), "current: %d" , p_vcdplayer->play_item.num); itemid.type = p_vcdplayer->play_item.type; if (vcdplayer_pbc_is_on(p_vcdplayer)) { #if defined(LIBVCD_VERSION) lid_t lid=vcdinfo_get_multi_default_lid(p_vcdplayer->vcd, p_vcdplayer->i_lid, p_vcdplayer->i_lsn); if (VCDINFO_INVALID_LID != lid) { itemid.num = lid; itemid.type = VCDINFO_ITEM_TYPE_LID; dbg_print(INPUT_DBG_PBC, "DEFAULT to %d", itemid.num); } else { dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d", p_vcdplayer->i_lid); return VLC_EGENERIC; } #else vcdinfo_lid_get_pxd(p_vcdplayer->vcd, &(p_vcdplayer->pxd), p_vcdplayer->i_lid); switch (p_vcdplayer->pxd.descriptor_type) { case PSD_TYPE_SELECTION_LIST: case PSD_TYPE_EXT_SELECTION_LIST: if (p_vcdplayer->pxd.psd == NULL) return false; vcdplayer_update_entry(p_access, vcdinfo_get_default_offset(p_vcdplayer->vcd, p_vcdplayer->i_lid), &itemid.num, "default"); break; case PSD_TYPE_PLAY_LIST: case PSD_TYPE_END_LIST: case PSD_TYPE_COMMAND_LIST: LOG_WARN( "There is no PBC 'default' selection here" ); return false; } #endif /* LIBVCD_VERSION (< 0.7.21) */ } else { /* PBC is not on. "default" selection beginning of current selection . */ itemid.num = p_vcdplayer->play_item.num; } /** ??? p_vcdplayer->update_title(); ***/ vcdplayer_play( p_access, itemid ); return VLC_SUCCESS; }
/*! Read block into p_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 (access_t * p_access, uint8_t *p_buf) { /* p_access->handle_events (); */ uint8_t wait_time=0; vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys; if ( p_vcdplayer->i_lsn > p_vcdplayer->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", p_vcdplayer->i_lsn, p_vcdplayer->end_lsn); handle_item_continuation: read_status = vcdplayer_pbc_is_on( p_vcdplayer ) ? vcdplayer_pbc_nav( p_access, &wait_time ) : vcdplayer_non_pbc_nav( p_access, &wait_time ); if (READ_STILL_FRAME == read_status) { *p_buf = wait_time; return READ_STILL_FRAME; } 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 *p_img = vcdinfo_get_cd_image(p_vcdplayer->vcd); typedef struct { uint8_t subheader [CDIO_CD_SUBHEADER_SIZE]; uint8_t data [M2F2_SECTOR_SIZE]; uint8_t spare [4]; } vcdsector_t; vcdsector_t vcd_sector; do { if (cdio_read_mode2_sector(p_img, &vcd_sector, p_vcdplayer->i_lsn, true)!=0) { dbg_print(INPUT_DBG_LSN, "read error\n"); p_vcdplayer->i_lsn++; return READ_ERROR; } p_vcdplayer->i_lsn++; if ( p_vcdplayer->i_lsn >= p_vcdplayer->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", p_vcdplayer->i_lsn, p_vcdplayer->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 ( p_vcdplayer->i_lsn >= p_vcdplayer->end_lsn ) /* We've run off of the end of this entry. Do we continue or stop? */ goto handle_item_continuation; memcpy (p_buf, vcd_sector.data, M2F2_SECTOR_SIZE); return READ_BLOCK; } }
/***************************************************************************** * RunIntf: main loop *****************************************************************************/ static void RunIntf( intf_thread_t *p_intf ) { vlc_object_t * p_vout = NULL; mtime_t mtime = 0; mtime_t mlast = 0; thread_vcd_data_t * p_vcd; input_thread_t * p_input; /* What you add to the last input number entry. It accumulates all of the 10_ADD keypresses */ int number_addend = 0; if( InitThread( p_intf ) < 0 ) { msg_Err( p_intf, "can't initialize intf" ); return; } p_input = p_intf->p_sys->p_input; p_vcd = p_intf->p_sys->p_vcd = (thread_vcd_data_t *) p_input->p_access_data; dbg_print( INPUT_DBG_CALL, "intf initialized" ); /* Main loop */ while( !p_intf->b_die ) { vlc_mutex_lock( &p_intf->change_lock ); /* * Have we timed-out in showing a still frame? */ if( p_intf->p_sys->b_still && !p_intf->p_sys->b_inf_still ) { if( p_intf->p_sys->m_still_time > 0 ) { /* Update remaining still time */ dbg_print(INPUT_DBG_STILL, "updating still time"); mtime = mdate(); if( mlast ) { p_intf->p_sys->m_still_time -= mtime - mlast; } mlast = mtime; } else { /* Still time has elasped; set to continue playing. */ dbg_print(INPUT_DBG_STILL, "wait time done - setting play"); var_SetInteger( p_intf->p_sys->p_input, "state", PLAYING_S ); p_intf->p_sys->m_still_time = 0; p_intf->p_sys->b_still = 0; mlast = 0; } } /* * Do we have a keyboard event? */ if( p_vout && p_intf->p_sys->b_key_pressed ) { vlc_value_t val; int i, i_action = -1; struct hotkey *p_hotkeys = p_intf->p_vlc->p_hotkeys; p_intf->p_sys->b_key_pressed = VLC_FALSE; /* Find action triggered by hotkey (if any) */ var_Get( p_intf->p_vlc, "key-pressed", &val ); dbg_print( INPUT_DBG_EVENT, "Key pressed %d", val.i_int ); for( i = 0; p_hotkeys[i].psz_action != NULL; i++ ) { if( p_hotkeys[i].i_key == val.i_int ) { i_action = p_hotkeys[i].i_action; } } if( i_action != -1) { switch (i_action) { case ACTIONID_NAV_LEFT: dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_LEFT - prev (%d)", number_addend ); do { vcdplayer_play_prev( p_input ); } while (number_addend-- > 0); break; case ACTIONID_NAV_RIGHT: dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_RIGHT - next (%d)", number_addend ); do { vcdplayer_play_next( p_input ); } while (number_addend-- > 0); break; case ACTIONID_NAV_UP: dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_UP - return" ); do { vcdplayer_play_return( p_input ); } while (number_addend-- > 0); break; case ACTIONID_NAV_DOWN: dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_DOWN - default" ); vcdplayer_play_default( p_input ); break; case ACTIONID_NAV_ACTIVATE: { vcdinfo_itemid_t itemid; itemid.type=p_vcd->play_item.type; dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_ACTIVATE" ); if ( vcdplayer_pbc_is_on( p_vcd ) && number_addend != 0 ) { lid_t next_num=vcdinfo_selection_get_lid(p_vcd->vcd, p_vcd->cur_lid, number_addend); if (VCDINFO_INVALID_LID != next_num) { itemid.num = next_num; itemid.type = VCDINFO_ITEM_TYPE_LID; VCDPlay( p_input, itemid ); } } else { itemid.num = number_addend; VCDPlay( p_input, itemid ); } break; } } number_addend = 0; /* Any keypress gets rid of still frame waiting. FIXME - should handle just the ones that cause an action. */ if( p_intf->p_sys->b_still ) { dbg_print(INPUT_DBG_STILL, "Playing still after activate"); var_SetInteger( p_intf->p_sys->p_input, "state", PLAYING_S ); p_intf->p_sys->b_still = 0; p_intf->p_sys->b_inf_still = 0; p_intf->p_sys->m_still_time = 0; } } else { unsigned int digit_entered=0; switch (val.i_int) { case '9': digit_entered++; case '8': digit_entered++; case '7': digit_entered++; case '6': digit_entered++; case '5': digit_entered++; case '4': digit_entered++; case '3': digit_entered++; case '2': digit_entered++; case '1': digit_entered++; case '0': { number_addend *= 10; number_addend += digit_entered; dbg_print( INPUT_DBG_EVENT, "Added %d. Number is now: %d\n", digit_entered, number_addend); break; } } } } vlc_mutex_unlock( &p_intf->change_lock ); if( p_vout == NULL ) { p_vout = vlc_object_find( p_intf->p_sys->p_input, VLC_OBJECT_VOUT, FIND_CHILD ); if( p_vout ) { var_AddCallback( p_vout, "key-pressed", KeyEvent, p_intf ); } } /* Wait a bit */ msleep( INTF_IDLE_SLEEP ); } if( p_vout ) { var_DelCallback( p_vout, "key-pressed", KeyEvent, p_intf ); vlc_object_release( p_vout ); } vlc_object_release( p_intf->p_sys->p_input ); }