Skip to content

Commit

Permalink
feat(content): support android content protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
I-m-SuperMan authored and pingkai committed Sep 25, 2020
1 parent e0fb059 commit 92805b1
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 1 deletion.
6 changes: 6 additions & 0 deletions framework/data_source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
147 changes: 147 additions & 0 deletions framework/data_source/ContentDataSource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
//
// Created by SuperMan on 2020/9/21.
//

#include <utils/Android/FindClass.h>
#include <utils/Android/JniEnv.h>
#include <utils/Android/NewStringUTF.h>
#include <utils/Android/JniException.h>
#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<jclass>(pEnv->NewGlobalRef(dataSourceClass));
jContentDataSource_init = pEnv->GetMethodID(jContentDataSourceClass, "<init>", "()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;
}
}
72 changes: 72 additions & 0 deletions framework/data_source/ContentDataSource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// Created by SuperMan on 2020/9/21.
//

#ifndef SOURCE_CONTENTDATASOURCE_H
#define SOURCE_CONTENTDATASOURCE_H

#include <utils/Android/FindClass.h>
#include <utils/Android/JniEnv.h>
#include <utils/Android/NewStringUTF.h>
#include <utils/Android/JniException.h>
#include <utils/af_string.h>
#include <fcntl.h>
#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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

}

}
}
Loading

0 comments on commit 92805b1

Please sign in to comment.