2019-08-08 19:01:45 +08:00
|
|
|
|
#include <jni.h>
|
2019-05-10 13:25:22 +08:00
|
|
|
|
#include <string>
|
2019-09-24 15:21:20 +08:00
|
|
|
|
#include "Util/logger.h"
|
|
|
|
|
|
#include "Thread/semaphore.h"
|
|
|
|
|
|
#include "Common/config.h"
|
|
|
|
|
|
#include "Player/MediaPlayer.h"
|
|
|
|
|
|
#include "Extension/Frame.h"
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
using namespace toolkit;
|
|
|
|
|
|
using namespace mediakit;
|
2019-05-10 13:25:22 +08:00
|
|
|
|
|
|
|
|
|
|
#define JNI_API(retType,funName,...) extern "C" JNIEXPORT retType Java_com_zlmediakit_jni_ZLMediaKit_##funName(JNIEnv* env, jclass cls,##__VA_ARGS__)
|
2019-05-10 16:25:24 +08:00
|
|
|
|
#define MediaPlayerCallBackSign "com/zlmediakit/jni/ZLMediaKit$MediaPlayerCallBack"
|
|
|
|
|
|
#define MediaFrameSign "com/zlmediakit/jni/ZLMediaKit$MediaFrame"
|
2019-05-10 13:25:22 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string stringFromJstring(JNIEnv *env,jstring jstr){
|
|
|
|
|
|
if(!env || !jstr){
|
|
|
|
|
|
WarnL << "invalid args";
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
const char *field_char = env->GetStringUTFChars(jstr, 0);
|
|
|
|
|
|
string ret(field_char,env->GetStringUTFLength(jstr));
|
|
|
|
|
|
env->ReleaseStringUTFChars(jstr, field_char);
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
string stringFromJbytes(JNIEnv *env,jbyteArray jbytes){
|
|
|
|
|
|
if(!env || !jbytes){
|
|
|
|
|
|
WarnL << "invalid args";
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
jbyte *bytes = env->GetByteArrayElements(jbytes, 0);
|
|
|
|
|
|
string ret((char *)bytes,env->GetArrayLength(jbytes));
|
|
|
|
|
|
env->ReleaseByteArrayElements(jbytes,bytes,0);
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
string stringFieldFromJava(JNIEnv *env, jobject jdata,jfieldID jid){
|
|
|
|
|
|
if(!env || !jdata || !jid){
|
|
|
|
|
|
WarnL << "invalid args";
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
jstring field_str = (jstring)env->GetObjectField(jdata,jid);
|
|
|
|
|
|
auto ret = stringFromJstring(env,field_str);
|
|
|
|
|
|
env->DeleteLocalRef(field_str);
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string bytesFieldFromJava(JNIEnv *env, jobject jdata,jfieldID jid){
|
|
|
|
|
|
if(!env || !jdata || !jid){
|
|
|
|
|
|
WarnL << "invalid args";
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
jbyteArray jbufArray = (jbyteArray)env->GetObjectField(jdata, jid);
|
|
|
|
|
|
string ret = stringFromJbytes(env,jbufArray);
|
|
|
|
|
|
env->DeleteLocalRef(jbufArray);
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
jstring jstringFromString(JNIEnv* env, const char* pat) {
|
|
|
|
|
|
return (jstring)env->NewStringUTF(pat);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
jbyteArray jbyteArrayFromString(JNIEnv* env, const char* pat,int len = 0){
|
|
|
|
|
|
if(len <= 0){
|
|
|
|
|
|
len = strlen(pat);
|
|
|
|
|
|
}
|
|
|
|
|
|
jbyteArray jarray = env->NewByteArray(len);
|
|
|
|
|
|
env->SetByteArrayRegion(jarray, 0, len, (jbyte *)(pat));
|
|
|
|
|
|
return jarray;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-10 16:25:24 +08:00
|
|
|
|
jobject makeJavaFrame(JNIEnv* env,const Frame::Ptr &frame){
|
|
|
|
|
|
static jclass jclass_obj = (jclass)env->NewGlobalRef(env->FindClass(MediaFrameSign));
|
|
|
|
|
|
static jmethodID jmethodID_init = env->GetMethodID(jclass_obj, "<init>", "()V");
|
|
|
|
|
|
static jfieldID jfieldID_dts = env->GetFieldID(jclass_obj,"dts","I");
|
|
|
|
|
|
static jfieldID jfieldID_pts = env->GetFieldID(jclass_obj,"pts","I");
|
|
|
|
|
|
static jfieldID jfieldID_prefixSize = env->GetFieldID(jclass_obj,"prefixSize","I");
|
|
|
|
|
|
static jfieldID jfieldID_keyFrame = env->GetFieldID(jclass_obj,"keyFrame","Z");
|
|
|
|
|
|
static jfieldID jfieldID_data = env->GetFieldID(jclass_obj,"data","[B");
|
|
|
|
|
|
static jfieldID jfieldID_trackType = env->GetFieldID(jclass_obj,"trackType","I");
|
|
|
|
|
|
static jfieldID jfieldID_codecId = env->GetFieldID(jclass_obj,"codecId","I");
|
|
|
|
|
|
|
|
|
|
|
|
if(!frame){
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
jobject ret = env->NewObject(jclass_obj, jmethodID_init);
|
|
|
|
|
|
env->SetIntField(ret,jfieldID_dts,frame->dts());
|
|
|
|
|
|
env->SetIntField(ret,jfieldID_pts,frame->pts());
|
|
|
|
|
|
env->SetIntField(ret,jfieldID_prefixSize,frame->prefixSize());
|
|
|
|
|
|
env->SetBooleanField(ret,jfieldID_keyFrame,frame->keyFrame());
|
|
|
|
|
|
env->SetObjectField(ret,jfieldID_data,jbyteArrayFromString(env,frame->data(),frame->size()));
|
|
|
|
|
|
env->SetIntField(ret,jfieldID_trackType,frame->getTrackType());
|
|
|
|
|
|
env->SetIntField(ret,jfieldID_codecId,frame->getCodecId());
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static JavaVM *s_jvm = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
template <typename FUN>
|
|
|
|
|
|
void doInJavaThread(FUN &&fun){
|
|
|
|
|
|
JNIEnv *env;
|
|
|
|
|
|
int status = s_jvm->GetEnv((void **) &env, JNI_VERSION_1_6);
|
|
|
|
|
|
if (status != JNI_OK) {
|
|
|
|
|
|
if (s_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
fun(env);
|
|
|
|
|
|
if (status != JNI_OK) {
|
|
|
|
|
|
//Detach线程
|
|
|
|
|
|
s_jvm->DetachCurrentThread();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define emitEvent(delegate,method,argFmt,...) \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
doInJavaThread([&](JNIEnv* env) { \
|
|
|
|
|
|
static jclass cls = env->GetObjectClass(delegate); \
|
|
|
|
|
|
static jmethodID jmid = env->GetMethodID(cls, method, argFmt); \
|
|
|
|
|
|
jobject localRef = env->NewLocalRef(delegate); \
|
|
|
|
|
|
if(localRef){ \
|
|
|
|
|
|
env->CallVoidMethod(localRef, jmid, ##__VA_ARGS__); \
|
|
|
|
|
|
}else{ \
|
|
|
|
|
|
WarnL << "弱引用已经释放:" << method << " " << argFmt; \
|
|
|
|
|
|
}\
|
|
|
|
|
|
}); \
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-10 13:25:22 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* 加载动态库
|
|
|
|
|
|
*/
|
|
|
|
|
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
|
|
|
|
//设置日志
|
2019-05-10 16:25:24 +08:00
|
|
|
|
s_jvm = vm;
|
2019-05-10 13:25:22 +08:00
|
|
|
|
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
|
|
|
|
|
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
|
|
|
|
|
InfoL;
|
|
|
|
|
|
return JNI_VERSION_1_6;
|
|
|
|
|
|
}
|
2019-09-24 15:21:20 +08:00
|
|
|
|
|
|
|
|
|
|
static pthread_t s_tread_id = 0;
|
2019-05-10 13:25:22 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* 卸载动态库
|
|
|
|
|
|
*/
|
|
|
|
|
|
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved){
|
|
|
|
|
|
InfoL;
|
2019-09-24 15:21:20 +08:00
|
|
|
|
if(s_tread_id){
|
|
|
|
|
|
pthread_kill(s_tread_id,SIGINT);
|
|
|
|
|
|
}
|
2019-05-10 13:25:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-24 15:21:20 +08:00
|
|
|
|
extern int start_main(int argc,char *argv[]);
|
|
|
|
|
|
JNI_API(jboolean,startDemo,jstring ini_dir){
|
|
|
|
|
|
string sd_path = stringFromJstring(env,ini_dir);
|
|
|
|
|
|
string ini_file = sd_path + "/zlmediakit.ini";
|
2019-05-10 13:25:22 +08:00
|
|
|
|
|
2019-09-24 15:21:20 +08:00
|
|
|
|
//可以在sd卡根目录下放置ssl证书以便支持https服务器,证书支持p12或pem格式
|
|
|
|
|
|
string pem_file = sd_path + "/zlmediakit.pem";
|
2019-05-10 13:25:22 +08:00
|
|
|
|
|
|
|
|
|
|
DebugL << "sd_path:" << sd_path;
|
2019-09-24 15:21:20 +08:00
|
|
|
|
DebugL << "ini file:" << ini_file;
|
2019-05-10 13:25:22 +08:00
|
|
|
|
|
2019-09-24 15:21:20 +08:00
|
|
|
|
static thread s_th([sd_path,ini_file,pem_file](){
|
|
|
|
|
|
s_tread_id = pthread_self();
|
2019-05-10 13:25:22 +08:00
|
|
|
|
try {
|
2019-09-24 15:21:20 +08:00
|
|
|
|
//http根目录修改默认路径
|
2019-05-10 13:25:22 +08:00
|
|
|
|
mINI::Instance()[Http::kRootPath] = mINI::Instance()[Hls::kFilePath] = sd_path + "/httpRoot";
|
2019-09-24 15:21:20 +08:00
|
|
|
|
//mp4录制点播根目录修改默认路径
|
|
|
|
|
|
mINI::Instance()[Record::kFilePath] = mINI::Instance()[Hls::kFilePath] = sd_path + "/httpRoot";
|
|
|
|
|
|
//hls根目录修改默认路径
|
|
|
|
|
|
mINI::Instance()[Hls::kFilePath] = mINI::Instance()[Hls::kFilePath] = sd_path + "/httpRoot";
|
|
|
|
|
|
//替换默认端口号(在配置文件未生成时有效)
|
|
|
|
|
|
mINI::Instance()["http.port"] = 8080;
|
|
|
|
|
|
mINI::Instance()["http.sslport"] = 8443;
|
|
|
|
|
|
mINI::Instance()["rtsp.port"] = 8554;
|
|
|
|
|
|
mINI::Instance()["rtsp.sslport"] = 8332;
|
2019-10-09 22:14:41 +08:00
|
|
|
|
mINI::Instance()["general.enableVhost"] = 0;
|
2019-09-24 15:38:07 +08:00
|
|
|
|
for(auto &pr : mINI::Instance()){
|
|
|
|
|
|
//替换hook默认地址
|
|
|
|
|
|
replace(pr.second,"https://127.0.0.1/","http://127.0.0.1:8080/");
|
|
|
|
|
|
}
|
|
|
|
|
|
//默认打开hook
|
2019-09-24 16:09:10 +08:00
|
|
|
|
mINI::Instance()["hook.enable"] = 0;
|
2019-09-24 15:38:07 +08:00
|
|
|
|
//默认打开http api调试
|
|
|
|
|
|
mINI::Instance()["api.apiDebug"] = 1;
|
2019-09-24 15:21:20 +08:00
|
|
|
|
|
|
|
|
|
|
int argc = 5;
|
|
|
|
|
|
const char *argv[] = {"","-c",ini_file.data(),"-s",pem_file.data()};
|
|
|
|
|
|
|
|
|
|
|
|
start_main(argc,(char **)argv);
|
2019-05-10 13:25:22 +08:00
|
|
|
|
}catch (std::exception &ex){
|
|
|
|
|
|
WarnL << ex.what();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
static onceToken s_token([]{
|
|
|
|
|
|
s_th.detach();
|
|
|
|
|
|
});
|
2019-09-24 15:21:20 +08:00
|
|
|
|
|
2019-05-10 13:25:22 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
};
|
2019-05-10 16:25:24 +08:00
|
|
|
|
|
|
|
|
|
|
JNI_API(jlong,createMediaPlayer,jstring url,jobject callback){
|
|
|
|
|
|
static auto loadFrameClass = makeJavaFrame(env,nullptr);
|
|
|
|
|
|
MediaPlayer::Ptr *ret = new MediaPlayer::Ptr(new MediaPlayer());
|
|
|
|
|
|
MediaPlayer::Ptr &player = *ret;
|
|
|
|
|
|
|
|
|
|
|
|
weak_ptr<MediaPlayer> weakPlayer = player;
|
|
|
|
|
|
jobject globalWeakRef = env->NewWeakGlobalRef(callback);
|
|
|
|
|
|
player->setOnPlayResult([weakPlayer,globalWeakRef](const SockException &ex) {
|
|
|
|
|
|
auto strongPlayer = weakPlayer.lock();
|
|
|
|
|
|
if (!strongPlayer) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
emitEvent((jobject)globalWeakRef,"onPlayResult","(ILjava/lang/String;)V",(jint)ex.getErrCode(),env->NewStringUTF(ex.what()));
|
|
|
|
|
|
|
|
|
|
|
|
if(ex){
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto viedoTrack = strongPlayer->getTrack(TrackVideo);
|
|
|
|
|
|
if (viedoTrack) {
|
|
|
|
|
|
viedoTrack->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([globalWeakRef](const Frame::Ptr &frame) {
|
|
|
|
|
|
emitEvent((jobject)globalWeakRef,"onData","(L" MediaFrameSign ";)V",makeJavaFrame(env,frame));
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto audioTrack = strongPlayer->getTrack(TrackAudio);
|
|
|
|
|
|
if (audioTrack) {
|
|
|
|
|
|
audioTrack->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([globalWeakRef](const Frame::Ptr &frame) {
|
|
|
|
|
|
emitEvent((jobject)globalWeakRef,"onData","(L" MediaFrameSign ";)V",makeJavaFrame(env,frame));
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
player->setOnShutdown([globalWeakRef,weakPlayer](const SockException &ex) {
|
|
|
|
|
|
auto strongPlayer = weakPlayer.lock();
|
|
|
|
|
|
if (!strongPlayer) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
emitEvent((jobject)globalWeakRef,"onShutdown","(ILjava/lang/String;)V",(jint)ex.getErrCode(),env->NewStringUTF(ex.what()));
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2019-06-24 16:07:44 +08:00
|
|
|
|
(*player)[Client::kRtpType] = Rtsp::RTP_TCP;
|
2019-05-10 16:25:24 +08:00
|
|
|
|
player->play(stringFromJstring(env,url));
|
|
|
|
|
|
|
|
|
|
|
|
return (jlong)(ret);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JNI_API(void,releaseMediaPlayer,jlong ptr){
|
|
|
|
|
|
MediaPlayer::Ptr *player = (MediaPlayer::Ptr *)ptr;
|
|
|
|
|
|
delete player;
|
|
|
|
|
|
}
|