FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ffjni.c
Go to the documentation of this file.
1 /*
2  * JNI utility functions
3  *
4  * Copyright (c) 2015-2016 Matthieu Bouron <matthieu.bouron stupeflix.com>
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 #include <jni.h>
24 #include <pthread.h>
25 #include <stdlib.h>
26 
27 #include "libavutil/bprint.h"
28 #include "libavutil/log.h"
29 
30 #include "config.h"
31 #include "jni.h"
32 #include "ffjni.h"
33 
34 static JavaVM *java_vm;
35 static pthread_key_t current_env;
38 
39 static void jni_detach_env(void *data)
40 {
41  if (java_vm) {
42  (*java_vm)->DetachCurrentThread(java_vm);
43  }
44 }
45 
46 static void jni_create_pthread_key(void)
47 {
48  pthread_key_create(&current_env, jni_detach_env);
49 }
50 
51 JNIEnv *ff_jni_get_env(void *log_ctx)
52 {
53  int ret = 0;
54  JNIEnv *env = NULL;
55 
57  if (java_vm == NULL) {
58  java_vm = av_jni_get_java_vm(log_ctx);
59  }
60 
61  if (!java_vm) {
62  av_log(log_ctx, AV_LOG_ERROR, "No Java virtual machine has been registered\n");
63  goto done;
64  }
65 
67 
68  if ((env = pthread_getspecific(current_env)) != NULL) {
69  goto done;
70  }
71 
72  ret = (*java_vm)->GetEnv(java_vm, (void **)&env, JNI_VERSION_1_6);
73  switch(ret) {
74  case JNI_EDETACHED:
75  if ((*java_vm)->AttachCurrentThread(java_vm, &env, NULL) != 0) {
76  av_log(log_ctx, AV_LOG_ERROR, "Failed to attach the JNI environment to the current thread\n");
77  env = NULL;
78  } else {
79  pthread_setspecific(current_env, env);
80  }
81  break;
82  case JNI_OK:
83  break;
84  case JNI_EVERSION:
85  av_log(log_ctx, AV_LOG_ERROR, "The specified JNI version is not supported\n");
86  break;
87  default:
88  av_log(log_ctx, AV_LOG_ERROR, "Failed to get the JNI environment attached to this thread\n");
89  break;
90  }
91 
92 done:
94  return env;
95 }
96 
97 char *ff_jni_jstring_to_utf_chars(JNIEnv *env, jstring string, void *log_ctx)
98 {
99  char *ret = NULL;
100  const char *utf_chars = NULL;
101 
102  jboolean copy = 0;
103 
104  if (!string) {
105  return NULL;
106  }
107 
108  utf_chars = (*env)->GetStringUTFChars(env, string, &copy);
109  if ((*env)->ExceptionCheck(env)) {
110  (*env)->ExceptionClear(env);
111  av_log(log_ctx, AV_LOG_ERROR, "String.getStringUTFChars() threw an exception\n");
112  return NULL;
113  }
114 
115  ret = av_strdup(utf_chars);
116 
117  (*env)->ReleaseStringUTFChars(env, string, utf_chars);
118  if ((*env)->ExceptionCheck(env)) {
119  (*env)->ExceptionClear(env);
120  av_log(log_ctx, AV_LOG_ERROR, "String.releaseStringUTFChars() threw an exception\n");
121  return NULL;
122  }
123 
124  return ret;
125 }
126 
127 jstring ff_jni_utf_chars_to_jstring(JNIEnv *env, const char *utf_chars, void *log_ctx)
128 {
129  jstring ret;
130 
131  ret = (*env)->NewStringUTF(env, utf_chars);
132  if ((*env)->ExceptionCheck(env)) {
133  (*env)->ExceptionClear(env);
134  av_log(log_ctx, AV_LOG_ERROR, "NewStringUTF() threw an exception\n");
135  return NULL;
136  }
137 
138  return ret;
139 }
140 
141 int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
142 {
143  int ret = 0;
144 
145  AVBPrint bp;
146 
147  char *name = NULL;
148  char *message = NULL;
149 
150  jclass class_class = NULL;
151  jmethodID get_name_id = NULL;
152 
153  jclass exception_class = NULL;
154  jmethodID get_message_id = NULL;
155 
156  jstring string = NULL;
157 
159 
160  exception_class = (*env)->GetObjectClass(env, exception);
161  if ((*env)->ExceptionCheck(env)) {
162  (*env)->ExceptionClear(env);
163  av_log(log_ctx, AV_LOG_ERROR, "Could not find Throwable class\n");
164  ret = AVERROR_EXTERNAL;
165  goto done;
166  }
167 
168  class_class = (*env)->GetObjectClass(env, exception_class);
169  if ((*env)->ExceptionCheck(env)) {
170  (*env)->ExceptionClear(env);
171  av_log(log_ctx, AV_LOG_ERROR, "Could not find Throwable class's class\n");
172  ret = AVERROR_EXTERNAL;
173  goto done;
174  }
175 
176  get_name_id = (*env)->GetMethodID(env, class_class, "getName", "()Ljava/lang/String;");
177  if ((*env)->ExceptionCheck(env)) {
178  (*env)->ExceptionClear(env);
179  av_log(log_ctx, AV_LOG_ERROR, "Could not find method Class.getName()\n");
180  ret = AVERROR_EXTERNAL;
181  goto done;
182  }
183 
184  string = (*env)->CallObjectMethod(env, exception_class, get_name_id);
185  if ((*env)->ExceptionCheck(env)) {
186  (*env)->ExceptionClear(env);
187  av_log(log_ctx, AV_LOG_ERROR, "Class.getName() threw an exception\n");
188  ret = AVERROR_EXTERNAL;
189  goto done;
190  }
191 
192  if (string) {
193  name = ff_jni_jstring_to_utf_chars(env, string, log_ctx);
194  (*env)->DeleteLocalRef(env, string);
195  string = NULL;
196  }
197 
198  get_message_id = (*env)->GetMethodID(env, exception_class, "getMessage", "()Ljava/lang/String;");
199  if ((*env)->ExceptionCheck(env)) {
200  (*env)->ExceptionClear(env);
201  av_log(log_ctx, AV_LOG_ERROR, "Could not find method java/lang/Throwable.getMessage()\n");
202  ret = AVERROR_EXTERNAL;
203  goto done;
204  }
205 
206  string = (*env)->CallObjectMethod(env, exception, get_message_id);
207  if ((*env)->ExceptionCheck(env)) {
208  (*env)->ExceptionClear(env);
209  av_log(log_ctx, AV_LOG_ERROR, "Throwable.getMessage() threw an exception\n");
210  ret = AVERROR_EXTERNAL;
211  goto done;
212  }
213 
214  if (string) {
215  message = ff_jni_jstring_to_utf_chars(env, string, log_ctx);
216  (*env)->DeleteLocalRef(env, string);
217  string = NULL;
218  }
219 
220  if (name && message) {
221  av_bprintf(&bp, "%s: %s", name, message);
222  } else if (name && !message) {
223  av_bprintf(&bp, "%s occurred", name);
224  } else if (!name && message) {
225  av_bprintf(&bp, "Exception: %s", message);
226  } else {
227  av_log(log_ctx, AV_LOG_WARNING, "Could not retrieve exception name and message\n");
228  av_bprintf(&bp, "Exception occurred");
229  }
230 
231  ret = av_bprint_finalize(&bp, error);
232 done:
233 
234  av_free(name);
235  av_free(message);
236 
237  if (class_class) {
238  (*env)->DeleteLocalRef(env, class_class);
239  }
240 
241  if (exception_class) {
242  (*env)->DeleteLocalRef(env, exception_class);
243  }
244 
245  if (string) {
246  (*env)->DeleteLocalRef(env, string);
247  }
248 
249  return ret;
250 }
251 
252 int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
253 {
254  int ret;
255 
256  jthrowable exception;
257 
258  char *message = NULL;
259 
260  if (!(*(env))->ExceptionCheck((env))) {
261  return 0;
262  }
263 
264  if (!log) {
265  (*(env))->ExceptionClear((env));
266  return -1;
267  }
268 
269  exception = (*env)->ExceptionOccurred(env);
270  (*(env))->ExceptionClear((env));
271 
272  if ((ret = ff_jni_exception_get_summary(env, exception, &message, log_ctx)) < 0) {
273  (*env)->DeleteLocalRef(env, exception);
274  return ret;
275  }
276 
277  (*env)->DeleteLocalRef(env, exception);
278 
279  av_log(log_ctx, AV_LOG_ERROR, "%s\n", message);
280  av_free(message);
281 
282  return -1;
283 }
284 
285 int ff_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
286 {
287  int i, ret = 0;
288  jclass last_clazz = NULL;
289 
290  for (i = 0; jfields_mapping[i].name; i++) {
291  int mandatory = jfields_mapping[i].mandatory;
292  enum FFJniFieldType type = jfields_mapping[i].type;
293 
294  if (type == FF_JNI_CLASS) {
295  jclass clazz;
296 
297  last_clazz = NULL;
298 
299  clazz = (*env)->FindClass(env, jfields_mapping[i].name);
300  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
301  goto done;
302  }
303 
304  last_clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) =
305  global ? (*env)->NewGlobalRef(env, clazz) : clazz;
306 
307  if (global) {
308  (*env)->DeleteLocalRef(env, clazz);
309  }
310 
311  } else {
312 
313  if (!last_clazz) {
314  ret = AVERROR_EXTERNAL;
315  break;
316  }
317 
318  switch(type) {
319  case FF_JNI_FIELD: {
320  jfieldID field_id = (*env)->GetFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
321  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
322  goto done;
323  }
324 
325  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
326  break;
327  }
328  case FF_JNI_STATIC_FIELD: {
329  jfieldID field_id = (*env)->GetStaticFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
330  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
331  goto done;
332  }
333 
334  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
335  break;
336  }
337  case FF_JNI_METHOD: {
338  jmethodID method_id = (*env)->GetMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
339  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
340  goto done;
341  }
342 
343  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
344  break;
345  }
346  case FF_JNI_STATIC_METHOD: {
347  jmethodID method_id = (*env)->GetStaticMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
348  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
349  goto done;
350  }
351 
352  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
353  break;
354  }
355  default:
356  av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
357  ret = AVERROR(EINVAL);
358  goto done;
359  }
360 
361  ret = 0;
362  }
363  }
364 
365 done:
366  if (ret < 0) {
367  /* reset jfields in case of failure so it does not leak references */
368  ff_jni_reset_jfields(env, jfields, jfields_mapping, global, log_ctx);
369  }
370 
371  return ret;
372 }
373 
374 int ff_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
375 {
376  int i;
377 
378  for (i = 0; jfields_mapping[i].name; i++) {
379  enum FFJniFieldType type = jfields_mapping[i].type;
380 
381  switch(type) {
382  case FF_JNI_CLASS: {
383  jclass clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset);
384  if (!clazz)
385  continue;
386 
387  if (global) {
388  (*env)->DeleteGlobalRef(env, clazz);
389  } else {
390  (*env)->DeleteLocalRef(env, clazz);
391  }
392 
393  *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
394  break;
395  }
396  case FF_JNI_FIELD: {
397  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
398  break;
399  }
400  case FF_JNI_STATIC_FIELD: {
401  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
402  break;
403  }
404  case FF_JNI_METHOD: {
405  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
406  break;
407  }
408  case FF_JNI_STATIC_METHOD: {
409  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
410  break;
411  }
412  default:
413  av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
414  }
415  }
416 
417  return 0;
418 }
#define NULL
Definition: coverity.c:32
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
static void copy(const float *p1, float *p2, const int length)
#define pthread_mutex_lock(a)
Definition: ffprobe.c:61
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:101
int ff_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
Definition: ffjni.c:374
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
int offset
Definition: ffjni.h:108
enum FFJniFieldType type
Definition: ffjni.h:107
jstring ff_jni_utf_chars_to_jstring(JNIEnv *env, const char *utf_chars, void *log_ctx)
Definition: ffjni.c:127
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
static void jni_create_pthread_key(void)
Definition: ffjni.c:46
static const char signature[]
Definition: ipmovie.c:615
uint8_t
#define av_log(a,...)
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
#define AVERROR(e)
Definition: error.h:43
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
static pthread_once_t once
Definition: ffjni.c:36
static pthread_key_t current_env
Definition: ffjni.c:35
void * av_jni_get_java_vm(void *log_ctx)
Definition: jni.c:74
char * ff_jni_jstring_to_utf_chars(JNIEnv *env, jstring string, void *log_ctx)
Definition: ffjni.c:97
static void jni_detach_env(void *data)
Definition: ffjni.c:39
int ff_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
Definition: ffjni.c:285
int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
Definition: ffjni.c:252
#define pthread_mutex_unlock(a)
Definition: ffprobe.c:65
static void error(const char *err)
#define PTHREAD_MUTEX_INITIALIZER
Definition: os2threads.h:52
#define AV_BPRINT_SIZE_AUTOMATIC
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:251
static JavaVM * java_vm
Definition: ffjni.c:34
GLint GLenum type
Definition: opengl_enc.c:105
int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
Definition: ffjni.c:141
const char * name
Definition: ffjni.h:104
static pthread_mutex_t lock
Definition: ffjni.c:37
JNIEnv * ff_jni_get_env(void *log_ctx)
Definition: ffjni.c:51
_fmutex pthread_mutex_t
Definition: os2threads.h:49
FFJniFieldType
Definition: ffjni.h:88
int mandatory
Definition: ffjni.h:109
#define PTHREAD_ONCE_INIT
Definition: os2threads.h:67
#define av_free(p)
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:57
static av_always_inline int pthread_once(pthread_once_t *once_control, void(*init_routine)(void))
Definition: os2threads.h:184
const char * name
Definition: opengl_enc.c:103