/***************************************************************************** * info.c: DVD Video Manager library. ***************************************************************************** * Copyright (C) 2002 VideoLAN * $Id: info.c,v 1.7 2003/01/29 22:09:46 sam Exp $ * * Authors: Stéphane Borel * * Adapted from Ogle - A video player * Copyright (C) 2000, 2001 Håkan Hjort * * 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, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include "common.h" #include #include #include #include "dvdplay/dvdplay.h" #include "command.h" #include "vmg.h" #include "tools.h" #include "msg.h" /* * Exported functions */ /***************************************************************************** * dvdplay_title_nr: *****************************************************************************/ extern int dvdplay_title_nr( dvdplay_ptr dvdplay ) { _dvdplay_dbg( dvdplay, "retrieving number of titles" ); if( dvdplay != NULL && dvdplay->p_vmgi != NULL && dvdplay->p_vmgi->tt_srpt != NULL ) { return dvdplay->p_vmgi->tt_srpt->nr_of_srpts; } return -1; } /***************************************************************************** * dvdplay_title_cur: *****************************************************************************/ extern int dvdplay_title_cur( dvdplay_ptr dvdplay ) { _dvdplay_dbg( dvdplay, "retrieving current title" ); if( dvdplay != NULL ) { /* is TTN_REG up to date? */ return dvdplay->TTN_REG; } return -1; } /***************************************************************************** * dvdplay_title_first: *****************************************************************************/ extern int dvdplay_title_first( dvdplay_ptr dvdplay ) { if( dvdplay != NULL && dvdplay->state.p_pgc != NULL && dvdplay->state.p_pgc->nr_of_programs ) { int i_cell; _dvdplay_dbg( dvdplay, "retrieving title first byte" ); /* FIXME: we return PGC start byte that might be different from title */ i_cell = dvdplay->state.p_pgc->program_map[0]; if( i_cell > 0 && i_cell <= dvdplay->state.p_pgc->nr_of_cells ) { return dvdplay->state.p_pgc->cell_playback[i_cell-1].first_sector; } _dvdplay_warn( dvdplay, "cannot find start of title" ); } return -1; } /***************************************************************************** * dvdplay_title_end: *****************************************************************************/ extern int dvdplay_title_end( dvdplay_ptr dvdplay ) { if( dvdplay != NULL && dvdplay->state.p_pgc != NULL ) { int i_cell; _dvdplay_dbg( dvdplay, "retrieving title end byte" ); /* FIXME: we return PGC end byte that might be different from title */ i_cell = dvdplay->state.p_pgc->nr_of_cells; if( i_cell ) { return dvdplay->state.p_pgc->cell_playback[i_cell-1].last_sector; } _dvdplay_warn( dvdplay, "cannot find end of title" ); } return -1; } /***************************************************************************** * dvdplay_title_time: return the time length of the selected title in seconds *****************************************************************************/ static unsigned int convert_bcd( unsigned int i_x ) { int y = 0, z = 1; for( ; i_x ; ) { y += z * ( i_x & 0xf ); i_x = i_x >> 4; z = z * 10; } return y; } extern int dvdplay_title_time( dvdplay_ptr dvdplay ) { if( dvdplay != NULL ) { dvd_time_t * p_time; int i_sec; _dvdplay_dbg( dvdplay, "retrieving title time in seconds" ); /* XXX FIXME: this is only the time of the PGC. A title may be composed * of several PGCs */ // fprintf(stderr, "playback type: %d\n", // dvdplay->p_vmgi->tt_srpt->title[dvdplay->TTN_REG - 1].pb_ty.multi_or_random_pgc_title ); p_time = &dvdplay->state.p_pgc->playback_time; if( p_time != NULL ) { i_sec = convert_bcd( p_time->second ); i_sec += convert_bcd( p_time->minute ) * 60; i_sec += convert_bcd( p_time->hour ) * 3600; return i_sec; } _dvdplay_warn( dvdplay, "time undefined for current title" ); } return -1; } /***************************************************************************** * dvdplay_chapter_nr: *****************************************************************************/ extern int dvdplay_chapter_nr( dvdplay_ptr dvdplay, int i_title ) { _dvdplay_dbg( dvdplay, "retrieving number of chapter for title %d", i_title ); if( i_title > 0 && i_title <= dvdplay_title_nr( dvdplay ) ) { return dvdplay->p_vmgi->tt_srpt->title[i_title-1].nr_of_ptts; } return -1; } /***************************************************************************** * dvdplay_chapter_cur: *****************************************************************************/ extern int dvdplay_chapter_cur( dvdplay_ptr dvdplay ) { if( dvdplay != NULL ) { _dvdplay_dbg( dvdplay, "retrieving current chapter" ); // FIXME return dvdplay->PTTN_REG; // or state.i_pgN ??? return dvdplay->state.i_pgN; } return -1; } /***************************************************************************** * dvdplay_angle_info: return the number of angles and the currently * selected one. *****************************************************************************/ extern int dvdplay_angle_info( dvdplay_ptr dvdplay, int* i_nr, int* i_current ) { *i_nr = 1; *i_current = 1; _dvdplay_dbg( dvdplay, "retrieving angle info" ); if( dvdplay->state.domain == VTS_DOMAIN ) { // TTN_REG does not allways point to the correct title.. title_info_t * p_title; if( dvdplay->TTN_REG > dvdplay->p_vmgi->tt_srpt->nr_of_srpts ) { _dvdplay_warn( dvdplay, "TTN_REG not up to date" ); return -1; } p_title = &dvdplay->p_vmgi->tt_srpt->title[dvdplay->TTN_REG - 1]; if( p_title->title_set_nr != dvdplay->state.i_vtsN || p_title->vts_ttn != dvdplay->VTS_TTN_REG ) { return -1; } *i_nr = p_title->nr_of_angles; *i_current = dvdplay->AGL_REG; if( *i_current > *i_nr ) { _dvdplay_warn( dvdplay, "current angle > angle number" ); *i_current = 1; return 1; } } return 0; } /***************************************************************************** * dvdplay_audio_info: retrieve total number of audio streams and the * currently selected one. *****************************************************************************/ extern int dvdplay_audio_info( dvdplay_ptr dvdplay, int *i_nr, int *i_current ) { _dvdplay_dbg( dvdplay, "retrieving audio info" ); switch( dvdplay->state.domain ) { case VTS_DOMAIN: *i_nr = dvdplay->p_vtsi->vtsi_mat->nr_of_vts_audio_streams; if( *i_current <= 0 ) { *i_current = dvdplay->AST_REG + 1; } else { dvdplay->AST_REG = *i_current - 1; } if( *i_current > *i_nr ) { _dvdplay_warn( dvdplay, "current audio > audio number" ); *i_current = 1; return 1; } break; case VTSM_DOMAIN: *i_nr = dvdplay->p_vtsi->vtsi_mat->nr_of_vtsm_audio_streams; // 1 *i_current = 1; break; case VMGM_DOMAIN: case FP_DOMAIN: *i_nr = dvdplay->p_vmgi->vmgi_mat->nr_of_vmgm_audio_streams; // 1 *i_current = 1; break; } return 0; } /***************************************************************************** * dvdplay_audio_id: return the substream id for 'logical' audio stream audioN. ***************************************************************************** * 0 <= audioN < 8. * The stream number returned is the real stream id (with the audio type) *****************************************************************************/ extern int dvdplay_audio_id( dvdplay_ptr dvdplay, int i_audioN ) { int i_format; int i_streamN = -1; int i_stream_id; _dvdplay_dbg( dvdplay, "retrieving audio id for audio %d", i_audioN ); if( dvdplay->state.domain != VTS_DOMAIN && i_audioN ) { _dvdplay_warn( dvdplay, "audio number is not 0 in menu domain (%d)", i_audioN ); i_audioN = 0; } if( dvdplay->state.p_pgc != NULL && i_audioN < 8 ) { if( dvdplay->state.p_pgc->audio_control[i_audioN] & ( 1<<15 ) ) { /* There is any control info for this logical stream */ i_streamN = ( dvdplay->state.p_pgc->audio_control[i_audioN] >> 8 ) & 0x07; } else { _dvdplay_err( dvdplay, "no control for audio %d", i_audioN ); i_streamN = -1; } } else { _dvdplay_err( dvdplay, "audio >= 8 (%d)", i_audioN ); i_streamN = -1; } if( i_streamN < 0 ) { _dvdplay_err( dvdplay, "invalid audio stream number (%d)", i_streamN ); return -1; } /* We have the stream position among the other streams of the same type * Now, compute the real stream id */ switch( dvdplay->state.domain ) { case FP_DOMAIN: case VMGM_DOMAIN: i_format = dvdplay->p_vmgi->vmgi_mat->vmgm_audio_attr.audio_format; break; case VTSM_DOMAIN: i_format = dvdplay->p_vtsi->vtsi_mat->vtsm_audio_attr.audio_format; break; case VTS_DOMAIN: i_format = dvdplay->p_vtsi->vtsi_mat->vts_audio_attr[i_audioN].audio_format; break; } /* We return the full stream id to keep coherence between MPEG and * private elementary streams */ switch( i_format ) { case 0x00: /* AC3 */ i_stream_id = ( ( i_streamN + 0x80 ) << 8 ) | 0xbd; break; case 0x01: /* unknown */ _dvdplay_err( dvdplay, "unknown audio format" ); i_stream_id = -1; break; case 0x02: case 0x03: /* MPEG audio */ i_stream_id = i_streamN + 0xc0; break; case 0x04: /* LPCM */ i_stream_id = ( ( i_streamN + 0xa0 ) << 8 ) | 0xbd; break; case 0x05: /* SDDS */ _dvdplay_err( dvdplay, "SDDS audio format" " - please tell me how to handle this" ); i_stream_id = -1; break; case 0x06: /* DTS */ i_stream_id = ( ( i_streamN + 0x88 ) << 8 ) | 0xbd; } return i_stream_id; } /***************************************************************************** * dvdplay_audio_attr *****************************************************************************/ extern audio_attr_t * dvdplay_audio_attr( dvdplay_ptr dvdplay, int i_streamN ) { _dvdplay_dbg( dvdplay, "retrieving attributes for audio stream %d", i_streamN ); switch( dvdplay->state.domain ) { case VTS_DOMAIN: if( i_streamN >= dvdplay->p_vtsi->vtsi_mat->nr_of_vts_audio_streams ) { _dvdplay_warn( dvdplay, "audio > audio number (%d)", i_streamN ); i_streamN = 0; } return &dvdplay->p_vtsi->vtsi_mat->vts_audio_attr[i_streamN]; case VTSM_DOMAIN: return &dvdplay->p_vtsi->vtsi_mat->vtsm_audio_attr; case VMGM_DOMAIN: case FP_DOMAIN: return &dvdplay->p_vmgi->vmgi_mat->vmgm_audio_attr; } return NULL; } /***************************************************************************** * dvdplay_subp_info: retrieve total number and current sub-picture stream. *****************************************************************************/ extern int dvdplay_subp_info( dvdplay_ptr dvdplay, int *i_nr, int *i_current ) { _dvdplay_dbg( dvdplay, "retrieving sub picture info" ); switch( dvdplay->state.domain ) { case VTS_DOMAIN: *i_nr = dvdplay->p_vtsi->vtsi_mat->nr_of_vts_subp_streams; if( *i_current < 0 ) { *i_current = dvdplay->SPST_REG & 0x40 ? ( dvdplay->SPST_REG & ~0x40 ) + 1 : 0; } else if( *i_current == 0 ) { dvdplay->SPST_REG &= ~0x40; } else { dvdplay->SPST_REG = *i_current - 1; dvdplay->SPST_REG |= 0x40; } if( *i_current > *i_nr ) { _dvdplay_warn( dvdplay, "current sub picture > sub picture number " "(%d)", *i_current ); *i_current = 1; } break; case VTSM_DOMAIN: *i_nr = dvdplay->p_vtsi->vtsi_mat->nr_of_vtsm_subp_streams; // 1 *i_current = 1; break; case VMGM_DOMAIN: case FP_DOMAIN: *i_nr = dvdplay->p_vmgi->vmgi_mat->nr_of_vmgm_subp_streams; // 1 *i_current = 1; break; } return 0; } /***************************************************************************** * dvdplay_subp_id: return the stream id for 'logical' subpicture stream subpN. ***************************************************************************** * 0 <= subpN < 32. * The stream number returned depends on the format of the video. *****************************************************************************/ extern int dvdplay_subp_id( dvdplay_ptr dvdplay, int i_subpN ) { int i_streamN = -1; int i_source_aspect = _GetVideoAspect( dvdplay ); _dvdplay_dbg( dvdplay, "retrieving audio id for audio %d", i_subpN ); if( dvdplay->state.domain != VTS_DOMAIN && i_subpN ) { _dvdplay_warn( dvdplay, "sub picture number is not 0 in menu domain " "(%d)", i_subpN ); i_subpN = 0; } if( dvdplay->state.p_pgc != NULL && i_subpN < 32 ) { if( dvdplay->state.p_pgc->subp_control[i_subpN] & ( 1<<31 ) ) { /* This logical stream is present */ switch( i_source_aspect ) { case 0: /* 4:3 */ i_streamN = 0x1f & ( dvdplay->state.p_pgc->subp_control[i_subpN] >> 24 ); break; case 3: /* 16:9 */ i_streamN = 0x1f & ( dvdplay->state.p_pgc->subp_control[i_subpN] >> 16 ); break; } } else { _dvdplay_warn( dvdplay, "no control for sub picture %d", i_subpN ); i_streamN = 0; } } else { _dvdplay_warn( dvdplay, "sub picture >= 32 (%d)", i_subpN ); i_streamN = 0; } /* Paranoia.. if no stream select 0 anyway */ if( i_streamN < 0 ) { _dvdplay_err( dvdplay, "invalid sub picture stream (%d)", i_streamN ); return -1; } /* We return full stream id to keep coherence with audio */ return ( ( 0x20 + i_streamN ) << 8 ) | 0xbd; } /***************************************************************************** * dvdplay_subp_attr *****************************************************************************/ extern subp_attr_t * dvdplay_subp_attr( dvdplay_ptr dvdplay, int i_streamN ) { _dvdplay_dbg( dvdplay, "retrieving attributes for sub picture stream %d", i_streamN ); switch( dvdplay->state.domain ) { case VTS_DOMAIN: if( i_streamN >= dvdplay->p_vtsi->vtsi_mat->nr_of_vts_subp_streams ) { _dvdplay_warn( dvdplay, "sub picture > sub picture number (%d)", i_streamN ); i_streamN = 0; } return &dvdplay->p_vtsi->vtsi_mat->vts_subp_attr[i_streamN]; case VTSM_DOMAIN: return &dvdplay->p_vtsi->vtsi_mat->vtsm_subp_attr; case VMGM_DOMAIN: case FP_DOMAIN: return &dvdplay->p_vmgi->vmgi_mat->vmgm_subp_attr; } _dvdplay_err( dvdplay, "unknown domain (%d)", dvdplay->state.domain ); return NULL; } /***************************************************************************** * dvdplay_subp_palette: send the subpicutre palette *****************************************************************************/ extern uint32_t * dvdplay_subp_palette( dvdplay_ptr dvdplay ) { if( dvdplay->state.p_pgc == NULL ) { _dvdplay_err( dvdplay, "no Program Chain in state" ); return NULL; } return dvdplay->state.p_pgc->palette; } #if 0 int vm_get_subp_active_stream(void) { int subpN = state.SPST_REG & ~0x40; int streamN = vm_get_subp_stream(subpN); /* If no such stream, then select the first one that exists. */ if(streamN == -1) for(subpN = 0; subpN < 32; subpN++) if(state.pgc->subp_control[subpN] & (1<<31)) { streamN = vm_get_subp_stream(subpN); break; } /* We should instead send the on/off status to the spudecoder / mixer */ /* If we are in the title domain see if the spu mixing is on */ if(state.domain == VTS_DOMAIN && !(state.SPST_REG & 0x40)) { return -1; } else { return streamN; } } #endif /***************************************************************************** * dvdplay_video_attr *****************************************************************************/ extern video_attr_t * dvdplay_video_attr( dvdplay_ptr dvdplay ) { switch( dvdplay->state.domain ) { case VTS_DOMAIN: return &dvdplay->p_vtsi->vtsi_mat->vts_video_attr; break; case VTSM_DOMAIN: return &dvdplay->p_vtsi->vtsi_mat->vtsm_video_attr; break; case VMGM_DOMAIN: case FP_DOMAIN: return &dvdplay->p_vmgi->vmgi_mat->vmgm_video_attr; break; } _dvdplay_err( dvdplay, "unknown domain (%d)", dvdplay->state.domain ); return NULL; } #if 0 void vm_get_video_res(int *width, int *height) { video_attr_t attr; attr = vm_get_video_attr(); if(attr.video_format != 0) *height = 576; else *height = 480; switch(attr.picture_size) { case 0: *width = 720; break; case 1: *width = 704; break; case 2: *width = 352; break; case 3: *width = 352; *height /= 2; break; } } #endif #if 0 int set_sprm(unsigned int nr, uint16_t val) { if(nr < 0 || nr > 23) { return 0; } state.registers.SPRM[nr] = val; return 1; } #endif