mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-26 04:31:37 +08:00
封装MediaPlayer JNI接口
This commit is contained in:
parent
d1035e28e9
commit
7b71115c90
@ -3,6 +3,8 @@
|
|||||||
#include "test_server.cpp"
|
#include "test_server.cpp"
|
||||||
|
|
||||||
#define JNI_API(retType,funName,...) extern "C" JNIEXPORT retType Java_com_zlmediakit_jni_ZLMediaKit_##funName(JNIEnv* env, jclass cls,##__VA_ARGS__)
|
#define JNI_API(retType,funName,...) extern "C" JNIEXPORT retType Java_com_zlmediakit_jni_ZLMediaKit_##funName(JNIEnv* env, jclass cls,##__VA_ARGS__)
|
||||||
|
#define MediaPlayerCallBackSign "com/zlmediakit/jni/ZLMediaKit$MediaPlayerCallBack"
|
||||||
|
#define MediaFrameSign "com/zlmediakit/jni/ZLMediaKit$MediaFrame"
|
||||||
|
|
||||||
|
|
||||||
string stringFromJstring(JNIEnv *env,jstring jstr){
|
string stringFromJstring(JNIEnv *env,jstring jstr){
|
||||||
@ -60,11 +62,69 @@ jbyteArray jbyteArrayFromString(JNIEnv* env, const char* pat,int len = 0){
|
|||||||
return jarray;
|
return jarray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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; \
|
||||||
|
}\
|
||||||
|
}); \
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 加载动态库
|
* 加载动态库
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
//设置日志
|
//设置日志
|
||||||
|
s_jvm = vm;
|
||||||
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
||||||
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||||
InfoL;
|
InfoL;
|
||||||
@ -100,3 +160,57 @@ JNI_API(jboolean,startDemo,jstring path_to_jni_file){
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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()));
|
||||||
|
});
|
||||||
|
|
||||||
|
player->play(stringFromJstring(env,url));
|
||||||
|
|
||||||
|
return (jlong)(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
JNI_API(void,releaseMediaPlayer,jlong ptr){
|
||||||
|
MediaPlayer::Ptr *player = (MediaPlayer::Ptr *)ptr;
|
||||||
|
delete player;
|
||||||
|
}
|
||||||
|
@ -6,16 +6,20 @@ import android.os.Environment;
|
|||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.zlmediakit.jni.ZLMediaKit;
|
import com.zlmediakit.jni.ZLMediaKit;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
public static final String TAG = "ZLMediaKit";
|
||||||
private static String[] PERMISSIONS_STORAGE = {
|
private static String[] PERMISSIONS_STORAGE = {
|
||||||
"android.permission.READ_EXTERNAL_STORAGE",
|
"android.permission.READ_EXTERNAL_STORAGE",
|
||||||
"android.permission.WRITE_EXTERNAL_STORAGE",
|
"android.permission.WRITE_EXTERNAL_STORAGE",
|
||||||
"android.permission.INTERNET"};
|
"android.permission.INTERNET"};
|
||||||
|
|
||||||
|
private ZLMediaKit.MediaPlayer _player;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -39,6 +43,31 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}else{
|
}else{
|
||||||
Toast.makeText(this,"请给予我权限,否则无法启动测试!" ,Toast.LENGTH_LONG).show();
|
Toast.makeText(this,"请给予我权限,否则无法启动测试!" ,Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_player = new ZLMediaKit.MediaPlayer("rtmp://live.hkstv.hk.lxdns.com/live/hks1", new ZLMediaKit.MediaPlayerCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onPlayResult(int code, String msg) {
|
||||||
|
Log.d(TAG,"onPlayResult:" + code + "," + msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onShutdown(int code, String msg) {
|
||||||
|
Log.d(TAG,"onShutdown:" + code + "," + msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onData(ZLMediaKit.MediaFrame frame) {
|
||||||
|
Log.d(TAG,"onData:"
|
||||||
|
+ frame.trackType + ","
|
||||||
|
+ frame.codecId + ","
|
||||||
|
+ frame.dts + ","
|
||||||
|
+ frame.pts + ","
|
||||||
|
+ frame.keyFrame + ","
|
||||||
|
+ frame.prefixSize + ","
|
||||||
|
+ frame.data.length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,93 @@
|
|||||||
package com.zlmediakit.jni;
|
package com.zlmediakit.jni;
|
||||||
|
|
||||||
public class ZLMediaKit {
|
public class ZLMediaKit {
|
||||||
|
static public class MediaFrame{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回解码时间戳,单位毫秒
|
||||||
|
*/
|
||||||
|
public int dts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回显示时间戳,单位毫秒
|
||||||
|
*/
|
||||||
|
public int pts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前缀长度,譬如264前缀为0x00 00 00 01,那么前缀长度就是4
|
||||||
|
* aac前缀则为7个字节
|
||||||
|
*/
|
||||||
|
public int prefixSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回是否为关键帧
|
||||||
|
*/
|
||||||
|
public boolean keyFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音视频数据
|
||||||
|
*/
|
||||||
|
public byte[] data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是音频还是视频
|
||||||
|
* typedef enum {
|
||||||
|
* TrackInvalid = -1,
|
||||||
|
* TrackVideo = 0,
|
||||||
|
* TrackAudio,
|
||||||
|
* TrackTitle,
|
||||||
|
* TrackMax = 0x7FFF
|
||||||
|
* } TrackType;
|
||||||
|
*/
|
||||||
|
public int trackType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码类型
|
||||||
|
* typedef enum {
|
||||||
|
* CodecInvalid = -1,
|
||||||
|
* CodecH264 = 0,
|
||||||
|
* CodecH265,
|
||||||
|
* CodecAAC,
|
||||||
|
* CodecMax = 0x7FFF
|
||||||
|
* } CodecId;
|
||||||
|
*/
|
||||||
|
public int codecId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public interface MediaPlayerCallBack{
|
||||||
|
void onPlayResult(int code,String msg);
|
||||||
|
void onShutdown(int code,String msg);
|
||||||
|
void onData(MediaFrame frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static public class MediaPlayer{
|
||||||
|
private long _ptr;
|
||||||
|
private MediaPlayerCallBack _callback;
|
||||||
|
public MediaPlayer(String url,MediaPlayerCallBack callBack){
|
||||||
|
_callback = callBack;
|
||||||
|
_ptr = createMediaPlayer(url,callBack);
|
||||||
|
}
|
||||||
|
public void release(){
|
||||||
|
if(_ptr != 0){
|
||||||
|
releaseMediaPlayer(_ptr);
|
||||||
|
_ptr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
super.finalize();
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public native boolean startDemo(String sd_path);
|
||||||
|
static public native void releaseMediaPlayer(long ptr);
|
||||||
|
static public native long createMediaPlayer(String url,MediaPlayerCallBack callback);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.loadLibrary("zlmediakit_jni");
|
System.loadLibrary("zlmediakit_jni");
|
||||||
}
|
}
|
||||||
static public native boolean startDemo(String sd_path);
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user