ZLMediaKit/Android/app/src/main/cpp/native-lib.cpp

269 lines
9.3 KiB
C++
Raw Normal View History

2020-04-04 20:30:09 +08:00
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
2020-04-04 20:30:09 +08:00
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#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"
2022-06-19 01:12:14 +08:00
2019-09-24 15:21:20 +08:00
using namespace std;
using namespace toolkit;
using namespace mediakit;
2019-05-10 13:25:22 +08:00
2022-06-19 01:12:14 +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
2022-06-19 01:12:14 +08:00
string stringFromJstring(JNIEnv *env, jstring jstr) {
if (!env || !jstr) {
2019-05-10 13:25:22 +08:00
WarnL << "invalid args";
return "";
}
const char *field_char = env->GetStringUTFChars(jstr, 0);
2022-06-19 01:12:14 +08:00
string ret(field_char, env->GetStringUTFLength(jstr));
2019-05-10 13:25:22 +08:00
env->ReleaseStringUTFChars(jstr, field_char);
return ret;
}
2022-06-19 01:12:14 +08:00
string stringFromJbytes(JNIEnv *env, jbyteArray jbytes) {
if (!env || !jbytes) {
2019-05-10 13:25:22 +08:00
WarnL << "invalid args";
return "";
}
jbyte *bytes = env->GetByteArrayElements(jbytes, 0);
2022-06-19 01:12:14 +08:00
string ret((char *) bytes, env->GetArrayLength(jbytes));
env->ReleaseByteArrayElements(jbytes, bytes, 0);
2019-05-10 13:25:22 +08:00
return ret;
}
2022-06-19 01:12:14 +08:00
string stringFieldFromJava(JNIEnv *env, jobject jdata, jfieldID jid) {
if (!env || !jdata || !jid) {
2019-05-10 13:25:22 +08:00
WarnL << "invalid args";
return "";
}
2022-06-19 01:12:14 +08:00
jstring field_str = (jstring) env->GetObjectField(jdata, jid);
auto ret = stringFromJstring(env, field_str);
2019-05-10 13:25:22 +08:00
env->DeleteLocalRef(field_str);
return ret;
}
2022-06-19 01:12:14 +08:00
string bytesFieldFromJava(JNIEnv *env, jobject jdata, jfieldID jid) {
if (!env || !jdata || !jid) {
2019-05-10 13:25:22 +08:00
WarnL << "invalid args";
return "";
}
2022-06-19 01:12:14 +08:00
jbyteArray jbufArray = (jbyteArray) env->GetObjectField(jdata, jid);
string ret = stringFromJbytes(env, jbufArray);
2019-05-10 13:25:22 +08:00
env->DeleteLocalRef(jbufArray);
return ret;
}
2022-06-19 01:12:14 +08:00
jstring jstringFromString(JNIEnv *env, const char *pat) {
return (jstring) env->NewStringUTF(pat);
2019-05-10 13:25:22 +08:00
}
2022-06-19 01:12:14 +08:00
jbyteArray jbyteArrayFromString(JNIEnv *env, const char *pat, int len = 0) {
if (len <= 0) {
2019-05-10 13:25:22 +08:00
len = strlen(pat);
}
jbyteArray jarray = env->NewByteArray(len);
2022-06-19 01:12:14 +08:00
env->SetByteArrayRegion(jarray, 0, len, (jbyte * )(pat));
2019-05-10 13:25:22 +08:00
return jarray;
}
2022-06-19 01:12:14 +08:00
jobject makeJavaFrame(JNIEnv *env, const Frame::Ptr &frame) {
static jclass jclass_obj = (jclass) env->NewGlobalRef(env->FindClass(MediaFrameSign));
2019-05-10 16:25:24 +08:00
static jmethodID jmethodID_init = env->GetMethodID(jclass_obj, "<init>", "()V");
2022-06-19 01:12:14 +08:00
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) {
2019-05-10 16:25:24 +08:00
return nullptr;
}
jobject ret = env->NewObject(jclass_obj, jmethodID_init);
2022-06-19 01:12:14 +08:00
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());
2019-05-10 16:25:24 +08:00
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[]);
2022-06-19 01:12:14 +08:00
JNI_API(jboolean, startDemo, jstring ini_dir){
2019-09-24 15:21:20 +08:00
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;
mINI::Instance()["general.enableVhost"] = 0;
2022-06-19 01:12:14 +08:00
for (auto &pr : mINI::Instance()) {
2019-09-24 15:38:07 +08:00
//替换hook默认地址
2022-06-19 01:12:14 +08:00
replace(pr.second, "https://127.0.0.1/", "http://127.0.0.1:8080/");
2019-09-24 15:38:07 +08:00
}
//默认打开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;
2022-06-19 01:12:14 +08:00
const char *argv[] = {"", "-c", ini_file.data(), "-s", pem_file.data()};
start_main(argc, (char **) argv);
} catch (std::exception &ex) {
2019-05-10 13:25:22 +08:00
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
2022-06-19 01:12:14 +08:00
JNI_API(jlong, createMediaPlayer, jstring url, jobject callback){
2019-05-10 16:25:24 +08:00
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([globalWeakRef](const Frame::Ptr &frame) {
2019-05-10 16:25:24 +08:00
emitEvent((jobject)globalWeakRef,"onData","(L" MediaFrameSign ";)V",makeJavaFrame(env,frame));
2021-10-03 19:48:09 +08:00
return true;
});
2019-05-10 16:25:24 +08:00
}
auto audioTrack = strongPlayer->getTrack(TrackAudio);
if (audioTrack) {
audioTrack->addDelegate([globalWeakRef](const Frame::Ptr &frame) {
2019-05-10 16:25:24 +08:00
emitEvent((jobject)globalWeakRef,"onData","(L" MediaFrameSign ";)V",makeJavaFrame(env,frame));
2021-10-03 19:48:09 +08:00
return true;
});
2019-05-10 16:25:24 +08:00
}
});
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;
}