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; 
}