mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-26 20:47:08 +08:00
Merge branch 'master' into dev
This commit is contained in:
commit
5f8a77e6b1
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@ -40,9 +40,7 @@ jobs:
|
|||||||
# https://github.com/sigstore/cosign-installer
|
# https://github.com/sigstore/cosign-installer
|
||||||
- name: Install cosign
|
- name: Install cosign
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: sigstore/cosign-installer@d6a3abf1bdea83574e28d40543793018b6035605
|
uses: sigstore/cosign-installer@d572c9c13673d2e0a26fabf90b5748f36886883f
|
||||||
with:
|
|
||||||
cosign-release: 'v1.7.1'
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
|
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -6,4 +6,7 @@
|
|||||||
url = https://gitee.com/ireader/media-server
|
url = https://gitee.com/ireader/media-server
|
||||||
[submodule "3rdpart/jsoncpp"]
|
[submodule "3rdpart/jsoncpp"]
|
||||||
path = 3rdpart/jsoncpp
|
path = 3rdpart/jsoncpp
|
||||||
url = https://gitee.com/mirrors/jsoncpp.git
|
url = https://gitee.com/mirrors/jsoncpp.git
|
||||||
|
[submodule "www/webassist"]
|
||||||
|
path = www/webassist
|
||||||
|
url = https://gitee.com/victor1002/zlm_webassist
|
||||||
|
@ -7,3 +7,6 @@
|
|||||||
[submodule "3rdpart/jsoncpp"]
|
[submodule "3rdpart/jsoncpp"]
|
||||||
path = 3rdpart/jsoncpp
|
path = 3rdpart/jsoncpp
|
||||||
url = https://github.com/open-source-parsers/jsoncpp.git
|
url = https://github.com/open-source-parsers/jsoncpp.git
|
||||||
|
[submodule "www/webassist"]
|
||||||
|
path = www/webassist
|
||||||
|
url = https://github.com/1002victor/zlm_webassist
|
@ -1 +1 @@
|
|||||||
Subproject commit b11582c38e8dbbb8d93ca9ce33c9a0b0cd58f59a
|
Subproject commit 97871cfa78fcd2fae164243a8c653e323385772d
|
@ -1 +1 @@
|
|||||||
Subproject commit 8190e061bc2d95da37479a638aa2c9e483e58ec6
|
Subproject commit 69098a18b9af0c47549d9a271c054d13ca92b006
|
@ -1 +1 @@
|
|||||||
Subproject commit cdbb3d6b9ea254f454c6e466c5962af5ace01199
|
Subproject commit 3dc623a899eee3810587fb267dbff770b626a55b
|
5
AUTHORS
5
AUTHORS
@ -84,4 +84,7 @@ WuPeng <wp@zafu.edu.cn>
|
|||||||
[Luosh](https://github.com/Luosh)
|
[Luosh](https://github.com/Luosh)
|
||||||
[linxiaoyan87](https://github.com/linxiaoyan)
|
[linxiaoyan87](https://github.com/linxiaoyan)
|
||||||
[waken](https://github.com/mc373906408)
|
[waken](https://github.com/mc373906408)
|
||||||
[Deepslient](https://github.com/Deepslient)
|
[Deepslient](https://github.com/Deepslient)
|
||||||
|
[imp_rayjay](https://github.com/rayjay214)
|
||||||
|
[ArmstrongCN](https://github.com/ArmstrongCN)
|
||||||
|
[leibnewton](https://github.com/leibnewton)
|
@ -184,6 +184,7 @@ bash build_docker_images.sh
|
|||||||
- [BXC_gb28181Player](https://github.com/any12345com/BXC_gb28181Player) C++开发的支持国标GB28181协议的视频流播放器
|
- [BXC_gb28181Player](https://github.com/any12345com/BXC_gb28181Player) C++开发的支持国标GB28181协议的视频流播放器
|
||||||
|
|
||||||
- WEB管理网站
|
- WEB管理网站
|
||||||
|
- [zlm_webassist](https://github.com/1002victor/zlm_webassist) 本项目配套的前后端分离web管理项目
|
||||||
- [AKStreamNVR](https://github.com/langmansh/AKStreamNVR) 前后端分离web项目,支持webrtc播放
|
- [AKStreamNVR](https://github.com/langmansh/AKStreamNVR) 前后端分离web项目,支持webrtc播放
|
||||||
|
|
||||||
- SDK
|
- SDK
|
||||||
@ -329,6 +330,10 @@ bash build_docker_images.sh
|
|||||||
[linxiaoyan87](https://github.com/linxiaoyan)
|
[linxiaoyan87](https://github.com/linxiaoyan)
|
||||||
[waken](https://github.com/mc373906408)
|
[waken](https://github.com/mc373906408)
|
||||||
[Deepslient](https://github.com/Deepslient)
|
[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开发与调试:
|
同时感谢JetBrains对开源项目的支持,本项目使用CLion开发与调试:
|
||||||
|
|
||||||
|
@ -342,6 +342,7 @@ bash build_docker_images.sh
|
|||||||
## Collaborative Projects
|
## Collaborative Projects
|
||||||
|
|
||||||
- Visual management website
|
- 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)
|
- [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 master branch](https://gitee.com/kkkkk5G/MediaServerUI)
|
||||||
- [Management web site based on ZLMediaKit branch](https://github.com/chenxiaolei/ZLMediaKit_NVR_UI)
|
- [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)
|
[linxiaoyan87](https://github.com/linxiaoyan)
|
||||||
[waken](https://github.com/mc373906408)
|
[waken](https://github.com/mc373906408)
|
||||||
[Deepslient](https://github.com/Deepslient)
|
[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:
|
Also thank to JetBrains for their support for open source project, we developed and debugged zlmediakit with CLion:
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
"info": {
|
"info": {
|
||||||
"_postman_id": "39e8a1df-cc8e-4e3f-bf5e-197c86e7bf0f",
|
"_postman_id": "509e5f6b-728c-4d5f-b3e8-521d76b2cc7a",
|
||||||
"name": "ZLMediaKit",
|
"name": "ZLMediaKit",
|
||||||
"description": "媒体服务器",
|
"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": [
|
"item": [
|
||||||
{
|
{
|
||||||
@ -918,7 +919,7 @@
|
|||||||
"method": "GET",
|
"method": "GET",
|
||||||
"header": [],
|
"header": [],
|
||||||
"url": {
|
"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": [
|
"host": [
|
||||||
"{{ZLMediaKit_URL}}"
|
"{{ZLMediaKit_URL}}"
|
||||||
],
|
],
|
||||||
@ -1247,7 +1248,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "stamp",
|
"key": "stamp",
|
||||||
"value": 1000,
|
"value": "1000",
|
||||||
"description": "要设置的录像播放位置"
|
"description": "要设置的录像播放位置"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -1478,6 +1479,53 @@
|
|||||||
},
|
},
|
||||||
"response": []
|
"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)",
|
"name": "连接RTP服务器(connectRtpServer)",
|
||||||
"request": {
|
"request": {
|
||||||
@ -1710,10 +1758,16 @@
|
|||||||
"value": "obs",
|
"value": "obs",
|
||||||
"description": "流id,例如 obs"
|
"description": "流id,例如 obs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "ssrc_multi_send",
|
||||||
|
"value": "0",
|
||||||
|
"description": "是否支持同ssrc推流到多个上级服务器,该参数非必选参数 默认false",
|
||||||
|
"disabled": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "ssrc",
|
"key": "ssrc",
|
||||||
"value": "1",
|
"value": "1",
|
||||||
"description": "rtp推流的ssrc,ssrc不同时,可以推流到多个上级服务器"
|
"description": "rtp推流的ssrc"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "dst_url",
|
"key": "dst_url",
|
||||||
|
@ -370,6 +370,7 @@ Value makeMediaSourceJson(MediaSource &media){
|
|||||||
obj["loss"] = loss;
|
obj["loss"] = loss;
|
||||||
}
|
}
|
||||||
obj["frames"] = track->getFrames();
|
obj["frames"] = track->getFrames();
|
||||||
|
obj["duration"] = track->getDuration();
|
||||||
switch(codec_type){
|
switch(codec_type){
|
||||||
case TrackAudio : {
|
case TrackAudio : {
|
||||||
auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
|
auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
|
||||||
@ -403,7 +404,7 @@ Value makeMediaSourceJson(MediaSource &media){
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ENABLE_RTPPROXY)
|
#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<recursive_mutex> lck(s_rtpServerMapMtx);
|
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
|
||||||
if (s_rtpServerMap.find(stream_id) != s_rtpServerMap.end()) {
|
if (s_rtpServerMap.find(stream_id) != s_rtpServerMap.end()) {
|
||||||
//为了防止RtpProcess所有权限混乱的问题,不允许重复添加相同的stream_id
|
//为了防止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<RtpServer>();
|
RtpServer::Ptr server = std::make_shared<RtpServer>();
|
||||||
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]() {
|
server->setOnDetach([stream_id]() {
|
||||||
//设置rtp超时移除事件
|
//设置rtp超时移除事件
|
||||||
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
|
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
|
||||||
@ -1182,6 +1183,25 @@ void installWebApi() {
|
|||||||
val["port"] = port;
|
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<int>();
|
||||||
|
if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
|
||||||
|
// 兼容老版本请求,新版本去除enable_tcp参数并新增tcp_mode参数
|
||||||
|
tcp_mode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto port = openRtpServer(
|
||||||
|
allArgs["port"], stream_id, tcp_mode, "::", true, 0, allArgs["only_audio"].as<bool>(),true);
|
||||||
|
if (port == 0) {
|
||||||
|
throw InvalidArgsException("该stream_id已存在");
|
||||||
|
}
|
||||||
|
// 回复json
|
||||||
|
val["port"] = port;
|
||||||
|
});
|
||||||
|
|
||||||
api_regist("/index/api/connectRtpServer", [](API_ARGS_MAP_ASYNC) {
|
api_regist("/index/api/connectRtpServer", [](API_ARGS_MAP_ASYNC) {
|
||||||
CHECK_SECRET();
|
CHECK_SECRET();
|
||||||
CHECK_ARGS("stream_id", "dst_url", "dst_port");
|
CHECK_ARGS("stream_id", "dst_url", "dst_port");
|
||||||
@ -1244,6 +1264,7 @@ void installWebApi() {
|
|||||||
args.passive = false;
|
args.passive = false;
|
||||||
args.dst_url = allArgs["dst_url"];
|
args.dst_url = allArgs["dst_url"];
|
||||||
args.dst_port = allArgs["dst_port"];
|
args.dst_port = allArgs["dst_port"];
|
||||||
|
args.ssrc_multi_send = allArgs["ssrc_multi_send"].empty() ? false : allArgs["ssrc_multi_send"].as<bool>();
|
||||||
args.ssrc = allArgs["ssrc"];
|
args.ssrc = allArgs["ssrc"];
|
||||||
args.is_udp = allArgs["is_udp"];
|
args.is_udp = allArgs["is_udp"];
|
||||||
args.src_port = allArgs["src_port"];
|
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
|
// 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) {
|
api_regist("/index/api/deleteRecordDirectory", [](API_ARGS_MAP) {
|
||||||
CHECK_SECRET();
|
CHECK_SECRET();
|
||||||
CHECK_ARGS("vhost", "app", "stream");
|
CHECK_ARGS("vhost", "app", "stream", "period");
|
||||||
auto tuple = MediaTuple{allArgs["vhost"], allArgs["app"], allArgs["stream"]};
|
auto tuple = MediaTuple{allArgs["vhost"], allArgs["app"], allArgs["stream"]};
|
||||||
auto record_path = Recorder::getRecordPath(Recorder::type_mp4, tuple, allArgs["customized_path"]);
|
auto record_path = Recorder::getRecordPath(Recorder::type_mp4, tuple, allArgs["customized_path"]);
|
||||||
auto period = allArgs["period"];
|
auto period = allArgs["period"];
|
||||||
record_path = record_path + period + "/";
|
record_path = record_path + period + "/";
|
||||||
|
auto name = allArgs["name"];
|
||||||
|
if (!name.empty()) {
|
||||||
|
record_path += name;
|
||||||
|
}
|
||||||
int result = File::delete_file(record_path.data());
|
int result = File::delete_file(record_path.data());
|
||||||
if (result) {
|
if (result) {
|
||||||
// 不等于0时代表失败
|
// 不等于0时代表失败
|
||||||
@ -1911,24 +1936,28 @@ void installWebApi() {
|
|||||||
void unInstallWebApi(){
|
void unInstallWebApi(){
|
||||||
{
|
{
|
||||||
lock_guard<recursive_mutex> lck(s_proxyMapMtx);
|
lock_guard<recursive_mutex> lck(s_proxyMapMtx);
|
||||||
s_proxyMap.clear();
|
auto proxyMap(std::move(s_proxyMap));
|
||||||
|
proxyMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
lock_guard<recursive_mutex> lck(s_ffmpegMapMtx);
|
lock_guard<recursive_mutex> lck(s_ffmpegMapMtx);
|
||||||
s_ffmpegMap.clear();
|
auto ffmpegMap(std::move(s_ffmpegMap));
|
||||||
|
ffmpegMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
lock_guard<recursive_mutex> lck(s_proxyPusherMapMtx);
|
lock_guard<recursive_mutex> lck(s_proxyPusherMapMtx);
|
||||||
s_proxyPusherMap.clear();
|
auto proxyPusherMap(std::move(s_proxyPusherMap));
|
||||||
|
proxyPusherMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_RTPPROXY)
|
#if defined(ENABLE_RTPPROXY)
|
||||||
RtpSelector::Instance().clear();
|
RtpSelector::Instance().clear();
|
||||||
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
|
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
|
||||||
s_rtpServerMap.clear();
|
auto rtpServerMap(std::move(s_rtpServerMap));
|
||||||
|
rtpServerMap.clear();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
NoticeCenter::Instance().delListener(&web_api_tag);
|
NoticeCenter::Instance().delListener(&web_api_tag);
|
||||||
|
@ -239,7 +239,7 @@ void installWebApi();
|
|||||||
void unInstallWebApi();
|
void unInstallWebApi();
|
||||||
|
|
||||||
#if defined(ENABLE_RTPPROXY)
|
#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<void(const toolkit::SockException &ex)> &cb);
|
void connectRtpServer(const std::string &stream_id, const std::string &dst_url, uint16_t dst_port, const std::function<void(const toolkit::SockException &ex)> &cb);
|
||||||
bool closeRtpServer(const std::string &stream_id);
|
bool closeRtpServer(const std::string &stream_id);
|
||||||
#endif
|
#endif
|
||||||
|
@ -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);
|
GET_CONFIG(string, rtp_server_timeout, Hook::kOnRtpServerTimeout);
|
||||||
if (!hook_enable || rtp_server_timeout.empty()) {
|
if (!hook_enable || rtp_server_timeout.empty()) {
|
||||||
return;
|
return;
|
||||||
@ -703,4 +703,4 @@ void unInstallWebHook() {
|
|||||||
|
|
||||||
void onProcessExited() {
|
void onProcessExited() {
|
||||||
reportServerExited();
|
reportServerExited();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "JemallocUtil.h"
|
#include "JemallocUtil.h"
|
||||||
#include "Util/logger.h"
|
#include "Util/logger.h"
|
||||||
#ifdef USE_JEMALLOC
|
#ifdef USE_JEMALLOC
|
||||||
|
#include <array>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <jemalloc/jemalloc.h>
|
#include <jemalloc/jemalloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -99,11 +99,13 @@ public:
|
|||||||
// rtp采用ps还是es方式
|
// rtp采用ps还是es方式
|
||||||
bool use_ps = true;
|
bool use_ps = true;
|
||||||
//发送es流时指定是否只发送纯音频流
|
//发送es流时指定是否只发送纯音频流
|
||||||
bool only_audio = true;
|
bool only_audio = false;
|
||||||
//tcp被动方式
|
//tcp被动方式
|
||||||
bool passive = false;
|
bool passive = false;
|
||||||
// rtp payload type
|
// rtp payload type
|
||||||
uint8_t pt = 96;
|
uint8_t pt = 96;
|
||||||
|
//是否支持同ssrc多服务器发送
|
||||||
|
bool ssrc_multi_send = false;
|
||||||
// 指定rtp ssrc
|
// 指定rtp ssrc
|
||||||
std::string ssrc;
|
std::string ssrc;
|
||||||
// 指定本地发送端口
|
// 指定本地发送端口
|
||||||
|
@ -45,6 +45,7 @@ static string getTrackInfoStr(const TrackSource *track_src){
|
|||||||
_StrPrinter codec_info;
|
_StrPrinter codec_info;
|
||||||
auto tracks = track_src->getTracks(true);
|
auto tracks = track_src->getTracks(true);
|
||||||
for (auto &track : tracks) {
|
for (auto &track : tracks) {
|
||||||
|
track->update();
|
||||||
auto codec_type = track->getTrackType();
|
auto codec_type = track->getTrackType();
|
||||||
codec_info << track->getCodecName();
|
codec_info << track->getCodecName();
|
||||||
switch (codec_type) {
|
switch (codec_type) {
|
||||||
@ -294,12 +295,14 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE
|
|||||||
|
|
||||||
auto ring = _ring;
|
auto ring = _ring;
|
||||||
auto ssrc = args.ssrc;
|
auto ssrc = args.ssrc;
|
||||||
|
auto ssrc_multi_send = args.ssrc_multi_send;
|
||||||
auto tracks = getTracks(false);
|
auto tracks = getTracks(false);
|
||||||
auto poller = getOwnerPoller(sender);
|
auto poller = getOwnerPoller(sender);
|
||||||
auto rtp_sender = std::make_shared<RtpSender>(poller);
|
auto rtp_sender = std::make_shared<RtpSender>(poller);
|
||||||
|
|
||||||
weak_ptr<MultiMediaSourceMuxer> weak_self = shared_from_this();
|
weak_ptr<MultiMediaSourceMuxer> 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);
|
cb(local_port, ex);
|
||||||
auto strong_self = weak_self.lock();
|
auto strong_self = weak_self.lock();
|
||||||
if (!strong_self || ex) {
|
if (!strong_self || ex) {
|
||||||
@ -328,7 +331,10 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE
|
|||||||
|
|
||||||
// 可能归属线程发生变更
|
// 可能归属线程发生变更
|
||||||
strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() {
|
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
|
#else
|
||||||
|
@ -173,7 +173,7 @@ private:
|
|||||||
toolkit::Ticker _last_check;
|
toolkit::Ticker _last_check;
|
||||||
Stamp _stamp[2];
|
Stamp _stamp[2];
|
||||||
std::weak_ptr<Listener> _track_listener;
|
std::weak_ptr<Listener> _track_listener;
|
||||||
std::unordered_map<std::string, RingType::RingReader::Ptr> _rtp_sender;
|
std::unordered_multimap<std::string, RingType::RingReader::Ptr> _rtp_sender;
|
||||||
FMP4MediaSourceMuxer::Ptr _fmp4;
|
FMP4MediaSourceMuxer::Ptr _fmp4;
|
||||||
RtmpMediaSourceMuxer::Ptr _rtmp;
|
RtmpMediaSourceMuxer::Ptr _rtmp;
|
||||||
RtspMediaSourceMuxer::Ptr _rtsp;
|
RtspMediaSourceMuxer::Ptr _rtsp;
|
||||||
|
@ -57,7 +57,7 @@ const string kBroadcastNotFoundStream = "kBroadcastNotFoundStream";
|
|||||||
const string kBroadcastStreamNoneReader = "kBroadcastStreamNoneReader";
|
const string kBroadcastStreamNoneReader = "kBroadcastStreamNoneReader";
|
||||||
const string kBroadcastHttpBeforeAccess = "kBroadcastHttpBeforeAccess";
|
const string kBroadcastHttpBeforeAccess = "kBroadcastHttpBeforeAccess";
|
||||||
const string kBroadcastSendRtpStopped = "kBroadcastSendRtpStopped";
|
const string kBroadcastSendRtpStopped = "kBroadcastSendRtpStopped";
|
||||||
const string KBroadcastRtpServerTimeout = "KBroadcastRtpServerTimeout";
|
const string kBroadcastRtpServerTimeout = "kBroadcastRtpServerTimeout";
|
||||||
|
|
||||||
} // namespace Broadcast
|
} // namespace Broadcast
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ extern const std::string kBroadcastReloadConfig;
|
|||||||
#define BroadcastReloadConfigArgs void
|
#define BroadcastReloadConfigArgs void
|
||||||
|
|
||||||
// rtp server 超时
|
// 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 BroadcastRtpServerTimeoutArgs uint16_t &local_port, const string &stream_id,int &tcp_mode, bool &re_use_port, uint32_t &ssrc
|
||||||
|
|
||||||
#define ReloadConfigTag ((void *)(0xFF))
|
#define ReloadConfigTag ((void *)(0xFF))
|
||||||
|
@ -36,20 +36,21 @@ void UnicodeToUTF8(char *pOut, const wchar_t *pText) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char CharToInt(char ch) {
|
char HexCharToBin(char ch) {
|
||||||
if (ch >= '0' && ch <= '9')return (char) (ch - '0');
|
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);
|
||||||
if (ch >= 'A' && ch <= 'F')return (char) (ch - 'A' + 10);
|
if (ch >= 'A' && ch <= 'F') return (char)(ch - 'A' + 10);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char StrToBin(const char *str) {
|
char HexStrToBin(const char *str) {
|
||||||
char tempWord[2];
|
auto high = HexCharToBin(str[0]);
|
||||||
char chn;
|
auto low = HexCharToBin(str[1]);
|
||||||
tempWord[0] = CharToInt(str[0]); //make the B to 11 -- 00001011
|
if (high == -1 || low == -1) {
|
||||||
tempWord[1] = CharToInt(str[1]); //make the 0 to 0 -- 00000000
|
// 无法把16进制字符串转换为二进制
|
||||||
chn = (tempWord[0] << 4) | tempWord[1]; //to change the BO to 10110000
|
return -1;
|
||||||
return chn;
|
}
|
||||||
|
return (high << 4) | low;
|
||||||
}
|
}
|
||||||
|
|
||||||
string strCoding::UrlEncode(const string &str) {
|
string strCoding::UrlEncode(const string &str) {
|
||||||
@ -70,26 +71,51 @@ string strCoding::UrlEncode(const string &str) {
|
|||||||
|
|
||||||
string strCoding::UrlDecode(const string &str) {
|
string strCoding::UrlDecode(const string &str) {
|
||||||
string output;
|
string output;
|
||||||
char tmp[2];
|
|
||||||
size_t i = 0, len = str.length();
|
size_t i = 0, len = str.length();
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
if (str[i] == '%') {
|
if (str[i] == '%') {
|
||||||
if (i > len - 3) {
|
if (i + 3 > len) {
|
||||||
//防止内存溢出
|
// %后面必须还有两个字节才会反转义
|
||||||
|
output.append(str, i, len - i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp[0] = str[i + 1];
|
char ch = HexStrToBin(&(str[i + 1]));
|
||||||
tmp[1] = str[i + 2];
|
if (ch == -1) {
|
||||||
output += StrToBin(tmp);
|
// %后面两个字节不是16进制字符串,转义失败;直接拼接3个原始字符
|
||||||
i = i + 3;
|
output.append(str, i, 3);
|
||||||
|
} else {
|
||||||
|
output += ch;
|
||||||
|
}
|
||||||
|
i += 3;
|
||||||
} else {
|
} else {
|
||||||
output += str[i];
|
output += str[i];
|
||||||
i++;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
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专用///////////////////////////////////
|
///////////////////////////////windows专用///////////////////////////////////
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
void UnicodeToGB2312(char* pOut, wchar_t uData)
|
void UnicodeToGB2312(char* pOut, wchar_t uData)
|
||||||
|
@ -21,53 +21,56 @@ namespace mediakit{
|
|||||||
#ifndef ENABLE_MP4
|
#ifndef ENABLE_MP4
|
||||||
unsigned const samplingFrequencyTable[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 };
|
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:
|
public:
|
||||||
unsigned int syncword = 0; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始
|
unsigned int syncword = 0; // 12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始
|
||||||
unsigned int id; //1 bslbf MPEG 标示符, 设置为1
|
unsigned int id; // 1 bslbf MPEG 标示符, 设置为1
|
||||||
unsigned int layer; //2 uimsbf Indicates which layer is used. Set to ‘00’
|
unsigned int layer; // 2 uimsbf Indicates which layer is used. Set to ‘00’
|
||||||
unsigned int protection_absent; //1 bslbf 表示是否误码校验
|
unsigned int protection_absent; // 1 bslbf 表示是否误码校验
|
||||||
unsigned int profile; //2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC
|
unsigned int profile; // 2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC
|
||||||
unsigned int sf_index; //4 uimsbf 表示使用的采样率下标
|
unsigned int sf_index; // 4 uimsbf 表示使用的采样率下标
|
||||||
unsigned int private_bit; //1 bslbf
|
unsigned int private_bit; // 1 bslbf
|
||||||
unsigned int channel_configuration; //3 uimsbf 表示声道数
|
unsigned int channel_configuration; // 3 uimsbf 表示声道数
|
||||||
unsigned int original; //1 bslbf
|
unsigned int original; // 1 bslbf
|
||||||
unsigned int home; //1 bslbf
|
unsigned int home; // 1 bslbf
|
||||||
//下面的为改变的参数即每一帧都不同
|
// 下面的为改变的参数即每一帧都不同
|
||||||
unsigned int copyright_identification_bit; //1 bslbf
|
unsigned int copyright_identification_bit; // 1 bslbf
|
||||||
unsigned int copyright_identification_start; //1 bslbf
|
unsigned int copyright_identification_start; // 1 bslbf
|
||||||
unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block
|
unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block
|
||||||
unsigned int adts_buffer_fullness; //11 bslbf 0x7FF 说明是码率可变的码流
|
unsigned int adts_buffer_fullness; // 11 bslbf 0x7FF 说明是码率可变的码流
|
||||||
//no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.
|
// no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.
|
||||||
//所以说number_of_raw_data_blocks_in_frame == 0
|
// 所以说number_of_raw_data_blocks_in_frame == 0
|
||||||
//表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
|
// 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
|
||||||
unsigned int no_raw_data_blocks_in_frame; //2 uimsfb
|
unsigned int no_raw_data_blocks_in_frame; // 2 uimsfb
|
||||||
};
|
};
|
||||||
|
|
||||||
static void dumpAdtsHeader(const AdtsHeader &hed, uint8_t *out) {
|
static void dumpAdtsHeader(const AdtsHeader &hed, uint8_t *out) {
|
||||||
out[0] = (hed.syncword >> 4 & 0xFF); //8bit
|
out[0] = (hed.syncword >> 4 & 0xFF); // 8bit
|
||||||
out[1] = (hed.syncword << 4 & 0xF0); //4 bit
|
out[1] = (hed.syncword << 4 & 0xF0); // 4 bit
|
||||||
out[1] |= (hed.id << 3 & 0x08); //1 bit
|
out[1] |= (hed.id << 3 & 0x08); // 1 bit
|
||||||
out[1] |= (hed.layer << 1 & 0x06); //2bit
|
out[1] |= (hed.layer << 1 & 0x06); // 2bit
|
||||||
out[1] |= (hed.protection_absent & 0x01); //1 bit
|
out[1] |= (hed.protection_absent & 0x01); // 1 bit
|
||||||
out[2] = (hed.profile << 6 & 0xC0); // 2 bit
|
out[2] = (hed.profile << 6 & 0xC0); // 2 bit
|
||||||
out[2] |= (hed.sf_index << 2 & 0x3C); //4bit
|
out[2] |= (hed.sf_index << 2 & 0x3C); // 4bit
|
||||||
out[2] |= (hed.private_bit << 1 & 0x02); //1 bit
|
out[2] |= (hed.private_bit << 1 & 0x02); // 1 bit
|
||||||
out[2] |= (hed.channel_configuration >> 2 & 0x03); //1 bit
|
out[2] |= (hed.channel_configuration >> 2 & 0x03); // 1 bit
|
||||||
out[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit
|
out[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit
|
||||||
out[3] |= (hed.original << 5 & 0x20); //1 bit
|
out[3] |= (hed.original << 5 & 0x20); // 1 bit
|
||||||
out[3] |= (hed.home << 4 & 0x10); //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_bit << 3 & 0x08); // 1 bit
|
||||||
out[3] |= (hed.copyright_identification_start << 2 & 0x04); //1 bit
|
out[3] |= (hed.copyright_identification_start << 2 & 0x04); // 1 bit
|
||||||
out[3] |= (hed.aac_frame_length >> 11 & 0x03); //2 bit
|
out[3] |= (hed.aac_frame_length >> 11 & 0x03); // 2 bit
|
||||||
out[4] = (hed.aac_frame_length >> 3 & 0xFF); //8 bit
|
out[4] = (hed.aac_frame_length >> 3 & 0xFF); // 8 bit
|
||||||
out[5] = (hed.aac_frame_length << 5 & 0xE0); //3 bit
|
out[5] = (hed.aac_frame_length << 5 & 0xE0); // 3 bit
|
||||||
out[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); //5 bit
|
out[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); // 5 bit
|
||||||
out[6] = (hed.adts_buffer_fullness << 2 & 0xFC); //6 bit
|
out[6] = (hed.adts_buffer_fullness << 2 & 0xFC); // 6 bit
|
||||||
out[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); //2 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 cfg1 = config[0];
|
||||||
uint8_t cfg2 = config[1];
|
uint8_t cfg2 = config[1];
|
||||||
|
|
||||||
@ -94,6 +97,7 @@ static void parseAacConfig(const string &config, AdtsHeader &adts) {
|
|||||||
adts.aac_frame_length = 7;
|
adts.aac_frame_length = 7;
|
||||||
adts.adts_buffer_fullness = 2047;
|
adts.adts_buffer_fullness = 2047;
|
||||||
adts.no_raw_data_blocks_in_frame = 0;
|
adts.no_raw_data_blocks_in_frame = 0;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif// ENABLE_MP4
|
#endif// ENABLE_MP4
|
||||||
|
|
||||||
@ -168,10 +172,12 @@ int dumpAacConfig(const string &config, size_t length, uint8_t *out, size_t out_
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parseAacConfig(const string &config, int &samplerate, int &channels){
|
bool parseAacConfig(const string &config, int &samplerate, int &channels) {
|
||||||
#ifndef ENABLE_MP4
|
#ifndef ENABLE_MP4
|
||||||
AdtsHeader header;
|
AdtsHeader header;
|
||||||
parseAacConfig(config, header);
|
if (!parseAacConfig(config, header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
samplerate = samplingFrequencyTable[header.sf_index];
|
samplerate = samplingFrequencyTable[header.sf_index];
|
||||||
channels = header.channel_configuration;
|
channels = header.channel_configuration;
|
||||||
return true;
|
return true;
|
||||||
@ -326,11 +332,14 @@ bool AACTrack::inputFrame_l(const Frame::Ptr &frame) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AACTrack::update() {
|
||||||
|
return parseAacConfig(_cfg, _sampleRate, _channel);
|
||||||
|
}
|
||||||
|
|
||||||
void AACTrack::onReady() {
|
void AACTrack::onReady() {
|
||||||
if (_cfg.size() < 2) {
|
if (!parseAacConfig(_cfg, _sampleRate, _channel)) {
|
||||||
return;
|
_cfg.clear();
|
||||||
}
|
}
|
||||||
parseAacConfig(_cfg, _sampleRate, _channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Track::Ptr AACTrack::clone() {
|
Track::Ptr AACTrack::clone() {
|
||||||
@ -342,6 +351,7 @@ Sdp::Ptr AACTrack::getSdp() {
|
|||||||
WarnL << getCodecName() << " Track未准备好";
|
WarnL << getCodecName() << " Track未准备好";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
update();
|
||||||
return std::make_shared<AACSdp>(getConfig(), getAudioSampleRate(), getAudioChannel(), getBitRate() / 1024);
|
return std::make_shared<AACSdp>(getConfig(), getAudioSampleRate(), getAudioChannel(), getBitRate() / 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ public:
|
|||||||
int getAudioSampleRate() const override;
|
int getAudioSampleRate() const override;
|
||||||
int getAudioSampleBit() const override;
|
int getAudioSampleBit() const override;
|
||||||
bool inputFrame(const Frame::Ptr &frame) override;
|
bool inputFrame(const Frame::Ptr &frame) override;
|
||||||
|
bool update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onReady();
|
void onReady();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include "Util/List.h"
|
#include "Util/List.h"
|
||||||
#include "Util/TimeTicker.h"
|
#include "Util/TimeTicker.h"
|
||||||
|
#include "Common/Stamp.h"
|
||||||
#include "Network/Buffer.h"
|
#include "Network/Buffer.h"
|
||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
@ -361,11 +362,18 @@ public:
|
|||||||
return _gop_interval_ms;
|
return _gop_interval_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t getDuration() const {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(_mtx);
|
||||||
|
return _stamp.getRelativeStamp();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doStatistics(const Frame::Ptr &frame) {
|
void doStatistics(const Frame::Ptr &frame) {
|
||||||
if (!frame->configFrame() && !frame->dropAble()) {
|
if (!frame->configFrame() && !frame->dropAble()) {
|
||||||
// 忽略配置帧与可丢弃的帧
|
// 忽略配置帧与可丢弃的帧
|
||||||
++_frames;
|
++_frames;
|
||||||
|
int64_t out;
|
||||||
|
_stamp.revise(frame->dts(), frame->pts(), out, out);
|
||||||
if (frame->keyFrame() && frame->getTrackType() == TrackVideo) {
|
if (frame->keyFrame() && frame->getTrackType() == TrackVideo) {
|
||||||
// 遇视频关键帧时统计
|
// 遇视频关键帧时统计
|
||||||
++_video_key_frames;
|
++_video_key_frames;
|
||||||
@ -384,6 +392,7 @@ private:
|
|||||||
uint64_t _last_frames = 0;
|
uint64_t _last_frames = 0;
|
||||||
uint64_t _frames = 0;
|
uint64_t _frames = 0;
|
||||||
uint64_t _video_key_frames = 0;
|
uint64_t _video_key_frames = 0;
|
||||||
|
Stamp _stamp;
|
||||||
mutable std::recursive_mutex _mtx;
|
mutable std::recursive_mutex _mtx;
|
||||||
std::map<void *, FrameWriterInterface::Ptr> _delegates;
|
std::map<void *, FrameWriterInterface::Ptr> _delegates;
|
||||||
};
|
};
|
||||||
|
@ -168,6 +168,10 @@ bool H264Track::inputFrame(const Frame::Ptr &frame) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool H264Track::update() {
|
||||||
|
return getAVCInfo(_sps, _width, _height, _fps);
|
||||||
|
}
|
||||||
|
|
||||||
void H264Track::onReady() {
|
void H264Track::onReady() {
|
||||||
if (!getAVCInfo(_sps, _width, _height, _fps)) {
|
if (!getAVCInfo(_sps, _width, _height, _fps)) {
|
||||||
_sps.clear();
|
_sps.clear();
|
||||||
|
@ -128,6 +128,7 @@ public:
|
|||||||
int getVideoWidth() const override;
|
int getVideoWidth() const override;
|
||||||
float getVideoFps() const override;
|
float getVideoFps() const override;
|
||||||
bool inputFrame(const Frame::Ptr &frame) override;
|
bool inputFrame(const Frame::Ptr &frame) override;
|
||||||
|
bool update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onReady();
|
void onReady();
|
||||||
|
@ -44,13 +44,15 @@ H264Frame::Ptr H264RtpDecoder::obtainFrame() {
|
|||||||
|
|
||||||
bool H264RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
|
bool H264RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
|
||||||
auto seq = rtp->getSeq();
|
auto seq = rtp->getSeq();
|
||||||
auto ret = decodeRtp(rtp);
|
auto last_is_gop = _is_gop;
|
||||||
if (!_gop_dropped && seq != (uint16_t) (_last_seq + 1) && _last_seq) {
|
_is_gop = decodeRtp(rtp);
|
||||||
|
if (!_gop_dropped && seq != (uint16_t)(_last_seq + 1) && _last_seq) {
|
||||||
_gop_dropped = true;
|
_gop_dropped = true;
|
||||||
WarnL << "start drop h264 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString();
|
WarnL << "start drop h264 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString();
|
||||||
}
|
}
|
||||||
_last_seq = seq;
|
_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.assign("\x00\x00\x00\x01", 4);
|
||||||
_frame->_buffer.append((char *) ptr, size);
|
_frame->_buffer.append((char *) ptr, size);
|
||||||
_frame->_pts = stamp;
|
_frame->_pts = stamp;
|
||||||
auto key = _frame->keyFrame();
|
auto key = _frame->keyFrame() || _frame->configFrame();
|
||||||
outputFrame(rtp, _frame);
|
outputFrame(rtp, _frame);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
@ -127,7 +129,7 @@ bool H264RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz
|
|||||||
|
|
||||||
if (!fu->end_bit) {
|
if (!fu->end_bit) {
|
||||||
//非末尾包
|
//非末尾包
|
||||||
return fu->start_bit ? _frame->keyFrame() : false;
|
return fu->start_bit ? (_frame->keyFrame() || _frame->configFrame()) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//确保下一次fu必须收到第一个包
|
//确保下一次fu必须收到第一个包
|
||||||
|
@ -51,6 +51,7 @@ private:
|
|||||||
void outputFrame(const RtpPacket::Ptr &rtp, const H264Frame::Ptr &frame);
|
void outputFrame(const RtpPacket::Ptr &rtp, const H264Frame::Ptr &frame);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool _is_gop = false;
|
||||||
bool _gop_dropped = false;
|
bool _gop_dropped = false;
|
||||||
bool _fu_dropped = true;
|
bool _fu_dropped = true;
|
||||||
uint16_t _last_seq = 0;
|
uint16_t _last_seq = 0;
|
||||||
|
@ -144,6 +144,10 @@ bool H265Track::inputFrame_l(const Frame::Ptr &frame) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool H265Track::update() {
|
||||||
|
return getHEVCInfo(_vps, _sps, _width, _height, _fps);
|
||||||
|
}
|
||||||
|
|
||||||
void H265Track::onReady() {
|
void H265Track::onReady() {
|
||||||
if (!getHEVCInfo(_vps, _sps, _width, _height, _fps)) {
|
if (!getHEVCInfo(_vps, _sps, _width, _height, _fps)) {
|
||||||
_vps.clear();
|
_vps.clear();
|
||||||
|
@ -150,6 +150,7 @@ public:
|
|||||||
int getVideoHeight() const override;
|
int getVideoHeight() const override;
|
||||||
float getVideoFps() const override;
|
float getVideoFps() const override;
|
||||||
bool inputFrame(const Frame::Ptr &frame) override;
|
bool inputFrame(const Frame::Ptr &frame) override;
|
||||||
|
bool update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onReady();
|
void onReady();
|
||||||
|
@ -163,7 +163,7 @@ bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz
|
|||||||
|
|
||||||
if (!e_bit) {
|
if (!e_bit) {
|
||||||
//非末尾包
|
//非末尾包
|
||||||
return s_bit ? _frame->keyFrame() : false;
|
return s_bit ? (_frame->keyFrame() || _frame->configFrame()) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//确保下一次fu必须收到第一个包
|
//确保下一次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) {
|
bool H265RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool) {
|
||||||
auto seq = rtp->getSeq();
|
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) {
|
if (!_gop_dropped && seq != (uint16_t) (_last_seq + 1) && _last_seq) {
|
||||||
_gop_dropped = true;
|
_gop_dropped = true;
|
||||||
WarnL << "start drop h265 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString();
|
WarnL << "start drop h265 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString();
|
||||||
}
|
}
|
||||||
_last_seq = seq;
|
_last_seq = seq;
|
||||||
return ret;
|
// 确保有sps rtp的时候,gop从sps开始;否则从关键帧开始
|
||||||
|
return _is_gop && !last_is_gop;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
|
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.assign("\x00\x00\x00\x01", 4);
|
||||||
_frame->_buffer.append((char *) ptr, size);
|
_frame->_buffer.append((char *) ptr, size);
|
||||||
_frame->_pts = stamp;
|
_frame->_pts = stamp;
|
||||||
auto key = _frame->keyFrame();
|
auto key = _frame->keyFrame() || _frame->configFrame();
|
||||||
outputFrame(rtp, _frame);
|
outputFrame(rtp, _frame);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ private:
|
|||||||
void outputFrame(const RtpPacket::Ptr &rtp, const H265Frame::Ptr &frame);
|
void outputFrame(const RtpPacket::Ptr &rtp, const H265Frame::Ptr &frame);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool _is_gop = false;
|
||||||
bool _using_donl_field = false;
|
bool _using_donl_field = false;
|
||||||
bool _gop_dropped = false;
|
bool _gop_dropped = false;
|
||||||
bool _fu_dropped = true;
|
bool _fu_dropped = true;
|
||||||
|
@ -39,6 +39,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual Track::Ptr clone() = 0;
|
virtual Track::Ptr clone() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新track信息,比如触发sps/pps解析
|
||||||
|
*/
|
||||||
|
virtual bool update() { return false; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成sdp
|
* 生成sdp
|
||||||
* @return sdp对象
|
* @return sdp对象
|
||||||
|
@ -80,7 +80,7 @@ void HttpClient::sendRequest(const string &url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!alive() || host_changed) {
|
if (!alive() || host_changed) {
|
||||||
startConnect(host, port, _wait_header_ms);
|
startConnect(host, port, _wait_header_ms / 1000.0f);
|
||||||
} else {
|
} else {
|
||||||
SockException ex;
|
SockException ex;
|
||||||
onConnect_l(ex);
|
onConnect_l(ex);
|
||||||
|
@ -166,11 +166,11 @@ void HttpSession::onError(const SockException &err) {
|
|||||||
if (_is_live_stream) {
|
if (_is_live_stream) {
|
||||||
// flv/ts播放器
|
// flv/ts播放器
|
||||||
uint64_t duration = _ticker.createdTime() / 1000;
|
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);
|
GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold);
|
||||||
if (_total_bytes_usage >= iFlowThreshold * 1024) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@ -263,9 +263,9 @@ bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析带上协议+参数完整的url
|
// 解析带上协议+参数完整的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不合法
|
// url不合法
|
||||||
return false;
|
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();
|
auto strong_self = weak_self.lock();
|
||||||
if (!strong_self) {
|
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) {
|
if (!flag) {
|
||||||
// 该事件无人监听,默认不鉴权
|
// 该事件无人监听,默认不鉴权
|
||||||
onRes("");
|
onRes("");
|
||||||
|
@ -124,7 +124,7 @@ private:
|
|||||||
void setSocketFlags();
|
void setSocketFlags();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MediaInfo _mediaInfo;
|
MediaInfo _media_info;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _is_live_stream = false;
|
bool _is_live_stream = false;
|
||||||
|
@ -69,7 +69,7 @@ template <typename SessionType>
|
|||||||
class SessionCreator {
|
class SessionCreator {
|
||||||
public:
|
public:
|
||||||
//返回的Session必须派生于SendInterceptor,可以返回null
|
//返回的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<SessionTypeImp<SessionType> >(header,parent,pSock);
|
return std::make_shared<SessionTypeImp<SessionType> >(header,parent,pSock);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -128,7 +128,8 @@ protected:
|
|||||||
*/
|
*/
|
||||||
bool onWebSocketConnect(const mediakit::Parser &header) override{
|
bool onWebSocketConnect(const mediakit::Parser &header) override{
|
||||||
//创建websocket session类
|
//创建websocket session类
|
||||||
_session = _creator(header, *this, HttpSessionType::getSock());
|
auto data_type = DataType;
|
||||||
|
_session = _creator(header, *this, HttpSessionType::getSock(), data_type);
|
||||||
if (!_session) {
|
if (!_session) {
|
||||||
// 此url不允许创建websocket连接
|
// 此url不允许创建websocket连接
|
||||||
return false;
|
return false;
|
||||||
@ -140,13 +141,13 @@ protected:
|
|||||||
|
|
||||||
//此处截取数据并进行websocket协议打包
|
//此处截取数据并进行websocket协议打包
|
||||||
std::weak_ptr<WebSocketSessionBase> weakSelf = std::static_pointer_cast<WebSocketSessionBase>(HttpSessionType::shared_from_this());
|
std::weak_ptr<WebSocketSessionBase> weakSelf = std::static_pointer_cast<WebSocketSessionBase>(HttpSessionType::shared_from_this());
|
||||||
std::dynamic_pointer_cast<SendInterceptor>(_session)->setOnBeforeSendCB([weakSelf](const toolkit::Buffer::Ptr &buf) {
|
std::dynamic_pointer_cast<SendInterceptor>(_session)->setOnBeforeSendCB([weakSelf, data_type](const toolkit::Buffer::Ptr &buf) {
|
||||||
auto strongSelf = weakSelf.lock();
|
auto strongSelf = weakSelf.lock();
|
||||||
if (strongSelf) {
|
if (strongSelf) {
|
||||||
mediakit::WebSocketHeader header;
|
mediakit::WebSocketHeader header;
|
||||||
header._fin = true;
|
header._fin = true;
|
||||||
header._reserved = 0;
|
header._reserved = 0;
|
||||||
header._opcode = DataType;
|
header._opcode = data_type;
|
||||||
header._mask_flag = false;
|
header._mask_flag = false;
|
||||||
strongSelf->HttpSessionType::encode(header, buf);
|
strongSelf->HttpSessionType::encode(header, buf);
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ void PlayerProxy::setTranslationInfo()
|
|||||||
_transtalion_info.stream_info.clear();
|
_transtalion_info.stream_info.clear();
|
||||||
auto tracks = _muxer->getTracks();
|
auto tracks = _muxer->getTracks();
|
||||||
for (auto &track : tracks) {
|
for (auto &track : tracks) {
|
||||||
|
track->update();
|
||||||
_transtalion_info.stream_info.emplace_back();
|
_transtalion_info.stream_info.emplace_back();
|
||||||
auto &back = _transtalion_info.stream_info.back();
|
auto &back = _transtalion_info.stream_info.back();
|
||||||
back.bitrate = track->getBitRate();
|
back.bitrate = track->getBitRate();
|
||||||
|
@ -16,8 +16,10 @@
|
|||||||
#include "Extension/AAC.h"
|
#include "Extension/AAC.h"
|
||||||
#include "Extension/G711.h"
|
#include "Extension/G711.h"
|
||||||
#include "Extension/Opus.h"
|
#include "Extension/Opus.h"
|
||||||
using namespace toolkit;
|
#include "Extension/JPEG.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace toolkit;
|
||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
@ -105,8 +107,9 @@ void MP4Demuxer::onVideoTrack(uint32_t track, uint8_t object, int width, int hei
|
|||||||
video->inputFrame(std::make_shared<H264FrameNoCacheAble>((char *)config, size, 0, 0,4));
|
video->inputFrame(std::make_shared<H264FrameNoCacheAble>((char *)config, size, 0, 0,4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case MOV_OBJECT_HEVC: {
|
case MOV_OBJECT_HEVC: {
|
||||||
auto video = std::make_shared<H265Track>();
|
auto video = std::make_shared<H265Track>();
|
||||||
_track_to_codec.emplace(track,video);
|
_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<H265FrameNoCacheAble>((char *) config, size, 0, 0,4));
|
video->inputFrame(std::make_shared<H265FrameNoCacheAble>((char *) config, size, 0, 0,4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MOV_OBJECT_JPEG: {
|
||||||
|
auto video = std::make_shared<JPEGTrack>();
|
||||||
|
_track_to_codec.emplace(track,video);
|
||||||
break;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CodecJPEG: {
|
||||||
|
ret = std::make_shared<JPEGFrame>(buf, (uint64_t)dts, 0, DATA_OFFSET);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case CodecAAC: {
|
case CodecAAC: {
|
||||||
AACTrack::Ptr track = dynamic_pointer_cast<AACTrack>(it->second);
|
AACTrack::Ptr track = dynamic_pointer_cast<AACTrack>(it->second);
|
||||||
assert(track);
|
assert(track);
|
||||||
@ -283,4 +296,4 @@ uint64_t MP4Demuxer::getDurationMS() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
#endif// ENABLE_MP4
|
#endif// ENABLE_MP4
|
||||||
|
@ -198,6 +198,7 @@ bool MP4MuxerInterface::addTrack(const Track::Ptr &track) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
track->update();
|
||||||
switch (track->getCodecId()) {
|
switch (track->getCodecId()) {
|
||||||
case CodecG711A:
|
case CodecG711A:
|
||||||
case CodecG711U:
|
case CodecG711U:
|
||||||
|
@ -34,8 +34,12 @@ MP4Recorder::MP4Recorder(const string &path, const string &vhost, const string &
|
|||||||
}
|
}
|
||||||
|
|
||||||
MP4Recorder::~MP4Recorder() {
|
MP4Recorder::~MP4Recorder() {
|
||||||
flush();
|
try {
|
||||||
closeFile();
|
flush();
|
||||||
|
closeFile();
|
||||||
|
} catch (std::exception &ex) {
|
||||||
|
WarnL << ex.what();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MP4Recorder::createFile() {
|
void MP4Recorder::createFile() {
|
||||||
|
@ -57,6 +57,7 @@ AudioMeta::AudioMeta(const AudioTrack::Ptr &audio) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t getAudioRtmpFlags(const Track::Ptr &track) {
|
uint8_t getAudioRtmpFlags(const Track::Ptr &track) {
|
||||||
|
track->update();
|
||||||
switch (track->getTrackType()) {
|
switch (track->getTrackType()) {
|
||||||
case TrackAudio: {
|
case TrackAudio: {
|
||||||
auto audioTrack = std::dynamic_pointer_cast<AudioTrack>(track);
|
auto audioTrack = std::dynamic_pointer_cast<AudioTrack>(track);
|
||||||
@ -115,6 +116,7 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track) {
|
|||||||
|
|
||||||
void Metadata::addTrack(AMFValue &metadata, const Track::Ptr &track) {
|
void Metadata::addTrack(AMFValue &metadata, const Track::Ptr &track) {
|
||||||
Metadata::Ptr new_metadata;
|
Metadata::Ptr new_metadata;
|
||||||
|
track->update();
|
||||||
switch (track->getTrackType()) {
|
switch (track->getTrackType()) {
|
||||||
case TrackVideo: {
|
case TrackVideo: {
|
||||||
new_metadata = std::make_shared<VideoMeta>(std::dynamic_pointer_cast<VideoTrack>(track));
|
new_metadata = std::make_shared<VideoMeta>(std::dynamic_pointer_cast<VideoTrack>(track));
|
||||||
|
@ -52,17 +52,18 @@ void RtmpPlayer::teardown() {
|
|||||||
|
|
||||||
void RtmpPlayer::play(const string &url) {
|
void RtmpPlayer::play(const string &url) {
|
||||||
teardown();
|
teardown();
|
||||||
string host_url = findSubString(url.data(), "://", "/");
|
auto schema = findSubString(url.data(), nullptr, "://");
|
||||||
{
|
auto host_url = findSubString(url.data(), "://", "/");
|
||||||
auto pos = url.find_last_of('/');
|
_app = findSubString(url.data(), (host_url + "/").data(), "/");
|
||||||
if (pos != string::npos) {
|
_stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL);
|
||||||
_stream_id = url.substr(pos + 1);
|
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 = schema + "://" + host_url + "/" + _app;
|
||||||
_tc_url = string("rtmp://") + host_url + "/" + _app;
|
if (_app.empty() || _stream_id.empty()) {
|
||||||
|
|
||||||
if (!_app.size() || !_stream_id.size()) {
|
|
||||||
onPlayResult_l(SockException(Err_other, "rtmp url非法"), false);
|
onPlayResult_l(SockException(Err_other, "rtmp url非法"), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
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(), "/");
|
_app = findSubString(url.data(), (host_url + "/").data(), "/");
|
||||||
_stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL);
|
_stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL);
|
||||||
_tc_url = string("rtmp://") + host_url + "/" + _app;
|
auto app_second = findSubString(_stream_id.data(), nullptr, "/");
|
||||||
|
if (!app_second.empty() && app_second.find('?') == std::string::npos) {
|
||||||
if (!_app.size() || !_stream_id.size()) {
|
// _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);
|
onPublishResult_l(SockException(Err_other, "rtmp url非法"), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct
|
|||||||
cb(0, SockException(Err_other, ex.what()));
|
cb(0, SockException(Err_other, ex.what()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
strong_self->_socket_rtp->bindPeerAddr((struct sockaddr *)&addr);
|
strong_self->_socket_rtp->bindPeerAddr((struct sockaddr *)&addr, 0, true);
|
||||||
strong_self->onConnect();
|
strong_self->onConnect();
|
||||||
cb(strong_self->_socket_rtp->get_local_port(), SockException());
|
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;
|
case AF_INET6: ((sockaddr_in6 *)&addr)->sin6_port = htons(ntohs(((sockaddr_in6 *)&addr)->sin6_port) + 1); break;
|
||||||
default: assert(0); break;
|
default: assert(0); break;
|
||||||
}
|
}
|
||||||
_socket_rtcp->bindPeerAddr((struct sockaddr *)&addr);
|
_socket_rtcp->bindPeerAddr((struct sockaddr *)&addr, 0, true);
|
||||||
|
|
||||||
_rtcp_context = std::make_shared<RtcpContextForSend>();
|
_rtcp_context = std::make_shared<RtcpContextForSend>();
|
||||||
weak_ptr<RtpSender> weak_self = shared_from_this();
|
weak_ptr<RtpSender> weak_self = shared_from_this();
|
||||||
@ -349,4 +349,4 @@ void RtpSender::setOnClose(std::function<void(const toolkit::SockException &ex)>
|
|||||||
}
|
}
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
#endif// defined(ENABLE_RTPPROXY)
|
#endif// defined(ENABLE_RTPPROXY)
|
||||||
|
@ -89,6 +89,8 @@ public:
|
|||||||
for (auto &rtcp : rtcps) {
|
for (auto &rtcp : rtcps) {
|
||||||
strong_self->_process->onRtcp(rtcp);
|
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);
|
GET_CONFIG(uint64_t, timeoutSec, RtpProxy::kTimeoutSec);
|
||||||
@ -102,7 +104,7 @@ public:
|
|||||||
process->setOnDetach(std::move(strong_self->_on_detach));
|
process->setOnDetach(std::move(strong_self->_on_detach));
|
||||||
}
|
}
|
||||||
if (!process) { // process 未创建,触发rtp server 超时事件
|
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);
|
(int)strong_self->_tcp_mode, strong_self->_re_use_port, strong_self->_ssrc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,7 +156,7 @@ private:
|
|||||||
EventPoller::DelayTask::Ptr _delay_task;
|
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服务器
|
//创建udp服务器
|
||||||
Socket::Ptr rtp_socket = Socket::createSocket(nullptr, true);
|
Socket::Ptr rtp_socket = Socket::createSocket(nullptr, true);
|
||||||
Socket::Ptr rtcp_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服务器
|
//创建udp服务器
|
||||||
UdpServer::Ptr udp_server;
|
UdpServer::Ptr udp_server;
|
||||||
RtcpHelper::Ptr helper;
|
RtcpHelper::Ptr helper;
|
||||||
if (!stream_id.empty()) {
|
//增加了多路复用判断,如果多路复用为true,就走else逻辑,同时保留了原来stream_id为空走else逻辑
|
||||||
|
if (!stream_id.empty() && !multiplex) {
|
||||||
//指定了流id,那么一个端口一个流(不管是否包含多个ssrc的多个流,绑定rtp源后,会筛选掉ip端口不匹配的流)
|
//指定了流id,那么一个端口一个流(不管是否包含多个ssrc的多个流,绑定rtp源后,会筛选掉ip端口不匹配的流)
|
||||||
helper = std::make_shared<RtcpHelper>(std::move(rtcp_socket), stream_id);
|
helper = std::make_shared<RtcpHelper>(std::move(rtcp_socket), stream_id);
|
||||||
helper->startRtcp();
|
helper->startRtcp();
|
||||||
|
@ -42,9 +42,10 @@ public:
|
|||||||
* @param local_ip 绑定的本地网卡ip
|
* @param local_ip 绑定的本地网卡ip
|
||||||
* @param re_use_port 是否设置socket为re_use属性
|
* @param re_use_port 是否设置socket为re_use属性
|
||||||
* @param ssrc 指定的ssrc
|
* @param ssrc 指定的ssrc
|
||||||
|
* @param multiplex 多路复用
|
||||||
*/
|
*/
|
||||||
void start(uint16_t local_port, const std::string &stream_id = "", TcpMode tcp_mode = PASSIVE,
|
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主动模式)
|
* 连接到tcp服务(tcp主动模式)
|
||||||
|
@ -26,7 +26,13 @@ public:
|
|||||||
_media_src = std::make_shared<TSMediaSource>(tuple);
|
_media_src = std::make_shared<TSMediaSource>(tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
~TSMediaSourceMuxer() override { MpegMuxer::flush(); };
|
~TSMediaSourceMuxer() override {
|
||||||
|
try {
|
||||||
|
MpegMuxer::flush();
|
||||||
|
} catch (std::exception &ex) {
|
||||||
|
WarnL << ex.what();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
|
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
|
||||||
setDelegate(listener);
|
setDelegate(listener);
|
||||||
|
@ -81,9 +81,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
struct EchoSessionCreator {
|
struct EchoSessionCreator {
|
||||||
//返回的Session必须派生于SendInterceptor,可以返回null(拒绝连接)
|
//返回的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;
|
// return nullptr;
|
||||||
if (header.url() == "/") {
|
if (header.url() == "/") {
|
||||||
|
// 可以指定传输方式
|
||||||
|
// type = mediakit::WebSocketHeader::BINARY;
|
||||||
return std::make_shared<SessionTypeImp<EchoSession> >(header, parent, pSock);
|
return std::make_shared<SessionTypeImp<EchoSession> >(header, parent, pSock);
|
||||||
}
|
}
|
||||||
return std::make_shared<SessionTypeImp<EchoSessionWithUrl> >(header, parent, pSock);
|
return std::make_shared<SessionTypeImp<EchoSessionWithUrl> >(header, parent, pSock);
|
||||||
|
@ -29,6 +29,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||||||
#include <cstdio> // std::sprintf(), std::fopen()
|
#include <cstdio> // std::sprintf(), std::fopen()
|
||||||
#include <cstring> // std::memcpy(), std::strcmp()
|
#include <cstring> // std::memcpy(), std::strcmp()
|
||||||
#include "Util/util.h"
|
#include "Util/util.h"
|
||||||
|
#include "Util/SSLBox.h"
|
||||||
|
#include "Util/SSLUtil.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -129,16 +131,10 @@ namespace RTC
|
|||||||
MS_TRACE();
|
MS_TRACE();
|
||||||
|
|
||||||
// Generate a X509 certificate and private key (unless PEM files are provided).
|
// Generate a X509 certificate and private key (unless PEM files are provided).
|
||||||
if (true /*
|
auto ssl = toolkit::SSL_Initor::Instance().getSSLCtx("", true);
|
||||||
Settings::configuration.dtlsCertificateFile.empty() ||
|
if (!ssl || !ReadCertificateAndPrivateKeyFromContext(ssl.get())) {
|
||||||
Settings::configuration.dtlsPrivateKeyFile.empty()*/)
|
|
||||||
{
|
|
||||||
GenerateCertificateAndPrivateKey();
|
GenerateCertificateAndPrivateKey();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ReadCertificateAndPrivateKeyFromFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a global SSL_CTX.
|
// Create a global SSL_CTX.
|
||||||
CreateSslCtx();
|
CreateSslCtx();
|
||||||
@ -297,59 +293,22 @@ namespace RTC
|
|||||||
MS_THROW_ERROR("DTLS certificate and private key generation failed");
|
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();
|
MS_TRACE();
|
||||||
|
certificate = SSL_CTX_get0_certificate(ctx);
|
||||||
FILE* file{ nullptr };
|
if (!certificate) {
|
||||||
|
return false;
|
||||||
file = fopen(Settings::configuration.dtlsCertificateFile.c_str(), "r");
|
|
||||||
|
|
||||||
if (!file)
|
|
||||||
{
|
|
||||||
MS_ERROR("error reading DTLS certificate file: %s", std::strerror(errno));
|
|
||||||
|
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
|
X509_up_ref(certificate);
|
||||||
|
|
||||||
certificate = PEM_read_X509(file, nullptr, nullptr, nullptr);
|
privateKey = SSL_CTX_get0_privatekey(ctx);
|
||||||
|
if (!privateKey) {
|
||||||
if (!certificate)
|
return false;
|
||||||
{
|
|
||||||
LOG_OPENSSL_ERROR("PEM_read_X509() failed");
|
|
||||||
|
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
|
EVP_PKEY_up_ref(privateKey);
|
||||||
fclose(file);
|
InfoL << "Load webrtc dtls certificate: " << toolkit::SSLUtil::getServerName(certificate);
|
||||||
|
return true;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DtlsTransport::DtlsEnvironment::CreateSslCtx()
|
void DtlsTransport::DtlsEnvironment::CreateSslCtx()
|
||||||
|
@ -88,7 +88,7 @@ namespace RTC
|
|||||||
private:
|
private:
|
||||||
DtlsEnvironment();
|
DtlsEnvironment();
|
||||||
void GenerateCertificateAndPrivateKey();
|
void GenerateCertificateAndPrivateKey();
|
||||||
void ReadCertificateAndPrivateKeyFromFiles();
|
bool ReadCertificateAndPrivateKeyFromContext(SSL_CTX *ctx);
|
||||||
void CreateSslCtx();
|
void CreateSslCtx();
|
||||||
void GenerateFingerprints();
|
void GenerateFingerprints();
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ void WebRtcTransport::sendSockData(const char *buf, size_t len, RTC::TransportTu
|
|||||||
}
|
}
|
||||||
|
|
||||||
Session::Ptr WebRtcTransport::getSession() const {
|
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<Session>(tuple->shared_from_this()) : nullptr;
|
return tuple ? static_pointer_cast<Session>(tuple->shared_from_this()) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
www/webassist
Submodule
1
www/webassist
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit b02d2a4c1abf95db45e50bb77d789defa0fcc4b7
|
Loading…
Reference in New Issue
Block a user