MobileFFmpeg Android API  2.0
log.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 Taner Sener
3  *
4  * This file is part of MobileFFmpeg.
5  *
6  * MobileFFmpeg is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * MobileFFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "log.h"
21 
23 static JavaVM *globalVm;
24 
26 static jclass logClass;
27 
29 static jmethodID logMethod;
30 
32 static int pipeFd[2];
33 
35 static pthread_t logThread;
36 
38 static int logThreadEnabled = 1;
39 
41 const char *logClassName = "com/arthenica/mobileffmpeg/Log";
42 
44 JNINativeMethod logMethods[] = {
45  {"startNativeCollector", "()I", (void*) Java_com_arthenica_mobileffmpeg_Log_startNativeCollector},
46  {"stopNativeCollector", "()I", (void*) Java_com_arthenica_mobileffmpeg_Log_stopNativeCollector}
47 };
48 
56 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
57  JNIEnv *env;
58  if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
59  LOGE("OnLoad failed to GetEnv for class %s.", logClassName);
60  return JNI_FALSE;
61  }
62 
63  (*env)->GetJavaVM(env, &globalVm);
64 
65  jclass clazz = (*env)->FindClass(env, logClassName);
66  if (clazz == NULL) {
67  LOGE("OnLoad failed to FindClass %s.", logClassName);
68  return JNI_FALSE;
69  }
70 
71  logMethod = (*env)->GetStaticMethodID(env, clazz, "log", "([B)V");
72  if (logMethod == NULL) {
73  LOGE("OnLoad thread failed to GetMethodID for %s.", "log");
74  (*globalVm)->DetachCurrentThread(globalVm);
75  return JNI_FALSE;
76  }
77 
78  if ((*env)->RegisterNatives(env, clazz, logMethods, 2) < 0) {
79  LOGE("OnLoad failed to RegisterNatives for class %s.", logClassName);
80  return JNI_FALSE;
81  }
82 
83  logClass = (jclass) ((*env)->NewGlobalRef(env, clazz));
84 
85  return JNI_VERSION_1_6;
86 }
87 
91 static void *logThreadFunction() {
92  int readSize;
93  char buffer[512];
94 
95  JNIEnv *env;
96  jint getEnvRc = (*globalVm)->GetEnv(globalVm, (void**) &env, JNI_VERSION_1_6);
97  if (getEnvRc != JNI_OK) {
98  if (getEnvRc != JNI_EDETACHED) {
99  LOGE("Log thread failed to GetEnv for class %s with rc %d.", logClassName, getEnvRc);
100  return JNI_FALSE;
101  }
102 
103  if ((*globalVm)->AttachCurrentThread(globalVm, &env, NULL) != 0) {
104  LOGE("Log thread failed to AttachCurrentThread for class %s.", logClassName);
105  return JNI_FALSE;
106  }
107  }
108 
109  LOGI("Native log thread started.");
110 
111  while(logThreadEnabled && ((readSize = (int)read(pipeFd[0], buffer, sizeof(buffer) - 1)) > 0)) {
112  if (readSize > 0) {
113  if (buffer[readSize - 1] == '\n') {
114  readSize--;
115  }
116  buffer[readSize] = 0; /* add null-terminator */
117 
118  jbyteArray byteArray = (jbyteArray) (*env)->NewByteArray(env, readSize);
119  (*env)->SetByteArrayRegion(env, byteArray, 0, readSize, (jbyte *)buffer);
120  (*env)->CallStaticVoidMethod(env, logClass, logMethod, byteArray);
121  (*env)->DeleteLocalRef(env, byteArray);
122  }
123  }
124 
125  (*globalVm)->DetachCurrentThread(globalVm);
126 
127  LOGI("Native log thread stopped.");
128 
129  return 0;
130 }
131 
140 JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Log_startNativeCollector(JNIEnv *env, jobject object) {
141 
142  /* make stdout line-buffered and stderr unbuffered */
143  setvbuf(stdout, 0, _IOLBF, 0);
144  setvbuf(stderr, 0, _IONBF, 0);
145 
146  /* create the pipe and redirect stdout and stderr */
147  pipe(pipeFd);
148  dup2(pipeFd[1], 1);
149  dup2(pipeFd[1], 2);
150 
151  /* spawn the logging thread */
152  int rc = pthread_create(&logThread, 0, logThreadFunction, 0);
153  if (rc != 0) {
154  LOGE("Failed to create native log thread (rc=%d).", rc);
155  return rc;
156  }
157 
158  return 0;
159 }
160 
169 JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Log_stopNativeCollector(JNIEnv *env, jobject object) {
170  logThreadEnabled = 0;
171 
172  LOGI("Stopping native log thread\n");
173 
174  return 0;
175 }
jint JNI_OnLoad(JavaVM *vm, void *reserved)
Definition: log.c:56
static pthread_t logThread
Definition: log.c:35
static jmethodID logMethod
Definition: log.c:29
JNINativeMethod logMethods[]
Definition: log.c:44
static JavaVM * globalVm
Definition: log.c:23
static jclass logClass
Definition: log.c:26
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Log_stopNativeCollector(JNIEnv *env, jobject object)
Definition: log.c:169
static int pipeFd[2]
Definition: log.c:32
#define LOGE(...)
Definition: log.h:45
const char * logClassName
Definition: log.c:41
#define LOGI(...)
Definition: log.h:39
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Log_startNativeCollector(JNIEnv *env, jobject object)
Definition: log.c:140
static int logThreadEnabled
Definition: log.c:38
static void * logThreadFunction()
Definition: log.c:91