diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5332e02d..aec266ac 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -180,8 +180,9 @@ if (WIN32)
set(INSTALL_PATH_LIB $ENV{HOME}/${CMAKE_PROJECT_NAME}/lib)
set(INSTALL_PATH_INCLUDE $ENV{HOME}/${CMAKE_PROJECT_NAME}/include)
else ()
- set(INSTALL_PATH_LIB lib)
+ set(INSTALL_PATH_LIB lib${LIB_SUFFIX})
set(INSTALL_PATH_INCLUDE include)
+ set(INSTALL_PATH_EXECUTABLE bin)
endif ()
if(ENABLE_CXX_API)
diff --git a/README.md b/README.md
index 1d12175d..ee1f12e6 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@
## 功能清单
### 功能一览
-
+
- RTSP[S]
- RTSP[S] 服务器,支持RTMP/MP4/HLS转RTSP[S],支持亚马逊echo show这样的设备
@@ -140,7 +140,7 @@ bash build_docker_images.sh
- 流媒体管理平台
- [功能强大的流媒体控制管理接口平台,支持GB28181](https://github.com/chatop2020/StreamNode)
- - [GB28181-2016网络视频平台](https://github.com/648540858/wvp-GB28181)
+ - [GB28181-2016网络视频平台](https://github.com/648540858/wvp-GB28181-pro)
- [node-js版本的GB28181平台](https://gitee.com/hfwudao/GB28181_Node_Http)
- [Go实现的海康ehome服务器](https://github.com/tsingeye/FreeEhome)
diff --git a/conf/config.ini b/conf/config.ini
index 404f6f8b..057aee90 100644
--- a/conf/config.ini
+++ b/conf/config.ini
@@ -55,7 +55,7 @@ modifyStamp=0
#服务器唯一id,用于触发hook时区别是哪台服务器
mediaServerId=your_server_id
-###### 以下是按需转协议的开关,在测试ZLMediaKit的接收推流性能时,请关闭以下全部开关
+###### 以下是按需转协议的开关,在测试ZLMediaKit的接收推流性能时,请把下面开关置1
###### 如果某种协议你用不到,你可以把以下开关置1以便节省资源(但是还是可以播放,只是第一个播放者体验稍微差点),
###### 如果某种协议你想获取最好的用户体验,请置0(第一个播放者可以秒开,且不花屏)
diff --git a/package/rpm/ZLMediaKit.spec b/package/rpm/ZLMediaKit.spec
new file mode 100644
index 00000000..58b2b633
--- /dev/null
+++ b/package/rpm/ZLMediaKit.spec
@@ -0,0 +1,142 @@
+%if 0%{?fedora} >= 15 || 0%{?rhel} >= 7
+%global use_devtoolset 0
+%bcond_without faac
+%bcond_without x264
+%else
+%global use_devtoolset 1
+%bcond_with faac
+%bcond_with x264
+%endif
+
+%bcond_without openssl
+%bcond_without mysql
+
+Name: ZLMediaKit
+Version: 5.0.0
+Release: 1%{?dist}
+Summary: A lightweight, high performance and stable stream server and client framework based on C++11.
+
+Group: development
+License: MIT
+URL: https://github.com/xia-chu/ZLMediaKit
+Source0: %{name}-%{version}.tar.xz
+
+%if %{with openssl}
+BuildRequires: openssl-devel
+%endif
+
+%if %{with mysql}
+BuildRequires: mysql-devel
+%endif
+
+%if %{with faac}
+BuildRequires: faac-devel
+%endif
+
+%if %{with x264}
+BuildRequires: x264-devel
+%endif
+
+%if 0%{?use_devtoolset}
+BuildRequires: devtoolset-8-gcc-c++
+%endif
+
+%description
+A lightweight RTSP/RTMP/HTTP/HLS/HTTP-FLV/WebSocket-FLV/HTTP-TS/HTTP-fMP4/WebSocket-TS/WebSocket-fMP4/GB28181 server and client framework based on C++11.
+
+%package media-server
+Requires: %{name} = %{version}
+Summary: A lightweight, high performance and stable stream server
+
+%description media-server
+A lightweight RTSP/RTMP/HTTP/HLS/HTTP-FLV/WebSocket-FLV/HTTP-TS/HTTP-fMP4/WebSocket-TS/WebSocket-fMP4/GB28181 server.
+
+%package c-libs
+Requires: %{name} = %{version}
+Summary: The %{name} C libraries.
+%description c-libs
+The %{name} C libraries.
+
+%package c-devel
+Requires: %{name}-c-libs = %{version}
+Summary: The %{name} C API headers.
+%description c-devel
+The %{name} C API headers.
+
+%package cxx-devel
+Requires: %{name} = %{version}
+Summary: The %{name} C++ API headers and development libraries.
+%description cxx-devel
+The %{name} C++ API headers and development libraries.
+
+%prep
+%setup -q
+
+%build
+mkdir -p %{_target_platform}
+
+pushd %{_target_platform}
+
+%if 0%{?use_devtoolset}
+. /opt/rh/devtoolset-8/enable
+%endif
+
+%cmake3 \
+ -DCMAKE_BUILD_TYPE:STRING=Release \
+ -DENABLE_HLS:BOOL=ON \
+ -DENABLE_OPENSSL:BOOL=%{with openssl} \
+ -DENABLE_MYSQL:BOOL=%{with mysql} \
+ -DENABLE_FAAC:BOOL=%{with faac} \
+ -DENABLE_X264:BOOL=%{with x264} \
+ -DENABLE_MP4:BOOL=ON \
+ -DENABLE_RTPPROXY:BOOL=ON \
+ -DENABLE_API:BOOL=ON \
+ -DENABLE_CXX_API:BOOL=ON \
+ -DENABLE_TESTS:BOOL=OFF \
+ -DENABLE_SERVERL:BOOL=ON \
+ ..
+
+make %{?_smp_mflags}
+
+popd
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+pushd %{_target_platform}
+%make_install
+popd
+
+install -m 0755 -d %{buildroot}%{_docdir}/%{name}
+install -m 0644 LICENSE %{buildroot}%{_docdir}/%{name}
+install -m 0644 README.md %{buildroot}%{_docdir}/%{name}
+install -m 0644 README_en.md %{buildroot}%{_docdir}/%{name}
+
+# TODO: service files
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%doc %{_docdir}/*
+
+%files media-server
+%{_bindir}/*
+
+%files c-libs
+%{_libdir}/libmk_api.so
+
+%files c-devel
+%{_includedir}/mk_*
+
+%files cxx-devel
+%{_includedir}/ZLMediaKit/*
+%{_includedir}/ZLToolKit/*
+%{_libdir}/libzlmediakit.a
+%{_libdir}/libzltoolkit.a
+%{_libdir}/libmpeg.a
+%{_libdir}/libmov.a
+%{_libdir}/libflv.a
+
+%changelog
+
diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json
index 3b1d5a43..48d93811 100644
--- a/postman/ZLMediaKit.postman_collection.json
+++ b/postman/ZLMediaKit.postman_collection.json
@@ -428,7 +428,7 @@
"method": "GET",
"header": [],
"url": {
- "raw": "{{ZLMediaKit_URL}}/index/api/addStreamProxy?secret={{ZLMediaKit_secret}}&vhost={{defaultVhost}}&app=live&stream=test&url=rtmp://live.hkstv.hk.lxdns.com/live/hks2&enable_rtsp=1&enable_rtmp=1",
+ "raw": "{{ZLMediaKit_URL}}/index/api/addStreamProxy?secret={{ZLMediaKit_secret}}&vhost={{defaultVhost}}&app=live&stream=test&url=rtmp://live.hkstv.hk.lxdns.com/live/hks2",
"host": [
"{{ZLMediaKit_URL}}"
],
@@ -523,7 +523,7 @@
"method": "GET",
"header": [],
"url": {
- "raw": "{{ZLMediaKit_URL}}/index/api/addFFmpegSource?secret={{ZLMediaKit_secret}}&src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://10.8.9.115:8554/live/hks2&timeout_ms=10000",
+ "raw": "{{ZLMediaKit_URL}}/index/api/addFFmpegSource?secret={{ZLMediaKit_secret}}&src_url=http://hefeng.live.tempsource.cjyun.org/videotmp/s10100-hftv.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000&enable_hls=0&enable_mp4=0",
"host": [
"{{ZLMediaKit_URL}}"
],
@@ -540,7 +540,7 @@
},
{
"key": "src_url",
- "value": "http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8",
+ "value": "http://hefeng.live.tempsource.cjyun.org/videotmp/s10100-hftv.m3u8",
"description": "FFmpeg拉流地址,支持任意协议或格式(只要FFmpeg支持即可)"
},
{
@@ -552,6 +552,16 @@
"key": "timeout_ms",
"value": "10000",
"description": "FFmpeg推流成功超时时间,单位毫秒"
+ },
+ {
+ "key": "enable_hls",
+ "value": "0",
+ "description": "是否开启hls录制"
+ },
+ {
+ "key": "enable_mp4",
+ "value": "0",
+ "description": "是否开启mp4录制"
}
]
}
diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt
index 092a4221..9b9692b3 100644
--- a/server/CMakeLists.txt
+++ b/server/CMakeLists.txt
@@ -10,8 +10,8 @@ add_executable(MediaServer ${MediaServer_src_list})
if(WIN32)
set_target_properties(MediaServer PROPERTIES COMPILE_FLAGS ${VS_FALGS} )
+else()
+ install(TARGETS MediaServer DESTINATION ${INSTALL_PATH_EXECUTABLE})
endif()
target_link_libraries(MediaServer jsoncpp ${LINK_LIB_LIST})
-
-
diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp
index 6ae12e85..62e0e404 100644
--- a/server/FFmpegSource.cpp
+++ b/server/FFmpegSource.cpp
@@ -59,6 +59,11 @@ static bool is_local_ip(const string &ip){
return false;
}
+void FFmpegSource::setupRecord(bool enable_hls, bool enable_mp4){
+ _enable_hls = enable_hls;
+ _enable_mp4 = enable_mp4;
+}
+
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);
@@ -263,6 +268,12 @@ void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) {
//防止多次进入onGetMediaSource函数导致无限递归调用的bug
setDelegate(listener);
src->setListener(shared_from_this());
+ if (_enable_hls) {
+ src->setupRecord(Recorder::type_hls, true, "");
+ }
+ if (_enable_mp4) {
+ src->setupRecord(Recorder::type_mp4, true, "");
+ }
}
}
diff --git a/server/FFmpegSource.h b/server/FFmpegSource.h
index 1b9c1e9f..48c21ae7 100644
--- a/server/FFmpegSource.h
+++ b/server/FFmpegSource.h
@@ -46,13 +46,29 @@ public:
typedef function onPlay;
FFmpegSource();
- virtual ~FFmpegSource();
+ ~FFmpegSource();
+
/**
* 设置主动关闭回调
- * @param cb
*/
void setOnClose(const function &cb);
- void play(const string &src_url,const string &dst_url,int timeout_ms,const onPlay &cb);
+
+ /**
+ * 开始播放url
+ * @param src_url FFmpeg拉流地址
+ * @param dst_url FFmpeg推流地址
+ * @param timeout_ms 等待结果超时时间,单位毫秒
+ * @param cb 成功与否回调
+ */
+ void play(const string &src_url, const string &dst_url, int timeout_ms, const onPlay &cb);
+
+ /**
+ * 设置录制
+ * @param enable_hls 是否开启hls直播或录制
+ * @param enable_mp4 是否录制mp4
+ */
+ void setupRecord(bool enable_hls, bool enable_mp4);
+
private:
void findAsync(int maxWaitMS ,const function &cb);
void startTimer(int timeout_ms);
@@ -69,6 +85,8 @@ private:
std::shared_ptr getOriginSock(MediaSource &sender) const override;
private:
+ bool _enable_hls = false;
+ bool _enable_mp4 = false;
Process _process;
Timer::Ptr _timer;
EventPoller::Ptr _poller;
diff --git a/server/WebApi.cpp b/server/WebApi.cpp
index fb2b118a..68cd38c4 100644
--- a/server/WebApi.cpp
+++ b/server/WebApi.cpp
@@ -830,28 +830,31 @@ void installWebApi() {
static auto addFFmpegSource = [](const string &src_url,
const string &dst_url,
int timeout_ms,
- const function &cb){
+ bool enable_hls,
+ bool enable_mp4,
+ const function &cb) {
auto key = MD5(dst_url).hexdigest();
lock_guard lck(s_ffmpegMapMtx);
- if(s_ffmpegMap.find(key) != s_ffmpegMap.end()){
+ if (s_ffmpegMap.find(key) != s_ffmpegMap.end()) {
//已经在拉流了
- cb(SockException(Err_success),key);
+ cb(SockException(Err_success), key);
return;
}
FFmpegSource::Ptr ffmpeg = std::make_shared();
s_ffmpegMap[key] = ffmpeg;
- ffmpeg->setOnClose([key](){
+ ffmpeg->setOnClose([key]() {
lock_guard lck(s_ffmpegMapMtx);
s_ffmpegMap.erase(key);
});
- ffmpeg->play(src_url, dst_url,timeout_ms,[cb , key](const SockException &ex){
- if(ex){
+ ffmpeg->setupRecord(enable_hls, enable_mp4);
+ ffmpeg->play(src_url, dst_url, timeout_ms, [cb, key](const SockException &ex) {
+ if (ex) {
lock_guard lck(s_ffmpegMapMtx);
s_ffmpegMap.erase(key);
}
- cb(ex,key);
+ cb(ex, key);
});
};
@@ -863,12 +866,15 @@ void installWebApi() {
auto src_url = allArgs["src_url"];
auto dst_url = allArgs["dst_url"];
int timeout_ms = allArgs["timeout_ms"];
+ auto enable_hls = allArgs["enable_hls"].as();
+ auto enable_mp4 = allArgs["enable_mp4"].as();
- addFFmpegSource(src_url,dst_url,timeout_ms,[invoker,val,headerOut](const SockException &ex,const string &key){
- if(ex){
+ addFFmpegSource(src_url, dst_url, timeout_ms, enable_hls, enable_mp4,
+ [invoker, val, headerOut](const SockException &ex, const string &key) {
+ if (ex) {
const_cast(val)["code"] = API::OtherFailed;
const_cast(val)["msg"] = ex.what();
- }else{
+ } else {
const_cast(val)["data"]["key"] = key;
}
invoker("200 OK", headerOut, val.toStyledString());
@@ -1234,6 +1240,8 @@ void installWebApi() {
addFFmpegSource("http://hls-ott-zhibo.wasu.tv/live/272/index.m3u8",/** ffmpeg拉流支持任意编码格式任意协议 **/
dst_url,
(1000 * timeout_sec) - 500,
+ false,
+ false,
[invoker,val,headerOut](const SockException &ex,const string &key){
if(ex){
const_cast(val)["code"] = API::OtherFailed;
diff --git a/src/Http/HlsParser.cpp b/src/Http/HlsParser.cpp
index b6b35f01..60e578d7 100644
--- a/src/Http/HlsParser.cpp
+++ b/src/Http/HlsParser.cpp
@@ -9,7 +9,7 @@
*/
#include
-#include
+#include
#include "HlsParser.h"
#include "Util/util.h"
#include "Common/Parser.h"
diff --git a/src/Rtp/Decoder.cpp b/src/Rtp/Decoder.cpp
index c0aadd7d..35a6663d 100644
--- a/src/Rtp/Decoder.cpp
+++ b/src/Rtp/Decoder.cpp
@@ -223,6 +223,7 @@ void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d
}
#else
void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes) {}
+void DecoderImp::onStream(int stream,int codecid,const void *extra,int bytes,int finish) {}
#endif
void DecoderImp::onTrack(const Track::Ptr &track) {
diff --git a/src/Rtp/RtpSession.cpp b/src/Rtp/RtpSession.cpp
index 97b62155..1338468d 100644
--- a/src/Rtp/RtpSession.cpp
+++ b/src/Rtp/RtpSession.cpp
@@ -57,9 +57,10 @@ void RtpSession::onManager() {
}
void RtpSession::onRtpPacket(const char *data, uint64_t len) {
- if (len > 1024 * 4) {
- throw SockException(Err_shutdown, "rtp包长度异常,发送端可能缓存溢出并覆盖");
+ if (len > 1024 * 10) {
+ throw SockException(Err_shutdown, StrPrinter << "rtp包长度异常(" << len << "),发送端可能缓存溢出并覆盖");
}
+
if (!_process) {
uint32_t ssrc;
if (!RtpSelector::getSSRC(data, len, ssrc)) {
diff --git a/src/Rtsp/RtpReceiver.cpp b/src/Rtsp/RtpReceiver.cpp
index 75d5b45e..6e3826f9 100644
--- a/src/Rtsp/RtpReceiver.cpp
+++ b/src/Rtsp/RtpReceiver.cpp
@@ -113,7 +113,7 @@ bool RtpReceiver::handleOneRtp(int track_index, TrackType type, int samplerate,
}
if (rtp_raw_len <= rtp.offset) {
- WarnL << "无有效负载的rtp包:" << rtp_raw_len << " <= " << (int) rtp.offset;
+ //无有效负载的rtp包
return false;
}
diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp
index de3aa0d8..3ebde80f 100644
--- a/src/Rtsp/RtspPlayer.cpp
+++ b/src/Rtsp/RtspPlayer.cpp
@@ -37,8 +37,10 @@ RtspPlayer::~RtspPlayer(void) {
}
void RtspPlayer::teardown(){
if (alive()) {
- sendRtspRequest("TEARDOWN" ,_content_base);
- shutdown(SockException(Err_shutdown,"teardown"));
+ if (!_content_base.empty()) {
+ sendRtspRequest("TEARDOWN", _content_base);
+ }
+ shutdown(SockException(Err_shutdown, "teardown"));
}
_md5_nonce.clear();
@@ -232,7 +234,7 @@ void RtspPlayer::sendSetup(unsigned int track_idx) {
}
break;
case Rtsp::RTP_MULTICAST: {
- sendRtspRequest("SETUP",baseUrl,{"Transport","Transport: RTP/AVP;multicast"});
+ sendRtspRequest("SETUP",baseUrl,{"Transport","RTP/AVP;multicast"});
}
break;
case Rtsp::RTP_UDP: {