Skip to content

Commit

Permalink
build: add content uri support to FFmpeg
Browse files Browse the repository at this point in the history
  • Loading branch information
mbouron committed Mar 4, 2024
1 parent e37daa2 commit d527cef
Show file tree
Hide file tree
Showing 7 changed files with 1,016 additions and 0 deletions.
9 changes: 9 additions & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ def _gen_android_cross_file(cfg):
url="https://ffmpeg.org/releases/ffmpeg-@[email protected]",
dst_file="ffmpeg-@[email protected]",
sha256="8684f4b00f94b85461884c3719382f1261f0d9eb3d59640a1f4ac0873616f968",
patches=[
"0001-avcodec-add-av_jni_-get-set-_android_app_ctx-helper.patch",
"0002-avformat-add-Android-content-resolver-protocol-suppo.patch",
"0003-avcodec-jni-use-size_t-to-store-structure-offsets.patch",
"0004-avcodec-jni-remove-unnecessary-NULL-checks-before-ca.patch",
"0005-avcodec-mediacodec_wrapper-use-an-OFFSET-macro-where.patch",
"0006-avcodec-mediacodec_wrapper-remove-unnecessary-NULL-c.patch",
],
),
ffmpeg_Windows=dict(
version="6.1.1",
Expand Down Expand Up @@ -523,6 +531,7 @@ def _ffmpeg_setup(cfg):
"http",
"https",
"pipe",
"android_content",
]
filters = [
"aformat",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
From 15533f90bc98b4cebd84b4ead2d0dd322b4f8ea7 Mon Sep 17 00:00:00 2001
From: Matthieu Bouron <[email protected]>
Date: Mon, 12 Feb 2024 23:13:09 +0100
Subject: [PATCH 1/6] avcodec: add av_jni_{get,set}_android_app_ctx helper

This will allow users to pass the Android ApplicationContext which is mandatory
to retrieve the ContentResolver responsible to resolve/open Android content-uri.
---
libavcodec/jni.c | 40 ++++++++++++++++++++++++++++++++++++++++
libavcodec/jni.h | 17 +++++++++++++++++
2 files changed, 57 insertions(+)

diff --git a/libavcodec/jni.c b/libavcodec/jni.c
index ae6490de9d..7d04d0a268 100644
--- a/libavcodec/jni.c
+++ b/libavcodec/jni.c
@@ -64,6 +64,36 @@ void *av_jni_get_java_vm(void *log_ctx)
return vm;
}

+int av_jni_set_android_app_ctx(void *app_ctx, void *log_ctx)
+{
+ JNIEnv *env = avpriv_jni_get_env(c);
+ if (!env)
+ return AVERROR(EINVAL);
+
+ jobjectRefType type = (*env)->GetObjectRefType(env, app_ctx);
+ if (type != JNIGlobalRefType) {
+ av_log(log_ctx, AV_LOG_ERROR, "Application context must be passed as a global reference");
+ return AVERROR(EINVAL);
+ }
+
+ pthread_mutex_lock(&lock);
+ android_app_ctx = app_ctx;
+ pthread_mutex_unlock(&lock);
+
+ return 0;
+}
+
+void *av_jni_get_android_app_ctx(void)
+{
+ void *ctx;
+
+ pthread_mutex_lock(&lock);
+ ctx = android_app_ctx;
+ pthread_mutex_unlock(&lock);
+
+ return ctx;
+}
+
#else

int av_jni_set_java_vm(void *vm, void *log_ctx)
@@ -76,4 +106,14 @@ void *av_jni_get_java_vm(void *log_ctx)
return NULL;
}

+int av_jni_set_android_app_ctx(void *app_ctx, void *log_ctx)
+{
+ return AVERROR(ENOSYS);
+}
+
+void *av_jni_get_android_app_ctx(void)
+{
+ return NULL;
+}
+
#endif
diff --git a/libavcodec/jni.h b/libavcodec/jni.h
index dd99e92611..da8025f830 100644
--- a/libavcodec/jni.h
+++ b/libavcodec/jni.h
@@ -43,4 +43,21 @@ int av_jni_set_java_vm(void *vm, void *log_ctx);
*/
void *av_jni_get_java_vm(void *log_ctx);

+/*
+ * Set the Android application context which will be used to retrieve the Android
+ * content resolver to resolve content uris.
+ *
+ * @param app_ctx global JNI reference to the Android application context
+ * @return 0 on success, < 0 otherwise
+ */
+int av_jni_set_android_app_ctx(void *app_ctx, void *log_ctx);
+
+/*
+ * Get the Android application context that has been set with
+ * av_jni_set_android_app_ctx.
+ *
+ * @return a pointer the the Android application context
+ */
+void *av_jni_get_android_app_ctx(void);
+
#endif /* AVCODEC_JNI_H */
--
2.44.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
From 4cdded89ff4f00726d086021fe0de81ebc3eca73 Mon Sep 17 00:00:00 2001
From: Matthieu Bouron <[email protected]>
Date: Mon, 12 Feb 2024 23:14:35 +0100
Subject: [PATCH 2/6] avformat: add Android content resolver protocol support

