2001-07-22 17:18:56 +03:00
/*
2011-10-30 19:56:57 +03:00
* buffered file I / O
2002-05-26 01:34:32 +03:00
* Copyright ( c ) 2001 Fabrice Bellard
2001-07-22 17:18:56 +03:00
*
2006-10-07 18:30:46 +03:00
* This file is part of FFmpeg .
*
* FFmpeg 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
*
2006-10-07 18:30:46 +03:00
* FFmpeg 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
2006-10-07 18:30:46 +03:00
* License along with FFmpeg ; 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
2022-02-23 14:56:49 +02:00
# include "config_components.h"
2008-05-09 14:56:36 +03:00
# include "libavutil/avstring.h"
2022-08-27 23:33:06 +02:00
# include "libavutil/file_open.h"
2013-08-06 21:19:28 +03:00
# include "libavutil/internal.h"
2024-03-25 02:30:37 +02:00
# include "libavutil/mem.h"
2012-09-09 22:42:56 +03:00
# include "libavutil/opt.h"
2022-08-27 23:33:06 +02:00
# include "avio.h"
2015-06-29 00:13:43 +02:00
# if HAVE_DIRENT_H
2015-06-22 00:01:32 +02:00
# include <dirent.h>
2015-06-29 00:13:43 +02:00
# endif
2001-07-22 17:18:56 +03:00
# include <fcntl.h>
2012-06-25 00:42:27 +03:00
# if HAVE_IO_H
2009-01-12 00:05:43 +02:00
# include <io.h>
# endif
2012-06-25 00:42:27 +03:00
# if HAVE_UNISTD_H
2001-08-14 00:37:10 +03:00
# include <unistd.h>
2012-06-25 00:42:27 +03:00
# endif
2010-04-04 17:21:29 +03:00
# include <sys/stat.h>
2007-08-17 18:14:29 +03:00
# include <stdlib.h>
2007-11-22 04:27:39 +02:00
# include "os_support.h"
2011-04-07 21:25:52 +03:00
# include "url.h"
2001-07-22 17:18:56 +03:00
2012-09-07 16:45:09 +03:00
/* Some systems may not have S_ISFIFO */
# ifndef S_ISFIFO
# ifdef S_IFIFO
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
# else
# define S_ISFIFO(m) 0
# endif
# endif
2001-07-22 17:18:56 +03:00
2015-06-29 00:13:43 +02:00
/* Not available in POSIX.1-1996 */
# ifndef S_ISLNK
# ifdef S_IFLNK
# define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK)
# else
# define S_ISLNK(m) 0
# endif
# endif
/* Not available in POSIX.1-1996 */
# ifndef S_ISSOCK
# ifdef S_IFSOCK
# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
# else
# define S_ISSOCK(m) 0
# endif
# endif
2023-01-11 11:00:35 +02:00
/* S_ISREG not available on Windows */
# ifndef S_ISREG
# ifdef S_IFREG
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
# else
# define S_ISREG(m) 0
# endif
# endif
/* S_ISBLK not available on Windows */
# ifndef S_ISBLK
# ifdef S_IFBLK
# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
# else
# define S_ISBLK(m) 0
# endif
# endif
2001-07-22 17:18:56 +03:00
/* standard file protocol */
2012-09-09 22:35:50 +03:00
typedef struct FileContext {
2012-09-09 22:42:56 +03:00
const AVClass * class ;
2012-09-09 22:35:50 +03:00
int fd ;
2012-09-09 22:42:56 +03:00
int trunc ;
2013-07-31 15:22:01 +03:00
int blocksize ;
2015-02-05 15:06:42 +02:00
int follow ;
2019-04-03 01:13:55 +02:00
int seekable ;
2015-06-29 00:13:43 +02:00
# if HAVE_DIRENT_H
2015-06-22 00:01:32 +02:00
DIR * dir ;
2015-06-29 00:13:43 +02:00
# endif
2012-09-09 22:35:50 +03:00
} FileContext ;
2012-09-09 22:42:56 +03:00
static const AVOption file_options [ ] = {
2015-11-21 23:05:07 +02:00
{ " truncate " , " truncate existing files on write " , offsetof ( FileContext , trunc ) , AV_OPT_TYPE_BOOL , { . i64 = 1 } , 0 , 1 , AV_OPT_FLAG_ENCODING_PARAM } ,
2013-07-31 15:22:01 +03:00
{ " blocksize " , " set I/O operation maximum block size " , offsetof ( FileContext , blocksize ) , AV_OPT_TYPE_INT , { . i64 = INT_MAX } , 1 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
2015-02-05 15:06:42 +02:00
{ " follow " , " Follow a file as it is being written " , offsetof ( FileContext , follow ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , 1 , AV_OPT_FLAG_DECODING_PARAM } ,
2019-04-03 01:13:55 +02:00
{ " seekable " , " Sets if the file is seekable " , offsetof ( FileContext , seekable ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , 0 , AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM } ,
2013-07-31 15:22:01 +03:00
{ NULL }
} ;
static const AVOption pipe_options [ ] = {
{ " blocksize " , " set I/O operation maximum block size " , offsetof ( FileContext , blocksize ) , AV_OPT_TYPE_INT , { . i64 = INT_MAX } , 1 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
2022-12-14 19:10:07 +02:00
{ " fd " , " set file descriptor " , offsetof ( FileContext , fd ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
2012-09-09 22:42:56 +03:00
{ NULL }
} ;
static const AVClass file_class = {
. class_name = " file " ,
2024-01-19 14:33:28 +02:00
. item_name = av_default_item_name ,
2012-09-09 22:42:56 +03:00
. option = file_options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2013-07-31 15:22:01 +03:00
static const AVClass pipe_class = {
. class_name = " pipe " ,
2024-01-19 14:33:28 +02:00
. item_name = av_default_item_name ,
2013-07-31 15:22:01 +03:00
. option = pipe_options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2022-12-14 19:10:10 +02:00
static const AVClass fd_class = {
. class_name = " fd " ,
2024-01-19 14:33:28 +02:00
. item_name = av_default_item_name ,
2022-12-14 19:10:10 +02:00
. option = pipe_options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2010-07-06 17:28:32 +03:00
static int file_read ( URLContext * h , unsigned char * buf , int size )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2015-09-07 16:11:57 +02:00
int ret ;
2013-07-31 15:22:01 +03:00
size = FFMIN ( size , c - > blocksize ) ;
2015-09-07 16:11:57 +02:00
ret = read ( c - > fd , buf , size ) ;
2015-02-05 15:06:42 +02:00
if ( ret = = 0 & & c - > follow )
return AVERROR ( EAGAIN ) ;
2017-06-05 20:30:16 +02:00
if ( ret = = 0 )
return AVERROR_EOF ;
2015-08-27 06:04:15 +02:00
return ( ret = = - 1 ) ? AVERROR ( errno ) : ret ;
2010-07-06 17:28:32 +03:00
}
static int file_write ( URLContext * h , const unsigned char * buf , int size )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2015-09-07 16:11:57 +02:00
int ret ;
2013-07-31 15:22:01 +03:00
size = FFMIN ( size , c - > blocksize ) ;
2015-09-07 16:11:57 +02:00
ret = write ( c - > fd , buf , size ) ;
2015-08-27 06:04:15 +02:00
return ( ret = = - 1 ) ? AVERROR ( errno ) : ret ;
2010-07-06 17:28:32 +03:00
}
static int file_get_handle ( URLContext * h )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
return c - > fd ;
2010-07-06 17:28:32 +03:00
}
2011-05-04 20:20:03 +03:00
static int file_check ( URLContext * h , int mask )
{
2012-11-27 04:57:20 +03:00
int ret = 0 ;
2014-01-02 21:00:15 +03:00
const char * filename = h - > filename ;
av_strstart ( filename , " file: " , & filename ) ;
{
# if HAVE_ACCESS && defined(R_OK)
if ( access ( filename , F_OK ) < 0 )
2011-05-04 20:20:03 +03:00
return AVERROR ( errno ) ;
2012-11-27 04:57:20 +03:00
if ( mask & AVIO_FLAG_READ )
2014-01-02 21:00:15 +03:00
if ( access ( filename , R_OK ) > = 0 )
2012-11-27 04:57:20 +03:00
ret | = AVIO_FLAG_READ ;
if ( mask & AVIO_FLAG_WRITE )
2014-01-02 21:00:15 +03:00
if ( access ( filename , W_OK ) > = 0 )
2012-11-27 04:57:20 +03:00
ret | = AVIO_FLAG_WRITE ;
2012-11-27 06:57:18 +03:00
# else
struct stat st ;
2014-01-02 21:00:15 +03:00
ret = stat ( filename , & st ) ;
2012-11-27 06:57:18 +03:00
if ( ret < 0 )
return AVERROR ( errno ) ;
ret | = st . st_mode & S_IRUSR ? mask & AVIO_FLAG_READ : 0 ;
ret | = st . st_mode & S_IWUSR ? mask & AVIO_FLAG_WRITE : 0 ;
# endif
2014-01-02 21:00:15 +03:00
}
2011-05-04 20:20:03 +03:00
return ret ;
}
2024-06-30 12:33:19 +02:00
# if CONFIG_FD_PROTOCOL || CONFIG_PIPE_PROTOCOL
2022-12-14 19:10:09 +02:00
static int fd_dup ( URLContext * h , int oldfd )
{
int newfd ;
# ifdef F_DUPFD_CLOEXEC
newfd = fcntl ( oldfd , F_DUPFD_CLOEXEC , 0 ) ;
# else
newfd = dup ( oldfd ) ;
# endif
if ( newfd = = - 1 )
return newfd ;
# if HAVE_FCNTL
if ( fcntl ( newfd , F_SETFD , FD_CLOEXEC ) = = - 1 )
av_log ( h , AV_LOG_DEBUG , " Failed to set close on exec \n " ) ;
# endif
# if HAVE_SETMODE
setmode ( newfd , O_BINARY ) ;
# endif
return newfd ;
}
2024-06-30 12:33:19 +02:00
# endif
2022-12-14 19:10:09 +02:00
static int file_close ( URLContext * h )
{
FileContext * c = h - > priv_data ;
2024-05-27 17:19:42 +02:00
int ret = close ( c - > fd ) ;
2022-12-14 19:10:09 +02:00
return ( ret = = - 1 ) ? AVERROR ( errno ) : 0 ;
}
2022-12-14 19:10:10 +02:00
/* XXX: use llseek */
static int64_t file_seek ( URLContext * h , int64_t pos , int whence )
{
FileContext * c = h - > priv_data ;
int64_t ret ;
if ( whence = = AVSEEK_SIZE ) {
struct stat st ;
ret = fstat ( c - > fd , & st ) ;
return ret < 0 ? AVERROR ( errno ) : ( S_ISFIFO ( st . st_mode ) ? 0 : st . st_size ) ;
}
ret = lseek ( c - > fd , pos , whence ) ;
return ret < 0 ? AVERROR ( errno ) : ret ;
}
2021-08-10 18:25:00 +02:00
# if CONFIG_FILE_PROTOCOL
2015-06-20 14:52:49 +02:00
static int file_delete ( URLContext * h )
{
# if HAVE_UNISTD_H
int ret ;
const char * filename = h - > filename ;
av_strstart ( filename , " file: " , & filename ) ;
ret = rmdir ( filename ) ;
2018-12-31 09:37:49 +02:00
if ( ret < 0 & & ( errno = = ENOTDIR
# ifdef _WIN32
| | errno = = EINVAL
# endif
) )
2015-06-20 14:52:49 +02:00
ret = unlink ( filename ) ;
if ( ret < 0 )
return AVERROR ( errno ) ;
return ret ;
# else
return AVERROR ( ENOSYS ) ;
# endif /* HAVE_UNISTD_H */
}
static int file_move ( URLContext * h_src , URLContext * h_dst )
{
const char * filename_src = h_src - > filename ;
const char * filename_dst = h_dst - > filename ;
av_strstart ( filename_src , " file: " , & filename_src ) ;
2015-09-03 12:40:12 +02:00
av_strstart ( filename_dst , " file: " , & filename_dst ) ;
2015-06-20 14:52:49 +02:00
if ( rename ( filename_src , filename_dst ) < 0 )
return AVERROR ( errno ) ;
return 0 ;
}
2001-07-22 17:18:56 +03:00
static int file_open ( URLContext * h , const char * filename , int flags )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2001-07-22 17:18:56 +03:00
int access ;
int fd ;
2012-06-28 20:31:04 +03:00
struct stat st ;
2001-07-22 17:18:56 +03:00
2007-06-24 14:27:12 +03:00
av_strstart ( filename , " file: " , & filename ) ;
2002-11-29 21:19:47 +02:00
2011-04-15 17:42:09 +03:00
if ( flags & AVIO_FLAG_WRITE & & flags & AVIO_FLAG_READ ) {
2012-09-09 22:42:56 +03:00
access = O_CREAT | O_RDWR ;
if ( c - > trunc )
access | = O_TRUNC ;
2011-04-15 17:42:09 +03:00
} else if ( flags & AVIO_FLAG_WRITE ) {
2012-09-09 22:42:56 +03:00
access = O_CREAT | O_WRONLY ;
if ( c - > trunc )
access | = O_TRUNC ;
2001-07-22 17:18:56 +03:00
} else {
access = O_RDONLY ;
}
2007-06-25 23:34:20 +03:00
# ifdef O_BINARY
2001-08-14 00:37:10 +03:00
access | = O_BINARY ;
# endif
2013-08-06 21:19:28 +03:00
fd = avpriv_open ( filename , access , 0666 ) ;
2009-07-27 15:36:22 +03:00
if ( fd = = - 1 )
2010-04-27 01:36:51 +03:00
return AVERROR ( errno ) ;
2012-09-09 22:35:50 +03:00
c - > fd = fd ;
2012-06-28 20:31:04 +03:00
2012-07-01 01:47:49 +03:00
h - > is_streamed = ! fstat ( fd , & st ) & & S_ISFIFO ( st . st_mode ) ;
2012-06-28 20:31:04 +03:00
2017-06-04 04:25:43 +02:00
/* Buffer writes more than the default 32k to improve throughput especially
* with networked file systems */
if ( ! h - > is_streamed & & flags & AVIO_FLAG_WRITE )
h - > min_packet_size = h - > max_packet_size = 262144 ;
2019-04-03 01:13:55 +02:00
if ( c - > seekable > = 0 )
h - > is_streamed = ! c - > seekable ;
2001-07-22 17:18:56 +03:00
return 0 ;
}
2015-06-22 00:01:32 +02:00
static int file_open_dir ( URLContext * h )
{
2015-08-25 12:48:12 +02:00
# if HAVE_LSTAT
2015-06-22 00:01:32 +02:00
FileContext * c = h - > priv_data ;
c - > dir = opendir ( h - > filename ) ;
if ( ! c - > dir )
return AVERROR ( errno ) ;
return 0 ;
2015-06-29 00:13:43 +02:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 12:48:12 +02:00
# endif /* HAVE_LSTAT */
2015-06-22 00:01:32 +02:00
}
static int file_read_dir ( URLContext * h , AVIODirEntry * * next )
{
2015-08-25 12:48:12 +02:00
# if HAVE_LSTAT
2015-06-22 00:01:32 +02:00
FileContext * c = h - > priv_data ;
struct dirent * dir ;
char * fullpath = NULL ;
* next = ff_alloc_dir_entry ( ) ;
if ( ! * next )
return AVERROR ( ENOMEM ) ;
do {
errno = 0 ;
dir = readdir ( c - > dir ) ;
if ( ! dir ) {
av_freep ( next ) ;
return AVERROR ( errno ) ;
}
} while ( ! strcmp ( dir - > d_name , " . " ) | | ! strcmp ( dir - > d_name , " .. " ) ) ;
fullpath = av_append_path_component ( h - > filename , dir - > d_name ) ;
if ( fullpath ) {
struct stat st ;
2015-06-29 00:13:43 +02:00
if ( ! lstat ( fullpath , & st ) ) {
if ( S_ISDIR ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_DIRECTORY ;
else if ( S_ISFIFO ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_NAMED_PIPE ;
else if ( S_ISCHR ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_CHARACTER_DEVICE ;
else if ( S_ISBLK ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_BLOCK_DEVICE ;
else if ( S_ISLNK ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_SYMBOLIC_LINK ;
else if ( S_ISSOCK ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_SOCKET ;
else if ( S_ISREG ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_FILE ;
else
( * next ) - > type = AVIO_ENTRY_UNKNOWN ;
2015-06-22 00:01:32 +02:00
( * next ) - > group_id = st . st_gid ;
( * next ) - > user_id = st . st_uid ;
( * next ) - > size = st . st_size ;
( * next ) - > filemode = st . st_mode & 0777 ;
( * next ) - > modification_timestamp = INT64_C ( 1000000 ) * st . st_mtime ;
( * next ) - > access_timestamp = INT64_C ( 1000000 ) * st . st_atime ;
( * next ) - > status_change_timestamp = INT64_C ( 1000000 ) * st . st_ctime ;
}
av_free ( fullpath ) ;
}
( * next ) - > name = av_strdup ( dir - > d_name ) ;
return 0 ;
2015-06-29 00:13:43 +02:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 12:48:12 +02:00
# endif /* HAVE_LSTAT */
2015-06-22 00:01:32 +02:00
}
static int file_close_dir ( URLContext * h )
{
2015-08-25 12:48:12 +02:00
# if HAVE_LSTAT
2015-06-22 00:01:32 +02:00
FileContext * c = h - > priv_data ;
closedir ( c - > dir ) ;
return 0 ;
2015-06-29 00:13:43 +02:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 12:48:12 +02:00
# endif /* HAVE_LSTAT */
2015-06-22 00:01:32 +02:00
}
2016-02-19 11:39:29 +02:00
const URLProtocol ff_file_protocol = {
2011-04-08 08:41:47 +03:00
. name = " file " ,
. url_open = file_open ,
. url_read = file_read ,
. url_write = file_write ,
. url_seek = file_seek ,
. url_close = file_close ,
2009-03-03 19:04:51 +02:00
. url_get_file_handle = file_get_handle ,
2010-09-30 14:21:42 +03:00
. url_check = file_check ,
2015-06-20 14:52:49 +02:00
. url_delete = file_delete ,
. url_move = file_move ,
2012-09-09 22:35:50 +03:00
. priv_data_size = sizeof ( FileContext ) ,
2012-09-09 22:42:56 +03:00
. priv_data_class = & file_class ,
2015-06-22 00:01:32 +02:00
. url_open_dir = file_open_dir ,
. url_read_dir = file_read_dir ,
. url_close_dir = file_close_dir ,
2019-09-24 05:18:59 +02:00
. default_whitelist = " file,crypto,data "
2001-07-22 17:18:56 +03:00
} ;
2010-07-06 17:28:32 +03:00
# endif /* CONFIG_FILE_PROTOCOL */
# if CONFIG_PIPE_PROTOCOL
2001-07-22 17:18:56 +03:00
static int pipe_open ( URLContext * h , const char * filename , int flags )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2001-07-22 17:18:56 +03:00
int fd ;
2008-05-27 04:19:19 +03:00
char * final ;
2022-12-14 19:10:07 +02:00
if ( c - > fd < 0 ) {
2022-12-14 19:10:08 +02:00
av_strstart ( filename , " pipe: " , & filename ) ;
2024-05-05 21:31:47 +02:00
if ( ! * filename ) {
2022-12-14 19:10:08 +02:00
if ( flags & AVIO_FLAG_WRITE ) {
fd = 1 ;
} else {
fd = 0 ;
}
2024-05-05 21:31:47 +02:00
} else {
fd = strtol ( filename , & final , 10 ) ;
if ( * final ) /* No digits found, or something like 10ab */
return AVERROR ( EINVAL ) ;
2007-08-17 18:12:30 +03:00
}
2022-12-14 19:10:08 +02:00
c - > fd = fd ;
2022-12-14 19:10:07 +02:00
}
2022-12-14 19:10:09 +02:00
c - > fd = fd_dup ( h , c - > fd ) ;
if ( c - > fd = = - 1 )
return AVERROR ( errno ) ;
2004-12-06 02:08:37 +02:00
h - > is_streamed = 1 ;
2001-07-22 17:18:56 +03:00
return 0 ;
}
2016-02-19 11:39:29 +02:00
const URLProtocol ff_pipe_protocol = {
2011-04-08 08:41:47 +03:00
. name = " pipe " ,
. url_open = pipe_open ,
. url_read = file_read ,
. url_write = file_write ,
2022-12-14 19:10:09 +02:00
. url_close = file_close ,
2009-03-03 19:04:51 +02:00
. url_get_file_handle = file_get_handle ,
2010-09-30 14:21:42 +03:00
. url_check = file_check ,
2012-09-09 22:35:50 +03:00
. priv_data_size = sizeof ( FileContext ) ,
2013-07-31 15:22:01 +03:00
. priv_data_class = & pipe_class ,
2019-09-24 05:18:59 +02:00
. default_whitelist = " crypto,data "
2001-07-22 17:18:56 +03:00
} ;
2010-07-06 17:28:32 +03:00
# endif /* CONFIG_PIPE_PROTOCOL */
2022-12-14 19:10:10 +02:00
# if CONFIG_FD_PROTOCOL
static int fd_open ( URLContext * h , const char * filename , int flags )
{
FileContext * c = h - > priv_data ;
struct stat st ;
if ( strcmp ( filename , " fd: " ) ! = 0 ) {
av_log ( h , AV_LOG_ERROR , " Doesn't support pass file descriptor via URL, "
" please set it via -fd {num} \n " ) ;
return AVERROR ( EINVAL ) ;
}
if ( c - > fd < 0 ) {
if ( flags & AVIO_FLAG_WRITE ) {
c - > fd = 1 ;
} else {
c - > fd = 0 ;
}
}
if ( fstat ( c - > fd , & st ) < 0 )
return AVERROR ( errno ) ;
h - > is_streamed = ! ( S_ISREG ( st . st_mode ) | | S_ISBLK ( st . st_mode ) ) ;
c - > fd = fd_dup ( h , c - > fd ) ;
if ( c - > fd = = - 1 )
return AVERROR ( errno ) ;
return 0 ;
}
const URLProtocol ff_fd_protocol = {
. name = " fd " ,
. url_open = fd_open ,
. url_read = file_read ,
. url_write = file_write ,
. url_seek = file_seek ,
. url_close = file_close ,
. url_get_file_handle = file_get_handle ,
. url_check = file_check ,
. priv_data_size = sizeof ( FileContext ) ,
. priv_data_class = & fd_class ,
. default_whitelist = " crypto,data "
} ;
# endif /* CONFIG_FD_PROTOCOL */
2024-02-13 00:14:35 +02:00
# if CONFIG_ANDROID_CONTENT_PROTOCOL
2024-03-23 14:10:40 +02:00
# include <jni.h>
2024-03-27 15:06:19 +02:00
# include "libavcodec/ffjni.h"
2024-03-23 14:10:40 +02:00
# include "libavcodec/jni.h"
2024-02-13 00:14:35 +02:00
typedef struct JFields {
jclass uri_class ;
jmethodID parse_id ;
jclass context_class ;
jmethodID get_content_resolver_id ;
jclass content_resolver_class ;
jmethodID open_file_descriptor_id ;
jclass parcel_file_descriptor_class ;
jmethodID detach_fd_id ;
} JFields ;
# define OFFSET(x) offsetof(JFields, x)
static const struct FFJniField jfields_mapping [ ] = {
{ " android/net/Uri " , NULL , NULL , FF_JNI_CLASS , OFFSET ( uri_class ) , 1 } ,
{ " android/net/Uri " , " parse " , " (Ljava/lang/String;)Landroid/net/Uri; " , FF_JNI_STATIC_METHOD , OFFSET ( parse_id ) , 1 } ,
{ " android/content/Context " , NULL , NULL , FF_JNI_CLASS , OFFSET ( context_class ) , 1 } ,
{ " android/content/Context " , " getContentResolver " , " ()Landroid/content/ContentResolver; " , FF_JNI_METHOD , OFFSET ( get_content_resolver_id ) , 1 } ,
{ " android/content/ContentResolver " , NULL , NULL , FF_JNI_CLASS , OFFSET ( content_resolver_class ) , 1 } ,
{ " android/content/ContentResolver " , " openFileDescriptor " , " (Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor; " , FF_JNI_METHOD , OFFSET ( open_file_descriptor_id ) , 1 } ,
{ " android/os/ParcelFileDescriptor " , NULL , NULL , FF_JNI_CLASS , OFFSET ( parcel_file_descriptor_class ) , 1 } ,
{ " android/os/ParcelFileDescriptor " , " detachFd " , " ()I " , FF_JNI_METHOD , OFFSET ( detach_fd_id ) , 1 } ,
{ NULL }
} ;
# undef OFFSET
static int android_content_open ( URLContext * h , const char * filename , int flags )
{
FileContext * c = h - > priv_data ;
int fd , ret ;
struct stat st ;
const char * mode_str = " r " ;
JNIEnv * env ;
JFields jfields = { 0 } ;
jobject application_context = NULL ;
jobject url = NULL ;
jobject mode = NULL ;
jobject uri = NULL ;
jobject content_resolver = NULL ;
jobject parcel_file_descriptor = NULL ;
env = ff_jni_get_env ( c ) ;
if ( ! env ) {
return AVERROR ( EINVAL ) ;
}
ret = ff_jni_init_jfields ( env , & jfields , jfields_mapping , 0 , c ) ;
if ( ret < 0 ) {
av_log ( c , AV_LOG_ERROR , " failed to initialize jni fields \n " ) ;
return ret ;
}
application_context = av_jni_get_android_app_ctx ( ) ;
if ( ! application_context ) {
av_log ( c , AV_LOG_ERROR , " application context is not set \n " ) ;
ret = AVERROR_EXTERNAL ;
goto done ;
}
url = ff_jni_utf_chars_to_jstring ( env , filename , c ) ;
if ( ! url ) {
ret = AVERROR_EXTERNAL ;
goto done ;
}
if ( flags & AVIO_FLAG_WRITE & & flags & AVIO_FLAG_READ )
mode_str = " rw " ;
else if ( flags & AVIO_FLAG_WRITE )
mode_str = " w " ;
mode = ff_jni_utf_chars_to_jstring ( env , mode_str , c ) ;
if ( ! mode ) {
ret = AVERROR_EXTERNAL ;
goto done ;
}
uri = ( * env ) - > CallStaticObjectMethod ( env , jfields . uri_class , jfields . parse_id , url ) ;
ret = ff_jni_exception_check ( env , 1 , c ) ;
if ( ret < 0 )
goto done ;
content_resolver = ( * env ) - > CallObjectMethod ( env , application_context , jfields . get_content_resolver_id ) ;
ret = ff_jni_exception_check ( env , 1 , c ) ;
if ( ret < 0 )
goto done ;
parcel_file_descriptor = ( * env ) - > CallObjectMethod ( env , content_resolver , jfields . open_file_descriptor_id , uri , mode ) ;
ret = ff_jni_exception_check ( env , 1 , c ) ;
if ( ret < 0 )
goto done ;
fd = ( * env ) - > CallIntMethod ( env , parcel_file_descriptor , jfields . detach_fd_id ) ;
ret = ff_jni_exception_check ( env , 1 , c ) ;
if ( ret < 0 )
goto done ;
if ( fstat ( fd , & st ) < 0 ) {
close ( fd ) ;
return AVERROR ( errno ) ;
}
c - > fd = fd ;
h - > is_streamed = ! ( S_ISREG ( st . st_mode ) | | S_ISBLK ( st . st_mode ) ) ;
done :
( * env ) - > DeleteLocalRef ( env , url ) ;
( * env ) - > DeleteLocalRef ( env , mode ) ;
( * env ) - > DeleteLocalRef ( env , uri ) ;
( * env ) - > DeleteLocalRef ( env , content_resolver ) ;
( * env ) - > DeleteLocalRef ( env , parcel_file_descriptor ) ;
ff_jni_reset_jfields ( env , & jfields , jfields_mapping , 0 , c ) ;
return ret ;
}
2024-03-23 14:10:40 +02:00
static const AVOption android_content_options [ ] = {
{ " blocksize " , " set I/O operation maximum block size " , offsetof ( FileContext , blocksize ) , AV_OPT_TYPE_INT , { . i64 = INT_MAX } , 1 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
{ NULL }
} ;
static const AVClass android_content_class = {
. class_name = " android_content " ,
. item_name = av_default_item_name ,
. option = android_content_options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2024-03-23 14:06:05 +02:00
const URLProtocol ff_android_content_protocol = {
2024-02-13 00:14:35 +02:00
. name = " content " ,
. url_open = android_content_open ,
. url_read = file_read ,
. url_write = file_write ,
. url_seek = file_seek ,
. url_close = file_close ,
. url_get_file_handle = file_get_handle ,
. url_check = NULL ,
. priv_data_size = sizeof ( FileContext ) ,
. priv_data_class = & android_content_class ,
} ;
# endif /* CONFIG_ANDROID_CONTENT_PROTOCOL */