添加android测试程序
@ -1 +1 @@
|
||||
Subproject commit beb3be990a26b395359c9e1965a1f645267133fa
|
||||
Subproject commit da51d5969a11e573e2dc4c9846e92cb6521fc726
|
10
Android/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
1
Android/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
43
Android/app/build.gradle
Normal file
@ -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'
|
||||
}
|
21
Android/app/proguard-rules.pro
vendored
Normal file
@ -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
|
@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@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());
|
||||
}
|
||||
}
|
25
Android/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.zlmediakit.demo">
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
110
Android/app/src/main/cpp/CMakeLists.txt
Normal file
@ -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)
|
102
Android/app/src/main/cpp/native-lib.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#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<ConsoleChannel>());
|
||||
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||
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;
|
||||
};
|
344
Android/app/src/main/cpp/test_server.cpp
Normal file
@ -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 <map>
|
||||
#include <signal.h>
|
||||
#include <iostream>
|
||||
|
||||
#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<string,FlvRecorder::Ptr> 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<mutex> 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<RtmpMediaSource>(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<string, PlayerProxy::Ptr> 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<ShellSession>(shellPort);
|
||||
rtspSrv->start<RtspSession>(rtspPort);//默认554
|
||||
rtmpSrv->start<RtmpSession>(rtmpPort);//默认1935
|
||||
//http服务器,支持websocket
|
||||
httpSrv->start<EchoWebSocketSession>(httpPort);//默认80
|
||||
|
||||
//如果支持ssl,还可以开启https服务器
|
||||
TcpServer::Ptr httpsSrv(new TcpServer());
|
||||
//https服务器,支持websocket
|
||||
httpsSrv->start<SSLEchoWebSocketSession>(httpsPort);//默认443
|
||||
|
||||
//支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问
|
||||
TcpServer::Ptr rtspSSLSrv(new TcpServer());
|
||||
rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort);//默认322
|
||||
|
||||
//服务器支持动态切换端口(不影响现有连接)
|
||||
NoticeCenter::Instance().addListener(ReloadConfigTag,Broadcast::kBroadcastReloadConfig,[&](BroadcastReloadConfigArgs){
|
||||
//重新创建服务器
|
||||
if(shellPort != mINI::Instance()[Shell::kPort].as<uint16_t>()){
|
||||
shellPort = mINI::Instance()[Shell::kPort];
|
||||
shellSrv->start<ShellSession>(shellPort);
|
||||
InfoL << "重启shell服务器:" << shellPort;
|
||||
}
|
||||
if(rtspPort != mINI::Instance()[Rtsp::kPort].as<uint16_t>()){
|
||||
rtspPort = mINI::Instance()[Rtsp::kPort];
|
||||
rtspSrv->start<RtspSession>(rtspPort);
|
||||
InfoL << "重启rtsp服务器" << rtspPort;
|
||||
}
|
||||
if(rtmpPort != mINI::Instance()[Rtmp::kPort].as<uint16_t>()){
|
||||
rtmpPort = mINI::Instance()[Rtmp::kPort];
|
||||
rtmpSrv->start<RtmpSession>(rtmpPort);
|
||||
InfoL << "重启rtmp服务器" << rtmpPort;
|
||||
}
|
||||
if(httpPort != mINI::Instance()[Http::kPort].as<uint16_t>()){
|
||||
httpPort = mINI::Instance()[Http::kPort];
|
||||
httpSrv->start<EchoWebSocketSession>(httpPort);
|
||||
InfoL << "重启http服务器" << httpPort;
|
||||
}
|
||||
if(httpsPort != mINI::Instance()[Http::kSSLPort].as<uint16_t>()){
|
||||
httpsPort = mINI::Instance()[Http::kSSLPort];
|
||||
httpsSrv->start<SSLEchoWebSocketSession>(httpsPort);
|
||||
InfoL << "重启https服务器" << httpsPort;
|
||||
}
|
||||
|
||||
if(rtspsPort != mINI::Instance()[Rtsp::kSSLPort].as<uint16_t>()){
|
||||
rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
|
||||
rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort);
|
||||
InfoL << "重启rtsps服务器" << rtspsPort;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
signal(SIGINT, [](int) { s_sem.post(); });// 设置退出信号
|
||||
signal(SIGHUP, [](int) { loadIniConfig(); });
|
||||
s_sem.wait();
|
||||
|
||||
lock_guard<mutex> lck(s_mtxFlvRecorder);
|
||||
s_mapFlvRecorder.clear();
|
||||
return 0;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.zlmediakit.jni;
|
||||
|
||||
public class ZLMediaKit {
|
||||
static {
|
||||
System.loadLibrary("zlmediakit_jni");
|
||||
}
|
||||
static public native boolean startDemo(String sd_path);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
170
Android/app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
19
Android/app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sample_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
BIN
Android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 15 KiB |
6
Android/app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
</resources>
|
3
Android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">demo</string>
|
||||
</resources>
|
11
Android/app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
27
Android/build.gradle
Normal file
@ -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
|
||||
}
|
15
Android/gradle.properties
Normal file
@ -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
|
||||
|
||||
|
BIN
Android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
Android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -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
|
172
Android/gradlew
vendored
Executable file
@ -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" "$@"
|
84
Android/gradlew.bat
vendored
Normal file
@ -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
|
1
Android/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
include ':app'
|
@ -24,10 +24,11 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <Util/NoticeCenter.h>
|
||||
#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;
|
||||
}
|
||||
|