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/log.h"
29 #include "libavutil/mem.h"
30 
31 #include "config.h"
32 #include "jni.h"
33 #include "ffjni.h"
34 
35 static JavaVM *java_vm;
36 static pthread_key_t current_env;
39 
40 static void jni_detach_env(void *data)
41 {
42  if (java_vm) {
43  (*java_vm)->DetachCurrentThread(java_vm);
44  }
45 }
46 
47 static void jni_create_pthread_key(void)
48 {
49  pthread_key_create(&current_env, jni_detach_env);
50 }
51 
52 JNIEnv *ff_jni_get_env(void *log_ctx)
53 {
54  int ret = 0;
55  JNIEnv *env = NULL;
56 
58  if (java_vm == NULL) {
59  java_vm = av_jni_get_java_vm(log_ctx);
60  }
61 
62  if (!java_vm) {
63  av_log(log_ctx, AV_LOG_ERROR, "No Java virtual machine has been registered\n");
64  goto done;
65  }
66 
68 
69  if ((env = pthread_getspecific(current_env)) != NULL) {
70  goto done;
71  }
72 
73  ret = (*java_vm)->GetEnv(java_vm, (void **)&env, JNI_VERSION_1_6);
74  switch(ret) {
75  case JNI_EDETACHED:
76  if ((*java_vm)->AttachCurrentThread(java_vm, &env, NULL) != 0) {
77  av_log(log_ctx, AV_LOG_ERROR, "Failed to attach the JNI environment to the current thread\n");
78  env = NULL;
79  } else {
80  pthread_setspecific(current_env, env);
81  }
82  break;
83  case JNI_OK:
84  break;
85  case JNI_EVERSION:
86  av_log(log_ctx, AV_LOG_ERROR, "The specified JNI version is not supported\n");
87  break;
88  default:
89  av_log(log_ctx, AV_LOG_ERROR, "Failed to get the JNI environment attached to this thread\n");
90  break;
91  }
92 
93 done:
95  return env;
96 }
97 
98 char *ff_jni_jstring_to_utf_chars(JNIEnv *env, jstring string, void *log_ctx)
99 {
100  char *ret = NULL;
101  const char *utf_chars = NULL;
102 
103  jboolean copy = 0;
104 
105  if (!string) {
106  return NULL;
107  }
108 
109  utf_chars = (*env)->GetStringUTFChars(env, string, &copy);
110  if ((*env)->ExceptionCheck(env)) {
111  (*env)->ExceptionClear(env);
112  av_log(log_ctx, AV_LOG_ERROR, "String.getStringUTFChars() threw an exception\n");
113  return NULL;
114  }
115 
116  ret = av_strdup(utf_chars);
117 
118  (*env)->ReleaseStringUTFChars(env, string, utf_chars);
119  if ((*env)->ExceptionCheck(env)) {
120  (*env)->ExceptionClear(env);
121  av_log(log_ctx, AV_LOG_ERROR, "String.releaseStringUTFChars() threw an exception\n");
122  return NULL;
123  }
124 
125  return ret;
126 }
127 
128 jstring ff_jni_utf_chars_to_jstring(JNIEnv *env, const char *utf_chars, void *log_ctx)
129 {
130  jstring ret;
131 
132  ret = (*env)->NewStringUTF(env, utf_chars);
133  if ((*env)->ExceptionCheck(env)) {
134  (*env)->ExceptionClear(env);
135  av_log(log_ctx, AV_LOG_ERROR, "NewStringUTF() threw an exception\n");
136  return NULL;
137  }
138 
139  return ret;
140 }
141 
142 int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
143 {
144  int ret = 0;
145 
146  AVBPrint bp;
147 
148  char *name = NULL;
149  char *message = NULL;
150 
151  jclass class_class = NULL;
152  jmethodID get_name_id = NULL;
153 
154  jclass exception_class = NULL;
155  jmethodID get_message_id = NULL;
156 
157  jstring string = NULL;
158 
160 
161  exception_class = (*env)->GetObjectClass(env, exception);
162  if ((*env)->ExceptionCheck(env)) {
163  (*env)->ExceptionClear(env);
164  av_log(log_ctx, AV_LOG_ERROR, "Could not find Throwable class\n");
166  goto done;
167  }
168 
169  class_class = (*env)->GetObjectClass(env, exception_class);
170  if ((*env)->ExceptionCheck(env)) {
171  (*env)->ExceptionClear(env);
172  av_log(log_ctx, AV_LOG_ERROR, "Could not find Throwable class's class\n");
174  goto done;
175  }
176 
177  get_name_id = (*env)->GetMethodID(env, class_class, "getName", "()Ljava/lang/String;");
178  if ((*env)->ExceptionCheck(env)) {
179  (*env)->ExceptionClear(env);
180  av_log(log_ctx, AV_LOG_ERROR, "Could not find method Class.getName()\n");
182  goto done;
183  }
184 
185  string = (*env)->CallObjectMethod(env, exception_class, get_name_id);
186  if ((*env)->ExceptionCheck(env)) {
187  (*env)->ExceptionClear(env);
188  av_log(log_ctx, AV_LOG_ERROR, "Class.getName() threw an exception\n");
190  goto done;
191  }
192 
193  if (string) {
194  name = ff_jni_jstring_to_utf_chars(env, string, log_ctx);
195  (*env)->DeleteLocalRef(env, string);
196  string = NULL;
197  }
198 
199  get_message_id = (*env)->GetMethodID(env, exception_class, "getMessage", "()Ljava/lang/String;");
200  if ((*env)->ExceptionCheck(env)) {
201  (*env)->ExceptionClear(env);
202  av_log(log_ctx, AV_LOG_ERROR, "Could not find method java/lang/Throwable.getMessage()\n");
204  goto done;
205  }
206 
207  string = (*env)->CallObjectMethod(env, exception, get_message_id);
208  if ((*env)->ExceptionCheck(env)) {
209  (*env)->ExceptionClear(env);
210  av_log(log_ctx, AV_LOG_ERROR, "Throwable.getMessage() threw an exception\n");
212  goto done;
213  }
214 
215  if (string) {
216  message = ff_jni_jstring_to_utf_chars(env, string, log_ctx);
217  (*env)->DeleteLocalRef(env, string);
218  string = NULL;
219  }
220 
221  if (name && message) {
222  av_bprintf(&bp, "%s: %s", name, message);
223  } else if (name && !message) {
224  av_bprintf(&bp, "%s occurred", name);
225  } else if (!name && message) {
226  av_bprintf(&bp, "Exception: %s", message);
227  } else {
228  av_log(log_ctx, AV_LOG_WARNING, "Could not retrieve exception name and message\n");
229  av_bprintf(&bp, "Exception occurred");
230  }
231 
232  ret = av_bprint_finalize(&bp, error);
233 done:
234 
235  av_free(name);
236  av_free(message);
237 
238  if (class_class) {
239  (*env)->DeleteLocalRef(env, class_class);
240  }
241 
242  if (exception_class) {
243  (*env)->DeleteLocalRef(env, exception_class);
244  }
245 
246  if (string) {
247  (*env)->DeleteLocalRef(env, string);
248  }
249 
250  return ret;
251 }
252 
253 int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
254 {
255  int ret;
256 
257  jthrowable exception;
258 
259  char *message = NULL;
260 
261  if (!(*(env))->ExceptionCheck((env))) {
262  return 0;
263  }
264 
265  if (!log) {
266  (*(env))->ExceptionClear((env));
267  return -1;
268  }
269 
270  exception = (*env)->ExceptionOccurred(env);
271  (*(env))->ExceptionClear((env));
272 
273  if ((ret = ff_jni_exception_get_summary(env, exception, &message, log_ctx)) < 0) {
274  (*env)->DeleteLocalRef(env, exception);
275  return ret;
276  }
277 
278  (*env)->DeleteLocalRef(env, exception);
279 
280  av_log(log_ctx, AV_LOG_ERROR, "%s\n", message);
281  av_free(message);
282 
283  return -1;
284 }
285 
286 int ff_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
287 {
288  int i, ret = 0;
289  jclass last_clazz = NULL;
290 
291  for (i = 0; jfields_mapping[i].name; i++) {
292  int mandatory = jfields_mapping[i].mandatory;
293  enum FFJniFieldType type = jfields_mapping[i].type;
294 
295  if (type == FF_JNI_CLASS) {
296  jclass clazz;
297 
298  last_clazz = NULL;
299 
300  clazz = (*env)->FindClass(env, jfields_mapping[i].name);
301  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
302  goto done;
303  }
304 
305  last_clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) =
306  global ? (*env)->NewGlobalRef(env, clazz) : clazz;
307 
308  if (global) {
309  (*env)->DeleteLocalRef(env, clazz);
310  }
311 
312  } else {
313 
314  if (!last_clazz) {
316  break;
317  }
318 
319  switch(type) {
320  case FF_JNI_FIELD: {
321  jfieldID field_id = (*env)->GetFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
322  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
323  goto done;
324  }
325 
326  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
327  break;
328  }
329  case FF_JNI_STATIC_FIELD: {
330  jfieldID field_id = (*env)->GetStaticFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
331  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
332  goto done;
333  }
334 
335  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
336  break;
337  }
338  case FF_JNI_METHOD: {
339  jmethodID method_id = (*env)->GetMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
340  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
341  goto done;
342  }
343 
344  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
345  break;
346  }
347  case FF_JNI_STATIC_METHOD: {
348  jmethodID method_id = (*env)->GetStaticMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
349  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
350  goto done;
351  }
352 
353  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
354  break;
355  }
356  default:
357  av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
358  ret = AVERROR(EINVAL);
359  goto done;
360  }
361 
362  ret = 0;
363  }
364  }
365 
366 done:
367  if (ret < 0) {
368  /* reset jfields in case of failure so it does not leak references */
369  ff_jni_reset_jfields(env, jfields, jfields_mapping, global, log_ctx);
370  }
371 
372  return ret;
373 }
374 
375 int ff_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
376 {
377  int i;
378 
379  for (i = 0; jfields_mapping[i].name; i++) {
380  enum FFJniFieldType type = jfields_mapping[i].type;
381 
382  switch(type) {
383  case FF_JNI_CLASS: {
384  jclass clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset);
385  if (!clazz)
386  continue;
387 
388  if (global) {
389  (*env)->DeleteGlobalRef(env, clazz);
390  } else {
391  (*env)->DeleteLocalRef(env, clazz);
392  }
393 
394  *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
395  break;
396  }
397  case FF_JNI_FIELD: {
398  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
399  break;
400  }
401  case FF_JNI_STATIC_FIELD: {
402  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
403  break;
404  }
405  case FF_JNI_METHOD: {
406  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
407  break;
408  }
409  case FF_JNI_STATIC_METHOD: {
410  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
411  break;
412  }
413  default:
414  av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
415  }
416  }
417 
418  return 0;
419 }
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:40
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:47
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
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
data
const char data[16]
Definition: mxf.c:146
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:375
ff_jni_exception_get_summary
int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
Definition: ffjni.c:142
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:128
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:36
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:286
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:52
java_vm
static JavaVM * java_vm
Definition: ffjni.c:35
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:77
copy
static void copy(const float *p1, float *p2, const int length)
Definition: vf_vaguedenoiser.c:187
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:98
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:269
ffjni.h
ff_jni_exception_check
int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
Definition: ffjni.c:253
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:94
FF_JNI_STATIC_FIELD
@ FF_JNI_STATIC_FIELD
Definition: ffjni.h:92
lock
static pthread_mutex_t lock
Definition: ffjni.c:38
pthread_once_t
Definition: os2threads.h:66
av_strdup
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:280
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:37
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:73