mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 19:00:01 +08:00
1.添加windows下ffmpeg拉流分发支持,目前不是很完善,后续再修改
2.server/system下未兼容完成
This commit is contained in:
parent
d082955510
commit
306e202ca1
@ -1 +1 @@
|
||||
Subproject commit ba5a796d583cd2906e06ec15c0d942e484ea1dbf
|
||||
Subproject commit 7cb850d79ba798e6d1f0bba7c655d38d2865ed92
|
24
README.md
24
README.md
@ -1,17 +1,5 @@
|
||||
![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/logo.png)
|
||||
|
||||
[english readme](https://github.com/xiongziliang/ZLMediaKit/blob/master/README_en.md)
|
||||
|
||||
# 一个基于C++11的高性能运营级流媒体服务框架
|
||||
[![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit)
|
||||
|
||||
|
||||
## 国内用户请使用gitee镜像下载
|
||||
```
|
||||
git clone --depth 1 https://gitee.com/xiahcu/ZLMediaKit
|
||||
cd ZLMediaKit
|
||||
git submodule update --init
|
||||
```
|
||||
## 项目特点
|
||||
- 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。
|
||||
- 打包多种流媒体协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV),支持协议间的互相转换,提供一站式的服务。
|
||||
@ -29,6 +17,7 @@ git submodule update --init
|
||||
- 移动嵌入式跨平台流媒体解决方案。
|
||||
- 商用级流媒体服务器。
|
||||
- 网络编程二次开发SDK。
|
||||
- 免费NVR(rtxp拉流代理、分发)。
|
||||
|
||||
|
||||
## 功能清单
|
||||
@ -362,8 +351,9 @@ git submodule update --init
|
||||
由于使用本项目而产生的商业纠纷或侵权行为一概与本项项目及开发者无关,请自行承担法律风险。
|
||||
|
||||
## 联系方式
|
||||
- 邮箱:<771730766@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复)
|
||||
- QQ群:542509000
|
||||
- 邮箱:<18675721@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复)
|
||||
- QQ群:542509000 (ZLMediakit)
|
||||
- QQ群:936467414 (FreeNVR)
|
||||
|
||||
## 怎么提问?
|
||||
如果要对项目有相关疑问,建议您这么做:
|
||||
@ -372,12 +362,6 @@ git submodule update --init
|
||||
- 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提.
|
||||
- 4、QQ私聊一般不接受无偿技术咨询和支持(谈谈人生理想还是可以的😂),毕竟精力有限,谢谢理解.
|
||||
|
||||
## 捐赠
|
||||
欢迎捐赠以便更好的推动项目的发展,谢谢您的支持!
|
||||
|
||||
[支付宝](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3919.JPG)
|
||||
|
||||
[微信](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3920.JPG)
|
||||
|
||||
|
||||
|
||||
|
@ -3,12 +3,7 @@ file(GLOB jsoncpp_src_list ../3rdpart/jsoncpp/*.cpp ../3rdpart/jsoncpp/*.h )
|
||||
add_library(jsoncpp STATIC ${jsoncpp_src_list})
|
||||
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
set(MediaServer_src_list ./WebApi.cpp ./WebHook.cpp main.cpp)
|
||||
else()
|
||||
file(GLOB MediaServer_src_list ./*.cpp ./*.h)
|
||||
endif()
|
||||
|
||||
#message(STATUS ${MediaServer_src_list})
|
||||
|
||||
add_executable(MediaServer ${MediaServer_src_list})
|
||||
|
@ -37,9 +37,15 @@ const char kCmd[] = FFmpeg_FIELD"cmd";
|
||||
const char kLog[] = FFmpeg_FIELD"log";
|
||||
|
||||
onceToken token([]() {
|
||||
mINI::Instance()[kBin] = trim(System::execute("which ffmpeg"));
|
||||
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";
|
||||
#ifdef _WIN32
|
||||
string strFFmpeg = "where ffmpeg";
|
||||
#else
|
||||
string strFFmpeg = "which ffmpeg";
|
||||
#endif
|
||||
|
||||
mINI::Instance()[kBin] = trim(System::execute(strFFmpeg)); //todo:暂定如此:配置文件无此配置的话,改为环境变量的ffmpeg
|
||||
mINI::Instance()[kCmd] = "%s -re -i \"%s\" -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; //防止url中特殊字符
|
||||
mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log"; //win下建议使用ffmpeg/ffmpeg.log
|
||||
});
|
||||
}
|
||||
|
||||
@ -51,7 +57,6 @@ FFmpegSource::~FFmpegSource() {
|
||||
DebugL;
|
||||
}
|
||||
|
||||
|
||||
void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ms,const onPlay &cb) {
|
||||
GET_CONFIG(string,ffmpeg_bin,FFmpeg::kBin);
|
||||
GET_CONFIG(string,ffmpeg_cmd,FFmpeg::kCmd);
|
||||
@ -63,7 +68,7 @@ void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_
|
||||
|
||||
char cmd[1024] = {0};
|
||||
snprintf(cmd, sizeof(cmd), ffmpeg_cmd.data(), ffmpeg_bin.data(), src_url.data(), dst_url.data());
|
||||
_process.run(cmd,File::absolutePath("",ffmpeg_log));
|
||||
_process.run(cmd, ffmpeg_log.empty() ? "" : File::absolutePath("", ffmpeg_log));
|
||||
InfoL << cmd;
|
||||
|
||||
if(_media_info._host == "127.0.0.1"){
|
||||
|
@ -26,8 +26,15 @@
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
//#include <TlHelp32.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <signal.h>
|
||||
#include "Util/util.h"
|
||||
@ -40,6 +47,39 @@ using namespace toolkit;
|
||||
|
||||
void Process::run(const string &cmd, const string &log_file_tmp) {
|
||||
kill(2000);
|
||||
#ifdef _WIN32
|
||||
// string log_file = "";
|
||||
// if (log_file_tmp.empty())
|
||||
// {
|
||||
// log_file = R"( >2&1)";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// log_file = log_file_tmp + R"( >2&1)";
|
||||
// }
|
||||
// string strCmd = cmd + " " + log_file; //防止cmd后面没有空格
|
||||
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&si, sizeof(si)); //结构体初始化;
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
LPTSTR lpDir = const_cast<char*>(cmd .data());
|
||||
|
||||
if (CreateProcess(NULL, lpDir, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
|
||||
{
|
||||
//下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
_pid = pi.dwProcessId;
|
||||
InfoL << "start child proces " << _pid;
|
||||
}
|
||||
else
|
||||
{
|
||||
WarnL << "start child proces fail: " << GetLastError();
|
||||
}
|
||||
#else
|
||||
_pid = fork();
|
||||
if (_pid < 0) {
|
||||
throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg());
|
||||
@ -57,7 +97,8 @@ void Process::run(const string &cmd, const string &log_file_tmp) {
|
||||
string log_file;
|
||||
if (log_file_tmp.empty()) {
|
||||
log_file = "/dev/null";
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
log_file = StrPrinter << log_file_tmp << "." << getpid();
|
||||
}
|
||||
|
||||
@ -67,7 +108,8 @@ void Process::run(const string &cmd, const string &log_file_tmp) {
|
||||
File::createfile_path(log_file.data(), mode);
|
||||
if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) {
|
||||
fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// dup to stdout and stderr.
|
||||
if (dup2(log_fd, STDOUT_FILENO) < 0) {
|
||||
fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
|
||||
@ -103,11 +145,10 @@ void Process::run(const string &cmd, const string &log_file_tmp) {
|
||||
}
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
InfoL << "start child proces " << _pid;
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取进程是否存活状态
|
||||
* @param pid 进程号
|
||||
@ -120,6 +161,15 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) {
|
||||
return false;
|
||||
}
|
||||
int status = 0;
|
||||
#ifdef _WIN32
|
||||
HANDLE hProcess = NULL;
|
||||
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程
|
||||
if (hProcess == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(hProcess);
|
||||
#else
|
||||
pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG);
|
||||
int exit_code = (status & 0xFF00) >> 8;
|
||||
if (exit_code_ptr) {
|
||||
@ -133,7 +183,9 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) {
|
||||
InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code;
|
||||
return false;
|
||||
}
|
||||
//WarnL << "process is running, pid=" << _pid;
|
||||
WarnL << "process is running, pid=" << _pid;
|
||||
#endif // _WIN32
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -142,12 +194,27 @@ static void s_kill(pid_t pid,int max_delay,bool force){
|
||||
//pid无效
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE hProcess = NULL;
|
||||
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程
|
||||
if (hProcess == NULL)
|
||||
{
|
||||
WarnL << "\nOpen Process fAiled: " << GetLastError();
|
||||
return;
|
||||
}
|
||||
DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程
|
||||
if (ret == 0)
|
||||
{
|
||||
WarnL << GetLastError;
|
||||
}
|
||||
#else
|
||||
if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) {
|
||||
//进程可能已经退出了
|
||||
WarnL << "kill process " << pid << " failed:" << get_uv_errmsg();
|
||||
return;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
|
||||
if(force){
|
||||
//发送SIGKILL信号后,阻塞等待退出
|
||||
|
@ -26,9 +26,16 @@
|
||||
#ifndef IPTV_PROCESS_H
|
||||
#define IPTV_PROCESS_H
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef int pid_t;
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Process {
|
||||
|
@ -27,14 +27,19 @@
|
||||
#include "System.h"
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <limits.h>
|
||||
#ifdef _WIN32
|
||||
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#ifndef ANDROID
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
@ -247,16 +252,38 @@ bool System::getNetworkUsage(vector<NetworkUsage> &usage) {
|
||||
|
||||
|
||||
bool System::getTcpUsage(System::TcpUsage &usage) {
|
||||
usage.established = atoi(trim(System::execute("netstat -na|grep ESTABLISHED|wc -l")).data());
|
||||
usage.syn_recv = atoi(trim(System::execute("netstat -na|grep SYN_RECV|wc -l")).data());
|
||||
usage.time_wait = atoi(trim(System::execute("netstat -na|grep TIME_WAIT|wc -l")).data());
|
||||
usage.close_wait = atoi(trim(System::execute("netstat -na|grep CLOSE_WAIT|wc -l")).data());
|
||||
string strEstab;
|
||||
string strSynRecv;
|
||||
string strTimeWait;
|
||||
string strCloseWait;
|
||||
#ifdef _WIN32
|
||||
//使用R"()" 可以不用转义
|
||||
strEstab = R"(netstat -na|find /i /c "ESTABLISHED")";
|
||||
strSynRecv = R"(netstat -na|find /i /c "SYN_RECV")";
|
||||
strTimeWait = R"(netstat -na|find /i /c "TIME_WAIT")";
|
||||
strCloseWait = R"(netstat -na|find /i /c "CLOSE_WAIT")";
|
||||
#else
|
||||
strEstab = "netstat -na|grep ESTABLISHED|wc -l";
|
||||
strSynRecv = "netstat -na|grep SYN_RECV|wc -l";
|
||||
strTimeWait = "netstat -na|grep TIME_WAIT|wc -l";
|
||||
strCloseWait = "netstat -na|grep CLOSE_WAIT|wc -l";
|
||||
#endif
|
||||
usage.established = atoi(trim(System::execute(strEstab)).data());
|
||||
usage.syn_recv = atoi(trim(System::execute(strSynRecv)).data());
|
||||
usage.time_wait = atoi(trim(System::execute(strTimeWait)).data());
|
||||
usage.close_wait = atoi(trim(System::execute(strCloseWait)).data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string System::execute(const string &cmd) {
|
||||
// DebugL << cmd;
|
||||
FILE *fPipe = popen(cmd.data(), "r");
|
||||
FILE *fPipe = NULL;
|
||||
#ifdef _WIN32
|
||||
fPipe = _popen(cmd.data(), "r");
|
||||
#else
|
||||
fPipe = popen(cmd.data(), "r");
|
||||
#endif
|
||||
if(!fPipe){
|
||||
return "";
|
||||
}
|
||||
@ -265,7 +292,12 @@ string System::execute(const string &cmd) {
|
||||
while(fgets(buff, sizeof(buff) - 1, fPipe)){
|
||||
ret.append(buff);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
_pclose(fPipe);
|
||||
#else
|
||||
pclose(fPipe);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -274,8 +306,11 @@ static string addr2line(const string &address) {
|
||||
return System::execute(cmd);
|
||||
}
|
||||
|
||||
#ifndef ANDROID
|
||||
static void sig_crash(int sig) {
|
||||
#ifdef _WIN32
|
||||
|
||||
#else
|
||||
#ifndef ANDROID
|
||||
signal(sig, SIG_DFL);
|
||||
void *array[MAX_STACK_FRAMES];
|
||||
int size = backtrace(array, MAX_STACK_FRAMES);
|
||||
@ -296,12 +331,14 @@ static void sig_crash(int sig) {
|
||||
free(strings);
|
||||
|
||||
NoticeCenter::Instance().emitEvent(kBroadcastOnCrashDump,sig,stack);
|
||||
#endif//#ifndef ANDROID
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
#endif//#ifndef ANDROID
|
||||
|
||||
|
||||
void System::startDaemon() {
|
||||
#ifdef _WIN32
|
||||
|
||||
#else
|
||||
static pid_t pid;
|
||||
do{
|
||||
pid = fork();
|
||||
@ -336,14 +373,17 @@ void System::startDaemon() {
|
||||
DebugL << "waitpid被中断:" << get_uv_errmsg();
|
||||
}while (true);
|
||||
}while (true);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
|
||||
|
||||
static string currentDateTime(){
|
||||
time_t ts = time(NULL);
|
||||
std::tm tm_snapshot;
|
||||
#ifndef _WIN32
|
||||
localtime_r(&ts, &tm_snapshot);
|
||||
#else
|
||||
localtime_s(&tm_snapshot, &ts);
|
||||
#endif // !_WIN32
|
||||
|
||||
char buffer[1024] = {0};
|
||||
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_snapshot);
|
||||
@ -351,6 +391,9 @@ static string currentDateTime(){
|
||||
}
|
||||
|
||||
void System::systemSetup(){
|
||||
#ifdef _WIN32
|
||||
|
||||
#else
|
||||
struct rlimit rlim,rlim_new;
|
||||
if (getrlimit(RLIMIT_CORE, &rlim)==0) {
|
||||
rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
|
||||
@ -394,5 +437,6 @@ void System::systemSetup(){
|
||||
|
||||
cerr << stack_info << endl;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -47,10 +47,7 @@
|
||||
#include "WebHook.h"
|
||||
#include "Thread/WorkThreadPool.h"
|
||||
#include "Rtp/RtpSelector.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include "FFmpegSource.h"
|
||||
#endif//!defined(_WIN32)
|
||||
|
||||
using namespace Json;
|
||||
using namespace toolkit;
|
||||
@ -268,10 +265,8 @@ static inline string getProxyKey(const string &vhost,const string &app,const str
|
||||
return vhost + "/" + app + "/" + stream;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
static unordered_map<string ,FFmpegSource::Ptr> s_ffmpegMap;
|
||||
static recursive_mutex s_ffmpegMapMtx;
|
||||
#endif//#if !defined(_WIN32)
|
||||
|
||||
/**
|
||||
* 安装api接口
|
||||
@ -646,7 +641,6 @@ void installWebApi() {
|
||||
val["data"]["flag"] = s_proxyMap.erase(allArgs["key"]) == 1;
|
||||
});
|
||||
|
||||
#if !defined(_WIN32)
|
||||
static auto addFFmpegSource = [](const string &src_url,
|
||||
const string &dst_url,
|
||||
int timeout_ms,
|
||||
@ -713,7 +707,6 @@ void installWebApi() {
|
||||
API_REGIST(api,delFFmepgSource,{
|
||||
api_delFFmpegSource(API_ARGS_VALUE);
|
||||
});
|
||||
#endif
|
||||
|
||||
//新增http api下载可执行程序文件接口
|
||||
//测试url http://127.0.0.1/index/api/downloadBin
|
||||
@ -932,10 +925,8 @@ void unInstallWebApi(){
|
||||
s_proxyMap.clear();
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(s_ffmpegMapMtx);
|
||||
s_ffmpegMap.clear();
|
||||
}
|
||||
#endif
|
||||
}
|
@ -239,7 +239,7 @@ int main(int argc,char *argv[]) {
|
||||
|
||||
//这里是拉流地址,支持rtmp/rtsp协议,负载必须是H264+AAC
|
||||
//如果是其他不识别的音视频将会被忽略(譬如说h264+adpcm转发后会去除音频)
|
||||
auto urlList = {"rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov"
|
||||
auto urlList = {"rtsp://admin:admin123@192.168.1.64:554/cam/realmonitor?channel=1&subtype=1"
|
||||
//rtsp链接支持输入用户名密码
|
||||
/*"rtsp://admin:jzan123456@192.168.0.122/"*/};
|
||||
map<string, PlayerProxy::Ptr> proxyMap;
|
||||
@ -258,7 +258,7 @@ int main(int argc,char *argv[]) {
|
||||
//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()));
|
||||
PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "live", std::string("chn") + to_string(i).data()));
|
||||
//指定RTP over TCP(播放rtsp时有效)
|
||||
(*player)[kRtpType] = Rtsp::RTP_TCP;
|
||||
//开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试
|
||||
|
Loading…
Reference in New Issue
Block a user