提交 5d31cd95 编写于 作者: Z Zhang Rui

jni: ijkmp: introduce ref count

上级 92bebf29
......@@ -191,6 +191,16 @@ inline void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowab
_rc; })
#endif
#define JNI_CHECK_GOTO(condition__, env__, exception__, msg__, label__) \
do { \
if (!(condition__)) { \
if (exception__) { \
jniThrowException(env__, exception__, msg__); \
} \
goto label__; \
} \
}while(0)
#define JNI_CHECK_RET_VOID(condition__, env__, exception__, msg__) \
do { \
if (!(condition__)) { \
......
/*****************************************************************************
* ijkerror.h
*****************************************************************************
*
* copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef IJKPLAYER__IJKERROR_H
#define IJKPLAYER__IJKERROR_H
#define EIJK_OK 0
#define EIJK_FAILED -1
#define EIJK_OUT_OF_MEMORY -2
#endif
......@@ -23,8 +23,52 @@
#include "ijkplayer.h"
#include <assert.h>
#include "ijkerror.h"
#include "ffplay.h"
IjkMediaPlayer *ijkmp_create()
{
IjkMediaPlayer *mp = (IjkMediaPlayer *) malloc(sizeof(IjkMediaPlayer));
if (!mp) {
return NULL;
}
memset(mp, 0, sizeof(IjkMediaPlayer));
mp->ffplayer = malloc(sizeof(FFPlayer));
if (!mp) {
free(mp);
return NULL;
}
memset(mp->ffplayer, 0, sizeof(FFPlayer));
return mp;
}
void ijkmp_shutdown(IjkMediaPlayer *mp)
{
assert(mp);
// FIXME: implement
}
void ijkmp_inc_ref(IjkMediaPlayer *mp)
{
assert(mp);
__sync_fetch_and_add(&mp->ref_count, 1);
}
void ijkmp_dec_ref(IjkMediaPlayer **pmp)
{
assert(pmp);
assert(*pmp);
IjkMediaPlayer *mp = *pmp;
int ref_count = __sync_fetch_and_sub(&mp->ref_count, 1);
if (ref_count == 0) {
ijkmp_shutdown(mp);
*pmp = NULL;
}
}
void ijkmp_init(IjkMediaPlayer *mp)
{
ijkmp_destroy(mp);
......
......@@ -29,23 +29,34 @@
struct IjkMediaPlayer;
typedef struct IjkMediaPlayer {
volatile int ref_count;
void *ffplayer;
} IjkMediaPlayer;
void ijkmp_init(IjkMediaPlayer *mp);
void ijkmp_destroy(IjkMediaPlayer *mp);
// ref_count is 0 after open
IjkMediaPlayer *ijkmp_create();
// preferred to be called explicity, can be called multiple times
// NOTE: ijkmp_shutdown may block thread
void ijkmp_shutdown(IjkMediaPlayer *mp);
void ijkmp_inc_ref(IjkMediaPlayer *mp);
// call close at last release, also free memory
// NOTE: ijkmp_dec_ref may block thread
void ijkmp_dec_ref(IjkMediaPlayer **pmp);
void ijkmp_set_data_source(IjkMediaPlayer *mp, const char *url);
void ijkmp_prepare_async(IjkMediaPlayer *mp);
void ijkmp_start(IjkMediaPlayer *mp);
void ijkmp_stop(IjkMediaPlayer *mp);
void ijkmp_pause(IjkMediaPlayer *mp);
int ijkmp_get_video_width(IjkMediaPlayer *mp);
int ijkmp_get_video_height(IjkMediaPlayer *mp);
int ijkmp_get_video_width(IjkMediaPlayer *mp);
int ijkmp_get_video_height(IjkMediaPlayer *mp);
void ijkmp_seek_to(IjkMediaPlayer *mp, int msec);
bool ijkmp_is_playing(IjkMediaPlayer *mp);
int ijkmp_get_current_position(IjkMediaPlayer *mp);
int ijkmp_get_duration(IjkMediaPlayer *mp);
int ijkmp_get_current_position(IjkMediaPlayer *mp);
int ijkmp_get_duration(IjkMediaPlayer *mp);
void ijkmp_reset(IjkMediaPlayer *mp);
// android api
......
......@@ -23,6 +23,7 @@
#include <assert.h>
#include <string.h>
#include <pthread.h>
#include <jni.h>
#include "helpers/loghelp.h"
#include "helpers/jnihelp.h"
......@@ -32,9 +33,11 @@
#define JNI_CLASS_IJKPLAYER "tv/danmaku/ijk/media/player/IjkMediaPlayer"
typedef struct player_fields_t {
pthread_mutex_t mutex;
jclass clazz;
jfieldID context;
jfieldID mNativeMediaPlayer;
jfieldID surface_texture;
jmethodID post_event;
......@@ -49,167 +52,245 @@ inline static int jni_get_int_fields(JNIEnv* env, jobject thiz, jfieldID field)
static IjkMediaPlayer *get_media_player(JNIEnv* env, jobject thiz)
{
// FIXME: lock ref count
IjkMediaPlayer *mp = (IjkMediaPlayer *) jni_get_int_fields(env, thiz, g_clazz.context);
pthread_mutex_lock(&g_clazz.mutex);
IjkMediaPlayer *mp = (IjkMediaPlayer *) jni_get_int_fields(env, thiz, g_clazz.mNativeMediaPlayer);
ijkmp_inc_ref(mp);
pthread_mutex_unlock(&g_clazz.mutex);
return mp;
}
static IjkMediaPlayer *set_media_player(JNIEnv* env, jobject thiz, IjkMediaPlayer *mp)
{
pthread_mutex_lock(&g_clazz.mutex);
IjkMediaPlayer *old = (IjkMediaPlayer*) (intptr_t) (*env)->GetLongField(env, thiz, g_clazz.mNativeMediaPlayer);
if (mp) {
ijkmp_inc_ref(mp);
}
(*env)->SetLongField(env, thiz, g_clazz.mNativeMediaPlayer, (intptr_t) mp);
pthread_mutex_unlock(&g_clazz.mutex);
// NOTE: ijkmp_dec_ref may block thread
if (old != NULL) {
ijkmp_dec_ref(&old);
}
return old;
}
static void
IjkMediaPlayer_setDataSourceAndHeaders(
JNIEnv *env, jobject thiz, jstring path,
jobjectArray keys, jobjectArray values) {
JNI_CHECK_RET_VOID(path, env, "java/lang/IllegalArgumentException", "mpjni: setDataSource: null path");
const char *c_path = NULL;
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, "java/lang/IllegalStateException", "mpjni: setDataSource: null mp");
JNI_CHECK_GOTO(path, env, "java/lang/IllegalArgumentException", "mpjni: setDataSource: null path", LABEL_RETURN);
JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: setDataSource: null mp", LABEL_RETURN);
const char *c_path = (*env)->GetStringUTFChars(env, path, NULL);
JNI_CHECK_RET_VOID(c_path, env, "java/lang/OutOfMemoryError", "mpjni: setDataSource: path.string oom");
c_path = (*env)->GetStringUTFChars(env, path, NULL);
JNI_CHECK_GOTO(c_path, env, "java/lang/OutOfMemoryError", "mpjni: setDataSource: path.string oom", LABEL_RETURN);
ALOGV("setDataSource: path %s", c_path);
ijkmp_set_data_source(mp, c_path);
(*env)->ReleaseStringUTFChars(env, path, c_path);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static void
IjkMediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
{
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, NULL, "mpjni: setVideoSurface: null mp");
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: setVideoSurface: null mp", LABEL_RETURN);
// FIXME: implement
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static void
IjkMediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
{
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, "java/lang/IllegalStateException", "mpjni: prepareAsync: null mp");
JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: prepareAsync: null mp", LABEL_RETURN);
ijkmp_prepare_async(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static void
IjkMediaPlayer_start(JNIEnv *env, jobject thiz)
{
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, "java/lang/IllegalStateException", "mpjni: start: null mp");
JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: start: null mp", LABEL_RETURN);
ijkmp_start(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static void
IjkMediaPlayer_stop(JNIEnv *env, jobject thiz)
{
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, "java/lang/IllegalStateException", "mpjni: stop: null mp");
JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: stop: null mp", LABEL_RETURN);
ijkmp_stop(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static void
IjkMediaPlayer_pause(JNIEnv *env, jobject thiz)
{
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, "java/lang/IllegalStateException", "mpjni: pause: null mp");
JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: pause: null mp", LABEL_RETURN);
ijkmp_pause(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static int
IjkMediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz)
{
int retval = 0;
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET(mp, env, NULL, "mpjni: getVideoWidth: null mp", 0);
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: getVideoWidth: null mp", LABEL_RETURN);
retval = ijkmp_get_video_width(mp);
return ijkmp_get_video_width(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
return retval;
}
static int
IjkMediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz)
{
int retval = 0;
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET(mp, env, NULL, "mpjni: getVideoHeight: null mp", 0);
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: getVideoHeight: null mp", LABEL_RETURN);
return ijkmp_get_video_height(mp);
retval = ijkmp_get_video_height(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
return retval;
}
static void
IjkMediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec)
{
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, "java/lang/IllegalStateException", "mpjni: seekTo: null mp");
JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: seekTo: null mp", LABEL_RETURN);
ijkmp_seek_to(mp, msec);
IjkMediaPlayer_seekTo(mp, msec);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static jboolean
IjkMediaPlayer_isPlaying(JNIEnv *env, jobject thiz)
{
jboolean retval = JNI_FALSE;
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET(mp, env, NULL, "mpjni: isPlaying: null mp", JNI_FALSE);
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: isPlaying: null mp", LABEL_RETURN);
retval = ijkmp_is_playing(mp) ? JNI_TRUE : JNI_FALSE;
return ijkmp_is_playing(mp) ? JNI_TRUE: JNI_FALSE;
LABEL_RETURN:
ijkmp_dec_ref(&mp);
return retval;
}
static int
IjkMediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz)
{
int retval = 0;
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET(mp, env, NULL, "mpjni: getCurrentPosition: null mp", 0);
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: getCurrentPosition: null mp", LABEL_RETURN);
return ijkmp_get_current_position(mp);
retval = ijkmp_get_current_position(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
return retval;
}
static int
IjkMediaPlayer_getDuration(JNIEnv *env, jobject thiz)
{
int retval = 0;
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET(mp, env, NULL, "mpjni: getDuration: null mp", 0);
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: getDuration: null mp", LABEL_RETURN);
retval = ijkmp_get_duration(mp);
return ijkmp_get_duration(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
return retval;
}
static void
IjkMediaPlayer_release(JNIEnv *env, jobject thiz)
{
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, NULL, "mpjni: release: null mp");
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: release: null mp", LABEL_RETURN);
// explicit close mp
ijkmp_shutdown(mp);
return ijkmp_destroy(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static void
IjkMediaPlayer_reset(JNIEnv *env, jobject thiz)
{
// FIXME: consider create new MediaPlayer
IjkMediaPlayer *mp = get_media_player(env, thiz);
JNI_CHECK_RET_VOID(mp, env, NULL, "mpjni: reset: null mp");
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: reset: null mp", LABEL_RETURN);
return ijkmp_ret(mp);
ijkmp_reset(mp);
LABEL_RETURN:
ijkmp_dec_ref(&mp);
}
static void
IjkMediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int streamtype)
{
// FIXME: implement
// FIXME: implement
}
static void
IjkMediaPlayer_native_init(JNIEnv *env)
{
// FIXME: implement
// FIXME: implement
}
static void
IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
// FIXME: implement
// FIXME: implement
}
static void
IjkMediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
{
// FIXME: implement
// FIXME: implement
}
// ----------------------------------------------------------------------------
......@@ -239,12 +320,6 @@ static JNINativeMethod g_methods[] = {
{ "native_finalize", "()V", (void *) IjkMediaPlayer_native_finalize },
};
void jni_init(JavaVM *vm, JNIEnv* env) {
g_clazz.clazz = (*env)->FindClass(env, JNI_CLASS_IJKPLAYER);
(*env)->RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods));
}
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env = NULL;
......@@ -254,10 +329,19 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
}
assert(env != NULL);
jni_init(vm, env);
pthread_mutex_init(&g_clazz.mutex, NULL);
g_clazz.clazz = (*env)->FindClass(env, JNI_CLASS_IJKPLAYER);
JNI_CHECK_RET(g_clazz.clazz, env, NULL, NULL, -1);
g_clazz.mNativeMediaPlayer = (*env)->GetFieldID(env, g_clazz.clazz, "mNativeMediaPlayer", "J");
JNI_CHECK_RET(g_clazz.mNativeMediaPlayer, env, NULL, NULL, -1);
(*env)->RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods));
return JNI_VERSION_1_4;
}
void JNI_OnUnload(JavaVM *jvm, void *reserved)
{
pthread_mutex_destroy(&g_clazz.mutex);
}
......@@ -21,6 +21,8 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.ref.WeakReference;
import tv.danmaku.ijk.media.player.annotations.AccessedByNative;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.SurfaceTexture;
......@@ -40,9 +42,15 @@ public final class IjkMediaPlayer extends AbstractMediaPlayer {
native_init();
}
private int mNativeContext; // accessed by native methods
private int mNativeSurfaceTexture; // accessed by native methods
private int mListenerContext; // accessed by native methods
@AccessedByNative
private long mNativeMediaPlayer;
@AccessedByNative
private int mNativeSurfaceTexture;
@AccessedByNative
private int mListenerContext;
private SurfaceHolder mSurfaceHolder;
private EventHandler mEventHandler;
private PowerManager.WakeLock mWakeLock = null;
......@@ -324,7 +332,7 @@ public final class IjkMediaPlayer extends AbstractMediaPlayer {
@Override
public void handleMessage(Message msg) {
if (mIjkMediaPlayer.mNativeContext == 0) {
if (mIjkMediaPlayer.mNativeMediaPlayer == 0) {
DebugLog.w(TAG,
"IjkMediaPlayer went away with unhandled events");
return;
......
package tv.danmaku.ijk.media.player.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @CalledByNative is used by the JNI generator to create the necessary JNI
* bindings and expose this method to native code.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface AccessedByNative {
}
\ No newline at end of file
package tv.danmaku.ijk.media.player.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @CalledByNative is used by the JNI generator to create the necessary JNI
* bindings and expose this method to native code.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface CalledByNative {
/*
* If present, tells which inner class the method belongs to.
*/
public String value() default "";
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册