Merge pull request #1 from xiongziliang/master

同步
This commit is contained in:
tsingeye 2019-11-19 14:20:12 +08:00 committed by GitHub
commit e2d548284a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 182 additions and 51 deletions

25
.github/workflows/ccpp.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: C/C++ CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: 下载submodule源码
run: git submodule update --init
- name: apt-get安装依赖库(非必选)
run: sudo apt-get install -y cmake libmysqlclient-dev libssl-dev libx264-dev libfaac-dev libmp4v2-dev libsdl-dev libavcodec-dev libavutil-dev
- name: 编译
run: mkdir -p linux_build && cd linux_build && cmake .. && make -j4
- name: 运行MediaServer
run: pwd && cd release/linux/Debug && sudo ./MediaServer -d &

@ -1 +1 @@
Subproject commit 73eb9077e19e473c643708cf586517bacaa16302
Subproject commit 628d3b2527f63b54a5eb38b9e9973254d4a2192b

View File

@ -117,6 +117,12 @@
- Apple OSX(Darwin), both 32 and 64bits.
- All hardware with x86/x86_64/arm/mips cpu.
- Windows.
## How to build
It is recommended to compile on Ubuntu or MacOScompiling on windows is cumbersome, and some features are not compiled by default.
### Before build
- **You must use git to clone the complete code. Do not download the source code by downloading zip package. Otherwise, the sub-module code will not be downloaded by default.You can do it like this:**
```
git clone https://github.com/zlmediakit/ZLMediaKit.git
@ -124,12 +130,6 @@ cd ZLMediaKit
git submodule update --init
```
## How to build
It is recommended to compile on Ubuntu or MacOScompiling on windows is cumbersome, and some features are not compiled by default.
### Build on linux
- My environment

View File

