Commit 559c0081 authored by Simon Morlat's avatar Simon Morlat

add framework to retrieve java vm and jni env easily and in a safe manner

parent a0e4a661
......@@ -67,7 +67,8 @@ LOCAL_SRC_FILES = \
kiss_fftr.c \
void.c \
msandroid.cpp \
eventqueue.c
eventqueue.c \
msjava.c
LOCAL_SRC_FILES += audiostream.c
......@@ -133,7 +134,7 @@ LOCAL_SRC_FILES += gsm.c
LOCAL_CFLAGS += \
-UHAVE_CONFIG_H \
-include $(LOCAL_PATH)/../build/android/libmediastreamer2_AndroidConfig.h \
-D_POSIX_SOURCE
-D_POSIX_SOURCE -Wall
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
......
......@@ -29,7 +29,8 @@ mediastreamer2_include_HEADERS= ice.h \
msaudiomixer.h \
msitc.h \
msextdisplay.h \
msjpegwriter.h
msjpegwriter.h \
msjava.h
EXTRA_DIST=$(mediastreamer2_include_HEADERS)
......@@ -102,6 +102,8 @@ typedef struct _MSList MSList;
#define ms_list_next(elem) ((elem)->next)
typedef int (*MSCompareFunc)(const void *a, const void *b);
#ifdef __cplusplus
extern "C"{
#endif
......@@ -116,11 +118,11 @@ void ms_list_for_each(const MSList *list, void (*func)(void *));
void ms_list_for_each2(const MSList *list, void (*func)(void *, void *), void *user_data);
MSList *ms_list_remove_link(MSList *list, MSList *elem);
MSList *ms_list_find(MSList *list, void *data);
MSList *ms_list_find_custom(MSList *list, int (*compare_func)(const void *, const void*), void *user_data);
MSList *ms_list_find_custom(MSList *list, MSCompareFunc compare_func, const void *user_data);
void * ms_list_nth_data(const MSList *list, int index);
int ms_list_position(const MSList *list, MSList *elem);
int ms_list_index(const MSList *list, void *data);
MSList *ms_list_insert_sorted(MSList *list, void *data, int (*compare_func)(const void *, const void*));
MSList *ms_list_insert_sorted(MSList *list, void *data, MSCompareFunc compare_func);
MSList *ms_list_insert(MSList *list, MSList *before, void *data);
MSList *ms_list_copy(const MSList *list);
......
......@@ -95,6 +95,15 @@ enum _MSFilterFlags{
typedef enum _MSFilterFlags MSFilterFlags;
struct _MSFilterStats{
const char *name; /*<filter name*/
uint64_t elapsed; /*<cumulative number of nanoseconds elapsed */
unsigned int count; /*<number of time the filter is called for processing*/
};
typedef struct _MSFilterStats MSFilterStats;
struct _MSFilterDesc{
MSFilterId id; /* the id declared in allfilters.h */
const char *name; /* filter name */
......@@ -130,6 +139,7 @@ struct _MSFilter{
struct _MSTicker *ticker;
/*private attributes */
uint32_t last_tick;
MSFilterStats *stats;
bool_t seen;
bool_t synchronous_notifies;
};
......@@ -433,6 +443,22 @@ int ms_connection_helper_link(MSConnectionHelper *h, MSFilter *f, int inpin, int
**/
int ms_connection_helper_unlink(MSConnectionHelper *h, MSFilter *f, int inpin, int outpin);
/**
* \brief Enable time measurements statistics for filters.
*
**/
void ms_filter_enable_statistics(bool_t enabled);
/**
* \brief Retrieves statistics for running filters.
* Returns a list of MSFilterStats
**/
const MSList * ms_filter_get_statistics(void);
void ms_filter_log_statistics(void);
/* I define the id taking the lower bits of the address of the MSFilterDesc object,
the method index (_cnt_) and the argument size */
/* I hope using this to avoid type mismatch (calling a method on the wrong filter)*/
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2010 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef msjava_h
#define msjava_h
/* Helper routines for filters that use a jvm with upcalls to perform some processing */
#include <jni.h>
#ifdef __cplusplus
extern "C"{
#endif
void ms_set_jvm(JavaVM *vm);
JavaVM *ms_get_jvm(void);
JNIEnv *ms_get_jni_env(void);
#ifdef __cplusplus
}
#endif
#endif
......@@ -2,7 +2,7 @@
EXTRA_DIST= winsnd2.c winsnd.c winvideo.c \
winvideods.c wincevideods.c dxfilter.h dxfilter.cpp \
msfileplayer_win.c msfilerec_win.c winsndds.cpp nowebcamCIF.jpg winsnd3.c vfw-missing.h \
winvideo2.c
winvideo2.c msjava.c
BUILT_SOURCES=alldescs.h
......
......@@ -21,13 +21,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msvideo.h"
#include "mediastreamer2/msjava.h"
#include "layouts.h"
#include <android/bitmap.h>
#include <jni.h>
#include <dlfcn.h>
/*defined in msandroid.cpp*/
extern JavaVM *ms_andsnd_jvm;
static int android_version=0;
......@@ -110,15 +109,11 @@ typedef struct AndroidDisplay{
static void android_display_init(MSFilter *f){
AndroidDisplay *ad=(AndroidDisplay*)ms_new0(AndroidDisplay,1);
JNIEnv *jenv=NULL;
JNIEnv *jenv=ms_get_jni_env();
jclass wc;
ad->jvm=ms_andsnd_jvm;
ad->jvm=ms_get_jvm();
if (ad->jvm->AttachCurrentThread(&jenv,NULL)!=0){
ms_error("Could not get JNIEnv");
return ;
}
wc=jenv->FindClass("org/linphone/core/AndroidVideoWindowImpl");
if (wc==0){
ms_fatal("Could not find org.linphone.core.AndroidVideoWindowImpl class !");
......@@ -161,13 +156,6 @@ static void android_display_process(MSFilter *f){
AndroidDisplay *ad=(AndroidDisplay*)f->data;
MSPicture pic;
mblk_t *m;
JNIEnv *jenv=NULL;
jint result = ad->jvm->AttachCurrentThread(&jenv,NULL);
if (result != 0) {
ms_error("android_display_process(): cannot attach VM");
goto end;
}
ms_filter_lock(f);
if (ad->surf!=NULL){
......@@ -214,32 +202,19 @@ static void android_display_process(MSFilter *f){
}
}
ms_filter_unlock(f);
end:
ms_queue_flush(f->inputs[0]);
ms_queue_flush(f->inputs[1]);
if (jenv!=NULL){
jint result = ad->jvm->DetachCurrentThread();
if (result != 0) {
ms_error("android_display_process(): cannot detach VM");
}
}
}
static int android_display_set_window(MSFilter *f, void *arg){
AndroidDisplay *ad=(AndroidDisplay*)f->data;
unsigned long id=*(unsigned long*)arg;
int err;
JNIEnv *jenv=NULL;
JNIEnv *jenv=ms_get_jni_env();
jobject jsurface=NULL;
jobject android_window=(jobject)id;
Surface *oldsurf;
if (ad->jvm->AttachCurrentThread(&jenv,NULL)!=0){
ms_error("Could not get JNIEnv");
return -1;
}
if (android_window!=NULL)
jsurface=jenv->CallObjectMethod(android_window,ad->get_surface_id);
......@@ -254,6 +229,7 @@ static int android_display_set_window(MSFilter *f, void *arg){
ms_filter_unlock(f);
ms_message("Got new surface to draw (%p)",ad->surf);
return 0;
}
static MSFilterMethod methods[]={
......
......@@ -21,17 +21,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msvideo.h"
#include "mediastreamer2/msjava.h"
#include "layouts.h"
#include <android/bitmap.h>
#include <jni.h>
#include <dlfcn.h>
/*defined in msandroid.cpp*/
extern JavaVM *ms_andsnd_jvm;
typedef struct AndroidDisplay{
JavaVM *jvm;
JNIEnv *jenv;
jobject android_video_window;
jobject jbitmap;
jmethodID get_bitmap_id;
......@@ -53,7 +52,7 @@ static void android_display_init(MSFilter *f){
JNIEnv *jenv=NULL;
jclass wc;
ad->jvm=ms_andsnd_jvm;
ad->jvm=ms_get_jvm();
if ((*(ad->jvm))->AttachCurrentThread(ad->jvm,&jenv,NULL)!=0){
ms_error("Could not get JNIEnv");
......@@ -90,29 +89,23 @@ static int vsize_get_orientation(MSVideoSize vs){
return vs.width>=vs.height ? LANDSCAPE : PORTRAIT;
}
static bool_t select_orientation(AndroidDisplay *ad, MSVideoSize wsize, MSVideoSize vsize){
static void select_orientation(AndroidDisplay *ad, MSVideoSize wsize, MSVideoSize vsize){
int wo,vo;
JNIEnv *jenv=ms_get_jni_env();
wo=vsize_get_orientation(wsize);
vo=vsize_get_orientation(vsize);
if (wo!=vo){
ms_message("Requesting orientation change !");
(*ad->jenv)->CallVoidMethod(ad->jenv,ad->android_video_window,ad->request_orientation_id,vo);
(*jenv)->CallVoidMethod(jenv,ad->android_video_window,ad->request_orientation_id,vo);
ad->orientation_change_pending=TRUE;
}
ad->orientation_change_pending=TRUE;
}
static void android_display_process(MSFilter *f){
AndroidDisplay *ad=(AndroidDisplay*)f->data;
MSPicture pic;
mblk_t *m;
if (ad->jenv==NULL){
jint result = (*(ad->jvm))->AttachCurrentThread(ad->jvm,&ad->jenv,NULL);
if (result != 0) {
ms_error("android_display_process(): cannot attach VM");
goto end;
}
}
ms_filter_lock(f);
if (ad->jbitmap!=0 && !ad->orientation_change_pending){
......@@ -123,6 +116,7 @@ static void android_display_process(MSFilter *f){
MSRect vrect;
MSPicture dest={0};
void *pixels=NULL;
JNIEnv *jenv=ms_get_jni_env();
if (!ms_video_size_equal(vsize,ad->vsize)){
ms_message("Video to display has size %ix%i",vsize.width,vsize.height);
......@@ -143,46 +137,38 @@ static void android_display_process(MSFilter *f){
ms_fatal("Could not obtain sws context !");
}
}
if (sym_AndroidBitmap_lockPixels(ad->jenv,ad->jbitmap,&pixels)==0){
dest.planes[0]=(uint8_t*)pixels+(vrect.y*ad->bmpinfo.stride)+(vrect.x*2);
dest.strides[0]=ad->bmpinfo.stride;
ms_sws_scale (ad->sws,pic.planes,pic.strides,0,pic.h,dest.planes,dest.strides);
sym_AndroidBitmap_unlockPixels(ad->jenv,ad->jbitmap);
if (sym_AndroidBitmap_lockPixels(jenv,ad->jbitmap,&pixels)==0){
/*
if (pixels!=NULL){
dest.planes[0]=(uint8_t*)pixels+(vrect.y*ad->bmpinfo.stride)+(vrect.x*2);
dest.strides[0]=ad->bmpinfo.stride;
ms_sws_scale (ad->sws,pic.planes,pic.strides,0,pic.h,dest.planes,dest.strides);
}else ms_warning("Pixels==NULL in android bitmap !");
*/
sym_AndroidBitmap_unlockPixels(jenv,ad->jbitmap);
}else{
ms_error("AndroidBitmap_lockPixels() failed !");
}
(*ad->jenv)->CallVoidMethod(ad->jenv,ad->android_video_window,ad->update_id);
(*jenv)->CallVoidMethod(jenv,ad->android_video_window,ad->update_id);
}
}
}
ms_filter_unlock(f);
end:
ms_queue_flush(f->inputs[0]);
ms_queue_flush(f->inputs[1]);
if (ad->jenv!=NULL){
jint result = (*(ad->jvm))->DetachCurrentThread(ad->jvm);
if (result != 0) {
ms_error("android_display_process(): cannot detach VM");
}
ad->jenv=NULL;
}
}
static int android_display_set_window(MSFilter *f, void *arg){
AndroidDisplay *ad=(AndroidDisplay*)f->data;
unsigned long id=*(unsigned long*)arg;
int err;
JNIEnv *jenv=NULL;
JNIEnv *jenv=ms_get_jni_env();
jobject window=(jobject)id;
if ((*(ad->jvm))->AttachCurrentThread(ad->jvm,&jenv,NULL)!=0){
ms_error("Could not get JNIEnv");
return -1;
}
ms_filter_lock(f);
if (window!=NULL)
ad->jbitmap=(*jenv)->CallObjectMethod(jenv,window,ad->get_bitmap_id);
......@@ -206,6 +192,7 @@ static int android_display_set_window(MSFilter *f, void *arg){
ms_filter_unlock(f);
if (ad->jbitmap!=NULL) ms_message("New java bitmap given with w=%i,h=%i,stride=%i,format=%i",
ad->bmpinfo.width,ad->bmpinfo.height,ad->bmpinfo.stride,ad->bmpinfo.format);
return 0;
}
static MSFilterMethod methods[]={
......@@ -213,7 +200,7 @@ static MSFilterMethod methods[]={
{ 0, NULL}
};
static MSFilterDesc ms_android_display_desc={
MSFilterDesc ms_android_display_desc={
.id=MS_ANDROID_DISPLAY_ID,
.name="MSAndroidDisplay",
.text="Video display filter for Android.",
......@@ -234,9 +221,9 @@ extern void libmsandroiddisplaybad_init(void);
void libmsandroiddisplay_init(void){
/*See if we can use AndroidBitmap_* symbols (only since android 2.2 normally)*/
void *handle=NULL;
#if USE_ANDROID_BITMAP
void *handle=NULL;
handle=dlopen("libjnigraphics.so",RTLD_LAZY);
if (handle!=NULL){
sym_AndroidBitmap_getInfo=dlsym(handle,"AndroidBitmap_getInfo");
......
......@@ -475,6 +475,7 @@ AudioStream *audio_stream_new(int locport, bool_t ipv6){
stream->use_gc=FALSE;
stream->use_agc=FALSE;
stream->use_ng=FALSE;
ms_filter_enable_statistics(TRUE);
return stream;
}
......@@ -578,6 +579,7 @@ void audio_stream_stop(AudioStream * stream)
}
audio_stream_free(stream);
ms_filter_log_statistics();
}
RingStream * ring_start(const char *file, int interval, MSSndCard *sndcard){
......
......@@ -163,7 +163,7 @@ MSList *ms_list_find(MSList *list, void *data){
return NULL;
}
MSList *ms_list_find_custom(MSList *list, int (*compare_func)(const void *, const void*), void *user_data){
MSList *ms_list_find_custom(MSList *list, int (*compare_func)(const void *, const void*), const void *user_data){
for(;list!=NULL;list=list->next){
if (compare_func(list->data,user_data)==0) return list;
}
......
......@@ -21,6 +21,23 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mediastreamer2/mscommon.h"
static MSList *desc_list=NULL;
static bool_t statistics_enabled=FALSE;
static MSList *stats_list=NULL;
static int compare_stats_with_name(const MSFilterStats *stat, const char *name){
return strcmp(stat->name,name);
}
static MSFilterStats *find_or_create_stats(MSFilterDesc *desc){
MSList *elem=ms_list_find_custom(stats_list,(MSCompareFunc)compare_stats_with_name,desc->name);
MSFilterStats *ret=NULL;
if (elem==NULL){
ret=ms_new0(MSFilterStats,1);
ret->name=desc->name;
stats_list=ms_list_append(stats_list,ret);
}else ret=(MSFilterStats*)elem->data;
return ret;
}
void ms_filter_register(MSFilterDesc *desc){
if (desc->id==MS_FILTER_NOT_SET_ID){
......@@ -31,7 +48,15 @@ void ms_filter_register(MSFilterDesc *desc){
}
void ms_filter_unregister_all(){
if (desc_list!=NULL) ms_list_free(desc_list);
if (desc_list!=NULL) {
ms_list_free(desc_list);
desc_list=NULL;
}
if (stats_list!=NULL){
ms_list_for_each(stats_list,ms_free);
ms_list_free(stats_list);
stats_list=NULL;
}
}
bool_t ms_filter_codec_supported(const char *mime){
......@@ -83,6 +108,10 @@ MSFilter *ms_filter_new_from_desc(MSFilterDesc *desc){
obj->desc=desc;
if (desc->ninputs>0) obj->inputs=(MSQueue**)ms_new0(MSQueue*,desc->ninputs);
if (desc->noutputs>0) obj->outputs=(MSQueue**)ms_new0(MSQueue*,desc->noutputs);
if (statistics_enabled){
obj->stats=find_or_create_stats(desc);
}
if (obj->desc->init!=NULL)
obj->desc->init(obj);
return obj;
......@@ -202,19 +231,19 @@ void ms_filter_destroy(MSFilter *f){
ms_free(f);
}
#ifdef DEBUG
static long filter_get_cur_time(void *unused)
static uint64_t get_cur_time_ns(void)
{
#if defined(_WIN32_WCE)
DWORD timemillis = GetTickCount();
return timemillis;
return (uint64_t)timemillis*1000000;
#elif defined(WIN32)
return timeGetTime() ;
return timeGetTime()*1000000LL ;
#elif defined(__MACH__) && defined(__GNUC__) && (__GNUC__ >= 3)
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec*1000LL) + (tv.tv_usec/1000LL);
return (tv.tv_sec*1000000000LL) + (tv.tv_usec*1000LL);
#elif defined(__MACH__)
struct timespec ts;
struct timeb time_val;
......@@ -222,35 +251,31 @@ static long filter_get_cur_time(void *unused)
ftime (&time_val);
ts.tv_sec = time_val.time;
ts.tv_nsec = time_val.millitm * 1000000;
return (ts.tv_sec*1000LL) + (ts.tv_nsec/1000000LL);
return (ts.tv_sec*1000000000LL) + ts.tv_nsec;
#else
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC,&ts)<0){
fprintf(stderr, "clock_gettime() doesn't work: %s",strerror(errno));
ms_fatal("clock_gettime() doesn't work: %s",strerror(errno));
}
return (ts.tv_sec*1000LL) + (ts.tv_nsec/1000000LL);
return (ts.tv_sec*1000000000LL) + ts.tv_nsec;
#endif
}
#endif
void ms_filter_process(MSFilter *f){
uint64_t start=0,stop;
ms_debug("Executing process of filter %s:%p",f->desc->name,f);
#ifdef DEBUG
long start,stop;
start = filter_get_cur_time(NULL);
#endif
if (f->stats)
start = get_cur_time_ns();
f->desc->process(f);
#ifdef DEBUG
stop = filter_get_cur_time(NULL);
if(stop-start > 10)
{
ms_warning("%s take too much time:%ldms\n",f->desc->name,stop-start);
}
else
{
ms_debug("%s take:%ldms\n",f->desc->name,stop-start);
if (f->stats){
stop = get_cur_time_ns();
f->stats->count++;
f->stats->elapsed+=stop-start;
}
#endif
}
void ms_filter_preprocess(MSFilter *f, struct _MSTicker *t){
......@@ -352,4 +377,37 @@ int ms_connection_helper_unlink(MSConnectionHelper *h, MSFilter *f, int inpin, i
return err;
}
void ms_filter_enable_statistics(bool_t enabled){
statistics_enabled=enabled;
}
const MSList * ms_filter_get_statistics(void){
return stats_list;
}
static int usage_compare(const MSFilterStats *s1, const MSFilterStats *s2){
if (s1->elapsed==s2->elapsed) return 0;
if (s1->elapsed<s2->elapsed) return 1;
return -1;
}
void ms_filter_log_statistics(void){
MSList *sorted=NULL;
MSList *elem;
uint64_t total=0;
ms_message("Filter usage statistics:");
for(elem=stats_list;elem!=NULL;elem=elem->next){
MSFilterStats *stats=(MSFilterStats *)elem->data;
sorted=ms_list_insert_sorted(sorted,stats,(MSCompareFunc)usage_compare);
total+=stats->elapsed;
}
ms_message("Name\tCount\tCPU Usage");
for(elem=sorted;elem!=NULL;elem=elem->next){
MSFilterStats *stats=(MSFilterStats *)elem->data;
double percentage=100.0*((double)stats->elapsed)/(double)total;
ms_message("%s %i %g",stats->name,stats->count,percentage);
}
ms_list_free(sorted);
}
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2010 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "mediastreamer2/msjava.h"
#include "mediastreamer2/mscommon.h"
static JavaVM *ms2_vm=NULL;
#ifndef WIN32
#include <pthread.h>
static pthread_key_t jnienv_key;
static void key_cleanup(void *data){
(*ms2_vm)->DetachCurrentThread(ms2_vm);
}
#endif
void ms_set_jvm(JavaVM *vm){
ms2_vm=vm;
#ifndef WIN32
pthread_key_create(&jnienv_key,key_cleanup);
#endif
}
JavaVM *ms_get_jvm(void){
return ms2_vm;
}
JNIEnv *ms_get_jni_env(void){
JNIEnv *env=NULL;
if (ms2_vm==NULL){
ms_error("Calling ms_get_jni_env() while no jvm has been set using ms_set_jvm().");
}else{
#ifndef WIN32
env=(JNIEnv*)pthread_getspecific(jnienv_key);
if (env==NULL){
if ((*ms2_vm)->AttachCurrentThread(ms2_vm,&env,NULL)!=0){
ms_error("AttachCurrentThread() failed !");
return NULL;
}
pthread_setspecific(jnienv_key,env);
}
#else
ms_error("ms_get_jni_env() not implemented on windows.");
#endif
}
return env;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment