diff --git a/ZLMediaKit/3rdpart/ZLToolKit/src/Network/TcpClient.cpp b/ZLMediaKit/3rdpart/ZLToolKit/src/Network/TcpClient.cpp index 6de5fb7..d8e8494 100644 --- a/ZLMediaKit/3rdpart/ZLToolKit/src/Network/TcpClient.cpp +++ b/ZLMediaKit/3rdpart/ZLToolKit/src/Network/TcpClient.cpp @@ -139,7 +139,11 @@ void TcpClient::onSockConnect(const SockException &ex) { std::string TcpClient::getIdentifier() const { if (_id.empty()) { static atomic s_index { 0 }; + #ifndef DISABLE_RTTI _id = toolkit::demangle(typeid(*this).name()) + "-" + to_string(++s_index); + #else + _id = toolkit::demangle("TcpClient") + "-" + to_string(++s_index); + #endif } return _id; } diff --git a/ZLMediaKit/3rdpart/ZLToolKit/src/Util/util.h b/ZLMediaKit/3rdpart/ZLToolKit/src/Util/util.h index 319fa83..7e92545 100644 --- a/ZLMediaKit/3rdpart/ZLToolKit/src/Util/util.h +++ b/ZLMediaKit/3rdpart/ZLToolKit/src/Util/util.h @@ -146,7 +146,9 @@ public: try { CLASS_FUNC_INVOKE(C, *ptr, Destory); } catch (std::exception &ex){ + #ifndef DISABLE_RTTI onDestoryException(typeid(C), ex); + #endif } delete ptr; }); @@ -172,7 +174,9 @@ public: try { CLASS_FUNC_INVOKE(C, *ptr, Destory); } catch (std::exception &ex){ + #ifndef DISABLE_RTTI onDestoryException(typeid(C), ex); + #endif } delete ptr; }); @@ -455,7 +459,11 @@ public: throw std::invalid_argument("Any is empty"); } if (safe && !is()) { + #ifndef DISABLE_RTTI throw std::invalid_argument("Any::get(): " + demangle(_type->name()) + " unable cast to " + demangle(typeid(T).name())); + #else + throw std::invalid_argument("Any::get(): " + demangle(_type->name()) + " unable cast"); + #endif } return *((T *)_data.get()); } diff --git a/ZLMediaKit/src/Common/MediaSource.cpp b/ZLMediaKit/src/Common/MediaSource.cpp index 14c18d1..ec793da 100644 --- a/ZLMediaKit/src/Common/MediaSource.cpp +++ b/ZLMediaKit/src/Common/MediaSource.cpp @@ -226,7 +226,11 @@ toolkit::EventPoller::Ptr MediaSource::getOwnerPoller() { if (listener) { return listener->getOwnerPoller(*this); } + #ifndef DISABLE_RTTI throw std::runtime_error(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller failed: " + getUrl()); + #else + throw std::runtime_error(toolkit::demangle("MediaSource") + "::getOwnerPoller failed: " + getUrl()); + #endif } std::shared_ptr MediaSource::getMuxer() const { diff --git a/ZLMediaKit/src/Common/MediaSource.h b/ZLMediaKit/src/Common/MediaSource.h index abb5420..8ebd39a 100644 --- a/ZLMediaKit/src/Common/MediaSource.h +++ b/ZLMediaKit/src/Common/MediaSource.h @@ -80,7 +80,11 @@ public: virtual bool close(MediaSource &sender) { return false; } // 获取观看总人数,此函数一般强制重载 [AUTO-TRANSLATED:1da20a10] // Get the total number of viewers, this function is generally forced to overload + #ifndef DISABLE_RTTI virtual int totalReaderCount(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::totalReaderCount not implemented"); } + #else + virtual int totalReaderCount(MediaSource &sender) { throw NotImplemented(toolkit::demangle("MediaSourceEvent") + "::totalReaderCount not implemented"); } + #endif // 通知观看人数变化 [AUTO-TRANSLATED:bad89528] // Notify the change in the number of viewers virtual void onReaderChanged(MediaSource &sender, int size); @@ -92,7 +96,11 @@ public: virtual float getLossRate(MediaSource &sender, TrackType type) { return -1; } // 获取所在线程, 此函数一般强制重载 [AUTO-TRANSLATED:71c99afb] // Get the current thread, this function is generally forced to overload + #ifndef DISABLE_RTTI virtual toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller not implemented"); } + #else + virtual toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller not implemented"); } + #endif // //////////////////////仅供MultiMediaSourceMuxer对象继承//////////////////////// [AUTO-TRANSLATED:6e810d1f] // //////////////////////Only for MultiMediaSourceMuxer object inheritance//////////////////////// diff --git a/app/build.gradle b/app/build.gradle index f21c93e..2806bd0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'com.android.application' // 10,00,000 major-minor-build def AppMajorVersion = 1 def AppMinorVersion = 0 -def AppBuildNumber = 2 +def AppBuildNumber = 3 def AppVersionName = AppMajorVersion + "." + AppMinorVersion + "." + AppBuildNumber def AppVersionCode = AppMajorVersion * 100000 + AppMinorVersion * 1000 + AppBuildNumber @@ -13,8 +13,8 @@ android { defaultConfig { applicationId "com.xypower.mplive" - minSdkVersion 21 - targetSdkVersion 28 + minSdkVersion 28 + targetSdkVersion 30 versionCode AppVersionCode versionName AppVersionName diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 6ce7a09..5f80227 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -41,6 +41,8 @@ include_directories(${ZLMediaKit_Root}/3rdpart) include_directories(${ZLMediaKit_Root}/3rdpart/media-server) include_directories(${ZLMediaKit_Root}/3rdpart/ZLToolKit/src) +message(WARNING " include path: ${MK_LINK_LIBRARIES}") + #收集源代码添加动态库 file(GLOB JNI_src_list ${JNI_Root}/*.cpp ${JNI_Root}/*.h) add_library(mplive SHARED ${JNI_src_list}) diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 8fe85ff..dd43425 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -14,6 +14,13 @@ #include "Thread/semaphore.h" #include "Common/config.h" #include "Player/MediaPlayer.h" +#include "Network/TcpServer.h" +#include "Network/sockutil.h" +#include "Http/HttpSession.h" +#include "Rtmp/RtmpSession.h" +#include "Rtsp/RtspSession.h" +#include "Rtmp/RtmpPusher.h" +#include "Pusher/MediaPusher.h" #include "Extension/Frame.h" using namespace std; @@ -164,6 +171,89 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved){ extern int start_main(int argc,char *argv[]); +std::shared_ptr startRtmpServer(uint16_t port) { + auto server = std::make_shared(); + server->start(port); + + InfoL << "RTMP Server listening on port " << port; + return server; +} + +std::shared_ptr startRtspServer(uint16_t port) { + auto server = std::make_shared(); + if (port == 0) + { + port = 554; + } + server->start(port); + + InfoL << "RTSP Server listening on port " << port; + return server; +} + +void startRtmpPushers(const std::string& url, const std::string& targetUrl) { + + MediaInfo mediaInfo(url); + auto mediaSource = MediaSource::find(mediaInfo); + if (!mediaSource) { + // 文件不存在 [AUTO-TRANSLATED:f97c8ce8] + // File does not exist + WarnL << url << " not existed: "; + return; + } + + auto poller = EventPollerPool::Instance().getPoller(); + auto pusher = std::make_shared(mediaSource, poller); + + std::weak_ptr weak_src = mediaSource; + // src用完了,可以直接置空,防止main函数持有它(MP4Reader持有它即可) [AUTO-TRANSLATED:52e6393a] + // src is used up, can be set to empty directly, to prevent the main function from holding it (MP4Reader holds it) + mediaSource = nullptr; + + std::weak_ptr weak_pusher = pusher; + + pusher->setOnShutdown([poller, url, weak_pusher, weak_src](const SockException &ex) { + if (!weak_src.lock()) { + // 媒体注销导致的推流中断,不在重试推流 [AUTO-TRANSLATED:625b3e1a] + // Media cancellation causes push interruption, no retry push + WarnL << "MediaSource released:" << ex << ", publish stopped"; + return; + } + WarnL << "Server connection is closed:" << ex << ", republish after 2 seconds"; + // 重新推流, 2秒后重试 [AUTO-TRANSLATED:f8a261a3] + // Repush, retry after 2 seconds + poller->doDelayTask(2 * 1000, [weak_pusher, url]() { + if (auto strong_push = weak_pusher.lock()) { + strong_push->publish(url); + } + return 0; + }); + }); + + // 设置发布结果处理逻辑 [AUTO-TRANSLATED:ce9de055] + // Set the publish result handling logic + pusher->setOnPublished([poller, weak_pusher, url](const SockException &ex) { + if (!ex) { + InfoL << "Publish success, please play with player:" << url; + return; + } + + WarnL << "Publish fail:" << ex << ", republish after 2 seconds"; + // 如果发布失败,就重试 [AUTO-TRANSLATED:b37fd4aa] + // If the publish fails, retry + poller->doDelayTask(2 * 1000, [weak_pusher, url]() { + if (auto strong_push = weak_pusher.lock()) { + strong_push->publish(url); + } + return 0; + }); + }); + + pusher->publish(targetUrl); +} + +static semaphore sem; + JNI_API(jboolean, startServer, jstring ini_dir){ string sd_path = stringFromJstring(env,ini_dir); string ini_file = sd_path + "/zlmediakit.ini"; @@ -175,38 +265,28 @@ JNI_API(jboolean, startServer, jstring ini_dir){ DebugL << "ini file:" << ini_file; thread s_th([sd_path,ini_file,pem_file](){ - s_tread_id = pthread_self(); - try { - //http根目录修改默认路径 - mINI::Instance()[Http::kRootPath] = sd_path + "/httpRoot"; - //mp4录制点播根目录修改默认路径 - mINI::Instance()[Protocol::kMP4SavePath] = sd_path + "/httpRoot"; - //hls根目录修改默认路径 - mINI::Instance()[Protocol::kHlsSavePath] = sd_path + "/httpRoot"; - //替换默认端口号(在配置文件未生成时有效) - mINI::Instance()["http.port"] = 8080; - mINI::Instance()["http.sslport"] = 8443; - mINI::Instance()["rtsp.port"] = 8554; - mINI::Instance()["rtsp.port"] = 0; - mINI::Instance()["rtsp.sslport"] = 8332; - mINI::Instance()["rtsp.sslport"] = 0; - mINI::Instance()["general.enableVhost"] = 0; - for (auto &pr : mINI::Instance()) { - //替换hook默认地址 - replace(pr.second, "https://127.0.0.1/", "http://127.0.0.1:8080/"); - } - //默认打开hook - mINI::Instance()["hook.enable"] = 0; - //默认打开http api调试 - mINI::Instance()["api.apiDebug"] = 1; - - int argc = 5; - const char *argv[] = {"", "-c", ini_file.data(), "-s", pem_file.data()}; - start_main(argc, (char **) argv); - s_tread_id = 0; - } catch (std::exception &ex) { - WarnL << ex.what(); - } + Logger::Instance().add(std::make_shared("ConsoleChannel", LTrace)); + + // Load the configuration file + // initConfig(); + + // Initialize the event loop + EventPollerPool::Instance().setPoolSize(4); + + // Start the RTMP server + auto rtmpSvr = startRtmpServer(1935); + auto rtspSvr = startRtspServer(554); + + // Start the RTMP pushers + + // startRtmpPushers("", ""); + + // Wait for the signal to exit + + signal(SIGINT, [](int) { sem.post(); }); + signal(SIGTERM, [](int) { sem.post(); }); + sem.wait(); + }); // static onceToken s_token([]{ @@ -216,17 +296,8 @@ JNI_API(jboolean, startServer, jstring ini_dir){ return true; }; -extern semaphore g_zl_sem; - JNI_API(void, stopServer) { - g_zl_sem.post(); - s_tread_id = 0; -#if 0 - if(s_tread_id){ - pthread_kill(s_tread_id, SIGINT); - s_tread_id = 0; - } -#endif + sem.post(); } JNI_API(jlong, createMediaPlayer, jstring url, jobject callback){ diff --git a/app/src/main/java/com/xypower/mplive/MainActivity.java b/app/src/main/java/com/xypower/mplive/MainActivity.java index 42c7a66..42a1044 100644 --- a/app/src/main/java/com/xypower/mplive/MainActivity.java +++ b/app/src/main/java/com/xypower/mplive/MainActivity.java @@ -135,6 +135,11 @@ public class MainActivity extends AppCompatActivity implements RtmpHandler.RtmpL Intent intent = getIntent(); final int autoStart = intent.getIntExtra("autoStart", 1); + final int netCamera = intent.getIntExtra("netCamera", 0); + final int vendor = intent.getIntExtra("vendor", 0); + final int disableLocalServer = intent.getIntExtra("disableLocalServer", 0); + final int rotation = intent.getIntExtra("rotation", -1); + int autoClose = intent.getIntExtra("autoClose", 0); // restore data. sp = getSharedPreferences("MpLive", MODE_PRIVATE); @@ -143,13 +148,13 @@ public class MainActivity extends AppCompatActivity implements RtmpHandler.RtmpL // initialize url. final EditText efu = (EditText) findViewById(R.id.url); - btnPublish = (Button) findViewById(R.id.publish); btnSwitchCamera = (Button) findViewById(R.id.swCam); btnRecord = (Button) findViewById(R.id.record); btnSwitchEncoder = (Button) findViewById(R.id.swEnc); btnPause = (Button) findViewById(R.id.pause); btnPause.setEnabled(false); + mCameraView = (SrsCameraView) findViewById(R.id.glsurfaceview_camera); String url = intent.getStringExtra("url"); if (!TextUtils.isEmpty(url)) { @@ -160,70 +165,76 @@ public class MainActivity extends AppCompatActivity implements RtmpHandler.RtmpL rtmpUrl = "rtmp://127.0.0.1/live/0"; } efu.setText(rtmpUrl); - mCameraView = (SrsCameraView) findViewById(R.id.glsurfaceview_camera); - final int rotation = intent.getIntExtra("rotation", -1); - if (rotation != -1) { - //设置图像显示方向 - mCameraView.setPreviewOrientation(rotation); - } - cameraData = mCameraView.getCameraData(); - int size = cameraData.size(); - if (size == 0) { - Toast.makeText(getApplicationContext(), "没有查询到摄像头", Toast.LENGTH_SHORT).show(); - } - mPublisher = new SrsPublisher(mCameraView); - mPublisher.setEncodeHandler(new SrsEncodeHandler(this)); - mPublisher.setRtmpHandler(new RtmpHandler(this)); - mPublisher.setRecordHandler(new SrsRecordHandler(this)); - - mPublisher.setPreviewResolution(mWidth, mHeight);//设置预览宽高 - mPublisher.setOutputResolution(mHeight, mWidth); // 这里要和preview反过来 - mPublisher.setVideoHDMode(); + if (autoStart != 0) { + if (disableLocalServer == 0) { + startRTMPServer(); + } + } - cameraId = intent.getIntExtra("cameraId", 0); -// mPublisher.startCamera(); + if (netCamera != 0) { - mCameraView.setCameraCallbacksHandler(new SrsCameraView.CameraCallbacksHandler() { - @Override - public void onCameraParameters(Camera.Parameters params) { - Log.e("fsfs", "fsdf"); + } else { + if (rotation != -1) { + //设置图像显示方向 + mCameraView.setPreviewOrientation(rotation); } - }); + cameraData = mCameraView.getCameraData(); + int size = cameraData.size(); + if (size == 0) { + Toast.makeText(getApplicationContext(), "没有查询到摄像头", Toast.LENGTH_SHORT).show(); + } + mPublisher = new SrsPublisher(mCameraView); + mPublisher.setEncodeHandler(new SrsEncodeHandler(this)); + mPublisher.setRtmpHandler(new RtmpHandler(this)); + mPublisher.setRecordHandler(new SrsRecordHandler(this)); + mPublisher.setPreviewResolution(mWidth, mHeight);//设置预览宽高 + mPublisher.setOutputResolution(mHeight, mWidth); // 这里要和preview反过来 - if (autoStart != 0) { + mPublisher.setVideoHDMode(); - startRTMPServer(); + cameraId = intent.getIntExtra("cameraId", 0); +// mPublisher.startCamera(); - mPublisher.switchCameraFace(cameraId, rotation); - mHandler.postDelayed(new Runnable() { + mCameraView.setCameraCallbacksHandler(new SrsCameraView.CameraCallbacksHandler() { @Override - public void run() { + public void onCameraParameters(Camera.Parameters params) { + Log.e("fsfs", "fsdf"); + } + }); + + if (autoStart != 0) { + + mPublisher.switchCameraFace(cameraId, rotation); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { // rtmpUrl = "rtmp://61.169.135.146/live/0"; - SharedPreferences.Editor editor = sp.edit(); - editor.putString("rtmpUrl", rtmpUrl); - editor.apply(); - efu.setText(rtmpUrl + "rotation= " + rotation + " cameraid=" + cameraId + " auto=" + autoStart); - // efu.setText(rtmpUrl + " cameraid=" + cameraId + " auto=" + autoStart); - efu.setText(rtmpUrl); - mPublisher.startPublish(rtmpUrl); - if (btnSwitchEncoder.getText().toString().contentEquals("soft encoder")) { - // Toast.makeText(getApplicationContext(), "Use hard encoder", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(getApplicationContext(), "Use soft encoder", Toast.LENGTH_SHORT).show(); + SharedPreferences.Editor editor = sp.edit(); + editor.putString("rtmpUrl", rtmpUrl); + editor.apply(); + efu.setText(rtmpUrl + "rotation= " + rotation + " cameraid=" + cameraId + " auto=" + autoStart); + // efu.setText(rtmpUrl + " cameraid=" + cameraId + " auto=" + autoStart); + efu.setText(rtmpUrl); + mPublisher.startPublish(rtmpUrl); + if (btnSwitchEncoder.getText().toString().contentEquals("soft encoder")) { + // Toast.makeText(getApplicationContext(), "Use hard encoder", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getApplicationContext(), "Use soft encoder", Toast.LENGTH_SHORT).show(); + } + btnPublish.setText("stop"); + btnSwitchEncoder.setEnabled(false); + btnPause.setEnabled(true); } - btnPublish.setText("stop"); - btnSwitchEncoder.setEnabled(false); - btnPause.setEnabled(true); - } - }, 500); - } else { - mPublisher.switchCameraFace(cameraId, rotation); + }, 500); + } else { + mPublisher.switchCameraFace(cameraId, rotation); + } } - int autoClose = intent.getIntExtra("autoClose", 0); + if (autoClose != 0) { mHandler.postDelayed(new Runnable() { @Override diff --git a/build.gradle b/build.gradle index 0bda327..eaed045 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:7.2.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 220ca0e..316bab6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-6.5-all.zip +distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.5-bin.zip