2001-07-22 17:18:56 +03:00
/*
2011-10-30 19:56:57 +03:00
* unbuffered I / O
2002-05-26 01:34:32 +03:00
* Copyright ( c ) 2001 Fabrice Bellard
2001-07-22 17:18:56 +03:00
*
2011-03-18 19:35:10 +02:00
* This file is part of Libav .
2006-10-07 18:30:46 +03:00
*
2011-03-18 19:35:10 +02:00
* Libav is free software ; you can redistribute it and / or
2002-05-26 01:34:32 +03:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 18:30:46 +03:00
* version 2.1 of the License , or ( at your option ) any later version .
2001-07-22 17:18:56 +03:00
*
2011-03-18 19:35:10 +02:00
* Libav is distributed in the hope that it will be useful ,
2001-07-22 17:18:56 +03:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2002-05-26 01:34:32 +03:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
2001-07-22 17:18:56 +03:00
*
2002-05-26 01:34:32 +03:00
* You should have received a copy of the GNU Lesser General Public
2011-03-18 19:35:10 +02:00
* License along with Libav ; if not , write to the Free Software
2006-01-13 00:43:26 +02:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2001-07-22 17:18:56 +03:00
*/
2008-05-09 14:56:36 +03:00
# include "libavutil/avstring.h"
2011-11-05 12:04:04 +03:00
# include "libavutil/dict.h"
2010-09-26 17:25:22 +03:00
# include "libavutil/opt.h"
2012-06-21 22:31:44 +03:00
# include "libavutil/time.h"
2008-10-10 19:59:37 +03:00
# include "os_support.h"
2001-07-22 17:18:56 +03:00
# include "avformat.h"
2010-03-06 00:30:21 +02:00
# if CONFIG_NETWORK
# include "network.h"
# endif
2011-03-31 17:04:59 +03:00
# include "url.h"
2008-03-10 21:03:39 +02:00
/** @name Logging context. */
/*@{*/
static const char * urlcontext_to_name ( void * ptr )
{
URLContext * h = ( URLContext * ) ptr ;
2013-10-27 16:45:01 +03:00
if ( h - > prot )
return h - > prot - > name ;
else
return " NULL " ;
2008-03-10 21:03:39 +02:00
}
2011-11-05 12:04:04 +03:00
static void * urlcontext_child_next ( void * obj , void * prev )
{
URLContext * h = obj ;
if ( ! prev & & h - > priv_data & & h - > prot - > priv_data_class )
return h - > priv_data ;
return NULL ;
}
2012-08-27 16:31:08 +03:00
static const AVOption options [ ] = {
{ " rw_timeout " , " Timeout for IO operations (in microseconds) " , offsetof ( URLContext , rw_timeout ) , AV_OPT_TYPE_INT64 , { . i64 = 0 } , 0 , INT64_MAX , AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM } ,
{ NULL }
} ;
2011-11-07 00:03:45 +03:00
const AVClass ffurl_context_class = {
2013-10-27 16:45:01 +03:00
. class_name = " URLContext " ,
. item_name = urlcontext_to_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
. child_next = urlcontext_child_next ,
2016-02-19 11:59:46 +02:00
. child_class_next = ff_urlcontext_child_class_next ,
2011-04-29 12:30:02 +03:00
} ;
2008-03-10 21:03:39 +02:00
/*@}*/
2001-07-22 17:18:56 +03:00
2016-02-19 11:39:29 +02:00
static int url_alloc_for_protocol ( URLContext * * puc , const URLProtocol * up ,
2013-10-27 16:45:01 +03:00
const char * filename , int flags ,
2016-02-19 19:02:45 +02:00
const AVIOInterruptCB * int_cb ,
const URLProtocol * * protocols )
2001-07-22 17:18:56 +03:00
{
URLContext * uc ;
int err ;
2010-03-06 00:30:21 +02:00
# if CONFIG_NETWORK
2011-12-30 12:43:10 +03:00
if ( up - > flags & URL_PROTOCOL_FLAG_NETWORK & & ! ff_network_init ( ) )
2010-03-06 00:30:21 +02:00
return AVERROR ( EIO ) ;
# endif
2010-01-28 11:11:26 +02:00
uc = av_mallocz ( sizeof ( URLContext ) + strlen ( filename ) + 1 ) ;
2002-07-24 20:50:23 +03:00
if ( ! uc ) {
2007-02-13 20:26:14 +02:00
err = AVERROR ( ENOMEM ) ;
2002-07-24 20:50:23 +03:00
goto fail ;
}
2011-11-07 00:03:45 +03:00
uc - > av_class = & ffurl_context_class ;
2013-10-27 16:45:01 +03:00
uc - > filename = ( char * ) & uc [ 1 ] ;
2003-01-11 06:59:17 +02:00
strcpy ( uc - > filename , filename ) ;
2013-10-27 16:45:01 +03:00
uc - > prot = up ;
uc - > flags = flags ;
uc - > is_streamed = 0 ; /* default = not streamed */
2002-07-24 20:50:23 +03:00
uc - > max_packet_size = 0 ; /* default: stream file */
2016-02-19 19:02:45 +02:00
uc - > protocols = protocols ;
2010-06-22 17:09:08 +03:00
if ( up - > priv_data_size ) {
uc - > priv_data = av_mallocz ( up - > priv_data_size ) ;
2013-10-22 22:22:13 +03:00
if ( ! uc - > priv_data ) {
err = AVERROR ( ENOMEM ) ;
goto fail ;
}
2010-06-22 17:09:08 +03:00
if ( up - > priv_data_class ) {
2013-10-27 16:45:01 +03:00
* ( const AVClass * * ) uc - > priv_data = up - > priv_data_class ;
2010-06-22 17:09:08 +03:00
av_opt_set_defaults ( uc - > priv_data ) ;
}
}
2011-11-06 23:50:44 +03:00
if ( int_cb )
uc - > interrupt_callback = * int_cb ;
2008-05-05 12:17:56 +03:00
2001-07-22 17:18:56 +03:00
* puc = uc ;
return 0 ;
2013-10-27 16:45:01 +03:00
fail :
2002-07-24 20:50:23 +03:00
* puc = NULL ;
2013-10-22 22:22:13 +03:00
if ( uc )
av_freep ( & uc - > priv_data ) ;
av_freep ( & uc ) ;
2010-03-06 00:30:21 +02:00
# if CONFIG_NETWORK
2011-12-30 12:43:10 +03:00
if ( up - > flags & URL_PROTOCOL_FLAG_NETWORK )
ff_network_close ( ) ;
2010-03-06 00:30:21 +02:00
# endif
2002-07-24 20:50:23 +03:00
return err ;
2001-07-22 17:18:56 +03:00
}
2013-10-27 16:45:01 +03:00
int ffurl_connect ( URLContext * uc , AVDictionary * * options )
2010-06-22 17:03:37 +03:00
{
2011-11-05 12:04:04 +03:00
int err =
2013-10-27 16:45:01 +03:00
uc - > prot - > url_open2 ? uc - > prot - > url_open2 ( uc ,
uc - > filename ,
uc - > flags ,
options ) :
2011-11-05 12:04:04 +03:00
uc - > prot - > url_open ( uc , uc - > filename , uc - > flags ) ;
2010-06-22 17:03:37 +03:00
if ( err )
return err ;
uc - > is_connected = 1 ;
2013-10-27 16:45:01 +03:00
/* We must be careful here as ffurl_seek() could be slow,
* for example for http */
if ( ( uc - > flags & AVIO_FLAG_WRITE ) | | ! strcmp ( uc - > prot - > name , " file " ) )
if ( ! uc - > is_streamed & & ffurl_seek ( uc , 0 , SEEK_SET ) < 0 )
uc - > is_streamed = 1 ;
2010-06-22 17:03:37 +03:00
return 0 ;
}
2010-07-18 21:38:23 +03:00
# define URL_SCHEME_CHARS \
" abcdefghijklmnopqrstuvwxyz " \
" ABCDEFGHIJKLMNOPQRSTUVWXYZ " \
" 0123456789+-. "
2011-11-06 23:50:44 +03:00
int ffurl_alloc ( URLContext * * puc , const char * filename , int flags ,
2016-02-19 19:02:45 +02:00
const AVIOInterruptCB * int_cb ,
const URLProtocol * * protocols )
2008-08-20 02:44:23 +03:00
{
2011-02-28 15:39:36 +02:00
char proto_str [ 128 ] , proto_nested [ 128 ] , * ptr ;
2010-07-18 21:38:23 +03:00
size_t proto_len = strspn ( filename , URL_SCHEME_CHARS ) ;
2016-02-19 11:39:29 +02:00
int i ;
2010-07-18 21:38:23 +03:00
if ( filename [ proto_len ] ! = ' : ' | | is_dos_path ( filename ) )
2008-08-20 02:44:23 +03:00
strcpy ( proto_str , " file " ) ;
2010-07-18 21:38:23 +03:00
else
2013-10-27 16:45:01 +03:00
av_strlcpy ( proto_str , filename ,
FFMIN ( proto_len + 1 , sizeof ( proto_str ) ) ) ;
2008-08-20 02:44:23 +03:00
2011-02-28 15:39:36 +02:00
av_strlcpy ( proto_nested , proto_str , sizeof ( proto_nested ) ) ;
if ( ( ptr = strchr ( proto_nested , ' + ' ) ) )
* ptr = ' \0 ' ;
2016-02-19 12:17:22 +02:00
for ( i = 0 ; protocols [ i ] ; i + + ) {
const URLProtocol * up = protocols [ i ] ;
2016-02-19 19:02:45 +02:00
if ( ! strcmp ( proto_str , up - > name ) )
return url_alloc_for_protocol ( puc , up , filename , flags , int_cb ,
protocols ) ;
2011-02-28 15:39:36 +02:00
if ( up - > flags & URL_PROTOCOL_FLAG_NESTED_SCHEME & &
2016-02-19 19:02:45 +02:00
! strcmp ( proto_nested , up - > name ) )
return url_alloc_for_protocol ( puc , up , filename , flags , int_cb ,
protocols ) ;
2008-08-20 02:44:23 +03:00
}
* puc = NULL ;
2013-10-20 23:01:54 +03:00
return AVERROR_PROTOCOL_NOT_FOUND ;
2008-08-20 02:44:23 +03:00
}
2011-11-06 23:50:44 +03:00
int ffurl_open ( URLContext * * puc , const char * filename , int flags ,
2016-02-19 19:02:45 +02:00
const AVIOInterruptCB * int_cb , AVDictionary * * options ,
2015-02-28 02:00:50 +02:00
const URLProtocol * * protocols ,
URLContext * parent )
2010-06-22 17:03:37 +03:00
{
2016-02-19 19:02:45 +02:00
int ret = ffurl_alloc ( puc , filename , flags , int_cb , protocols ) ;
2010-06-22 17:03:37 +03:00
if ( ret )
return ret ;
2015-02-28 02:00:50 +02:00
if ( parent )
av_opt_copy ( * puc , parent ) ;
2015-02-28 01:17:09 +02:00
if ( options & &
( ret = av_opt_set_dict ( * puc , options ) ) < 0 )
goto fail ;
2011-11-05 12:04:04 +03:00
if ( options & & ( * puc ) - > prot - > priv_data_class & &
( ret = av_opt_set_dict ( ( * puc ) - > priv_data , options ) ) < 0 )
goto fail ;
ret = ffurl_connect ( * puc , options ) ;
2010-06-22 17:03:37 +03:00
if ( ! ret )
return 0 ;
2011-11-05 12:04:04 +03:00
fail :
2011-03-31 18:36:06 +03:00
ffurl_close ( * puc ) ;
2010-06-22 17:03:37 +03:00
* puc = NULL ;
return ret ;
}
2013-10-27 16:45:01 +03:00
static inline int retry_transfer_wrapper ( URLContext * h , uint8_t * buf ,
int size , int size_min ,
int ( * transfer_func ) ( URLContext * h ,
uint8_t * buf ,
int size ) )
2009-06-04 09:25:53 +03:00
{
int ret , len ;
2010-01-24 20:09:46 +02:00
int fast_retries = 5 ;
2012-08-27 16:31:08 +03:00
int64_t wait_since = 0 ;
2009-06-04 09:25:53 +03:00
len = 0 ;
2011-02-04 20:12:37 +02:00
while ( len < size_min ) {
2013-10-27 16:45:01 +03:00
ret = transfer_func ( h , buf + len , size - len ) ;
2011-02-04 20:12:37 +02:00
if ( ret = = AVERROR ( EINTR ) )
continue ;
2011-04-04 21:11:19 +03:00
if ( h - > flags & AVIO_FLAG_NONBLOCK )
2011-02-04 20:12:37 +02:00
return ret ;
2010-01-23 12:23:47 +02:00
if ( ret = = AVERROR ( EAGAIN ) ) {
ret = 0 ;
2012-08-27 16:31:08 +03:00
if ( fast_retries ) {
2010-01-24 20:09:46 +02:00
fast_retries - - ;
2012-08-27 16:31:08 +03:00
} else {
if ( h - > rw_timeout ) {
if ( ! wait_since )
wait_since = av_gettime_relative ( ) ;
else if ( av_gettime_relative ( ) > wait_since + h - > rw_timeout )
return AVERROR ( EIO ) ;
}
2012-06-21 22:31:44 +03:00
av_usleep ( 1000 ) ;
2012-08-27 16:31:08 +03:00
}
2010-01-23 12:23:47 +02:00
} else if ( ret < 1 )
2013-06-24 15:23:44 +03:00
return ( ret < 0 & & ret ! = AVERROR_EOF ) ? ret : len ;
2012-08-27 16:31:08 +03:00
if ( ret ) {
2013-10-27 16:45:01 +03:00
fast_retries = FFMAX ( fast_retries , 2 ) ;
2012-08-27 16:31:08 +03:00
wait_since = 0 ;
}
2009-06-04 09:25:53 +03:00
len + = ret ;
2011-11-06 23:34:24 +03:00
if ( ff_check_interrupt ( & h - > interrupt_callback ) )
2011-03-13 01:42:27 +02:00
return AVERROR_EXIT ;
2009-06-04 09:25:53 +03:00
}
return len ;
}
2011-03-31 17:31:43 +03:00
int ffurl_read ( URLContext * h , unsigned char * buf , int size )
2011-02-04 20:12:37 +02:00
{
2011-04-20 17:09:46 +03:00
if ( ! ( h - > flags & AVIO_FLAG_READ ) )
2011-02-04 20:12:37 +02:00
return AVERROR ( EIO ) ;
return retry_transfer_wrapper ( h , buf , size , 1 , h - > prot - > url_read ) ;
}
2011-03-31 17:40:31 +03:00
int ffurl_read_complete ( URLContext * h , unsigned char * buf , int size )
2010-10-06 14:18:38 +03:00
{
2011-04-20 17:09:46 +03:00
if ( ! ( h - > flags & AVIO_FLAG_READ ) )
2011-02-04 20:12:37 +02:00
return AVERROR ( EIO ) ;
return retry_transfer_wrapper ( h , buf , size , size , h - > prot - > url_read ) ;
2010-10-06 14:18:38 +03:00
}
2011-03-31 17:48:01 +03:00
int ffurl_write ( URLContext * h , const unsigned char * buf , int size )
2001-07-22 17:18:56 +03:00
{
2011-04-15 17:42:09 +03:00
if ( ! ( h - > flags & AVIO_FLAG_WRITE ) )
2007-07-19 18:23:32 +03:00
return AVERROR ( EIO ) ;
2002-07-24 20:50:23 +03:00
/* avoid sending too big packets */
if ( h - > max_packet_size & & size > h - > max_packet_size )
2007-07-19 18:23:32 +03:00
return AVERROR ( EIO ) ;
2010-10-06 14:18:43 +03:00
2015-02-10 17:02:30 +02:00
return retry_transfer_wrapper ( h , buf , size , size ,
( int ( * ) ( struct URLContext * , uint8_t * , int ) )
h - > prot - > url_write ) ;
2001-07-22 17:18:56 +03:00
}
2011-03-31 18:30:31 +03:00
int64_t ffurl_seek ( URLContext * h , int64_t pos , int whence )
2001-07-22 17:18:56 +03:00
{
2008-10-03 13:16:29 +03:00
int64_t ret ;
2001-07-22 17:18:56 +03:00
if ( ! h - > prot - > url_seek )
2010-04-18 20:37:16 +03:00
return AVERROR ( ENOSYS ) ;
2010-03-16 00:54:22 +02:00
ret = h - > prot - > url_seek ( h , pos , whence & ~ AVSEEK_FORCE ) ;
2001-07-22 17:18:56 +03:00
return ret ;
}
2011-03-31 18:36:06 +03:00
int ffurl_close ( URLContext * h )
2001-07-22 17:18:56 +03:00
{
2007-11-16 02:14:48 +02:00
int ret = 0 ;
2013-10-27 16:45:01 +03:00
if ( ! h )
return 0 ; /* can happen when ffurl_open fails */
2001-07-22 17:18:56 +03:00
2010-06-22 17:03:37 +03:00
if ( h - > is_connected & & h - > prot - > url_close )
2007-11-16 02:14:48 +02:00
ret = h - > prot - > url_close ( h ) ;
2010-03-06 00:30:21 +02:00
# if CONFIG_NETWORK
2011-12-30 12:43:10 +03:00
if ( h - > prot - > flags & URL_PROTOCOL_FLAG_NETWORK )
ff_network_close ( ) ;
2010-03-06 00:30:21 +02:00
# endif
2011-11-09 01:48:40 +03:00
if ( h - > prot - > priv_data_size ) {
if ( h - > prot - > priv_data_class )
av_opt_free ( h - > priv_data ) ;
2010-06-22 17:09:08 +03:00
av_free ( h - > priv_data ) ;
2011-11-09 01:48:40 +03:00
}
2002-05-19 02:11:09 +03:00
av_free ( h ) ;
2001-07-22 17:18:56 +03:00
return ret ;
}
2011-04-08 19:32:25 +03:00
int avio_check ( const char * url , int flags )
{
2016-02-19 19:02:45 +02:00
const URLProtocol * * protocols ;
2011-04-08 19:32:25 +03:00
URLContext * h ;
2016-02-19 19:02:45 +02:00
int ret ;
protocols = ffurl_get_protocols ( NULL , NULL ) ;
if ( ! protocols )
return AVERROR ( ENOMEM ) ;
ret = ffurl_alloc ( & h , url , flags , NULL , protocols ) ;
if ( ret ) {
av_freep ( & protocols ) ;
2011-04-08 19:32:25 +03:00
return ret ;
2016-02-19 19:02:45 +02:00
}
2011-04-08 19:32:25 +03:00
if ( h - > prot - > url_check ) {
ret = h - > prot - > url_check ( h , flags ) ;
} else {
2011-11-05 12:04:04 +03:00
ret = ffurl_connect ( h , NULL ) ;
2011-04-08 19:32:25 +03:00
if ( ret > = 0 )
ret = flags ;
}
ffurl_close ( h ) ;
2016-02-19 19:02:45 +02:00
av_freep ( & protocols ) ;
2011-04-08 19:32:25 +03:00
return ret ;
}
2011-03-31 18:46:00 +03:00
int64_t ffurl_size ( URLContext * h )
2001-07-22 17:18:56 +03:00
{
2008-10-03 13:16:29 +03:00
int64_t pos , size ;
2005-12-17 20:14:38 +02:00
2013-10-27 16:45:01 +03:00
size = ffurl_seek ( h , 0 , AVSEEK_SIZE ) ;
if ( size < 0 ) {
2011-03-31 18:30:31 +03:00
pos = ffurl_seek ( h , 0 , SEEK_CUR ) ;
if ( ( size = ffurl_seek ( h , - 1 , SEEK_END ) ) < 0 )
2007-01-30 12:37:52 +02:00
return size ;
size + + ;
2011-03-31 18:30:31 +03:00
ffurl_seek ( h , pos , SEEK_SET ) ;
2007-01-01 23:49:09 +02:00
}
2001-07-22 17:18:56 +03:00
return size ;
}
2002-07-24 20:50:23 +03:00
2011-03-31 18:51:24 +03:00
int ffurl_get_file_handle ( URLContext * h )
2009-03-03 19:04:51 +02:00
{
if ( ! h - > prot - > url_get_file_handle )
return - 1 ;
return h - > prot - > url_get_file_handle ( h ) ;
}
2012-08-17 19:38:59 +03:00
int ffurl_get_multi_file_handle ( URLContext * h , int * * handles , int * numhandles )
{
if ( ! h - > prot - > url_get_multi_file_handle ) {
if ( ! h - > prot - > url_get_file_handle )
return AVERROR ( ENOSYS ) ;
2014-10-18 03:12:12 +03:00
* handles = av_malloc ( sizeof ( * * handles ) ) ;
2012-08-17 19:38:59 +03:00
if ( ! * handles )
return AVERROR ( ENOMEM ) ;
* numhandles = 1 ;
* handles [ 0 ] = h - > prot - > url_get_file_handle ( h ) ;
return 0 ;
}
return h - > prot - > url_get_multi_file_handle ( h , handles , numhandles ) ;
}
2012-05-21 12:24:54 +03:00
int ffurl_shutdown ( URLContext * h , int flags )
{
if ( ! h - > prot - > url_shutdown )
return AVERROR ( EINVAL ) ;
return h - > prot - > url_shutdown ( h , flags ) ;
}
2011-11-06 23:10:21 +03:00
int ff_check_interrupt ( AVIOInterruptCB * cb )
{
int ret ;
if ( cb & & cb - > callback & & ( ret = cb - > callback ( cb - > opaque ) ) )
return ret ;
2011-11-07 13:17:50 +03:00
return 0 ;
2011-11-06 23:10:21 +03:00
}