www.pudn.com > kceasy-0.19-rc1-src.rar > giftstate.c
/***************************************************************************** * giftstate.c: file input from giFT state files (file: access plug-in) ***************************************************************************** * Copyright (C) 2001, 2002 VideoLAN * $Id: giftstate.c,v 1.5 2004/08/06 09:47:48 mkern Exp $ * * Authors: Christophe Massiot* Markus Kern * * 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. *****************************************************************************/ /***************************************************************************** * VLC's file access plugin modified for use on giFT state files. * Specifically allowing accesing the file while giFT is writing to it. * Actual chunk recognition is not implemented at this time. * * Theory of operation: * - If windows NT use FILE_SHARE_DELETE so giFT is able to move the file on * completion. Windows will delete the old one automatically when all file * handles are closed. * - If we don't have FILE_SHARE_DELETE open the file normally. giFT will * copy the file on completion and the unlinking will fail. When we are * finished with playback and the state file is gone we delete the media * file. * *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_UNISTD_H # include #elif defined( WIN32 ) && !defined( UNDER_CE ) # include #endif /* stat() support for large files on win32 */ #if defined( WIN32 ) && !defined( UNDER_CE ) #define stat _stati64 #define fstat(a,b) _fstati64(a,b) #endif /***************************************************************************** * Exported prototypes *****************************************************************************/ static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); static int Seek( access_t *, int64_t ); static int Read( access_t *, uint8_t *, int ); static int Control( access_t *, int, va_list ); static int OpenMediaFile( access_t *p_access, char *psz_mediafile ); static char * ReadStateFile( access_t *p_access, char *psz_statefile ); static char * GetDemuxer( access_t *p_access, char *psz_mediafile ); /***************************************************************************** * Module descriptor *****************************************************************************/ vlc_module_begin(); set_description( "giFT state file input" ); set_capability( "access2", 51 ); /* 51 to get invoked before file_access */ add_shortcut( "file" ); add_shortcut( "giftstate" ); set_callbacks( Open, Close ); vlc_module_end(); /***************************************************************************** * _input_socket_t: private access plug-in data, modified to add private * fields *****************************************************************************/ struct access_sys_t { #ifdef WIN32 HANDLE fd; #else int fd; #endif unsigned int i_nb_reads; char *psz_mediafile; int b_manual_delete; vlc_bool_t b_seekable; vlc_bool_t b_pace_control; }; /***************************************************************************** * Open: open the file *****************************************************************************/ static int Open( vlc_object_t *p_this ) { access_t *p_access = (access_t*)p_this; access_sys_t *p_access_data; char *psz_statefile = p_access->psz_path; char *psz_mediafile; char *psz_demuxer; struct stat stat_info; /* get data from state file */ if (!(psz_mediafile = ReadStateFile (p_access, psz_statefile))) { msg_Dbg( p_access, "not a gift state file '%s'", psz_statefile ); return VLC_EGENERIC; } /* stat the incomplete file */ if (stat( psz_mediafile, &stat_info ) == -1) { msg_Warn( p_access, "cannot stat() media file '%s'", psz_mediafile ); free (psz_mediafile); return VLC_EGENERIC; } p_access->pf_read = Read; p_access->pf_seek = Seek; p_access->pf_control = Control; p_access->pf_block = NULL; p_access->info.i_update = 0; p_access->info.i_size = 0; p_access->info.i_pos = 0; p_access->info.b_eof = VLC_FALSE; p_access->info.i_title = 0; p_access->info.i_seekpoint = 0; p_access_data = malloc( sizeof(access_sys_t) ); p_access->p_sys = p_access_data; if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode) || S_ISBLK(stat_info.st_mode) ) { p_access_data->b_seekable = VLC_TRUE; p_access_data->b_pace_control = VLC_TRUE; p_access->info.i_size = stat_info.st_size; } else { msg_Err( p_access, "unknown file type for '%s'", psz_mediafile ); free (psz_mediafile); free (p_access_data); return VLC_EGENERIC; } msg_Dbg( p_access, "incomplete file size: %d", (int)p_access->info.i_size ); msg_Dbg( p_access, "opening incomplete file '%s'", psz_mediafile ); p_access_data->i_nb_reads = 0; p_access_data->psz_mediafile = psz_mediafile; p_access_data->b_manual_delete = 1; /* actually open file */ if( OpenMediaFile( p_access, psz_mediafile ) != VLC_SUCCESS ) { msg_Err( p_access, "cannot open media file '%s'", psz_mediafile ); free (psz_mediafile); free (p_access_data); return VLC_EGENERIC; } if ( p_access_data->b_seekable && p_access->info.i_size == 0) { /* FIXME that's bad because all others access will be probed */ msg_Err( p_access, "media file '%s' is empty, aborting", psz_mediafile ); Close (p_this); return VLC_EGENERIC; } /* force right demuxer if necessary */ if ((psz_demuxer = GetDemuxer (p_access, psz_mediafile))) { msg_Dbg( p_access, "forcing demuxer '%s'", psz_demuxer ); p_access->psz_demux = psz_demuxer; } /* Update default_pts to a suitable value for file access */ var_Create( p_access, "file-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); return VLC_SUCCESS; } /***************************************************************************** * Close: close the target *****************************************************************************/ static void Close( vlc_object_t * p_this ) { access_t *p_access = (access_t*)p_this; access_sys_t *p_access_data = p_access->p_sys; struct stat stat_info; msg_Info( p_access, "closing `%s/%s://%s'", p_access->psz_access, p_access->psz_demux, p_access->psz_path ); #ifdef WIN32 CloseHandle( (HANDLE)p_access_data->fd ); #else close( p_access_data->fd ); #endif /* delete file if necessary */ if (p_access_data->b_manual_delete) { /* check if state file is still there */ if (stat( p_access->psz_path, &stat_info ) == -1) { msg_Info( p_access, "state file gone, removing media file '%s'", p_access_data->psz_mediafile ); if(unlink (p_access_data->psz_mediafile) == -1) { msg_Warn( p_access, "deletion failed '%s'", p_access_data->psz_mediafile ); } } } free( p_access_data->psz_mediafile ); free( p_access_data ); } /***************************************************************************** * Read: standard read on a file descriptor. *****************************************************************************/ static int Read( access_t * p_access, uint8_t * p_buffer, int i_len ) { access_sys_t *p_access_data = p_access->p_sys; ssize_t i_ret; #ifdef WIN32 if( !ReadFile( (HANDLE)p_access_data->fd, p_buffer, i_len, (LPDWORD)&i_ret, NULL ) ) { i_ret = -1; } #else { /* WIN32 */ i_ret = read( p_access_data->fd, p_buffer, i_len ); } #endif // msg_Dbg( p_access, "read %d / %d", (int)i_ret, (int)i_len ); if( i_ret < 0 ) { msg_Err( p_access, "read failed" ); /* Delay a bit to avoid consuming all the CPU. This is particularly * useful when reading from an unconnected FIFO. */ msleep( INPUT_ERROR_SLEEP ); } p_access_data->i_nb_reads++; /* check for file size changes */ if ( p_access->info.i_size != 0 && (p_access_data->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 ) { size_t i_size; #ifdef WIN32 if((i_size = GetFileSize( (HANDLE)p_access_data->fd, NULL )) == 0xFFFFFFFF) { i_size = 0; msg_Warn( p_access, "couldn't get file size again" ); } #else struct stat stat_info; if ( fstat( p_access_data->fd, &stat_info ) == -1 ) { i_size = 0; msg_Warn( p_access, "couldn't stat file again" ); } else { i_size = stat_info.st_size; } #endif /* WIN32 */ if ( i_size > 0 ) { /* msg_Dbg( p_access, "resetting file size to %d", i_size ); */ p_access->info.i_size = i_size; p_access->info.i_update |= INPUT_UPDATE_SIZE; } } if( i_ret > 0 ) p_access->info.i_pos += i_ret; else if( i_ret == 0 ) p_access->info.b_eof = VLC_TRUE; return i_ret; } /***************************************************************************** * Seek: seek to a specific location in a file *****************************************************************************/ static int Seek( access_t * p_access, int64_t i_pos ) { access_sys_t *p_access_data = p_access->p_sys; /* msg_Dbg( p_access, "seeking to %d", (int)i_pos ); */ #ifdef WIN32 SetFilePointer( (HANDLE)p_access_data->fd, i_pos, 0, FILE_BEGIN ); #else lseek( p_access_data->fd, i_pos, SEEK_SET ); #endif p_access->info.i_pos = i_pos; if( p_access->info.i_size < p_access->info.i_pos ) { msg_Err( p_access, "seeking too far" ); p_access->info.i_pos = p_access->info.i_size; } else if( p_access->info.i_pos < 0 ) { msg_Err( p_access, "seeking too early" ); p_access->info.i_pos = 0; } /* Reset eof */ p_access->info.b_eof = VLC_FALSE; return VLC_SUCCESS; } /***************************************************************************** * Control: *****************************************************************************/ static int Control( access_t *p_access, int i_query, va_list args ) { access_sys_t *p_access_data = p_access->p_sys; vlc_bool_t *pb_bool; int *pi_int; int64_t *pi_64; switch( i_query ) { /* */ case ACCESS_CAN_SEEK: case ACCESS_CAN_FASTSEEK: pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); *pb_bool = p_access_data->b_seekable; break; case ACCESS_CAN_PAUSE: case ACCESS_CAN_CONTROL_PACE: pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); *pb_bool = p_access_data->b_pace_control; break; /* */ case ACCESS_GET_MTU: pi_int = (int*)va_arg( args, int * ); *pi_int = 0; break; case ACCESS_GET_PTS_DELAY: pi_64 = (int64_t*)va_arg( args, int64_t * ); *pi_64 = (int64_t)var_GetInteger( p_access, "file-caching" ) * I64C(1000); break; /* */ case ACCESS_SET_PAUSE_STATE: /* Nothing to do */ break; case ACCESS_GET_TITLE_INFO: case ACCESS_SET_TITLE: case ACCESS_SET_SEEKPOINT: case ACCESS_SET_PRIVATE_ID_STATE: return VLC_EGENERIC; default: msg_Err( p_access, "unimplemented query in control" ); return VLC_EGENERIC; } return VLC_SUCCESS; } /***************************************************************************** * OpenMediaFile: Open media file *****************************************************************************/ static int OpenMediaFile( access_t * p_access, char *psz_mediafile ) { access_sys_t *p_access_data = p_access->p_sys; #ifdef WIN32 OSVERSIONINFO osvi; DWORD dw_share_flags; size_t i_size; #endif #ifdef WIN32 dw_share_flags = FILE_SHARE_READ | FILE_SHARE_WRITE; /* check if we have FILE_SHARE_DELETE */ memset (&osvi, 0, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx (&osvi)) { if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) { p_access_data->b_manual_delete = 0; dw_share_flags |= FILE_SHARE_DELETE; } } p_access_data->fd = CreateFile( psz_mediafile, GENERIC_READ, dw_share_flags, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( p_access_data->fd == INVALID_HANDLE_VALUE ) { return VLC_EGENERIC; } /* Set size again since the stat in Open() doesn't always work correctly * when the file is being written to. */ i_size = GetFileSize( (HANDLE)p_access_data->fd, NULL ); if(i_size != 0xFFFFFFFF) { msg_Dbg( p_access, "resetting file size: %d", i_size ); p_access->info.i_size = i_size; p_access->info.i_update |= INPUT_UPDATE_SIZE; } #else p_access_data->fd = open( psz_mediafile, O_NONBLOCK); if ( p_access_data->fd == -1 ) { return VLC_EGENERIC; } #endif return VLC_SUCCESS; } /***************************************************************************** * ReadStateFile: Reads data from giFT state file *****************************************************************************/ static char * ReadStateFile( access_t * p_access, char *psz_statefile ) { char *psz_mediafile; /* check that this is a .state file */ if ( strlen(psz_statefile) < 8 || strcasecmp (psz_statefile + strlen( psz_statefile ) - 6, ".state") != 0 ) { return NULL; } /* get path from state file */ if(!(psz_mediafile = malloc (sizeof (char) * MAX_PATH))) { msg_Err( p_access, "out of memory" ); return NULL; } #ifdef WIN32 if (GetPrivateProfileString( "transfer", "path", "", psz_mediafile, MAX_PATH, psz_statefile) == 0) { msg_Err( p_access, "invalid state file %s", psz_statefile ); free (psz_mediafile); return NULL; } #else # error "state file reading not implemented" #endif return psz_mediafile; } /***************************************************************************** * GetDemuxer: Get the right demuxer from file name *****************************************************************************/ static char * GetDemuxer( access_t * p_access, char *psz_mediafile ) { char *psz_extension; char *psz_demuxer = NULL; if (!(psz_extension = strrchr (psz_mediafile, '.'))) { msg_Dbg( p_access, "no extension in file %s", psz_mediafile ); return NULL; } /* skip '.' */ psz_extension++; if (!strcasecmp (psz_extension, "mp3")) { psz_demuxer = strdup ("mp3"); } else if (!strcasecmp (psz_extension, "aac")) { psz_demuxer = strdup ("aac"); } return psz_demuxer; }