You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

419 lines
12KB

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