Add Android File Log

This commit is contained in:
Florian Märkl 2019-10-30 15:19:55 +01:00
commit 84cdbe41dc
8 changed files with 270 additions and 107 deletions

View file

@ -5,6 +5,8 @@ set(CMAKE_CXX_STANDARD 14)
add_library(chiaki-jni SHARED
src/main/cpp/chiaki-jni.c
src/main/cpp/log.h
src/main/cpp/log.c
src/main/cpp/video-decoder.h
src/main/cpp/video-decoder.c
src/main/cpp/audio-decoder.h

View file

@ -33,41 +33,8 @@
#include "video-decoder.h"
#include "audio-decoder.h"
#include "audio-output.h"
#define LOG_TAG "Chiaki"
#define JNI_VERSION JNI_VERSION_1_6
#define BASE_PACKAGE "com/metallic/chiaki/lib"
#define JNI_FCN(name) Java_com_metallic_chiaki_lib_ChiakiNative_##name
#define E (*env)
static void log_cb_android(ChiakiLogLevel level, const char *msg, void *user)
{
int prio;
switch(level)
{
case CHIAKI_LOG_DEBUG:
prio = ANDROID_LOG_DEBUG;
break;
case CHIAKI_LOG_VERBOSE:
prio = ANDROID_LOG_VERBOSE;
break;
case CHIAKI_LOG_INFO:
prio = ANDROID_LOG_INFO;
break;
case CHIAKI_LOG_WARNING:
prio = ANDROID_LOG_ERROR;
break;
case CHIAKI_LOG_ERROR:
prio = ANDROID_LOG_ERROR;
break;
default:
prio = ANDROID_LOG_INFO;
break;
}
__android_log_write(prio, LOG_TAG, msg);
}
#include "log.h"
#include "chiaki-jni.h"
static char *strdup_jni(const char *str)
{
@ -84,7 +51,7 @@ static char *strdup_jni(const char *str)
return r;
}
static jobject jnistr_from_ascii(JNIEnv *env, const char *str)
jobject jnistr_from_ascii(JNIEnv *env, const char *str)
{
if(!str)
return NULL;
@ -121,18 +88,33 @@ static jobject get_kotlin_global_object(JNIEnv *env, const char *id)
}
static ChiakiLog global_log;
static JavaVM *global_vm;
JavaVM *global_vm;
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
global_vm = vm;
chiaki_log_init(&global_log, CHIAKI_LOG_ALL & ~CHIAKI_LOG_VERBOSE, log_cb_android, NULL);
android_chiaki_file_log_init(&global_log, CHIAKI_LOG_ALL & ~CHIAKI_LOG_VERBOSE, NULL);
CHIAKI_LOGI(&global_log, "Loading Chiaki Library");
ChiakiErrorCode err = chiaki_lib_init();
CHIAKI_LOGI(&global_log, "Chiaki Library Init Result: %s\n", chiaki_error_string(err));
return JNI_VERSION;
}
JNIEnv *attach_thread_jni()
{
JNIEnv *env;
int r = (*global_vm)->GetEnv(global_vm, (void **)&env, JNI_VERSION);
if(r == JNI_OK)
return env;
if((*global_vm)->AttachCurrentThread(global_vm, &env, NULL) == 0)
return env;
CHIAKI_LOGE(&global_log, "Failed to get JNIEnv from JavaVM or attach");
return NULL;
}
JNIEXPORT jstring JNICALL JNI_FCN(errorCodeToString)(JNIEnv *env, jobject obj, jint value)
{
return E->NewStringUTF(env, chiaki_error_string((ChiakiErrorCode)value));
@ -160,6 +142,7 @@ JNIEXPORT jobject JNICALL JNI_FCN(videoProfilePreset)(JNIEnv *env, jobject obj,
typedef struct android_chiaki_session_t
{
ChiakiSession session;
ChiakiLog *log;
jobject java_session;
jclass java_session_class;
jmethodID java_session_event_connected_meth;
@ -178,54 +161,6 @@ typedef struct android_chiaki_session_t
void *audio_output;
} AndroidChiakiSession;
static JNIEnv *attach_thread_jni()
{
JNIEnv *env;
int r = (*global_vm)->GetEnv(global_vm, (void **)&env, JNI_VERSION);
if(r == JNI_OK)
return env;
if((*global_vm)->AttachCurrentThread(global_vm, &env, NULL) == 0)
return env;
CHIAKI_LOGE(&global_log, "Failed to get JNIEnv from JavaVM or attach");
return NULL;
}
typedef struct android_chiaki_log_t
{
jobject java_log;
jmethodID java_log_meth;
ChiakiLog log;
} AndroidChiakiLog;
static void android_chiaki_log_cb(ChiakiLogLevel level, const char *msg, void *user)
{
log_cb_android(level, msg, NULL);
AndroidChiakiLog *log = user;
JNIEnv *env = attach_thread_jni();
if(!env)
return;
E->CallVoidMethod(env, log->java_log, log->java_log_meth, (jint)level, jnistr_from_ascii(env, msg));
(*global_vm)->DetachCurrentThread(global_vm);
}
static void android_chiaki_log_init(AndroidChiakiLog *log, JNIEnv *env, jobject java_log)
{
log->java_log = E->NewGlobalRef(env, java_log);
jclass log_class = E->GetObjectClass(env, log->java_log);
log->java_log_meth = E->GetMethodID(env, log_class, "log", "(ILjava/lang/String;)V");
log->log.level_mask = (uint32_t)E->GetIntField(env, log->java_log, E->GetFieldID(env, log_class, "levelMask", "I"));
log->log.cb = android_chiaki_log_cb;
log->log.user = log;
}
static void android_chiaki_log_fini(AndroidChiakiLog *log, JNIEnv *env)
{
E->DeleteGlobalRef(env, log->java_log);
}
static void android_chiaki_event_cb(ChiakiEvent *event, void *user)
{
AndroidChiakiSession *session = user;
@ -263,9 +198,15 @@ static void android_chiaki_event_cb(ChiakiEvent *event, void *user)
(*global_vm)->DetachCurrentThread(global_vm);
}
JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject result, jobject connect_info_obj, jobject java_session)
JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject result, jobject connect_info_obj, jstring log_file_str, jboolean log_verbose, jobject java_session)
{
AndroidChiakiSession *session = NULL;
ChiakiLog *log = malloc(sizeof(ChiakiLog));
const char *log_file = log_file_str ? E->GetStringUTFChars(env, log_file_str, NULL) : NULL;
android_chiaki_file_log_init(log, log_verbose ? CHIAKI_LOG_ALL : (CHIAKI_LOG_ALL & ~CHIAKI_LOG_VERBOSE), log_file);
if(log_file)
E->ReleaseStringUTFChars(env, log_file_str, log_file);
ChiakiErrorCode err = CHIAKI_ERR_SUCCESS;
char *host_str = NULL;
@ -290,7 +231,7 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
if(E->GetArrayLength(env, regist_key_array) != sizeof(connect_info.regist_key))
{
CHIAKI_LOGE(&global_log, "Regist Key passed from Java has invalid length");
CHIAKI_LOGE(log, "Regist Key passed from Java has invalid length");
err = CHIAKI_ERR_INVALID_DATA;
goto beach;
}
@ -300,7 +241,7 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
if(E->GetArrayLength(env, morning_array) != sizeof(connect_info.morning))
{
CHIAKI_LOGE(&global_log, "Morning passed from Java has invalid length");
CHIAKI_LOGE(log, "Morning passed from Java has invalid length");
err = CHIAKI_ERR_INVALID_DATA;
goto beach;
}
@ -320,7 +261,8 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
goto beach;
}
memset(session, 0, sizeof(AndroidChiakiSession));
err = android_chiaki_video_decoder_init(&session->video_decoder, &global_log);
session->log = log;
err = android_chiaki_video_decoder_init(&session->video_decoder, log);
if(err != CHIAKI_ERR_SUCCESS)
{
free(session);
@ -328,7 +270,7 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
goto beach;
}
err = android_chiaki_audio_decoder_init(&session->audio_decoder, &global_log);
err = android_chiaki_audio_decoder_init(&session->audio_decoder, log);
if(err != CHIAKI_ERR_SUCCESS)
{
android_chiaki_video_decoder_fini(&session->video_decoder);
@ -337,14 +279,14 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
goto beach;
}
session->audio_output = android_chiaki_audio_output_new(&global_log);
session->audio_output = android_chiaki_audio_output_new(log);
android_chiaki_audio_decoder_set_cb(&session->audio_decoder, android_chiaki_audio_output_settings, android_chiaki_audio_output_frame, session->audio_output);
err = chiaki_session_init(&session->session, &connect_info, &global_log);
err = chiaki_session_init(&session->session, &connect_info, log);
if(err != CHIAKI_ERR_SUCCESS)
{
CHIAKI_LOGE(&global_log, "JNI ChiakiSession failed to init");
CHIAKI_LOGE(log, "JNI ChiakiSession failed to init");
android_chiaki_video_decoder_fini(&session->video_decoder);
android_chiaki_audio_decoder_fini(&session->audio_decoder);
android_chiaki_audio_output_free(session->audio_output);
@ -376,6 +318,12 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
chiaki_session_set_audio_sink(&session->session, &audio_sink);
beach:
if(!session && log)
{
android_chiaki_file_log_fini(log);
free(log);
}
free(host_str);
E->SetIntField(env, result, E->GetFieldID(env, result_class, "errorCode", "I"), (jint)err);
E->SetLongField(env, result, E->GetFieldID(env, result_class, "ptr", "J"), (jlong)session);
@ -384,9 +332,9 @@ beach:
JNIEXPORT void JNICALL JNI_FCN(sessionFree)(JNIEnv *env, jobject obj, jlong ptr)
{
AndroidChiakiSession *session = (AndroidChiakiSession *)ptr;
CHIAKI_LOGI(&global_log, "Shutting down JNI Session");
if(!session)
return;
CHIAKI_LOGI(session->log, "Shutting down JNI Session");
chiaki_session_fini(&session->session);
free(session);
android_chiaki_video_decoder_fini(&session->video_decoder);
@ -400,21 +348,21 @@ JNIEXPORT void JNICALL JNI_FCN(sessionFree)(JNIEnv *env, jobject obj, jlong ptr)
JNIEXPORT jint JNICALL JNI_FCN(sessionStart)(JNIEnv *env, jobject obj, jlong ptr)
{
AndroidChiakiSession *session = (AndroidChiakiSession *)ptr;
CHIAKI_LOGI(&global_log, "Start JNI Session");
CHIAKI_LOGI(session->log, "Start JNI Session");
return chiaki_session_start(&session->session);
}
JNIEXPORT jint JNICALL JNI_FCN(sessionStop)(JNIEnv *env, jobject obj, jlong ptr)
{
AndroidChiakiSession *session = (AndroidChiakiSession *)ptr;
CHIAKI_LOGI(&global_log, "Stop JNI Session");
CHIAKI_LOGI(session->log, "Stop JNI Session");
return chiaki_session_stop(&session->session);
}
JNIEXPORT jint JNICALL JNI_FCN(sessionJoin)(JNIEnv *env, jobject obj, jlong ptr)
{
AndroidChiakiSession *session = (AndroidChiakiSession *)ptr;
CHIAKI_LOGI(&global_log, "Join JNI Session");
CHIAKI_LOGI(session->log, "Join JNI Session");
return chiaki_session_join(&session->session);
}
@ -651,7 +599,7 @@ JNIEXPORT jint JNICALL JNI_FCN(discoveryServiceWakeup)(JNIEnv *env, jobject obj,
typedef struct android_chiaki_regist_t
{
AndroidChiakiLog log;
AndroidChiakiJNILog log;
ChiakiRegist regist;
jobject java_regist;
@ -709,7 +657,7 @@ static void android_chiaki_regist_cb(ChiakiRegistEvent *event, void *user)
static void android_chiaki_regist_fini_partial(JNIEnv *env, AndroidChiakiRegist *regist)
{
android_chiaki_log_fini(&regist->log, env);
android_chiaki_jni_log_fini(&regist->log, env);
E->DeleteGlobalRef(env, regist->java_regist);
E->DeleteGlobalRef(env, regist->java_regist_event_canceled);
E->DeleteGlobalRef(env, regist->java_regist_event_failed);
@ -728,7 +676,7 @@ JNIEXPORT void JNICALL JNI_FCN(registStart)(JNIEnv *env, jobject obj, jobject re
goto beach;
}
android_chiaki_log_init(&regist->log, env, log_obj);
android_chiaki_jni_log_init(&regist->log, env, log_obj);
regist->java_regist = E->NewGlobalRef(env, java_regist);
regist->java_regist_event_meth = E->GetMethodID(env, E->GetObjectClass(env, regist->java_regist), "event", "(L"BASE_PACKAGE"/RegistEvent;)V");

View file

@ -0,0 +1,33 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_JNI_H
#define CHIAKI_JNI_H
#define JNI_VERSION JNI_VERSION_1_6
#define BASE_PACKAGE "com/metallic/chiaki/lib"
#define JNI_FCN(name) Java_com_metallic_chiaki_lib_ChiakiNative_##name
#define E (*env)
JNIEnv *attach_thread_jni();
jobject jnistr_from_ascii(JNIEnv *env, const char *str);
extern JavaVM *global_vm;
#endif

View file

@ -0,0 +1,142 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#include "log.h"
#include <android/log.h>
#include <string.h>
#include <errno.h>
#include <jni.h>
#include "chiaki-jni.h"
#define LOG_TAG "Chiaki"
void log_cb_android(ChiakiLogLevel level, const char *msg, void *user)
{
int prio;
switch(level)
{
case CHIAKI_LOG_DEBUG:
prio = ANDROID_LOG_DEBUG;
break;
case CHIAKI_LOG_VERBOSE:
prio = ANDROID_LOG_VERBOSE;
break;
case CHIAKI_LOG_INFO:
prio = ANDROID_LOG_INFO;
break;
case CHIAKI_LOG_WARNING:
prio = ANDROID_LOG_ERROR;
break;
case CHIAKI_LOG_ERROR:
prio = ANDROID_LOG_ERROR;
break;
default:
prio = ANDROID_LOG_INFO;
break;
}
__android_log_write(prio, LOG_TAG, msg);
}
static void log_cb_android_file(ChiakiLogLevel level, const char *msg, void *user)
{
log_cb_android(level, msg, user);
FILE *f = user;
if(!f)
return;
switch(level)
{
case CHIAKI_LOG_DEBUG:
fwrite("[D] ", 4, 1, f);
break;
case CHIAKI_LOG_VERBOSE:
fwrite("[V] ", 4, 1, f);
break;
case CHIAKI_LOG_INFO:
fwrite("[I] ", 4, 1, f);
break;
case CHIAKI_LOG_WARNING:
fwrite("[W] ", 4, 1, f);
break;
case CHIAKI_LOG_ERROR:
fwrite("[E] ", 4, 1, f);
break;
default:
fwrite("[?] ", 4, 1, f);
break;
}
fwrite(msg, strlen(msg), 1, f);
fwrite("\n", 1, 1, f);
}
ChiakiErrorCode android_chiaki_file_log_init(ChiakiLog *log, uint32_t level, const char *file)
{
chiaki_log_init(log, level, log_cb_android, NULL);
if(file)
{
FILE *f = fopen(file, "w+");
if(!f)
{
CHIAKI_LOGE(log, "Failed to open log file %s for writing: %s", file, strerror(errno));
return CHIAKI_ERR_UNKNOWN;
}
log->user = f;
log->cb = log_cb_android_file;
}
return CHIAKI_ERR_SUCCESS;
}
void android_chiaki_file_log_fini(ChiakiLog *log)
{
if(log->user)
{
FILE *f = log->user;
fclose(f);
log->user = NULL;
}
}
static void android_chiaki_log_cb(ChiakiLogLevel level, const char *msg, void *user)
{
log_cb_android(level, msg, NULL);
AndroidChiakiJNILog *log = user;
JNIEnv *env = attach_thread_jni();
if(!env)
return;
E->CallVoidMethod(env, log->java_log, log->java_log_meth, (jint)level, jnistr_from_ascii(env, msg));
(*global_vm)->DetachCurrentThread(global_vm);
}
void android_chiaki_jni_log_init(AndroidChiakiJNILog *log, JNIEnv *env, jobject java_log)
{
log->java_log = E->NewGlobalRef(env, java_log);
jclass log_class = E->GetObjectClass(env, log->java_log);
log->java_log_meth = E->GetMethodID(env, log_class, "log", "(ILjava/lang/String;)V");
log->log.level_mask = (uint32_t)E->GetIntField(env, log->java_log, E->GetFieldID(env, log_class, "levelMask", "I"));
log->log.cb = android_chiaki_log_cb;
log->log.user = log;
}
void android_chiaki_jni_log_fini(AndroidChiakiJNILog *log, JNIEnv *env)
{
E->DeleteGlobalRef(env, log->java_log);
}

View file

@ -0,0 +1,38 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_JNI_LOG_H
#define CHIAKI_JNI_LOG_H
#include <chiaki/log.h>
#include <jni.h>
typedef struct android_jni_chiaki_log_t
{
jobject java_log;
jmethodID java_log_meth;
ChiakiLog log;
} AndroidChiakiJNILog;
ChiakiErrorCode android_chiaki_file_log_init(ChiakiLog *log, uint32_t level, const char *file);
void android_chiaki_file_log_fini(ChiakiLog *log);
void android_chiaki_jni_log_init(AndroidChiakiJNILog *log, JNIEnv *env, jobject java_log);
void android_chiaki_jni_log_fini(AndroidChiakiJNILog *log, JNIEnv *env);
#endif

View file

@ -58,7 +58,7 @@ private class ChiakiNative
@JvmStatic external fun quitReasonToString(value: Int): String
@JvmStatic external fun quitReasonIsStopped(value: Int): Boolean
@JvmStatic external fun videoProfilePreset(resolutionPreset: Int, fpsPreset: Int): ConnectVideoProfile
@JvmStatic external fun sessionCreate(result: CreateResult, connectInfo: ConnectInfo, javaSession: Session)
@JvmStatic external fun sessionCreate(result: CreateResult, connectInfo: ConnectInfo, logFile: String?, logVerbose: Boolean, javaSession: Session)
@JvmStatic external fun sessionFree(ptr: Long)
@JvmStatic external fun sessionStart(ptr: Long): Int
@JvmStatic external fun sessionStop(ptr: Long): Int
@ -179,7 +179,7 @@ data class QuitEvent(val reason: QuitReason, val reasonString: String?): Event()
class CreateError(val errorCode: ErrorCode): Exception("Failed to create a native object: $errorCode")
class Session(connectInfo: ConnectInfo)
class Session(connectInfo: ConnectInfo, logFile: String?, logVerbose: Boolean)
{
interface EventCallback
{
@ -192,7 +192,7 @@ class Session(connectInfo: ConnectInfo)
init
{
val result = ChiakiNative.CreateResult(0, 0)
ChiakiNative.sessionCreate(result, connectInfo, this)
ChiakiNative.sessionCreate(result, connectInfo, logFile, logVerbose, this)
val errorCode = ErrorCode(result.errorCode)
if(!errorCode.isSuccess)
throw CreateError(errorCode)

View file

@ -32,7 +32,7 @@ data class StreamStateCreateError(val error: CreateError): StreamState()
data class StreamStateQuit(val reason: QuitReason, val reasonString: String?): StreamState()
data class StreamStateLoginPinRequest(val pinIncorrect: Boolean): StreamState()
class StreamSession(val connectInfo: ConnectInfo, val input: StreamInput)
class StreamSession(val connectInfo: ConnectInfo, val logVerbose: Boolean, val input: StreamInput)
{
var session: Session? = null
private set
@ -69,7 +69,7 @@ class StreamSession(val connectInfo: ConnectInfo, val input: StreamInput)
return
try
{
val session = Session(connectInfo)
val session = Session(connectInfo, null, logVerbose) // TODO: log file
_state.value = StreamStateConnecting
session.eventCallback = this::eventCallback
session.start()

View file

@ -29,7 +29,7 @@ class StreamViewModel(val preferences: Preferences, val connectInfo: ConnectInfo
{
private var _session: StreamSession? = null
val input = StreamInput(preferences)
val session = StreamSession(connectInfo, input)
val session = StreamSession(connectInfo, preferences.logVerbose, input)
private var _onScreenControlsEnabled = MutableLiveData<Boolean>(preferences.onScreenControlsEnabled)
val onScreenControlsEnabled: LiveData<Boolean> get() = _onScreenControlsEnabled