diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c18493ac..81c002c9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -40,9 +40,7 @@ jobs: # https://github.com/sigstore/cosign-installer - name: Install cosign if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@d6a3abf1bdea83574e28d40543793018b6035605 - with: - cosign-release: 'v1.7.1' + uses: sigstore/cosign-installer@d572c9c13673d2e0a26fabf90b5748f36886883f - name: Set up QEMU uses: docker/setup-qemu-action@v2 diff --git a/.gitmodules b/.gitmodules index 7ef0bc71..c6211ba1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,7 @@ url = https://gitee.com/ireader/media-server [submodule "3rdpart/jsoncpp"] path = 3rdpart/jsoncpp - url = https://gitee.com/mirrors/jsoncpp.git \ No newline at end of file + url = https://gitee.com/mirrors/jsoncpp.git +[submodule "www/webassist"] + path = www/webassist + url = https://gitee.com/victor1002/zlm_webassist diff --git a/.gitmodules_github b/.gitmodules_github index ddd310e6..87b576ee 100644 --- a/.gitmodules_github +++ b/.gitmodules_github @@ -7,3 +7,6 @@ [submodule "3rdpart/jsoncpp"] path = 3rdpart/jsoncpp url = https://github.com/open-source-parsers/jsoncpp.git +[submodule "www/webassist"] + path = www/webassist + url = https://github.com/1002victor/zlm_webassist \ No newline at end of file diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index b11582c3..97871cfa 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit b11582c38e8dbbb8d93ca9ce33c9a0b0cd58f59a +Subproject commit 97871cfa78fcd2fae164243a8c653e323385772d diff --git a/3rdpart/jsoncpp b/3rdpart/jsoncpp index 8190e061..69098a18 160000 --- a/3rdpart/jsoncpp +++ b/3rdpart/jsoncpp @@ -1 +1 @@ -Subproject commit 8190e061bc2d95da37479a638aa2c9e483e58ec6 +Subproject commit 69098a18b9af0c47549d9a271c054d13ca92b006 diff --git a/3rdpart/media-server b/3rdpart/media-server index cdbb3d6b..3dc623a8 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit cdbb3d6b9ea254f454c6e466c5962af5ace01199 +Subproject commit 3dc623a899eee3810587fb267dbff770b626a55b diff --git a/AUTHORS b/AUTHORS index 57928af2..c26bfefb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -84,4 +84,7 @@ WuPeng [Luosh](https://github.com/Luosh) [linxiaoyan87](https://github.com/linxiaoyan) [waken](https://github.com/mc373906408) -[Deepslient](https://github.com/Deepslient) \ No newline at end of file +[Deepslient](https://github.com/Deepslient) +[imp_rayjay](https://github.com/rayjay214) +[ArmstrongCN](https://github.com/ArmstrongCN) +[leibnewton](https://github.com/leibnewton) \ No newline at end of file diff --git a/README.md b/README.md index 97fb5751..a32a5d9f 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ bash build_docker_images.sh - [BXC_gb28181Player](https://github.com/any12345com/BXC_gb28181Player) C++开发的支持国标GB28181协议的视频流播放器 - WEB管理网站 + - [zlm_webassist](https://github.com/1002victor/zlm_webassist) 本项目配套的前后端分离web管理项目 - [AKStreamNVR](https://github.com/langmansh/AKStreamNVR) 前后端分离web项目,支持webrtc播放 - SDK @@ -329,6 +330,10 @@ bash build_docker_images.sh [linxiaoyan87](https://github.com/linxiaoyan) [waken](https://github.com/mc373906408) [Deepslient](https://github.com/Deepslient) +[imp_rayjay](https://github.com/rayjay214) +[ArmstrongCN](https://github.com/ArmstrongCN) +[leibnewton](https://github.com/leibnewton) +[1002victor](https://github.com/1002victor) 同时感谢JetBrains对开源项目的支持,本项目使用CLion开发与调试: diff --git a/README_en.md b/README_en.md index 5eee72d1..0c8c6437 100644 --- a/README_en.md +++ b/README_en.md @@ -342,6 +342,7 @@ bash build_docker_images.sh ## Collaborative Projects - Visual management website + - [A backend management website for this project](https://github.com/1002victor/zlm_webassist) - [The latest web project with front-end and back-end separation, supporting webrtc playback](https://github.com/langmansh/AKStreamNVR) - [Management web site based on ZLMediaKit master branch](https://gitee.com/kkkkk5G/MediaServerUI) - [Management web site based on ZLMediaKit branch](https://github.com/chenxiaolei/ZLMediaKit_NVR_UI) @@ -493,6 +494,10 @@ Thanks to all those who have supported this project in various ways, including b [linxiaoyan87](https://github.com/linxiaoyan) [waken](https://github.com/mc373906408) [Deepslient](https://github.com/Deepslient) +[imp_rayjay](https://github.com/rayjay214) +[ArmstrongCN](https://github.com/ArmstrongCN) +[leibnewton](https://github.com/leibnewton) +[1002victor](https://github.com/1002victor) Also thank to JetBrains for their support for open source project, we developed and debugged zlmediakit with CLion: diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json index fdda9d2a..784bb60c 100644 --- a/postman/ZLMediaKit.postman_collection.json +++ b/postman/ZLMediaKit.postman_collection.json @@ -1,9 +1,10 @@ { "info": { - "_postman_id": "39e8a1df-cc8e-4e3f-bf5e-197c86e7bf0f", + "_postman_id": "509e5f6b-728c-4d5f-b3e8-521d76b2cc7a", "name": "ZLMediaKit", "description": "媒体服务器", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "29185956" }, "item": [ { @@ -918,7 +919,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{ZLMediaKit_URL}}/index/api/broadcastMessage?secret={{ZLMediaKit_secret}}&schema=rtsp&vhost={{defaultVhost}}&app=live&stream=test&msg=Hello zlmediakit123", + "raw": "{{ZLMediaKit_URL}}/index/api/broadcastMessage?secret={{ZLMediaKit_secret}}&schema=rtsp&vhost={{defaultVhost}}&app=live&stream=test&msg=Hello ZLMediakit", "host": [ "{{ZLMediaKit_URL}}" ], @@ -1247,7 +1248,7 @@ }, { "key": "stamp", - "value": 1000, + "value": "1000", "description": "要设置的录像播放位置" } ] @@ -1478,6 +1479,53 @@ }, "response": [] }, + { + "name": "创建多路复用RTP服务器(openRtpServerMultiplex)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{ZLMediaKit_URL}}/index/api/openRtpServer?secret={{ZLMediaKit_secret}}&port=0&tcp_mode=1&stream_id=test", + "host": [ + "{{ZLMediaKit_URL}}" + ], + "path": [ + "index", + "api", + "openRtpServer" + ], + "query": [ + { + "key": "secret", + "value": "{{ZLMediaKit_secret}}", + "description": "api操作密钥(配置文件配置)" + }, + { + "key": "port", + "value": "0", + "description": "绑定的端口,0时为随机端口" + }, + { + "key": "tcp_mode", + "value": "1", + "description": "tcp模式,0时为不启用tcp监听,1时为启用tcp监听" + }, + { + "key": "stream_id", + "value": "test", + "description": "该端口绑定的流id\n" + }, + { + "key": "only_audio", + "value": "0", + "description": "是否为单音频track,用于语音对讲", + "disabled": true + } + ] + } + }, + "response": [] + }, { "name": "连接RTP服务器(connectRtpServer)", "request": { @@ -1710,10 +1758,16 @@ "value": "obs", "description": "流id,例如 obs" }, + { + "key": "ssrc_multi_send", + "value": "0", + "description": "是否支持同ssrc推流到多个上级服务器,该参数非必选参数 默认false", + "disabled": true + }, { "key": "ssrc", "value": "1", - "description": "rtp推流的ssrc,ssrc不同时,可以推流到多个上级服务器" + "description": "rtp推流的ssrc" }, { "key": "dst_url", diff --git a/server/WebApi.cpp b/server/WebApi.cpp index e8081756..63b4d5e3 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -370,6 +370,7 @@ Value makeMediaSourceJson(MediaSource &media){ obj["loss"] = loss; } obj["frames"] = track->getFrames(); + obj["duration"] = track->getDuration(); switch(codec_type){ case TrackAudio : { auto audio_track = dynamic_pointer_cast(track); @@ -403,7 +404,7 @@ Value makeMediaSourceJson(MediaSource &media){ } #if defined(ENABLE_RTPPROXY) -uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio) { +uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio, bool multiplex) { lock_guard lck(s_rtpServerMapMtx); if (s_rtpServerMap.find(stream_id) != s_rtpServerMap.end()) { //为了防止RtpProcess所有权限混乱的问题,不允许重复添加相同的stream_id @@ -411,7 +412,7 @@ uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mod } RtpServer::Ptr server = std::make_shared(); - server->start(local_port, stream_id, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc, only_audio); + server->start(local_port, stream_id, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc, only_audio, multiplex); server->setOnDetach([stream_id]() { //设置rtp超时移除事件 lock_guard lck(s_rtpServerMapMtx); @@ -1182,6 +1183,25 @@ void installWebApi() { val["port"] = port; }); + api_regist("/index/api/openRtpServerMultiplex", [](API_ARGS_MAP) { + CHECK_SECRET(); + CHECK_ARGS("port", "stream_id"); + auto stream_id = allArgs["stream_id"]; + auto tcp_mode = allArgs["tcp_mode"].as(); + if (allArgs["enable_tcp"].as() && !tcp_mode) { + // 兼容老版本请求,新版本去除enable_tcp参数并新增tcp_mode参数 + tcp_mode = 1; + } + + auto port = openRtpServer( + allArgs["port"], stream_id, tcp_mode, "::", true, 0, allArgs["only_audio"].as(),true); + if (port == 0) { + throw InvalidArgsException("该stream_id已存在"); + } + // 回复json + val["port"] = port; + }); + api_regist("/index/api/connectRtpServer", [](API_ARGS_MAP_ASYNC) { CHECK_SECRET(); CHECK_ARGS("stream_id", "dst_url", "dst_port"); @@ -1244,6 +1264,7 @@ void installWebApi() { args.passive = false; args.dst_url = allArgs["dst_url"]; args.dst_port = allArgs["dst_port"]; + args.ssrc_multi_send = allArgs["ssrc_multi_send"].empty() ? false : allArgs["ssrc_multi_send"].as(); args.ssrc = allArgs["ssrc"]; args.is_udp = allArgs["is_udp"]; args.src_port = allArgs["src_port"]; @@ -1491,11 +1512,15 @@ void installWebApi() { // http://127.0.0.1/index/api/deleteRecordDirectroy?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01-01 api_regist("/index/api/deleteRecordDirectory", [](API_ARGS_MAP) { CHECK_SECRET(); - CHECK_ARGS("vhost", "app", "stream"); + CHECK_ARGS("vhost", "app", "stream", "period"); auto tuple = MediaTuple{allArgs["vhost"], allArgs["app"], allArgs["stream"]}; auto record_path = Recorder::getRecordPath(Recorder::type_mp4, tuple, allArgs["customized_path"]); auto period = allArgs["period"]; record_path = record_path + period + "/"; + auto name = allArgs["name"]; + if (!name.empty()) { + record_path += name; + } int result = File::delete_file(record_path.data()); if (result) { // 不等于0时代表失败 @@ -1911,24 +1936,28 @@ void installWebApi() { void unInstallWebApi(){ { lock_guard lck(s_proxyMapMtx); - s_proxyMap.clear(); + auto proxyMap(std::move(s_proxyMap)); + proxyMap.clear(); } { lock_guard lck(s_ffmpegMapMtx); - s_ffmpegMap.clear(); + auto ffmpegMap(std::move(s_ffmpegMap)); + ffmpegMap.clear(); } { lock_guard lck(s_proxyPusherMapMtx); - s_proxyPusherMap.clear(); + auto proxyPusherMap(std::move(s_proxyPusherMap)); + proxyPusherMap.clear(); } { #if defined(ENABLE_RTPPROXY) RtpSelector::Instance().clear(); lock_guard lck(s_rtpServerMapMtx); - s_rtpServerMap.clear(); + auto rtpServerMap(std::move(s_rtpServerMap)); + rtpServerMap.clear(); #endif } NoticeCenter::Instance().delListener(&web_api_tag); diff --git a/server/WebApi.h b/server/WebApi.h index 460d0132..f7ec0cb3 100755 --- a/server/WebApi.h +++ b/server/WebApi.h @@ -239,7 +239,7 @@ void installWebApi(); void unInstallWebApi(); #if defined(ENABLE_RTPPROXY) -uint16_t openRtpServer(uint16_t local_port, const std::string &stream_id, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio); +uint16_t openRtpServer(uint16_t local_port, const std::string &stream_id, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio, bool multiplex=false); void connectRtpServer(const std::string &stream_id, const std::string &dst_url, uint16_t dst_port, const std::function &cb); bool closeRtpServer(const std::string &stream_id); #endif diff --git a/server/WebHook.cpp b/server/WebHook.cpp index d05c4f60..43329927 100755 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -674,7 +674,7 @@ void installWebHook() { }); }); - NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::KBroadcastRtpServerTimeout, [](BroadcastRtpServerTimeoutArgs) { + NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastRtpServerTimeout, [](BroadcastRtpServerTimeoutArgs) { GET_CONFIG(string, rtp_server_timeout, Hook::kOnRtpServerTimeout); if (!hook_enable || rtp_server_timeout.empty()) { return; @@ -703,4 +703,4 @@ void unInstallWebHook() { void onProcessExited() { reportServerExited(); -} \ No newline at end of file +} diff --git a/src/Common/JemallocUtil.cpp b/src/Common/JemallocUtil.cpp index fa2de64a..d8b8414e 100644 --- a/src/Common/JemallocUtil.cpp +++ b/src/Common/JemallocUtil.cpp @@ -12,6 +12,7 @@ #include "JemallocUtil.h" #include "Util/logger.h" #ifdef USE_JEMALLOC +#include #include #include #endif diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index bcf75a09..73306385 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -99,11 +99,13 @@ public: // rtp采用ps还是es方式 bool use_ps = true; //发送es流时指定是否只发送纯音频流 - bool only_audio = true; + bool only_audio = false; //tcp被动方式 bool passive = false; // rtp payload type uint8_t pt = 96; + //是否支持同ssrc多服务器发送 + bool ssrc_multi_send = false; // 指定rtp ssrc std::string ssrc; // 指定本地发送端口 diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index 19dd41f7..d31ab77f 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -45,6 +45,7 @@ static string getTrackInfoStr(const TrackSource *track_src){ _StrPrinter codec_info; auto tracks = track_src->getTracks(true); for (auto &track : tracks) { + track->update(); auto codec_type = track->getTrackType(); codec_info << track->getCodecName(); switch (codec_type) { @@ -294,12 +295,14 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE auto ring = _ring; auto ssrc = args.ssrc; + auto ssrc_multi_send = args.ssrc_multi_send; auto tracks = getTracks(false); auto poller = getOwnerPoller(sender); auto rtp_sender = std::make_shared(poller); + weak_ptr weak_self = shared_from_this(); - rtp_sender->startSend(args, [ssrc, weak_self, rtp_sender, cb, tracks, ring, poller](uint16_t local_port, const SockException &ex) mutable { + rtp_sender->startSend(args, [ssrc,ssrc_multi_send, weak_self, rtp_sender, cb, tracks, ring, poller](uint16_t local_port, const SockException &ex) mutable { cb(local_port, ex); auto strong_self = weak_self.lock(); if (!strong_self || ex) { @@ -328,7 +331,10 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE // 可能归属线程发生变更 strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() { - strong_self->_rtp_sender[ssrc] = std::move(reader); + if(!ssrc_multi_send) { + strong_self->_rtp_sender.erase(ssrc); + } + strong_self->_rtp_sender.emplace(ssrc,reader); }); }); #else diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 3e6d7e7f..c35ef5e9 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -173,7 +173,7 @@ private: toolkit::Ticker _last_check; Stamp _stamp[2]; std::weak_ptr _track_listener; - std::unordered_map _rtp_sender; + std::unordered_multimap _rtp_sender; FMP4MediaSourceMuxer::Ptr _fmp4; RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 6e75e27d..603c434c 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -57,7 +57,7 @@ const string kBroadcastNotFoundStream = "kBroadcastNotFoundStream"; const string kBroadcastStreamNoneReader = "kBroadcastStreamNoneReader"; const string kBroadcastHttpBeforeAccess = "kBroadcastHttpBeforeAccess"; const string kBroadcastSendRtpStopped = "kBroadcastSendRtpStopped"; -const string KBroadcastRtpServerTimeout = "KBroadcastRtpServerTimeout"; +const string kBroadcastRtpServerTimeout = "kBroadcastRtpServerTimeout"; } // namespace Broadcast diff --git a/src/Common/config.h b/src/Common/config.h index 341f1d6c..25f36271 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -106,7 +106,7 @@ extern const std::string kBroadcastReloadConfig; #define BroadcastReloadConfigArgs void // rtp server 超时 -extern const std::string KBroadcastRtpServerTimeout; +extern const std::string kBroadcastRtpServerTimeout; #define BroadcastRtpServerTimeoutArgs uint16_t &local_port, const string &stream_id,int &tcp_mode, bool &re_use_port, uint32_t &ssrc #define ReloadConfigTag ((void *)(0xFF)) diff --git a/src/Common/strCoding.cpp b/src/Common/strCoding.cpp index 144bf1f2..4f6c4e53 100644 --- a/src/Common/strCoding.cpp +++ b/src/Common/strCoding.cpp @@ -36,20 +36,21 @@ void UnicodeToUTF8(char *pOut, const wchar_t *pText) { return; } -char CharToInt(char ch) { - if (ch >= '0' && ch <= '9')return (char) (ch - '0'); - if (ch >= 'a' && ch <= 'f')return (char) (ch - 'a' + 10); - if (ch >= 'A' && ch <= 'F')return (char) (ch - 'A' + 10); +char HexCharToBin(char ch) { + if (ch >= '0' && ch <= '9') return (char)(ch - '0'); + if (ch >= 'a' && ch <= 'f') return (char)(ch - 'a' + 10); + if (ch >= 'A' && ch <= 'F') return (char)(ch - 'A' + 10); return -1; } -char StrToBin(const char *str) { - char tempWord[2]; - char chn; - tempWord[0] = CharToInt(str[0]); //make the B to 11 -- 00001011 - tempWord[1] = CharToInt(str[1]); //make the 0 to 0 -- 00000000 - chn = (tempWord[0] << 4) | tempWord[1]; //to change the BO to 10110000 - return chn; +char HexStrToBin(const char *str) { + auto high = HexCharToBin(str[0]); + auto low = HexCharToBin(str[1]); + if (high == -1 || low == -1) { + // 无法把16进制字符串转换为二进制 + return -1; + } + return (high << 4) | low; } string strCoding::UrlEncode(const string &str) { @@ -70,26 +71,51 @@ string strCoding::UrlEncode(const string &str) { string strCoding::UrlDecode(const string &str) { string output; - char tmp[2]; size_t i = 0, len = str.length(); while (i < len) { if (str[i] == '%') { - if (i > len - 3) { - //防止内存溢出 + if (i + 3 > len) { + // %后面必须还有两个字节才会反转义 + output.append(str, i, len - i); break; } - tmp[0] = str[i + 1]; - tmp[1] = str[i + 2]; - output += StrToBin(tmp); - i = i + 3; + char ch = HexStrToBin(&(str[i + 1])); + if (ch == -1) { + // %后面两个字节不是16进制字符串,转义失败;直接拼接3个原始字符 + output.append(str, i, 3); + } else { + output += ch; + } + i += 3; } else { output += str[i]; - i++; + ++i; } } return output; } +#if 0 +#include "Util/onceToken.h" +static toolkit::onceToken token([]() { + auto str0 = strCoding::UrlDecode( + "rtsp%3A%2F%2Fadmin%3AJm13317934%25jm%40111.47.84.69%3A554%2FStreaming%2FChannels%2F101%3Ftransportmode%3Dunicast%26amp%3Bprofile%3DProfile_1"); + auto str1 = strCoding::UrlDecode("%j1"); // 测试%后面两个字节不是16进制字符串 + auto str2 = strCoding::UrlDecode("%a"); // 测试%后面字节数不够 + auto str3 = strCoding::UrlDecode("%"); // 测试只有% + auto str4 = strCoding::UrlDecode("%%%"); // 测试多个% + auto str5 = strCoding::UrlDecode("%%%%40"); // 测试多个非法%后恢复正常解析 + auto str6 = strCoding::UrlDecode("Jm13317934%jm"); // 测试多个非法%后恢复正常解析 + cout << str0 << endl; + cout << str1 << endl; + cout << str2 << endl; + cout << str3 << endl; + cout << str4 << endl; + cout << str5 << endl; + cout << str6 << endl; +}); +#endif + ///////////////////////////////windows专用/////////////////////////////////// #if defined(_WIN32) void UnicodeToGB2312(char* pOut, wchar_t uData) diff --git a/src/Extension/AAC.cpp b/src/Extension/AAC.cpp index 1047a8b3..27f18359 100644 --- a/src/Extension/AAC.cpp +++ b/src/Extension/AAC.cpp @@ -21,53 +21,56 @@ namespace mediakit{ #ifndef ENABLE_MP4 unsigned const samplingFrequencyTable[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; -class AdtsHeader{ +class AdtsHeader { public: - unsigned int syncword = 0; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始 - unsigned int id; //1 bslbf MPEG 标示符, 设置为1 - unsigned int layer; //2 uimsbf Indicates which layer is used. Set to ‘00’ - unsigned int protection_absent; //1 bslbf 表示是否误码校验 - unsigned int profile; //2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC - unsigned int sf_index; //4 uimsbf 表示使用的采样率下标 - unsigned int private_bit; //1 bslbf - unsigned int channel_configuration; //3 uimsbf 表示声道数 - unsigned int original; //1 bslbf - unsigned int home; //1 bslbf - //下面的为改变的参数即每一帧都不同 - unsigned int copyright_identification_bit; //1 bslbf - unsigned int copyright_identification_start; //1 bslbf + unsigned int syncword = 0; // 12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始 + unsigned int id; // 1 bslbf MPEG 标示符, 设置为1 + unsigned int layer; // 2 uimsbf Indicates which layer is used. Set to ‘00’ + unsigned int protection_absent; // 1 bslbf 表示是否误码校验 + unsigned int profile; // 2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC + unsigned int sf_index; // 4 uimsbf 表示使用的采样率下标 + unsigned int private_bit; // 1 bslbf + unsigned int channel_configuration; // 3 uimsbf 表示声道数 + unsigned int original; // 1 bslbf + unsigned int home; // 1 bslbf + // 下面的为改变的参数即每一帧都不同 + unsigned int copyright_identification_bit; // 1 bslbf + unsigned int copyright_identification_start; // 1 bslbf unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block - unsigned int adts_buffer_fullness; //11 bslbf 0x7FF 说明是码率可变的码流 - //no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. - //所以说number_of_raw_data_blocks_in_frame == 0 - //表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) - unsigned int no_raw_data_blocks_in_frame; //2 uimsfb + unsigned int adts_buffer_fullness; // 11 bslbf 0x7FF 说明是码率可变的码流 + // no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. + // 所以说number_of_raw_data_blocks_in_frame == 0 + // 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) + unsigned int no_raw_data_blocks_in_frame; // 2 uimsfb }; static void dumpAdtsHeader(const AdtsHeader &hed, uint8_t *out) { - out[0] = (hed.syncword >> 4 & 0xFF); //8bit - out[1] = (hed.syncword << 4 & 0xF0); //4 bit - out[1] |= (hed.id << 3 & 0x08); //1 bit - out[1] |= (hed.layer << 1 & 0x06); //2bit - out[1] |= (hed.protection_absent & 0x01); //1 bit + out[0] = (hed.syncword >> 4 & 0xFF); // 8bit + out[1] = (hed.syncword << 4 & 0xF0); // 4 bit + out[1] |= (hed.id << 3 & 0x08); // 1 bit + out[1] |= (hed.layer << 1 & 0x06); // 2bit + out[1] |= (hed.protection_absent & 0x01); // 1 bit out[2] = (hed.profile << 6 & 0xC0); // 2 bit - out[2] |= (hed.sf_index << 2 & 0x3C); //4bit - out[2] |= (hed.private_bit << 1 & 0x02); //1 bit - out[2] |= (hed.channel_configuration >> 2 & 0x03); //1 bit - out[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit - out[3] |= (hed.original << 5 & 0x20); //1 bit - out[3] |= (hed.home << 4 & 0x10); //1 bit - out[3] |= (hed.copyright_identification_bit << 3 & 0x08); //1 bit - out[3] |= (hed.copyright_identification_start << 2 & 0x04); //1 bit - out[3] |= (hed.aac_frame_length >> 11 & 0x03); //2 bit - out[4] = (hed.aac_frame_length >> 3 & 0xFF); //8 bit - out[5] = (hed.aac_frame_length << 5 & 0xE0); //3 bit - out[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); //5 bit - out[6] = (hed.adts_buffer_fullness << 2 & 0xFC); //6 bit - out[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); //2 bit + out[2] |= (hed.sf_index << 2 & 0x3C); // 4bit + out[2] |= (hed.private_bit << 1 & 0x02); // 1 bit + out[2] |= (hed.channel_configuration >> 2 & 0x03); // 1 bit + out[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit + out[3] |= (hed.original << 5 & 0x20); // 1 bit + out[3] |= (hed.home << 4 & 0x10); // 1 bit + out[3] |= (hed.copyright_identification_bit << 3 & 0x08); // 1 bit + out[3] |= (hed.copyright_identification_start << 2 & 0x04); // 1 bit + out[3] |= (hed.aac_frame_length >> 11 & 0x03); // 2 bit + out[4] = (hed.aac_frame_length >> 3 & 0xFF); // 8 bit + out[5] = (hed.aac_frame_length << 5 & 0xE0); // 3 bit + out[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); // 5 bit + out[6] = (hed.adts_buffer_fullness << 2 & 0xFC); // 6 bit + out[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); // 2 bit } -static void parseAacConfig(const string &config, AdtsHeader &adts) { +static bool parseAacConfig(const string &config, AdtsHeader &adts) { + if (config.size() < 2) { + return false; + } uint8_t cfg1 = config[0]; uint8_t cfg2 = config[1]; @@ -94,6 +97,7 @@ static void parseAacConfig(const string &config, AdtsHeader &adts) { adts.aac_frame_length = 7; adts.adts_buffer_fullness = 2047; adts.no_raw_data_blocks_in_frame = 0; + return true; } #endif// ENABLE_MP4 @@ -168,10 +172,12 @@ int dumpAacConfig(const string &config, size_t length, uint8_t *out, size_t out_ #endif } -bool parseAacConfig(const string &config, int &samplerate, int &channels){ +bool parseAacConfig(const string &config, int &samplerate, int &channels) { #ifndef ENABLE_MP4 AdtsHeader header; - parseAacConfig(config, header); + if (!parseAacConfig(config, header)) { + return false; + } samplerate = samplingFrequencyTable[header.sf_index]; channels = header.channel_configuration; return true; @@ -326,11 +332,14 @@ bool AACTrack::inputFrame_l(const Frame::Ptr &frame) { return false; } +bool AACTrack::update() { + return parseAacConfig(_cfg, _sampleRate, _channel); +} + void AACTrack::onReady() { - if (_cfg.size() < 2) { - return; + if (!parseAacConfig(_cfg, _sampleRate, _channel)) { + _cfg.clear(); } - parseAacConfig(_cfg, _sampleRate, _channel); } Track::Ptr AACTrack::clone() { @@ -342,6 +351,7 @@ Sdp::Ptr AACTrack::getSdp() { WarnL << getCodecName() << " Track未准备好"; return nullptr; } + update(); return std::make_shared(getConfig(), getAudioSampleRate(), getAudioChannel(), getBitRate() / 1024); } diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index b95fc6f2..32cf934b 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -52,6 +52,7 @@ public: int getAudioSampleRate() const override; int getAudioSampleBit() const override; bool inputFrame(const Frame::Ptr &frame) override; + bool update() override; private: void onReady(); diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 3609ff41..a1b2b6dc 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -16,6 +16,7 @@ #include #include "Util/List.h" #include "Util/TimeTicker.h" +#include "Common/Stamp.h" #include "Network/Buffer.h" namespace mediakit { @@ -361,11 +362,18 @@ public: return _gop_interval_ms; } + int64_t getDuration() const { + std::lock_guard lck(_mtx); + return _stamp.getRelativeStamp(); + } + private: void doStatistics(const Frame::Ptr &frame) { if (!frame->configFrame() && !frame->dropAble()) { // 忽略配置帧与可丢弃的帧 ++_frames; + int64_t out; + _stamp.revise(frame->dts(), frame->pts(), out, out); if (frame->keyFrame() && frame->getTrackType() == TrackVideo) { // 遇视频关键帧时统计 ++_video_key_frames; @@ -384,6 +392,7 @@ private: uint64_t _last_frames = 0; uint64_t _frames = 0; uint64_t _video_key_frames = 0; + Stamp _stamp; mutable std::recursive_mutex _mtx; std::map _delegates; }; diff --git a/src/Extension/H264.cpp b/src/Extension/H264.cpp index eeccd600..eda6b188 100644 --- a/src/Extension/H264.cpp +++ b/src/Extension/H264.cpp @@ -168,6 +168,10 @@ bool H264Track::inputFrame(const Frame::Ptr &frame) { return ret; } +bool H264Track::update() { + return getAVCInfo(_sps, _width, _height, _fps); +} + void H264Track::onReady() { if (!getAVCInfo(_sps, _width, _height, _fps)) { _sps.clear(); diff --git a/src/Extension/H264.h b/src/Extension/H264.h index 30a8f747..0afe593d 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -128,6 +128,7 @@ public: int getVideoWidth() const override; float getVideoFps() const override; bool inputFrame(const Frame::Ptr &frame) override; + bool update() override; private: void onReady(); diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index 28c775c6..442205a6 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -44,13 +44,15 @@ H264Frame::Ptr H264RtpDecoder::obtainFrame() { bool H264RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) { auto seq = rtp->getSeq(); - auto ret = decodeRtp(rtp); - if (!_gop_dropped && seq != (uint16_t) (_last_seq + 1) && _last_seq) { + auto last_is_gop = _is_gop; + _is_gop = decodeRtp(rtp); + if (!_gop_dropped && seq != (uint16_t)(_last_seq + 1) && _last_seq) { _gop_dropped = true; WarnL << "start drop h264 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString(); } _last_seq = seq; - return ret; + // 确保有sps rtp的时候,gop从sps开始;否则从关键帧开始 + return _is_gop && !last_is_gop; } /* @@ -74,7 +76,7 @@ bool H264RtpDecoder::singleFrame(const RtpPacket::Ptr &rtp, const uint8_t *ptr, _frame->_buffer.assign("\x00\x00\x00\x01", 4); _frame->_buffer.append((char *) ptr, size); _frame->_pts = stamp; - auto key = _frame->keyFrame(); + auto key = _frame->keyFrame() || _frame->configFrame(); outputFrame(rtp, _frame); return key; } @@ -127,7 +129,7 @@ bool H264RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz if (!fu->end_bit) { //非末尾包 - return fu->start_bit ? _frame->keyFrame() : false; + return fu->start_bit ? (_frame->keyFrame() || _frame->configFrame()) : false; } //确保下一次fu必须收到第一个包 diff --git a/src/Extension/H264Rtp.h b/src/Extension/H264Rtp.h index 31200cb8..98d49cda 100644 --- a/src/Extension/H264Rtp.h +++ b/src/Extension/H264Rtp.h @@ -51,6 +51,7 @@ private: void outputFrame(const RtpPacket::Ptr &rtp, const H264Frame::Ptr &frame); private: + bool _is_gop = false; bool _gop_dropped = false; bool _fu_dropped = true; uint16_t _last_seq = 0; diff --git a/src/Extension/H265.cpp b/src/Extension/H265.cpp index 9985a9cb..926ab2a4 100644 --- a/src/Extension/H265.cpp +++ b/src/Extension/H265.cpp @@ -144,6 +144,10 @@ bool H265Track::inputFrame_l(const Frame::Ptr &frame) { return ret; } +bool H265Track::update() { + return getHEVCInfo(_vps, _sps, _width, _height, _fps); +} + void H265Track::onReady() { if (!getHEVCInfo(_vps, _sps, _width, _height, _fps)) { _vps.clear(); diff --git a/src/Extension/H265.h b/src/Extension/H265.h index 521663f4..912f1f46 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -150,6 +150,7 @@ public: int getVideoHeight() const override; float getVideoFps() const override; bool inputFrame(const Frame::Ptr &frame) override; + bool update() override; private: void onReady(); diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index 23bbb109..22866ff4 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -163,7 +163,7 @@ bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz if (!e_bit) { //非末尾包 - return s_bit ? _frame->keyFrame() : false; + return s_bit ? (_frame->keyFrame() || _frame->configFrame()) : false; } //确保下一次fu必须收到第一个包 @@ -175,13 +175,15 @@ bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz bool H265RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool) { auto seq = rtp->getSeq(); - auto ret = decodeRtp(rtp); + auto last_is_gop = _is_gop; + _is_gop = decodeRtp(rtp); if (!_gop_dropped && seq != (uint16_t) (_last_seq + 1) && _last_seq) { _gop_dropped = true; WarnL << "start drop h265 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString(); } _last_seq = seq; - return ret; + // 确保有sps rtp的时候,gop从sps开始;否则从关键帧开始 + return _is_gop && !last_is_gop; } bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) { @@ -220,7 +222,7 @@ bool H265RtpDecoder::singleFrame(const RtpPacket::Ptr &rtp, const uint8_t *ptr, _frame->_buffer.assign("\x00\x00\x00\x01", 4); _frame->_buffer.append((char *) ptr, size); _frame->_pts = stamp; - auto key = _frame->keyFrame(); + auto key = _frame->keyFrame() || _frame->configFrame(); outputFrame(rtp, _frame); return key; } diff --git a/src/Extension/H265Rtp.h b/src/Extension/H265Rtp.h index cd9702f4..569d8091 100644 --- a/src/Extension/H265Rtp.h +++ b/src/Extension/H265Rtp.h @@ -51,6 +51,7 @@ private: void outputFrame(const RtpPacket::Ptr &rtp, const H265Frame::Ptr &frame); private: + bool _is_gop = false; bool _using_donl_field = false; bool _gop_dropped = false; bool _fu_dropped = true; diff --git a/src/Extension/Track.h b/src/Extension/Track.h index e7aa2b7c..3a1b8519 100644 --- a/src/Extension/Track.h +++ b/src/Extension/Track.h @@ -39,6 +39,11 @@ public: */ virtual Track::Ptr clone() = 0; + /** + * 更新track信息,比如触发sps/pps解析 + */ + virtual bool update() { return false; } + /** * 生成sdp * @return sdp对象 diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index 328872e6..dd1546db 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -80,7 +80,7 @@ void HttpClient::sendRequest(const string &url) { } if (!alive() || host_changed) { - startConnect(host, port, _wait_header_ms); + startConnect(host, port, _wait_header_ms / 1000.0f); } else { SockException ex; onConnect_l(ex); diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 69407928..c6d390d1 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -166,11 +166,11 @@ void HttpSession::onError(const SockException &err) { if (_is_live_stream) { // flv/ts播放器 uint64_t duration = _ticker.createdTime() / 1000; - WarnP(this) << "FLV/TS/FMP4播放器(" << _mediaInfo.shortUrl() << ")断开:" << err << ",耗时(s):" << duration; + WarnP(this) << "FLV/TS/FMP4播放器(" << _media_info.shortUrl() << ")断开:" << err << ",耗时(s):" << duration; GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); if (_total_bytes_usage >= iFlowThreshold * 1024) { - NOTICE_EMIT(BroadcastFlowReportArgs, Broadcast::kBroadcastFlowReport, _mediaInfo, _total_bytes_usage, duration, true, *this); + NOTICE_EMIT(BroadcastFlowReportArgs, Broadcast::kBroadcastFlowReport, _media_info, _total_bytes_usage, duration, true, *this); } return; } @@ -263,9 +263,9 @@ bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix } // 解析带上协议+参数完整的url - _mediaInfo.parse(schema + "://" + _parser["Host"] + url); + _media_info.parse(schema + "://" + _parser["Host"] + url); - if (_mediaInfo.app.empty() || _mediaInfo.stream.empty()) { + if (_media_info.app.empty() || _media_info.stream.empty()) { // url不合法 return false; } @@ -288,7 +288,7 @@ bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix } // 异步查找直播流 - MediaSource::findAsync(strong_self->_mediaInfo, strong_self, [weak_self, close_flag, cb](const MediaSource::Ptr &src) { + MediaSource::findAsync(strong_self->_media_info, strong_self, [weak_self, close_flag, cb](const MediaSource::Ptr &src) { auto strong_self = weak_self.lock(); if (!strong_self) { // 本对象已经销毁 @@ -311,7 +311,7 @@ bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix } }; - auto flag = NOTICE_EMIT(BroadcastMediaPlayedArgs, Broadcast::kBroadcastMediaPlayed, _mediaInfo, invoker, *this); + auto flag = NOTICE_EMIT(BroadcastMediaPlayedArgs, Broadcast::kBroadcastMediaPlayed, _media_info, invoker, *this); if (!flag) { // 该事件无人监听,默认不鉴权 onRes(""); diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index 513f8b63..98b43d71 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -124,7 +124,7 @@ private: void setSocketFlags(); protected: - MediaInfo _mediaInfo; + MediaInfo _media_info; private: bool _is_live_stream = false; diff --git a/src/Http/WebSocketSession.h b/src/Http/WebSocketSession.h index 96021c4d..8978330c 100644 --- a/src/Http/WebSocketSession.h +++ b/src/Http/WebSocketSession.h @@ -69,7 +69,7 @@ template class SessionCreator { public: //返回的Session必须派生于SendInterceptor,可以返回null - toolkit::Session::Ptr operator()(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock){ + toolkit::Session::Ptr operator()(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock, mediakit::WebSocketHeader::Type &data_type){ return std::make_shared >(header,parent,pSock); } }; @@ -128,7 +128,8 @@ protected: */ bool onWebSocketConnect(const mediakit::Parser &header) override{ //创建websocket session类 - _session = _creator(header, *this, HttpSessionType::getSock()); + auto data_type = DataType; + _session = _creator(header, *this, HttpSessionType::getSock(), data_type); if (!_session) { // 此url不允许创建websocket连接 return false; @@ -140,13 +141,13 @@ protected: //此处截取数据并进行websocket协议打包 std::weak_ptr weakSelf = std::static_pointer_cast(HttpSessionType::shared_from_this()); - std::dynamic_pointer_cast(_session)->setOnBeforeSendCB([weakSelf](const toolkit::Buffer::Ptr &buf) { + std::dynamic_pointer_cast(_session)->setOnBeforeSendCB([weakSelf, data_type](const toolkit::Buffer::Ptr &buf) { auto strongSelf = weakSelf.lock(); if (strongSelf) { mediakit::WebSocketHeader header; header._fin = true; header._reserved = 0; - header._opcode = DataType; + header._opcode = data_type; header._mask_flag = false; strongSelf->HttpSessionType::encode(header, buf); } diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 2a319ff8..73e46b71 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -70,6 +70,7 @@ void PlayerProxy::setTranslationInfo() _transtalion_info.stream_info.clear(); auto tracks = _muxer->getTracks(); for (auto &track : tracks) { + track->update(); _transtalion_info.stream_info.emplace_back(); auto &back = _transtalion_info.stream_info.back(); back.bitrate = track->getBitRate(); diff --git a/src/Record/MP4Demuxer.cpp b/src/Record/MP4Demuxer.cpp index 02c66976..87936137 100644 --- a/src/Record/MP4Demuxer.cpp +++ b/src/Record/MP4Demuxer.cpp @@ -16,8 +16,10 @@ #include "Extension/AAC.h" #include "Extension/G711.h" #include "Extension/Opus.h" -using namespace toolkit; +#include "Extension/JPEG.h" + using namespace std; +using namespace toolkit; namespace mediakit { @@ -105,8 +107,9 @@ void MP4Demuxer::onVideoTrack(uint32_t track, uint8_t object, int width, int hei video->inputFrame(std::make_shared((char *)config, size, 0, 0,4)); } } - } break; + } + case MOV_OBJECT_HEVC: { auto video = std::make_shared(); _track_to_codec.emplace(track,video); @@ -120,11 +123,16 @@ void MP4Demuxer::onVideoTrack(uint32_t track, uint8_t object, int width, int hei video->inputFrame(std::make_shared((char *) config, size, 0, 0,4)); } } + break; } + + case MOV_OBJECT_JPEG: { + auto video = std::make_shared(); + _track_to_codec.emplace(track,video); break; - default: - WarnL << "不支持该编码类型的MP4,已忽略:" << getObjectName(object); - break; + } + + default: WarnL << "不支持该编码类型的MP4,已忽略:" << getObjectName(object); break; } } @@ -243,6 +251,11 @@ Frame::Ptr MP4Demuxer::makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int6 break; } + case CodecJPEG: { + ret = std::make_shared(buf, (uint64_t)dts, 0, DATA_OFFSET); + break; + } + case CodecAAC: { AACTrack::Ptr track = dynamic_pointer_cast(it->second); assert(track); @@ -283,4 +296,4 @@ uint64_t MP4Demuxer::getDurationMS() const { } }//namespace mediakit -#endif// ENABLE_MP4 \ No newline at end of file +#endif// ENABLE_MP4 diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index c79cc132..5458a31d 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -198,6 +198,7 @@ bool MP4MuxerInterface::addTrack(const Track::Ptr &track) { return false; } + track->update(); switch (track->getCodecId()) { case CodecG711A: case CodecG711U: diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index 5373b8a1..59a0d590 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -34,8 +34,12 @@ MP4Recorder::MP4Recorder(const string &path, const string &vhost, const string & } MP4Recorder::~MP4Recorder() { - flush(); - closeFile(); + try { + flush(); + closeFile(); + } catch (std::exception &ex) { + WarnL << ex.what(); + } } void MP4Recorder::createFile() { diff --git a/src/Rtmp/Rtmp.cpp b/src/Rtmp/Rtmp.cpp index 140afeed..33c46f11 100644 --- a/src/Rtmp/Rtmp.cpp +++ b/src/Rtmp/Rtmp.cpp @@ -57,6 +57,7 @@ AudioMeta::AudioMeta(const AudioTrack::Ptr &audio) { } uint8_t getAudioRtmpFlags(const Track::Ptr &track) { + track->update(); switch (track->getTrackType()) { case TrackAudio: { auto audioTrack = std::dynamic_pointer_cast(track); @@ -115,6 +116,7 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track) { void Metadata::addTrack(AMFValue &metadata, const Track::Ptr &track) { Metadata::Ptr new_metadata; + track->update(); switch (track->getTrackType()) { case TrackVideo: { new_metadata = std::make_shared(std::dynamic_pointer_cast(track)); diff --git a/src/Rtmp/RtmpPlayer.cpp b/src/Rtmp/RtmpPlayer.cpp index 42914cda..13c0131b 100644 --- a/src/Rtmp/RtmpPlayer.cpp +++ b/src/Rtmp/RtmpPlayer.cpp @@ -52,17 +52,18 @@ void RtmpPlayer::teardown() { void RtmpPlayer::play(const string &url) { teardown(); - string host_url = findSubString(url.data(), "://", "/"); - { - auto pos = url.find_last_of('/'); - if (pos != string::npos) { - _stream_id = url.substr(pos + 1); - } + auto schema = findSubString(url.data(), nullptr, "://"); + auto host_url = findSubString(url.data(), "://", "/"); + _app = findSubString(url.data(), (host_url + "/").data(), "/"); + _stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL); + auto app_second = findSubString(_stream_id.data(), nullptr, "/"); + if (!app_second.empty() && app_second.find('?') == std::string::npos) { + // _stream_id存在多级;不包含'?', 说明分割符'/'不是url参数的一部分 + _app += "/" + app_second; + _stream_id.erase(0, app_second.size() + 1); } - _app = findSubString(url.data(), (host_url + "/").data(), ("/" + _stream_id).data()); - _tc_url = string("rtmp://") + host_url + "/" + _app; - - if (!_app.size() || !_stream_id.size()) { + _tc_url = schema + "://" + host_url + "/" + _app; + if (_app.empty() || _stream_id.empty()) { onPlayResult_l(SockException(Err_other, "rtmp url非法"), false); return; } diff --git a/src/Rtmp/RtmpPusher.cpp b/src/Rtmp/RtmpPusher.cpp index f401cf34..c48f3820 100644 --- a/src/Rtmp/RtmpPusher.cpp +++ b/src/Rtmp/RtmpPusher.cpp @@ -63,14 +63,20 @@ void RtmpPusher::onPublishResult_l(const SockException &ex, bool handshake_done) } } -void RtmpPusher::publish(const string &url) { +void RtmpPusher::publish(const string &url) { teardown(); - string host_url = findSubString(url.data(), "://", "/"); + auto schema = findSubString(url.data(), nullptr, "://"); + auto host_url = findSubString(url.data(), "://", "/"); _app = findSubString(url.data(), (host_url + "/").data(), "/"); _stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL); - _tc_url = string("rtmp://") + host_url + "/" + _app; - - if (!_app.size() || !_stream_id.size()) { + auto app_second = findSubString(_stream_id.data(), nullptr, "/"); + if (!app_second.empty() && app_second.find('?') == std::string::npos) { + // _stream_id存在多级;不包含'?', 说明分割符'/'不是url参数的一部分 + _app += "/" + app_second; + _stream_id.erase(0, app_second.size() + 1); + } + _tc_url = schema + "://" + host_url + "/" + _app; + if (_app.empty() || _stream_id.empty()) { onPublishResult_l(SockException(Err_other, "rtmp url非法"), false); return; } diff --git a/src/Rtp/RtpSender.cpp b/src/Rtp/RtpSender.cpp index a102d5e2..172c9f2e 100644 --- a/src/Rtp/RtpSender.cpp +++ b/src/Rtp/RtpSender.cpp @@ -140,7 +140,7 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct cb(0, SockException(Err_other, ex.what())); return; } - strong_self->_socket_rtp->bindPeerAddr((struct sockaddr *)&addr); + strong_self->_socket_rtp->bindPeerAddr((struct sockaddr *)&addr, 0, true); strong_self->onConnect(); cb(strong_self->_socket_rtp->get_local_port(), SockException()); }); @@ -182,7 +182,7 @@ void RtpSender::createRtcpSocket() { case AF_INET6: ((sockaddr_in6 *)&addr)->sin6_port = htons(ntohs(((sockaddr_in6 *)&addr)->sin6_port) + 1); break; default: assert(0); break; } - _socket_rtcp->bindPeerAddr((struct sockaddr *)&addr); + _socket_rtcp->bindPeerAddr((struct sockaddr *)&addr, 0, true); _rtcp_context = std::make_shared(); weak_ptr weak_self = shared_from_this(); @@ -349,4 +349,4 @@ void RtpSender::setOnClose(std::function } }//namespace mediakit -#endif// defined(ENABLE_RTPPROXY) \ No newline at end of file +#endif// defined(ENABLE_RTPPROXY) diff --git a/src/Rtp/RtpServer.cpp b/src/Rtp/RtpServer.cpp index 78aee8e1..1f71a4a8 100644 --- a/src/Rtp/RtpServer.cpp +++ b/src/Rtp/RtpServer.cpp @@ -89,6 +89,8 @@ public: for (auto &rtcp : rtcps) { strong_self->_process->onRtcp(rtcp); } + // 收到sr rtcp后驱动返回rr rtcp + strong_self->sendRtcp(strong_self->_ssrc, (struct sockaddr *)(strong_self->_rtcp_addr.get())); }); GET_CONFIG(uint64_t, timeoutSec, RtpProxy::kTimeoutSec); @@ -102,7 +104,7 @@ public: process->setOnDetach(std::move(strong_self->_on_detach)); } if (!process) { // process 未创建,触发rtp server 超时事件 - NOTICE_EMIT(BroadcastRtpServerTimeoutArgs, Broadcast::KBroadcastRtpServerTimeout, strong_self->_local_port, strong_self->_stream_id, + NOTICE_EMIT(BroadcastRtpServerTimeoutArgs, Broadcast::kBroadcastRtpServerTimeout, strong_self->_local_port, strong_self->_stream_id, (int)strong_self->_tcp_mode, strong_self->_re_use_port, strong_self->_ssrc); } } @@ -154,7 +156,7 @@ private: EventPoller::DelayTask::Ptr _delay_task; }; -void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_mode, const char *local_ip, bool re_use_port, uint32_t ssrc, bool only_audio) { +void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_mode, const char *local_ip, bool re_use_port, uint32_t ssrc, bool only_audio, bool multiplex) { //创建udp服务器 Socket::Ptr rtp_socket = Socket::createSocket(nullptr, true); Socket::Ptr rtcp_socket = Socket::createSocket(nullptr, true); @@ -193,7 +195,8 @@ void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_ //创建udp服务器 UdpServer::Ptr udp_server; RtcpHelper::Ptr helper; - if (!stream_id.empty()) { + //增加了多路复用判断,如果多路复用为true,就走else逻辑,同时保留了原来stream_id为空走else逻辑 + if (!stream_id.empty() && !multiplex) { //指定了流id,那么一个端口一个流(不管是否包含多个ssrc的多个流,绑定rtp源后,会筛选掉ip端口不匹配的流) helper = std::make_shared(std::move(rtcp_socket), stream_id); helper->startRtcp(); diff --git a/src/Rtp/RtpServer.h b/src/Rtp/RtpServer.h index 4efce859..71aa88e7 100644 --- a/src/Rtp/RtpServer.h +++ b/src/Rtp/RtpServer.h @@ -42,9 +42,10 @@ public: * @param local_ip 绑定的本地网卡ip * @param re_use_port 是否设置socket为re_use属性 * @param ssrc 指定的ssrc + * @param multiplex 多路复用 */ void start(uint16_t local_port, const std::string &stream_id = "", TcpMode tcp_mode = PASSIVE, - const char *local_ip = "::", bool re_use_port = true, uint32_t ssrc = 0, bool only_audio = false); + const char *local_ip = "::", bool re_use_port = true, uint32_t ssrc = 0, bool only_audio = false, bool multiplex = false); /** * 连接到tcp服务(tcp主动模式) diff --git a/src/TS/TSMediaSourceMuxer.h b/src/TS/TSMediaSourceMuxer.h index 389ea901..cdf08fbb 100644 --- a/src/TS/TSMediaSourceMuxer.h +++ b/src/TS/TSMediaSourceMuxer.h @@ -26,7 +26,13 @@ public: _media_src = std::make_shared(tuple); } - ~TSMediaSourceMuxer() override { MpegMuxer::flush(); }; + ~TSMediaSourceMuxer() override { + try { + MpegMuxer::flush(); + } catch (std::exception &ex) { + WarnL << ex.what(); + } + }; void setListener(const std::weak_ptr &listener){ setDelegate(listener); diff --git a/tests/test_wsServer.cpp b/tests/test_wsServer.cpp index bd46787a..f839d352 100644 --- a/tests/test_wsServer.cpp +++ b/tests/test_wsServer.cpp @@ -81,9 +81,11 @@ public: */ struct EchoSessionCreator { //返回的Session必须派生于SendInterceptor,可以返回null(拒绝连接) - Session::Ptr operator()(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock) { + Session::Ptr operator()(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock, mediakit::WebSocketHeader::Type &type) { // return nullptr; if (header.url() == "/") { + // 可以指定传输方式 + // type = mediakit::WebSocketHeader::BINARY; return std::make_shared >(header, parent, pSock); } return std::make_shared >(header, parent, pSock); diff --git a/webrtc/DtlsTransport.cpp b/webrtc/DtlsTransport.cpp index 66183f8e..5ae4f4b0 100644 --- a/webrtc/DtlsTransport.cpp +++ b/webrtc/DtlsTransport.cpp @@ -29,6 +29,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include // std::sprintf(), std::fopen() #include // std::memcpy(), std::strcmp() #include "Util/util.h" +#include "Util/SSLBox.h" +#include "Util/SSLUtil.h" using namespace std; @@ -129,16 +131,10 @@ namespace RTC MS_TRACE(); // Generate a X509 certificate and private key (unless PEM files are provided). - if (true /* - Settings::configuration.dtlsCertificateFile.empty() || - Settings::configuration.dtlsPrivateKeyFile.empty()*/) - { + auto ssl = toolkit::SSL_Initor::Instance().getSSLCtx("", true); + if (!ssl || !ReadCertificateAndPrivateKeyFromContext(ssl.get())) { GenerateCertificateAndPrivateKey(); } - else - { - ReadCertificateAndPrivateKeyFromFiles(); - } // Create a global SSL_CTX. CreateSslCtx(); @@ -297,59 +293,22 @@ namespace RTC MS_THROW_ERROR("DTLS certificate and private key generation failed"); } - void DtlsTransport::DtlsEnvironment::ReadCertificateAndPrivateKeyFromFiles() + bool DtlsTransport::DtlsEnvironment::ReadCertificateAndPrivateKeyFromContext(SSL_CTX *ctx) { -#if 0 MS_TRACE(); - - FILE* file{ nullptr }; - - file = fopen(Settings::configuration.dtlsCertificateFile.c_str(), "r"); - - if (!file) - { - MS_ERROR("error reading DTLS certificate file: %s", std::strerror(errno)); - - goto error; + certificate = SSL_CTX_get0_certificate(ctx); + if (!certificate) { + return false; } + X509_up_ref(certificate); - certificate = PEM_read_X509(file, nullptr, nullptr, nullptr); - - if (!certificate) - { - LOG_OPENSSL_ERROR("PEM_read_X509() failed"); - - goto error; + privateKey = SSL_CTX_get0_privatekey(ctx); + if (!privateKey) { + return false; } - - fclose(file); - - file = fopen(Settings::configuration.dtlsPrivateKeyFile.c_str(), "r"); - - if (!file) - { - MS_ERROR("error reading DTLS private key file: %s", std::strerror(errno)); - - goto error; - } - - privateKey = PEM_read_PrivateKey(file, nullptr, nullptr, nullptr); - - if (!privateKey) - { - LOG_OPENSSL_ERROR("PEM_read_PrivateKey() failed"); - - goto error; - } - - fclose(file); - - return; - - error: - - MS_THROW_ERROR("error reading DTLS certificate and private key PEM files"); -#endif + EVP_PKEY_up_ref(privateKey); + InfoL << "Load webrtc dtls certificate: " << toolkit::SSLUtil::getServerName(certificate); + return true; } void DtlsTransport::DtlsEnvironment::CreateSslCtx() diff --git a/webrtc/DtlsTransport.hpp b/webrtc/DtlsTransport.hpp index bf57d01d..53a1981d 100644 --- a/webrtc/DtlsTransport.hpp +++ b/webrtc/DtlsTransport.hpp @@ -88,7 +88,7 @@ namespace RTC private: DtlsEnvironment(); void GenerateCertificateAndPrivateKey(); - void ReadCertificateAndPrivateKeyFromFiles(); + bool ReadCertificateAndPrivateKeyFromContext(SSL_CTX *ctx); void CreateSslCtx(); void GenerateFingerprints(); diff --git a/webrtc/WebRtcTransport.cpp b/webrtc/WebRtcTransport.cpp index 4ec13e6f..9ac6251f 100644 --- a/webrtc/WebRtcTransport.cpp +++ b/webrtc/WebRtcTransport.cpp @@ -251,7 +251,7 @@ void WebRtcTransport::sendSockData(const char *buf, size_t len, RTC::TransportTu } Session::Ptr WebRtcTransport::getSession() const { - auto tuple = _ice_server->GetSelectedTuple(true); + auto tuple = _ice_server ? _ice_server->GetSelectedTuple(true) : nullptr; return tuple ? static_pointer_cast(tuple->shared_from_this()) : nullptr; } diff --git a/www/webassist b/www/webassist new file mode 160000 index 00000000..b02d2a4c --- /dev/null +++ b/www/webassist @@ -0,0 +1 @@ +Subproject commit b02d2a4c1abf95db45e50bb77d789defa0fcc4b7