mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-27 05:38:31 +08:00
commit
2d16e39445
@ -1 +1 @@
|
|||||||
Subproject commit c3acf2bd7fff96651da4f77d47e7e0aeb728e5d0
|
Subproject commit fe572323b10d72819a4d69b326dd70e73c7bf1a6
|
@ -1 +1 @@
|
|||||||
Subproject commit 0e726dd4e06ab4ed3723deaf3f73386e100bb10d
|
Subproject commit e399b93802610dcf574ff64bcb7677572cd028c1
|
@ -102,112 +102,158 @@ static onceToken token1([](){
|
|||||||
#define REALM "realm_zlmedaikit"
|
#define REALM "realm_zlmedaikit"
|
||||||
static map<string,FlvRecorder::Ptr> s_mapFlvRecorder;
|
static map<string,FlvRecorder::Ptr> s_mapFlvRecorder;
|
||||||
static mutex s_mtxFlvRecorder;
|
static mutex s_mtxFlvRecorder;
|
||||||
static onceToken s_token([](){
|
|
||||||
//监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问
|
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastOnGetRtspRealm,[](BroadcastOnGetRtspRealmArgs){
|
|
||||||
DebugL << "RTSP是否需要鉴权事件:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ;
|
|
||||||
if(string("1") == args._streamid ){
|
|
||||||
// live/1需要认证
|
|
||||||
//该流需要认证,并且设置realm
|
|
||||||
invoker(REALM);
|
|
||||||
}else{
|
|
||||||
//有时我们要查询redis或数据库来判断该流是否需要认证,通过invoker的方式可以做到完全异步
|
|
||||||
//该流我们不需要认证
|
|
||||||
invoker("");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码
|
static void initEvent() {
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastOnRtspAuth,[](BroadcastOnRtspAuthArgs){
|
static onceToken s_token([]() {
|
||||||
DebugL << "RTSP播放鉴权:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ;
|
//监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问
|
||||||
DebugL << "RTSP用户:" << user_name << (must_no_encrypt ? " Base64" : " MD5" )<< " 方式登录";
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastOnGetRtspRealm,
|
||||||
string user = user_name;
|
[](BroadcastOnGetRtspRealmArgs) {
|
||||||
//假设我们异步读取数据库
|
DebugL << "RTSP是否需要鉴权事件:" << args._schema << " "
|
||||||
if(user == "test0"){
|
<< args._vhost << " " << args._app << " "
|
||||||
//假设数据库保存的是明文
|
<< args._streamid << " "
|
||||||
invoker(false,"pwd0");
|
<< args._param_strs;
|
||||||
return;
|
if (string("1") == args._streamid) {
|
||||||
}
|
// live/1需要认证
|
||||||
|
//该流需要认证,并且设置realm
|
||||||
|
invoker(REALM);
|
||||||
|
} else {
|
||||||
|
//有时我们要查询redis或数据库来判断该流是否需要认证,通过invoker的方式可以做到完全异步
|
||||||
|
//该流我们不需要认证
|
||||||
|
invoker("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if(user == "test1"){
|
//监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码
|
||||||
//假设数据库保存的是密文
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastOnRtspAuth,
|
||||||
auto encrypted_pwd = MD5(user + ":" + REALM + ":" + "pwd1").hexdigest();
|
[](BroadcastOnRtspAuthArgs) {
|
||||||
invoker(true,encrypted_pwd);
|
DebugL << "RTSP播放鉴权:" << args._schema << " "
|
||||||
return;
|
<< args._vhost << " " << args._app << " "
|
||||||
}
|
<< args._streamid << " "
|
||||||
if(user == "test2" && must_no_encrypt){
|
<< args._param_strs;
|
||||||
//假设登录的是test2,并且以base64方式登录,此时我们提供加密密码,那么会导致认证失败
|
DebugL << "RTSP用户:" << user_name
|
||||||
//可以通过这个方式屏蔽base64这种不安全的加密方式
|
<< (must_no_encrypt ? " Base64" : " MD5")
|
||||||
invoker(true,"pwd2");
|
<< " 方式登录";
|
||||||
return;
|
string user = user_name;
|
||||||
}
|
//假设我们异步读取数据库
|
||||||
|
if (user == "test0") {
|
||||||
|
//假设数据库保存的是明文
|
||||||
|
invoker(false, "pwd0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//其他用户密码跟用户名一致
|
if (user == "test1") {
|
||||||
invoker(false,user);
|
//假设数据库保存的是密文
|
||||||
});
|
auto encrypted_pwd = MD5(
|
||||||
|
user + ":" + REALM + ":" +
|
||||||
|
"pwd1").hexdigest();
|
||||||
|
invoker(true, encrypted_pwd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (user == "test2" && must_no_encrypt) {
|
||||||
|
//假设登录的是test2,并且以base64方式登录,此时我们提供加密密码,那么会导致认证失败
|
||||||
|
//可以通过这个方式屏蔽base64这种不安全的加密方式
|
||||||
|
invoker(true, "pwd2");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//其他用户密码跟用户名一致
|
||||||
|
invoker(false, user);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
//监听rtsp/rtmp推流事件,返回结果告知是否有推流权限
|
//监听rtsp/rtmp推流事件,返回结果告知是否有推流权限
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPublish,[](BroadcastMediaPublishArgs){
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaPublish,
|
||||||
DebugL << "推流鉴权:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ;
|
[](BroadcastMediaPublishArgs) {
|
||||||
invoker("");//鉴权成功
|
DebugL << "推流鉴权:" << args._schema << " "
|
||||||
//invoker("this is auth failed message");//鉴权失败
|
<< args._vhost << " " << args._app << " "
|
||||||
});
|
<< args._streamid << " "
|
||||||
|
<< args._param_strs;
|
||||||
|
invoker("");//鉴权成功
|
||||||
|
//invoker("this is auth failed message");//鉴权失败
|
||||||
|
});
|
||||||
|
|
||||||
//监听rtsp/rtsps/rtmp/http-flv播放事件,返回结果告知是否有播放权限(rtsp通过kBroadcastOnRtspAuth或此事件都可以实现鉴权)
|
//监听rtsp/rtsps/rtmp/http-flv播放事件,返回结果告知是否有播放权限(rtsp通过kBroadcastOnRtspAuth或此事件都可以实现鉴权)
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPlayed,[](BroadcastMediaPlayedArgs){
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaPlayed,
|
||||||
DebugL << "播放鉴权:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ;
|
[](BroadcastMediaPlayedArgs) {
|
||||||
invoker("");//鉴权成功
|
DebugL << "播放鉴权:" << args._schema << " "
|
||||||
//invoker("this is auth failed message");//鉴权失败
|
<< args._vhost << " " << args._app << " "
|
||||||
});
|
<< args._streamid << " "
|
||||||
|
<< args._param_strs;
|
||||||
|
invoker("");//鉴权成功
|
||||||
|
//invoker("this is auth failed message");//鉴权失败
|
||||||
|
});
|
||||||
|
|
||||||
//shell登录事件,通过shell可以登录进服务器执行一些命令
|
//shell登录事件,通过shell可以登录进服务器执行一些命令
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastShellLogin,
|
||||||
DebugL << "shell login:" << user_name << " " << passwd;
|
[](BroadcastShellLoginArgs) {
|
||||||
invoker("");//鉴权成功
|
DebugL << "shell login:" << user_name << " "
|
||||||
//invoker("this is auth failed message");//鉴权失败
|
<< passwd;
|
||||||
});
|
invoker("");//鉴权成功
|
||||||
|
//invoker("this is auth failed message");//鉴权失败
|
||||||
|
});
|
||||||
|
|
||||||
//监听rtsp、rtmp源注册或注销事件;此处用于测试rtmp保存为flv录像,保存在http根目录下
|
//监听rtsp、rtmp源注册或注销事件;此处用于测试rtmp保存为flv录像,保存在http根目录下
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaChanged,[](BroadcastMediaChangedArgs){
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaChanged,
|
||||||
if(schema == RTMP_SCHEMA && app == "live"){
|
[](BroadcastMediaChangedArgs) {
|
||||||
lock_guard<mutex> lck(s_mtxFlvRecorder);
|
if (schema == RTMP_SCHEMA && app == "live") {
|
||||||
if(bRegist){
|
lock_guard<mutex> lck(s_mtxFlvRecorder);
|
||||||
DebugL << "开始录制RTMP:" << schema << " " << vhost << " " << app << " " << stream;
|
if (bRegist) {
|
||||||
GET_CONFIG(string,http_root,Http::kRootPath);
|
DebugL << "开始录制RTMP:" << schema << " "
|
||||||
auto path = http_root + "/" + vhost + "/" + app + "/" + stream + "_" + to_string(time(NULL)) + ".flv";
|
<< vhost << " " << app << " "
|
||||||
FlvRecorder::Ptr recorder(new FlvRecorder);
|
<< stream;
|
||||||
try{
|
GET_CONFIG(string, http_root,
|
||||||
recorder->startRecord(EventPollerPool::Instance().getPoller(),dynamic_pointer_cast<RtmpMediaSource>(sender.shared_from_this()),path);
|
Http::kRootPath);
|
||||||
s_mapFlvRecorder[vhost + "/" + app + "/" + stream] = recorder;
|
auto path = http_root + "/" + vhost + "/" +
|
||||||
}catch(std::exception &ex){
|
app + "/" + stream + "_" +
|
||||||
WarnL << ex.what();
|
to_string(time(NULL)) + ".flv";
|
||||||
}
|
FlvRecorder::Ptr recorder(new FlvRecorder);
|
||||||
}else{
|
try {
|
||||||
s_mapFlvRecorder.erase(vhost + "/" + app + "/" + stream);
|
recorder->startRecord(
|
||||||
}
|
EventPollerPool::Instance().getPoller(),
|
||||||
}
|
dynamic_pointer_cast<RtmpMediaSource>(
|
||||||
});
|
sender.shared_from_this()),
|
||||||
|
path);
|
||||||
|
s_mapFlvRecorder[vhost + "/" + app +
|
||||||
|
"/" +
|
||||||
|
stream] = recorder;
|
||||||
|
} catch (std::exception &ex) {
|
||||||
|
WarnL << ex.what();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_mapFlvRecorder.erase(
|
||||||
|
vhost + "/" + app + "/" + stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//监听播放失败(未找到特定的流)事件
|
//监听播放失败(未找到特定的流)事件
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastNotFoundStream,[](BroadcastNotFoundStreamArgs){
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastNotFoundStream,
|
||||||
/**
|
[](BroadcastNotFoundStreamArgs) {
|
||||||
* 你可以在这个事件触发时再去拉流,这样就可以实现按需拉流
|
/**
|
||||||
* 拉流成功后,ZLMediaKit会把其立即转发给播放器(最大等待时间约为5秒,如果5秒都未拉流成功,播放器会播放失败)
|
* 你可以在这个事件触发时再去拉流,这样就可以实现按需拉流
|
||||||
*/
|
* 拉流成功后,ZLMediaKit会把其立即转发给播放器(最大等待时间约为5秒,如果5秒都未拉流成功,播放器会播放失败)
|
||||||
DebugL << "未找到流事件:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs ;
|
*/
|
||||||
});
|
DebugL << "未找到流事件:" << args._schema << " "
|
||||||
|
<< args._vhost << " " << args._app << " "
|
||||||
|
<< args._streamid << " "
|
||||||
|
<< args._param_strs;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
//监听播放或推流结束时消耗流量事件
|
//监听播放或推流结束时消耗流量事件
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastFlowReport,[](BroadcastFlowReportArgs){
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastFlowReport,
|
||||||
DebugL << "播放器(推流器)断开连接事件:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs
|
[](BroadcastFlowReportArgs) {
|
||||||
<< "\r\n使用流量:" << totalBytes << " bytes,连接时长:" << totalDuration << "秒" ;
|
DebugL << "播放器(推流器)断开连接事件:" << args._schema << " "
|
||||||
|
<< args._vhost << " " << args._app << " "
|
||||||
|
<< args._streamid << " " << args._param_strs
|
||||||
|
<< "\r\n使用流量:" << totalBytes
|
||||||
|
<< " bytes,连接时长:" << totalDuration << "秒";
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(SIGHUP)
|
#if !defined(SIGHUP)
|
||||||
#define SIGHUP 1
|
#define SIGHUP 1
|
||||||
@ -296,6 +342,7 @@ static int do_main(string ini_file) {
|
|||||||
TcpServer::Ptr rtspSSLSrv(new TcpServer());
|
TcpServer::Ptr rtspSSLSrv(new TcpServer());
|
||||||
rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort);//默认322
|
rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort);//默认322
|
||||||
|
|
||||||
|
initEvent();
|
||||||
//服务器支持动态切换端口(不影响现有连接)
|
//服务器支持动态切换端口(不影响现有连接)
|
||||||
NoticeCenter::Instance().addListener(ReloadConfigTag,Broadcast::kBroadcastReloadConfig,[&](BroadcastReloadConfigArgs){
|
NoticeCenter::Instance().addListener(ReloadConfigTag,Broadcast::kBroadcastReloadConfig,[&](BroadcastReloadConfigArgs){
|
||||||
//重新创建服务器
|
//重新创建服务器
|
||||||
|
@ -9,7 +9,7 @@ else()
|
|||||||
file(GLOB MediaServer_src_list ./*.cpp ./*.h)
|
file(GLOB MediaServer_src_list ./*.cpp ./*.h)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS ${MediaServer_src_list})
|
#message(STATUS ${MediaServer_src_list})
|
||||||
|
|
||||||
add_executable(MediaServer ${MediaServer_src_list})
|
add_executable(MediaServer ${MediaServer_src_list})
|
||||||
|
|
||||||
|
@ -449,6 +449,8 @@ void installWebApi() {
|
|||||||
const string &app,
|
const string &app,
|
||||||
const string &stream,
|
const string &stream,
|
||||||
const string &url,
|
const string &url,
|
||||||
|
bool enable_rtsp,
|
||||||
|
bool enable_rtmp,
|
||||||
bool enable_hls,
|
bool enable_hls,
|
||||||
bool enable_mp4,
|
bool enable_mp4,
|
||||||
int rtp_type,
|
int rtp_type,
|
||||||
@ -461,7 +463,7 @@ void installWebApi() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//添加拉流代理
|
//添加拉流代理
|
||||||
PlayerProxy::Ptr player(new PlayerProxy(vhost,app,stream,enable_hls,enable_mp4));
|
PlayerProxy::Ptr player(new PlayerProxy(vhost,app,stream,enable_rtsp,enable_rtmp,enable_hls,enable_mp4));
|
||||||
s_proxyMap[key] = player;
|
s_proxyMap[key] = player;
|
||||||
|
|
||||||
//指定RTP over TCP(播放rtsp时有效)
|
//指定RTP over TCP(播放rtsp时有效)
|
||||||
@ -484,16 +486,18 @@ void installWebApi() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//动态添加rtsp/rtmp拉流代理
|
//动态添加rtsp/rtmp拉流代理
|
||||||
//测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&stream=0&url=rtmp://127.0.0.1/live/obs
|
//测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs
|
||||||
API_REGIST_INVOKER(api,addStreamProxy,{
|
API_REGIST_INVOKER(api,addStreamProxy,{
|
||||||
CHECK_SECRET();
|
CHECK_SECRET();
|
||||||
CHECK_ARGS("vhost","app","stream","url");
|
CHECK_ARGS("vhost","app","stream","url","enable_rtsp","enable_rtmp");
|
||||||
addStreamProxy(allArgs["vhost"],
|
addStreamProxy(allArgs["vhost"],
|
||||||
allArgs["app"],
|
allArgs["app"],
|
||||||
allArgs["stream"],
|
allArgs["stream"],
|
||||||
allArgs["url"],
|
allArgs["url"],
|
||||||
allArgs["enable_hls"],
|
allArgs["enable_rtsp"],/* 是否rtsp转发 */
|
||||||
allArgs["enable_mp4"],
|
allArgs["enable_rtmp"],/* 是否rtmp转发 */
|
||||||
|
allArgs["enable_hls"],/* 是否hls转发 */
|
||||||
|
allArgs["enable_mp4"],/* 是否MP4录制 */
|
||||||
allArgs["rtp_type"],
|
allArgs["rtp_type"],
|
||||||
[invoker,val,headerOut](const SockException &ex,const string &key){
|
[invoker,val,headerOut](const SockException &ex,const string &key){
|
||||||
if(ex){
|
if(ex){
|
||||||
@ -612,7 +616,7 @@ void installWebApi() {
|
|||||||
|
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
API_REGIST_INVOKER(hook,on_stream_not_found,{
|
API_REGIST_INVOKER(hook,on_stream_not_found_ffmpeg,{
|
||||||
//媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
|
//媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
|
||||||
CHECK_SECRET();
|
CHECK_SECRET();
|
||||||
CHECK_ARGS("vhost","app","stream");
|
CHECK_ARGS("vhost","app","stream");
|
||||||
@ -640,8 +644,7 @@ void installWebApi() {
|
|||||||
invoker("200 OK", headerOut, val.toStyledString());
|
invoker("200 OK", headerOut, val.toStyledString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
#endif//!defined(_WIN32)
|
||||||
#else
|
|
||||||
|
|
||||||
API_REGIST_INVOKER(hook,on_stream_not_found,{
|
API_REGIST_INVOKER(hook,on_stream_not_found,{
|
||||||
//媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
|
//媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
|
||||||
@ -652,9 +655,11 @@ void installWebApi() {
|
|||||||
allArgs["app"],
|
allArgs["app"],
|
||||||
allArgs["stream"],
|
allArgs["stream"],
|
||||||
/** 支持rtsp和rtmp方式拉流 ,rtsp支持h265/h264/aac,rtmp仅支持h264/aac **/
|
/** 支持rtsp和rtmp方式拉流 ,rtsp支持h265/h264/aac,rtmp仅支持h264/aac **/
|
||||||
"rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov",//rtmp://live.hkstv.hk.lxdns.com/live/hks2
|
"rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov",
|
||||||
false,
|
true,/* 开启rtsp转发 */
|
||||||
false,
|
true,/* 开启rtmp转发 */
|
||||||
|
true,/* 开启hls转发 */
|
||||||
|
false,/* 禁用MP4录制 */
|
||||||
0,//rtp over tcp方式拉流
|
0,//rtp over tcp方式拉流
|
||||||
[invoker,val,headerOut](const SockException &ex,const string &key){
|
[invoker,val,headerOut](const SockException &ex,const string &key){
|
||||||
if(ex){
|
if(ex){
|
||||||
@ -666,7 +671,6 @@ void installWebApi() {
|
|||||||
invoker("200 OK", headerOut, val.toStyledString());
|
invoker("200 OK", headerOut, val.toStyledString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
#endif // !defined(_WIN32)
|
|
||||||
|
|
||||||
API_REGIST(hook,on_record_mp4,{
|
API_REGIST(hook,on_record_mp4,{
|
||||||
//录制mp4分片完毕事件
|
//录制mp4分片完毕事件
|
||||||
|
@ -41,9 +41,11 @@ DevChannel::DevChannel(const string &strVhost,
|
|||||||
const string &strApp,
|
const string &strApp,
|
||||||
const string &strId,
|
const string &strId,
|
||||||
float fDuration,
|
float fDuration,
|
||||||
|
bool bEanbleRtsp,
|
||||||
|
bool bEanbleRtmp,
|
||||||
bool bEanbleHls,
|
bool bEanbleHls,
|
||||||
bool bEnableMp4) :
|
bool bEnableMp4) :
|
||||||
MultiMediaSourceMuxer(strVhost, strApp, strId, fDuration, bEanbleHls, bEnableMp4) {}
|
MultiMediaSourceMuxer(strVhost, strApp, strId, fDuration, bEanbleRtsp, bEanbleRtmp, bEanbleHls, bEnableMp4) {}
|
||||||
|
|
||||||
DevChannel::~DevChannel() {}
|
DevChannel::~DevChannel() {}
|
||||||
|
|
||||||
@ -101,7 +103,7 @@ void DevChannel::inputH264(const char* pcData, int iDataLen, uint32_t dts,uint32
|
|||||||
} else {
|
} else {
|
||||||
prefixeSize = 0;
|
prefixeSize = 0;
|
||||||
}
|
}
|
||||||
inputFrame(std::make_shared<H264FrameNoCopyAble>((char *)pcData,iDataLen,dts,pts,prefixeSize));
|
inputFrame(std::make_shared<H264FrameNoCacheAble>((char *)pcData,iDataLen,dts,pts,prefixeSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevChannel::inputAAC(const char* pcData, int iDataLen, uint32_t uiStamp,bool withAdtsHeader) {
|
void DevChannel::inputAAC(const char* pcData, int iDataLen, uint32_t uiStamp,bool withAdtsHeader) {
|
||||||
@ -117,12 +119,12 @@ void DevChannel::inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t u
|
|||||||
uiStamp = (uint32_t)_aTicker[1].elapsedTime();
|
uiStamp = (uint32_t)_aTicker[1].elapsedTime();
|
||||||
}
|
}
|
||||||
if(pcAdtsHeader + 7 == pcDataWithoutAdts){
|
if(pcAdtsHeader + 7 == pcDataWithoutAdts){
|
||||||
inputFrame(std::make_shared<AACFrameNoCopyAble>((char *)pcDataWithoutAdts - 7,iDataLen + 7,uiStamp,7));
|
inputFrame(std::make_shared<AACFrameNoCacheAble>((char *)pcDataWithoutAdts - 7,iDataLen + 7,uiStamp,7));
|
||||||
} else {
|
} else {
|
||||||
char *dataWithAdts = new char[iDataLen + 7];
|
char *dataWithAdts = new char[iDataLen + 7];
|
||||||
memcpy(dataWithAdts,pcAdtsHeader,7);
|
memcpy(dataWithAdts,pcAdtsHeader,7);
|
||||||
memcpy(dataWithAdts + 7 , pcDataWithoutAdts , iDataLen);
|
memcpy(dataWithAdts + 7 , pcDataWithoutAdts , iDataLen);
|
||||||
inputFrame(std::make_shared<AACFrameNoCopyAble>(dataWithAdts,iDataLen + 7,uiStamp,7));
|
inputFrame(std::make_shared<AACFrameNoCacheAble>(dataWithAdts,iDataLen + 7,uiStamp,7));
|
||||||
delete [] dataWithAdts;
|
delete [] dataWithAdts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,8 @@ public:
|
|||||||
const string &strApp,
|
const string &strApp,
|
||||||
const string &strId,
|
const string &strId,
|
||||||
float fDuration = 0,
|
float fDuration = 0,
|
||||||
|
bool bEanbleRtsp = true,
|
||||||
|
bool bEanbleRtmp = true,
|
||||||
bool bEanbleHls = true,
|
bool bEanbleHls = true,
|
||||||
bool bEnableMp4 = false);
|
bool bEnableMp4 = false);
|
||||||
|
|
||||||
|
@ -241,9 +241,17 @@ void MediaInfo::parse(const string &url){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MediaSourceEvent::onNoneReader(MediaSource &sender){
|
void MediaSourceEvent::onNoneReader(MediaSource &sender){
|
||||||
WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId();
|
|
||||||
//没有任何读取器消费该源,表明该源可以关闭了
|
//没有任何读取器消费该源,表明该源可以关闭了
|
||||||
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader,sender);
|
WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId();
|
||||||
|
weak_ptr<MediaSource> weakPtr = sender.shared_from_this();
|
||||||
|
|
||||||
|
//异步广播该事件,防止同步调用sender.close()导致在接收rtp或rtmp包时清空包缓存等操作
|
||||||
|
EventPollerPool::Instance().getPoller()->async([weakPtr](){
|
||||||
|
auto strongPtr = weakPtr.lock();
|
||||||
|
if(strongPtr){
|
||||||
|
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader,*strongPtr);
|
||||||
|
}
|
||||||
|
},false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,12 +39,18 @@ public:
|
|||||||
const string &strApp,
|
const string &strApp,
|
||||||
const string &strId,
|
const string &strId,
|
||||||
float dur_sec = 0.0,
|
float dur_sec = 0.0,
|
||||||
|
bool bEanbleRtsp = true,
|
||||||
|
bool bEanbleRtmp = true,
|
||||||
bool bEanbleHls = true,
|
bool bEanbleHls = true,
|
||||||
bool bEnableMp4 = false){
|
bool bEnableMp4 = false
|
||||||
_rtmp = std::make_shared<RtmpMediaSourceMuxer>(vhost,strApp,strId,std::make_shared<TitleMete>(dur_sec));
|
){
|
||||||
_rtsp = std::make_shared<RtspMediaSourceMuxer>(vhost,strApp,strId,std::make_shared<TitleSdp>(dur_sec));
|
if (bEanbleRtmp) {
|
||||||
|
_rtmp = std::make_shared<RtmpMediaSourceMuxer>(vhost, strApp, strId, std::make_shared<TitleMete>(dur_sec));
|
||||||
|
}
|
||||||
|
if (bEanbleRtsp) {
|
||||||
|
_rtsp = std::make_shared<RtspMediaSourceMuxer>(vhost, strApp, strId, std::make_shared<TitleSdp>(dur_sec));
|
||||||
|
}
|
||||||
_record = std::make_shared<MediaRecorder>(vhost,strApp,strId,bEanbleHls,bEnableMp4);
|
_record = std::make_shared<MediaRecorder>(vhost,strApp,strId,bEanbleHls,bEnableMp4);
|
||||||
|
|
||||||
}
|
}
|
||||||
virtual ~MultiMediaSourceMuxer(){}
|
virtual ~MultiMediaSourceMuxer(){}
|
||||||
|
|
||||||
@ -54,8 +60,12 @@ public:
|
|||||||
* @param track 媒体描述
|
* @param track 媒体描述
|
||||||
*/
|
*/
|
||||||
void addTrack(const Track::Ptr & track) {
|
void addTrack(const Track::Ptr & track) {
|
||||||
_rtmp->addTrack(track);
|
if(_rtmp){
|
||||||
_rtsp->addTrack(track);
|
_rtmp->addTrack(track);
|
||||||
|
}
|
||||||
|
if(_rtsp){
|
||||||
|
_rtsp->addTrack(track);
|
||||||
|
}
|
||||||
_record->addTrack(track);
|
_record->addTrack(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +74,12 @@ public:
|
|||||||
* @param frame 帧数据
|
* @param frame 帧数据
|
||||||
*/
|
*/
|
||||||
void inputFrame(const Frame::Ptr &frame) override {
|
void inputFrame(const Frame::Ptr &frame) override {
|
||||||
_rtmp->inputFrame(frame);
|
if(_rtmp) {
|
||||||
_rtsp->inputFrame(frame);
|
_rtmp->inputFrame(frame);
|
||||||
|
}
|
||||||
|
if(_rtsp) {
|
||||||
|
_rtsp->inputFrame(frame);
|
||||||
|
}
|
||||||
_record->inputFrame(frame);
|
_record->inputFrame(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,8 +88,12 @@ public:
|
|||||||
* @param listener
|
* @param listener
|
||||||
*/
|
*/
|
||||||
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
|
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
|
||||||
_rtmp->setListener(listener);
|
if(_rtmp) {
|
||||||
_rtsp->setListener(listener);
|
_rtmp->setListener(listener);
|
||||||
|
}
|
||||||
|
if(_rtsp) {
|
||||||
|
_rtsp->setListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,11 +101,13 @@ public:
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
int readerCount() const{
|
int readerCount() const{
|
||||||
return _rtsp->readerCount() + _rtmp->readerCount();
|
return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTimeStamp(uint32_t stamp){
|
void setTimeStamp(uint32_t stamp){
|
||||||
_rtsp->setTimeStamp(stamp);
|
if(_rtsp){
|
||||||
|
_rtsp->setTimeStamp(stamp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
RtmpMediaSourceMuxer::Ptr _rtmp;
|
RtmpMediaSourceMuxer::Ptr _rtmp;
|
||||||
|
@ -16,7 +16,9 @@ namespace mediakit{
|
|||||||
string FindField(const char *buf, const char *start, const char *end, int bufSize = 0);
|
string FindField(const char *buf, const char *start, const char *end, int bufSize = 0);
|
||||||
|
|
||||||
struct StrCaseCompare {
|
struct StrCaseCompare {
|
||||||
bool operator()(const string &__x, const string &__y) const { return strcasecmp(__x.data(), __y.data()) < 0; }
|
bool operator()(const string &__x, const string &__y) const {
|
||||||
|
return strcasecmp(__x.data(), __y.data()) < 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -25,17 +27,19 @@ class StrCaseMap : public multimap<string, string, StrCaseCompare>{
|
|||||||
typedef multimap<string, string, StrCaseCompare> Super ;
|
typedef multimap<string, string, StrCaseCompare> Super ;
|
||||||
StrCaseMap() = default;
|
StrCaseMap() = default;
|
||||||
~StrCaseMap() = default;
|
~StrCaseMap() = default;
|
||||||
string &operator[](const string &key){
|
|
||||||
auto it = find(key);
|
template <class K>
|
||||||
|
string &operator[](K &&k){
|
||||||
|
auto it = find(std::forward<K>(k));
|
||||||
if(it == end()){
|
if(it == end()){
|
||||||
it = Super::emplace(key,"");
|
it = Super::emplace(std::forward<K>(k),"");
|
||||||
}
|
}
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class K,class V>
|
template <class K,class V>
|
||||||
void emplace(K &&k , V &&v) {
|
void emplace(K &&k , V &&v) {
|
||||||
auto it = find(k);
|
auto it = find(std::forward<K>(k));
|
||||||
if(it != end()){
|
if(it != end()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -161,11 +161,13 @@ namespace Rtsp {
|
|||||||
const string kAuthBasic = RTSP_FIELD"authBasic";
|
const string kAuthBasic = RTSP_FIELD"authBasic";
|
||||||
const string kHandshakeSecond = RTSP_FIELD"handshakeSecond";
|
const string kHandshakeSecond = RTSP_FIELD"handshakeSecond";
|
||||||
const string kKeepAliveSecond = RTSP_FIELD"keepAliveSecond";
|
const string kKeepAliveSecond = RTSP_FIELD"keepAliveSecond";
|
||||||
|
const string kDirectProxy = RTSP_FIELD"directProxy";;
|
||||||
onceToken token([](){
|
onceToken token([](){
|
||||||
//默认Md5方式认证
|
//默认Md5方式认证
|
||||||
mINI::Instance()[kAuthBasic] = 0;
|
mINI::Instance()[kAuthBasic] = 0;
|
||||||
mINI::Instance()[kHandshakeSecond] = 15;
|
mINI::Instance()[kHandshakeSecond] = 15;
|
||||||
mINI::Instance()[kKeepAliveSecond] = 15;
|
mINI::Instance()[kKeepAliveSecond] = 15;
|
||||||
|
mINI::Instance()[kDirectProxy] = 1;
|
||||||
},nullptr);
|
},nullptr);
|
||||||
|
|
||||||
} //namespace Rtsp
|
} //namespace Rtsp
|
||||||
|
@ -202,6 +202,13 @@ extern const string kAuthBasic;
|
|||||||
extern const string kHandshakeSecond;
|
extern const string kHandshakeSecond;
|
||||||
//维持链接超时时间,默认15秒
|
//维持链接超时时间,默认15秒
|
||||||
extern const string kKeepAliveSecond;
|
extern const string kKeepAliveSecond;
|
||||||
|
|
||||||
|
//rtsp拉流代理是否直接代理
|
||||||
|
//直接代理后支持任意编码格式,但是会导致GOP缓存无法定位到I帧,可能会导致开播花屏
|
||||||
|
//并且如果是tcp方式拉流,如果rtp大于mtu会导致无法使用udp方式代理
|
||||||
|
//假定您的拉流源地址不是264或265或AAC,那么你可以使用直接代理的方式来支持rtsp代理
|
||||||
|
//默认开启rtsp直接代理,rtmp由于没有这些问题,是强制开启直接代理的
|
||||||
|
extern const string kDirectProxy;
|
||||||
} //namespace Rtsp
|
} //namespace Rtsp
|
||||||
|
|
||||||
////////////RTMP服务器配置///////////
|
////////////RTMP服务器配置///////////
|
||||||
|
@ -105,11 +105,11 @@ public:
|
|||||||
uint32_t iPrefixSize = 7;
|
uint32_t iPrefixSize = 7;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
class AACFrameNoCopyAble : public FrameNoCopyAble {
|
class AACFrameNoCacheAble : public FrameNoCacheAble {
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<AACFrameNoCopyAble> Ptr;
|
typedef std::shared_ptr<AACFrameNoCacheAble> Ptr;
|
||||||
|
|
||||||
AACFrameNoCopyAble(char *ptr,uint32_t size,uint32_t dts,int prefixeSize = 7){
|
AACFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts,int prefixeSize = 7){
|
||||||
_ptr = ptr;
|
_ptr = ptr;
|
||||||
_size = size;
|
_size = size;
|
||||||
_dts = dts;
|
_dts = dts;
|
||||||
|
@ -118,8 +118,20 @@ Track::Ptr Factory::getTrackByCodecId(CodecId codecId) {
|
|||||||
RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) {
|
RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) {
|
||||||
GET_CONFIG(uint32_t,audio_mtu,Rtp::kAudioMtuSize);
|
GET_CONFIG(uint32_t,audio_mtu,Rtp::kAudioMtuSize);
|
||||||
GET_CONFIG(uint32_t,video_mtu,Rtp::kVideoMtuSize);
|
GET_CONFIG(uint32_t,video_mtu,Rtp::kVideoMtuSize);
|
||||||
// ssrc不冲突即可
|
// ssrc不冲突即可,可以为任意的32位整形
|
||||||
uint32_t ssrc = ((uint64_t) sdp.get()) & 0xFFFFFFFF;
|
static atomic<uint32_t> s_ssrc(0);
|
||||||
|
uint32_t ssrc = s_ssrc++;
|
||||||
|
if(!ssrc){
|
||||||
|
//ssrc不能为0
|
||||||
|
ssrc = 1;
|
||||||
|
}
|
||||||
|
if(sdp->getTrackType() == TrackVideo){
|
||||||
|
//视频的ssrc是偶数,方便调试
|
||||||
|
ssrc = 2 * ssrc;
|
||||||
|
}else{
|
||||||
|
//音频ssrc是奇数
|
||||||
|
ssrc = 2 * ssrc + 1;
|
||||||
|
}
|
||||||
auto mtu = (sdp->getTrackType() == TrackVideo ? video_mtu : audio_mtu);
|
auto mtu = (sdp->getTrackType() == TrackVideo ? video_mtu : audio_mtu);
|
||||||
auto sample_rate = sdp->getSampleRate();
|
auto sample_rate = sdp->getSampleRate();
|
||||||
auto pt = sdp->getPlayloadType();
|
auto pt = sdp->getPlayloadType();
|
||||||
|
42
src/Extension/Frame.cpp
Normal file
42
src/Extension/Frame.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
|
||||||
|
*
|
||||||
|
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Frame.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace toolkit;
|
||||||
|
|
||||||
|
namespace mediakit{
|
||||||
|
|
||||||
|
Frame::Ptr Frame::getCacheAbleFrame(const Frame::Ptr &frame){
|
||||||
|
if(frame->cacheAble()){
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
return std::make_shared<FrameCacheAble>(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
}//namespace mediakit
|
||||||
|
|
@ -77,7 +77,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* 帧类型的抽象接口
|
* 帧类型的抽象接口
|
||||||
*/
|
*/
|
||||||
class Frame : public Buffer, public CodecInfo{
|
class Frame : public Buffer, public CodecInfo {
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<Frame> Ptr;
|
typedef std::shared_ptr<Frame> Ptr;
|
||||||
virtual ~Frame(){}
|
virtual ~Frame(){}
|
||||||
@ -116,6 +116,17 @@ public:
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
virtual bool keyFrame() const = 0;
|
virtual bool keyFrame() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否可以缓存
|
||||||
|
*/
|
||||||
|
virtual bool cacheAble() const { return true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回可缓存的frame
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static Ptr getCacheAbleFrame(const Ptr &frame);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -281,9 +292,12 @@ private:
|
|||||||
map<void *,FrameWriterInterface::Ptr> _delegateMap;
|
map<void *,FrameWriterInterface::Ptr> _delegateMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FrameNoCopyAble : public Frame{
|
/**
|
||||||
|
* 通过Frame接口包装指针,方便使用者把自己的数据快速接入ZLMediaKit
|
||||||
|
*/
|
||||||
|
class FrameFromPtr : public Frame{
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<FrameNoCopyAble> Ptr;
|
typedef std::shared_ptr<FrameFromPtr> Ptr;
|
||||||
char *data() const override{
|
char *data() const override{
|
||||||
return _ptr;
|
return _ptr;
|
||||||
}
|
}
|
||||||
@ -305,7 +319,6 @@ public:
|
|||||||
uint32_t prefixSize() const override{
|
uint32_t prefixSize() const override{
|
||||||
return _prefixSize;
|
return _prefixSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char *_ptr;
|
char *_ptr;
|
||||||
uint32_t _size;
|
uint32_t _size;
|
||||||
@ -314,6 +327,81 @@ protected:
|
|||||||
uint32_t _prefixSize;
|
uint32_t _prefixSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不可缓存的帧,在DevChannel类中有用到。
|
||||||
|
* 该帧类型用于防止内存拷贝,直接使用指针传递数据
|
||||||
|
* 在大多数情况下,ZLMediaKit是同步对帧数据进行使用和处理的
|
||||||
|
* 所以提供此类型的帧很有必要,但是有时又无法避免缓存帧做后续处理
|
||||||
|
* 所以可以通过Frame::getCacheAbleFrame方法拷贝一个可缓存的帧
|
||||||
|
*/
|
||||||
|
class FrameNoCacheAble : public FrameFromPtr{
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<FrameNoCacheAble> Ptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该帧不可缓存
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool cacheAble() const override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该对象的功能是把一个不可缓存的帧转换成可缓存的帧
|
||||||
|
* @see FrameNoCacheAble
|
||||||
|
*/
|
||||||
|
class FrameCacheAble : public FrameFromPtr {
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<FrameCacheAble> Ptr;
|
||||||
|
|
||||||
|
FrameCacheAble(const Frame::Ptr &frame){
|
||||||
|
if(frame->cacheAble()){
|
||||||
|
_frame = frame;
|
||||||
|
_ptr = frame->data();
|
||||||
|
}else{
|
||||||
|
_buffer = std::make_shared<BufferRaw>();
|
||||||
|
_buffer->assign(frame->data(),frame->size());
|
||||||
|
_ptr = _buffer->data();
|
||||||
|
}
|
||||||
|
_size = frame->size();
|
||||||
|
_dts = frame->dts();
|
||||||
|
_pts = frame->pts();
|
||||||
|
_prefixSize = frame->prefixSize();
|
||||||
|
_trackType = frame->getTrackType();
|
||||||
|
_codec = frame->getCodecId();
|
||||||
|
_key = frame->keyFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~FrameCacheAble() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可以被缓存
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool cacheAble() const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackType getTrackType() const override{
|
||||||
|
return _trackType;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodecId getCodecId() const override{
|
||||||
|
return _codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyFrame() const override{
|
||||||
|
return _key;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Frame::Ptr _frame;
|
||||||
|
BufferRaw::Ptr _buffer;
|
||||||
|
TrackType _trackType;
|
||||||
|
CodecId _codec;
|
||||||
|
bool _key;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
|
|
||||||
|
@ -92,11 +92,16 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class H264FrameNoCopyAble : public FrameNoCopyAble {
|
/**
|
||||||
|
* 防止内存拷贝的H264类
|
||||||
|
* 用户可以通过该类型快速把一个指针无拷贝的包装成Frame类
|
||||||
|
* 该类型在DevChannel中有使用
|
||||||
|
*/
|
||||||
|
class H264FrameNoCacheAble : public FrameNoCacheAble {
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<H264FrameNoCopyAble> Ptr;
|
typedef std::shared_ptr<H264FrameNoCacheAble> Ptr;
|
||||||
|
|
||||||
H264FrameNoCopyAble(char *ptr,uint32_t size,uint32_t dts , uint32_t pts ,int prefixeSize = 4){
|
H264FrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts , uint32_t pts ,int prefixeSize = 4){
|
||||||
_ptr = ptr;
|
_ptr = ptr;
|
||||||
_size = size;
|
_size = size;
|
||||||
_dts = dts;
|
_dts = dts;
|
||||||
@ -117,17 +122,26 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class H264FrameSubFrame : public H264FrameNoCopyAble{
|
/**
|
||||||
|
* 一个H264Frame类中可以有多个帧,他们通过 0x 00 00 01 分隔
|
||||||
|
* ZLMediaKit会先把这种复合帧split成单个帧然后再处理
|
||||||
|
* 一个复合帧可以通过无内存拷贝的方式切割成多个H264FrameSubFrame
|
||||||
|
* 提供该类的目的是切换复合帧时防止内存拷贝,提高性能
|
||||||
|
*/
|
||||||
|
class H264FrameSubFrame : public H264FrameNoCacheAble{
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<H264FrameSubFrame> Ptr;
|
typedef std::shared_ptr<H264FrameSubFrame> Ptr;
|
||||||
H264FrameSubFrame(const Frame::Ptr &strongRef,
|
H264FrameSubFrame(const Frame::Ptr &parent_frame,
|
||||||
char *ptr,
|
char *ptr,
|
||||||
uint32_t size,
|
uint32_t size,
|
||||||
int prefixeSize) : H264FrameNoCopyAble(ptr,size,strongRef->dts(),strongRef->pts(),prefixeSize){
|
int prefixeSize) : H264FrameNoCacheAble(ptr,size,parent_frame->dts(),parent_frame->pts(),prefixeSize){
|
||||||
_strongRef = strongRef;
|
_parent_frame = parent_frame;
|
||||||
|
}
|
||||||
|
bool cacheAble() const override {
|
||||||
|
return _parent_frame->cacheAble();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
Frame::Ptr _strongRef;
|
Frame::Ptr _parent_frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,6 +108,7 @@ inline void H264RtmpDecoder::onGetH264_l(const char* pcData, int iLen, uint32_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dts,uint32_t pts) {
|
inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dts,uint32_t pts) {
|
||||||
|
#if 1
|
||||||
_h264frame->type = H264_TYPE(pcData[0]);
|
_h264frame->type = H264_TYPE(pcData[0]);
|
||||||
_h264frame->timeStamp = dts;
|
_h264frame->timeStamp = dts;
|
||||||
_h264frame->ptsStamp = pts;
|
_h264frame->ptsStamp = pts;
|
||||||
@ -117,6 +118,11 @@ inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dt
|
|||||||
//写入环形缓存
|
//写入环形缓存
|
||||||
RtmpCodec::inputFrame(_h264frame);
|
RtmpCodec::inputFrame(_h264frame);
|
||||||
_h264frame = obtainFrame();
|
_h264frame = obtainFrame();
|
||||||
|
#else
|
||||||
|
//防止内存拷贝,这样产生的264帧不会有0x00 00 01头
|
||||||
|
auto frame = std::make_shared<H264FrameNoCacheAble>((char *)pcData,iLen,dts,pts,0);
|
||||||
|
RtmpCodec::inputFrame(frame);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,11 +121,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class H265FrameNoCopyAble : public FrameNoCopyAble {
|
class H265FrameNoCacheAble : public FrameNoCacheAble {
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<H265FrameNoCopyAble> Ptr;
|
typedef std::shared_ptr<H265FrameNoCacheAble> Ptr;
|
||||||
|
|
||||||
H265FrameNoCopyAble(char *ptr, uint32_t size, uint32_t dts,uint32_t pts, int prefixeSize = 4) {
|
H265FrameNoCacheAble(char *ptr, uint32_t size, uint32_t dts,uint32_t pts, int prefixeSize = 4) {
|
||||||
_ptr = ptr;
|
_ptr = ptr;
|
||||||
_size = size;
|
_size = size;
|
||||||
_dts = dts;
|
_dts = dts;
|
||||||
|
@ -918,7 +918,11 @@ void HttpSession::responseDelay(const string &Origin,bool bClose,
|
|||||||
headerOther["Access-Control-Allow-Origin"] = Origin;
|
headerOther["Access-Control-Allow-Origin"] = Origin;
|
||||||
headerOther["Access-Control-Allow-Credentials"] = "true";
|
headerOther["Access-Control-Allow-Credentials"] = "true";
|
||||||
}
|
}
|
||||||
const_cast<KeyValue &>(headerOut).insert(headerOther.begin(), headerOther.end());
|
|
||||||
|
for (auto &pr : headerOther){
|
||||||
|
//添加默认http头,默认http头不能覆盖用户自定义的头
|
||||||
|
const_cast<KeyValue &>(headerOut).emplace(pr.first,pr.second);
|
||||||
|
}
|
||||||
sendResponse(codeOut.data(), headerOut, contentOut);
|
sendResponse(codeOut.data(), headerOut, contentOut);
|
||||||
}
|
}
|
||||||
inline void HttpSession::sendNotFound(bool bClose) {
|
inline void HttpSession::sendNotFound(bool bClose) {
|
||||||
|
@ -37,6 +37,7 @@ namespace mediakit {
|
|||||||
|
|
||||||
#ifdef ENABLE_MP4V2
|
#ifdef ENABLE_MP4V2
|
||||||
MediaReader::MediaReader(const string &strVhost,const string &strApp, const string &strId,const string &filePath ) {
|
MediaReader::MediaReader(const string &strVhost,const string &strApp, const string &strId,const string &filePath ) {
|
||||||
|
_poller = EventPollerPool::Instance().getPoller();
|
||||||
auto strFileName = filePath;
|
auto strFileName = filePath;
|
||||||
if(strFileName.empty()){
|
if(strFileName.empty()){
|
||||||
GET_CONFIG(string,recordPath,Record::kFilePath);
|
GET_CONFIG(string,recordPath,Record::kFilePath);
|
||||||
@ -137,7 +138,7 @@ MediaReader::MediaReader(const string &strVhost,const string &strApp, const stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
_iDuration = MAX(_video_ms,_audio_ms);
|
_iDuration = MAX(_video_ms,_audio_ms);
|
||||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost,strApp,strId,_iDuration/1000.0,false, false));
|
_mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _iDuration / 1000.0, true, true, false, false));
|
||||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||||
AACTrack::Ptr track = std::make_shared<AACTrack>(_strAacCfg);
|
AACTrack::Ptr track = std::make_shared<AACTrack>(_strAacCfg);
|
||||||
_mediaMuxer->addTrack(track);
|
_mediaMuxer->addTrack(track);
|
||||||
@ -164,7 +165,7 @@ void MediaReader::startReadMP4() {
|
|||||||
|
|
||||||
_timer = std::make_shared<Timer>(sampleMS / 1000.0f,[strongSelf](){
|
_timer = std::make_shared<Timer>(sampleMS / 1000.0f,[strongSelf](){
|
||||||
return strongSelf->readSample(0,false);
|
return strongSelf->readSample(0,false);
|
||||||
}, nullptr);
|
}, _poller);
|
||||||
|
|
||||||
//先读sampleMS毫秒的数据用于产生MediaSouce
|
//先读sampleMS毫秒的数据用于产生MediaSouce
|
||||||
readSample(sampleMS, false);
|
readSample(sampleMS, false);
|
||||||
@ -260,11 +261,11 @@ inline bool MediaReader::readAudioSample(int iTimeInc,bool justSeekSyncFrame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void MediaReader::writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts) {
|
inline void MediaReader::writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts) {
|
||||||
_mediaMuxer->inputFrame(std::make_shared<H264FrameNoCopyAble>((char*)pucData,iLen,dts,pts));
|
_mediaMuxer->inputFrame(std::make_shared<H264FrameNoCacheAble>((char*)pucData,iLen,dts,pts));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MediaReader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) {
|
inline void MediaReader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) {
|
||||||
_mediaMuxer->inputFrame(std::make_shared<AACFrameNoCopyAble>((char*)pucData,iLen,uiStamp));
|
_mediaMuxer->inputFrame(std::make_shared<AACFrameNoCacheAble>((char*)pucData,iLen,uiStamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline MP4SampleId MediaReader::getVideoSampleId(int iTimeInc ) {
|
inline MP4SampleId MediaReader::getVideoSampleId(int iTimeInc ) {
|
||||||
|
@ -132,6 +132,7 @@ private:
|
|||||||
Ticker _alive;
|
Ticker _alive;
|
||||||
recursive_mutex _mtx;
|
recursive_mutex _mtx;
|
||||||
Timer::Ptr _timer;
|
Timer::Ptr _timer;
|
||||||
|
EventPoller::Ptr _poller;
|
||||||
#endif //ENABLE_MP4V2
|
#endif //ENABLE_MP4V2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef ENABLE_MP4V2
|
#ifdef ENABLE_MP4V2
|
||||||
|
#include <ctime>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include "Common/config.h"
|
#include "Common/config.h"
|
||||||
#include "Mp4Maker.h"
|
#include "Mp4Maker.h"
|
||||||
|
@ -70,7 +70,12 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) {
|
|||||||
if(_frameCached.size() != 1){
|
if(_frameCached.size() != 1){
|
||||||
string merged;
|
string merged;
|
||||||
_frameCached.for_each([&](const Frame::Ptr &frame){
|
_frameCached.for_each([&](const Frame::Ptr &frame){
|
||||||
merged.append(frame->data(),frame->size());
|
if(frame->prefixSize()){
|
||||||
|
merged.append(frame->data(),frame->size());
|
||||||
|
} else{
|
||||||
|
merged.append("\x00\x00\x00\x01",4);
|
||||||
|
merged.append(frame->data(),frame->size());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
merged_frame = std::make_shared<BufferString>(std::move(merged));
|
merged_frame = std::make_shared<BufferString>(std::move(merged));
|
||||||
}
|
}
|
||||||
@ -78,7 +83,7 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) {
|
|||||||
mpeg_ts_write(_context, it->second, back->keyFrame() ? 0x0001 : 0, back->pts() * 90LL, back->dts() * 90LL, merged_frame->data(), merged_frame->size());
|
mpeg_ts_write(_context, it->second, back->keyFrame() ? 0x0001 : 0, back->pts() * 90LL, back->dts() * 90LL, merged_frame->data(), merged_frame->size());
|
||||||
_frameCached.clear();
|
_frameCached.clear();
|
||||||
}
|
}
|
||||||
_frameCached.emplace_back(frame);
|
_frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
|
@ -40,12 +40,23 @@ PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const st
|
|||||||
ptr->teardown();
|
ptr->teardown();
|
||||||
};
|
};
|
||||||
string prefix = FindField(strUrl.data(), NULL, "://");
|
string prefix = FindField(strUrl.data(), NULL, "://");
|
||||||
|
|
||||||
|
if (strcasecmp("rtsps",prefix.data()) == 0) {
|
||||||
|
return PlayerBase::Ptr(new TcpClientWithSSL<RtspPlayerImp>(poller),releasePlayer);
|
||||||
|
}
|
||||||
|
|
||||||
if (strcasecmp("rtsp",prefix.data()) == 0) {
|
if (strcasecmp("rtsp",prefix.data()) == 0) {
|
||||||
return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer);
|
return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcasecmp("rtmps",prefix.data()) == 0) {
|
||||||
|
return PlayerBase::Ptr(new TcpClientWithSSL<RtmpPlayerImp>(poller),releasePlayer);
|
||||||
|
}
|
||||||
|
|
||||||
if (strcasecmp("rtmp",prefix.data()) == 0) {
|
if (strcasecmp("rtmp",prefix.data()) == 0) {
|
||||||
return PlayerBase::Ptr(new RtmpPlayerImp(poller),releasePlayer);
|
return PlayerBase::Ptr(new RtmpPlayerImp(poller),releasePlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer);
|
return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ public:
|
|||||||
|
|
||||||
void setMediaSouce(const MediaSource::Ptr & src) override {
|
void setMediaSouce(const MediaSource::Ptr & src) override {
|
||||||
if (_parser) {
|
if (_parser) {
|
||||||
return _parser->setMediaSouce(src);
|
_parser->setMediaSouce(src);
|
||||||
}
|
}
|
||||||
_pMediaSrc = src;
|
_pMediaSrc = src;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,8 @@ static uint8_t s_mute_adts[] = {0xff, 0xf1, 0x6c, 0x40, 0x2d, 0x3f, 0xfc, 0x00,
|
|||||||
PlayerProxy::PlayerProxy(const string &strVhost,
|
PlayerProxy::PlayerProxy(const string &strVhost,
|
||||||
const string &strApp,
|
const string &strApp,
|
||||||
const string &strSrc,
|
const string &strSrc,
|
||||||
|
bool bEnableRtsp,
|
||||||
|
bool bEnableRtmp,
|
||||||
bool bEnableHls,
|
bool bEnableHls,
|
||||||
bool bEnableMp4,
|
bool bEnableMp4,
|
||||||
int iRetryCount,
|
int iRetryCount,
|
||||||
@ -72,6 +74,8 @@ PlayerProxy::PlayerProxy(const string &strVhost,
|
|||||||
_strVhost = strVhost;
|
_strVhost = strVhost;
|
||||||
_strApp = strApp;
|
_strApp = strApp;
|
||||||
_strSrc = strSrc;
|
_strSrc = strSrc;
|
||||||
|
_bEnableRtsp = bEnableRtsp;
|
||||||
|
_bEnableRtmp = bEnableRtmp;
|
||||||
_bEnableHls = bEnableHls;
|
_bEnableHls = bEnableHls;
|
||||||
_bEnableMp4 = bEnableMp4;
|
_bEnableMp4 = bEnableMp4;
|
||||||
_iRetryCount = iRetryCount;
|
_iRetryCount = iRetryCount;
|
||||||
@ -126,13 +130,30 @@ void PlayerProxy::play(const string &strUrlTmp) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
MediaPlayer::play(strUrlTmp);
|
MediaPlayer::play(strUrlTmp);
|
||||||
|
|
||||||
|
MediaSource::Ptr mediaSource;
|
||||||
|
if(dynamic_pointer_cast<RtspPlayer>(_parser)){
|
||||||
|
//rtsp拉流
|
||||||
|
GET_CONFIG(bool,directProxy,Rtsp::kDirectProxy);
|
||||||
|
if(directProxy && _bEnableRtsp){
|
||||||
|
mediaSource = std::make_shared<RtspMediaSource>(_strVhost,_strApp,_strSrc);
|
||||||
|
}
|
||||||
|
}else if(dynamic_pointer_cast<RtmpPlayer>(_parser)){
|
||||||
|
//rtmp拉流
|
||||||
|
if(_bEnableRtmp){
|
||||||
|
mediaSource = std::make_shared<RtmpMediaSource>(_strVhost,_strApp,_strSrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(mediaSource){
|
||||||
|
setMediaSouce(mediaSource);
|
||||||
|
mediaSource->setListener(shared_from_this());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerProxy::~PlayerProxy() {
|
PlayerProxy::~PlayerProxy() {
|
||||||
_timer.reset();
|
_timer.reset();
|
||||||
}
|
}
|
||||||
void PlayerProxy::rePlay(const string &strUrl,int iFailedCnt){
|
void PlayerProxy::rePlay(const string &strUrl,int iFailedCnt){
|
||||||
auto iTaskId = reinterpret_cast<uint64_t>(this);
|
|
||||||
auto iDelay = MAX(2 * 1000, MIN(iFailedCnt * 3000,60*1000));
|
auto iDelay = MAX(2 * 1000, MIN(iFailedCnt * 3000,60*1000));
|
||||||
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
|
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
|
||||||
_timer = std::make_shared<Timer>(iDelay / 1000.0f,[weakSelf,strUrl,iFailedCnt]() {
|
_timer = std::make_shared<Timer>(iDelay / 1000.0f,[weakSelf,strUrl,iFailedCnt]() {
|
||||||
@ -146,8 +167,13 @@ void PlayerProxy::rePlay(const string &strUrl,int iFailedCnt){
|
|||||||
return false;
|
return false;
|
||||||
}, getPoller());
|
}, getPoller());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PlayerProxy::readerCount(){
|
||||||
|
return (_mediaMuxer ? _mediaMuxer->readerCount() : 0) + (_pMediaSrc ? _pMediaSrc->readerCount() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
bool PlayerProxy::close(MediaSource &sender,bool force) {
|
bool PlayerProxy::close(MediaSource &sender,bool force) {
|
||||||
if(!_mediaMuxer || (!force && _mediaMuxer->readerCount() != 0)){
|
if(!force && readerCount() != 0){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +183,7 @@ bool PlayerProxy::close(MediaSource &sender,bool force) {
|
|||||||
auto stronSelf = weakSlef.lock();
|
auto stronSelf = weakSlef.lock();
|
||||||
if (stronSelf) {
|
if (stronSelf) {
|
||||||
stronSelf->_mediaMuxer.reset();
|
stronSelf->_mediaMuxer.reset();
|
||||||
|
stronSelf->setMediaSouce(nullptr);
|
||||||
stronSelf->teardown();
|
stronSelf->teardown();
|
||||||
if(stronSelf->_onClose){
|
if(stronSelf->_onClose){
|
||||||
stronSelf->_onClose();
|
stronSelf->_onClose();
|
||||||
@ -185,7 +212,7 @@ public:
|
|||||||
auto iAudioIndex = frame->stamp() / MUTE_ADTS_DATA_MS;
|
auto iAudioIndex = frame->stamp() / MUTE_ADTS_DATA_MS;
|
||||||
if(_iAudioIndex != iAudioIndex){
|
if(_iAudioIndex != iAudioIndex){
|
||||||
_iAudioIndex = iAudioIndex;
|
_iAudioIndex = iAudioIndex;
|
||||||
auto aacFrame = std::make_shared<AACFrameNoCopyAble>((char *)MUTE_ADTS_DATA,
|
auto aacFrame = std::make_shared<AACFrameNoCacheAble>((char *)MUTE_ADTS_DATA,
|
||||||
MUTE_ADTS_DATA_LEN,
|
MUTE_ADTS_DATA_LEN,
|
||||||
_iAudioIndex * MUTE_ADTS_DATA_MS);
|
_iAudioIndex * MUTE_ADTS_DATA_MS);
|
||||||
FrameRingInterfaceDelegate::inputFrame(aacFrame);
|
FrameRingInterfaceDelegate::inputFrame(aacFrame);
|
||||||
@ -197,7 +224,16 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void PlayerProxy::onPlaySuccess() {
|
void PlayerProxy::onPlaySuccess() {
|
||||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost,_strApp,_strSrc,getDuration(),_bEnableHls,_bEnableMp4));
|
if (dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc)) {
|
||||||
|
//rtsp拉流代理
|
||||||
|
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), false, _bEnableRtmp, _bEnableHls, _bEnableMp4));
|
||||||
|
} else if (dynamic_pointer_cast<RtmpMediaSource>(_pMediaSrc)) {
|
||||||
|
//rtmp拉流代理
|
||||||
|
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, false, _bEnableHls, _bEnableMp4));
|
||||||
|
} else {
|
||||||
|
//其他拉流代理
|
||||||
|
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, _bEnableRtmp, _bEnableHls, _bEnableMp4));
|
||||||
|
}
|
||||||
_mediaMuxer->setListener(shared_from_this());
|
_mediaMuxer->setListener(shared_from_this());
|
||||||
|
|
||||||
auto videoTrack = getTrack(TrackVideo,false);
|
auto videoTrack = getTrack(TrackVideo,false);
|
||||||
|
@ -49,6 +49,8 @@ public:
|
|||||||
PlayerProxy(const string &strVhost,
|
PlayerProxy(const string &strVhost,
|
||||||
const string &strApp,
|
const string &strApp,
|
||||||
const string &strSrc,
|
const string &strSrc,
|
||||||
|
bool bEnableRtsp = true,
|
||||||
|
bool bEnableRtmp = true,
|
||||||
bool bEnableHls = true,
|
bool bEnableHls = true,
|
||||||
bool bEnableMp4 = false,
|
bool bEnableMp4 = false,
|
||||||
int iRetryCount = -1,
|
int iRetryCount = -1,
|
||||||
@ -84,9 +86,12 @@ private:
|
|||||||
void onNoneReader(MediaSource &sender) override;
|
void onNoneReader(MediaSource &sender) override;
|
||||||
void rePlay(const string &strUrl,int iFailedCnt);
|
void rePlay(const string &strUrl,int iFailedCnt);
|
||||||
void onPlaySuccess();
|
void onPlaySuccess();
|
||||||
|
int readerCount() ;
|
||||||
private:
|
private:
|
||||||
bool _bEnableHls;
|
bool _bEnableRtsp;
|
||||||
bool _bEnableMp4;
|
bool _bEnableRtmp;
|
||||||
|
bool _bEnableHls;
|
||||||
|
bool _bEnableMp4;
|
||||||
int _iRetryCount;
|
int _iRetryCount;
|
||||||
MultiMediaSourceMuxer::Ptr _mediaMuxer;
|
MultiMediaSourceMuxer::Ptr _mediaMuxer;
|
||||||
string _strVhost;
|
string _strVhost;
|
||||||
|
@ -44,12 +44,23 @@ PusherBase::Ptr PusherBase::createPusher(const EventPoller::Ptr &poller,
|
|||||||
ptr->teardown();
|
ptr->teardown();
|
||||||
};
|
};
|
||||||
string prefix = FindField(strUrl.data(), NULL, "://");
|
string prefix = FindField(strUrl.data(), NULL, "://");
|
||||||
|
|
||||||
|
if (strcasecmp("rtsps",prefix.data()) == 0) {
|
||||||
|
return PusherBase::Ptr(new TcpClientWithSSL<RtspPusher>(poller,dynamic_pointer_cast<RtspMediaSource>(src)),releasePusher);
|
||||||
|
}
|
||||||
|
|
||||||
if (strcasecmp("rtsp",prefix.data()) == 0) {
|
if (strcasecmp("rtsp",prefix.data()) == 0) {
|
||||||
return PusherBase::Ptr(new RtspPusher(poller,dynamic_pointer_cast<RtspMediaSource>(src)),releasePusher);
|
return PusherBase::Ptr(new RtspPusher(poller,dynamic_pointer_cast<RtspMediaSource>(src)),releasePusher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcasecmp("rtmps",prefix.data()) == 0) {
|
||||||
|
return PusherBase::Ptr(new TcpClientWithSSL<RtmpPusher>(poller,dynamic_pointer_cast<RtmpMediaSource>(src)),releasePusher);
|
||||||
|
}
|
||||||
|
|
||||||
if (strcasecmp("rtmp",prefix.data()) == 0) {
|
if (strcasecmp("rtmp",prefix.data()) == 0) {
|
||||||
return PusherBase::Ptr(new RtmpPusher(poller,dynamic_pointer_cast<RtmpMediaSource>(src)),releasePusher);
|
return PusherBase::Ptr(new RtmpPusher(poller,dynamic_pointer_cast<RtmpMediaSource>(src)),releasePusher);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PusherBase::Ptr(new RtspPusher(poller,dynamic_pointer_cast<RtspMediaSource>(src)),releasePusher);
|
return PusherBase::Ptr(new RtspPusher(poller,dynamic_pointer_cast<RtspMediaSource>(src)),releasePusher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@
|
|||||||
#include "Rtmp.h"
|
#include "Rtmp.h"
|
||||||
#include "RtmpMediaSource.h"
|
#include "RtmpMediaSource.h"
|
||||||
#include "RtmpDemuxer.h"
|
#include "RtmpDemuxer.h"
|
||||||
#include "MediaFile/MediaRecorder.h"
|
#include "Common/MultiMediaSourceMuxer.h"
|
||||||
#include "Rtsp/RtspMediaSourceMuxer.h"
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace toolkit;
|
using namespace toolkit;
|
||||||
|
|
||||||
@ -54,49 +54,53 @@ public:
|
|||||||
const string &id,
|
const string &id,
|
||||||
bool bEnableHls = true,
|
bool bEnableHls = true,
|
||||||
bool bEnableMp4 = false,
|
bool bEnableMp4 = false,
|
||||||
int ringSize = 0):RtmpMediaSource(vhost, app, id,ringSize){
|
int ringSize = 0) : RtmpMediaSource(vhost, app, id,ringSize){
|
||||||
_recorder = std::make_shared<MediaRecorder>(vhost, app, id, bEnableHls, bEnableMp4);
|
_bEnableHls = bEnableHls;
|
||||||
_rtmpDemuxer = std::make_shared<RtmpDemuxer>();
|
_bEnableMp4 = bEnableMp4;
|
||||||
|
_demuxer = std::make_shared<RtmpDemuxer>();
|
||||||
}
|
}
|
||||||
virtual ~RtmpToRtspMediaSource(){}
|
virtual ~RtmpToRtspMediaSource(){}
|
||||||
|
|
||||||
void onGetMetaData(const AMFValue &metadata) override {
|
void onGetMetaData(const AMFValue &metadata) override {
|
||||||
_rtmpDemuxer = std::make_shared<RtmpDemuxer>(metadata);
|
_demuxer = std::make_shared<RtmpDemuxer>(metadata);
|
||||||
RtmpMediaSource::onGetMetaData(metadata);
|
RtmpMediaSource::onGetMetaData(metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos) override {
|
void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos) override {
|
||||||
_rtmpDemuxer->inputRtmp(pkt);
|
_demuxer->inputRtmp(pkt);
|
||||||
if(!_rtspMuxer && _rtmpDemuxer->isInited(2000)){
|
if(!_muxer && _demuxer->isInited(2000)){
|
||||||
_rtspMuxer = std::make_shared<RtspMediaSourceMuxer>(getVhost(),
|
_muxer = std::make_shared<MultiMediaSourceMuxer>(getVhost(),
|
||||||
getApp(),
|
getApp(),
|
||||||
getId(),
|
getId(),
|
||||||
std::make_shared<TitleSdp>(_rtmpDemuxer->getDuration()));
|
_demuxer->getDuration(),
|
||||||
for (auto &track : _rtmpDemuxer->getTracks(false)){
|
true,//转rtsp
|
||||||
_rtspMuxer->addTrack(track);
|
false,//不重复生成rtmp
|
||||||
_recorder->addTrack(track);
|
_bEnableHls,
|
||||||
track->addDelegate(_rtspMuxer);
|
_bEnableMp4);
|
||||||
track->addDelegate(_recorder);
|
for (auto &track : _demuxer->getTracks(false)){
|
||||||
|
_muxer->addTrack(track);
|
||||||
|
track->addDelegate(_muxer);
|
||||||
}
|
}
|
||||||
_rtspMuxer->setListener(_listener);
|
_muxer->setListener(_listener);
|
||||||
}
|
}
|
||||||
RtmpMediaSource::onWrite(pkt,key_pos);
|
RtmpMediaSource::onWrite(pkt,key_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override {
|
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override {
|
||||||
RtmpMediaSource::setListener(listener);
|
RtmpMediaSource::setListener(listener);
|
||||||
if(_rtspMuxer){
|
if(_muxer){
|
||||||
_rtspMuxer->setListener(listener);
|
_muxer->setListener(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int readerCount() override {
|
int readerCount() override {
|
||||||
return RtmpMediaSource::readerCount() + (_rtspMuxer ? _rtspMuxer->readerCount() : 0);
|
return RtmpMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
RtmpDemuxer::Ptr _rtmpDemuxer;
|
RtmpDemuxer::Ptr _demuxer;
|
||||||
RtspMediaSourceMuxer::Ptr _rtspMuxer;
|
MultiMediaSourceMuxer::Ptr _muxer;
|
||||||
MediaRecorder::Ptr _recorder;
|
bool _bEnableHls;
|
||||||
|
bool _bEnableMp4;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace mediakit */
|
} /* namespace mediakit */
|
||||||
|
@ -232,6 +232,31 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
|
|||||||
|
|
||||||
sendSetup(0);
|
sendSetup(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//有必要的情况下创建udp端口
|
||||||
|
void RtspPlayer::createUdpSockIfNecessary(int track_idx){
|
||||||
|
auto &rtpSockRef = _apRtpSock[track_idx];
|
||||||
|
auto &rtcpSockRef = _apRtcpSock[track_idx];
|
||||||
|
if(!rtpSockRef){
|
||||||
|
rtpSockRef.reset(new Socket(getPoller()));
|
||||||
|
//rtp随机端口
|
||||||
|
if (!rtpSockRef->bindUdpSock(0, get_local_ip().data())) {
|
||||||
|
rtpSockRef.reset();
|
||||||
|
throw std::runtime_error("open rtp sock failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!rtcpSockRef){
|
||||||
|
rtcpSockRef.reset(new Socket(getPoller()));
|
||||||
|
//rtcp端口为rtp端口+1,目的是为了兼容某些服务器,其实更推荐随机端口
|
||||||
|
if (!rtcpSockRef->bindUdpSock(rtpSockRef->get_local_port() + 1, get_local_ip().data())) {
|
||||||
|
rtcpSockRef.reset();
|
||||||
|
throw std::runtime_error("open rtcp sock failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//发送SETUP命令
|
//发送SETUP命令
|
||||||
void RtspPlayer::sendSetup(unsigned int trackIndex) {
|
void RtspPlayer::sendSetup(unsigned int trackIndex) {
|
||||||
_onHandshake = std::bind(&RtspPlayer::handleResSETUP,this, placeholders::_1,trackIndex);
|
_onHandshake = std::bind(&RtspPlayer::handleResSETUP,this, placeholders::_1,trackIndex);
|
||||||
@ -247,16 +272,7 @@ void RtspPlayer::sendSetup(unsigned int trackIndex) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Rtsp::RTP_UDP: {
|
case Rtsp::RTP_UDP: {
|
||||||
_apRtpSock[trackIndex].reset(new Socket(getPoller()));
|
createUdpSockIfNecessary(trackIndex);
|
||||||
if (!_apRtpSock[trackIndex]->bindUdpSock(0, get_local_ip().data())) {
|
|
||||||
_apRtpSock[trackIndex].reset();
|
|
||||||
throw std::runtime_error("open rtp sock err");
|
|
||||||
}
|
|
||||||
_apRtcpSock[trackIndex].reset(new Socket(getPoller()));
|
|
||||||
if (!_apRtcpSock[trackIndex]->bindUdpSock(_apRtpSock[trackIndex]->get_local_port() + 1, get_local_ip().data())) {
|
|
||||||
_apRtcpSock[trackIndex].reset();
|
|
||||||
throw std::runtime_error("open rtcp sock err");
|
|
||||||
}
|
|
||||||
sendRtspRequest("SETUP",baseUrl,{"Transport",
|
sendRtspRequest("SETUP",baseUrl,{"Transport",
|
||||||
StrPrinter << "RTP/AVP;unicast;client_port="
|
StrPrinter << "RTP/AVP;unicast;client_port="
|
||||||
<< _apRtpSock[trackIndex]->get_local_port() << "-"
|
<< _apRtpSock[trackIndex]->get_local_port() << "-"
|
||||||
@ -280,7 +296,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto strTransport = parser["Transport"];
|
auto strTransport = parser["Transport"];
|
||||||
if(strTransport.find("TCP") != string::npos){
|
if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){
|
||||||
_eType = Rtsp::RTP_TCP;
|
_eType = Rtsp::RTP_TCP;
|
||||||
}else if(strTransport.find("multicast") != string::npos){
|
}else if(strTransport.find("multicast") != string::npos){
|
||||||
_eType = Rtsp::RTP_MULTICAST;
|
_eType = Rtsp::RTP_MULTICAST;
|
||||||
@ -314,7 +330,8 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex)
|
|||||||
SockUtil::joinMultiAddr(fd, multiAddr.data(),get_local_ip().data());
|
SockUtil::joinMultiAddr(fd, multiAddr.data(),get_local_ip().data());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//udp单播
|
createUdpSockIfNecessary(uiTrackIndex);
|
||||||
|
//udp单播
|
||||||
struct sockaddr_in rtpto;
|
struct sockaddr_in rtpto;
|
||||||
rtpto.sin_port = ntohs(rtp_port);
|
rtpto.sin_port = ntohs(rtp_port);
|
||||||
rtpto.sin_family = AF_INET;
|
rtpto.sin_family = AF_INET;
|
||||||
|
@ -93,6 +93,11 @@ protected:
|
|||||||
* @param uiLen
|
* @param uiLen
|
||||||
*/
|
*/
|
||||||
virtual void onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen);
|
virtual void onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen);
|
||||||
|
|
||||||
|
/////////////TcpClient override/////////////
|
||||||
|
void onConnect(const SockException &err) override;
|
||||||
|
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||||
|
void onErr(const SockException &ex) override;
|
||||||
private:
|
private:
|
||||||
void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track);
|
void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track);
|
||||||
void onPlayResult_l(const SockException &ex);
|
void onPlayResult_l(const SockException &ex);
|
||||||
@ -102,10 +107,6 @@ private:
|
|||||||
int getTrackIndexByTrackType(TrackType trackType) const;
|
int getTrackIndexByTrackType(TrackType trackType) const;
|
||||||
|
|
||||||
void play(const string &strUrl, const string &strUser, const string &strPwd, Rtsp::eRtpType eType);
|
void play(const string &strUrl, const string &strUser, const string &strPwd, Rtsp::eRtpType eType);
|
||||||
void onConnect(const SockException &err) override;
|
|
||||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
|
||||||
void onErr(const SockException &ex) override;
|
|
||||||
|
|
||||||
void handleResSETUP(const Parser &parser, unsigned int uiTrackIndex);
|
void handleResSETUP(const Parser &parser, unsigned int uiTrackIndex);
|
||||||
void handleResDESCRIBE(const Parser &parser);
|
void handleResDESCRIBE(const Parser &parser);
|
||||||
bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr);
|
bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr);
|
||||||
@ -120,6 +121,7 @@ private:
|
|||||||
void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap());
|
void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap());
|
||||||
void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list<string> &header);
|
void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list<string> &header);
|
||||||
void sendReceiverReport(bool overTcp,int iTrackIndex);
|
void sendReceiverReport(bool overTcp,int iTrackIndex);
|
||||||
|
void createUdpSockIfNecessary(int track_idx);
|
||||||
private:
|
private:
|
||||||
string _strUrl;
|
string _strUrl;
|
||||||
SdpParser _sdpParser;
|
SdpParser _sdpParser;
|
||||||
|
@ -242,6 +242,19 @@ bool RtspPusher::handleAuthenticationFailure(const string ¶msStr) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//有必要的情况下创建udp端口
|
||||||
|
void RtspPusher::createUdpSockIfNecessary(int track_idx){
|
||||||
|
auto &rtpSockRef = _apUdpSock[track_idx];
|
||||||
|
if(!rtpSockRef){
|
||||||
|
rtpSockRef.reset(new Socket(getPoller()));
|
||||||
|
//rtp随机端口
|
||||||
|
if (!rtpSockRef->bindUdpSock(0, get_local_ip().data())) {
|
||||||
|
rtpSockRef.reset();
|
||||||
|
throw std::runtime_error("open rtp sock failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RtspPusher::sendSetup(unsigned int trackIndex) {
|
void RtspPusher::sendSetup(unsigned int trackIndex) {
|
||||||
_onHandshake = std::bind(&RtspPusher::handleResSetup,this, placeholders::_1,trackIndex);
|
_onHandshake = std::bind(&RtspPusher::handleResSetup,this, placeholders::_1,trackIndex);
|
||||||
auto &track = _aTrackInfo[trackIndex];
|
auto &track = _aTrackInfo[trackIndex];
|
||||||
@ -252,11 +265,7 @@ void RtspPusher::sendSetup(unsigned int trackIndex) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Rtsp::RTP_UDP: {
|
case Rtsp::RTP_UDP: {
|
||||||
_apUdpSock[trackIndex].reset(new Socket(getPoller()));
|
createUdpSockIfNecessary(trackIndex);
|
||||||
if (!_apUdpSock[trackIndex]->bindUdpSock(0, get_local_ip().data())) {
|
|
||||||
_apUdpSock[trackIndex].reset();
|
|
||||||
throw std::runtime_error("open udp sock err");
|
|
||||||
}
|
|
||||||
int port = _apUdpSock[trackIndex]->get_local_port();
|
int port = _apUdpSock[trackIndex]->get_local_port();
|
||||||
sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1});
|
sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1});
|
||||||
}
|
}
|
||||||
@ -266,6 +275,7 @@ void RtspPusher::sendSetup(unsigned int trackIndex) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex) {
|
void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex) {
|
||||||
if (parser.Url() != "200") {
|
if (parser.Url() != "200") {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
@ -278,7 +288,7 @@ void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto strTransport = parser["Transport"];
|
auto strTransport = parser["Transport"];
|
||||||
if(strTransport.find("TCP") != string::npos){
|
if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){
|
||||||
_eType = Rtsp::RTP_TCP;
|
_eType = Rtsp::RTP_TCP;
|
||||||
string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-");
|
string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-");
|
||||||
_aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data());
|
_aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data());
|
||||||
@ -286,19 +296,15 @@ void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex)
|
|||||||
throw std::runtime_error("SETUP rtsp pusher can not support multicast!");
|
throw std::runtime_error("SETUP rtsp pusher can not support multicast!");
|
||||||
}else{
|
}else{
|
||||||
_eType = Rtsp::RTP_UDP;
|
_eType = Rtsp::RTP_UDP;
|
||||||
|
createUdpSockIfNecessary(uiTrackIndex);
|
||||||
const char *strPos = "server_port=" ;
|
const char *strPos = "server_port=" ;
|
||||||
auto port_str = FindField((strTransport + ";").data(), strPos, ";");
|
auto port_str = FindField((strTransport + ";").data(), strPos, ";");
|
||||||
uint16_t port = atoi(FindField(port_str.data(), NULL, "-").data());
|
uint16_t port = atoi(FindField(port_str.data(), NULL, "-").data());
|
||||||
auto &pUdpSockRef = _apUdpSock[uiTrackIndex];
|
|
||||||
if(!pUdpSockRef){
|
|
||||||
pUdpSockRef.reset(new Socket(getPoller()));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in rtpto;
|
struct sockaddr_in rtpto;
|
||||||
rtpto.sin_port = ntohs(port);
|
rtpto.sin_port = ntohs(port);
|
||||||
rtpto.sin_family = AF_INET;
|
rtpto.sin_family = AF_INET;
|
||||||
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
||||||
pUdpSockRef->setSendPeerAddr((struct sockaddr *)&(rtpto));
|
_apUdpSock[uiTrackIndex]->setSendPeerAddr((struct sockaddr *)&(rtpto));
|
||||||
}
|
}
|
||||||
|
|
||||||
RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP);
|
RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP);
|
||||||
|
@ -65,6 +65,8 @@ private:
|
|||||||
void sendRtpPacket(const RtpPacket::Ptr & pkt) ;
|
void sendRtpPacket(const RtpPacket::Ptr & pkt) ;
|
||||||
void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap(),const string &sdp = "" );
|
void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap(),const string &sdp = "" );
|
||||||
void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list<string> &header,const string &sdp = "");
|
void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list<string> &header,const string &sdp = "");
|
||||||
|
|
||||||
|
void createUdpSockIfNecessary(int track_idx);
|
||||||
private:
|
private:
|
||||||
//rtsp鉴权相关
|
//rtsp鉴权相关
|
||||||
string _rtspMd5Nonce;
|
string _rtspMd5Nonce;
|
||||||
|
@ -29,10 +29,8 @@
|
|||||||
|
|
||||||
#include "Rtmp/amf.h"
|
#include "Rtmp/amf.h"
|
||||||
#include "RtspMediaSource.h"
|
#include "RtspMediaSource.h"
|
||||||
#include "MediaFile/MediaRecorder.h"
|
|
||||||
#include "Rtmp/RtmpMediaSource.h"
|
|
||||||
#include "RtspDemuxer.h"
|
#include "RtspDemuxer.h"
|
||||||
#include "Rtmp/RtmpMediaSourceMuxer.h"
|
#include "Common/MultiMediaSourceMuxer.h"
|
||||||
|
|
||||||
using namespace toolkit;
|
using namespace toolkit;
|
||||||
|
|
||||||
@ -48,31 +46,34 @@ public:
|
|||||||
bool bEnableHls = true,
|
bool bEnableHls = true,
|
||||||
bool bEnableMp4 = false,
|
bool bEnableMp4 = false,
|
||||||
int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) {
|
int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) {
|
||||||
_recorder = std::make_shared<MediaRecorder>(vhost, app, id, bEnableHls, bEnableMp4);
|
_bEnableHls = bEnableHls;
|
||||||
|
_bEnableMp4 = bEnableMp4;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~RtspToRtmpMediaSource() {}
|
virtual ~RtspToRtmpMediaSource() {}
|
||||||
|
|
||||||
virtual void onGetSDP(const string &strSdp) override {
|
virtual void onGetSDP(const string &strSdp) override {
|
||||||
_rtspDemuxer = std::make_shared<RtspDemuxer>(strSdp);
|
_demuxer = std::make_shared<RtspDemuxer>(strSdp);
|
||||||
RtspMediaSource::onGetSDP(strSdp);
|
RtspMediaSource::onGetSDP(strSdp);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void onWrite(const RtpPacket::Ptr &rtp, bool bKeyPos) override {
|
virtual void onWrite(const RtpPacket::Ptr &rtp, bool bKeyPos) override {
|
||||||
if (_rtspDemuxer) {
|
if (_demuxer) {
|
||||||
bKeyPos = _rtspDemuxer->inputRtp(rtp);
|
bKeyPos = _demuxer->inputRtp(rtp);
|
||||||
if (!_rtmpMuxer && _rtspDemuxer->isInited(2000)) {
|
if (!_muxer && _demuxer->isInited(2000)) {
|
||||||
_rtmpMuxer = std::make_shared<RtmpMediaSourceMuxer>(getVhost(),
|
_muxer = std::make_shared<MultiMediaSourceMuxer>(getVhost(),
|
||||||
getApp(),
|
getApp(),
|
||||||
getId(),
|
getId(),
|
||||||
std::make_shared<TitleMete>(_rtspDemuxer->getDuration()));
|
_demuxer->getDuration(),
|
||||||
for (auto &track : _rtspDemuxer->getTracks(false)) {
|
false,//不重复生成rtsp
|
||||||
_rtmpMuxer->addTrack(track);
|
true,//转rtmp
|
||||||
_recorder->addTrack(track);
|
_bEnableHls,
|
||||||
track->addDelegate(_rtmpMuxer);
|
_bEnableMp4);
|
||||||
track->addDelegate(_recorder);
|
for (auto &track : _demuxer->getTracks(false)) {
|
||||||
|
_muxer->addTrack(track);
|
||||||
|
track->addDelegate(_muxer);
|
||||||
}
|
}
|
||||||
_rtmpMuxer->setListener(_listener);
|
_muxer->setListener(_listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RtspMediaSource::onWrite(rtp, bKeyPos);
|
RtspMediaSource::onWrite(rtp, bKeyPos);
|
||||||
@ -80,17 +81,18 @@ public:
|
|||||||
|
|
||||||
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override {
|
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override {
|
||||||
RtspMediaSource::setListener(listener);
|
RtspMediaSource::setListener(listener);
|
||||||
if(_rtmpMuxer){
|
if(_muxer){
|
||||||
_rtmpMuxer->setListener(listener);
|
_muxer->setListener(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int readerCount() override {
|
int readerCount() override {
|
||||||
return RtspMediaSource::readerCount() + (_rtmpMuxer ? _rtmpMuxer->readerCount() : 0);
|
return RtspMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
RtspDemuxer::Ptr _rtspDemuxer;
|
RtspDemuxer::Ptr _demuxer;
|
||||||
RtmpMediaSourceMuxer::Ptr _rtmpMuxer;
|
MultiMediaSourceMuxer::Ptr _muxer;
|
||||||
MediaRecorder::Ptr _recorder;
|
bool _bEnableHls;
|
||||||
|
bool _bEnableMp4;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace mediakit */
|
} /* namespace mediakit */
|
||||||
|
@ -56,49 +56,50 @@ onceToken token1([](){
|
|||||||
}//namespace Http
|
}//namespace Http
|
||||||
} // namespace mediakit
|
} // namespace mediakit
|
||||||
|
|
||||||
|
void initEventListener(){
|
||||||
|
static onceToken s_token([](){
|
||||||
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastHttpRequest,[](BroadcastHttpRequestArgs){
|
||||||
|
//const Parser &parser,HttpSession::HttpResponseInvoker &invoker,bool &consumed
|
||||||
|
if(strstr(parser.Url().data(),"/api/") != parser.Url().data()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//url以"/api/起始,说明是http api"
|
||||||
|
consumed = true;//该http请求已被消费
|
||||||
|
|
||||||
static onceToken s_token([](){
|
_StrPrinter printer;
|
||||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastHttpRequest,[](BroadcastHttpRequestArgs){
|
////////////////method////////////////////
|
||||||
//const Parser &parser,HttpSession::HttpResponseInvoker &invoker,bool &consumed
|
printer << "\r\nmethod:\r\n\t" << parser.Method();
|
||||||
if(strstr(parser.Url().data(),"/api/") != parser.Url().data()){
|
////////////////url/////////////////
|
||||||
return;
|
printer << "\r\nurl:\r\n\t" << parser.Url();
|
||||||
}
|
////////////////protocol/////////////////
|
||||||
//url以"/api/起始,说明是http api"
|
printer << "\r\nprotocol:\r\n\t" << parser.Tail();
|
||||||
consumed = true;//该http请求已被消费
|
///////////////args//////////////////
|
||||||
|
printer << "\r\nargs:\r\n";
|
||||||
|
for(auto &pr : parser.getUrlArgs()){
|
||||||
|
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
||||||
|
}
|
||||||
|
///////////////header//////////////////
|
||||||
|
printer << "\r\nheader:\r\n";
|
||||||
|
for(auto &pr : parser.getValues()){
|
||||||
|
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
||||||
|
}
|
||||||
|
////////////////content/////////////////
|
||||||
|
printer << "\r\ncontent:\r\n" << parser.Content();
|
||||||
|
auto contentOut = printer << endl;
|
||||||
|
|
||||||
_StrPrinter printer;
|
////////////////我们测算异步回复,当然你也可以同步回复/////////////////
|
||||||
////////////////method////////////////////
|
EventPollerPool::Instance().getPoller()->async([invoker,contentOut](){
|
||||||
printer << "\r\nmethod:\r\n\t" << parser.Method();
|
HttpSession::KeyValue headerOut;
|
||||||
////////////////url/////////////////
|
//你可以自定义header,如果跟默认header重名,则会覆盖之
|
||||||
printer << "\r\nurl:\r\n\t" << parser.Url();
|
//默认header有:Server,Connection,Date,Content-Type,Content-Length
|
||||||
////////////////protocol/////////////////
|
//请勿覆盖Connection、Content-Length键
|
||||||
printer << "\r\nprotocol:\r\n\t" << parser.Tail();
|
//键名覆盖时不区分大小写
|
||||||
///////////////args//////////////////
|
headerOut["TestHeader"] = "HeaderValue";
|
||||||
printer << "\r\nargs:\r\n";
|
invoker("200 OK",headerOut,contentOut);
|
||||||
for(auto &pr : parser.getUrlArgs()){
|
});
|
||||||
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
});
|
||||||
}
|
}, nullptr);
|
||||||
///////////////header//////////////////
|
}
|
||||||
printer << "\r\nheader:\r\n";
|
|
||||||
for(auto &pr : parser.getValues()){
|
|
||||||
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
|
||||||
}
|
|
||||||
////////////////content/////////////////
|
|
||||||
printer << "\r\ncontent:\r\n" << parser.Content();
|
|
||||||
auto contentOut = printer << endl;
|
|
||||||
|
|
||||||
////////////////我们测算异步回复,当然你也可以同步回复/////////////////
|
|
||||||
EventPollerPool::Instance().getPoller()->async([invoker,contentOut](){
|
|
||||||
HttpSession::KeyValue headerOut;
|
|
||||||
//你可以自定义header,如果跟默认header重名,则会覆盖之
|
|
||||||
//默认header有:Server,Connection,Date,Content-Type,Content-Length
|
|
||||||
//请勿覆盖Connection、Content-Length键
|
|
||||||
//键名覆盖时不区分大小写
|
|
||||||
headerOut["TestHeader"] = "HeaderValue";
|
|
||||||
invoker("200 OK",headerOut,contentOut);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, nullptr);
|
|
||||||
|
|
||||||
int main(int argc,char *argv[]){
|
int main(int argc,char *argv[]){
|
||||||
//设置退出信号处理函数
|
//设置退出信号处理函数
|
||||||
@ -111,6 +112,7 @@ int main(int argc,char *argv[]){
|
|||||||
|
|
||||||
//加载配置文件,如果配置文件不存在就创建一个
|
//加载配置文件,如果配置文件不存在就创建一个
|
||||||
loadIniConfig();
|
loadIniConfig();
|
||||||
|
initEventListener();
|
||||||
|
|
||||||
//加载证书,证书包含公钥和私钥
|
//加载证书,证书包含公钥和私钥
|
||||||
SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data());
|
SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data());
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include "Util/util.h"
|
||||||
#include "Util/logger.h"
|
#include "Util/logger.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "Poller/EventPoller.h"
|
#include "Poller/EventPoller.h"
|
||||||
@ -68,22 +69,27 @@ int main(int argc, char *argv[]) {
|
|||||||
WarnL << "没有视频或者视频不是264编码!";
|
WarnL << "没有视频或者视频不是264编码!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SDLDisplayerHelper::Instance().doTask([viedoTrack]() {
|
|
||||||
std::shared_ptr<H264Decoder> decoder(new H264Decoder);
|
AnyStorage::Ptr storage(new AnyStorage);
|
||||||
std::shared_ptr<YuvDisplayer> displayer(new YuvDisplayer);
|
viedoTrack->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([storage](const Frame::Ptr &frame) {
|
||||||
viedoTrack->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([decoder, displayer](const Frame::Ptr &frame) {
|
SDLDisplayerHelper::Instance().doTask([frame,storage]() {
|
||||||
SDLDisplayerHelper::Instance().doTask([decoder, displayer, frame]() {
|
auto &decoder = (*storage)["decoder"];
|
||||||
AVFrame *pFrame = nullptr;
|
auto &displayer = (*storage)["displayer"];
|
||||||
bool flag = decoder->inputVideo((unsigned char *) frame->data(), frame->size(),
|
if(!decoder){
|
||||||
frame->stamp(), &pFrame);
|
decoder.set<H264Decoder>();
|
||||||
if (flag) {
|
}
|
||||||
displayer->displayYUV(pFrame);
|
if(!displayer){
|
||||||
}
|
displayer.set<YuvDisplayer>();
|
||||||
return true;
|
}
|
||||||
});
|
|
||||||
}));
|
AVFrame *pFrame = nullptr;
|
||||||
return true;
|
bool flag = decoder.get<H264Decoder>().inputVideo((unsigned char *) frame->data(), frame->size(), frame->stamp(), &pFrame);
|
||||||
});
|
if (flag) {
|
||||||
|
displayer.get<YuvDisplayer>().displayYUV(pFrame);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ int domain(const string &playUrl, const string &pushUrl) {
|
|||||||
|
|
||||||
//拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream"
|
//拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream"
|
||||||
//你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请查看test_rtmpPusherMp4.cpp代码)
|
//你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请查看test_rtmpPusherMp4.cpp代码)
|
||||||
PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "app", "stream",false,false,-1 , poller));
|
PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "app", "stream",true,true,false,false,-1 , poller));
|
||||||
//可以指定rtsp拉流方式,支持tcp和udp方式,默认tcp
|
//可以指定rtsp拉流方式,支持tcp和udp方式,默认tcp
|
||||||
// (*player)[Client::kRtpType] = Rtsp::RTP_UDP;
|
// (*player)[Client::kRtpType] = Rtsp::RTP_UDP;
|
||||||
player->play(playUrl.data());
|
player->play(playUrl.data());
|
||||||
|
Loading…
Reference in New Issue
Block a user