FFmpeg
Loading...
Searching...
No Matches
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
36static JavaVM *java_vm;
37static pthread_key_t current_env;
40
41static void jni_detach_env(void *data)
42{
43 if (java_vm) {
44 (*java_vm)->DetachCurrentThread(java_vm);
45 }
46}
47
48static void jni_create_pthread_key(void)
49{
50 pthread_key_create(&current_env, jni_detach_env);
51}
52
53JNIEnv *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
94done:
96 return env;
97}
98
99char *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
129jstring 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
143int 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");
166 ret = AVERROR_EXTERNAL;
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");
174 ret = AVERROR_EXTERNAL;
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");
182 ret = AVERROR_EXTERNAL;
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");
190 ret = AVERROR_EXTERNAL;
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");
204 ret = AVERROR_EXTERNAL;
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");
212 ret = AVERROR_EXTERNAL;
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);
234done:
235
236 av_free(name);
238
239 (*env)->DeleteLocalRef(env, class_class);
240 (*env)->DeleteLocalRef(env, exception_class);
241 (*env)->DeleteLocalRef(env, string);
242
243 return ret;
244}
245
246int 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);
275
276 return -1;
277}
278
279int 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) {
308 ret = AVERROR_EXTERNAL;
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 }
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
359done:
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
368int 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 }
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}
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition bprint.c:122
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition bprint.c:69
AVBPrint public header.
#define AV_BPRINT_SIZE_AUTOMATIC
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define NULL
Definition coverity.c:32
error code definitions
FFJniFieldType
Definition ffjni.h:89
@ FF_JNI_CLASS
Definition ffjni.h:91
@ FF_JNI_FIELD
Definition ffjni.h:92
@ FF_JNI_STATIC_FIELD
Definition ffjni.h:93
@ FF_JNI_METHOD
Definition ffjni.h:94
@ FF_JNI_STATIC_METHOD
Definition ffjni.h:95
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition bprint.c:235
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
cl_device_type type
static const char signature[]
Definition ipmovie.c:592
void * av_jni_get_java_vm(void *log_ctx)
Definition jni.c:75
unsigned offset
Definition libaomenc.c:763
static pthread_mutex_t lock
Definition ffjni.c:39
static void jni_create_pthread_key(void)
Definition ffjni.c:48
int ff_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
Definition ffjni.c:279
jstring ff_jni_utf_chars_to_jstring(JNIEnv *env, const char *utf_chars, void *log_ctx)
Definition ffjni.c:129
int ff_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
Definition ffjni.c:368
char * ff_jni_jstring_to_utf_chars(JNIEnv *env, jstring string, void *log_ctx)
Definition ffjni.c:99
static void jni_detach_env(void *data)
Definition ffjni.c:41
int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
Definition ffjni.c:246
JNIEnv * ff_jni_get_env(void *log_ctx)
Definition ffjni.c:53
static JavaVM * java_vm
Definition ffjni.c:36
int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
Definition ffjni.c:143
static pthread_once_t once
Definition ffjni.c:38
static pthread_key_t current_env
Definition ffjni.c:37
Memory handling functions.
const char data[16]
Definition mxf.c:149
#define av_strdup(s)
Definition ops_static.c:55
static av_always_inline int pthread_mutex_lock(pthread_mutex_t *mutex)
Definition os2threads.h:119
#define PTHREAD_ONCE_INIT
Definition os2threads.h:71
#define PTHREAD_MUTEX_INITIALIZER
Definition os2threads.h:56
static av_always_inline int pthread_once(pthread_once_t *once_control, void(*init_routine)(void))
Definition os2threads.h:210
_fmutex pthread_mutex_t
Definition os2threads.h:53
static av_always_inline int pthread_mutex_unlock(pthread_mutex_t *mutex)
Definition os2threads.h:126
const char * name
Definition qsvenc.c:142
int mandatory
Definition ffjni.h:110
const char * name
Definition ffjni.h:105
enum FFJniFieldType type
Definition ffjni.h:108
#define av_free(p)
#define av_log(a,...)
static void error(const char *err)
static void copy(const float *p1, float *p2, const int length)