FFmpeg
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/error.h"
29 #include "libavutil/log.h"
30 #include "libavutil/mem.h"
31 
32 #include "config.h"
33 #include "jni.h"
34 #include "ffjni.h"
35 
36 static JavaVM *java_vm;
37 static pthread_key_t current_env;
40 
41 static void jni_detach_env(void *data)
42 {
43  if (java_vm) {
44  (*java_vm)->DetachCurrentThread(java_vm);
45  }
46 }
47 
48 static void jni_create_pthread_key(void)
49 {
50  pthread_key_create(&current_env, jni_detach_env);
51 }
52 
53 JNIEnv *ff_jni_get_env(void *log_ctx)
54 {
55  int ret = 0;
56  JNIEnv *env = NULL;
57 
59  if (java_vm == NULL) {
60  java_vm = av_jni_get_java_vm(log_ctx);
61  }
62 
63  if (!java_vm) {
64  av_log(log_ctx, AV_LOG_ERROR, "No Java virtual machine has been registered\n");
65  goto done;
66  }
67 
69 
70  if ((env = pthread_getspecific(current_env)) != NULL) {
71  goto done;
72  }
73 
74  ret = (*java_vm)->GetEnv(java_vm, (void **)&env, JNI_VERSION_1_6);
75  switch(ret) {
76  case JNI_EDETACHED:
77  if ((*java_vm)->AttachCurrentThread(java_vm, &env, NULL) != 0) {
78  av_log(log_ctx, AV_LOG_ERROR, "Failed to attach the JNI environment to the current thread\n");
79  env = NULL;
80  } else {
81  pthread_setspecific(current_env, env);
82  }
83  break;
84  case JNI_OK:
85  break;
86  case JNI_EVERSION:
87  av_log(log_ctx, AV_LOG_ERROR, "The specified JNI version is not supported\n");
88  break;
89  default:
90  av_log(log_ctx, AV_LOG_ERROR, "Failed to get the JNI environment attached to this thread\n");
91  break;
92  }
93 
94 done:
96  return env;
97 }
98 
99 char *ff_jni_jstring_to_utf_chars(JNIEnv *env, jstring string, void *log_ctx)
100 {
101  char *ret = NULL;
102  const char *utf_chars = NULL;
103 
104  jboolean copy = 0;
105 
106  if (!string) {
107  return NULL;
108  }
109 
110  utf_chars = (*env)->GetStringUTFChars(env, string, &copy);
111  if ((*env)->ExceptionCheck(env)) {
112  (*env)->ExceptionClear(env);
113  av_log(log_ctx, AV_LOG_ERROR, "String.getStringUTFChars() threw an exception\n");
114  return NULL;
115  }
116 
117  ret = av_strdup(utf_chars);
118 
119  (*env)->ReleaseStringUTFChars(env, string, utf_chars);
120  if ((*env)->ExceptionCheck(env)) {
121  (*env)->ExceptionClear(env);
122  av_log(log_ctx, AV_LOG_ERROR, "String.releaseStringUTFChars() threw an exception\n");
123  return NULL;
124  }
125 
126  return ret;
127 }
128 
129 jstring ff_jni_utf_chars_to_jstring(JNIEnv *env, const char *utf_chars, void *log_ctx)
130 {
131  jstring ret;
132 
133  ret = (*env)->NewStringUTF(env, utf_chars);
134  if ((*env)->ExceptionCheck(env)) {
135  (*env)->ExceptionClear(env);
136  av_log(log_ctx, AV_LOG_ERROR, "NewStringUTF() threw an exception\n");
137  return NULL;
138  }
139 
140  return ret;
141 }
142 
143 int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
144 {
145  int ret = 0;
146 
147  AVBPrint bp;
148 
149  char *name = NULL;
150  char *message = NULL;
151 
152  jclass class_class = NULL;
153  jmethodID get_name_id = NULL;
154 
155  jclass exception_class = NULL;
156  jmethodID get_message_id = NULL;
157 
158  jstring string = NULL;
159 
161 
162  exception_class = (*env)->GetObjectClass(env, exception);
163  if ((*env)->ExceptionCheck(env)) {
164  (*env)->ExceptionClear(env);
165  av_log(log_ctx, AV_LOG_ERROR, "Could not find Throwable class\n");
167  goto done;
168  }
169 
170  class_class = (*env)->GetObjectClass(env, exception_class);
171  if ((*env)->ExceptionCheck(env)) {
172  (*env)->ExceptionClear(env);
173  av_log(log_ctx, AV_LOG_ERROR, "Could not find Throwable class's class\n");
175  goto done;
176  }
177 
178  get_name_id = (*env)->GetMethodID(env, class_class, "getName", "()Ljava/lang/String;");
179  if ((*env)->ExceptionCheck(env)) {
180  (*env)->ExceptionClear(env);
181  av_log(log_ctx, AV_LOG_ERROR, "Could not find method Class.getName()\n");
183  goto done;
184  }
185 
186  string = (*env)->CallObjectMethod(env, exception_class, get_name_id);
187  if ((*env)->ExceptionCheck(env)) {
188  (*env)->ExceptionClear(env);
189  av_log(log_ctx, AV_LOG_ERROR, "Class.getName() threw an exception\n");
191  goto done;
192  }
193 
194  if (string) {
195  name = ff_jni_jstring_to_utf_chars(env, string, log_ctx);
196  (*env)->DeleteLocalRef(env, string);
197  string = NULL;
198  }
199 
200  get_message_id = (*env)->GetMethodID(env, exception_class, "getMessage", "()Ljava/lang/String;");
201  if ((*env)->ExceptionCheck(env)) {
202  (*env)->ExceptionClear(env);
203  av_log(log_ctx, AV_LOG_ERROR, "Could not find method java/lang/Throwable.getMessage()\n");
205  goto done;
206  }
207 
208  string = (*env)->CallObjectMethod(env, exception, get_message_id);
209  if ((*env)->ExceptionCheck(env)) {
210  (*env)->ExceptionClear(env);
211  av_log(log_ctx, AV_LOG_ERROR, "Throwable.getMessage() threw an exception\n");
213  goto done;
214  }
215 
216  if (string) {
217  message = ff_jni_jstring_to_utf_chars(env, string, log_ctx);
218  (*env)->DeleteLocalRef(env, string);
219  string = NULL;
220  }
221 
222  if (name && message) {
223  av_bprintf(&bp, "%s: %s", name, message);
224  } else if (name && !message) {
225  av_bprintf(&bp, "%s occurred", name);
226  } else if (!name && message) {
227  av_bprintf(&bp, "Exception: %s", message);
228  } else {
229  av_log(log_ctx, AV_LOG_WARNING, "Could not retrieve exception name and message\n");
230  av_bprintf(&bp, "Exception occurred");
231  }
232 
233  ret = av_bprint_finalize(&bp, error);
234 done:
235 
236  av_free(name);
237  av_free(message);
238 
239  if (class_class) {
240  (*env)->DeleteLocalRef(env, class_class);
241  }
242 
243  if (exception_class) {
244  (*env)->DeleteLocalRef(env, exception_class);
245  }
246 
247  if (string) {
248  (*env)->DeleteLocalRef(env, string);
249  }
250 
251  return ret;
252 }
253 
254 int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
255 {
256  int ret;
257 
258  jthrowable exception;
259 
260  char *message = NULL;
261 
262  if (!(*(env))->ExceptionCheck((env))) {
263  return 0;
264  }
265 
266  if (!log) {
267  (*(env))->ExceptionClear((env));
268  return -1;
269  }
270 
271  exception = (*env)->ExceptionOccurred(env);
272  (*(env))->ExceptionClear((env));
273 
274  if ((ret = ff_jni_exception_get_summary(env, exception, &message, log_ctx)) < 0) {
275  (*env)->DeleteLocalRef(env, exception);
276  return ret;
277  }
278 
279  (*env)->DeleteLocalRef(env, exception);
280 
281  av_log(log_ctx, AV_LOG_ERROR, "%s\n", message);
282  av_free(message);
283 
284  return -1;
285 }
286 
287 int ff_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
288 {
289  int i, ret = 0;
290  jclass last_clazz = NULL;
291 
292  for (i = 0; jfields_mapping[i].name; i++) {
293  int mandatory = jfields_mapping[i].mandatory;
294  enum FFJniFieldType type = jfields_mapping[i].type;
295 
296  if (type == FF_JNI_CLASS) {
297  jclass clazz;
298 
299  last_clazz = NULL;
300 
301  clazz = (*env)->FindClass(env, jfields_mapping[i].name);
302  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
303  goto done;
304  }
305 
306  last_clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) =
307  global ? (*env)->NewGlobalRef(env, clazz) : clazz;
308 
309  if (global) {
310  (*env)->DeleteLocalRef(env, clazz);
311  }
312 
313  } else {
314 
315  if (!last_clazz) {
317  break;
318  }
319 
320  switch(type) {
321  case FF_JNI_FIELD: {
322  jfieldID field_id = (*env)->GetFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
323  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
324  goto done;
325  }
326 
327  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
328  break;
329  }
330  case FF_JNI_STATIC_FIELD: {
331  jfieldID field_id = (*env)->GetStaticFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
332  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
333  goto done;
334  }
335 
336  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
337  break;
338  }
339  case FF_JNI_METHOD: {
340  jmethodID method_id = (*env)->GetMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
341  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
342  goto done;
343  }
344 
345  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
346  break;
347  }
348  case FF_JNI_STATIC_METHOD: {
349  jmethodID method_id = (*env)->GetStaticMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
350  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
351  goto done;
352  }
353 
354  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
355  break;
356  }
357  default:
358  av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
359  ret = AVERROR(EINVAL);
360  goto done;
361  }
362 
363  ret = 0;
364  }
365  }
366 
367 done:
368  if (ret < 0) {
369  /* reset jfields in case of failure so it does not leak references */
370  ff_jni_reset_jfields(env, jfields, jfields_mapping, global, log_ctx);
371  }
372 
373  return ret;
374 }
375 
376 int ff_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
377 {
378  int i;
379 
380  for (i = 0; jfields_mapping[i].name; i++) {
381  enum FFJniFieldType type = jfields_mapping[i].type;
382 
383  switch(type) {
384  case FF_JNI_CLASS: {
385  jclass clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset);
386  if (!clazz)
387  continue;
388 
389  if (global) {
390  (*env)->DeleteGlobalRef(env, clazz);
391  } else {
392  (*env)->DeleteLocalRef(env, clazz);
393  }
394 
395  *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
396  break;
397  }
398  case FF_JNI_FIELD: {
399  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
400  break;
401  }
402  case FF_JNI_STATIC_FIELD: {
403  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
404  break;
405  }
406  case FF_JNI_METHOD: {
407  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
408  break;
409  }
410  case FF_JNI_STATIC_METHOD: {
411  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
412  break;
413  }
414  default:
415  av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
416  }
417  }
418 
419  return 0;
420 }
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:31
jni_detach_env
static void jni_detach_env(void *data)
Definition: ffjni.c:41
pthread_mutex_t
_fmutex pthread_mutex_t
Definition: os2threads.h:53
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:186
name
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
FFJniField::type
enum FFJniFieldType type
Definition: ffjni.h:107
jni_create_pthread_key
static void jni_create_pthread_key(void)
Definition: ffjni.c:48
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
message
Definition: api-threadmessage-test.c:46
normalize.log
log
Definition: normalize.py:21
data
const char data[16]
Definition: mxf.c:148
ff_jni_reset_jfields
int ff_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
Definition: ffjni.c:376
ff_jni_exception_get_summary
int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
Definition: ffjni.c:143
AV_BPRINT_SIZE_AUTOMATIC
#define AV_BPRINT_SIZE_AUTOMATIC
FF_JNI_CLASS
@ FF_JNI_CLASS
Definition: ffjni.h:90
FFJniField::offset
int offset
Definition: ffjni.h:108
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
ff_jni_utf_chars_to_jstring
jstring ff_jni_utf_chars_to_jstring(JNIEnv *env, const char *utf_chars, void *log_ctx)
Definition: ffjni.c:129
signature
static const char signature[]
Definition: ipmovie.c:591
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
FFJniFieldType
FFJniFieldType
Definition: ffjni.h:88
current_env
static pthread_key_t current_env
Definition: ffjni.c:37
ff_jni_init_jfields
int ff_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
Definition: ffjni.c:287
FF_JNI_METHOD
@ FF_JNI_METHOD
Definition: ffjni.h:93
NULL
#define NULL
Definition: coverity.c:32
FFJniField
Definition: ffjni.h:102
ff_jni_get_env
JNIEnv * ff_jni_get_env(void *log_ctx)
Definition: ffjni.c:53
java_vm
static JavaVM * java_vm
Definition: ffjni.c:36
pthread_once
static av_always_inline int pthread_once(pthread_once_t *once_control, void(*init_routine)(void))
Definition: os2threads.h:210
pthread_mutex_unlock
#define pthread_mutex_unlock(a)
Definition: ffprobe.c:79
error.h
copy
static void copy(const float *p1, float *p2, const int length)
Definition: vf_vaguedenoiser.c:185
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:240
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
ff_jni_jstring_to_utf_chars
char * ff_jni_jstring_to_utf_chars(JNIEnv *env, jstring string, void *log_ctx)
Definition: ffjni.c:99
FF_JNI_FIELD
@ FF_JNI_FIELD
Definition: ffjni.h:91
bprint.h
log.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:255
ffjni.h
ff_jni_exception_check
int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
Definition: ffjni.c:254
av_jni_get_java_vm
void * av_jni_get_java_vm(void *log_ctx)
Definition: jni.c:74
ret
ret
Definition: filter_design.txt:187
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:99
FF_JNI_STATIC_FIELD
@ FF_JNI_STATIC_FIELD
Definition: ffjni.h:92
lock
static pthread_mutex_t lock
Definition: ffjni.c:39
pthread_once_t
Definition: os2threads.h:66
av_strdup
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:270
mem.h
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
FF_JNI_STATIC_METHOD
@ FF_JNI_STATIC_METHOD
Definition: ffjni.h:94
FFJniField::mandatory
int mandatory
Definition: ffjni.h:109
once
static pthread_once_t once
Definition: ffjni.c:38
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
jni.h
FFJniField::name
const char * name
Definition: ffjni.h:104
PTHREAD_ONCE_INIT
#define PTHREAD_ONCE_INIT
Definition: os2threads.h:71
PTHREAD_MUTEX_INITIALIZER
#define PTHREAD_MUTEX_INITIALIZER
Definition: os2threads.h:56
pthread_mutex_lock
#define pthread_mutex_lock(a)
Definition: ffprobe.c:75