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  (*env)->DeleteLocalRef(env, class_class);
240  (*env)->DeleteLocalRef(env, exception_class);
241  (*env)->DeleteLocalRef(env, string);
242 
243  return ret;
244 }
245 
246 int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
247 {
248  int ret;
249 
250  jthrowable exception;
251 
252  char *message = NULL;
253 
254  if (!(*(env))->ExceptionCheck((env))) {
255  return 0;
256  }
257 
258  if (!log) {
259  (*(env))->ExceptionClear((env));
260  return -1;
261  }
262 
263  exception = (*env)->ExceptionOccurred(env);
264  (*(env))->ExceptionClear((env));
265 
266  if ((ret = ff_jni_exception_get_summary(env, exception, &message, log_ctx)) < 0) {
267  (*env)->DeleteLocalRef(env, exception);
268  return ret;
269  }
270 
271  (*env)->DeleteLocalRef(env, exception);
272 
273  av_log(log_ctx, AV_LOG_ERROR, "%s\n", message);
274  av_free(message);
275 
276  return -1;
277 }
278 
279 int ff_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
280 {
281  int i, ret = 0;
282  jclass last_clazz = NULL;
283 
284  for (i = 0; jfields_mapping[i].name; i++) {
285  int mandatory = jfields_mapping[i].mandatory;
286  enum FFJniFieldType type = jfields_mapping[i].type;
287 
288  if (type == FF_JNI_CLASS) {
289  jclass clazz;
290 
291  last_clazz = NULL;
292 
293  clazz = (*env)->FindClass(env, jfields_mapping[i].name);
294  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
295  goto done;
296  }
297 
298  last_clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) =
299  global ? (*env)->NewGlobalRef(env, clazz) : clazz;
300 
301  if (global) {
302  (*env)->DeleteLocalRef(env, clazz);
303  }
304 
305  } else {
306 
307  if (!last_clazz) {
309  break;
310  }
311 
312  switch(type) {
313  case FF_JNI_FIELD: {
314  jfieldID field_id = (*env)->GetFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
315  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
316  goto done;
317  }
318 
319  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
320  break;
321  }
322  case FF_JNI_STATIC_FIELD: {
323  jfieldID field_id = (*env)->GetStaticFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
324  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
325  goto done;
326  }
327 
328  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
329  break;
330  }
331  case FF_JNI_METHOD: {
332  jmethodID method_id = (*env)->GetMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
333  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
334  goto done;
335  }
336 
337  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
338  break;
339  }
340  case FF_JNI_STATIC_METHOD: {
341  jmethodID method_id = (*env)->GetStaticMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
342  if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
343  goto done;
344  }
345 
346  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
347  break;
348  }
349  default:
350  av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
351  ret = AVERROR(EINVAL);
352  goto done;
353  }
354 
355  ret = 0;
356  }
357  }
358 
359 done:
360  if (ret < 0) {
361  /* reset jfields in case of failure so it does not leak references */
362  ff_jni_reset_jfields(env, jfields, jfields_mapping, global, log_ctx);
363  }
364 
365  return ret;
366 }
367 
368 int ff_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
369 {
370  int i;
371 
372  for (i = 0; jfields_mapping[i].name; i++) {
373  enum FFJniFieldType type = jfields_mapping[i].type;
374 
375  switch(type) {
376  case FF_JNI_CLASS: {
377  jclass clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset);
378  if (!clazz)
379  continue;
380 
381  if (global) {
382  (*env)->DeleteGlobalRef(env, clazz);
383  } else {
384  (*env)->DeleteLocalRef(env, clazz);
385  }
386 
387  *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
388  break;
389  }
390  case FF_JNI_FIELD: {
391  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
392  break;
393  }
394  case FF_JNI_STATIC_FIELD: {
395  *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
396  break;
397  }
398  case FF_JNI_METHOD: {
399  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
400  break;
401  }
402  case FF_JNI_STATIC_METHOD: {
403  *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
404  break;
405  }
406  default:
407  av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
408  }
409  }
410 
411  return 0;
412 }
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:32
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:215
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:108
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
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
message
Definition: api-threadmessage-test.c:47
normalize.log
log
Definition: normalize.py:21
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:368
data
const char data[16]
Definition: mxf.c:149
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:279
FFJniField::offset
size_t offset
Definition: ffjni.h:109
AV_BPRINT_SIZE_AUTOMATIC
#define AV_BPRINT_SIZE_AUTOMATIC
FF_JNI_CLASS
@ FF_JNI_CLASS
Definition: ffjni.h:91
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
once
static pthread_once_t once
Definition: ffjni.c:38
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:209
ff_jni_jstring_to_utf_chars
char * ff_jni_jstring_to_utf_chars(JNIEnv *env, jstring string, void *log_ctx)
Definition: ffjni.c:99
FFJniFieldType
FFJniFieldType
Definition: ffjni.h:89
java_vm
static JavaVM * java_vm
Definition: ffjni.c:36
FF_JNI_METHOD
@ FF_JNI_METHOD
Definition: ffjni.h:94
NULL
#define NULL
Definition: coverity.c:32
FFJniField
Definition: ffjni.h:103
jni_detach_env
static void jni_detach_env(void *data)
Definition: ffjni.c:41
pthread_once
static av_always_inline int pthread_once(pthread_once_t *once_control, void(*init_routine)(void))
Definition: os2threads.h:210
jni_create_pthread_key
static void jni_create_pthread_key(void)
Definition: ffjni.c:48
pthread_mutex_unlock
#define pthread_mutex_unlock(a)
Definition: ffprobe.c:82
error.h
copy
static void copy(const float *p1, float *p2, const int length)
Definition: vf_vaguedenoiser.c:186
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:240
ff_jni_exception_get_summary
int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
Definition: ffjni.c:143
current_env
static pthread_key_t current_env
Definition: ffjni.c:37
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
FF_JNI_FIELD
@ FF_JNI_FIELD
Definition: ffjni.h:92
lock
static pthread_mutex_t lock
Definition: ffjni.c:39
bprint.h
ff_jni_exception_check
int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
Definition: ffjni.c:246
log.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
ffjni.h
av_jni_get_java_vm
void * av_jni_get_java_vm(void *log_ctx)
Definition: jni.c:75
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:93
pthread_once_t
Definition: os2threads.h:66
ff_jni_get_env
JNIEnv * ff_jni_get_env(void *log_ctx)
Definition: ffjni.c:53
av_strdup
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:272
mem.h
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
FF_JNI_STATIC_METHOD
@ FF_JNI_STATIC_METHOD
Definition: ffjni.h:95
FFJniField::mandatory
int mandatory
Definition: ffjni.h:110
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
jni.h
FFJniField::name
const char * name
Definition: ffjni.h:105
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:78