实现rtsp服务

master
Matthew 3 months ago
parent 98d5e96650
commit 55ffc1c8dc

@ -139,7 +139,11 @@ void TcpClient::onSockConnect(const SockException &ex) {
std::string TcpClient::getIdentifier() const { std::string TcpClient::getIdentifier() const {
if (_id.empty()) { if (_id.empty()) {
static atomic<uint64_t> s_index { 0 }; static atomic<uint64_t> s_index { 0 };
#ifndef DISABLE_RTTI
_id = toolkit::demangle(typeid(*this).name()) + "-" + to_string(++s_index); _id = toolkit::demangle(typeid(*this).name()) + "-" + to_string(++s_index);
#else
_id = toolkit::demangle("TcpClient") + "-" + to_string(++s_index);
#endif
} }
return _id; return _id;
} }

@ -146,7 +146,9 @@ public:
try { try {
CLASS_FUNC_INVOKE(C, *ptr, Destory); CLASS_FUNC_INVOKE(C, *ptr, Destory);
} catch (std::exception &ex){ } catch (std::exception &ex){
#ifndef DISABLE_RTTI
onDestoryException(typeid(C), ex); onDestoryException(typeid(C), ex);
#endif
} }
delete ptr; delete ptr;
}); });
@ -172,7 +174,9 @@ public:
try { try {
CLASS_FUNC_INVOKE(C, *ptr, Destory); CLASS_FUNC_INVOKE(C, *ptr, Destory);
} catch (std::exception &ex){ } catch (std::exception &ex){
#ifndef DISABLE_RTTI
onDestoryException(typeid(C), ex); onDestoryException(typeid(C), ex);
#endif
} }
delete ptr; delete ptr;
}); });
@ -455,7 +459,11 @@ public:
throw std::invalid_argument("Any is empty"); throw std::invalid_argument("Any is empty");
} }
if (safe && !is<T>()) { if (safe && !is<T>()) {
#ifndef DISABLE_RTTI
throw std::invalid_argument("Any::get(): " + demangle(_type->name()) + " unable cast to " + demangle(typeid(T).name())); 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()); return *((T *)_data.get());
} }

@ -226,7 +226,11 @@ toolkit::EventPoller::Ptr MediaSource::getOwnerPoller() {
if (listener) { if (listener) {
return listener->getOwnerPoller(*this); return listener->getOwnerPoller(*this);
} }
#ifndef DISABLE_RTTI
throw std::runtime_error(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller failed: " + getUrl()); 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<MultiMediaSourceMuxer> MediaSource::getMuxer() const { std::shared_ptr<MultiMediaSourceMuxer> MediaSource::getMuxer() const {

@ -80,7 +80,11 @@ public:
virtual bool close(MediaSource &sender) { return false; } virtual bool close(MediaSource &sender) { return false; }
// 获取观看总人数,此函数一般强制重载 [AUTO-TRANSLATED:1da20a10] // 获取观看总人数,此函数一般强制重载 [AUTO-TRANSLATED:1da20a10]
// Get the total number of viewers, this function is generally forced to overload // 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"); } 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] // 通知观看人数变化 [AUTO-TRANSLATED:bad89528]
// Notify the change in the number of viewers // Notify the change in the number of viewers
virtual void onReaderChanged(MediaSource &sender, int size); virtual void onReaderChanged(MediaSource &sender, int size);
@ -92,7 +96,11 @@ public:
virtual float getLossRate(MediaSource &sender, TrackType type) { return -1; } virtual float getLossRate(MediaSource &sender, TrackType type) { return -1; }
// 获取所在线程, 此函数一般强制重载 [AUTO-TRANSLATED:71c99afb] // 获取所在线程, 此函数一般强制重载 [AUTO-TRANSLATED:71c99afb]
// Get the current thread, this function is generally forced to overload // 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"); } 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] // //////////////////////仅供MultiMediaSourceMuxer对象继承//////////////////////// [AUTO-TRANSLATED:6e810d1f]
// //////////////////////Only for MultiMediaSourceMuxer object inheritance//////////////////////// // //////////////////////Only for MultiMediaSourceMuxer object inheritance////////////////////////

@ -3,7 +3,7 @@ apply plugin: 'com.android.application'
// 10,00,000 major-minor-build // 10,00,000 major-minor-build
def AppMajorVersion = 1 def AppMajorVersion = 1
def AppMinorVersion = 0 def AppMinorVersion = 0
def AppBuildNumber = 2 def AppBuildNumber = 3
def AppVersionName = AppMajorVersion + "." + AppMinorVersion + "." + AppBuildNumber def AppVersionName = AppMajorVersion + "." + AppMinorVersion + "." + AppBuildNumber
def AppVersionCode = AppMajorVersion * 100000 + AppMinorVersion * 1000 + AppBuildNumber def AppVersionCode = AppMajorVersion * 100000 + AppMinorVersion * 1000 + AppBuildNumber
@ -13,8 +13,8 @@ android {
defaultConfig { defaultConfig {
applicationId "com.xypower.mplive" applicationId "com.xypower.mplive"
minSdkVersion 21 minSdkVersion 28
targetSdkVersion 28 targetSdkVersion 30
versionCode AppVersionCode versionCode AppVersionCode
versionName AppVersionName versionName AppVersionName

@ -41,6 +41,8 @@ include_directories(${ZLMediaKit_Root}/3rdpart)
include_directories(${ZLMediaKit_Root}/3rdpart/media-server) include_directories(${ZLMediaKit_Root}/3rdpart/media-server)
include_directories(${ZLMediaKit_Root}/3rdpart/ZLToolKit/src) 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) file(GLOB JNI_src_list ${JNI_Root}/*.cpp ${JNI_Root}/*.h)
add_library(mplive SHARED ${JNI_src_list}) add_library(mplive SHARED ${JNI_src_list})

@ -14,6 +14,13 @@
#include "Thread/semaphore.h" #include "Thread/semaphore.h"
#include "Common/config.h" #include "Common/config.h"
#include "Player/MediaPlayer.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" #include "Extension/Frame.h"
using namespace std; using namespace std;
@ -164,6 +171,89 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved){
extern int start_main(int argc,char *argv[]); extern int start_main(int argc,char *argv[]);
std::shared_ptr<TcpServer> startRtmpServer(uint16_t port) {
auto server = std::make_shared<TcpServer>();
server->start<RtmpSession>(port);
InfoL << "RTMP Server listening on port " << port;
return server;
}
std::shared_ptr<TcpServer> startRtspServer(uint16_t port) {
auto server = std::make_shared<TcpServer>();
if (port == 0)
{
port = 554;
}
server->start<RtspSession>(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<MediaPusher>(mediaSource, poller);
std::weak_ptr<MediaSource> 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<MediaPusher> 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){ JNI_API(jboolean, startServer, jstring ini_dir){
string sd_path = stringFromJstring(env,ini_dir); string sd_path = stringFromJstring(env,ini_dir);
string ini_file = sd_path + "/zlmediakit.ini"; string ini_file = sd_path + "/zlmediakit.ini";
@ -175,38 +265,28 @@ JNI_API(jboolean, startServer, jstring ini_dir){
DebugL << "ini file:" << ini_file; DebugL << "ini file:" << ini_file;
thread s_th([sd_path,ini_file,pem_file](){ thread s_th([sd_path,ini_file,pem_file](){
s_tread_id = pthread_self(); Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", LTrace));
try {
//http根目录修改默认路径 // Load the configuration file
mINI::Instance()[Http::kRootPath] = sd_path + "/httpRoot"; // initConfig();
//mp4录制点播根目录修改默认路径
mINI::Instance()[Protocol::kMP4SavePath] = sd_path + "/httpRoot"; // Initialize the event loop
//hls根目录修改默认路径 EventPollerPool::Instance().setPoolSize(4);
mINI::Instance()[Protocol::kHlsSavePath] = sd_path + "/httpRoot";
//替换默认端口号(在配置文件未生成时有效) // Start the RTMP server
mINI::Instance()["http.port"] = 8080; auto rtmpSvr = startRtmpServer(1935);
mINI::Instance()["http.sslport"] = 8443; auto rtspSvr = startRtspServer(554);
mINI::Instance()["rtsp.port"] = 8554;
mINI::Instance()["rtsp.port"] = 0; // Start the RTMP pushers
mINI::Instance()["rtsp.sslport"] = 8332;
mINI::Instance()["rtsp.sslport"] = 0; // startRtmpPushers("", "");
mINI::Instance()["general.enableVhost"] = 0;
for (auto &pr : mINI::Instance()) { // Wait for the signal to exit
//替换hook默认地址
replace(pr.second, "https://127.0.0.1/", "http://127.0.0.1:8080/"); signal(SIGINT, [](int) { sem.post(); });
} signal(SIGTERM, [](int) { sem.post(); });
//默认打开hook sem.wait();
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();
}
}); });
// static onceToken s_token([]{ // static onceToken s_token([]{
@ -216,17 +296,8 @@ JNI_API(jboolean, startServer, jstring ini_dir){
return true; return true;
}; };
extern semaphore g_zl_sem;
JNI_API(void, stopServer) { JNI_API(void, stopServer) {
g_zl_sem.post(); sem.post();
s_tread_id = 0;
#if 0
if(s_tread_id){
pthread_kill(s_tread_id, SIGINT);
s_tread_id = 0;
}
#endif
} }
JNI_API(jlong, createMediaPlayer, jstring url, jobject callback){ JNI_API(jlong, createMediaPlayer, jstring url, jobject callback){

@ -135,6 +135,11 @@ public class MainActivity extends AppCompatActivity implements RtmpHandler.RtmpL
Intent intent = getIntent(); Intent intent = getIntent();
final int autoStart = intent.getIntExtra("autoStart", 1); 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. // restore data.
sp = getSharedPreferences("MpLive", MODE_PRIVATE); sp = getSharedPreferences("MpLive", MODE_PRIVATE);
@ -143,13 +148,13 @@ public class MainActivity extends AppCompatActivity implements RtmpHandler.RtmpL
// initialize url. // initialize url.
final EditText efu = (EditText) findViewById(R.id.url); final EditText efu = (EditText) findViewById(R.id.url);
btnPublish = (Button) findViewById(R.id.publish); btnPublish = (Button) findViewById(R.id.publish);
btnSwitchCamera = (Button) findViewById(R.id.swCam); btnSwitchCamera = (Button) findViewById(R.id.swCam);
btnRecord = (Button) findViewById(R.id.record); btnRecord = (Button) findViewById(R.id.record);
btnSwitchEncoder = (Button) findViewById(R.id.swEnc); btnSwitchEncoder = (Button) findViewById(R.id.swEnc);
btnPause = (Button) findViewById(R.id.pause); btnPause = (Button) findViewById(R.id.pause);
btnPause.setEnabled(false); btnPause.setEnabled(false);
mCameraView = (SrsCameraView) findViewById(R.id.glsurfaceview_camera);
String url = intent.getStringExtra("url"); String url = intent.getStringExtra("url");
if (!TextUtils.isEmpty(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"; rtmpUrl = "rtmp://127.0.0.1/live/0";
} }
efu.setText(rtmpUrl); 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); if (netCamera != 0) {
// mPublisher.startCamera();
mCameraView.setCameraCallbacksHandler(new SrsCameraView.CameraCallbacksHandler() { } else {
@Override if (rotation != -1) {
public void onCameraParameters(Camera.Parameters params) { //设置图像显示方向
Log.e("fsfs", "fsdf"); 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); mCameraView.setCameraCallbacksHandler(new SrsCameraView.CameraCallbacksHandler() {
mHandler.postDelayed(new Runnable() {
@Override @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"; // rtmpUrl = "rtmp://61.169.135.146/live/0";
SharedPreferences.Editor editor = sp.edit(); SharedPreferences.Editor editor = sp.edit();
editor.putString("rtmpUrl", rtmpUrl); editor.putString("rtmpUrl", rtmpUrl);
editor.apply(); editor.apply();
efu.setText(rtmpUrl + "rotation= " + rotation + " cameraid=" + cameraId + " auto=" + autoStart); efu.setText(rtmpUrl + "rotation= " + rotation + " cameraid=" + cameraId + " auto=" + autoStart);
// efu.setText(rtmpUrl + " cameraid=" + cameraId + " auto=" + autoStart); // efu.setText(rtmpUrl + " cameraid=" + cameraId + " auto=" + autoStart);
efu.setText(rtmpUrl); efu.setText(rtmpUrl);
mPublisher.startPublish(rtmpUrl); mPublisher.startPublish(rtmpUrl);
if (btnSwitchEncoder.getText().toString().contentEquals("soft encoder")) { if (btnSwitchEncoder.getText().toString().contentEquals("soft encoder")) {
// Toast.makeText(getApplicationContext(), "Use hard encoder", Toast.LENGTH_SHORT).show(); // Toast.makeText(getApplicationContext(), "Use hard encoder", Toast.LENGTH_SHORT).show();
} else { } else {
Toast.makeText(getApplicationContext(), "Use soft encoder", Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), "Use soft encoder", Toast.LENGTH_SHORT).show();
}
btnPublish.setText("stop");
btnSwitchEncoder.setEnabled(false);
btnPause.setEnabled(true);
} }
btnPublish.setText("stop"); }, 500);
btnSwitchEncoder.setEnabled(false); } else {
btnPause.setEnabled(true); mPublisher.switchCameraFace(cameraId, rotation);
} }
}, 500);
} else {
mPublisher.switchCameraFace(cameraId, rotation);
} }
int autoClose = intent.getIntExtra("autoClose", 0);
if (autoClose != 0) { if (autoClose != 0) {
mHandler.postDelayed(new Runnable() { mHandler.postDelayed(new Runnable() {
@Override @Override

@ -7,7 +7,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { 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 // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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

Loading…
Cancel
Save