From 92805b13d155b4ece3263dc2481c7ab2df303ab7 Mon Sep 17 00:00:00 2001 From: lifujun <814084764@qq.com> Date: Fri, 25 Sep 2020 10:09:38 +0800 Subject: [PATCH] feat(content): support android content protocol --- framework/data_source/CMakeLists.txt | 6 + framework/data_source/ContentDataSource.cpp | 147 ++++++++++++++++++ framework/data_source/ContentDataSource.h | 72 +++++++++ .../player/nativeclass/NativePlayerBase.java | 7 +- .../player/utils/ContentDataSource.java | 131 ++++++++++++++++ .../premierlibrary/src/main/jni/JniLoader.cpp | 3 + 6 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 framework/data_source/ContentDataSource.cpp create mode 100644 framework/data_source/ContentDataSource.h create mode 100644 platform/Android/source/premierlibrary/src/main/java/com/cicada/player/utils/ContentDataSource.java diff --git a/framework/data_source/CMakeLists.txt b/framework/data_source/CMakeLists.txt index 6e15baa81..f89592c39 100644 --- a/framework/data_source/CMakeLists.txt +++ b/framework/data_source/CMakeLists.txt @@ -58,6 +58,12 @@ if (ENABLE_CACHED_SOURCE) ) endif () +if (ANDROID) + target_sources(data_source PRIVATE + ContentDataSource.cpp + ContentDataSource.h + ) +endif () target_include_directories(data_source PRIVATE ${FFMPEG_SOURCE_DIR} diff --git a/framework/data_source/ContentDataSource.cpp b/framework/data_source/ContentDataSource.cpp new file mode 100644 index 000000000..3f6ca90df --- /dev/null +++ b/framework/data_source/ContentDataSource.cpp @@ -0,0 +1,147 @@ +// +// Created by SuperMan on 2020/9/21. +// + +#include +#include +#include +#include +#include "ContentDataSource.h" + +using namespace Cicada; + +ContentDataSource ContentDataSource::se(0); + +jclass jContentDataSourceClass = nullptr; +jmethodID jContentDataSource_init = nullptr; +jmethodID jContentDataSource_setUri = nullptr; +jmethodID jContentDataSource_open = nullptr; +jmethodID jContentDataSource_close = nullptr; +jmethodID jContentDataSource_read = nullptr; +jmethodID jContentDataSource_seek = nullptr; + +ContentDataSource::ContentDataSource(const std::string &url) : IDataSource(url) { +} + +ContentDataSource::~ContentDataSource() { + +} + +int ContentDataSource::Open(int flags) { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return -EINVAL; + } + + if (jContentDataSourceClass == nullptr) { + return -EINVAL; + } + + jobject jContent = pEnv->NewObject(jContentDataSourceClass, jContentDataSource_init); + mJContentDataSource = pEnv->NewGlobalRef(jContent); + pEnv->DeleteLocalRef(jContent); + + NewStringUTF jurl(pEnv, mUri.c_str()); + pEnv->CallVoidMethod(mJContentDataSource, jContentDataSource_setUri, jurl.getString()); + + jint ret = pEnv->CallIntMethod(mJContentDataSource, jContentDataSource_open, (jint) flags); + JniException::clearException(pEnv); + return (int) ret; +} + +void ContentDataSource::Close() { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return; + } + + pEnv->CallVoidMethod(mJContentDataSource, jContentDataSource_close); + JniException::clearException(pEnv); +} + +int64_t ContentDataSource::Seek(int64_t offset, int whence) { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return -EINVAL; + } + + jlong ret = pEnv->CallLongMethod(mJContentDataSource, jContentDataSource_seek, (jlong) offset, + (jint) whence); + JniException::clearException(pEnv); + return (int64_t) ret; +} + +int ContentDataSource::Read(void *buf, size_t nbyte) { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return -EINVAL; + } + + jbyteArray buffer = pEnv->NewByteArray((jsize) nbyte); + int readSize = (int) pEnv->CallIntMethod(mJContentDataSource, jContentDataSource_read, buffer); + if (readSize <= 0) { + return readSize; + } + jboolean isCopy = false; + jbyte *jBytes = pEnv->GetByteArrayElements(buffer, &isCopy); + memcpy(buf, jBytes, readSize); + + pEnv->ReleaseByteArrayElements(buffer, jBytes, 0); + pEnv->DeleteLocalRef(buffer); + return readSize; +} +void ContentDataSource::Interrupt(bool interrupt) { + IDataSource::Interrupt(interrupt); +} + +std::string ContentDataSource::Get_error_info(int error) { + return IDataSource::Get_error_info(error); +} + +std::string ContentDataSource::GetOption(const std::string &key) { + return IDataSource::GetOption(key); +} + +void ContentDataSource::init() { + + if (jContentDataSourceClass != nullptr) { + return; + } + + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return; + } + + FindClass nativePlayerClass(pEnv, "com/cicada/player/utils/ContentDataSource"); + jclass dataSourceClass = nativePlayerClass.getClass(); + if (dataSourceClass == nullptr) { + return; + } + jContentDataSourceClass = static_cast(pEnv->NewGlobalRef(dataSourceClass)); + jContentDataSource_init = pEnv->GetMethodID(jContentDataSourceClass, "", "()V"); + jContentDataSource_setUri = pEnv->GetMethodID(jContentDataSourceClass, "setUri", + "(Ljava/lang/String;)V"); + jContentDataSource_open = pEnv->GetMethodID(jContentDataSourceClass, "open", "(I)I"); + jContentDataSource_read = pEnv->GetMethodID(jContentDataSourceClass, "read", "([B)I"); + jContentDataSource_seek = pEnv->GetMethodID(jContentDataSourceClass, "seek", "(JI)J"); + jContentDataSource_close = pEnv->GetMethodID(jContentDataSourceClass, "close", "()V"); +} + + +void ContentDataSource::unInit() { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return; + } + if (jContentDataSourceClass != nullptr) { + pEnv->DeleteGlobalRef(jContentDataSourceClass); + jContentDataSourceClass = nullptr; + } +} diff --git a/framework/data_source/ContentDataSource.h b/framework/data_source/ContentDataSource.h new file mode 100644 index 000000000..5bc410e18 --- /dev/null +++ b/framework/data_source/ContentDataSource.h @@ -0,0 +1,72 @@ +// +// Created by SuperMan on 2020/9/21. +// + +#ifndef SOURCE_CONTENTDATASOURCE_H +#define SOURCE_CONTENTDATASOURCE_H + +#include +#include +#include +#include +#include +#include +#include "IDataSource.h" +#include "dataSourcePrototype.h" + +namespace Cicada { + class ContentDataSource : public IDataSource, private dataSourcePrototype { + public: + + static bool probe(const std::string &path) { + return AfString::startWith(path, "content://"); + }; + + explicit ContentDataSource(const std::string &url); + + ~ContentDataSource() override; + + int Open(int flags) override; + + void Close() override; + + int64_t Seek(int64_t offset, int whence) override; + + int Read(void *buf, size_t nbyte) override; + + void Interrupt(bool interrupt) override; + + std::string Get_error_info(int error) override; + + std::string GetOption(const std::string &key) override; + + static void init(); + + static void unInit(); + private: + + private: + + private: + explicit ContentDataSource(int dummy) : IDataSource("") { + addPrototype(this); + } + + Cicada::IDataSource *clone(const std::string &uri) override { + return new ContentDataSource(uri); + }; + + bool is_supported(const std::string &uri) override { + return probe(uri); + }; + + static ContentDataSource se; + + private: + + jobject mJContentDataSource = nullptr; + }; +} + + +#endif //SOURCE_CONTENTDATASOURCE_H diff --git a/platform/Android/source/premierlibrary/src/main/java/com/cicada/player/nativeclass/NativePlayerBase.java b/platform/Android/source/premierlibrary/src/main/java/com/cicada/player/nativeclass/NativePlayerBase.java index 7731fb6c3..0f1b926ab 100644 --- a/platform/Android/source/premierlibrary/src/main/java/com/cicada/player/nativeclass/NativePlayerBase.java +++ b/platform/Android/source/premierlibrary/src/main/java/com/cicada/player/nativeclass/NativePlayerBase.java @@ -85,13 +85,18 @@ private void handleMessage(Message msg) { } - private Context mContext; + + private static Context mContext = null; private long mNativeContext; protected long getNativeContext() { return mNativeContext; } + public static Context getContext() { + return mContext; + } + protected void setNativeContext(long l) { log(TAG, "setNativeContext " + l); mNativeContext = l; diff --git a/platform/Android/source/premierlibrary/src/main/java/com/cicada/player/utils/ContentDataSource.java b/platform/Android/source/premierlibrary/src/main/java/com/cicada/player/utils/ContentDataSource.java new file mode 100644 index 000000000..c4e14fa1e --- /dev/null +++ b/platform/Android/source/premierlibrary/src/main/java/com/cicada/player/utils/ContentDataSource.java @@ -0,0 +1,131 @@ +package com.cicada.player.utils; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.text.TextUtils; + +import com.cicada.player.nativeclass.NativePlayerBase; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +@NativeUsed +public class ContentDataSource { + + private static final int SEEK_SET = 0; /* set file offset to offset */ + private static final int SEEK_CUR = 1; /* set file offset to current plus offset */ + private static final int SEEK_END = 2; /* set file offset to EOF plus offset */ + private static final int SEEK_SIZE = 0x10000; + + private static final int ENOENT = 2; + private static final int EIO = 5; + private static final int EINVAL = 22; + + private String mUri = null; + private InputStream mStream = null; + private int mStreamSize = -1; + private long mOffset = 0; + + public ContentDataSource() { + + } + + public void setUri(String uri) { + mUri = uri; + } + + public int open(int flags) { + + if (TextUtils.isEmpty(mUri)) { + return -EINVAL; + } + Context context = NativePlayerBase.getContext(); + if (context == null) { + return -EINVAL; + } + + ContentResolver contentResolver = context.getContentResolver(); + Uri uri = Uri.parse(mUri); + + try { + mStream = contentResolver.openInputStream(uri); + } catch (FileNotFoundException e) { + return -ENOENT; + } + + if (mStream == null) { + return -EINVAL; + } + + try { + mStreamSize = mStream.available(); + } catch (IOException e) { + return -EIO; + } + return 0; + } + + public int read(byte[] buffer) { + if (mStream == null) { + return -EINVAL; + } + int read = -1; + try { + read = mStream.read(buffer); + mOffset += read; + } catch (IOException e) { + return -EIO; + } + return read; + } + + public void close() { + if (mStream != null) { + try { + mStream.close(); + } catch (IOException e) { + } + } + } + + public long seek(long offset, int whence) { + if (mStream == null) { + return -EINVAL; + } + + if (whence == SEEK_SIZE) { + if (mStreamSize <= 0) { + return -EINVAL; + } else { + return mStreamSize; + } + } else { + long targetOffset = 0; + if (whence == SEEK_END) { + try { + targetOffset = mStream.available(); + } catch (IOException e) { + return -EIO; + } + } else if (whence == SEEK_SET) { + targetOffset = offset - mOffset; + } else if (whence == SEEK_CUR) { + targetOffset = offset; + } else { + return -EINVAL; + } + + try { + long skipBytes = mStream.skip(targetOffset); + mOffset += skipBytes; + return mOffset; + } catch (IOException e) { + return -EIO; + } + + } + + } +} diff --git a/platform/Android/source/premierlibrary/src/main/jni/JniLoader.cpp b/platform/Android/source/premierlibrary/src/main/jni/JniLoader.cpp index 6e170c45c..9de5dae3a 100644 --- a/platform/Android/source/premierlibrary/src/main/jni/JniLoader.cpp +++ b/platform/Android/source/premierlibrary/src/main/jni/JniLoader.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "player/JavaOptions.h" #include "player/JavaExternalPlayer.h" #include "utils/JavaLogger.h" @@ -31,6 +32,7 @@ int initJavaInfo(JNIEnv *env) JavaGlobalSettings::init(env); JavaOptions::init(env); JavaExternalPlayer::init(env); + ContentDataSource::init(); int result = NativeBase::registerMethod(env); if (result == JNI_FALSE) { @@ -71,6 +73,7 @@ void unInitJavaInfo(JNIEnv *env) JavaGlobalSettings::unInit(env); JavaOptions::unInit(env); JavaExternalPlayer::unInit(env); + ContentDataSource::unInit(); } extern "C"