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 
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:100
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
GLint GLenum type
Definition: opengl_enc.c:104
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 i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
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
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)
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
#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:102