implement custom ffmpeg-kit protocols for android, fixes #39
This commit is contained in:
parent
de101cf731
commit
c356b059c7
|
@ -121,6 +121,9 @@ while [ ! $# -eq 0 ]; do
|
|||
|
||||
export API=${API_LEVEL}
|
||||
;;
|
||||
--no-ffmpeg-kit-protocols)
|
||||
export NO_FFMPEG_KIT_PROTOCOLS="1"
|
||||
;;
|
||||
*)
|
||||
print_unknown_option "$1"
|
||||
;;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "config.h"
|
||||
#include "libavcodec/jni.h"
|
||||
#include "libavutil/bprint.h"
|
||||
#include "libavutil/file.h"
|
||||
#include "fftools_ffmpeg.h"
|
||||
#include "ffmpegkit.h"
|
||||
#include "ffprobekit.h"
|
||||
|
@ -560,12 +561,13 @@ void *callbackThreadFunction() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Used by saf_wrapper; is expected to be called from a Java thread, therefore we don't need attach/detach
|
||||
* Used by fd and saf protocols; is expected to be called from a Java thread, therefore we don't need attach/detach
|
||||
*/
|
||||
void closeParcelFileDescriptor(int fd) {
|
||||
int close_parcel_file_descriptor(int fd) {
|
||||
JNIEnv *env = NULL;
|
||||
(*globalVm)->GetEnv(globalVm, (void**) &env, JNI_VERSION_1_6);
|
||||
(*env)->CallStaticVoidMethod(env, configClass, closeParcelFileDescriptorMethod, fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -643,6 +645,8 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|||
|
||||
redirectionEnabled = 0;
|
||||
|
||||
av_set_fd_close(close_parcel_file_descriptor);
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,8 +51,6 @@
|
|||
#include "libavformat/avformat.h"
|
||||
#include "libswscale/swscale.h"
|
||||
|
||||
#include "saf_wrapper.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef main /* We don't want SDL to override our main() */
|
||||
#endif
|
||||
|
|
|
@ -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
|
|
@ -40,6 +40,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
@ -831,6 +832,20 @@ public class FFmpegKitConfig {
|
|||
}
|
||||
}
|
||||
|
||||
static String extractExtensionFromSafDisplayName(final String safDisplayName) {
|
||||
String rawExtension = safDisplayName;
|
||||
if (safDisplayName.lastIndexOf(".") >= 0) {
|
||||
rawExtension = safDisplayName.substring(safDisplayName.lastIndexOf("."));
|
||||
}
|
||||
try {
|
||||
// workaround for https://issuetracker.google.com/issues/162440528: ANDROID_CREATE_DOCUMENT generating file names like "transcode.mp3 (2)"
|
||||
return new StringTokenizer(rawExtension, " .").nextToken();
|
||||
} catch (final Exception e) {
|
||||
android.util.Log.w(TAG, String.format("Failed to extract extension from saf display name: %s.%s", safDisplayName, Exceptions.getStackTraceString(e)));
|
||||
return "raw";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the given Structured Access Framework Uri (<code>"content:…"</code>) into an
|
||||
* input/output url that can be used in FFmpeg and FFprobe commands.
|
||||
|
@ -863,14 +878,7 @@ public class FFmpegKitConfig {
|
|||
android.util.Log.e(TAG, String.format("Failed to obtain %s parcelFileDescriptor for %s.%s", openMode, uri.toString(), Exceptions.getStackTraceString(t)));
|
||||
}
|
||||
|
||||
// workaround for https://issuetracker.google.com/issues/162440528: ANDROID_CREATE_DOCUMENT generating file names like "transcode.mp3 (2)"
|
||||
if (displayName.lastIndexOf('.') > 0 && displayName.lastIndexOf(' ') > displayName.lastIndexOf('.')) {
|
||||
String extension = displayName.substring(displayName.lastIndexOf('.'), displayName.lastIndexOf(' '));
|
||||
displayName += extension;
|
||||
}
|
||||
// spaces can break argument list parsing, see https://github.com/alexcohn/mobile-ffmpeg/pull/1#issuecomment-688643836
|
||||
final char NBSP = (char) 0xa0;
|
||||
return "saf:" + fd + "/" + displayName.replace(' ', NBSP);
|
||||
return "saf:" + fd + "." + FFmpegKitConfig.extractExtensionFromSafDisplayName(displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,6 +32,10 @@ import java.util.List;
|
|||
*/
|
||||
public class FFmpegKitConfigTest {
|
||||
|
||||
static {
|
||||
System.setProperty("enable.ffmpeg.kit.test.mode", "true");
|
||||
}
|
||||
|
||||
private static final String externalLibrariesCommandOutput = " configuration:\n" +
|
||||
" --cross-prefix=i686-linux-android-\n" +
|
||||
" --sysroot=/Users/taner/Library/Android/sdk/ndk-bundle/toolchains/ffmpeg-kit-i686/sysroot\n" +
|
||||
|
@ -152,6 +156,21 @@ public class FFmpegKitConfigTest {
|
|||
Assert.assertEquals("https-gpl", listToPackageName(Arrays.asList("gnutls", "xvidcore")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractExtensionFromSafDisplayName() {
|
||||
String extension = FFmpegKitConfig.extractExtensionFromSafDisplayName("video.mp4 (2)");
|
||||
Assert.assertEquals("mp4", extension);
|
||||
|
||||
extension = FFmpegKitConfig.extractExtensionFromSafDisplayName("video file name.mp3 (2)");
|
||||
Assert.assertEquals("mp3", extension);
|
||||
|
||||
extension = FFmpegKitConfig.extractExtensionFromSafDisplayName("file.mp4");
|
||||
Assert.assertEquals("mp4", extension);
|
||||
|
||||
extension = FFmpegKitConfig.extractExtensionFromSafDisplayName("file name.mp4");
|
||||
Assert.assertEquals("mp4", extension);
|
||||
}
|
||||
|
||||
private String listToPackageName(final List<String> externalLibraryList) {
|
||||
boolean speex = externalLibraryList.contains("speex");
|
||||
boolean fribidi = externalLibraryList.contains("fribidi");
|
||||
|
|
|
@ -63,7 +63,7 @@ include $(BUILD_SHARED_LIBRARY)
|
|||
|
||||
$(call import-module, cpu-features)
|
||||
|
||||
MY_SRC_FILES := ffmpegkit.c ffprobekit.c ffmpegkit_exception.c fftools_cmdutils.c fftools_ffmpeg.c fftools_ffprobe.c fftools_ffmpeg_opt.c fftools_ffmpeg_hw.c fftools_ffmpeg_filter.c saf_wrapper.c
|
||||
MY_SRC_FILES := ffmpegkit.c ffprobekit.c ffmpegkit_exception.c fftools_cmdutils.c fftools_ffmpeg.c fftools_ffprobe.c fftools_ffmpeg_opt.c fftools_ffmpeg_hw.c fftools_ffmpeg_filter.c
|
||||
|
||||
ifeq ($(TARGET_PLATFORM),android-16)
|
||||
MY_SRC_FILES += android_lts_support.c
|
||||
|
|
|
@ -350,6 +350,12 @@ export CFLAGS="${HIGH_PRIORITY_INCLUDES} ${CFLAGS}"
|
|||
ulimit -n 2048 1>>"${BASEDIR}"/build.log 2>&1
|
||||
|
||||
########################### CUSTOMIZATIONS #######################
|
||||
cd "${BASEDIR}" 1>>"${BASEDIR}"/build.log 2>&1 || exit 1
|
||||
git checkout android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.c 1>>"${BASEDIR}"/build.log 2>&1
|
||||
cd "${BASEDIR}"/src/"${LIB_NAME}" 1>>"${BASEDIR}"/build.log 2>&1 || exit 1
|
||||
git checkout libavformat/file.c 1>>"${BASEDIR}"/build.log 2>&1
|
||||
git checkout libavformat/protocols.c 1>>"${BASEDIR}"/build.log 2>&1
|
||||
git checkout libavutil 1>>"${BASEDIR}"/build.log 2>&1
|
||||
|
||||
# 1. Use thread local log levels
|
||||
${SED_INLINE} 's/static int av_log_level/__thread int av_log_level/g' "${BASEDIR}"/src/"${LIB_NAME}"/libavutil/log.c 1>>"${BASEDIR}"/build.log 2>&1 || exit 1
|
||||
|
@ -358,6 +364,19 @@ ${SED_INLINE} 's/static int av_log_level/__thread int av_log_level/g' "${BASEDIR
|
|||
FFMPEG_VERSION="v$(get_user_friendly_ffmpeg_version)"
|
||||
${SED_INLINE} "s/\$version/$FFMPEG_VERSION/g" "${BASEDIR}"/src/"${LIB_NAME}"/ffbuild/version.sh 1>>"${BASEDIR}"/build.log 2>&1 || exit 1
|
||||
|
||||
# 3. Enable ffmpeg-kit protocols
|
||||
if [[ ${NO_FFMPEG_KIT_PROTOCOLS} == "1" ]]; then
|
||||
${SED_INLINE} "s/ av_set_fd_close/\/\/av_set_fd_close/g" "${BASEDIR}"/android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.c 1>>"${BASEDIR}"/build.log 2>&1
|
||||
echo -e "\nINFO: Disabled custom ffmpeg-kit protocols\n" 1>>"${BASEDIR}"/build.log 2>&1
|
||||
else
|
||||
cat ../../tools/protocols/libavformat_file.c >> libavformat/file.c
|
||||
cat ../../tools/protocols/libavutil_file.h >> libavutil/file.h
|
||||
cat ../../tools/protocols/libavutil_file.c >> libavutil/file.c
|
||||
awk '{gsub(/ff_file_protocol;/,"ff_file_protocol;\nextern const URLProtocol ff_saf_protocol;")}1' libavformat/protocols.c > libavformat/protocols.c.tmp
|
||||
awk '{gsub(/ff_file_protocol;/,"ff_file_protocol;\nextern const URLProtocol ff_fd_protocol;")}1' libavformat/protocols.c.tmp > libavformat/protocols.c
|
||||
echo -e "\nINFO: Enabled custom ffmpeg-kit protocols\n" 1>>"${BASEDIR}"/build.log 2>&1
|
||||
fi
|
||||
|
||||
###################################################################
|
||||
|
||||
./configure \
|
||||
|
|
|
@ -423,6 +423,9 @@ if [[ -z ${NO_WORKSPACE_CLEANUP_ffmpeg} ]]; then
|
|||
fi
|
||||
|
||||
########################### CUSTOMIZATIONS #######################
|
||||
git checkout libavformat/file.c 1>>"${BASEDIR}"/build.log 2>&1
|
||||
git checkout libavformat/protocols.c 1>>"${BASEDIR}"/build.log 2>&1
|
||||
git checkout libavutil 1>>"${BASEDIR}"/build.log 2>&1
|
||||
|
||||
# 1. Workaround to prevent adding of -mdynamic-no-pic flag
|
||||
${SED_INLINE} 's/check_cflags -mdynamic-no-pic && add_asflags -mdynamic-no-pic;/check_cflags -mdynamic-no-pic;/g' ./configure 1>>"${BASEDIR}"/build.log 2>&1 || exit 1
|
||||
|
|
|
@ -31,7 +31,7 @@ under the prebuilt folder.\n"
|
|||
echo -e "Usage: ./$COMMAND [OPTION]... [VAR=VALUE]...\n"
|
||||
echo -e "Specify environment variables as VARIABLE=VALUE to override default build options.\n"
|
||||
|
||||
display_help_options " -l, --lts\t\t\tbuild lts packages to support API 16+ devices" " --api-level=api\t\toverride Android api level [${API}]"
|
||||
display_help_options " -l, --lts\t\t\tbuild lts packages to support API 16+ devices" " --api-level=api\t\toverride Android api level" " --no-ffmpeg-kit-protocols\tdisable custom ffmpeg-kit protocols (fd, saf)"
|
||||
display_help_licensing
|
||||
|
||||
echo -e "Architectures:"
|
||||
|
|
|
@ -584,6 +584,9 @@ display_help_options() {
|
|||
if [ -n "$2" ]; then
|
||||
echo -e "$2"
|
||||
fi
|
||||
if [ -n "$3" ]; then
|
||||
echo -e "$3"
|
||||
fi
|
||||
echo -e ""
|
||||
}
|
||||
|
||||
|
|
202
tools/protocols/libavformat_file.c
Normal file
202
tools/protocols/libavformat_file.c
Normal file
|
@ -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"
|
||||
};
|
28
tools/protocols/libavutil_file.c
Normal file
28
tools/protocols/libavutil_file.c
Normal file
|
@ -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;
|
||||
}
|
29
tools/protocols/libavutil_file.h
Normal file
29
tools/protocols/libavutil_file.h
Normal file
|
@ -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
Block a user