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: {