Handles Android content-uri starting with content://.
---
configure | 2 +
doc/APIchanges | 3 +
libavcodec/jni.c | 3 +-
libavformat/Makefile | 1 +
libavformat/file.c | 157 ++++++++++++++++++++++++++++++++++++++++
libavformat/protocols.c | 1 +
6 files changed, 166 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index bb5e630bad..790a1df7c8 100755
--- a/configure
+++ b/configure
@@ -3655,6 +3655,8 @@ xcbgrab_indev_suggest="libxcb_shm libxcb_shape libxcb_xfixes"
xv_outdev_deps="xlib_xv xlib_x11 xlib_xext"

# protocols
+android_content_protocol_deps="jni"
+android_content_protocol_select="file_protocol"
async_protocol_deps="threads"
bluray_protocol_deps="libbluray"
ffrtmpcrypt_protocol_conflict="librtmp_protocol"
diff --git a/doc/APIchanges b/doc/APIchanges
index 10f6667e9e..258e953ca6 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2023-02-09

API changes, most recent first:

+2024-02-xx - xxxxxxxxxx - lavc 60.41.100 - jni.h
+ Add av_jni_set_android_app_ctx() and av_jni_get_android_app_ctx().
+
2024-02-26 - xxxxxxxxxx - lavf 60.22.101 - avformat.h
AV_DISPOSITION_DEPENDENT may now also be used for video streams
intended to be merged with other video streams for presentation.
diff --git a/libavcodec/jni.c b/libavcodec/jni.c
index 7d04d0a268..5a75d97f19 100644
--- a/libavcodec/jni.c
+++ b/libavcodec/jni.c
@@ -35,6 +35,7 @@
#include "ffjni.h"

static void *java_vm;
+static void *android_app_ctx;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

int av_jni_set_java_vm(void *vm, void *log_ctx)
@@ -66,7 +67,7 @@ void *av_jni_get_java_vm(void *log_ctx)

int av_jni_set_android_app_ctx(void *app_ctx, void *log_ctx)
{
- JNIEnv *env = avpriv_jni_get_env(c);
+ JNIEnv *env = ff_jni_get_env(log_ctx);
if (!env)
return AVERROR(EINVAL);

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 4a380668bd..08fe98a535 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -657,6 +657,7 @@ OBJS-$(CONFIG_LIBOPENMPT_DEMUXER) += libopenmpt.o
OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER) += vapoursynth.o

# protocols I/O
+OBJS-$(CONFIG_ANDROID_CONTENT_PROTOCOL) += file.o
OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o
OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o
OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o
diff --git a/libavformat/file.c b/libavformat/file.c
index 64df7ff6fb..1b2b69f090 100644
--- a/libavformat/file.c
+++ b/libavformat/file.c
@@ -40,6 +40,12 @@
#include <stdlib.h>
#include "os_support.h"
#include "url.h"
+#if CONFIG_ANDROID_CONTENT_PROTOCOL
+#include <jni.h>
+#include "libavcodec/jni.h"
+#include "libavcodec/ffjni.c"
+#endif
+

/* Some systems may not have S_ISFIFO */
#ifndef S_ISFIFO
@@ -101,6 +107,21 @@ typedef struct FileContext {
int64_t initial_pos;
} FileContext;

+
+#if CONFIG_ANDROID_CONTENT_PROTOCOL
+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,
+};
+#endif
+
static const AVOption file_options[] = {
{ "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
{ "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 },
@@ -524,3 +545,139 @@ const URLProtocol ff_fd_protocol = {
};

#endif /* CONFIG_FD_PROTOCOL */
+
+#if CONFIG_ANDROID_CONTENT_PROTOCOL
+
+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;
+};
+
+#define OFFSET(x) offsetof(struct 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;
+ const char *mode_str = "r";
+
+ JNIEnv *env;
+ struct 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 HAVE_SETMODE
+ setmode(fd, O_BINARY);
+#endif
+ c->fd = fd;
+ h->is_streamed = 0;
+
+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;
+}
+
+URLProtocol ff_android_content_protocol = {
+ .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 */
diff --git a/libavformat/protocols.c b/libavformat/protocols.c
index 360018b17c..93a6d67261 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.c
@@ -24,6 +24,7 @@

#include "url.h"

+extern const URLProtocol ff_android_content_protocol;
extern const URLProtocol ff_async_protocol;
extern const URLProtocol ff_bluray_protocol;
extern const URLProtocol ff_cache_protocol;
--
2.44.0

Loading

0 comments on commit d527cef

Please sign in to comment.