implement custom ffmpeg-kit protocols for android, fixes #39
parent
de101cf731
commit
c356b059c7
@ -1,139 +0,0 @@ |
||||
/*
|
||||
* Copyright (c) 2020-2021 Taner Sener |
||||
* |
||||
* This file is part of FFmpegKit. |
||||
* |
||||
* FFmpegKit is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Lesser General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* FFmpegKit 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 Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public License |
||||
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include <sys/stat.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
|
||||
#include "config.h" |
||||
#include "libavformat/avformat.h" |
||||
#include "libavutil/avstring.h" |
||||
|
||||
#include "saf_wrapper.h" |
||||
|
||||
/** JNI wrapper in ffmpegkit.c */ |
||||
void closeParcelFileDescriptor(int fd); |
||||
|
||||
// in these wrappers, we call the original functions, so we remove the shadow defines
|
||||
#undef avio_closep |
||||
#undef avformat_close_input |
||||
#undef avio_open |
||||
#undef avio_open2 |
||||
#undef avformat_open_input |
||||
|
||||
static int fd_read_packet(void* opaque, uint8_t* buf, int buf_size) { |
||||
return read(*(int*)opaque, buf, buf_size); |
||||
} |
||||
|
||||
static int fd_write_packet(void* opaque, uint8_t* buf, int buf_size) { |
||||
return write(*(int*)opaque, buf, buf_size); |
||||
} |
||||
|
||||
static int64_t fd_seek(void *opaque, int64_t offset, int whence) { |
||||
int *fd = opaque; |
||||
|
||||
if (*fd < 0) { |
||||
return AVERROR(EINVAL); |
||||
} |
||||
|
||||
int64_t ret; |
||||
if (whence == AVSEEK_SIZE) { |
||||
struct stat st; |
||||
ret = fstat(*fd, &st); |
||||
return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size); |
||||
} |
||||
|
||||
ret = lseek(*fd, offset, whence); |
||||
|
||||
return ret < 0 ? AVERROR(errno) : ret; |
||||
} |
||||
|
||||
/*
|
||||
* returns NULL if the filename is not of expected format (e.g. 'saf:72/video.md4') |
||||
*/ |
||||
static AVIOContext *create_fd_avio_context(const char *filename, int flags) { |
||||
int fd = -1; |
||||
const char* fd_ptr = NULL; |
||||
|
||||
if (av_strstart(filename, "saf:", &fd_ptr)) { |
||||
char *final; |
||||
fd = strtol(fd_ptr, &final, 10); |
||||
if (fd_ptr == final) { /* No digits found */ |
||||
fd = -1; |
||||
} |
||||
} |
||||
|
||||
if (fd >= 0) { |
||||
int *opaque = av_mallocz(sizeof(int)); |
||||
*opaque = fd; |
||||
int write_flag = flags & AVIO_FLAG_WRITE ? 1 : 0; |
||||
return avio_alloc_context(av_malloc(4096), 4096, write_flag, opaque, fd_read_packet, write_flag ? fd_write_packet : NULL, fd_seek); |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
static void close_fd_avio_context(AVIOContext *ctx) { |
||||
if (ctx) { |
||||
int *fd = ctx->opaque; |
||||
if (fd_seek((void*)fd, 0, AVSEEK_SIZE) >= 0) { |
||||
closeParcelFileDescriptor(*fd); |
||||
av_freep(&fd); |
||||
ctx->opaque = NULL; |
||||
} |
||||
} |
||||
} |
||||
|
||||
int android_avformat_open_input(AVFormatContext **ps, const char *filename, |
||||
ff_const59 AVInputFormat *fmt, AVDictionary **options) { |
||||
if (!(*ps) && !(*ps = avformat_alloc_context())) |
||||
return AVERROR(ENOMEM); |
||||
|
||||
(*ps)->pb = create_fd_avio_context(filename, AVIO_FLAG_READ); |
||||
|
||||
return avformat_open_input(ps, filename, fmt, options); |
||||
} |
||||
|
||||
int android_avio_open2(AVIOContext **s, const char *filename, int flags, |
||||
const AVIOInterruptCB *int_cb, AVDictionary **options) { |
||||
AVIOContext *fd_context = create_fd_avio_context(filename, flags); |
||||
|
||||
if (fd_context) { |
||||
*s = fd_context; |
||||
return 0; |
||||
} |
||||
|
||||
return avio_open2(s, filename, flags, int_cb, options); |
||||
} |
||||
|
||||
int android_avio_open(AVIOContext **s, const char *url, int flags) { |
||||
return android_avio_open2(s, url, flags, NULL, NULL); |
||||
} |
||||
|
||||
int android_avio_closep(AVIOContext **s) { |
||||
close_fd_avio_context(*s); |
||||
return avio_closep(s); |
||||
} |
||||
|
||||
void android_avformat_close_input(AVFormatContext **ps) { |
||||
if (*ps && (*ps)->pb) { |
||||
close_fd_avio_context((*ps)->pb); |
||||
} |
||||
avformat_close_input(ps); |
||||
} |
@ -1,46 +0,0 @@ |
||||
/*
|
||||
* Copyright (c) 2020-2021 Taner Sener |
||||
* |
||||
* This file is part of FFmpegKit. |
||||
* |
||||
* FFmpegKit is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Lesser General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* FFmpegKit 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 Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public License |
||||
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#ifndef FFMPEG_KIT_SAF_WRAPPER_H |
||||
#define FFMPEG_KIT_SAF_WRAPPER_H |
||||
|
||||
/*
|
||||
* These wrappers are intended to be used instead of the ffmpeg apis. |
||||
* You don't even need to change the source to call them. |
||||
* Instead, we redefine the public api names so that the wrapper be used. |
||||
*/ |
||||
|
||||
int android_avio_closep(AVIOContext **s); |
||||
#define avio_closep android_avio_closep |
||||
|
||||
void android_avformat_close_input(AVFormatContext **s); |
||||
#define avformat_close_input android_avformat_close_input |
||||
|
||||
int android_avio_open(AVIOContext **s, const char *url, int flags); |
||||
#define avio_open android_avio_open |
||||
|
||||
int android_avio_open2(AVIOContext **s, const char *url, int flags, |
||||
const AVIOInterruptCB *int_cb, AVDictionary **options); |
||||
#define avio_open2 android_avio_open2 |
||||
|
||||
int android_avformat_open_input(AVFormatContext **ps, const char *filename, |
||||
ff_const59 AVInputFormat *fmt, AVDictionary **options); |
||||
#define avformat_open_input android_avformat_open_input |
||||
|
||||
#endif //FFMPEG_KIT_SAF_WRAPPER_H
|
@ -0,0 +1,202 @@ |
||||
/*
|
||||
* Copyright (c) 2021 Taner Sener |
||||
* |
||||
* This file is part of FFmpegKit. |
||||
* |
||||
* FFmpegKit is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Lesser General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* FFmpegKit 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 Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public License |
||||
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "libavutil/file.h" |
||||
|
||||
static int64_t fd_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; |
||||
} |
||||
|
||||
static int fd_open(URLContext *h, const char *filename, int flags) |
||||
{ |
||||
FileContext *c = h->priv_data; |
||||
int fd; |
||||
struct stat st; |
||||
char *final; |
||||
char *saveptr = NULL; |
||||
char *fd_string = NULL; |
||||
char filename_backup[128]; |
||||
|
||||
av_strstart(filename, "fd:", &filename); |
||||
av_strstart(filename, "saf:", &filename); |
||||
av_strlcpy(filename_backup, filename, FFMIN(sizeof(filename), sizeof(filename_backup))); |
||||
fd_string = av_strtok(filename_backup, ".", &saveptr); |
||||
|
||||
fd = strtol(fd_string, &final, 10); |
||||
if ((fd_string == final) || *final ) { |
||||
fd = -1; |
||||
} |
||||
|
||||
c->fd = fd; |
||||
|
||||
h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode); |
||||
|
||||
/* 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; |
||||
|
||||
if (c->seekable >= 0) |
||||
h->is_streamed = !c->seekable; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int fd_check(URLContext *h, int mask) |
||||
{ |
||||
int ret = 0; |
||||
const char *filename = h->filename; |
||||
av_strstart(filename, "fd:", &filename); |
||||
av_strstart(filename, "saf:", &filename); |
||||
|
||||
{ |
||||
#if HAVE_ACCESS && defined(R_OK) |
||||
if (access(filename, F_OK) < 0) |
||||
return AVERROR(errno); |
||||
if (mask&AVIO_FLAG_READ) |
||||
if (access(filename, R_OK) >= 0) |
||||
ret |= AVIO_FLAG_READ; |
||||
if (mask&AVIO_FLAG_WRITE) |
||||
if (access(filename, W_OK) >= 0) |
||||
ret |= AVIO_FLAG_WRITE; |
||||
#else |
||||
struct stat st; |
||||
# ifndef _WIN32 |
||||
ret = stat(filename, &st); |
||||
# else |
||||
ret = win32_stat(filename, &st); |
||||
# endif |
||||
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 |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
static int fd_delete(URLContext *h) |
||||
{ |
||||
#if HAVE_UNISTD_H |
||||
int ret; |
||||
const char *filename = h->filename; |
||||
av_strstart(filename, "fd:", &filename); |
||||
av_strstart(filename, "saf:", &filename); |
||||
|
||||
ret = rmdir(filename); |
||||
if (ret < 0 && (errno == ENOTDIR |
||||
# ifdef _WIN32 |
||||
|| errno == EINVAL |
||||
# endif |
||||
)) |
||||
ret = unlink(filename); |
||||
if (ret < 0) |
||||
return AVERROR(errno); |
||||
|
||||
return ret; |
||||
#else |
||||
return AVERROR(ENOSYS); |
||||
#endif /* HAVE_UNISTD_H */ |
||||
} |
||||
|
||||
static int fd_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, "fd:", &filename_src); |
||||
av_strstart(filename_src, "saf:", &filename_src); |
||||
av_strstart(filename_dst, "fd:", &filename_dst); |
||||
av_strstart(filename_dst, "saf:", &filename_dst); |
||||
|
||||
if (rename(filename_src, filename_dst) < 0) |
||||
return AVERROR(errno); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int fd_close(URLContext *h) |
||||
{ |
||||
FileContext *c = h->priv_data; |
||||
|
||||
fd_close_function custom_fd_close = av_get_fd_close(); |
||||
if (custom_fd_close != NULL) { |
||||
return custom_fd_close(c->fd); |
||||
} else { |
||||
return close(c->fd); |
||||
} |
||||
} |
||||
|
||||
static const AVClass saf_class = { |
||||
.class_name = "saf", |
||||
.item_name = av_default_item_name, |
||||
.option = file_options, |
||||
.version = LIBAVUTIL_VERSION_INT, |
||||
}; |
||||
|
||||
static const AVClass fd_class = { |
||||
.class_name = "fd", |
||||
.item_name = av_default_item_name, |
||||
.option = file_options, |
||||
.version = LIBAVUTIL_VERSION_INT, |
||||
}; |
||||
|
||||
const URLProtocol ff_saf_protocol = { |
||||
.name = "saf", |
||||
.url_open = fd_open, |
||||
.url_read = file_read, |
||||
.url_write = file_write, |
||||
.url_seek = fd_seek, |
||||
.url_close = fd_close, |
||||
.url_get_file_handle = file_get_handle, |
||||
.url_check = fd_check, |
||||
.url_delete = fd_delete, |
||||
.url_move = fd_move, |
||||
.priv_data_size = sizeof(FileContext), |
||||
.priv_data_class = &saf_class, |
||||
.default_whitelist = "saf,crypto,data" |
||||
}; |
||||
|
||||
const URLProtocol ff_fd_protocol = { |
||||
.name = "fd", |
||||
.url_open = fd_open, |
||||
.url_read = file_read, |
||||
.url_write = file_write, |
||||
.url_seek = fd_seek, |
||||
.url_close = fd_close, |
||||
.url_get_file_handle = file_get_handle, |
||||
.url_check = fd_check, |
||||
.url_delete = fd_delete, |
||||
.url_move = fd_move, |
||||
.priv_data_size = sizeof(FileContext), |
||||
.priv_data_class = &fd_class, |
||||
.default_whitelist = "fd,crypto,data" |
||||
}; |
@ -0,0 +1,28 @@ |
||||
/*
|
||||
* Copyright (c) 2021 Taner Sener |
||||
* |
||||
* This file is part of FFmpegKit. |
||||
* |
||||
* FFmpegKit is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Lesser General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* FFmpegKit 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 Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public License |
||||
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
static fd_close_function _fd_close_function = NULL; |
||||
|
||||
fd_close_function av_get_fd_close() { |
||||
return _fd_close_function; |
||||
} |
||||
|
||||
void av_set_fd_close(fd_close_function close_function) { |
||||
_fd_close_function = close_function; |
||||
} |
@ -0,0 +1,29 @@ |
||||
/*
|
||||
* Copyright (c) 2021 Taner Sener |
||||
* |
||||
* This file is part of FFmpegKit. |
||||
* |
||||
* FFmpegKit is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Lesser General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* FFmpegKit 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 Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public License |
||||
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#ifndef AVUTIL_FILE_FFMPEG_KIT_PROTOCOLS_H |
||||
#define AVUTIL_FILE_FFMPEG_KIT_PROTOCOLS_H |
||||
|
||||
typedef int (*fd_close_function)(int); |
||||
|
||||
fd_close_function av_get_fd_close(void); |
||||
|
||||
void av_set_fd_close(fd_close_function); |
||||
|
||||
#endif /* AVUTIL_FILE_FFMPEG_KIT_PROTOCOLS_H */ |
Loading…
Reference in new issue