@ -128,6 +128,9 @@
## 编译要求
- 编译器支持C++11GCC4.8/Clang3.3/VC2015或以上
- cmake3.2或以上
## 编译前必看!!!
- **必须使用git下载完整的代码不要使用下载zip包的方式下载源码否则子模块代码默认不下载你可以像以下这样操作:**
```
git clone https://github.com/zlmediakit/ZLMediaKit.git

View File

@ -9,7 +9,7 @@ secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
#FFmpeg可执行程序绝对路径
bin=/usr/local/bin/ffmpeg
#FFmpeg拉流再推流的命令模板通过该模板可以设置再编码的一些参数
cmd=%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
#FFmpeg日志的路径如果置空则不生成FFmpeg日志
#可以为相对(相对于本可执行程序目录)或绝对路径
log=./ffmpeg/ffmpeg.log

View File

@ -38,7 +38,7 @@ const char kLog[] = FFmpeg_FIELD"log";
onceToken token([]() {
mINI::Instance()[kBin] = trim(System::execute("which ffmpeg"));
mINI::Instance()[kCmd] = "%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kCmd] = "%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log";
});
}
@ -48,7 +48,6 @@ FFmpegSource::FFmpegSource() {
}
FFmpegSource::~FFmpegSource() {
NoticeCenter::Instance().delListener(this, Broadcast::kBroadcastStreamNoneReader);
DebugL;
}
@ -83,6 +82,7 @@ void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_
if(src){
//推流给自己成功
cb(SockException());
strongSelf->onGetMediaSource(src);
strongSelf->startTimer(timeout_ms);
return;
}
@ -192,8 +192,7 @@ void FFmpegSource::startTimer(int timeout_ms) {
//同步查找流
if (!src) {
//流不在线,重新拉流
strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms,
[](const SockException &) {});
strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [](const SockException &) {});
}
});
} else {
@ -205,29 +204,35 @@ void FFmpegSource::startTimer(int timeout_ms) {
}
return true;
}, _poller);
NoticeCenter::Instance().delListener(this, Broadcast::kBroadcastStreamNoneReader);
NoticeCenter::Instance().addListener(this, Broadcast::kBroadcastStreamNoneReader,[weakSelf](BroadcastStreamNoneReaderArgs) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
//自身已经销毁
return;
}
if(sender.getVhost() != strongSelf->_media_info._vhost ||
sender.getApp() != strongSelf->_media_info._app ||
sender.getId() != strongSelf->_media_info._streamid){
//不是自己感兴趣的事件,忽略之
return;
}
//该流无人观看,我们停止吧
if(strongSelf->_onClose){
strongSelf->_onClose();
}
});
}
void FFmpegSource::setOnClose(const function<void()> &cb){
_onClose = cb;
}
}
bool FFmpegSource::close(MediaSource &sender, bool force) {
auto listener = _listener.lock();
if(listener && !listener->close(sender,force)){
//关闭失败
return false;
}
//该流无人观看,我们停止吧
if(_onClose){
_onClose();
}
return true;
}
void FFmpegSource::onNoneReader(MediaSource &sender) {
auto listener = _listener.lock();
if(listener){
listener->onNoneReader(sender);
}else{
MediaSourceEvent::onNoneReader(sender);
}
}
void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) {
_listener = src->getListener();
src->setListener(shared_from_this());
}

View File

@ -39,7 +39,7 @@ using namespace std;
using namespace toolkit;
using namespace mediakit;
class FFmpegSource : public std::enable_shared_from_this<FFmpegSource>{
class FFmpegSource : public std::enable_shared_from_this<FFmpegSource> , public MediaSourceEvent{
public:
typedef shared_ptr<FFmpegSource> Ptr;
typedef function<void(const SockException &ex)> onPlay;
@ -55,6 +55,10 @@ public:
private:
void findAsync(int maxWaitMS ,const function<void(const MediaSource::Ptr &src)> &cb);
void startTimer(int timeout_ms);
void onGetMediaSource(const MediaSource::Ptr &src);
bool close(MediaSource &sender,bool force) override;
void onNoneReader(MediaSource &sender) override ;
private:
Process _process;
Timer::Ptr _timer;
@ -63,6 +67,7 @@ private:
string _src_url;
string _dst_url;
function<void()> _onClose;
std::weak_ptr<MediaSourceEvent> _listener;
};

View File

@ -72,6 +72,8 @@ typedef map<string,variant,StrCaseCompare> ApiArgsType;
invoker("200 OK", headerOut, val.toStyledString()); \
});
#define API_ARGS_VALUE sender,headerIn,headerOut,allArgs,val,invoker
#define API_REGIST_INVOKER(field, name, ...) \
s_map_api.emplace("/index/"#field"/"#name,[](API_ARGS,const HttpSession::HttpResponseInvoker &invoker) __VA_ARGS__);
@ -294,6 +296,7 @@ void installWebApi() {
obj["delay"] = vecDelay[i++];
val["data"].append(obj);
}
val["code"] = API::Success;
invoker("200 OK", headerOut, val.toStyledString());
});
});
@ -311,6 +314,7 @@ void installWebApi() {
obj["delay"] = vecDelay[i++];
val["data"].append(obj);
}
val["code"] = API::Success;
invoker("200 OK", headerOut, val.toStyledString());
});
});
@ -391,8 +395,6 @@ void installWebApi() {
API_REGIST(api,getMediaList,{
CHECK_SECRET();
//获取所有MediaSource列表
val["code"] = API::Success;
val["msg"] = "success";
MediaSource::for_each_media([&](const string &schema,
const string &vhost,
const string &app,
@ -416,6 +418,13 @@ void installWebApi() {
});
});
//测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
API_REGIST(api,isMediaOnline,{
CHECK_SECRET();
CHECK_ARGS("schema","vhost","app","stream");
val["online"] = (bool) (MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"],false));
});
//主动关断流,包括关断拉流、推流
//测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
API_REGIST(api,close_stream,{
@ -428,14 +437,53 @@ void installWebApi() {
allArgs["stream"]);
if(src){
bool flag = src->close(allArgs["force"].as<bool>());
val["code"] = flag ? 0 : -1;
val["result"] = flag ? 0 : -1;
val["msg"] = flag ? "success" : "close failed";
}else{
val["code"] = -2;
val["result"] = -2;
val["msg"] = "can not find the stream";
}
});
//批量主动关断流,包括关断拉流、推流
//测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
API_REGIST(api,close_streams,{
CHECK_SECRET();
//筛选命中个数
int count_hit = 0;
int count_closed = 0;
list<MediaSource::Ptr> media_list;
MediaSource::for_each_media([&](const string &schema,
const string &vhost,
const string &app,
const string &stream,
const MediaSource::Ptr &media){
if(!allArgs["schema"].empty() && allArgs["schema"] != schema){
return;
}
if(!allArgs["vhost"].empty() && allArgs["vhost"] != vhost){
return;
}
if(!allArgs["app"].empty() && allArgs["app"] != app){
return;
}
if(!allArgs["stream"].empty() && allArgs["stream"] != stream){
return;
}
++count_hit;
media_list.emplace_back(media);
});
bool force = allArgs["force"].as<bool>();
for(auto &media : media_list){
if(media->close(force)){
++count_closed;
}
}
val["count_hit"] = count_hit;
val["count_closed"] = count_closed;
});
//获取所有TcpSession列表信息
//可以根据本地端口和远端ip来筛选
//测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935
@ -446,7 +494,7 @@ void installWebApi() {
string &peer_ip = allArgs["peer_ip"];
SessionMap::Instance().for_each_session([&](const string &id,const TcpSession::Ptr &session){
if(local_port != API::Success && local_port != session->get_local_port()){
if(local_port != 0 && local_port != session->get_local_port()){
return;
}
if(!peer_ip.empty() && peer_ip != session->get_peer_ip()){
@ -470,13 +518,36 @@ void installWebApi() {
//踢掉tcp会话
auto session = SessionMap::Instance().get(allArgs["id"]);
if(!session){
val["code"] = API::OtherFailed;
val["msg"] = "can not find the target";
return;
throw ApiRetException("can not find the target",API::OtherFailed);
}
session->safeShutdown();
val["code"] = API::Success;
val["msg"] = "success";
});
//批量断开tcp连接比如说可以断开rtsp、rtmp播放器等
//测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935
API_REGIST(api,kick_sessions,{
CHECK_SECRET();
uint16_t local_port = allArgs["local_port"].as<uint16_t>();
string &peer_ip = allArgs["peer_ip"];
uint64_t count_hit = 0;
list<TcpSession::Ptr> session_list;
SessionMap::Instance().for_each_session([&](const string &id,const TcpSession::Ptr &session){
if(local_port != 0 && local_port != session->get_local_port()){
return;
}
if(!peer_ip.empty() && peer_ip != session->get_peer_ip()){
return;
}
session_list.emplace_back(session);
++count_hit;
});
for(auto &session : session_list){
session->safeShutdown();
}
val["count_hit"] = (Json::UInt64)count_hit;
});
static auto addStreamProxy = [](const string &vhost,
@ -554,7 +625,7 @@ void installWebApi() {
});
#if !defined(_WIN32)
static auto addFFmepgSource = [](const string &src_url,
static auto addFFmpegSource = [](const string &src_url,
const string &dst_url,
int timeout_ms,
const function<void(const SockException &ex,const string &key)> &cb){
@ -591,7 +662,7 @@ void installWebApi() {
auto dst_url = allArgs["dst_url"];
int timeout_ms = allArgs["timeout_ms"];
addFFmepgSource(src_url,dst_url,timeout_ms,[invoker,val,headerOut](const SockException &ex,const string &key){
addFFmpegSource(src_url,dst_url,timeout_ms,[invoker,val,headerOut](const SockException &ex,const string &key){
if(ex){
const_cast<Value &>(val)["code"] = API::OtherFailed;
const_cast<Value &>(val)["msg"] = ex.what();
@ -602,13 +673,23 @@ void installWebApi() {
});
});
//关闭拉流代理
//测试url http://127.0.0.1/index/api/delFFmepgSource?key=key
API_REGIST(api,delFFmepgSource,{
static auto api_delFFmpegSource = [](API_ARGS,const HttpSession::HttpResponseInvoker &invoker){
CHECK_SECRET();
CHECK_ARGS("key");
lock_guard<decltype(s_ffmpegMapMtx)> lck(s_ffmpegMapMtx);
val["data"]["flag"] = s_ffmpegMap.erase(allArgs["key"]) == 1;
};
//关闭拉流代理
//测试url http://127.0.0.1/index/api/delFFmepgSource?key=key
API_REGIST(api,delFFmpegSource,{
api_delFFmpegSource(API_ARGS_VALUE);
});
//此处为了兼容之前的拼写错误
API_REGIST(api,delFFmepgSource,{
api_delFFmpegSource(API_ARGS_VALUE);
});
#endif
@ -677,7 +758,7 @@ void installWebApi() {
<< allArgs["stream"] << "?vhost="
<< allArgs["vhost"];
addFFmepgSource("http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8",/** ffmpeg拉流支持任意编码格式任意协议 **/
addFFmpegSource("http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8",/** ffmpeg拉流支持任意编码格式任意协议 **/
dst_url,
(1000 * timeout_sec) - 500,
[invoker,val,headerOut](const SockException &ex,const string &key){

View File

@ -167,10 +167,15 @@ public:
}
listener->onNoneReader(*this);
}
virtual void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
_listener = listener;
}
std::weak_ptr<MediaSourceEvent> getListener(){
return _listener;
}
template <typename FUN>
static void for_each_media(FUN && fun){
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);

View File

@ -277,6 +277,13 @@ void RtspPlayer::createUdpSockIfNecessary(int track_idx){
throw std::runtime_error("open rtcp sock failed");
}
}
if(rtpSockRef->get_local_port() % 2 != 0){
//如果rtp端口不是偶数那么与rtcp端口互换目的是兼容一些要求严格的服务器
Socket::Ptr tmp = rtpSockRef;
rtpSockRef = rtcpSockRef;
rtcpSockRef = tmp;
}
}