diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index beb3be99..da51d596 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit beb3be990a26b395359c9e1965a1f645267133fa +Subproject commit da51d5969a11e573e2dc4c9846e92cb6521fc726 diff --git a/Android/.gitignore b/Android/.gitignore new file mode 100644 index 00000000..5edb4eeb --- /dev/null +++ b/Android/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/Android/app/.gitignore b/Android/app/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/Android/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/Android/app/build.gradle b/Android/app/build.gradle new file mode 100644 index 00000000..da3ae89b --- /dev/null +++ b/Android/app/build.gradle @@ -0,0 +1,43 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.zlmediakit.demo" + minSdkVersion 19 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + externalNativeBuild { + externalNativeBuild { + cmake { + cppFlags "-std=c++11 -frtti -fexceptions" + } + } + ndk { + abiFilters "armeabi-v7a"//, "arm64-v8a" + } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} diff --git a/Android/app/proguard-rules.pro b/Android/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/Android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/Android/app/src/androidTest/java/com/zlmediakit/demo/ExampleInstrumentedTest.java b/Android/app/src/androidTest/java/com/zlmediakit/demo/ExampleInstrumentedTest.java new file mode 100644 index 00000000..8900124a --- /dev/null +++ b/Android/app/src/androidTest/java/com/zlmediakit/demo/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.zlmediakit.demo; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.zlmediakit.demo", appContext.getPackageName()); + } +} diff --git a/Android/app/src/main/AndroidManifest.xml b/Android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1da6cc39 --- /dev/null +++ b/Android/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/app/src/main/cpp/CMakeLists.txt b/Android/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..c4ba78b7 --- /dev/null +++ b/Android/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,110 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. + +cmake_minimum_required(VERSION 3.4.1) + +#设置生成的so动态库最后输出的路径 +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/libs_export/${ANDROID_ABI}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/libs_export/${ANDROID_ABI}) + +#设置工程源码根目录 +set(ZLMediaKit_Root ${CMAKE_SOURCE_DIR}/../../../../../) +set(JNI_Root ${CMAKE_SOURCE_DIR}) +set(ToolKit_Root ${ZLMediaKit_Root}/3rdpart/ZLToolKit/src) +set(MediaKit_Root ${ZLMediaKit_Root}/src) + +#设置头文件目录 +INCLUDE_DIRECTORIES(${ToolKit_Root}) +INCLUDE_DIRECTORIES(${MediaKit_Root}) +INCLUDE_DIRECTORIES(${JNI_Root}) + +#收集源代码 +file(GLOB ToolKit_src_list ${ToolKit_Root}/*/*.cpp ${ToolKit_Root}/*/*.h ${ToolKit_Root}/*/*.c) +file(GLOB MediaKit_src_list ${MediaKit_Root}/*/*.cpp ${MediaKit_Root}/*/*.h ${MediaKit_Root}/*/*.c) +file(GLOB JNI_src_list ${JNI_Root}/*.cpp ${JNI_Root}/*.h) + +#去除win32的适配代码 +list(REMOVE_ITEM ToolKit_src_list ${ToolKit_Root}/win32/getopt.c) + +set(ENABLE_HLS true) +#默认禁用的库,如果要开启,请指定其路径 +set(ENABLE_OPENSSL false) +set(ENABLE_MYSQL false) +set(ENABLE_MP4V2 false) +set(ENABLE_FAAC false) +set(ENABLE_X264 false) + +#添加两个静态库 +if(ENABLE_HLS) + message(STATUS "ENABLE_HLS defined") + add_definitions(-DENABLE_HLS) + set(MediaServer_Root ${ZLMediaKit_Root}/3rdpart/media-server) + set(LINK_LIB_LIST zlmediakit zltoolkit mpeg) +else() + set(LINK_LIB_LIST zlmediakit zltoolkit) +endif() + +#查找openssl是否安装 +find_package(OpenSSL QUIET) +if (OPENSSL_FOUND AND ENABLE_OPENSSL) + message(STATUS "found library:${OPENSSL_LIBRARIES},ENABLE_OPENSSL defined") + include_directories(${OPENSSL_INCLUDE_DIR}) + add_definitions(-DENABLE_OPENSSL) + list(APPEND LINK_LIB_LIST ${OPENSSL_LIBRARIES}) +endif () + +#查找mysql是否安装 +find_package(MYSQL QUIET) +if (MYSQL_FOUND AND ENABLE_MYSQL) + message(STATUS "found library:${MYSQL_LIBRARIES},ENABLE_MYSQL defined") + include_directories(${MYSQL_INCLUDE_DIR}) + add_definitions(-DENABLE_MYSQL) + list(APPEND LINK_LIB_LIST ${MYSQL_LIBRARIES}) +endif () + +#查找MP4V2是否安装 +find_package(MP4V2 QUIET) +if (MP4V2_FOUND AND ENABLE_MP4V2) + include_directories(${MP4V2_INCLUDE_DIR}) + list(APPEND LINK_LIB_LIST ${MP4V2_LIBRARY}) + add_definitions(-DENABLE_MP4V2) + message(STATUS "found library:${MP4V2_LIBRARY},ENABLE_MP4V2 defined") +endif () + +#查找x264是否安装 +find_package(X264 QUIET) +if (X264_FOUND AND ENABLE_X264) + message(STATUS "found library:${X264_LIBRARIES},ENABLE_X264 defined") + include_directories(${X264_INCLUDE_DIRS}) + add_definitions(-DENABLE_X264) + list(APPEND LINK_LIB_LIST ${X264_LIBRARIES}) +endif () + +#查找faac是否安装 +find_package(FAAC QUIET) +if (FAAC_FOUND AND ENABLE_FAAC) + message(STATUS "found library:${FAAC_LIBRARIES},ENABLE_FAAC defined") + include_directories(${FAAC_INCLUDE_DIR}) + add_definitions(-DENABLE_FAAC) + list(APPEND LINK_LIB_LIST ${FAAC_LIBRARIES}) +endif () + + +#libmpeg +if(ENABLE_HLS) + aux_source_directory(${MediaServer_Root}/libmpeg/include src_mpeg) + aux_source_directory(${MediaServer_Root}/libmpeg/source src_mpeg) + include_directories(${MediaServer_Root}/libmpeg/include) + add_library(mpeg STATIC ${src_mpeg}) +endif() + + +#添加库 +add_library(zltoolkit STATIC ${ToolKit_src_list}) +add_library(zlmediakit STATIC ${MediaKit_src_list}) +add_library(zlmediakit_jni SHARED ${JNI_src_list}) + +#链接 +target_link_libraries(zlmediakit_jni ${LINK_LIB_LIST} log z) \ No newline at end of file diff --git a/Android/app/src/main/cpp/native-lib.cpp b/Android/app/src/main/cpp/native-lib.cpp new file mode 100644 index 00000000..ec89d2fe --- /dev/null +++ b/Android/app/src/main/cpp/native-lib.cpp @@ -0,0 +1,102 @@ +#include +#include +#include "test_server.cpp" + +#define JNI_API(retType,funName,...) extern "C" JNIEXPORT retType Java_com_zlmediakit_jni_ZLMediaKit_##funName(JNIEnv* env, jclass cls,##__VA_ARGS__) + + +string stringFromJstring(JNIEnv *env,jstring jstr){ + if(!env || !jstr){ + WarnL << "invalid args"; + return ""; + } + const char *field_char = env->GetStringUTFChars(jstr, 0); + string ret(field_char,env->GetStringUTFLength(jstr)); + env->ReleaseStringUTFChars(jstr, field_char); + return ret; +} +string stringFromJbytes(JNIEnv *env,jbyteArray jbytes){ + if(!env || !jbytes){ + WarnL << "invalid args"; + return ""; + } + jbyte *bytes = env->GetByteArrayElements(jbytes, 0); + string ret((char *)bytes,env->GetArrayLength(jbytes)); + env->ReleaseByteArrayElements(jbytes,bytes,0); + return ret; +} +string stringFieldFromJava(JNIEnv *env, jobject jdata,jfieldID jid){ + if(!env || !jdata || !jid){ + WarnL << "invalid args"; + return ""; + } + jstring field_str = (jstring)env->GetObjectField(jdata,jid); + auto ret = stringFromJstring(env,field_str); + env->DeleteLocalRef(field_str); + return ret; +} + +string bytesFieldFromJava(JNIEnv *env, jobject jdata,jfieldID jid){ + if(!env || !jdata || !jid){ + WarnL << "invalid args"; + return ""; + } + jbyteArray jbufArray = (jbyteArray)env->GetObjectField(jdata, jid); + string ret = stringFromJbytes(env,jbufArray); + env->DeleteLocalRef(jbufArray); + return ret; +} + +jstring jstringFromString(JNIEnv* env, const char* pat) { + return (jstring)env->NewStringUTF(pat); +} + +jbyteArray jbyteArrayFromString(JNIEnv* env, const char* pat,int len = 0){ + if(len <= 0){ + len = strlen(pat); + } + jbyteArray jarray = env->NewByteArray(len); + env->SetByteArrayRegion(jarray, 0, len, (jbyte *)(pat)); + return jarray; +} + +/* + * 加载动态库 + */ +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { + //设置日志 + Logger::Instance().add(std::make_shared()); + Logger::Instance().setWriter(std::make_shared()); + InfoL; + return JNI_VERSION_1_6; +} +/* + * 卸载动态库 + */ +JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved){ + InfoL; + s_sem.post(); +} + + +JNI_API(jboolean,startDemo,jstring path_to_jni_file){ + string sd_path = stringFromJstring(env,path_to_jni_file); + string jni_file = sd_path + "/zlmediakit.jni"; + + DebugL << "sd_path:" << sd_path; + DebugL << "jni file:" << sd_path; + + static thread s_th([sd_path,jni_file](){ + try { + mINI::Instance()[Http::kRootPath] = mINI::Instance()[Hls::kFilePath] = sd_path + "/httpRoot"; + do_main(jni_file); + }catch (std::exception &ex){ + WarnL << ex.what(); + } + }); + + static onceToken s_token([]{ + s_th.detach(); + }); + return true; +}; diff --git a/Android/app/src/main/cpp/test_server.cpp b/Android/app/src/main/cpp/test_server.cpp new file mode 100644 index 00000000..e4ff8120 --- /dev/null +++ b/Android/app/src/main/cpp/test_server.cpp @@ -0,0 +1,344 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "Util/MD5.h" +#include "Util/File.h" +#include "Util/logger.h" +#include "Util/SSLBox.h" +#include "Util/onceToken.h" +#include "Network/TcpServer.h" +#include "Poller/EventPoller.h" + +#include "Common/config.h" +#include "Rtsp/UDPServer.h" +#include "Rtsp/RtspSession.h" +#include "Rtmp/RtmpSession.h" +#include "Shell/ShellSession.h" +#include "RtmpMuxer/FlvMuxer.h" +#include "Player/PlayerProxy.h" +#include "Http/WebSocketSession.h" + +using namespace std; +using namespace toolkit; +using namespace mediakit; + +namespace mediakit { +////////////HTTP配置/////////// +namespace Http { +#define HTTP_FIELD "http." +#define HTTP_PORT 8080 +const char kPort[] = HTTP_FIELD"port"; +#define HTTPS_PORT 8443 +const char kSSLPort[] = HTTP_FIELD"sslport"; +onceToken token1([](){ + mINI::Instance()[kPort] = HTTP_PORT; + mINI::Instance()[kSSLPort] = HTTPS_PORT; +},nullptr); +}//namespace Http + +////////////SHELL配置/////////// +namespace Shell { +#define SHELL_FIELD "shell." +#define SHELL_PORT 9000 +const char kPort[] = SHELL_FIELD"port"; +onceToken token1([](){ + mINI::Instance()[kPort] = SHELL_PORT; +},nullptr); +} //namespace Shell + +////////////RTSP服务器配置/////////// +namespace Rtsp { +#define RTSP_FIELD "rtsp." +#define RTSP_PORT 8554 +#define RTSPS_PORT 8322 +const char kPort[] = RTSP_FIELD"port"; +const char kSSLPort[] = RTSP_FIELD"sslport"; +onceToken token1([](){ + mINI::Instance()[kPort] = RTSP_PORT; + mINI::Instance()[kSSLPort] = RTSPS_PORT; +},nullptr); + +} //namespace Rtsp + +////////////RTMP服务器配置/////////// +namespace Rtmp { +#define RTMP_FIELD "rtmp." +#define RTMP_PORT 1935 +const char kPort[] = RTMP_FIELD"port"; +onceToken token1([](){ + mINI::Instance()[kPort] = RTMP_PORT; +},nullptr); +} //namespace RTMP +} // namespace mediakit + + +#define REALM "realm_zlmedaikit" +static map s_mapFlvRecorder; +static mutex s_mtxFlvRecorder; +static onceToken s_token([](){ + //监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastOnGetRtspRealm,[](BroadcastOnGetRtspRealmArgs){ + DebugL << "RTSP是否需要鉴权事件:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ; + if(string("1") == args._streamid ){ + // live/1需要认证 + //该流需要认证,并且设置realm + invoker(REALM); + }else{ + //有时我们要查询redis或数据库来判断该流是否需要认证,通过invoker的方式可以做到完全异步 + //该流我们不需要认证 + invoker(""); + } + }); + + //监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastOnRtspAuth,[](BroadcastOnRtspAuthArgs){ + DebugL << "RTSP播放鉴权:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ; + DebugL << "RTSP用户:" << user_name << (must_no_encrypt ? " Base64" : " MD5" )<< " 方式登录"; + string user = user_name; + //假设我们异步读取数据库 + if(user == "test0"){ + //假设数据库保存的是明文 + invoker(false,"pwd0"); + return; + } + + if(user == "test1"){ + //假设数据库保存的是密文 + auto encrypted_pwd = MD5(user + ":" + REALM + ":" + "pwd1").hexdigest(); + invoker(true,encrypted_pwd); + return; + } + if(user == "test2" && must_no_encrypt){ + //假设登录的是test2,并且以base64方式登录,此时我们提供加密密码,那么会导致认证失败 + //可以通过这个方式屏蔽base64这种不安全的加密方式 + invoker(true,"pwd2"); + return; + } + + //其他用户密码跟用户名一致 + invoker(false,user); + }); + + + //监听rtsp/rtmp推流事件,返回结果告知是否有推流权限 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPublish,[](BroadcastMediaPublishArgs){ + DebugL << "推流鉴权:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ; + invoker("");//鉴权成功 + //invoker("this is auth failed message");//鉴权失败 + }); + + //监听rtsp/rtsps/rtmp/http-flv播放事件,返回结果告知是否有播放权限(rtsp通过kBroadcastOnRtspAuth或此事件都可以实现鉴权) + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPlayed,[](BroadcastMediaPlayedArgs){ + DebugL << "播放鉴权:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ; + invoker("");//鉴权成功 + //invoker("this is auth failed message");//鉴权失败 + }); + + //shell登录事件,通过shell可以登录进服务器执行一些命令 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){ + DebugL << "shell login:" << user_name << " " << passwd; + invoker("");//鉴权成功 + //invoker("this is auth failed message");//鉴权失败 + }); + + //监听rtsp、rtmp源注册或注销事件;此处用于测试rtmp保存为flv录像,保存在http根目录下 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaChanged,[](BroadcastMediaChangedArgs){ + if(schema == RTMP_SCHEMA && app == "live"){ + lock_guard lck(s_mtxFlvRecorder); + if(bRegist){ + DebugL << "开始录制RTMP:" << schema << " " << vhost << " " << app << " " << stream; + GET_CONFIG_AND_REGISTER(string,http_root,Http::kRootPath); + auto path = http_root + "/" + vhost + "/" + app + "/" + stream + "_" + to_string(time(NULL)) + ".flv"; + FlvRecorder::Ptr recorder(new FlvRecorder); + try{ + recorder->startRecord(EventPollerPool::Instance().getPoller(),dynamic_pointer_cast(sender.shared_from_this()),path); + s_mapFlvRecorder[vhost + "/" + app + "/" + stream] = recorder; + }catch(std::exception &ex){ + WarnL << ex.what(); + } + }else{ + s_mapFlvRecorder.erase(vhost + "/" + app + "/" + stream); + } + } + }); + + //监听播放失败(未找到特定的流)事件 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastNotFoundStream,[](BroadcastNotFoundStreamArgs){ + /** + * 你可以在这个事件触发时再去拉流,这样就可以实现按需拉流 + * 拉流成功后,ZLMediaKit会把其立即转发给播放器(最大等待时间约为5秒,如果5秒都未拉流成功,播放器会播放失败) + */ + DebugL << "未找到流事件:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ; + }); + + + //监听播放或推流结束时消耗流量事件 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastFlowReport,[](BroadcastFlowReportArgs){ + DebugL << "播放器(推流器)断开连接事件:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs + << "\r\n使用流量:" << totalBytes << " bytes,连接时长:" << totalDuration << "秒" ; + + }); + + +}, nullptr); + +#if !defined(SIGHUP) +#define SIGHUP 1 +#endif + +//设置退出信号处理函数 +static semaphore s_sem; + +int do_main(string ini_file) { + //加载配置文件,如果配置文件不存在就创建一个 + loadIniConfig(ini_file.data()); + + //这里是拉流地址,支持rtmp/rtsp协议,负载必须是H264+AAC + //如果是其他不识别的音视频将会被忽略(譬如说h264+adpcm转发后会去除音频) + auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks1", + "rtmp://live.hkstv.hk.lxdns.com/live/hks2" + //rtsp链接支持输入用户名密码 + /*"rtsp://admin:jzan123456@192.168.0.122/"*/}; + map proxyMap; + int i = 0; + for (auto &url : urlList) { + //PlayerProxy构造函数前两个参数分别为应用名(app),流id(streamId) + //比如说应用为live,流id为0,那么直播地址为: + + //hls地址 : http://127.0.0.1/live/0/hls.m3u8 + //http-flv地址 : http://127.0.0.1/live/0.flv + //rtsp地址 : rtsp://127.0.0.1/live/0 + //rtmp地址 : rtmp://127.0.0.1/live/0 + + //录像地址为(当然vlc不支持这么多级的rtmp url,可以用test_player测试rtmp点播): + //http://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 + //rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 + //rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 + + PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "live", to_string(i).data())); + //指定RTP over TCP(播放rtsp时有效) + (*player)[kRtpType] = Rtsp::RTP_TCP; + //开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试 + player->play(url); + //需要保存PlayerProxy,否则作用域结束就会销毁该对象 + proxyMap.emplace(to_string(i), player); + ++i; + } + + DebugL << "\r\n" + " PlayerProxy构造函数前两个参数分别为应用名(app),流id(streamId)\n" + " 比如说应用为live,流id为0,那么直播地址为:\n" + " hls地址 : http://127.0.0.1/live/0/hls.m3u8\n" + " http-flv地址 : http://127.0.0.1/live/0.flv\n" + " rtsp地址 : rtsp://127.0.0.1/live/0\n" + " rtmp地址 : rtmp://127.0.0.1/live/0"; + + //加载证书,证书包含公钥和私钥 + SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data()); + //信任某个自签名证书 + SSL_Initor::Instance().trustCertificate((exeDir() + "ssl.p12").data()); + //不忽略无效证书证书(例如自签名或过期证书) + SSL_Initor::Instance().ignoreInvalidCertificate(false); + + uint16_t shellPort = mINI::Instance()[Shell::kPort]; + uint16_t rtspPort = mINI::Instance()[Rtsp::kPort]; + uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort]; + uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort]; + uint16_t httpPort = mINI::Instance()[Http::kPort]; + uint16_t httpsPort = mINI::Instance()[Http::kSSLPort]; + + //简单的telnet服务器,可用于服务器调试,但是不能使用23端口,否则telnet上了莫名其妙的现象 + //测试方法:telnet 127.0.0.1 9000 + TcpServer::Ptr shellSrv(new TcpServer()); + TcpServer::Ptr rtspSrv(new TcpServer()); + TcpServer::Ptr rtmpSrv(new TcpServer()); + TcpServer::Ptr httpSrv(new TcpServer()); + + shellSrv->start(shellPort); + rtspSrv->start(rtspPort);//默认554 + rtmpSrv->start(rtmpPort);//默认1935 + //http服务器,支持websocket + httpSrv->start(httpPort);//默认80 + + //如果支持ssl,还可以开启https服务器 + TcpServer::Ptr httpsSrv(new TcpServer()); + //https服务器,支持websocket + httpsSrv->start(httpsPort);//默认443 + + //支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 + TcpServer::Ptr rtspSSLSrv(new TcpServer()); + rtspSSLSrv->start(rtspsPort);//默认322 + + //服务器支持动态切换端口(不影响现有连接) + NoticeCenter::Instance().addListener(ReloadConfigTag,Broadcast::kBroadcastReloadConfig,[&](BroadcastReloadConfigArgs){ + //重新创建服务器 + if(shellPort != mINI::Instance()[Shell::kPort].as()){ + shellPort = mINI::Instance()[Shell::kPort]; + shellSrv->start(shellPort); + InfoL << "重启shell服务器:" << shellPort; + } + if(rtspPort != mINI::Instance()[Rtsp::kPort].as()){ + rtspPort = mINI::Instance()[Rtsp::kPort]; + rtspSrv->start(rtspPort); + InfoL << "重启rtsp服务器" << rtspPort; + } + if(rtmpPort != mINI::Instance()[Rtmp::kPort].as()){ + rtmpPort = mINI::Instance()[Rtmp::kPort]; + rtmpSrv->start(rtmpPort); + InfoL << "重启rtmp服务器" << rtmpPort; + } + if(httpPort != mINI::Instance()[Http::kPort].as()){ + httpPort = mINI::Instance()[Http::kPort]; + httpSrv->start(httpPort); + InfoL << "重启http服务器" << httpPort; + } + if(httpsPort != mINI::Instance()[Http::kSSLPort].as()){ + httpsPort = mINI::Instance()[Http::kSSLPort]; + httpsSrv->start(httpsPort); + InfoL << "重启https服务器" << httpsPort; + } + + if(rtspsPort != mINI::Instance()[Rtsp::kSSLPort].as()){ + rtspsPort = mINI::Instance()[Rtsp::kSSLPort]; + rtspSSLSrv->start(rtspsPort); + InfoL << "重启rtsps服务器" << rtspsPort; + } + }); + + + signal(SIGINT, [](int) { s_sem.post(); });// 设置退出信号 + signal(SIGHUP, [](int) { loadIniConfig(); }); + s_sem.wait(); + + lock_guard lck(s_mtxFlvRecorder); + s_mapFlvRecorder.clear(); + return 0; +} + diff --git a/Android/app/src/main/java/com/zlmediakit/demo/MainActivity.java b/Android/app/src/main/java/com/zlmediakit/demo/MainActivity.java new file mode 100644 index 00000000..a0b0bdd7 --- /dev/null +++ b/Android/app/src/main/java/com/zlmediakit/demo/MainActivity.java @@ -0,0 +1,44 @@ +package com.zlmediakit.demo; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Environment; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.widget.Toast; + +import com.zlmediakit.jni.ZLMediaKit; + +public class MainActivity extends AppCompatActivity { + private static String[] PERMISSIONS_STORAGE = { + "android.permission.READ_EXTERNAL_STORAGE", + "android.permission.WRITE_EXTERNAL_STORAGE", + "android.permission.INTERNET"}; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + boolean permissionSuccess = true; + for(String str : PERMISSIONS_STORAGE){ + int permission = ActivityCompat.checkSelfPermission(this, str); + if (permission != PackageManager.PERMISSION_GRANTED) { + // 没有写的权限,去申请写的权限,会弹出对话框 + ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE,1); + permissionSuccess = false; + break; + } + } + + String ini = Environment.getExternalStoragePublicDirectory("").toString(); + if(permissionSuccess){ + Toast.makeText(this,"你可以修改配置文件再启动:" + ini + "/zlmediakit.jni" ,Toast.LENGTH_LONG).show(); + ZLMediaKit.startDemo(ini); + }else{ + Toast.makeText(this,"请给予我权限,否则无法启动测试!" ,Toast.LENGTH_LONG).show(); + } + } + +} diff --git a/Android/app/src/main/java/com/zlmediakit/jni/ZLMediaKit.java b/Android/app/src/main/java/com/zlmediakit/jni/ZLMediaKit.java new file mode 100644 index 00000000..262dbff3 --- /dev/null +++ b/Android/app/src/main/java/com/zlmediakit/jni/ZLMediaKit.java @@ -0,0 +1,8 @@ +package com.zlmediakit.jni; + +public class ZLMediaKit { + static { + System.loadLibrary("zlmediakit_jni"); + } + static public native boolean startDemo(String sd_path); +} diff --git a/Android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..1f6bb290 --- /dev/null +++ b/Android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/Android/app/src/main/res/drawable/ic_launcher_background.xml b/Android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..0d025f9b --- /dev/null +++ b/Android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/app/src/main/res/layout/activity_main.xml b/Android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..7d8d24e9 --- /dev/null +++ b/Android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..898f3ed5 Binary files /dev/null and b/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..dffca360 Binary files /dev/null and b/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..64ba76f7 Binary files /dev/null and b/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..dae5e082 Binary files /dev/null and b/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..e5ed4659 Binary files /dev/null and b/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..14ed0af3 Binary files /dev/null and b/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..b0907cac Binary files /dev/null and b/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..d8ae0315 Binary files /dev/null and b/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..2c18de9e Binary files /dev/null and b/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..beed3cdd Binary files /dev/null and b/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/Android/app/src/main/res/values/colors.xml b/Android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..69b22338 --- /dev/null +++ b/Android/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #008577 + #00574B + #D81B60 + diff --git a/Android/app/src/main/res/values/strings.xml b/Android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..ad12620a --- /dev/null +++ b/Android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + demo + diff --git a/Android/app/src/main/res/values/styles.xml b/Android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..5885930d --- /dev/null +++ b/Android/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/Android/app/src/test/java/com/zlmediakit/demo/ExampleUnitTest.java b/Android/app/src/test/java/com/zlmediakit/demo/ExampleUnitTest.java new file mode 100644 index 00000000..1bcdb1ad --- /dev/null +++ b/Android/app/src/test/java/com/zlmediakit/demo/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.zlmediakit.demo; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/Android/build.gradle b/Android/build.gradle new file mode 100644 index 00000000..3c23accd --- /dev/null +++ b/Android/build.gradle @@ -0,0 +1,27 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + jcenter() + + } + dependencies { + classpath 'com.android.tools.build:gradle:3.3.1' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/Android/gradle.properties b/Android/gradle.properties new file mode 100644 index 00000000..82618cec --- /dev/null +++ b/Android/gradle.properties @@ -0,0 +1,15 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + + diff --git a/Android/gradle/wrapper/gradle-wrapper.jar b/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f6b961fd Binary files /dev/null and b/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Android/gradle/wrapper/gradle-wrapper.properties b/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..51f504ca --- /dev/null +++ b/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri May 10 12:15:32 CST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip diff --git a/Android/gradlew b/Android/gradlew new file mode 100755 index 00000000..cccdd3d5 --- /dev/null +++ b/Android/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/Android/gradlew.bat b/Android/gradlew.bat new file mode 100644 index 00000000..f9553162 --- /dev/null +++ b/Android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Android/settings.gradle b/Android/settings.gradle new file mode 100644 index 00000000..e7b4def4 --- /dev/null +++ b/Android/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/src/Common/config.cpp b/src/Common/config.cpp index a6b94b92..a35df89e 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -24,10 +24,11 @@ * SOFTWARE. */ -#include #include "Common/config.h" #include "Util/util.h" +#include "Util/logger.h" #include "Util/onceToken.h" +#include "Util/NoticeCenter.h" #include "Network/sockutil.h" using namespace toolkit; @@ -46,6 +47,7 @@ bool loadIniConfig(const char *ini_path){ NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastReloadConfig); return true; }catch (std::exception &ex) { + InfoL << "dump ini file to:" << ini; mINI::Instance().dumpFile(ini); return false; }