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