Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
ZLMediaKit
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
张翔宇
ZLMediaKit
Commits
50927548
Commit
50927548
authored
Nov 27, 2020
by
hewenyuan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
增加多路RTP视频流输出
parent
f7433b0f
隐藏空白字符变更
内嵌
并排
正在显示
9 个修改的文件
包含
3454 行增加
和
3431 行删除
+3454
-3431
server/WebApi.cpp
+1175
-1174
src/Common/MediaSink.cpp
+170
-170
src/Common/MediaSource.cpp
+718
-717
src/Common/MediaSource.h
+361
-360
src/Common/MultiMediaSourceMuxer.cpp
+480
-472
src/Common/MultiMediaSourceMuxer.h
+197
-197
src/Rtp/RtpSender.cpp
+165
-163
src/Rtp/RtpSender.h
+86
-85
src/Rtp/RtpServer.cpp
+102
-93
没有找到文件。
server/WebApi.cpp
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#include <sys/stat.h>
#include <sys/stat.h>
#include <math.h>
#include <math.h>
#include <signal.h>
#include <signal.h>
#include <functional>
#include <functional>
#include <sstream>
#include <sstream>
#include <unordered_map>
#include <unordered_map>
#include "jsoncpp/json.h"
#include "jsoncpp/json.h"
#include "Util/util.h"
#include "Util/util.h"
#include "Util/logger.h"
#include "Util/logger.h"
#include "Util/onceToken.h"
#include "Util/onceToken.h"
#include "Util/NoticeCenter.h"
#include "Util/NoticeCenter.h"
#ifdef ENABLE_MYSQL
#ifdef ENABLE_MYSQL
#include "Util/SqlPool.h"
#include "Util/SqlPool.h"
#endif //ENABLE_MYSQL
#endif //ENABLE_MYSQL
#include "Common/config.h"
#include "Common/config.h"
#include "Common/MediaSource.h"
#include "Common/MediaSource.h"
#include "Http/HttpRequester.h"
#include "Http/HttpRequester.h"
#include "Http/HttpSession.h"
#include "Http/HttpSession.h"
#include "Network/TcpServer.h"
#include "Network/TcpServer.h"
#include "Player/PlayerProxy.h"
#include "Player/PlayerProxy.h"
#include "Util/MD5.h"
#include "Util/MD5.h"
#include "WebApi.h"
#include "WebApi.h"
#include "WebHook.h"
#include "WebHook.h"
#include "Thread/WorkThreadPool.h"
#include "Thread/WorkThreadPool.h"
#include "Rtp/RtpSelector.h"
#include "Rtp/RtpSelector.h"
#include "FFmpegSource.h"
#include "FFmpegSource.h"
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
#include "Rtp/RtpServer.h"
#include "Rtp/RtpServer.h"
#endif
#endif
using
namespace
Json
;
using
namespace
Json
;
using
namespace
toolkit
;
using
namespace
toolkit
;
using
namespace
mediakit
;
using
namespace
mediakit
;
namespace
API
{
namespace
API
{
typedef
enum
{
typedef
enum
{
Exception
=
-
400
,
//代码抛异常
Exception
=
-
400
,
//代码抛异常
InvalidArgs
=
-
300
,
//参数不合法
InvalidArgs
=
-
300
,
//参数不合法
SqlFailed
=
-
200
,
//sql执行失败
SqlFailed
=
-
200
,
//sql执行失败
AuthFailed
=
-
100
,
//鉴权失败
AuthFailed
=
-
100
,
//鉴权失败
OtherFailed
=
-
1
,
//业务代码执行失败,
OtherFailed
=
-
1
,
//业务代码执行失败,
Success
=
0
//执行成功
Success
=
0
//执行成功
}
ApiErr
;
}
ApiErr
;
#define API_FIELD "api."
#define API_FIELD "api."
const
string
kApiDebug
=
API_FIELD
"apiDebug"
;
const
string
kApiDebug
=
API_FIELD
"apiDebug"
;
const
string
kSecret
=
API_FIELD
"secret"
;
const
string
kSecret
=
API_FIELD
"secret"
;
const
string
kSnapRoot
=
API_FIELD
"snapRoot"
;
const
string
kSnapRoot
=
API_FIELD
"snapRoot"
;
const
string
kDefaultSnap
=
API_FIELD
"defaultSnap"
;
const
string
kDefaultSnap
=
API_FIELD
"defaultSnap"
;
static
onceToken
token
([]()
{
static
onceToken
token
([]()
{
mINI
::
Instance
()[
kApiDebug
]
=
"1"
;
mINI
::
Instance
()[
kApiDebug
]
=
"1"
;
mINI
::
Instance
()[
kSecret
]
=
"035c73f7-bb6b-4889-a715-d9eb2d1925cc"
;
mINI
::
Instance
()[
kSecret
]
=
"035c73f7-bb6b-4889-a715-d9eb2d1925cc"
;
mINI
::
Instance
()[
kSnapRoot
]
=
"./www/snap/"
;
mINI
::
Instance
()[
kSnapRoot
]
=
"./www/snap/"
;
mINI
::
Instance
()[
kDefaultSnap
]
=
"./www/logo.png"
;
mINI
::
Instance
()[
kDefaultSnap
]
=
"./www/logo.png"
;
});
});
}
//namespace API
}
//namespace API
class
ApiRetException
:
public
std
::
runtime_error
{
class
ApiRetException
:
public
std
::
runtime_error
{
public
:
public
:
ApiRetException
(
const
char
*
str
=
"success"
,
int
code
=
API
::
Success
)
:
runtime_error
(
str
){
ApiRetException
(
const
char
*
str
=
"success"
,
int
code
=
API
::
Success
)
:
runtime_error
(
str
){
_code
=
code
;
_code
=
code
;
}
}
~
ApiRetException
()
=
default
;
~
ApiRetException
()
=
default
;
int
code
(){
return
_code
;
}
int
code
(){
return
_code
;
}
private
:
private
:
int
_code
;
int
_code
;
};
};
class
AuthException
:
public
ApiRetException
{
class
AuthException
:
public
ApiRetException
{
public
:
public
:
AuthException
(
const
char
*
str
)
:
ApiRetException
(
str
,
API
::
AuthFailed
){}
AuthException
(
const
char
*
str
)
:
ApiRetException
(
str
,
API
::
AuthFailed
){}
~
AuthException
()
=
default
;
~
AuthException
()
=
default
;
};
};
class
InvalidArgsException
:
public
ApiRetException
{
class
InvalidArgsException
:
public
ApiRetException
{
public
:
public
:
InvalidArgsException
(
const
char
*
str
)
:
ApiRetException
(
str
,
API
::
InvalidArgs
){}
InvalidArgsException
(
const
char
*
str
)
:
ApiRetException
(
str
,
API
::
InvalidArgs
){}
~
InvalidArgsException
()
=
default
;
~
InvalidArgsException
()
=
default
;
};
};
class
SuccessException
:
public
ApiRetException
{
class
SuccessException
:
public
ApiRetException
{
public
:
public
:
SuccessException
()
:
ApiRetException
(
"success"
,
API
::
Success
){}
SuccessException
()
:
ApiRetException
(
"success"
,
API
::
Success
){}
~
SuccessException
()
=
default
;
~
SuccessException
()
=
default
;
};
};
#define API_ARGS1 SockInfo &sender,HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, ApiArgsType &allArgs, Json::Value &val
#define API_ARGS1 SockInfo &sender,HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, ApiArgsType &allArgs, Json::Value &val
#define API_ARGS2 API_ARGS1, const HttpSession::HttpResponseInvoker &invoker
#define API_ARGS2 API_ARGS1, const HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_VALUE1 sender,headerIn,headerOut,allArgs,val
#define API_ARGS_VALUE1 sender,headerIn,headerOut,allArgs,val
#define API_ARGS_VALUE2 API_ARGS_VALUE1, invoker
#define API_ARGS_VALUE2 API_ARGS_VALUE1, invoker
typedef
map
<
string
,
variant
,
StrCaseCompare
>
ApiArgsType
;
typedef
map
<
string
,
variant
,
StrCaseCompare
>
ApiArgsType
;
//http api列表
//http api列表
static
map
<
string
,
std
::
function
<
void
(
API_ARGS2
)
>
>
s_map_api
;
static
map
<
string
,
std
::
function
<
void
(
API_ARGS2
)
>
>
s_map_api
;
template
<
typename
FUNC
>
template
<
typename
FUNC
>
static
void
api_regist1
(
const
string
&
api_path
,
FUNC
&&
func
)
{
static
void
api_regist1
(
const
string
&
api_path
,
FUNC
&&
func
)
{
s_map_api
.
emplace
(
api_path
,
[
func
](
API_ARGS2
)
{
s_map_api
.
emplace
(
api_path
,
[
func
](
API_ARGS2
)
{
func
(
API_ARGS_VALUE1
);
func
(
API_ARGS_VALUE1
);
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
});
});
}
}
template
<
typename
FUNC
>
template
<
typename
FUNC
>
static
void
api_regist2
(
const
string
&
api_path
,
FUNC
&&
func
)
{
static
void
api_regist2
(
const
string
&
api_path
,
FUNC
&&
func
)
{
s_map_api
.
emplace
(
api_path
,
std
::
forward
<
FUNC
>
(
func
));
s_map_api
.
emplace
(
api_path
,
std
::
forward
<
FUNC
>
(
func
));
}
}
//获取HTTP请求中url参数、content参数
//获取HTTP请求中url参数、content参数
static
ApiArgsType
getAllArgs
(
const
Parser
&
parser
)
{
static
ApiArgsType
getAllArgs
(
const
Parser
&
parser
)
{
ApiArgsType
allArgs
;
ApiArgsType
allArgs
;
if
(
parser
[
"Content-Type"
].
find
(
"application/x-www-form-urlencoded"
)
==
0
)
{
if
(
parser
[
"Content-Type"
].
find
(
"application/x-www-form-urlencoded"
)
==
0
)
{
auto
contentArgs
=
parser
.
parseArgs
(
parser
.
Content
());
auto
contentArgs
=
parser
.
parseArgs
(
parser
.
Content
());
for
(
auto
&
pr
:
contentArgs
)
{
for
(
auto
&
pr
:
contentArgs
)
{
allArgs
[
pr
.
first
]
=
HttpSession
::
urlDecode
(
pr
.
second
);
allArgs
[
pr
.
first
]
=
HttpSession
::
urlDecode
(
pr
.
second
);
}
}
}
else
if
(
parser
[
"Content-Type"
].
find
(
"application/json"
)
==
0
)
{
}
else
if
(
parser
[
"Content-Type"
].
find
(
"application/json"
)
==
0
)
{
try
{
try
{
stringstream
ss
(
parser
.
Content
());
stringstream
ss
(
parser
.
Content
());
Value
jsonArgs
;
Value
jsonArgs
;
ss
>>
jsonArgs
;
ss
>>
jsonArgs
;
auto
keys
=
jsonArgs
.
getMemberNames
();
auto
keys
=
jsonArgs
.
getMemberNames
();
for
(
auto
key
=
keys
.
begin
();
key
!=
keys
.
end
();
++
key
)
{
for
(
auto
key
=
keys
.
begin
();
key
!=
keys
.
end
();
++
key
)
{
allArgs
[
*
key
]
=
jsonArgs
[
*
key
].
asString
();
allArgs
[
*
key
]
=
jsonArgs
[
*
key
].
asString
();
}
}
}
catch
(
std
::
exception
&
ex
)
{
}
catch
(
std
::
exception
&
ex
)
{
WarnL
<<
ex
.
what
();
WarnL
<<
ex
.
what
();
}
}
}
else
if
(
!
parser
[
"Content-Type"
].
empty
())
{
}
else
if
(
!
parser
[
"Content-Type"
].
empty
())
{
WarnL
<<
"invalid Content-Type:"
<<
parser
[
"Content-Type"
];
WarnL
<<
"invalid Content-Type:"
<<
parser
[
"Content-Type"
];
}
}
for
(
auto
&
pr
:
parser
.
getUrlArgs
())
{
for
(
auto
&
pr
:
parser
.
getUrlArgs
())
{
allArgs
[
pr
.
first
]
=
pr
.
second
;
allArgs
[
pr
.
first
]
=
pr
.
second
;
}
}
return
allArgs
;
return
allArgs
;
}
}
static
inline
void
addHttpListener
(){
static
inline
void
addHttpListener
(){
GET_CONFIG
(
bool
,
api_debug
,
API
::
kApiDebug
);
GET_CONFIG
(
bool
,
api_debug
,
API
::
kApiDebug
);
//注册监听kBroadcastHttpRequest事件
//注册监听kBroadcastHttpRequest事件
NoticeCenter
::
Instance
().
addListener
(
nullptr
,
Broadcast
::
kBroadcastHttpRequest
,
[](
BroadcastHttpRequestArgs
)
{
NoticeCenter
::
Instance
().
addListener
(
nullptr
,
Broadcast
::
kBroadcastHttpRequest
,
[](
BroadcastHttpRequestArgs
)
{
auto
it
=
s_map_api
.
find
(
parser
.
Url
());
auto
it
=
s_map_api
.
find
(
parser
.
Url
());
if
(
it
==
s_map_api
.
end
())
{
if
(
it
==
s_map_api
.
end
())
{
return
;
return
;
}
}
//该api已被消费
//该api已被消费
consumed
=
true
;
consumed
=
true
;
//执行API
//执行API
Json
::
Value
val
;
Json
::
Value
val
;
val
[
"code"
]
=
API
::
Success
;
val
[
"code"
]
=
API
::
Success
;
HttpSession
::
KeyValue
headerOut
;
HttpSession
::
KeyValue
headerOut
;
auto
allArgs
=
getAllArgs
(
parser
);
auto
allArgs
=
getAllArgs
(
parser
);
HttpSession
::
KeyValue
&
headerIn
=
parser
.
getHeader
();
HttpSession
::
KeyValue
&
headerIn
=
parser
.
getHeader
();
GET_CONFIG
(
string
,
charSet
,
Http
::
kCharSet
);
GET_CONFIG
(
string
,
charSet
,
Http
::
kCharSet
);
headerOut
[
"Content-Type"
]
=
StrPrinter
<<
"application/json; charset="
<<
charSet
;
headerOut
[
"Content-Type"
]
=
StrPrinter
<<
"application/json; charset="
<<
charSet
;
if
(
api_debug
){
if
(
api_debug
){
auto
newInvoker
=
[
invoker
,
parser
,
allArgs
](
const
string
&
codeOut
,
auto
newInvoker
=
[
invoker
,
parser
,
allArgs
](
const
string
&
codeOut
,
const
HttpSession
::
KeyValue
&
headerOut
,
const
HttpSession
::
KeyValue
&
headerOut
,
const
HttpBody
::
Ptr
&
body
){
const
HttpBody
::
Ptr
&
body
){
stringstream
ss
;
stringstream
ss
;
for
(
auto
&
pr
:
allArgs
){
for
(
auto
&
pr
:
allArgs
){
ss
<<
pr
.
first
<<
" : "
<<
pr
.
second
<<
"
\r\n
"
;
ss
<<
pr
.
first
<<
" : "
<<
pr
.
second
<<
"
\r\n
"
;
}
}
//body默认为空
//body默认为空
int64_t
size
=
0
;
int64_t
size
=
0
;
if
(
body
&&
body
->
remainSize
())
{
if
(
body
&&
body
->
remainSize
())
{
//有body,获取body大小
//有body,获取body大小
size
=
body
->
remainSize
();
size
=
body
->
remainSize
();
}
}
if
(
size
&&
size
<
4
*
1024
){
if
(
size
&&
size
<
4
*
1024
){
string
contentOut
=
body
->
readData
(
size
)
->
toString
();
string
contentOut
=
body
->
readData
(
size
)
->
toString
();
DebugL
<<
"
\r\n
# request:
\r\n
"
<<
parser
.
Method
()
<<
" "
<<
parser
.
FullUrl
()
<<
"
\r\n
"
DebugL
<<
"
\r\n
# request:
\r\n
"
<<
parser
.
Method
()
<<
" "
<<
parser
.
FullUrl
()
<<
"
\r\n
"
<<
"# content:
\r\n
"
<<
parser
.
Content
()
<<
"
\r\n
"
<<
"# content:
\r\n
"
<<
parser
.
Content
()
<<
"
\r\n
"
<<
"# args:
\r\n
"
<<
ss
.
str
()
<<
"# args:
\r\n
"
<<
ss
.
str
()
<<
"# response:
\r\n
"
<<
"# response:
\r\n
"
<<
contentOut
<<
"
\r\n
"
;
<<
contentOut
<<
"
\r\n
"
;
invoker
(
codeOut
,
headerOut
,
contentOut
);
invoker
(
codeOut
,
headerOut
,
contentOut
);
}
else
{
}
else
{
DebugL
<<
"
\r\n
# request:
\r\n
"
<<
parser
.
Method
()
<<
" "
<<
parser
.
FullUrl
()
<<
"
\r\n
"
DebugL
<<
"
\r\n
# request:
\r\n
"
<<
parser
.
Method
()
<<
" "
<<
parser
.
FullUrl
()
<<
"
\r\n
"
<<
"# content:
\r\n
"
<<
parser
.
Content
()
<<
"
\r\n
"
<<
"# content:
\r\n
"
<<
parser
.
Content
()
<<
"
\r\n
"
<<
"# args:
\r\n
"
<<
ss
.
str
()
<<
"# args:
\r\n
"
<<
ss
.
str
()
<<
"# response size:"
<<
"# response size:"
<<
size
<<
"
\r\n
"
;
<<
size
<<
"
\r\n
"
;
invoker
(
codeOut
,
headerOut
,
body
);
invoker
(
codeOut
,
headerOut
,
body
);
}
}
};
};
((
HttpSession
::
HttpResponseInvoker
&
)
invoker
)
=
newInvoker
;
((
HttpSession
::
HttpResponseInvoker
&
)
invoker
)
=
newInvoker
;
}
}
try
{
try
{
it
->
second
(
sender
,
headerIn
,
headerOut
,
allArgs
,
val
,
invoker
);
it
->
second
(
sender
,
headerIn
,
headerOut
,
allArgs
,
val
,
invoker
);
}
catch
(
ApiRetException
&
ex
){
}
catch
(
ApiRetException
&
ex
){
val
[
"code"
]
=
ex
.
code
();
val
[
"code"
]
=
ex
.
code
();
val
[
"msg"
]
=
ex
.
what
();
val
[
"msg"
]
=
ex
.
what
();
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
}
}
#ifdef ENABLE_MYSQL
#ifdef ENABLE_MYSQL
catch
(
SqlException
&
ex
){
catch
(
SqlException
&
ex
){
val
[
"code"
]
=
API
::
SqlFailed
;
val
[
"code"
]
=
API
::
SqlFailed
;
val
[
"msg"
]
=
StrPrinter
<<
"操作数据库失败:"
<<
ex
.
what
()
<<
":"
<<
ex
.
getSql
();
val
[
"msg"
]
=
StrPrinter
<<
"操作数据库失败:"
<<
ex
.
what
()
<<
":"
<<
ex
.
getSql
();
WarnL
<<
ex
.
what
()
<<
":"
<<
ex
.
getSql
();
WarnL
<<
ex
.
what
()
<<
":"
<<
ex
.
getSql
();
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
}
}
#endif// ENABLE_MYSQL
#endif// ENABLE_MYSQL
catch
(
std
::
exception
&
ex
)
{
catch
(
std
::
exception
&
ex
)
{
val
[
"code"
]
=
API
::
Exception
;
val
[
"code"
]
=
API
::
Exception
;
val
[
"msg"
]
=
ex
.
what
();
val
[
"msg"
]
=
ex
.
what
();
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
}
}
});
});
}
}
template
<
typename
Args
,
typename
First
>
template
<
typename
Args
,
typename
First
>
bool
checkArgs
(
Args
&&
args
,
First
&&
first
){
bool
checkArgs
(
Args
&&
args
,
First
&&
first
){
return
!
args
[
first
].
empty
();
return
!
args
[
first
].
empty
();
}
}
template
<
typename
Args
,
typename
First
,
typename
...
KeyTypes
>
template
<
typename
Args
,
typename
First
,
typename
...
KeyTypes
>
bool
checkArgs
(
Args
&&
args
,
First
&&
first
,
KeyTypes
&&
...
keys
){
bool
checkArgs
(
Args
&&
args
,
First
&&
first
,
KeyTypes
&&
...
keys
){
return
!
args
[
first
].
empty
()
&&
checkArgs
(
std
::
forward
<
Args
>
(
args
),
std
::
forward
<
KeyTypes
>
(
keys
)...);
return
!
args
[
first
].
empty
()
&&
checkArgs
(
std
::
forward
<
Args
>
(
args
),
std
::
forward
<
KeyTypes
>
(
keys
)...);
}
}
#define CHECK_ARGS(...) \
#define CHECK_ARGS(...) \
if(!checkArgs(allArgs,##__VA_ARGS__)){ \
if
(
!
checkArgs
(
allArgs
,
##
__VA_ARGS__
)){
\
throw InvalidArgsException("缺少必要参数:" #__VA_ARGS__); \
throw
InvalidArgsException
(
"缺少必要参数:"
#
__VA_ARGS__
);
\
}
}
#define CHECK_SECRET() \
#define CHECK_SECRET() \
if(sender.get_peer_ip() != "127.0.0.1"){ \
if
(
sender
.
get_peer_ip
()
!=
"127.0.0.1"
){
\
CHECK_ARGS("secret"); \
CHECK_ARGS
(
"secret"
);
\
if(api_secret != allArgs["secret"]){ \
if
(
api_secret
!=
allArgs
[
"secret"
]){
\
throw AuthException("secret错误"); \
throw
AuthException
(
"secret错误"
);
\
} \
}
\
}
}
//拉流代理器列表
//拉流代理器列表
static
unordered_map
<
string
,
PlayerProxy
::
Ptr
>
s_proxyMap
;
static
unordered_map
<
string
,
PlayerProxy
::
Ptr
>
s_proxyMap
;
static
recursive_mutex
s_proxyMapMtx
;
static
recursive_mutex
s_proxyMapMtx
;
//FFmpeg拉流代理器列表
//FFmpeg拉流代理器列表
static
unordered_map
<
string
,
FFmpegSource
::
Ptr
>
s_ffmpegMap
;
static
unordered_map
<
string
,
FFmpegSource
::
Ptr
>
s_ffmpegMap
;
static
recursive_mutex
s_ffmpegMapMtx
;
static
recursive_mutex
s_ffmpegMapMtx
;
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
//rtp服务器列表
//rtp服务器列表
static
unordered_map
<
string
,
RtpServer
::
Ptr
>
s_rtpServerMap
;
static
unordered_map
<
string
,
RtpServer
::
Ptr
>
s_rtpServerMap
;
static
recursive_mutex
s_rtpServerMapMtx
;
static
recursive_mutex
s_rtpServerMapMtx
;
#endif
#endif
static
inline
string
getProxyKey
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
){
static
inline
string
getProxyKey
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
){
return
vhost
+
"/"
+
app
+
"/"
+
stream
;
return
vhost
+
"/"
+
app
+
"/"
+
stream
;
}
}
/**
/**
* 安装api接口
* 安装api接口
* 所有api都支持GET和POST两种方式
* 所有api都支持GET和POST两种方式
* POST方式参数支持application/json和application/x-www-form-urlencoded方式
* POST方式参数支持application/json和application/x-www-form-urlencoded方式
*/
*/
void
installWebApi
()
{
void
installWebApi
()
{
addHttpListener
();
addHttpListener
();
GET_CONFIG
(
string
,
api_secret
,
API
::
kSecret
);
GET_CONFIG
(
string
,
api_secret
,
API
::
kSecret
);
//获取线程负载
//获取线程负载
//测试url http://127.0.0.1/index/api/getThreadsLoad
//测试url http://127.0.0.1/index/api/getThreadsLoad
api_regist2
(
"/index/api/getThreadsLoad"
,[](
API_ARGS2
){
api_regist2
(
"/index/api/getThreadsLoad"
,[](
API_ARGS2
){
EventPollerPool
::
Instance
().
getExecutorDelay
([
invoker
,
headerOut
](
const
vector
<
int
>
&
vecDelay
)
{
EventPollerPool
::
Instance
().
getExecutorDelay
([
invoker
,
headerOut
](
const
vector
<
int
>
&
vecDelay
)
{
Value
val
;
Value
val
;
auto
vec
=
EventPollerPool
::
Instance
().
getExecutorLoad
();
auto
vec
=
EventPollerPool
::
Instance
().
getExecutorLoad
();
int
i
=
API
::
Success
;
int
i
=
API
::
Success
;
for
(
auto
load
:
vec
)
{
for
(
auto
load
:
vec
)
{
Value
obj
(
objectValue
);
Value
obj
(
objectValue
);
obj
[
"load"
]
=
load
;
obj
[
"load"
]
=
load
;
obj
[
"delay"
]
=
vecDelay
[
i
++
];
obj
[
"delay"
]
=
vecDelay
[
i
++
];
val
[
"data"
].
append
(
obj
);
val
[
"data"
].
append
(
obj
);
}
}
val
[
"code"
]
=
API
::
Success
;
val
[
"code"
]
=
API
::
Success
;
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
});
});
});
});
//获取后台工作线程负载
//获取后台工作线程负载
//测试url http://127.0.0.1/index/api/getWorkThreadsLoad
//测试url http://127.0.0.1/index/api/getWorkThreadsLoad
api_regist2
(
"/index/api/getWorkThreadsLoad"
,
[](
API_ARGS2
){
api_regist2
(
"/index/api/getWorkThreadsLoad"
,
[](
API_ARGS2
){
WorkThreadPool
::
Instance
().
getExecutorDelay
([
invoker
,
headerOut
](
const
vector
<
int
>
&
vecDelay
)
{
WorkThreadPool
::
Instance
().
getExecutorDelay
([
invoker
,
headerOut
](
const
vector
<
int
>
&
vecDelay
)
{
Value
val
;
Value
val
;
auto
vec
=
WorkThreadPool
::
Instance
().
getExecutorLoad
();
auto
vec
=
WorkThreadPool
::
Instance
().
getExecutorLoad
();
int
i
=
0
;
int
i
=
0
;
for
(
auto
load
:
vec
)
{
for
(
auto
load
:
vec
)
{
Value
obj
(
objectValue
);
Value
obj
(
objectValue
);
obj
[
"load"
]
=
load
;
obj
[
"load"
]
=
load
;
obj
[
"delay"
]
=
vecDelay
[
i
++
];
obj
[
"delay"
]
=
vecDelay
[
i
++
];
val
[
"data"
].
append
(
obj
);
val
[
"data"
].
append
(
obj
);
}
}
val
[
"code"
]
=
API
::
Success
;
val
[
"code"
]
=
API
::
Success
;
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
});
});
});
});
//获取服务器配置
//获取服务器配置
//测试url http://127.0.0.1/index/api/getServerConfig
//测试url http://127.0.0.1/index/api/getServerConfig
api_regist1
(
"/index/api/getServerConfig"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/getServerConfig"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
Value
obj
;
Value
obj
;
for
(
auto
&
pr
:
mINI
::
Instance
())
{
for
(
auto
&
pr
:
mINI
::
Instance
())
{
obj
[
pr
.
first
]
=
(
string
&
)
pr
.
second
;
obj
[
pr
.
first
]
=
(
string
&
)
pr
.
second
;
}
}
val
[
"data"
].
append
(
obj
);
val
[
"data"
].
append
(
obj
);
});
});
//设置服务器配置
//设置服务器配置
//测试url(比如关闭http api调试) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0
//测试url(比如关闭http api调试) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0
//你也可以通过http post方式传参,可以通过application/x-www-form-urlencoded或application/json方式传参
//你也可以通过http post方式传参,可以通过application/x-www-form-urlencoded或application/json方式传参
api_regist1
(
"/index/api/setServerConfig"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/setServerConfig"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
auto
&
ini
=
mINI
::
Instance
();
auto
&
ini
=
mINI
::
Instance
();
int
changed
=
API
::
Success
;
int
changed
=
API
::
Success
;
for
(
auto
&
pr
:
allArgs
)
{
for
(
auto
&
pr
:
allArgs
)
{
if
(
ini
.
find
(
pr
.
first
)
==
ini
.
end
())
{
if
(
ini
.
find
(
pr
.
first
)
==
ini
.
end
())
{
//没有这个key
//没有这个key
continue
;
continue
;
}
}
if
(
ini
[
pr
.
first
]
==
pr
.
second
)
{
if
(
ini
[
pr
.
first
]
==
pr
.
second
)
{
continue
;
continue
;
}
}
ini
[
pr
.
first
]
=
pr
.
second
;
ini
[
pr
.
first
]
=
pr
.
second
;
//替换成功
//替换成功
++
changed
;
++
changed
;
}
}
if
(
changed
>
0
)
{
if
(
changed
>
0
)
{
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastReloadConfig
);
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastReloadConfig
);
ini
.
dumpFile
(
g_ini_file
);
ini
.
dumpFile
(
g_ini_file
);
}
}
val
[
"changed"
]
=
changed
;
val
[
"changed"
]
=
changed
;
});
});
static
auto
s_get_api_list
=
[](
API_ARGS1
){
static
auto
s_get_api_list
=
[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
for
(
auto
&
pr
:
s_map_api
){
for
(
auto
&
pr
:
s_map_api
){
val
[
"data"
].
append
(
pr
.
first
);
val
[
"data"
].
append
(
pr
.
first
);
}
}
};
};
//获取服务器api列表
//获取服务器api列表
//测试url http://127.0.0.1/index/api/getApiList
//测试url http://127.0.0.1/index/api/getApiList
api_regist1
(
"/index/api/getApiList"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/getApiList"
,[](
API_ARGS1
){
s_get_api_list
(
API_ARGS_VALUE1
);
s_get_api_list
(
API_ARGS_VALUE1
);
});
});
//获取服务器api列表
//获取服务器api列表
//测试url http://127.0.0.1/index/
//测试url http://127.0.0.1/index/
api_regist1
(
"/index/"
,[](
API_ARGS1
){
api_regist1
(
"/index/"
,[](
API_ARGS1
){
s_get_api_list
(
API_ARGS_VALUE1
);
s_get_api_list
(
API_ARGS_VALUE1
);
});
});
#if !defined(_WIN32)
#if !defined(_WIN32)
//重启服务器,只有Daemon方式才能重启,否则是直接关闭!
//重启服务器,只有Daemon方式才能重启,否则是直接关闭!
//测试url http://127.0.0.1/index/api/restartServer
//测试url http://127.0.0.1/index/api/restartServer
api_regist1
(
"/index/api/restartServer"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/restartServer"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
EventPollerPool
::
Instance
().
getPoller
()
->
doDelayTask
(
1000
,[](){
EventPollerPool
::
Instance
().
getPoller
()
->
doDelayTask
(
1000
,[](){
//尝试正常退出
//尝试正常退出
::
kill
(
getpid
(),
SIGINT
);
::
kill
(
getpid
(),
SIGINT
);
//3秒后强制退出
//3秒后强制退出
EventPollerPool
::
Instance
().
getPoller
()
->
doDelayTask
(
3000
,[](){
EventPollerPool
::
Instance
().
getPoller
()
->
doDelayTask
(
3000
,[](){
exit
(
0
);
exit
(
0
);
return
0
;
return
0
;
});
});
return
0
;
return
0
;
});
});
val
[
"msg"
]
=
"服务器将在一秒后自动重启"
;
val
[
"msg"
]
=
"服务器将在一秒后自动重启"
;
});
});
#endif//#if !defined(_WIN32)
#endif//#if !defined(_WIN32)
static
auto
makeMediaSourceJson
=
[](
const
MediaSource
::
Ptr
&
media
){
static
auto
makeMediaSourceJson
=
[](
const
MediaSource
::
Ptr
&
media
){
Value
item
;
Value
item
;
item
[
"schema"
]
=
media
->
getSchema
();
item
[
"schema"
]
=
media
->
getSchema
();
item
[
"vhost"
]
=
media
->
getVhost
();
item
[
"vhost"
]
=
media
->
getVhost
();
item
[
"app"
]
=
media
->
getApp
();
item
[
"app"
]
=
media
->
getApp
();
item
[
"stream"
]
=
media
->
getId
();
item
[
"stream"
]
=
media
->
getId
();
item
[
"createStamp"
]
=
(
Json
::
UInt64
)
media
->
getCreateStamp
();
item
[
"createStamp"
]
=
(
Json
::
UInt64
)
media
->
getCreateStamp
();
item
[
"aliveSecond"
]
=
(
Json
::
UInt64
)
media
->
getAliveSecond
();
item
[
"aliveSecond"
]
=
(
Json
::
UInt64
)
media
->
getAliveSecond
();
item
[
"bytesSpeed"
]
=
media
->
getBytesSpeed
();
item
[
"bytesSpeed"
]
=
media
->
getBytesSpeed
();
item
[
"readerCount"
]
=
media
->
readerCount
();
item
[
"readerCount"
]
=
media
->
readerCount
();
item
[
"totalReaderCount"
]
=
media
->
totalReaderCount
();
item
[
"totalReaderCount"
]
=
media
->
totalReaderCount
();
item
[
"originType"
]
=
(
int
)
media
->
getOriginType
();
item
[
"originType"
]
=
(
int
)
media
->
getOriginType
();
item
[
"originTypeStr"
]
=
getOriginTypeString
(
media
->
getOriginType
());
item
[
"originTypeStr"
]
=
getOriginTypeString
(
media
->
getOriginType
());
item
[
"originUrl"
]
=
media
->
getOriginUrl
();
item
[
"originUrl"
]
=
media
->
getOriginUrl
();
auto
originSock
=
media
->
getOriginSock
();
auto
originSock
=
media
->
getOriginSock
();
if
(
originSock
)
{
if
(
originSock
)
{
item
[
"originSock"
][
"local_ip"
]
=
originSock
->
get_local_ip
();
item
[
"originSock"
][
"local_ip"
]
=
originSock
->
get_local_ip
();
item
[
"originSock"
][
"local_port"
]
=
originSock
->
get_local_port
();
item
[
"originSock"
][
"local_port"
]
=
originSock
->
get_local_port
();
item
[
"originSock"
][
"peer_ip"
]
=
originSock
->
get_peer_ip
();
item
[
"originSock"
][
"peer_ip"
]
=
originSock
->
get_peer_ip
();
item
[
"originSock"
][
"peer_port"
]
=
originSock
->
get_peer_port
();
item
[
"originSock"
][
"peer_port"
]
=
originSock
->
get_peer_port
();
item
[
"originSock"
][
"identifier"
]
=
originSock
->
getIdentifier
();
item
[
"originSock"
][
"identifier"
]
=
originSock
->
getIdentifier
();
}
else
{
}
else
{
item
[
"originSock"
]
=
Json
::
nullValue
;
item
[
"originSock"
]
=
Json
::
nullValue
;
}
}
for
(
auto
&
track
:
media
->
getTracks
()){
for
(
auto
&
track
:
media
->
getTracks
()){
Value
obj
;
Value
obj
;
auto
codec_type
=
track
->
getTrackType
();
auto
codec_type
=
track
->
getTrackType
();
obj
[
"codec_id"
]
=
track
->
getCodecId
();
obj
[
"codec_id"
]
=
track
->
getCodecId
();
obj
[
"codec_id_name"
]
=
track
->
getCodecName
();
obj
[
"codec_id_name"
]
=
track
->
getCodecName
();
obj
[
"ready"
]
=
track
->
ready
();
obj
[
"ready"
]
=
track
->
ready
();
obj
[
"codec_type"
]
=
codec_type
;
obj
[
"codec_type"
]
=
codec_type
;
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
);
obj
[
"sample_rate"
]
=
audio_track
->
getAudioSampleRate
();
obj
[
"sample_rate"
]
=
audio_track
->
getAudioSampleRate
();
obj
[
"channels"
]
=
audio_track
->
getAudioChannel
();
obj
[
"channels"
]
=
audio_track
->
getAudioChannel
();
obj
[
"sample_bit"
]
=
audio_track
->
getAudioSampleBit
();
obj
[
"sample_bit"
]
=
audio_track
->
getAudioSampleBit
();
break
;
break
;
}
}
case
TrackVideo
:
{
case
TrackVideo
:
{
auto
video_track
=
dynamic_pointer_cast
<
VideoTrack
>
(
track
);
auto
video_track
=
dynamic_pointer_cast
<
VideoTrack
>
(
track
);
obj
[
"width"
]
=
video_track
->
getVideoWidth
();
obj
[
"width"
]
=
video_track
->
getVideoWidth
();
obj
[
"height"
]
=
video_track
->
getVideoHeight
();
obj
[
"height"
]
=
video_track
->
getVideoHeight
();
obj
[
"fps"
]
=
round
(
video_track
->
getVideoFps
());
obj
[
"fps"
]
=
round
(
video_track
->
getVideoFps
());
break
;
break
;
}
}
default
:
default
:
break
;
break
;
}
}
item
[
"tracks"
].
append
(
obj
);
item
[
"tracks"
].
append
(
obj
);
}
}
return
item
;
return
item
;
};
};
//获取流列表,可选筛选参数
//获取流列表,可选筛选参数
//测试url0(获取所有流) http://127.0.0.1/index/api/getMediaList
//测试url0(获取所有流) http://127.0.0.1/index/api/getMediaList
//测试url1(获取虚拟主机为"__defaultVost__"的流) http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__
//测试url1(获取虚拟主机为"__defaultVost__"的流) http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__
//测试url2(获取rtsp类型的流) http://127.0.0.1/index/api/getMediaList?schema=rtsp
//测试url2(获取rtsp类型的流) http://127.0.0.1/index/api/getMediaList?schema=rtsp
api_regist1
(
"/index/api/getMediaList"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/getMediaList"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
//获取所有MediaSource列表
//获取所有MediaSource列表
MediaSource
::
for_each_media
([
&
](
const
MediaSource
::
Ptr
&
media
){
MediaSource
::
for_each_media
([
&
](
const
MediaSource
::
Ptr
&
media
){
if
(
!
allArgs
[
"schema"
].
empty
()
&&
allArgs
[
"schema"
]
!=
media
->
getSchema
()){
if
(
!
allArgs
[
"schema"
].
empty
()
&&
allArgs
[
"schema"
]
!=
media
->
getSchema
()){
return
;
return
;
}
}
if
(
!
allArgs
[
"vhost"
].
empty
()
&&
allArgs
[
"vhost"
]
!=
media
->
getVhost
()){
if
(
!
allArgs
[
"vhost"
].
empty
()
&&
allArgs
[
"vhost"
]
!=
media
->
getVhost
()){
return
;
return
;
}
}
if
(
!
allArgs
[
"app"
].
empty
()
&&
allArgs
[
"app"
]
!=
media
->
getApp
()){
if
(
!
allArgs
[
"app"
].
empty
()
&&
allArgs
[
"app"
]
!=
media
->
getApp
()){
return
;
return
;
}
}
val
[
"data"
].
append
(
makeMediaSourceJson
(
media
));
val
[
"data"
].
append
(
makeMediaSourceJson
(
media
));
});
});
});
});
//测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
//测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
api_regist1
(
"/index/api/isMediaOnline"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/isMediaOnline"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"schema"
,
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"schema"
,
"vhost"
,
"app"
,
"stream"
);
val
[
"online"
]
=
(
bool
)
(
MediaSource
::
find
(
allArgs
[
"schema"
],
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]));
val
[
"online"
]
=
(
bool
)
(
MediaSource
::
find
(
allArgs
[
"schema"
],
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]));
});
});
//测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
//测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
api_regist1
(
"/index/api/getMediaInfo"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/getMediaInfo"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"schema"
,
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"schema"
,
"vhost"
,
"app"
,
"stream"
);
auto
src
=
MediaSource
::
find
(
allArgs
[
"schema"
],
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
auto
src
=
MediaSource
::
find
(
allArgs
[
"schema"
],
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
if
(
!
src
){
if
(
!
src
){
val
[
"online"
]
=
false
;
val
[
"online"
]
=
false
;
return
;
return
;
}
}
val
=
makeMediaSourceJson
(
src
);
val
=
makeMediaSourceJson
(
src
);
val
[
"online"
]
=
true
;
val
[
"online"
]
=
true
;
val
[
"code"
]
=
API
::
Success
;
val
[
"code"
]
=
API
::
Success
;
});
});
//主动关断流,包括关断拉流、推流
//主动关断流,包括关断拉流、推流
//测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
//测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
api_regist1
(
"/index/api/close_stream"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/close_stream"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"schema"
,
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"schema"
,
"vhost"
,
"app"
,
"stream"
);
//踢掉推流器
//踢掉推流器
auto
src
=
MediaSource
::
find
(
allArgs
[
"schema"
],
auto
src
=
MediaSource
::
find
(
allArgs
[
"schema"
],
allArgs
[
"vhost"
],
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
allArgs
[
"stream"
]);
if
(
src
)
{
if
(
src
)
{
bool
flag
=
src
->
close
(
allArgs
[
"force"
].
as
<
bool
>
());
bool
flag
=
src
->
close
(
allArgs
[
"force"
].
as
<
bool
>
());
val
[
"result"
]
=
flag
?
0
:
-
1
;
val
[
"result"
]
=
flag
?
0
:
-
1
;
val
[
"msg"
]
=
flag
?
"success"
:
"close failed"
;
val
[
"msg"
]
=
flag
?
"success"
:
"close failed"
;
val
[
"code"
]
=
flag
?
API
::
Success
:
API
::
OtherFailed
;
val
[
"code"
]
=
flag
?
API
::
Success
:
API
::
OtherFailed
;
}
else
{
}
else
{
val
[
"result"
]
=
-
2
;
val
[
"result"
]
=
-
2
;
val
[
"msg"
]
=
"can not find the stream"
;
val
[
"msg"
]
=
"can not find the stream"
;
val
[
"code"
]
=
API
::
OtherFailed
;
val
[
"code"
]
=
API
::
OtherFailed
;
}
}
});
});
//批量主动关断流,包括关断拉流、推流
//批量主动关断流,包括关断拉流、推流
//测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
//测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
api_regist1
(
"/index/api/close_streams"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/close_streams"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
//筛选命中个数
//筛选命中个数
int
count_hit
=
0
;
int
count_hit
=
0
;
int
count_closed
=
0
;
int
count_closed
=
0
;
list
<
MediaSource
::
Ptr
>
media_list
;
list
<
MediaSource
::
Ptr
>
media_list
;
MediaSource
::
for_each_media
([
&
](
const
MediaSource
::
Ptr
&
media
){
MediaSource
::
for_each_media
([
&
](
const
MediaSource
::
Ptr
&
media
){
if
(
!
allArgs
[
"schema"
].
empty
()
&&
allArgs
[
"schema"
]
!=
media
->
getSchema
()){
if
(
!
allArgs
[
"schema"
].
empty
()
&&
allArgs
[
"schema"
]
!=
media
->
getSchema
()){
return
;
return
;
}
}
if
(
!
allArgs
[
"vhost"
].
empty
()
&&
allArgs
[
"vhost"
]
!=
media
->
getVhost
()){
if
(
!
allArgs
[
"vhost"
].
empty
()
&&
allArgs
[
"vhost"
]
!=
media
->
getVhost
()){
return
;
return
;
}
}
if
(
!
allArgs
[
"app"
].
empty
()
&&
allArgs
[
"app"
]
!=
media
->
getApp
()){
if
(
!
allArgs
[
"app"
].
empty
()
&&
allArgs
[
"app"
]
!=
media
->
getApp
()){
return
;
return
;
}
}
if
(
!
allArgs
[
"stream"
].
empty
()
&&
allArgs
[
"stream"
]
!=
media
->
getId
()){
if
(
!
allArgs
[
"stream"
].
empty
()
&&
allArgs
[
"stream"
]
!=
media
->
getId
()){
return
;
return
;
}
}
++
count_hit
;
++
count_hit
;
media_list
.
emplace_back
(
media
);
media_list
.
emplace_back
(
media
);
});
});
bool
force
=
allArgs
[
"force"
].
as
<
bool
>
();
bool
force
=
allArgs
[
"force"
].
as
<
bool
>
();
for
(
auto
&
media
:
media_list
){
for
(
auto
&
media
:
media_list
){
if
(
media
->
close
(
force
)){
if
(
media
->
close
(
force
)){
++
count_closed
;
++
count_closed
;
}
}
}
}
val
[
"count_hit"
]
=
count_hit
;
val
[
"count_hit"
]
=
count_hit
;
val
[
"count_closed"
]
=
count_closed
;
val
[
"count_closed"
]
=
count_closed
;
});
});
//获取所有TcpSession列表信息
//获取所有TcpSession列表信息
//可以根据本地端口和远端ip来筛选
//可以根据本地端口和远端ip来筛选
//测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935
//测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935
api_regist1
(
"/index/api/getAllSession"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/getAllSession"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
Value
jsession
;
Value
jsession
;
uint16_t
local_port
=
allArgs
[
"local_port"
].
as
<
uint16_t
>
();
uint16_t
local_port
=
allArgs
[
"local_port"
].
as
<
uint16_t
>
();
string
&
peer_ip
=
allArgs
[
"peer_ip"
];
string
&
peer_ip
=
allArgs
[
"peer_ip"
];
SessionMap
::
Instance
().
for_each_session
([
&
](
const
string
&
id
,
const
TcpSession
::
Ptr
&
session
){
SessionMap
::
Instance
().
for_each_session
([
&
](
const
string
&
id
,
const
TcpSession
::
Ptr
&
session
){
if
(
local_port
!=
0
&&
local_port
!=
session
->
get_local_port
()){
if
(
local_port
!=
0
&&
local_port
!=
session
->
get_local_port
()){
return
;
return
;
}
}
if
(
!
peer_ip
.
empty
()
&&
peer_ip
!=
session
->
get_peer_ip
()){
if
(
!
peer_ip
.
empty
()
&&
peer_ip
!=
session
->
get_peer_ip
()){
return
;
return
;
}
}
jsession
[
"peer_ip"
]
=
session
->
get_peer_ip
();
jsession
[
"peer_ip"
]
=
session
->
get_peer_ip
();
jsession
[
"peer_port"
]
=
session
->
get_peer_port
();
jsession
[
"peer_port"
]
=
session
->
get_peer_port
();
jsession
[
"local_ip"
]
=
session
->
get_local_ip
();
jsession
[
"local_ip"
]
=
session
->
get_local_ip
();
jsession
[
"local_port"
]
=
session
->
get_local_port
();
jsession
[
"local_port"
]
=
session
->
get_local_port
();
jsession
[
"id"
]
=
id
;
jsession
[
"id"
]
=
id
;
jsession
[
"typeid"
]
=
typeid
(
*
session
).
name
();
jsession
[
"typeid"
]
=
typeid
(
*
session
).
name
();
val
[
"data"
].
append
(
jsession
);
val
[
"data"
].
append
(
jsession
);
});
});
});
});
//断开tcp连接,比如说可以断开rtsp、rtmp播放器等
//断开tcp连接,比如说可以断开rtsp、rtmp播放器等
//测试url http://127.0.0.1/index/api/kick_session?id=123456
//测试url http://127.0.0.1/index/api/kick_session?id=123456
api_regist1
(
"/index/api/kick_session"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/kick_session"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"id"
);
CHECK_ARGS
(
"id"
);
//踢掉tcp会话
//踢掉tcp会话
auto
session
=
SessionMap
::
Instance
().
get
(
allArgs
[
"id"
]);
auto
session
=
SessionMap
::
Instance
().
get
(
allArgs
[
"id"
]);
if
(
!
session
){
if
(
!
session
){
throw
ApiRetException
(
"can not find the target"
,
API
::
OtherFailed
);
throw
ApiRetException
(
"can not find the target"
,
API
::
OtherFailed
);
}
}
session
->
safeShutdown
();
session
->
safeShutdown
();
});
});
//批量断开tcp连接,比如说可以断开rtsp、rtmp播放器等
//批量断开tcp连接,比如说可以断开rtsp、rtmp播放器等
//测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935
//测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935
api_regist1
(
"/index/api/kick_sessions"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/kick_sessions"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
uint16_t
local_port
=
allArgs
[
"local_port"
].
as
<
uint16_t
>
();
uint16_t
local_port
=
allArgs
[
"local_port"
].
as
<
uint16_t
>
();
string
&
peer_ip
=
allArgs
[
"peer_ip"
];
string
&
peer_ip
=
allArgs
[
"peer_ip"
];
uint64_t
count_hit
=
0
;
uint64_t
count_hit
=
0
;
list
<
TcpSession
::
Ptr
>
session_list
;
list
<
TcpSession
::
Ptr
>
session_list
;
SessionMap
::
Instance
().
for_each_session
([
&
](
const
string
&
id
,
const
TcpSession
::
Ptr
&
session
){
SessionMap
::
Instance
().
for_each_session
([
&
](
const
string
&
id
,
const
TcpSession
::
Ptr
&
session
){
if
(
local_port
!=
0
&&
local_port
!=
session
->
get_local_port
()){
if
(
local_port
!=
0
&&
local_port
!=
session
->
get_local_port
()){
return
;
return
;
}
}
if
(
!
peer_ip
.
empty
()
&&
peer_ip
!=
session
->
get_peer_ip
()){
if
(
!
peer_ip
.
empty
()
&&
peer_ip
!=
session
->
get_peer_ip
()){
return
;
return
;
}
}
session_list
.
emplace_back
(
session
);
session_list
.
emplace_back
(
session
);
++
count_hit
;
++
count_hit
;
});
});
for
(
auto
&
session
:
session_list
){
for
(
auto
&
session
:
session_list
){
session
->
safeShutdown
();
session
->
safeShutdown
();
}
}
val
[
"count_hit"
]
=
(
Json
::
UInt64
)
count_hit
;
val
[
"count_hit"
]
=
(
Json
::
UInt64
)
count_hit
;
});
});
static
auto
addStreamProxy
=
[](
const
string
&
vhost
,
static
auto
addStreamProxy
=
[](
const
string
&
vhost
,
const
string
&
app
,
const
string
&
app
,
const
string
&
stream
,
const
string
&
stream
,
const
string
&
url
,
const
string
&
url
,
bool
enable_hls
,
bool
enable_hls
,
bool
enable_mp4
,
bool
enable_mp4
,
int
rtp_type
,
int
rtp_type
,
const
function
<
void
(
const
SockException
&
ex
,
const
string
&
key
)
>
&
cb
){
const
function
<
void
(
const
SockException
&
ex
,
const
string
&
key
)
>
&
cb
){
auto
key
=
getProxyKey
(
vhost
,
app
,
stream
);
auto
key
=
getProxyKey
(
vhost
,
app
,
stream
);
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
if
(
s_proxyMap
.
find
(
key
)
!=
s_proxyMap
.
end
()){
if
(
s_proxyMap
.
find
(
key
)
!=
s_proxyMap
.
end
()){
//已经在拉流了
//已经在拉流了
cb
(
SockException
(
Err_success
),
key
);
cb
(
SockException
(
Err_success
),
key
);
return
;
return
;
}
}
//添加拉流代理
//添加拉流代理
PlayerProxy
::
Ptr
player
(
new
PlayerProxy
(
vhost
,
app
,
stream
,
enable_hls
,
enable_mp4
));
PlayerProxy
::
Ptr
player
(
new
PlayerProxy
(
vhost
,
app
,
stream
,
enable_hls
,
enable_mp4
));
s_proxyMap
[
key
]
=
player
;
s_proxyMap
[
key
]
=
player
;
//指定RTP over TCP(播放rtsp时有效)
//指定RTP over TCP(播放rtsp时有效)
(
*
player
)[
kRtpType
]
=
rtp_type
;
(
*
player
)[
kRtpType
]
=
rtp_type
;
//开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试
//开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试
player
->
setPlayCallbackOnce
([
cb
,
key
](
const
SockException
&
ex
){
player
->
setPlayCallbackOnce
([
cb
,
key
](
const
SockException
&
ex
){
if
(
ex
){
if
(
ex
){
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
s_proxyMap
.
erase
(
key
);
s_proxyMap
.
erase
(
key
);
}
}
cb
(
ex
,
key
);
cb
(
ex
,
key
);
});
});
//被主动关闭拉流
//被主动关闭拉流
player
->
setOnClose
([
key
](){
player
->
setOnClose
([
key
](){
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
s_proxyMap
.
erase
(
key
);
s_proxyMap
.
erase
(
key
);
});
});
player
->
play
(
url
);
player
->
play
(
url
);
};
};
//动态添加rtsp/rtmp拉流代理
//动态添加rtsp/rtmp拉流代理
//测试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
//测试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_regist2
(
"/index/api/addStreamProxy"
,[](
API_ARGS2
){
api_regist2
(
"/index/api/addStreamProxy"
,[](
API_ARGS2
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
,
"url"
);
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
,
"url"
);
addStreamProxy
(
allArgs
[
"vhost"
],
addStreamProxy
(
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"app"
],
allArgs
[
"stream"
],
allArgs
[
"stream"
],
allArgs
[
"url"
],
allArgs
[
"url"
],
allArgs
[
"enable_hls"
],
/* 是否hls转发 */
allArgs
[
"enable_hls"
],
/* 是否hls转发 */
allArgs
[
"enable_mp4"
],
/* 是否MP4录制 */
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
){
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
}
else
{
}
else
{
const_cast
<
Value
&>
(
val
)[
"data"
][
"key"
]
=
key
;
const_cast
<
Value
&>
(
val
)[
"data"
][
"key"
]
=
key
;
}
}
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
});
});
});
});
//关闭拉流代理
//关闭拉流代理
//测试url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0
//测试url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0
api_regist1
(
"/index/api/delStreamProxy"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/delStreamProxy"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"key"
);
CHECK_ARGS
(
"key"
);
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
val
[
"data"
][
"flag"
]
=
s_proxyMap
.
erase
(
allArgs
[
"key"
])
==
1
;
val
[
"data"
][
"flag"
]
=
s_proxyMap
.
erase
(
allArgs
[
"key"
])
==
1
;
});
});
static
auto
addFFmpegSource
=
[](
const
string
&
src_url
,
static
auto
addFFmpegSource
=
[](
const
string
&
src_url
,
const
string
&
dst_url
,
const
string
&
dst_url
,
int
timeout_ms
,
int
timeout_ms
,
const
function
<
void
(
const
SockException
&
ex
,
const
string
&
key
)
>
&
cb
){
const
function
<
void
(
const
SockException
&
ex
,
const
string
&
key
)
>
&
cb
){
auto
key
=
MD5
(
dst_url
).
hexdigest
();
auto
key
=
MD5
(
dst_url
).
hexdigest
();
lock_guard
<
decltype
(
s_ffmpegMapMtx
)
>
lck
(
s_ffmpegMapMtx
);
lock_guard
<
decltype
(
s_ffmpegMapMtx
)
>
lck
(
s_ffmpegMapMtx
);
if
(
s_ffmpegMap
.
find
(
key
)
!=
s_ffmpegMap
.
end
()){
if
(
s_ffmpegMap
.
find
(
key
)
!=
s_ffmpegMap
.
end
()){
//已经在拉流了
//已经在拉流了
cb
(
SockException
(
Err_success
),
key
);
cb
(
SockException
(
Err_success
),
key
);
return
;
return
;
}
}
FFmpegSource
::
Ptr
ffmpeg
=
std
::
make_shared
<
FFmpegSource
>
();
FFmpegSource
::
Ptr
ffmpeg
=
std
::
make_shared
<
FFmpegSource
>
();
s_ffmpegMap
[
key
]
=
ffmpeg
;
s_ffmpegMap
[
key
]
=
ffmpeg
;
ffmpeg
->
setOnClose
([
key
](){
ffmpeg
->
setOnClose
([
key
](){
lock_guard
<
decltype
(
s_ffmpegMapMtx
)
>
lck
(
s_ffmpegMapMtx
);
lock_guard
<
decltype
(
s_ffmpegMapMtx
)
>
lck
(
s_ffmpegMapMtx
);
s_ffmpegMap
.
erase
(
key
);
s_ffmpegMap
.
erase
(
key
);
});
});
ffmpeg
->
play
(
src_url
,
dst_url
,
timeout_ms
,[
cb
,
key
](
const
SockException
&
ex
){
ffmpeg
->
play
(
src_url
,
dst_url
,
timeout_ms
,[
cb
,
key
](
const
SockException
&
ex
){
if
(
ex
){
if
(
ex
){
lock_guard
<
decltype
(
s_ffmpegMapMtx
)
>
lck
(
s_ffmpegMapMtx
);
lock_guard
<
decltype
(
s_ffmpegMapMtx
)
>
lck
(
s_ffmpegMapMtx
);
s_ffmpegMap
.
erase
(
key
);
s_ffmpegMap
.
erase
(
key
);
}
}
cb
(
ex
,
key
);
cb
(
ex
,
key
);
});
});
};
};
//动态添加rtsp/rtmp拉流代理
//动态添加rtsp/rtmp拉流代理
//测试url http://127.0.0.1/index/api/addFFmpegSource?src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000
//测试url http://127.0.0.1/index/api/addFFmpegSource?src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000
api_regist2
(
"/index/api/addFFmpegSource"
,[](
API_ARGS2
){
api_regist2
(
"/index/api/addFFmpegSource"
,[](
API_ARGS2
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"src_url"
,
"dst_url"
,
"timeout_ms"
);
CHECK_ARGS
(
"src_url"
,
"dst_url"
,
"timeout_ms"
);
auto
src_url
=
allArgs
[
"src_url"
];
auto
src_url
=
allArgs
[
"src_url"
];
auto
dst_url
=
allArgs
[
"dst_url"
];
auto
dst_url
=
allArgs
[
"dst_url"
];
int
timeout_ms
=
allArgs
[
"timeout_ms"
];
int
timeout_ms
=
allArgs
[
"timeout_ms"
];
addFFmpegSource
(
src_url
,
dst_url
,
timeout_ms
,[
invoker
,
val
,
headerOut
](
const
SockException
&
ex
,
const
string
&
key
){
addFFmpegSource
(
src_url
,
dst_url
,
timeout_ms
,[
invoker
,
val
,
headerOut
](
const
SockException
&
ex
,
const
string
&
key
){
if
(
ex
){
if
(
ex
){
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
}
else
{
}
else
{
const_cast
<
Value
&>
(
val
)[
"data"
][
"key"
]
=
key
;
const_cast
<
Value
&>
(
val
)[
"data"
][
"key"
]
=
key
;
}
}
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
});
});
});
});
static
auto
api_delFFmpegSource
=
[](
API_ARGS1
){
static
auto
api_delFFmpegSource
=
[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"key"
);
CHECK_ARGS
(
"key"
);
lock_guard
<
decltype
(
s_ffmpegMapMtx
)
>
lck
(
s_ffmpegMapMtx
);
lock_guard
<
decltype
(
s_ffmpegMapMtx
)
>
lck
(
s_ffmpegMapMtx
);
val
[
"data"
][
"flag"
]
=
s_ffmpegMap
.
erase
(
allArgs
[
"key"
])
==
1
;
val
[
"data"
][
"flag"
]
=
s_ffmpegMap
.
erase
(
allArgs
[
"key"
])
==
1
;
};
};
//关闭拉流代理
//关闭拉流代理
//测试url http://127.0.0.1/index/api/delFFmepgSource?key=key
//测试url http://127.0.0.1/index/api/delFFmepgSource?key=key
api_regist1
(
"/index/api/delFFmpegSource"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/delFFmpegSource"
,[](
API_ARGS1
){
api_delFFmpegSource
(
API_ARGS_VALUE1
);
api_delFFmpegSource
(
API_ARGS_VALUE1
);
});
});
//此处为了兼容之前的拼写错误
//此处为了兼容之前的拼写错误
api_regist1
(
"/index/api/delFFmepgSource"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/delFFmepgSource"
,[](
API_ARGS1
){
api_delFFmpegSource
(
API_ARGS_VALUE1
);
api_delFFmpegSource
(
API_ARGS_VALUE1
);
});
});
//新增http api下载可执行程序文件接口
//新增http api下载可执行程序文件接口
//测试url http://127.0.0.1/index/api/downloadBin
//测试url http://127.0.0.1/index/api/downloadBin
api_regist2
(
"/index/api/downloadBin"
,[](
API_ARGS2
){
api_regist2
(
"/index/api/downloadBin"
,[](
API_ARGS2
){
CHECK_SECRET
();
CHECK_SECRET
();
invoker
.
responseFile
(
headerIn
,
StrCaseMap
(),
exePath
());
invoker
.
responseFile
(
headerIn
,
StrCaseMap
(),
exePath
());
});
});
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
api_regist1
(
"/index/api/getRtpInfo"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/getRtpInfo"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"stream_id"
);
CHECK_ARGS
(
"stream_id"
);
auto
process
=
RtpSelector
::
Instance
().
getProcess
(
allArgs
[
"stream_id"
],
false
);
auto
process
=
RtpSelector
::
Instance
().
getProcess
(
allArgs
[
"stream_id"
],
false
);
if
(
!
process
)
{
if
(
!
process
)
{
val
[
"exist"
]
=
false
;
val
[
"exist"
]
=
false
;
return
;
return
;
}
}
val
[
"exist"
]
=
true
;
val
[
"exist"
]
=
true
;
val
[
"peer_ip"
]
=
process
->
get_peer_ip
();
val
[
"peer_ip"
]
=
process
->
get_peer_ip
();
val
[
"peer_port"
]
=
process
->
get_peer_port
();
val
[
"peer_port"
]
=
process
->
get_peer_port
();
val
[
"local_port"
]
=
process
->
get_local_port
();
val
[
"local_port"
]
=
process
->
get_local_port
();
val
[
"local_ip"
]
=
process
->
get_local_ip
();
val
[
"local_ip"
]
=
process
->
get_local_ip
();
});
});
api_regist1
(
"/index/api/openRtpServer"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/openRtpServer"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"port"
,
"enable_tcp"
,
"stream_id"
);
CHECK_ARGS
(
"port"
,
"enable_tcp"
,
"stream_id"
);
auto
stream_id
=
allArgs
[
"stream_id"
];
auto
stream_id
=
allArgs
[
"stream_id"
];
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
throw
InvalidArgsException
(
"该stream_id已存在"
);
throw
InvalidArgsException
(
"该stream_id已存在"
);
}
}
RtpServer
::
Ptr
server
=
std
::
make_shared
<
RtpServer
>
();
RtpServer
::
Ptr
server
=
std
::
make_shared
<
RtpServer
>
();
server
->
start
(
allArgs
[
"port"
],
stream_id
,
allArgs
[
"enable_tcp"
].
as
<
bool
>
());
server
->
start
(
allArgs
[
"port"
],
stream_id
,
allArgs
[
"enable_tcp"
].
as
<
bool
>
());
server
->
setOnDetach
([
stream_id
]()
{
server
->
setOnDetach
([
stream_id
]()
{
//设置rtp超时移除事件
//设置rtp超时移除事件
lock_guard
<
recursive_mutex
>
lck
(
s_rtpServerMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_rtpServerMapMtx
);
s_rtpServerMap
.
erase
(
stream_id
);
s_rtpServerMap
.
erase
(
stream_id
);
});
});
//保存对象
//保存对象
s_rtpServerMap
.
emplace
(
stream_id
,
server
);
s_rtpServerMap
.
emplace
(
stream_id
,
server
);
//回复json
//回复json
val
[
"port"
]
=
server
->
getPort
();
val
[
"port"
]
=
server
->
getPort
();
});
});
api_regist1
(
"/index/api/closeRtpServer"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/closeRtpServer"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"stream_id"
);
CHECK_ARGS
(
"stream_id"
);
lock_guard
<
recursive_mutex
>
lck
(
s_rtpServerMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_rtpServerMapMtx
);
auto
it
=
s_rtpServerMap
.
find
(
allArgs
[
"stream_id"
]);
auto
it
=
s_rtpServerMap
.
find
(
allArgs
[
"stream_id"
]);
if
(
it
==
s_rtpServerMap
.
end
()){
if
(
it
==
s_rtpServerMap
.
end
()){
val
[
"hit"
]
=
0
;
val
[
"hit"
]
=
0
;
return
;
return
;
}
}
auto
server
=
it
->
second
;
auto
server
=
it
->
second
;
s_rtpServerMap
.
erase
(
it
);
s_rtpServerMap
.
erase
(
it
);
val
[
"hit"
]
=
1
;
val
[
"hit"
]
=
1
;
});
});
api_regist1
(
"/index/api/listRtpServer"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/listRtpServer"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
lock_guard
<
recursive_mutex
>
lck
(
s_rtpServerMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_rtpServerMapMtx
);
for
(
auto
&
pr
:
s_rtpServerMap
)
{
for
(
auto
&
pr
:
s_rtpServerMap
)
{
Value
obj
;
Value
obj
;
obj
[
"stream_id"
]
=
pr
.
first
;
obj
[
"stream_id"
]
=
pr
.
first
;
obj
[
"port"
]
=
pr
.
second
->
getPort
();
obj
[
"port"
]
=
pr
.
second
->
getPort
();
val
[
"data"
].
append
(
obj
);
val
[
"data"
].
append
(
obj
);
}
}
});
});
api_regist2
(
"/index/api/startSendRtp"
,[](
API_ARGS2
){
api_regist2
(
"/index/api/startSendRtp"
,[](
API_ARGS2
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
,
"ssrc"
,
"dst_url"
,
"dst_port"
,
"is_udp"
);
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
,
"ssrc"
,
"dst_url"
,
"dst_port"
,
"is_udp"
,
"src_port"
);
auto
src
=
MediaSource
::
find
(
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
auto
src
=
MediaSource
::
find
(
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
if
(
!
src
)
{
if
(
!
src
)
{
throw
ApiRetException
(
"该媒体流不存在"
,
API
::
OtherFailed
);
throw
ApiRetException
(
"该媒体流不存在"
,
API
::
OtherFailed
);
}
}
src
->
startSendRtp
(
allArgs
[
"dst_url"
],
allArgs
[
"dst_port"
],
allArgs
[
"ssrc"
],
allArgs
[
"is_udp"
],
[
val
,
headerOut
,
invoker
](
const
SockException
&
ex
){
src
->
startSendRtp
(
allArgs
[
"dst_url"
],
allArgs
[
"dst_port"
],
allArgs
[
"ssrc"
],
allArgs
[
"is_udp"
],
allArgs
[
"src_port"
],
[
val
,
headerOut
,
invoker
](
const
SockException
&
ex
){
if
(
ex
)
{
if
(
ex
)
{
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
}
}
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
});
});
});
});
api_regist1
(
"/index/api/stopSendRtp"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/stopSendRtp"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
,
"ssrc"
);
auto
src
=
MediaSource
::
find
(
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
auto
src
=
MediaSource
::
find
(
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
if
(
!
src
)
{
if
(
!
src
)
{
throw
ApiRetException
(
"该媒体流不存在"
,
API
::
OtherFailed
);
throw
ApiRetException
(
"该媒体流不存在"
,
API
::
OtherFailed
);
}
}
if
(
!
src
->
stopSendRtp
(
))
{
if
(
!
src
->
stopSendRtp
(
allArgs
[
"ssrc"
]))
{
throw
ApiRetException
(
"尚未开始推流,停止失败"
,
API
::
OtherFailed
);
throw
ApiRetException
(
"尚未开始推流,停止失败"
,
API
::
OtherFailed
);
}
}
});
});
#endif//ENABLE_RTPPROXY
#endif//ENABLE_RTPPROXY
// 开始录制hls或MP4
// 开始录制hls或MP4
api_regist1
(
"/index/api/startRecord"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/startRecord"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"type"
,
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"type"
,
"vhost"
,
"app"
,
"stream"
);
auto
result
=
Recorder
::
startRecord
((
Recorder
::
type
)
allArgs
[
"type"
].
as
<
int
>
(),
auto
result
=
Recorder
::
startRecord
((
Recorder
::
type
)
allArgs
[
"type"
].
as
<
int
>
(),
allArgs
[
"vhost"
],
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"app"
],
allArgs
[
"stream"
],
allArgs
[
"stream"
],
allArgs
[
"customized_path"
]);
allArgs
[
"customized_path"
]);
val
[
"result"
]
=
result
;
val
[
"result"
]
=
result
;
val
[
"code"
]
=
result
?
API
::
Success
:
API
::
OtherFailed
;
val
[
"code"
]
=
result
?
API
::
Success
:
API
::
OtherFailed
;
val
[
"msg"
]
=
result
?
"success"
:
"start record failed"
;
val
[
"msg"
]
=
result
?
"success"
:
"start record failed"
;
});
});
// 停止录制hls或MP4
// 停止录制hls或MP4
api_regist1
(
"/index/api/stopRecord"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/stopRecord"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"type"
,
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"type"
,
"vhost"
,
"app"
,
"stream"
);
auto
result
=
Recorder
::
stopRecord
((
Recorder
::
type
)
allArgs
[
"type"
].
as
<
int
>
(),
auto
result
=
Recorder
::
stopRecord
((
Recorder
::
type
)
allArgs
[
"type"
].
as
<
int
>
(),
allArgs
[
"vhost"
],
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
allArgs
[
"stream"
]);
val
[
"result"
]
=
result
;
val
[
"result"
]
=
result
;
val
[
"code"
]
=
result
?
API
::
Success
:
API
::
OtherFailed
;
val
[
"code"
]
=
result
?
API
::
Success
:
API
::
OtherFailed
;
val
[
"msg"
]
=
result
?
"success"
:
"stop record failed"
;
val
[
"msg"
]
=
result
?
"success"
:
"stop record failed"
;
});
});
// 获取hls或MP4录制状态
// 获取hls或MP4录制状态
api_regist1
(
"/index/api/isRecording"
,[](
API_ARGS1
){
api_regist1
(
"/index/api/isRecording"
,[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"type"
,
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"type"
,
"vhost"
,
"app"
,
"stream"
);
val
[
"status"
]
=
Recorder
::
isRecording
((
Recorder
::
type
)
allArgs
[
"type"
].
as
<
int
>
(),
val
[
"status"
]
=
Recorder
::
isRecording
((
Recorder
::
type
)
allArgs
[
"type"
].
as
<
int
>
(),
allArgs
[
"vhost"
],
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
allArgs
[
"stream"
]);
});
});
//获取录像文件夹列表或mp4文件列表
//获取录像文件夹列表或mp4文件列表
//http://127.0.0.1/index/api/getMp4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01
//http://127.0.0.1/index/api/getMp4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01
api_regist1
(
"/index/api/getMp4RecordFile"
,
[](
API_ARGS1
){
api_regist1
(
"/index/api/getMp4RecordFile"
,
[](
API_ARGS1
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
);
auto
record_path
=
Recorder
::
getRecordPath
(
Recorder
::
type_mp4
,
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
auto
record_path
=
Recorder
::
getRecordPath
(
Recorder
::
type_mp4
,
allArgs
[
"vhost"
],
allArgs
[
"app"
],
allArgs
[
"stream"
]);
auto
period
=
allArgs
[
"period"
];
auto
period
=
allArgs
[
"period"
];
//判断是获取mp4文件列表还是获取文件夹列表
//判断是获取mp4文件列表还是获取文件夹列表
bool
search_mp4
=
period
.
size
()
==
sizeof
(
"2020-02-01"
)
-
1
;
bool
search_mp4
=
period
.
size
()
==
sizeof
(
"2020-02-01"
)
-
1
;
if
(
search_mp4
)
{
if
(
search_mp4
)
{
record_path
=
record_path
+
period
+
"/"
;
record_path
=
record_path
+
period
+
"/"
;
}
}
Json
::
Value
paths
(
arrayValue
);
Json
::
Value
paths
(
arrayValue
);
//这是筛选日期,获取文件夹列表
//这是筛选日期,获取文件夹列表
File
::
scanDir
(
record_path
,
[
&
](
const
string
&
path
,
bool
isDir
)
{
File
::
scanDir
(
record_path
,
[
&
](
const
string
&
path
,
bool
isDir
)
{
int
pos
=
path
.
rfind
(
'/'
);
int
pos
=
path
.
rfind
(
'/'
);
if
(
pos
!=
string
::
npos
)
{
if
(
pos
!=
string
::
npos
)
{
string
relative_path
=
path
.
substr
(
pos
+
1
);
string
relative_path
=
path
.
substr
(
pos
+
1
);
if
(
search_mp4
)
{
if
(
search_mp4
)
{
if
(
!
isDir
)
{
if
(
!
isDir
)
{
//我们只收集mp4文件,对文件夹不感兴趣
//我们只收集mp4文件,对文件夹不感兴趣
paths
.
append
(
relative_path
);
paths
.
append
(
relative_path
);
}
}
}
else
if
(
isDir
&&
relative_path
.
find
(
period
)
==
0
)
{
}
else
if
(
isDir
&&
relative_path
.
find
(
period
)
==
0
)
{
//匹配到对应日期的文件夹
//匹配到对应日期的文件夹
paths
.
append
(
relative_path
);
paths
.
append
(
relative_path
);
}
}
}
}
return
true
;
return
true
;
},
false
);
},
false
);
val
[
"data"
][
"rootPath"
]
=
record_path
;
val
[
"data"
][
"rootPath"
]
=
record_path
;
val
[
"data"
][
"paths"
]
=
paths
;
val
[
"data"
][
"paths"
]
=
paths
;
});
});
static
auto
responseSnap
=
[](
const
string
&
snap_path
,
static
auto
responseSnap
=
[](
const
string
&
snap_path
,
const
HttpSession
::
KeyValue
&
headerIn
,
const
HttpSession
::
KeyValue
&
headerIn
,
const
HttpSession
::
HttpResponseInvoker
&
invoker
)
{
const
HttpSession
::
HttpResponseInvoker
&
invoker
)
{
StrCaseMap
headerOut
;
StrCaseMap
headerOut
;
struct
stat
statbuf
=
{
0
};
struct
stat
statbuf
=
{
0
};
GET_CONFIG
(
string
,
defaultSnap
,
API
::
kDefaultSnap
);
GET_CONFIG
(
string
,
defaultSnap
,
API
::
kDefaultSnap
);
if
(
!
(
stat
(
snap_path
.
data
(),
&
statbuf
)
==
0
&&
statbuf
.
st_size
!=
0
)
&&
!
defaultSnap
.
empty
())
{
if
(
!
(
stat
(
snap_path
.
data
(),
&
statbuf
)
==
0
&&
statbuf
.
st_size
!=
0
)
&&
!
defaultSnap
.
empty
())
{
//空文件且设置了预设图,则返回预设图片(也就是FFmpeg生成截图中空档期的默认图片)
//空文件且设置了预设图,则返回预设图片(也就是FFmpeg生成截图中空档期的默认图片)
const_cast
<
string
&>
(
snap_path
)
=
File
::
absolutePath
(
defaultSnap
,
""
);
const_cast
<
string
&>
(
snap_path
)
=
File
::
absolutePath
(
defaultSnap
,
""
);
headerOut
[
"Content-Type"
]
=
HttpFileManager
::
getContentType
(
snap_path
.
data
());
headerOut
[
"Content-Type"
]
=
HttpFileManager
::
getContentType
(
snap_path
.
data
());
}
else
{
}
else
{
//之前生成的截图文件,我们默认为jpeg格式
//之前生成的截图文件,我们默认为jpeg格式
headerOut
[
"Content-Type"
]
=
HttpFileManager
::
getContentType
(
".jpeg"
);
headerOut
[
"Content-Type"
]
=
HttpFileManager
::
getContentType
(
".jpeg"
);
}
}
//返回图片给http客户端
//返回图片给http客户端
invoker
.
responseFile
(
headerIn
,
headerOut
,
snap_path
);
invoker
.
responseFile
(
headerIn
,
headerOut
,
snap_path
);
};
};
//获取截图缓存或者实时截图
//获取截图缓存或者实时截图
//http://127.0.0.1/index/api/getSnap?url=rtmp://127.0.0.1/record/robot.mp4&timeout_sec=10&expire_sec=3
//http://127.0.0.1/index/api/getSnap?url=rtmp://127.0.0.1/record/robot.mp4&timeout_sec=10&expire_sec=3
api_regist2
(
"/index/api/getSnap"
,
[](
API_ARGS2
){
api_regist2
(
"/index/api/getSnap"
,
[](
API_ARGS2
){
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"url"
,
"timeout_sec"
,
"expire_sec"
);
CHECK_ARGS
(
"url"
,
"timeout_sec"
,
"expire_sec"
);
GET_CONFIG
(
string
,
snap_root
,
API
::
kSnapRoot
);
GET_CONFIG
(
string
,
snap_root
,
API
::
kSnapRoot
);
bool
have_old_snap
=
false
,
res_old_snap
=
false
;
bool
have_old_snap
=
false
,
res_old_snap
=
false
;
int
expire_sec
=
allArgs
[
"expire_sec"
];
int
expire_sec
=
allArgs
[
"expire_sec"
];
auto
scan_path
=
File
::
absolutePath
(
MD5
(
allArgs
[
"url"
]).
hexdigest
(),
snap_root
)
+
"/"
;
auto
scan_path
=
File
::
absolutePath
(
MD5
(
allArgs
[
"url"
]).
hexdigest
(),
snap_root
)
+
"/"
;
string
new_snap
=
StrPrinter
<<
scan_path
<<
time
(
NULL
)
<<
".jpeg"
;
string
new_snap
=
StrPrinter
<<
scan_path
<<
time
(
NULL
)
<<
".jpeg"
;
File
::
scanDir
(
scan_path
,
[
&
](
const
string
&
path
,
bool
isDir
)
{
File
::
scanDir
(
scan_path
,
[
&
](
const
string
&
path
,
bool
isDir
)
{
if
(
isDir
||
!
end_with
(
path
,
".jpeg"
))
{
if
(
isDir
||
!
end_with
(
path
,
".jpeg"
))
{
//忽略文件夹或其他类型的文件
//忽略文件夹或其他类型的文件
return
true
;
return
true
;
}
}
//找到截图
//找到截图
auto
tm
=
FindField
(
path
.
data
()
+
scan_path
.
size
(),
nullptr
,
".jpeg"
);
auto
tm
=
FindField
(
path
.
data
()
+
scan_path
.
size
(),
nullptr
,
".jpeg"
);
if
(
atoll
(
tm
.
data
())
+
expire_sec
<
time
(
NULL
))
{
if
(
atoll
(
tm
.
data
())
+
expire_sec
<
time
(
NULL
))
{
//截图已经过期,改名,以便再次请求时,可以返回老截图
//截图已经过期,改名,以便再次请求时,可以返回老截图
rename
(
path
.
data
(),
new_snap
.
data
());
rename
(
path
.
data
(),
new_snap
.
data
());
have_old_snap
=
true
;
have_old_snap
=
true
;
return
true
;
return
true
;
}
}
//截图存在,且未过期,那么返回之
//截图存在,且未过期,那么返回之
res_old_snap
=
true
;
res_old_snap
=
true
;
responseSnap
(
path
,
headerIn
,
invoker
);
responseSnap
(
path
,
headerIn
,
invoker
);
//中断遍历
//中断遍历
return
false
;
return
false
;
});
});
if
(
res_old_snap
)
{
if
(
res_old_snap
)
{
//已经回复了旧的截图
//已经回复了旧的截图
return
;
return
;
}
}
//无截图或者截图已经过期
//无截图或者截图已经过期
if
(
!
have_old_snap
)
{
if
(
!
have_old_snap
)
{
//无过期截图,生成一个空文件,目的是顺便创建文件夹路径
//无过期截图,生成一个空文件,目的是顺便创建文件夹路径
//同时防止在FFmpeg生成截图途中不停的尝试调用该api多次启动FFmpeg进程
//同时防止在FFmpeg生成截图途中不停的尝试调用该api多次启动FFmpeg进程
auto
file
=
File
::
create_file
(
new_snap
.
data
(),
"wb"
);
auto
file
=
File
::
create_file
(
new_snap
.
data
(),
"wb"
);
if
(
file
)
{
if
(
file
)
{
fclose
(
file
);
fclose
(
file
);
}
}
}
}
//启动FFmpeg进程,开始截图,生成临时文件,截图成功后替换为正式文件
//启动FFmpeg进程,开始截图,生成临时文件,截图成功后替换为正式文件
auto
new_snap_tmp
=
new_snap
+
".tmp"
;
auto
new_snap_tmp
=
new_snap
+
".tmp"
;
FFmpegSnap
::
makeSnap
(
allArgs
[
"url"
],
new_snap_tmp
,
allArgs
[
"timeout_sec"
],
[
invoker
,
headerIn
,
new_snap
,
new_snap_tmp
](
bool
success
)
{
FFmpegSnap
::
makeSnap
(
allArgs
[
"url"
],
new_snap_tmp
,
allArgs
[
"timeout_sec"
],
[
invoker
,
headerIn
,
new_snap
,
new_snap_tmp
](
bool
success
)
{
if
(
!
success
)
{
if
(
!
success
)
{
//生成截图失败,可能残留空文件
//生成截图失败,可能残留空文件
File
::
delete_file
(
new_snap_tmp
.
data
());
File
::
delete_file
(
new_snap_tmp
.
data
());
}
else
{
}
else
{
//临时文件改成正式文件
//临时文件改成正式文件
File
::
delete_file
(
new_snap
.
data
());
File
::
delete_file
(
new_snap
.
data
());
rename
(
new_snap_tmp
.
data
(),
new_snap
.
data
());
rename
(
new_snap_tmp
.
data
(),
new_snap
.
data
());
}
}
responseSnap
(
new_snap
,
headerIn
,
invoker
);
responseSnap
(
new_snap
,
headerIn
,
invoker
);
});
});
});
});
////////////以下是注册的Hook API////////////
////////////以下是注册的Hook API////////////
api_regist1
(
"/index/hook/on_publish"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_publish"
,[](
API_ARGS1
){
//开始推流事件
//开始推流事件
//转换成rtsp或rtmp
//转换成rtsp或rtmp
val
[
"enableRtxp"
]
=
true
;
val
[
"enableRtxp"
]
=
true
;
//转换hls
//转换hls
val
[
"enableHls"
]
=
true
;
val
[
"enableHls"
]
=
true
;
//不录制mp4
//不录制mp4
val
[
"enableMP4"
]
=
false
;
val
[
"enableMP4"
]
=
false
;
});
});
api_regist1
(
"/index/hook/on_play"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_play"
,[](
API_ARGS1
){
//开始播放事件
//开始播放事件
});
});
api_regist1
(
"/index/hook/on_flow_report"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_flow_report"
,[](
API_ARGS1
){
//流量统计hook api
//流量统计hook api
});
});
api_regist1
(
"/index/hook/on_rtsp_realm"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_rtsp_realm"
,[](
API_ARGS1
){
//rtsp是否需要鉴权,默认需要鉴权
//rtsp是否需要鉴权,默认需要鉴权
val
[
"code"
]
=
API
::
Success
;
val
[
"code"
]
=
API
::
Success
;
val
[
"realm"
]
=
"zlmediakit_reaml"
;
val
[
"realm"
]
=
"zlmediakit_reaml"
;
});
});
api_regist1
(
"/index/hook/on_rtsp_auth"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_rtsp_auth"
,[](
API_ARGS1
){
//rtsp鉴权密码,密码等于用户名
//rtsp鉴权密码,密码等于用户名
//rtsp可以有双重鉴权!后面还会触发on_play事件
//rtsp可以有双重鉴权!后面还会触发on_play事件
CHECK_ARGS
(
"user_name"
);
CHECK_ARGS
(
"user_name"
);
val
[
"code"
]
=
API
::
Success
;
val
[
"code"
]
=
API
::
Success
;
val
[
"encrypted"
]
=
false
;
val
[
"encrypted"
]
=
false
;
val
[
"passwd"
]
=
allArgs
[
"user_name"
].
data
();
val
[
"passwd"
]
=
allArgs
[
"user_name"
].
data
();
});
});
api_regist1
(
"/index/hook/on_stream_changed"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_stream_changed"
,[](
API_ARGS1
){
//媒体注册或反注册事件
//媒体注册或反注册事件
});
});
#if !defined(_WIN32)
#if !defined(_WIN32)
api_regist2
(
"/index/hook/on_stream_not_found_ffmpeg"
,[](
API_ARGS2
){
api_regist2
(
"/index/hook/on_stream_not_found_ffmpeg"
,[](
API_ARGS2
){
//媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
//媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
);
//通过FFmpeg按需拉流
//通过FFmpeg按需拉流
GET_CONFIG
(
int
,
rtmp_port
,
Rtmp
::
kPort
);
GET_CONFIG
(
int
,
rtmp_port
,
Rtmp
::
kPort
);
GET_CONFIG
(
int
,
timeout_sec
,
Hook
::
kTimeoutSec
);
GET_CONFIG
(
int
,
timeout_sec
,
Hook
::
kTimeoutSec
);
string
dst_url
=
StrPrinter
string
dst_url
=
StrPrinter
<<
"rtmp://127.0.0.1:"
<<
"rtmp://127.0.0.1:"
<<
rtmp_port
<<
"/"
<<
rtmp_port
<<
"/"
<<
allArgs
[
"app"
]
<<
"/"
<<
allArgs
[
"app"
]
<<
"/"
<<
allArgs
[
"stream"
]
<<
"?vhost="
<<
allArgs
[
"stream"
]
<<
"?vhost="
<<
allArgs
[
"vhost"
];
<<
allArgs
[
"vhost"
];
addFFmpegSource
(
"http://hls-ott-zhibo.wasu.tv/live/272/index.m3u8"
,
/** ffmpeg拉流支持任意编码格式任意协议 **/
addFFmpegSource
(
"http://hls-ott-zhibo.wasu.tv/live/272/index.m3u8"
,
/** ffmpeg拉流支持任意编码格式任意协议 **/
dst_url
,
dst_url
,
(
1000
*
timeout_sec
)
-
500
,
(
1000
*
timeout_sec
)
-
500
,
[
invoker
,
val
,
headerOut
](
const
SockException
&
ex
,
const
string
&
key
){
[
invoker
,
val
,
headerOut
](
const
SockException
&
ex
,
const
string
&
key
){
if
(
ex
){
if
(
ex
){
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
}
else
{
}
else
{
const_cast
<
Value
&>
(
val
)[
"data"
][
"key"
]
=
key
;
const_cast
<
Value
&>
(
val
)[
"data"
][
"key"
]
=
key
;
}
}
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
});
});
});
});
#endif//!defined(_WIN32)
#endif//!defined(_WIN32)
api_regist2
(
"/index/hook/on_stream_not_found"
,[](
API_ARGS2
){
api_regist2
(
"/index/hook/on_stream_not_found"
,[](
API_ARGS2
){
//媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
//媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
CHECK_SECRET
();
CHECK_SECRET
();
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
);
CHECK_ARGS
(
"vhost"
,
"app"
,
"stream"
);
//通过内置支持的rtsp/rtmp按需拉流
//通过内置支持的rtsp/rtmp按需拉流
addStreamProxy
(
allArgs
[
"vhost"
],
addStreamProxy
(
allArgs
[
"vhost"
],
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"
,
"rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov"
,
true
,
/* 开启hls转发 */
true
,
/* 开启hls转发 */
false
,
/* 禁用MP4录制 */
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
){
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"code"
]
=
API
::
OtherFailed
;
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
const_cast
<
Value
&>
(
val
)[
"msg"
]
=
ex
.
what
();
}
else
{
}
else
{
const_cast
<
Value
&>
(
val
)[
"data"
][
"key"
]
=
key
;
const_cast
<
Value
&>
(
val
)[
"data"
][
"key"
]
=
key
;
}
}
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
invoker
(
"200 OK"
,
headerOut
,
val
.
toStyledString
());
});
});
});
});
api_regist1
(
"/index/hook/on_record_mp4"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_record_mp4"
,[](
API_ARGS1
){
//录制mp4分片完毕事件
//录制mp4分片完毕事件
});
});
api_regist1
(
"/index/hook/on_shell_login"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_shell_login"
,[](
API_ARGS1
){
//shell登录调试事件
//shell登录调试事件
});
});
api_regist1
(
"/index/hook/on_stream_none_reader"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_stream_none_reader"
,[](
API_ARGS1
){
//无人观看流默认关闭
//无人观看流默认关闭
val
[
"close"
]
=
true
;
val
[
"close"
]
=
true
;
});
});
static
auto
checkAccess
=
[](
const
string
&
params
){
static
auto
checkAccess
=
[](
const
string
&
params
){
//我们假定大家都要权限访问
//我们假定大家都要权限访问
return
true
;
return
true
;
};
};
api_regist1
(
"/index/hook/on_http_access"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_http_access"
,[](
API_ARGS1
){
//在这里根据allArgs["params"](url参数)来判断该http客户端是否有权限访问该文件
//在这里根据allArgs["params"](url参数)来判断该http客户端是否有权限访问该文件
if
(
!
checkAccess
(
allArgs
[
"params"
])){
if
(
!
checkAccess
(
allArgs
[
"params"
])){
//无访问权限
//无访问权限
val
[
"err"
]
=
"无访问权限"
;
val
[
"err"
]
=
"无访问权限"
;
//仅限制访问当前目录
//仅限制访问当前目录
val
[
"path"
]
=
""
;
val
[
"path"
]
=
""
;
//标记该客户端无权限1分钟
//标记该客户端无权限1分钟
val
[
"second"
]
=
60
;
val
[
"second"
]
=
60
;
return
;
return
;
}
}
//可以访问
//可以访问
val
[
"err"
]
=
""
;
val
[
"err"
]
=
""
;
//只能访问当前目录
//只能访问当前目录
val
[
"path"
]
=
""
;
val
[
"path"
]
=
""
;
//该http客户端用户被授予10分钟的访问权限,该权限仅限访问当前目录
//该http客户端用户被授予10分钟的访问权限,该权限仅限访问当前目录
val
[
"second"
]
=
10
*
60
;
val
[
"second"
]
=
10
*
60
;
});
});
api_regist1
(
"/index/hook/on_server_started"
,[](
API_ARGS1
){
api_regist1
(
"/index/hook/on_server_started"
,[](
API_ARGS1
){
//服务器重启报告
//服务器重启报告
});
});
}
}
void
unInstallWebApi
(){
void
unInstallWebApi
(){
{
{
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_proxyMapMtx
);
s_proxyMap
.
clear
();
s_proxyMap
.
clear
();
}
}
{
{
lock_guard
<
recursive_mutex
>
lck
(
s_ffmpegMapMtx
);
lock_guard
<
recursive_mutex
>
lck
(
s_ffmpegMapMtx
);
s_ffmpegMap
.
clear
();
s_ffmpegMap
.
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
();
s_rtpServerMap
.
clear
();
#endif
#endif
}
}
}
}
\ No newline at end of file
src/Common/MediaSink.cpp
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#include "MediaSink.h"
#include "MediaSink.h"
//最多等待未初始化的Track 10秒,超时之后会忽略未初始化的Track
//最多等待未初始化的Track 10秒,超时之后会忽略未初始化的Track
#define MAX_WAIT_MS_READY 10000
#define MAX_WAIT_MS_READY 10000
//如果添加Track,最多等待5秒
//如果添加Track,最多等待5秒
#define MAX_WAIT_MS_ADD_TRACK
5000
#define MAX_WAIT_MS_ADD_TRACK
1000
namespace
mediakit
{
namespace
mediakit
{
void
MediaSink
::
addTrack
(
const
Track
::
Ptr
&
track_in
)
{
void
MediaSink
::
addTrack
(
const
Track
::
Ptr
&
track_in
)
{
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
if
(
_all_track_ready
)
{
if
(
_all_track_ready
)
{
WarnL
<<
"all track is ready, add this track too late!"
;
WarnL
<<
"all track is ready, add this track too late!"
;
return
;
return
;
}
}
//克隆Track,只拷贝其数据,不拷贝其数据转发关系
//克隆Track,只拷贝其数据,不拷贝其数据转发关系
auto
track
=
track_in
->
clone
();
auto
track
=
track_in
->
clone
();
auto
codec_id
=
track
->
getCodecId
();
auto
codec_id
=
track
->
getCodecId
();
_track_map
[
codec_id
]
=
track
;
_track_map
[
codec_id
]
=
track
;
_track_ready_callback
[
codec_id
]
=
[
this
,
track
]()
{
_track_ready_callback
[
codec_id
]
=
[
this
,
track
]()
{
onTrackReady
(
track
);
onTrackReady
(
track
);
};
};
_ticker
.
resetTime
();
_ticker
.
resetTime
();
track
->
addDelegate
(
std
::
make_shared
<
FrameWriterInterfaceHelper
>
([
this
](
const
Frame
::
Ptr
&
frame
)
{
track
->
addDelegate
(
std
::
make_shared
<
FrameWriterInterfaceHelper
>
([
this
](
const
Frame
::
Ptr
&
frame
)
{
if
(
_all_track_ready
)
{
if
(
_all_track_ready
)
{
onTrackFrame
(
frame
);
onTrackFrame
(
frame
);
}
else
{
}
else
{
//还有Track未就绪,先缓存之
//还有Track未就绪,先缓存之
_frame_unread
[
frame
->
getCodecId
()].
emplace_back
(
Frame
::
getCacheAbleFrame
(
frame
));
_frame_unread
[
frame
->
getCodecId
()].
emplace_back
(
Frame
::
getCacheAbleFrame
(
frame
));
}
}
}));
}));
}
}
void
MediaSink
::
resetTracks
()
{
void
MediaSink
::
resetTracks
()
{
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
_all_track_ready
=
false
;
_all_track_ready
=
false
;
_track_map
.
clear
();
_track_map
.
clear
();
_track_ready_callback
.
clear
();
_track_ready_callback
.
clear
();
_ticker
.
resetTime
();
_ticker
.
resetTime
();
_max_track_size
=
2
;
_max_track_size
=
2
;
_frame_unread
.
clear
();
_frame_unread
.
clear
();
}
}
void
MediaSink
::
inputFrame
(
const
Frame
::
Ptr
&
frame
)
{
void
MediaSink
::
inputFrame
(
const
Frame
::
Ptr
&
frame
)
{
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
auto
it
=
_track_map
.
find
(
frame
->
getCodecId
());
auto
it
=
_track_map
.
find
(
frame
->
getCodecId
());
if
(
it
==
_track_map
.
end
())
{
if
(
it
==
_track_map
.
end
())
{
return
;
return
;
}
}
it
->
second
->
inputFrame
(
frame
);
it
->
second
->
inputFrame
(
frame
);
checkTrackIfReady
(
nullptr
);
checkTrackIfReady
(
nullptr
);
}
}
void
MediaSink
::
checkTrackIfReady_l
(
const
Track
::
Ptr
&
track
){
void
MediaSink
::
checkTrackIfReady_l
(
const
Track
::
Ptr
&
track
){
//Track由未就绪状态转换成就绪状态,我们就触发onTrackReady回调
//Track由未就绪状态转换成就绪状态,我们就触发onTrackReady回调
auto
it_callback
=
_track_ready_callback
.
find
(
track
->
getCodecId
());
auto
it_callback
=
_track_ready_callback
.
find
(
track
->
getCodecId
());
if
(
it_callback
!=
_track_ready_callback
.
end
()
&&
track
->
ready
())
{
if
(
it_callback
!=
_track_ready_callback
.
end
()
&&
track
->
ready
())
{
it_callback
->
second
();
it_callback
->
second
();
_track_ready_callback
.
erase
(
it_callback
);
_track_ready_callback
.
erase
(
it_callback
);
}
}
}
}
void
MediaSink
::
checkTrackIfReady
(
const
Track
::
Ptr
&
track
){
void
MediaSink
::
checkTrackIfReady
(
const
Track
::
Ptr
&
track
){
if
(
!
_all_track_ready
&&
!
_track_ready_callback
.
empty
())
{
if
(
!
_all_track_ready
&&
!
_track_ready_callback
.
empty
())
{
if
(
track
)
{
if
(
track
)
{
checkTrackIfReady_l
(
track
);
checkTrackIfReady_l
(
track
);
}
else
{
}
else
{
for
(
auto
&
pr
:
_track_map
)
{
for
(
auto
&
pr
:
_track_map
)
{
checkTrackIfReady_l
(
pr
.
second
);
checkTrackIfReady_l
(
pr
.
second
);
}
}
}
}
}
}
if
(
!
_all_track_ready
){
if
(
!
_all_track_ready
){
if
(
_ticker
.
elapsedTime
()
>
MAX_WAIT_MS_READY
){
if
(
_ticker
.
elapsedTime
()
>
MAX_WAIT_MS_READY
){
//如果超过规定时间,那么不再等待并忽略未准备好的Track
//如果超过规定时间,那么不再等待并忽略未准备好的Track
emitAllTrackReady
();
emitAllTrackReady
();
return
;
return
;
}
}
if
(
!
_track_ready_callback
.
empty
()){
if
(
!
_track_ready_callback
.
empty
()){
//在超时时间内,如果存在未准备好的Track,那么继续等待
//在超时时间内,如果存在未准备好的Track,那么继续等待
return
;
return
;
}
}
if
(
_track_map
.
size
()
==
_max_track_size
){
if
(
_track_map
.
size
()
==
_max_track_size
){
//如果已经添加了音视频Track,并且不存在未准备好的Track,那么说明所有Track都准备好了
//如果已经添加了音视频Track,并且不存在未准备好的Track,那么说明所有Track都准备好了
emitAllTrackReady
();
emitAllTrackReady
();
return
;
return
;
}
}
if
(
_track_map
.
size
()
==
1
&&
_ticker
.
elapsedTime
()
>
MAX_WAIT_MS_ADD_TRACK
){
if
(
_track_map
.
size
()
==
1
&&
_ticker
.
elapsedTime
()
>
MAX_WAIT_MS_ADD_TRACK
){
//如果只有一个Track,那么在该Track添加后,我们最多还等待若干时间(可能后面还会添加Track)
//如果只有一个Track,那么在该Track添加后,我们最多还等待若干时间(可能后面还会添加Track)
emitAllTrackReady
();
emitAllTrackReady
();
return
;
return
;
}
}
}
}
}
}
void
MediaSink
::
addTrackCompleted
(){
void
MediaSink
::
addTrackCompleted
(){
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
_max_track_size
=
_track_map
.
size
();
_max_track_size
=
_track_map
.
size
();
checkTrackIfReady
(
nullptr
);
checkTrackIfReady
(
nullptr
);
}
}
void
MediaSink
::
emitAllTrackReady
()
{
void
MediaSink
::
emitAllTrackReady
()
{
if
(
_all_track_ready
)
{
if
(
_all_track_ready
)
{
return
;
return
;
}
}
DebugL
<<
"all track ready use "
<<
_ticker
.
elapsedTime
()
<<
"ms"
;
DebugL
<<
"all track ready use "
<<
_ticker
.
elapsedTime
()
<<
"ms"
;
if
(
!
_track_ready_callback
.
empty
())
{
if
(
!
_track_ready_callback
.
empty
())
{
//这是超时强制忽略未准备好的Track
//这是超时强制忽略未准备好的Track
_track_ready_callback
.
clear
();
_track_ready_callback
.
clear
();
//移除未准备好的Track
//移除未准备好的Track
for
(
auto
it
=
_track_map
.
begin
();
it
!=
_track_map
.
end
();)
{
for
(
auto
it
=
_track_map
.
begin
();
it
!=
_track_map
.
end
();)
{
if
(
!
it
->
second
->
ready
())
{
if
(
!
it
->
second
->
ready
())
{
WarnL
<<
"track not ready for a long time, ignored: "
<<
it
->
second
->
getCodecName
();
WarnL
<<
"track not ready for a long time, ignored: "
<<
it
->
second
->
getCodecName
();
it
=
_track_map
.
erase
(
it
);
it
=
_track_map
.
erase
(
it
);
continue
;
continue
;
}
}
++
it
;
++
it
;
}
}
}
}
if
(
!
_track_map
.
empty
())
{
if
(
!
_track_map
.
empty
())
{
//最少有一个有效的Track
//最少有一个有效的Track
_all_track_ready
=
true
;
_all_track_ready
=
true
;
onAllTrackReady
();
onAllTrackReady
();
//全部Track就绪,我们一次性把之前的帧输出
//全部Track就绪,我们一次性把之前的帧输出
for
(
auto
&
pr
:
_frame_unread
){
for
(
auto
&
pr
:
_frame_unread
){
if
(
_track_map
.
find
(
pr
.
first
)
==
_track_map
.
end
())
{
if
(
_track_map
.
find
(
pr
.
first
)
==
_track_map
.
end
())
{
//该Track已经被移除
//该Track已经被移除
continue
;
continue
;
}
}
pr
.
second
.
for_each
([
&
](
const
Frame
::
Ptr
&
frame
)
{
pr
.
second
.
for_each
([
&
](
const
Frame
::
Ptr
&
frame
)
{
onTrackFrame
(
frame
);
onTrackFrame
(
frame
);
});
});
}
}
_frame_unread
.
clear
();
_frame_unread
.
clear
();
}
}
}
}
vector
<
Track
::
Ptr
>
MediaSink
::
getTracks
(
bool
trackReady
)
const
{
vector
<
Track
::
Ptr
>
MediaSink
::
getTracks
(
bool
trackReady
)
const
{
vector
<
Track
::
Ptr
>
ret
;
vector
<
Track
::
Ptr
>
ret
;
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
lock_guard
<
recursive_mutex
>
lck
(
_mtx
);
for
(
auto
&
pr
:
_track_map
){
for
(
auto
&
pr
:
_track_map
){
if
(
trackReady
&&
!
pr
.
second
->
ready
()){
if
(
trackReady
&&
!
pr
.
second
->
ready
()){
continue
;
continue
;
}
}
ret
.
emplace_back
(
pr
.
second
);
ret
.
emplace_back
(
pr
.
second
);
}
}
return
ret
;
return
ret
;
}
}
}
//namespace mediakit
}
//namespace mediakit
src/Common/MediaSource.cpp
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#include "MediaSource.h"
#include "MediaSource.h"
#include "Record/MP4Reader.h"
#include "Record/MP4Reader.h"
#include "Util/util.h"
#include "Util/util.h"
#include "Network/sockutil.h"
#include "Network/sockutil.h"
#include "Network/TcpSession.h"
#include "Network/TcpSession.h"
using
namespace
toolkit
;
using
namespace
toolkit
;
namespace
mediakit
{
namespace
mediakit
{
recursive_mutex
s_media_source_mtx
;
recursive_mutex
s_media_source_mtx
;
MediaSource
::
SchemaVhostAppStreamMap
s_media_source_map
;
MediaSource
::
SchemaVhostAppStreamMap
s_media_source_map
;
string
getOriginTypeString
(
MediaOriginType
type
){
string
getOriginTypeString
(
MediaOriginType
type
){
#define SWITCH_CASE(type) case MediaOriginType::type : return #type
#define SWITCH_CASE(type) case MediaOriginType::type : return #type
switch
(
type
)
{
switch
(
type
)
{
SWITCH_CASE
(
unknown
);
SWITCH_CASE
(
unknown
);
SWITCH_CASE
(
rtmp_push
);
SWITCH_CASE
(
rtmp_push
);
SWITCH_CASE
(
rtsp_push
);
SWITCH_CASE
(
rtsp_push
);
SWITCH_CASE
(
rtp_push
);
SWITCH_CASE
(
rtp_push
);
SWITCH_CASE
(
pull
);
SWITCH_CASE
(
pull
);
SWITCH_CASE
(
ffmpeg_pull
);
SWITCH_CASE
(
ffmpeg_pull
);
SWITCH_CASE
(
mp4_vod
);
SWITCH_CASE
(
mp4_vod
);
SWITCH_CASE
(
device_chn
);
SWITCH_CASE
(
device_chn
);
}
}
}
}
MediaSource
::
MediaSource
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
){
MediaSource
::
MediaSource
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
){
GET_CONFIG
(
bool
,
enableVhost
,
General
::
kEnableVhost
);
GET_CONFIG
(
bool
,
enableVhost
,
General
::
kEnableVhost
);
if
(
!
enableVhost
)
{
if
(
!
enableVhost
)
{
_vhost
=
DEFAULT_VHOST
;
_vhost
=
DEFAULT_VHOST
;
}
else
{
}
else
{
_vhost
=
vhost
.
empty
()
?
DEFAULT_VHOST
:
vhost
;
_vhost
=
vhost
.
empty
()
?
DEFAULT_VHOST
:
vhost
;
}
}
_schema
=
schema
;
_schema
=
schema
;
_app
=
app
;
_app
=
app
;
_stream_id
=
stream_id
;
_stream_id
=
stream_id
;
_create_stamp
=
time
(
NULL
);
_create_stamp
=
time
(
NULL
);
}
}
MediaSource
::~
MediaSource
()
{
MediaSource
::~
MediaSource
()
{
unregist
();
unregist
();
}
}
const
string
&
MediaSource
::
getSchema
()
const
{
const
string
&
MediaSource
::
getSchema
()
const
{
return
_schema
;
return
_schema
;
}
}
const
string
&
MediaSource
::
getVhost
()
const
{
const
string
&
MediaSource
::
getVhost
()
const
{
return
_vhost
;
return
_vhost
;
}
}
const
string
&
MediaSource
::
getApp
()
const
{
const
string
&
MediaSource
::
getApp
()
const
{
//获取该源的id
//获取该源的id
return
_app
;
return
_app
;
}
}
const
string
&
MediaSource
::
getId
()
const
{
const
string
&
MediaSource
::
getId
()
const
{
return
_stream_id
;
return
_stream_id
;
}
}
int
MediaSource
::
getBytesSpeed
(){
int
MediaSource
::
getBytesSpeed
(){
return
_speed
.
getSpeed
();
return
_speed
.
getSpeed
();
}
}
uint64_t
MediaSource
::
getCreateStamp
()
const
{
uint64_t
MediaSource
::
getCreateStamp
()
const
{
return
_create_stamp
;
return
_create_stamp
;
}
}
uint64_t
MediaSource
::
getAliveSecond
()
const
{
uint64_t
MediaSource
::
getAliveSecond
()
const
{
//使用Ticker对象获取存活时间的目的是防止修改系统时间导致回退
//使用Ticker对象获取存活时间的目的是防止修改系统时间导致回退
return
_ticker
.
createdTime
()
/
1000
;
return
_ticker
.
createdTime
()
/
1000
;
}
}
vector
<
Track
::
Ptr
>
MediaSource
::
getTracks
(
bool
ready
)
const
{
vector
<
Track
::
Ptr
>
MediaSource
::
getTracks
(
bool
ready
)
const
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
){
if
(
!
listener
){
return
vector
<
Track
::
Ptr
>
();
return
vector
<
Track
::
Ptr
>
();
}
}
return
listener
->
getTracks
(
const_cast
<
MediaSource
&>
(
*
this
),
ready
);
return
listener
->
getTracks
(
const_cast
<
MediaSource
&>
(
*
this
),
ready
);
}
}
void
MediaSource
::
setListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
){
void
MediaSource
::
setListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
){
_listener
=
listener
;
_listener
=
listener
;
}
}
std
::
weak_ptr
<
MediaSourceEvent
>
MediaSource
::
getListener
(
bool
next
)
const
{
std
::
weak_ptr
<
MediaSourceEvent
>
MediaSource
::
getListener
(
bool
next
)
const
{
if
(
!
next
)
{
if
(
!
next
)
{
return
_listener
;
return
_listener
;
}
}
auto
listener
=
dynamic_pointer_cast
<
MediaSourceEventInterceptor
>
(
_listener
.
lock
());
auto
listener
=
dynamic_pointer_cast
<
MediaSourceEventInterceptor
>
(
_listener
.
lock
());
if
(
!
listener
)
{
if
(
!
listener
)
{
//不是MediaSourceEventInterceptor对象或者对象已经销毁
//不是MediaSourceEventInterceptor对象或者对象已经销毁
return
_listener
;
return
_listener
;
}
}
//获取被拦截的对象
//获取被拦截的对象
auto
next_obj
=
listener
->
getDelegate
();
auto
next_obj
=
listener
->
getDelegate
();
//有则返回之
//有则返回之
return
next_obj
?
next_obj
:
_listener
;
return
next_obj
?
next_obj
:
_listener
;
}
}
int
MediaSource
::
totalReaderCount
(){
int
MediaSource
::
totalReaderCount
(){
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
){
if
(
!
listener
){
return
readerCount
();
return
readerCount
();
}
}
return
listener
->
totalReaderCount
(
*
this
);
return
listener
->
totalReaderCount
(
*
this
);
}
}
MediaOriginType
MediaSource
::
getOriginType
()
const
{
MediaOriginType
MediaSource
::
getOriginType
()
const
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
MediaOriginType
::
unknown
;
return
MediaOriginType
::
unknown
;
}
}
return
listener
->
getOriginType
(
const_cast
<
MediaSource
&>
(
*
this
));
return
listener
->
getOriginType
(
const_cast
<
MediaSource
&>
(
*
this
));
}
}
string
MediaSource
::
getOriginUrl
()
const
{
string
MediaSource
::
getOriginUrl
()
const
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
""
;
return
""
;
}
}
return
listener
->
getOriginUrl
(
const_cast
<
MediaSource
&>
(
*
this
));
return
listener
->
getOriginUrl
(
const_cast
<
MediaSource
&>
(
*
this
));
}
}
std
::
shared_ptr
<
SockInfo
>
MediaSource
::
getOriginSock
()
const
{
std
::
shared_ptr
<
SockInfo
>
MediaSource
::
getOriginSock
()
const
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
nullptr
;
return
nullptr
;
}
}
return
listener
->
getOriginSock
(
const_cast
<
MediaSource
&>
(
*
this
));
return
listener
->
getOriginSock
(
const_cast
<
MediaSource
&>
(
*
this
));
}
}
bool
MediaSource
::
seekTo
(
uint32_t
stamp
)
{
bool
MediaSource
::
seekTo
(
uint32_t
stamp
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
){
if
(
!
listener
){
return
false
;
return
false
;
}
}
return
listener
->
seekTo
(
*
this
,
stamp
);
return
listener
->
seekTo
(
*
this
,
stamp
);
}
}
bool
MediaSource
::
close
(
bool
force
)
{
bool
MediaSource
::
close
(
bool
force
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
){
if
(
!
listener
){
return
false
;
return
false
;
}
}
return
listener
->
close
(
*
this
,
force
);
return
listener
->
close
(
*
this
,
force
);
}
}
void
MediaSource
::
onReaderChanged
(
int
size
)
{
void
MediaSource
::
onReaderChanged
(
int
size
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
listener
)
{
if
(
listener
)
{
listener
->
onReaderChanged
(
*
this
,
size
);
listener
->
onReaderChanged
(
*
this
,
size
);
}
}
}
}
bool
MediaSource
::
setupRecord
(
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
){
bool
MediaSource
::
setupRecord
(
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
){
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
WarnL
<<
"未设置MediaSource的事件监听者,setupRecord失败:"
<<
getSchema
()
<<
"/"
<<
getVhost
()
<<
"/"
<<
getApp
()
<<
"/"
<<
getId
();
WarnL
<<
"未设置MediaSource的事件监听者,setupRecord失败:"
<<
getSchema
()
<<
"/"
<<
getVhost
()
<<
"/"
<<
getApp
()
<<
"/"
<<
getId
();
return
false
;
return
false
;
}
}
return
listener
->
setupRecord
(
*
this
,
type
,
start
,
custom_path
);
return
listener
->
setupRecord
(
*
this
,
type
,
start
,
custom_path
);
}
}
bool
MediaSource
::
isRecording
(
Recorder
::
type
type
){
bool
MediaSource
::
isRecording
(
Recorder
::
type
type
){
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
){
if
(
!
listener
){
return
false
;
return
false
;
}
}
return
listener
->
isRecording
(
*
this
,
type
);
return
listener
->
isRecording
(
*
this
,
type
);
}
}
void
MediaSource
::
startSendRtp
(
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
){
void
MediaSource
::
startSendRtp
(
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
){
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
cb
(
SockException
(
Err_other
,
"尚未设置事件监听器"
));
cb
(
SockException
(
Err_other
,
"尚未设置事件监听器"
));
return
;
return
;
}
}
return
listener
->
startSendRtp
(
*
this
,
dst_url
,
dst_port
,
ssrc
,
is_udp
,
cb
);
return
listener
->
startSendRtp
(
*
this
,
dst_url
,
dst_port
,
ssrc
,
is_udp
,
src_port
,
cb
);
}
}
bool
MediaSource
::
stopSendRtp
(
)
{
bool
MediaSource
::
stopSendRtp
(
const
string
&
ssrc
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
false
;
return
false
;
}
}
return
listener
->
stopSendRtp
(
*
this
);
return
listener
->
stopSendRtp
(
*
this
,
ssrc
);
}
}
void
MediaSource
::
for_each_media
(
const
function
<
void
(
const
MediaSource
::
Ptr
&
src
)
>
&
cb
)
{
void
MediaSource
::
for_each_media
(
const
function
<
void
(
const
MediaSource
::
Ptr
&
src
)
>
&
cb
)
{
decltype
(
s_media_source_map
)
copy
;
decltype
(
s_media_source_map
)
copy
;
{
{
//拷贝s_media_source_map后再遍历,考虑到是高频使用的全局单例锁,并且在上锁时会执行回调代码
//拷贝s_media_source_map后再遍历,考虑到是高频使用的全局单例锁,并且在上锁时会执行回调代码
//很容易导致多个锁交叉死锁的情况,而且该函数使用频率不高,拷贝开销相对来说是可以接受的
//很容易导致多个锁交叉死锁的情况,而且该函数使用频率不高,拷贝开销相对来说是可以接受的
lock_guard
<
recursive_mutex
>
lock
(
s_media_source_mtx
);
lock_guard
<
recursive_mutex
>
lock
(
s_media_source_mtx
);
copy
=
s_media_source_map
;
copy
=
s_media_source_map
;
}
}
for
(
auto
&
pr0
:
copy
)
{
for
(
auto
&
pr0
:
copy
)
{
for
(
auto
&
pr1
:
pr0
.
second
)
{
for
(
auto
&
pr1
:
pr0
.
second
)
{
for
(
auto
&
pr2
:
pr1
.
second
)
{
for
(
auto
&
pr2
:
pr1
.
second
)
{
for
(
auto
&
pr3
:
pr2
.
second
)
{
for
(
auto
&
pr3
:
pr2
.
second
)
{
auto
src
=
pr3
.
second
.
lock
();
auto
src
=
pr3
.
second
.
lock
();
if
(
src
){
if
(
src
){
cb
(
src
);
cb
(
src
);
}
}
}
}
}
}
}
}
}
}
}
}
template
<
typename
MAP
,
typename
FUNC
>
template
<
typename
MAP
,
typename
FUNC
>
static
bool
searchMedia
(
MAP
&
map
,
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
id
,
FUNC
&&
func
)
{
static
bool
searchMedia
(
MAP
&
map
,
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
id
,
FUNC
&&
func
)
{
auto
it0
=
map
.
find
(
schema
);
auto
it0
=
map
.
find
(
schema
);
if
(
it0
==
map
.
end
())
{
if
(
it0
==
map
.
end
())
{
//未找到协议
//未找到协议
return
false
;
return
false
;
}
}
auto
it1
=
it0
->
second
.
find
(
vhost
);
auto
it1
=
it0
->
second
.
find
(
vhost
);
if
(
it1
==
it0
->
second
.
end
())
{
if
(
it1
==
it0
->
second
.
end
())
{
//未找到vhost
//未找到vhost
return
false
;
return
false
;
}
}
auto
it2
=
it1
->
second
.
find
(
app
);
auto
it2
=
it1
->
second
.
find
(
app
);
if
(
it2
==
it1
->
second
.
end
())
{
if
(
it2
==
it1
->
second
.
end
())
{
//未找到app
//未找到app
return
false
;
return
false
;
}
}
auto
it3
=
it2
->
second
.
find
(
id
);
auto
it3
=
it2
->
second
.
find
(
id
);
if
(
it3
==
it2
->
second
.
end
())
{
if
(
it3
==
it2
->
second
.
end
())
{
//未找到streamId
//未找到streamId
return
false
;
return
false
;
}
}
return
func
(
it0
,
it1
,
it2
,
it3
);
return
func
(
it0
,
it1
,
it2
,
it3
);
}
}
template
<
typename
MAP
,
typename
IT0
,
typename
IT1
,
typename
IT2
>
template
<
typename
MAP
,
typename
IT0
,
typename
IT1
,
typename
IT2
>
static
void
eraseIfEmpty
(
MAP
&
map
,
IT0
it0
,
IT1
it1
,
IT2
it2
)
{
static
void
eraseIfEmpty
(
MAP
&
map
,
IT0
it0
,
IT1
it1
,
IT2
it2
)
{
if
(
it2
->
second
.
empty
())
{
if
(
it2
->
second
.
empty
())
{
it1
->
second
.
erase
(
it2
);
it1
->
second
.
erase
(
it2
);
if
(
it1
->
second
.
empty
())
{
if
(
it1
->
second
.
empty
())
{
it0
->
second
.
erase
(
it1
);
it0
->
second
.
erase
(
it1
);
if
(
it0
->
second
.
empty
())
{
if
(
it0
->
second
.
empty
())
{
map
.
erase
(
it0
);
map
.
erase
(
it0
);
}
}
}
}
}
}
}
}
static
MediaSource
::
Ptr
find_l
(
const
string
&
schema
,
const
string
&
vhost_in
,
const
string
&
app
,
const
string
&
id
,
bool
create_new
)
{
static
MediaSource
::
Ptr
find_l
(
const
string
&
schema
,
const
string
&
vhost_in
,
const
string
&
app
,
const
string
&
id
,
bool
create_new
)
{
string
vhost
=
vhost_in
;
string
vhost
=
vhost_in
;
GET_CONFIG
(
bool
,
enableVhost
,
General
::
kEnableVhost
);
GET_CONFIG
(
bool
,
enableVhost
,
General
::
kEnableVhost
);
if
(
vhost
.
empty
()
||
!
enableVhost
){
if
(
vhost
.
empty
()
||
!
enableVhost
){
vhost
=
DEFAULT_VHOST
;
vhost
=
DEFAULT_VHOST
;
}
}
MediaSource
::
Ptr
ret
;
MediaSource
::
Ptr
ret
;
{
{
lock_guard
<
recursive_mutex
>
lock
(
s_media_source_mtx
);
lock_guard
<
recursive_mutex
>
lock
(
s_media_source_mtx
);
//查找某一媒体源,找到后返回
//查找某一媒体源,找到后返回
searchMedia
(
s_media_source_map
,
schema
,
vhost
,
app
,
id
,
searchMedia
(
s_media_source_map
,
schema
,
vhost
,
app
,
id
,
[
&
](
MediaSource
::
SchemaVhostAppStreamMap
::
iterator
&
it0
,
MediaSource
::
VhostAppStreamMap
::
iterator
&
it1
,
[
&
](
MediaSource
::
SchemaVhostAppStreamMap
::
iterator
&
it0
,
MediaSource
::
VhostAppStreamMap
::
iterator
&
it1
,
MediaSource
::
AppStreamMap
::
iterator
&
it2
,
MediaSource
::
StreamMap
::
iterator
&
it3
)
{
MediaSource
::
AppStreamMap
::
iterator
&
it2
,
MediaSource
::
StreamMap
::
iterator
&
it3
)
{
ret
=
it3
->
second
.
lock
();
ret
=
it3
->
second
.
lock
();
if
(
!
ret
)
{
if
(
!
ret
)
{
//该对象已经销毁
//该对象已经销毁
it2
->
second
.
erase
(
it3
);
it2
->
second
.
erase
(
it3
);
eraseIfEmpty
(
s_media_source_map
,
it0
,
it1
,
it2
);
eraseIfEmpty
(
s_media_source_map
,
it0
,
it1
,
it2
);
return
false
;
return
false
;
}
}
return
true
;
return
true
;
});
});
}
}
if
(
!
ret
&&
create_new
&&
schema
!=
HLS_SCHEMA
){
if
(
!
ret
&&
create_new
&&
schema
!=
HLS_SCHEMA
){
//未查找媒体源,则读取mp4创建一个
//未查找媒体源,则读取mp4创建一个
//播放hls不触发mp4点播(因为HLS也可以用于录像,不是纯粹的直播)
//播放hls不触发mp4点播(因为HLS也可以用于录像,不是纯粹的直播)
ret
=
MediaSource
::
createFromMP4
(
schema
,
vhost
,
app
,
id
);
ret
=
MediaSource
::
createFromMP4
(
schema
,
vhost
,
app
,
id
);
}
}
return
ret
;
return
ret
;
}
}
static
void
findAsync_l
(
const
MediaInfo
&
info
,
const
std
::
shared_ptr
<
TcpSession
>
&
session
,
bool
retry
,
static
void
findAsync_l
(
const
MediaInfo
&
info
,
const
std
::
shared_ptr
<
TcpSession
>
&
session
,
bool
retry
,
const
function
<
void
(
const
MediaSource
::
Ptr
&
src
)
>
&
cb
){
const
function
<
void
(
const
MediaSource
::
Ptr
&
src
)
>
&
cb
){
auto
src
=
find_l
(
info
.
_schema
,
info
.
_vhost
,
info
.
_app
,
info
.
_streamid
,
true
);
auto
src
=
find_l
(
info
.
_schema
,
info
.
_vhost
,
info
.
_app
,
info
.
_streamid
,
true
);
if
(
src
||
!
retry
)
{
if
(
src
||
!
retry
)
{
cb
(
src
);
cb
(
src
);
return
;
return
;
}
}
void
*
listener_tag
=
session
.
get
();
void
*
listener_tag
=
session
.
get
();
weak_ptr
<
TcpSession
>
weak_session
=
session
;
weak_ptr
<
TcpSession
>
weak_session
=
session
;
GET_CONFIG
(
int
,
maxWaitMS
,
General
::
kMaxStreamWaitTimeMS
);
GET_CONFIG
(
int
,
maxWaitMS
,
General
::
kMaxStreamWaitTimeMS
);
auto
on_timeout
=
session
->
getPoller
()
->
doDelayTask
(
maxWaitMS
,
[
cb
,
listener_tag
]()
{
auto
on_timeout
=
session
->
getPoller
()
->
doDelayTask
(
maxWaitMS
,
[
cb
,
listener_tag
]()
{
//最多等待一定时间,如果这个时间内,流未注册上,那么返回未找到流
//最多等待一定时间,如果这个时间内,流未注册上,那么返回未找到流
NoticeCenter
::
Instance
().
delListener
(
listener_tag
,
Broadcast
::
kBroadcastMediaChanged
);
NoticeCenter
::
Instance
().
delListener
(
listener_tag
,
Broadcast
::
kBroadcastMediaChanged
);
cb
(
nullptr
);
cb
(
nullptr
);
return
0
;
return
0
;
});
});
auto
cancel_all
=
[
on_timeout
,
listener_tag
]()
{
auto
cancel_all
=
[
on_timeout
,
listener_tag
]()
{
//取消延时任务,防止多次回调
//取消延时任务,防止多次回调
on_timeout
->
cancel
();
on_timeout
->
cancel
();
//取消媒体注册事件监听
//取消媒体注册事件监听
NoticeCenter
::
Instance
().
delListener
(
listener_tag
,
Broadcast
::
kBroadcastMediaChanged
);
NoticeCenter
::
Instance
().
delListener
(
listener_tag
,
Broadcast
::
kBroadcastMediaChanged
);
};
};
function
<
void
()
>
close_player
=
[
cb
,
cancel_all
]()
{
function
<
void
()
>
close_player
=
[
cb
,
cancel_all
]()
{
cancel_all
();
cancel_all
();
//告诉播放器,流不存在,这样会立即断开播放器
//告诉播放器,流不存在,这样会立即断开播放器
cb
(
nullptr
);
cb
(
nullptr
);
};
};
auto
on_regist
=
[
weak_session
,
info
,
cb
,
cancel_all
](
BroadcastMediaChangedArgs
)
{
auto
on_regist
=
[
weak_session
,
info
,
cb
,
cancel_all
](
BroadcastMediaChangedArgs
)
{
auto
strong_session
=
weak_session
.
lock
();
auto
strong_session
=
weak_session
.
lock
();
if
(
!
strong_session
)
{
if
(
!
strong_session
)
{
//自己已经销毁
//自己已经销毁
cancel_all
();
cancel_all
();
return
;
return
;
}
}
if
(
!
bRegist
||
if
(
!
bRegist
||
sender
.
getSchema
()
!=
info
.
_schema
||
sender
.
getSchema
()
!=
info
.
_schema
||
sender
.
getVhost
()
!=
info
.
_vhost
||
sender
.
getVhost
()
!=
info
.
_vhost
||
sender
.
getApp
()
!=
info
.
_app
||
sender
.
getApp
()
!=
info
.
_app
||
sender
.
getId
()
!=
info
.
_streamid
)
{
sender
.
getId
()
!=
info
.
_streamid
)
{
//不是自己感兴趣的事件,忽略之
//不是自己感兴趣的事件,忽略之
return
;
return
;
}
}
cancel_all
();
cancel_all
();
//播发器请求的流终于注册上了,切换到自己的线程再回复
//播发器请求的流终于注册上了,切换到自己的线程再回复
strong_session
->
async
([
weak_session
,
info
,
cb
]()
{
strong_session
->
async
([
weak_session
,
info
,
cb
]()
{
auto
strongSession
=
weak_session
.
lock
();
auto
strongSession
=
weak_session
.
lock
();
if
(
!
strongSession
)
{
if
(
!
strongSession
)
{
return
;
return
;
}
}
DebugL
<<
"收到媒体注册事件,回复播放器:"
<<
info
.
_schema
<<
"/"
<<
info
.
_vhost
<<
"/"
<<
info
.
_app
<<
"/"
<<
info
.
_streamid
;
DebugL
<<
"收到媒体注册事件,回复播放器:"
<<
info
.
_schema
<<
"/"
<<
info
.
_vhost
<<
"/"
<<
info
.
_app
<<
"/"
<<
info
.
_streamid
;
//再找一遍媒体源,一般能找到
//再找一遍媒体源,一般能找到
findAsync_l
(
info
,
strongSession
,
false
,
cb
);
findAsync_l
(
info
,
strongSession
,
false
,
cb
);
},
false
);
},
false
);
};
};
//监听媒体注册事件
//监听媒体注册事件
NoticeCenter
::
Instance
().
addListener
(
listener_tag
,
Broadcast
::
kBroadcastMediaChanged
,
on_regist
);
NoticeCenter
::
Instance
().
addListener
(
listener_tag
,
Broadcast
::
kBroadcastMediaChanged
,
on_regist
);
//广播未找到流,此时可以立即去拉流,这样还来得及
//广播未找到流,此时可以立即去拉流,这样还来得及
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastNotFoundStream
,
info
,
static_cast
<
SockInfo
&>
(
*
session
),
close_player
);
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastNotFoundStream
,
info
,
static_cast
<
SockInfo
&>
(
*
session
),
close_player
);
}
}
void
MediaSource
::
findAsync
(
const
MediaInfo
&
info
,
const
std
::
shared_ptr
<
TcpSession
>
&
session
,
const
function
<
void
(
const
Ptr
&
src
)
>
&
cb
){
void
MediaSource
::
findAsync
(
const
MediaInfo
&
info
,
const
std
::
shared_ptr
<
TcpSession
>
&
session
,
const
function
<
void
(
const
Ptr
&
src
)
>
&
cb
){
return
findAsync_l
(
info
,
session
,
true
,
cb
);
return
findAsync_l
(
info
,
session
,
true
,
cb
);
}
}
MediaSource
::
Ptr
MediaSource
::
find
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
id
)
{
MediaSource
::
Ptr
MediaSource
::
find
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
id
)
{
return
find_l
(
schema
,
vhost
,
app
,
id
,
false
);
return
find_l
(
schema
,
vhost
,
app
,
id
,
false
);
}
}
MediaSource
::
Ptr
MediaSource
::
find
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
){
MediaSource
::
Ptr
MediaSource
::
find
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
){
auto
src
=
MediaSource
::
find
(
RTMP_SCHEMA
,
vhost
,
app
,
stream_id
);
auto
src
=
MediaSource
::
find
(
RTMP_SCHEMA
,
vhost
,
app
,
stream_id
);
if
(
src
)
{
if
(
src
)
{
return
src
;
return
src
;
}
}
src
=
MediaSource
::
find
(
RTSP_SCHEMA
,
vhost
,
app
,
stream_id
);
src
=
MediaSource
::
find
(
RTSP_SCHEMA
,
vhost
,
app
,
stream_id
);
if
(
src
)
{
if
(
src
)
{
return
src
;
return
src
;
}
}
return
MediaSource
::
find
(
HLS_SCHEMA
,
vhost
,
app
,
stream_id
);
return
MediaSource
::
find
(
HLS_SCHEMA
,
vhost
,
app
,
stream_id
);
}
}
void
MediaSource
::
emitEvent
(
bool
regist
){
void
MediaSource
::
emitEvent
(
bool
regist
){
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
listener
)
{
if
(
listener
)
{
//触发回调
//触发回调
listener
->
onRegist
(
*
this
,
regist
);
listener
->
onRegist
(
*
this
,
regist
);
}
}
//触发广播
//触发广播
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastMediaChanged
,
regist
,
*
this
);
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastMediaChanged
,
regist
,
*
this
);
InfoL
<<
(
regist
?
"媒体注册:"
:
"媒体注销:"
)
<<
_schema
<<
" "
<<
_vhost
<<
" "
<<
_app
<<
" "
<<
_stream_id
;
InfoL
<<
(
regist
?
"媒体注册:"
:
"媒体注销:"
)
<<
_schema
<<
" "
<<
_vhost
<<
" "
<<
_app
<<
" "
<<
_stream_id
;
}
}
void
MediaSource
::
regist
()
{
void
MediaSource
::
regist
()
{
{
{
//减小互斥锁临界区
//减小互斥锁临界区
lock_guard
<
recursive_mutex
>
lock
(
s_media_source_mtx
);
lock_guard
<
recursive_mutex
>
lock
(
s_media_source_mtx
);
s_media_source_map
[
_schema
][
_vhost
][
_app
][
_stream_id
]
=
shared_from_this
();
s_media_source_map
[
_schema
][
_vhost
][
_app
][
_stream_id
]
=
shared_from_this
();
}
}
emitEvent
(
true
);
emitEvent
(
true
);
}
}
//反注册该源
//反注册该源
bool
MediaSource
::
unregist
()
{
bool
MediaSource
::
unregist
()
{
bool
ret
;
bool
ret
;
{
{
//减小互斥锁临界区
//减小互斥锁临界区
lock_guard
<
recursive_mutex
>
lock
(
s_media_source_mtx
);
lock_guard
<
recursive_mutex
>
lock
(
s_media_source_mtx
);
ret
=
searchMedia
(
s_media_source_map
,
_schema
,
_vhost
,
_app
,
_stream_id
,
ret
=
searchMedia
(
s_media_source_map
,
_schema
,
_vhost
,
_app
,
_stream_id
,
[
&
](
SchemaVhostAppStreamMap
::
iterator
&
it0
,
VhostAppStreamMap
::
iterator
&
it1
,
[
&
](
SchemaVhostAppStreamMap
::
iterator
&
it0
,
VhostAppStreamMap
::
iterator
&
it1
,
AppStreamMap
::
iterator
&
it2
,
StreamMap
::
iterator
&
it3
)
{
AppStreamMap
::
iterator
&
it2
,
StreamMap
::
iterator
&
it3
)
{
auto
strong_self
=
it3
->
second
.
lock
();
auto
strong_self
=
it3
->
second
.
lock
();
if
(
strong_self
&&
this
!=
strong_self
.
get
())
{
if
(
strong_self
&&
this
!=
strong_self
.
get
())
{
//不是自己,不允许反注册
//不是自己,不允许反注册
return
false
;
return
false
;
}
}
it2
->
second
.
erase
(
it3
);
it2
->
second
.
erase
(
it3
);
eraseIfEmpty
(
s_media_source_map
,
it0
,
it1
,
it2
);
eraseIfEmpty
(
s_media_source_map
,
it0
,
it1
,
it2
);
return
true
;
return
true
;
});
});
}
}
if
(
ret
)
{
if
(
ret
)
{
emitEvent
(
false
);
emitEvent
(
false
);
}
}
return
ret
;
return
ret
;
}
}
/////////////////////////////////////MediaInfo//////////////////////////////////////
/////////////////////////////////////MediaInfo//////////////////////////////////////
void
MediaInfo
::
parse
(
const
string
&
url_in
){
void
MediaInfo
::
parse
(
const
string
&
url_in
){
_full_url
=
url_in
;
_full_url
=
url_in
;
string
url
=
url_in
;
string
url
=
url_in
;
auto
pos
=
url
.
find
(
"?"
);
auto
pos
=
url
.
find
(
"?"
);
if
(
pos
!=
string
::
npos
)
{
if
(
pos
!=
string
::
npos
)
{
_param_strs
=
url
.
substr
(
pos
+
1
);
_param_strs
=
url
.
substr
(
pos
+
1
);
url
.
erase
(
pos
);
url
.
erase
(
pos
);
}
}
auto
schema_pos
=
url
.
find
(
"://"
);
auto
schema_pos
=
url
.
find
(
"://"
);
if
(
schema_pos
!=
string
::
npos
)
{
if
(
schema_pos
!=
string
::
npos
)
{
_schema
=
url
.
substr
(
0
,
schema_pos
);
_schema
=
url
.
substr
(
0
,
schema_pos
);
}
else
{
}
else
{
schema_pos
=
-
3
;
schema_pos
=
-
3
;
}
}
auto
split_vec
=
split
(
url
.
substr
(
schema_pos
+
3
),
"/"
);
auto
split_vec
=
split
(
url
.
substr
(
schema_pos
+
3
),
"/"
);
if
(
split_vec
.
size
()
>
0
)
{
if
(
split_vec
.
size
()
>
0
)
{
auto
vhost
=
split_vec
[
0
];
auto
vhost
=
split_vec
[
0
];
auto
pos
=
vhost
.
find
(
":"
);
auto
pos
=
vhost
.
find
(
":"
);
if
(
pos
!=
string
::
npos
)
{
if
(
pos
!=
string
::
npos
)
{
_host
=
_vhost
=
vhost
.
substr
(
0
,
pos
);
_host
=
_vhost
=
vhost
.
substr
(
0
,
pos
);
_port
=
vhost
.
substr
(
pos
+
1
);
_port
=
vhost
.
substr
(
pos
+
1
);
}
else
{
}
else
{
_host
=
_vhost
=
vhost
;
_host
=
_vhost
=
vhost
;
}
}
if
(
_vhost
==
"localhost"
||
INADDR_NONE
!=
inet_addr
(
_vhost
.
data
()))
{
if
(
_vhost
==
"localhost"
||
INADDR_NONE
!=
inet_addr
(
_vhost
.
data
()))
{
//如果访问的是localhost或ip,那么则为默认虚拟主机
//如果访问的是localhost或ip,那么则为默认虚拟主机
_vhost
=
DEFAULT_VHOST
;
_vhost
=
DEFAULT_VHOST
;
}
}
}
}
if
(
split_vec
.
size
()
>
1
)
{
if
(
split_vec
.
size
()
>
1
)
{
_app
=
split_vec
[
1
];
_app
=
split_vec
[
1
];
}
}
if
(
split_vec
.
size
()
>
2
)
{
if
(
split_vec
.
size
()
>
2
)
{
string
stream_id
;
string
stream_id
;
for
(
int
i
=
2
;
i
<
split_vec
.
size
();
++
i
)
{
for
(
int
i
=
2
;
i
<
split_vec
.
size
();
++
i
)
{
stream_id
.
append
(
split_vec
[
i
]
+
"/"
);
stream_id
.
append
(
split_vec
[
i
]
+
"/"
);
}
}
if
(
stream_id
.
back
()
==
'/'
)
{
if
(
stream_id
.
back
()
==
'/'
)
{
stream_id
.
pop_back
();
stream_id
.
pop_back
();
}
}
_streamid
=
stream_id
;
_streamid
=
stream_id
;
}
}
auto
params
=
Parser
::
parseArgs
(
_param_strs
);
auto
params
=
Parser
::
parseArgs
(
_param_strs
);
if
(
params
.
find
(
VHOST_KEY
)
!=
params
.
end
())
{
if
(
params
.
find
(
VHOST_KEY
)
!=
params
.
end
())
{
_vhost
=
params
[
VHOST_KEY
];
_vhost
=
params
[
VHOST_KEY
];
}
}
GET_CONFIG
(
bool
,
enableVhost
,
General
::
kEnableVhost
);
GET_CONFIG
(
bool
,
enableVhost
,
General
::
kEnableVhost
);
if
(
!
enableVhost
||
_vhost
.
empty
())
{
if
(
!
enableVhost
||
_vhost
.
empty
())
{
//如果关闭虚拟主机或者虚拟主机为空,则设置虚拟主机为默认
//如果关闭虚拟主机或者虚拟主机为空,则设置虚拟主机为默认
_vhost
=
DEFAULT_VHOST
;
_vhost
=
DEFAULT_VHOST
;
}
}
}
}
MediaSource
::
Ptr
MediaSource
::
createFromMP4
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
const
string
&
file_path
,
bool
check_app
){
MediaSource
::
Ptr
MediaSource
::
createFromMP4
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
const
string
&
file_path
,
bool
check_app
){
GET_CONFIG
(
string
,
appName
,
Record
::
kAppName
);
GET_CONFIG
(
string
,
appName
,
Record
::
kAppName
);
if
(
check_app
&&
app
!=
appName
)
{
if
(
check_app
&&
app
!=
appName
)
{
return
nullptr
;
return
nullptr
;
}
}
#ifdef ENABLE_MP4
#ifdef ENABLE_MP4
try
{
try
{
MP4Reader
::
Ptr
pReader
(
new
MP4Reader
(
vhost
,
app
,
stream
,
file_path
));
MP4Reader
::
Ptr
pReader
(
new
MP4Reader
(
vhost
,
app
,
stream
,
file_path
));
pReader
->
startReadMP4
();
pReader
->
startReadMP4
();
return
MediaSource
::
find
(
schema
,
vhost
,
app
,
stream
);
return
MediaSource
::
find
(
schema
,
vhost
,
app
,
stream
);
}
catch
(
std
::
exception
&
ex
)
{
}
catch
(
std
::
exception
&
ex
)
{
WarnL
<<
ex
.
what
();
WarnL
<<
ex
.
what
();
return
nullptr
;
return
nullptr
;
}
}
#else
#else
WarnL
<<
"创建MP4点播失败,请编译时打开
\"
ENABLE_MP4
\"
选项"
;
WarnL
<<
"创建MP4点播失败,请编译时打开
\"
ENABLE_MP4
\"
选项"
;
return
nullptr
;
return
nullptr
;
#endif //ENABLE_MP4
#endif //ENABLE_MP4
}
}
/////////////////////////////////////MediaSourceEvent//////////////////////////////////////
/////////////////////////////////////MediaSourceEvent//////////////////////////////////////
void
MediaSourceEvent
::
onReaderChanged
(
MediaSource
&
sender
,
int
size
){
void
MediaSourceEvent
::
onReaderChanged
(
MediaSource
&
sender
,
int
size
){
if
(
size
||
totalReaderCount
(
sender
))
{
if
(
size
||
totalReaderCount
(
sender
))
{
//还有人观看该视频,不触发关闭事件
//还有人观看该视频,不触发关闭事件
return
;
return
;
}
}
//没有任何人观看该视频源,表明该源可以关闭了
//没有任何人观看该视频源,表明该源可以关闭了
GET_CONFIG
(
string
,
record_app
,
Record
::
kAppName
);
GET_CONFIG
(
string
,
record_app
,
Record
::
kAppName
);
GET_CONFIG
(
int
,
stream_none_reader_delay
,
General
::
kStreamNoneReaderDelayMS
);
GET_CONFIG
(
int
,
stream_none_reader_delay
,
General
::
kStreamNoneReaderDelayMS
);
//如果mp4点播, 无人观看时我们强制关闭点播
//如果mp4点播, 无人观看时我们强制关闭点播
bool
is_mp4_vod
=
sender
.
getApp
()
==
record_app
;
bool
is_mp4_vod
=
sender
.
getApp
()
==
record_app
;
weak_ptr
<
MediaSource
>
weak_sender
=
sender
.
shared_from_this
();
weak_ptr
<
MediaSource
>
weak_sender
=
sender
.
shared_from_this
();
_async_close_timer
=
std
::
make_shared
<
Timer
>
(
stream_none_reader_delay
/
1000.0
,
[
weak_sender
,
is_mp4_vod
]()
{
_async_close_timer
=
std
::
make_shared
<
Timer
>
(
stream_none_reader_delay
/
1000.0
,
[
weak_sender
,
is_mp4_vod
]()
{
auto
strong_sender
=
weak_sender
.
lock
();
auto
strong_sender
=
weak_sender
.
lock
();
if
(
!
strong_sender
)
{
if
(
!
strong_sender
)
{
//对象已经销毁
//对象已经销毁
return
false
;
return
false
;
}
}
if
(
strong_sender
->
totalReaderCount
())
{
if
(
strong_sender
->
totalReaderCount
())
{
//还有人观看该视频,不触发关闭事件
//还有人观看该视频,不触发关闭事件
return
false
;
return
false
;
}
}
if
(
!
is_mp4_vod
)
{
if
(
!
is_mp4_vod
)
{
//直播时触发无人观看事件,让开发者自行选择是否关闭
//直播时触发无人观看事件,让开发者自行选择是否关闭
WarnL
<<
"无人观看事件:"
WarnL
<<
"无人观看事件:"
<<
strong_sender
->
getSchema
()
<<
"/"
<<
strong_sender
->
getSchema
()
<<
"/"
<<
strong_sender
->
getVhost
()
<<
"/"
<<
strong_sender
->
getVhost
()
<<
"/"
<<
strong_sender
->
getApp
()
<<
"/"
<<
strong_sender
->
getApp
()
<<
"/"
<<
strong_sender
->
getId
();
<<
strong_sender
->
getId
();
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastStreamNoneReader
,
*
strong_sender
);
NoticeCenter
::
Instance
().
emitEvent
(
Broadcast
::
kBroadcastStreamNoneReader
,
*
strong_sender
);
}
else
{
}
else
{
//这个是mp4点播,我们自动关闭
//这个是mp4点播,我们自动关闭
WarnL
<<
"MP4点播无人观看,自动关闭:"
WarnL
<<
"MP4点播无人观看,自动关闭:"
<<
strong_sender
->
getSchema
()
<<
"/"
<<
strong_sender
->
getSchema
()
<<
"/"
<<
strong_sender
->
getVhost
()
<<
"/"
<<
strong_sender
->
getVhost
()
<<
"/"
<<
strong_sender
->
getApp
()
<<
"/"
<<
strong_sender
->
getApp
()
<<
"/"
<<
strong_sender
->
getId
();
<<
strong_sender
->
getId
();
strong_sender
->
close
(
false
);
strong_sender
->
close
(
false
);
}
}
return
false
;
return
false
;
},
nullptr
);
},
nullptr
);
}
}
MediaOriginType
MediaSourceEventInterceptor
::
getOriginType
(
MediaSource
&
sender
)
const
{
MediaOriginType
MediaSourceEventInterceptor
::
getOriginType
(
MediaSource
&
sender
)
const
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
MediaOriginType
::
unknown
;
return
MediaOriginType
::
unknown
;
}
}
return
listener
->
getOriginType
(
sender
);
return
listener
->
getOriginType
(
sender
);
}
}
string
MediaSourceEventInterceptor
::
getOriginUrl
(
MediaSource
&
sender
)
const
{
string
MediaSourceEventInterceptor
::
getOriginUrl
(
MediaSource
&
sender
)
const
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
""
;
return
""
;
}
}
return
listener
->
getOriginUrl
(
sender
);
return
listener
->
getOriginUrl
(
sender
);
}
}
std
::
shared_ptr
<
SockInfo
>
MediaSourceEventInterceptor
::
getOriginSock
(
MediaSource
&
sender
)
const
{
std
::
shared_ptr
<
SockInfo
>
MediaSourceEventInterceptor
::
getOriginSock
(
MediaSource
&
sender
)
const
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
nullptr
;
return
nullptr
;
}
}
return
listener
->
getOriginSock
(
sender
);
return
listener
->
getOriginSock
(
sender
);
}
}
bool
MediaSourceEventInterceptor
::
seekTo
(
MediaSource
&
sender
,
uint32_t
stamp
)
{
bool
MediaSourceEventInterceptor
::
seekTo
(
MediaSource
&
sender
,
uint32_t
stamp
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
false
;
return
false
;
}
}
return
listener
->
seekTo
(
sender
,
stamp
);
return
listener
->
seekTo
(
sender
,
stamp
);
}
}
bool
MediaSourceEventInterceptor
::
close
(
MediaSource
&
sender
,
bool
force
)
{
bool
MediaSourceEventInterceptor
::
close
(
MediaSource
&
sender
,
bool
force
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
false
;
return
false
;
}
}
return
listener
->
close
(
sender
,
force
);
return
listener
->
close
(
sender
,
force
);
}
}
int
MediaSourceEventInterceptor
::
totalReaderCount
(
MediaSource
&
sender
)
{
int
MediaSourceEventInterceptor
::
totalReaderCount
(
MediaSource
&
sender
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
sender
.
readerCount
();
return
sender
.
readerCount
();
}
}
return
listener
->
totalReaderCount
(
sender
);
return
listener
->
totalReaderCount
(
sender
);
}
}
void
MediaSourceEventInterceptor
::
onReaderChanged
(
MediaSource
&
sender
,
int
size
)
{
void
MediaSourceEventInterceptor
::
onReaderChanged
(
MediaSource
&
sender
,
int
size
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
MediaSourceEvent
::
onReaderChanged
(
sender
,
size
);
MediaSourceEvent
::
onReaderChanged
(
sender
,
size
);
}
else
{
}
else
{
listener
->
onReaderChanged
(
sender
,
size
);
listener
->
onReaderChanged
(
sender
,
size
);
}
}
}
}
void
MediaSourceEventInterceptor
::
onRegist
(
MediaSource
&
sender
,
bool
regist
)
{
void
MediaSourceEventInterceptor
::
onRegist
(
MediaSource
&
sender
,
bool
regist
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
listener
)
{
if
(
listener
)
{
listener
->
onRegist
(
sender
,
regist
);
listener
->
onRegist
(
sender
,
regist
);
}
}
}
}
bool
MediaSourceEventInterceptor
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
bool
MediaSourceEventInterceptor
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
false
;
return
false
;
}
}
return
listener
->
setupRecord
(
sender
,
type
,
start
,
custom_path
);
return
listener
->
setupRecord
(
sender
,
type
,
start
,
custom_path
);
}
}
bool
MediaSourceEventInterceptor
::
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
bool
MediaSourceEventInterceptor
::
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
false
;
return
false
;
}
}
return
listener
->
isRecording
(
sender
,
type
);
return
listener
->
isRecording
(
sender
,
type
);
}
}
vector
<
Track
::
Ptr
>
MediaSourceEventInterceptor
::
getTracks
(
MediaSource
&
sender
,
bool
trackReady
)
const
{
vector
<
Track
::
Ptr
>
MediaSourceEventInterceptor
::
getTracks
(
MediaSource
&
sender
,
bool
trackReady
)
const
{
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
vector
<
Track
::
Ptr
>
();
return
vector
<
Track
::
Ptr
>
();
}
}
return
listener
->
getTracks
(
sender
,
trackReady
);
return
listener
->
getTracks
(
sender
,
trackReady
);
}
}
void
MediaSourceEventInterceptor
::
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
){
void
MediaSourceEventInterceptor
::
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
){
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
listener
)
{
if
(
listener
)
{
listener
->
startSendRtp
(
sender
,
dst_url
,
dst_port
,
ssrc
,
is_udp
,
cb
);
listener
->
startSendRtp
(
sender
,
dst_url
,
dst_port
,
ssrc
,
is_udp
,
src_port
,
cb
);
}
else
{
}
else
{
MediaSourceEvent
::
startSendRtp
(
sender
,
dst_url
,
dst_port
,
ssrc
,
is_udp
,
cb
);
MediaSourceEvent
::
startSendRtp
(
sender
,
dst_url
,
dst_port
,
ssrc
,
is_udp
,
src_port
,
cb
);
}
}
}
}
bool
MediaSourceEventInterceptor
::
stopSendRtp
(
MediaSource
&
sender
){
bool
MediaSourceEventInterceptor
::
stopSendRtp
(
MediaSource
&
sender
,
const
string
&
ssrc
){
auto
listener
=
_listener
.
lock
();
auto
listener
=
_listener
.
lock
();
if
(
listener
)
{
if
(
listener
)
{
return
listener
->
stopSendRtp
(
sender
);
return
listener
->
stopSendRtp
(
sender
,
ssrc
);
}
}
return
false
;
return
false
;
}
}
void
MediaSourceEventInterceptor
::
setDelegate
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
)
{
void
MediaSourceEventInterceptor
::
setDelegate
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
)
{
if
(
listener
.
lock
().
get
()
==
this
)
{
if
(
listener
.
lock
().
get
()
==
this
)
{
throw
std
::
invalid_argument
(
"can not set self as a delegate"
);
throw
std
::
invalid_argument
(
"can not set self as a delegate"
);
}
}
_listener
=
listener
;
_listener
=
listener
;
}
}
std
::
shared_ptr
<
MediaSourceEvent
>
MediaSourceEventInterceptor
::
getDelegate
()
const
{
std
::
shared_ptr
<
MediaSourceEvent
>
MediaSourceEventInterceptor
::
getDelegate
()
const
{
return
_listener
.
lock
();
return
_listener
.
lock
();
}
}
/////////////////////////////////////FlushPolicy//////////////////////////////////////
/////////////////////////////////////FlushPolicy//////////////////////////////////////
static
bool
isFlushAble_default
(
bool
is_video
,
uint64_t
last_stamp
,
uint64_t
new_stamp
,
int
cache_size
)
{
static
bool
isFlushAble_default
(
bool
is_video
,
uint64_t
last_stamp
,
uint64_t
new_stamp
,
int
cache_size
)
{
if
(
new_stamp
+
500
<
last_stamp
)
{
if
(
new_stamp
+
500
<
last_stamp
)
{
//时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的
//时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的
return
true
;
return
true
;
}
}
//时间戳发送变化或者缓存超过1024个,sendmsg接口一般最多只能发送1024个数据包
//时间戳发送变化或者缓存超过1024个,sendmsg接口一般最多只能发送1024个数据包
return
last_stamp
!=
new_stamp
||
cache_size
>=
1024
;
return
last_stamp
!=
new_stamp
||
cache_size
>=
1024
;
}
}
static
bool
isFlushAble_merge
(
bool
is_video
,
uint64_t
last_stamp
,
uint64_t
new_stamp
,
int
cache_size
,
int
merge_ms
)
{
static
bool
isFlushAble_merge
(
bool
is_video
,
uint64_t
last_stamp
,
uint64_t
new_stamp
,
int
cache_size
,
int
merge_ms
)
{
if
(
new_stamp
+
500
<
last_stamp
)
{
if
(
new_stamp
+
500
<
last_stamp
)
{
//时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的
//时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的
return
true
;
return
true
;
}
}
if
(
new_stamp
>
last_stamp
+
merge_ms
)
{
if
(
new_stamp
>
last_stamp
+
merge_ms
)
{
//时间戳增量超过合并写阈值
//时间戳增量超过合并写阈值
return
true
;
return
true
;
}
}
//缓存数超过1024个,这个逻辑用于避免时间戳异常的流导致的内存暴增问题
//缓存数超过1024个,这个逻辑用于避免时间戳异常的流导致的内存暴增问题
//而且sendmsg接口一般最多只能发送1024个数据包
//而且sendmsg接口一般最多只能发送1024个数据包
return
cache_size
>=
1024
;
return
cache_size
>=
1024
;
}
}
bool
FlushPolicy
::
isFlushAble
(
bool
is_video
,
bool
is_key
,
uint64_t
new_stamp
,
int
cache_size
)
{
bool
FlushPolicy
::
isFlushAble
(
bool
is_video
,
bool
is_key
,
uint64_t
new_stamp
,
int
cache_size
)
{
bool
flush_flag
=
false
;
bool
flush_flag
=
false
;
if
(
is_key
&&
is_video
)
{
if
(
is_key
&&
is_video
)
{
//遇到关键帧flush掉前面的数据,确保关键帧为该组数据的第一帧,确保GOP缓存有效
//遇到关键帧flush掉前面的数据,确保关键帧为该组数据的第一帧,确保GOP缓存有效
flush_flag
=
true
;
flush_flag
=
true
;
}
else
{
}
else
{
GET_CONFIG
(
int
,
mergeWriteMS
,
General
::
kMergeWriteMS
);
GET_CONFIG
(
int
,
mergeWriteMS
,
General
::
kMergeWriteMS
);
if
(
mergeWriteMS
<=
0
)
{
if
(
mergeWriteMS
<=
0
)
{
//关闭了合并写或者合并写阈值小于等于0
//关闭了合并写或者合并写阈值小于等于0
flush_flag
=
isFlushAble_default
(
is_video
,
_last_stamp
[
is_video
],
new_stamp
,
cache_size
);
flush_flag
=
isFlushAble_default
(
is_video
,
_last_stamp
[
is_video
],
new_stamp
,
cache_size
);
}
else
{
}
else
{
flush_flag
=
isFlushAble_merge
(
is_video
,
_last_stamp
[
is_video
],
new_stamp
,
cache_size
,
mergeWriteMS
);
flush_flag
=
isFlushAble_merge
(
is_video
,
_last_stamp
[
is_video
],
new_stamp
,
cache_size
,
mergeWriteMS
);
}
}
}
}
if
(
flush_flag
)
{
if
(
flush_flag
)
{
_last_stamp
[
is_video
]
=
new_stamp
;
_last_stamp
[
is_video
]
=
new_stamp
;
}
}
return
flush_flag
;
return
flush_flag
;
}
}
}
/* namespace mediakit */
}
/* namespace mediakit */
\ No newline at end of file
src/Common/MediaSource.h
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#ifndef ZLMEDIAKIT_MEDIASOURCE_H
#ifndef ZLMEDIAKIT_MEDIASOURCE_H
#define ZLMEDIAKIT_MEDIASOURCE_H
#define ZLMEDIAKIT_MEDIASOURCE_H
#include <mutex>
#include <mutex>
#include <string>
#include <string>
#include <memory>
#include <memory>
#include <functional>
#include <functional>
#include <unordered_map>
#include <unordered_map>
#include "Common/config.h"
#include "Common/config.h"
#include "Common/Parser.h"
#include "Common/Parser.h"
#include "Util/logger.h"
#include "Util/logger.h"
#include "Util/TimeTicker.h"
#include "Util/TimeTicker.h"
#include "Util/NoticeCenter.h"
#include "Util/NoticeCenter.h"
#include "Util/List.h"
#include "Util/List.h"
#include "Network/Socket.h"
#include "Network/Socket.h"
#include "Rtsp/Rtsp.h"
#include "Rtsp/Rtsp.h"
#include "Rtmp/Rtmp.h"
#include "Rtmp/Rtmp.h"
#include "Extension/Track.h"
#include "Extension/Track.h"
#include "Record/Recorder.h"
#include "Record/Recorder.h"
using
namespace
std
;
using
namespace
std
;
using
namespace
toolkit
;
using
namespace
toolkit
;
namespace
toolkit
{
namespace
toolkit
{
class
TcpSession
;
class
TcpSession
;
}
// namespace toolkit
}
// namespace toolkit
namespace
mediakit
{
namespace
mediakit
{
enum
class
MediaOriginType
:
uint8_t
{
enum
class
MediaOriginType
:
uint8_t
{
unknown
=
0
,
unknown
=
0
,
rtmp_push
,
rtmp_push
,
rtsp_push
,
rtsp_push
,
rtp_push
,
rtp_push
,
pull
,
pull
,
ffmpeg_pull
,
ffmpeg_pull
,
mp4_vod
,
mp4_vod
,
device_chn
device_chn
};
};
string
getOriginTypeString
(
MediaOriginType
type
);
string
getOriginTypeString
(
MediaOriginType
type
);
class
MediaSource
;
class
MediaSource
;
class
MediaSourceEvent
{
class
MediaSourceEvent
{
public
:
public
:
friend
class
MediaSource
;
friend
class
MediaSource
;
MediaSourceEvent
(){};
MediaSourceEvent
(){};
virtual
~
MediaSourceEvent
(){};
virtual
~
MediaSourceEvent
(){};
// 获取媒体源类型
// 获取媒体源类型
virtual
MediaOriginType
getOriginType
(
MediaSource
&
sender
)
const
{
return
MediaOriginType
::
unknown
;
}
virtual
MediaOriginType
getOriginType
(
MediaSource
&
sender
)
const
{
return
MediaOriginType
::
unknown
;
}
// 获取媒体源url或者文件路径
// 获取媒体源url或者文件路径
virtual
string
getOriginUrl
(
MediaSource
&
sender
)
const
{
return
""
;
}
virtual
string
getOriginUrl
(
MediaSource
&
sender
)
const
{
return
""
;
}
// 获取媒体源客户端相关信息
// 获取媒体源客户端相关信息
virtual
std
::
shared_ptr
<
SockInfo
>
getOriginSock
(
MediaSource
&
sender
)
const
{
return
nullptr
;
}
virtual
std
::
shared_ptr
<
SockInfo
>
getOriginSock
(
MediaSource
&
sender
)
const
{
return
nullptr
;
}
// 通知拖动进度条
// 通知拖动进度条
virtual
bool
seekTo
(
MediaSource
&
sender
,
uint32_t
stamp
)
{
return
false
;
}
virtual
bool
seekTo
(
MediaSource
&
sender
,
uint32_t
stamp
)
{
return
false
;
}
// 通知其停止产生流
// 通知其停止产生流
virtual
bool
close
(
MediaSource
&
sender
,
bool
force
)
{
return
false
;
}
virtual
bool
close
(
MediaSource
&
sender
,
bool
force
)
{
return
false
;
}
// 获取观看总人数
// 获取观看总人数
virtual
int
totalReaderCount
(
MediaSource
&
sender
)
=
0
;
virtual
int
totalReaderCount
(
MediaSource
&
sender
)
=
0
;
// 通知观看人数变化
// 通知观看人数变化
virtual
void
onReaderChanged
(
MediaSource
&
sender
,
int
size
);
virtual
void
onReaderChanged
(
MediaSource
&
sender
,
int
size
);
//流注册或注销事件
//流注册或注销事件
virtual
void
onRegist
(
MediaSource
&
sender
,
bool
regist
)
{};
virtual
void
onRegist
(
MediaSource
&
sender
,
bool
regist
)
{};
////////////////////////仅供MultiMediaSourceMuxer对象继承////////////////////////
////////////////////////仅供MultiMediaSourceMuxer对象继承////////////////////////
// 开启或关闭录制
// 开启或关闭录制
virtual
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
return
false
;
};
virtual
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
return
false
;
};
// 获取录制状态
// 获取录制状态
virtual
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
return
false
;
};
virtual
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
return
false
;
};
// 获取所有track相关信息
// 获取所有track相关信息
virtual
vector
<
Track
::
Ptr
>
getTracks
(
MediaSource
&
sender
,
bool
trackReady
=
true
)
const
{
return
vector
<
Track
::
Ptr
>
();
};
virtual
vector
<
Track
::
Ptr
>
getTracks
(
MediaSource
&
sender
,
bool
trackReady
=
true
)
const
{
return
vector
<
Track
::
Ptr
>
();
};
// 开始发送ps-rtp
// 开始发送ps-rtp
virtual
void
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
)
{
cb
(
SockException
(
Err_other
,
"not implemented"
));};
virtual
void
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
)
{
cb
(
SockException
(
Err_other
,
"not implemented"
));};
// 停止发送ps-rtp
// 停止发送ps-rtp
virtual
bool
stopSendRtp
(
MediaSource
&
sender
)
{
return
false
;
}
virtual
bool
stopSendRtp
(
MediaSource
&
sender
,
const
string
&
ssrc
)
{
return
false
;
}
private
:
private
:
Timer
::
Ptr
_async_close_timer
;
Timer
::
Ptr
_async_close_timer
;
};
};
//该对象用于拦截感兴趣的MediaSourceEvent事件
//该对象用于拦截感兴趣的MediaSourceEvent事件
class
MediaSourceEventInterceptor
:
public
MediaSourceEvent
{
class
MediaSourceEventInterceptor
:
public
MediaSourceEvent
{
public
:
public
:
MediaSourceEventInterceptor
(){}
MediaSourceEventInterceptor
(){}
~
MediaSourceEventInterceptor
()
override
{}
~
MediaSourceEventInterceptor
()
override
{}
void
setDelegate
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
);
void
setDelegate
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
);
std
::
shared_ptr
<
MediaSourceEvent
>
getDelegate
()
const
;
std
::
shared_ptr
<
MediaSourceEvent
>
getDelegate
()
const
;
MediaOriginType
getOriginType
(
MediaSource
&
sender
)
const
override
;
MediaOriginType
getOriginType
(
MediaSource
&
sender
)
const
override
;
string
getOriginUrl
(
MediaSource
&
sender
)
const
override
;
string
getOriginUrl
(
MediaSource
&
sender
)
const
override
;
std
::
shared_ptr
<
SockInfo
>
getOriginSock
(
MediaSource
&
sender
)
const
override
;
std
::
shared_ptr
<
SockInfo
>
getOriginSock
(
MediaSource
&
sender
)
const
override
;
bool
seekTo
(
MediaSource
&
sender
,
uint32_t
stamp
)
override
;
bool
seekTo
(
MediaSource
&
sender
,
uint32_t
stamp
)
override
;
bool
close
(
MediaSource
&
sender
,
bool
force
)
override
;
bool
close
(
MediaSource
&
sender
,
bool
force
)
override
;
int
totalReaderCount
(
MediaSource
&
sender
)
override
;
int
totalReaderCount
(
MediaSource
&
sender
)
override
;
void
onReaderChanged
(
MediaSource
&
sender
,
int
size
)
override
;
void
onReaderChanged
(
MediaSource
&
sender
,
int
size
)
override
;
void
onRegist
(
MediaSource
&
sender
,
bool
regist
)
override
;
void
onRegist
(
MediaSource
&
sender
,
bool
regist
)
override
;
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
override
;
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
override
;
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
override
;
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
override
;
vector
<
Track
::
Ptr
>
getTracks
(
MediaSource
&
sender
,
bool
trackReady
=
true
)
const
override
;
vector
<
Track
::
Ptr
>
getTracks
(
MediaSource
&
sender
,
bool
trackReady
=
true
)
const
override
;
void
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
)
override
;
void
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
)
override
;
bool
stopSendRtp
(
MediaSource
&
sender
)
override
;
bool
stopSendRtp
(
MediaSource
&
sender
,
const
string
&
ssrc
)
override
;
private
:
private
:
std
::
weak_ptr
<
MediaSourceEvent
>
_listener
;
std
::
weak_ptr
<
MediaSourceEvent
>
_listener
;
};
};
/**
/**
* 解析url获取媒体相关信息
* 解析url获取媒体相关信息
*/
*/
class
MediaInfo
{
class
MediaInfo
{
public
:
public
:
~
MediaInfo
()
{}
~
MediaInfo
()
{}
MediaInfo
()
{}
MediaInfo
()
{}
MediaInfo
(
const
string
&
url
)
{
parse
(
url
);
}
MediaInfo
(
const
string
&
url
)
{
parse
(
url
);
}
void
parse
(
const
string
&
url
);
void
parse
(
const
string
&
url
);
public
:
public
:
string
_full_url
;
string
_full_url
;
string
_schema
;
string
_schema
;
string
_host
;
string
_host
;
string
_port
;
string
_port
;
string
_vhost
;
string
_vhost
;
string
_app
;
string
_app
;
string
_streamid
;
string
_streamid
;
string
_param_strs
;
string
_param_strs
;
};
};
class
BytesSpeed
{
class
BytesSpeed
{
public
:
public
:
BytesSpeed
()
=
default
;
BytesSpeed
()
=
default
;
~
BytesSpeed
()
=
default
;
~
BytesSpeed
()
=
default
;
/**
/**
* 添加统计字节
* 添加统计字节
*/
*/
BytesSpeed
&
operator
+=
(
uint64_t
bytes
)
{
BytesSpeed
&
operator
+=
(
uint64_t
bytes
)
{
_bytes
+=
bytes
;
_bytes
+=
bytes
;
if
(
_bytes
>
1024
*
1024
)
{
if
(
_bytes
>
1024
*
1024
)
{
//数据大于1MB就计算一次网速
//数据大于1MB就计算一次网速
computeSpeed
();
computeSpeed
();
}
}
return
*
this
;
return
*
this
;
}
}
/**
/**
* 获取速度,单位bytes/s
* 获取速度,单位bytes/s
*/
*/
int
getSpeed
()
{
int
getSpeed
()
{
if
(
_ticker
.
elapsedTime
()
<
1000
)
{
if
(
_ticker
.
elapsedTime
()
<
1000
)
{
//获取频率小于1秒,那么返回上次计算结果
//获取频率小于1秒,那么返回上次计算结果
return
_speed
;
return
_speed
;
}
}
return
computeSpeed
();
return
computeSpeed
();
}
}
private
:
private
:
uint64_t
computeSpeed
()
{
uint64_t
computeSpeed
()
{
auto
elapsed
=
_ticker
.
elapsedTime
();
auto
elapsed
=
_ticker
.
elapsedTime
();
if
(
!
elapsed
)
{
if
(
!
elapsed
)
{
return
_speed
;
return
_speed
;
}
}
_speed
=
_bytes
*
1000
/
elapsed
;
_speed
=
_bytes
*
1000
/
elapsed
;
_ticker
.
resetTime
();
_ticker
.
resetTime
();
_bytes
=
0
;
_bytes
=
0
;
return
_speed
;
return
_speed
;
}
}
private
:
private
:
int
_speed
=
0
;
int
_speed
=
0
;
uint64_t
_bytes
=
0
;
uint64_t
_bytes
=
0
;
Ticker
_ticker
;
Ticker
_ticker
;
};
};
/**
/**
* 媒体源,任何rtsp/rtmp的直播流都源自该对象
* 媒体源,任何rtsp/rtmp的直播流都源自该对象
*/
*/
class
MediaSource
:
public
TrackSource
,
public
enable_shared_from_this
<
MediaSource
>
{
class
MediaSource
:
public
TrackSource
,
public
enable_shared_from_this
<
MediaSource
>
{
public
:
public
:
typedef
std
::
shared_ptr
<
MediaSource
>
Ptr
;
typedef
std
::
shared_ptr
<
MediaSource
>
Ptr
;
typedef
unordered_map
<
string
,
weak_ptr
<
MediaSource
>
>
StreamMap
;
typedef
unordered_map
<
string
,
weak_ptr
<
MediaSource
>
>
StreamMap
;
typedef
unordered_map
<
string
,
StreamMap
>
AppStreamMap
;
typedef
unordered_map
<
string
,
StreamMap
>
AppStreamMap
;
typedef
unordered_map
<
string
,
AppStreamMap
>
VhostAppStreamMap
;
typedef
unordered_map
<
string
,
AppStreamMap
>
VhostAppStreamMap
;
typedef
unordered_map
<
string
,
VhostAppStreamMap
>
SchemaVhostAppStreamMap
;
typedef
unordered_map
<
string
,
VhostAppStreamMap
>
SchemaVhostAppStreamMap
;
MediaSource
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
)
;
MediaSource
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
)
;
virtual
~
MediaSource
()
;
virtual
~
MediaSource
()
;
////////////////获取MediaSource相关信息////////////////
////////////////获取MediaSource相关信息////////////////
// 获取协议类型
// 获取协议类型
const
string
&
getSchema
()
const
;
const
string
&
getSchema
()
const
;
// 虚拟主机
// 虚拟主机
const
string
&
getVhost
()
const
;
const
string
&
getVhost
()
const
;
// 应用名
// 应用名
const
string
&
getApp
()
const
;
const
string
&
getApp
()
const
;
// 流id
// 流id
const
string
&
getId
()
const
;
const
string
&
getId
()
const
;
// 获取所有Track
// 获取所有Track
vector
<
Track
::
Ptr
>
getTracks
(
bool
ready
=
true
)
const
override
;
vector
<
Track
::
Ptr
>
getTracks
(
bool
ready
=
true
)
const
override
;
// 获取流当前时间戳
// 获取流当前时间戳
virtual
uint32_t
getTimeStamp
(
TrackType
type
)
{
return
0
;
};
virtual
uint32_t
getTimeStamp
(
TrackType
type
)
{
return
0
;
};
// 设置时间戳
// 设置时间戳
virtual
void
setTimeStamp
(
uint32_t
stamp
)
{};
virtual
void
setTimeStamp
(
uint32_t
stamp
)
{};
// 获取数据速率,单位bytes/s
// 获取数据速率,单位bytes/s
int
getBytesSpeed
();
int
getBytesSpeed
();
// 获取流创建GMT unix时间戳,单位秒
// 获取流创建GMT unix时间戳,单位秒
uint64_t
getCreateStamp
()
const
;
uint64_t
getCreateStamp
()
const
;
// 获取流上线时间,单位秒
// 获取流上线时间,单位秒
uint64_t
getAliveSecond
()
const
;
uint64_t
getAliveSecond
()
const
;
////////////////MediaSourceEvent相关接口实现////////////////
////////////////MediaSourceEvent相关接口实现////////////////
// 设置监听者
// 设置监听者
virtual
void
setListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
);
virtual
void
setListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
);
// 获取监听者
// 获取监听者
std
::
weak_ptr
<
MediaSourceEvent
>
getListener
(
bool
next
=
false
)
const
;
std
::
weak_ptr
<
MediaSourceEvent
>
getListener
(
bool
next
=
false
)
const
;
// 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数
// 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数
virtual
int
readerCount
()
=
0
;
virtual
int
readerCount
()
=
0
;
// 观看者个数,包括(hls/rtsp/rtmp)
// 观看者个数,包括(hls/rtsp/rtmp)
virtual
int
totalReaderCount
();
virtual
int
totalReaderCount
();
// 获取媒体源类型
// 获取媒体源类型
MediaOriginType
getOriginType
()
const
;
MediaOriginType
getOriginType
()
const
;
// 获取媒体源url或者文件路径
// 获取媒体源url或者文件路径
string
getOriginUrl
()
const
;
string
getOriginUrl
()
const
;
// 获取媒体源客户端相关信息
// 获取媒体源客户端相关信息
std
::
shared_ptr
<
SockInfo
>
getOriginSock
()
const
;
std
::
shared_ptr
<
SockInfo
>
getOriginSock
()
const
;
// 拖动进度条
// 拖动进度条
bool
seekTo
(
uint32_t
stamp
);
bool
seekTo
(
uint32_t
stamp
);
// 关闭该流
// 关闭该流
bool
close
(
bool
force
);
bool
close
(
bool
force
);
// 该流观看人数变化
// 该流观看人数变化
void
onReaderChanged
(
int
size
);
void
onReaderChanged
(
int
size
);
// 开启或关闭录制
// 开启或关闭录制
bool
setupRecord
(
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
);
bool
setupRecord
(
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
);
// 获取录制状态
// 获取录制状态
bool
isRecording
(
Recorder
::
type
type
);
bool
isRecording
(
Recorder
::
type
type
);
// 开始发送ps-rtp
// 开始发送ps-rtp
void
startSendRtp
(
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
);
void
startSendRtp
(
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
);
// 停止发送ps-rtp
// 停止发送ps-rtp
bool
stopSendRtp
(
);
bool
stopSendRtp
(
const
string
&
ssrc
);
////////////////static方法,查找或生成MediaSource////////////////
////////////////static方法,查找或生成MediaSource////////////////
// 同步查找流
// 同步查找流
static
Ptr
find
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
id
);
static
Ptr
find
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
id
);
// 忽略类型,同步查找流,可能返回rtmp/rtsp/hls类型
// 忽略类型,同步查找流,可能返回rtmp/rtsp/hls类型
static
Ptr
find
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
);
static
Ptr
find
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream_id
);
// 异步查找流
// 异步查找流
static
void
findAsync
(
const
MediaInfo
&
info
,
const
std
::
shared_ptr
<
TcpSession
>
&
session
,
const
function
<
void
(
const
Ptr
&
src
)
>
&
cb
);
static
void
findAsync
(
const
MediaInfo
&
info
,
const
std
::
shared_ptr
<
TcpSession
>
&
session
,
const
function
<
void
(
const
Ptr
&
src
)
>
&
cb
);
// 遍历所有流
// 遍历所有流
static
void
for_each_media
(
const
function
<
void
(
const
Ptr
&
src
)
>
&
cb
);
static
void
for_each_media
(
const
function
<
void
(
const
Ptr
&
src
)
>
&
cb
);
// 从mp4文件生成MediaSource
// 从mp4文件生成MediaSource
static
MediaSource
::
Ptr
createFromMP4
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
const
string
&
file_path
=
""
,
bool
check_app
=
true
);
static
MediaSource
::
Ptr
createFromMP4
(
const
string
&
schema
,
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
const
string
&
file_path
=
""
,
bool
check_app
=
true
);
protected
:
protected
:
//媒体注册
//媒体注册
void
regist
();
void
regist
();
private
:
private
:
//媒体注销
//媒体注销
bool
unregist
();
bool
unregist
();
//触发媒体事件
//触发媒体事件
void
emitEvent
(
bool
regist
);
void
emitEvent
(
bool
regist
);
protected
:
protected
:
BytesSpeed
_speed
;
BytesSpeed
_speed
;
private
:
private
:
time_t
_create_stamp
;
time_t
_create_stamp
;
Ticker
_ticker
;
Ticker
_ticker
;
string
_schema
;
string
_schema
;
string
_vhost
;
string
_vhost
;
string
_app
;
string
_app
;
string
_stream_id
;
string
_stream_id
;
std
::
weak_ptr
<
MediaSourceEvent
>
_listener
;
std
::
weak_ptr
<
MediaSourceEvent
>
_listener
;
};
};
///缓存刷新策略类
///缓存刷新策略类
class
FlushPolicy
{
class
FlushPolicy
{
public
:
public
:
FlushPolicy
()
=
default
;
FlushPolicy
()
=
default
;
~
FlushPolicy
()
=
default
;
~
FlushPolicy
()
=
default
;
bool
isFlushAble
(
bool
is_video
,
bool
is_key
,
uint64_t
new_stamp
,
int
cache_size
);
bool
isFlushAble
(
bool
is_video
,
bool
is_key
,
uint64_t
new_stamp
,
int
cache_size
);
private
:
private
:
uint64_t
_last_stamp
[
2
]
=
{
0
,
0
};
uint64_t
_last_stamp
[
2
]
=
{
0
,
0
};
};
};
/// 合并写缓存模板
/// 合并写缓存模板
/// \tparam packet 包类型
/// \tparam packet 包类型
/// \tparam policy 刷新缓存策略
/// \tparam policy 刷新缓存策略
/// \tparam packet_list 包缓存类型
/// \tparam packet_list 包缓存类型
template
<
typename
packet
,
typename
policy
=
FlushPolicy
,
typename
packet_list
=
List
<
std
::
shared_ptr
<
packet
>
>
>
template
<
typename
packet
,
typename
policy
=
FlushPolicy
,
typename
packet_list
=
List
<
std
::
shared_ptr
<
packet
>
>
>
class
PacketCache
{
class
PacketCache
{
public
:
public
:
PacketCache
(){
PacketCache
(){
_cache
=
std
::
make_shared
<
packet_list
>
();
_cache
=
std
::
make_shared
<
packet_list
>
();
}
}
virtual
~
PacketCache
()
=
default
;
virtual
~
PacketCache
()
=
default
;
void
inputPacket
(
uint64_t
stamp
,
bool
is_video
,
std
::
shared_ptr
<
packet
>
pkt
,
bool
key_pos
)
{
void
inputPacket
(
uint64_t
stamp
,
bool
is_video
,
std
::
shared_ptr
<
packet
>
pkt
,
bool
key_pos
)
{
if
(
_policy
.
isFlushAble
(
is_video
,
key_pos
,
stamp
,
_cache
->
size
()))
{
if
(
_policy
.
isFlushAble
(
is_video
,
key_pos
,
stamp
,
_cache
->
size
()))
{
flushAll
();
flushAll
();
}
}
//追加数据到最后
//追加数据到最后
_cache
->
emplace_back
(
std
::
move
(
pkt
));
_cache
->
emplace_back
(
std
::
move
(
pkt
));
if
(
key_pos
)
{
if
(
key_pos
)
{
_key_pos
=
key_pos
;
_key_pos
=
key_pos
;
}
}
}
}
virtual
void
clearCache
()
{
virtual
void
clearCache
()
{
_cache
->
clear
();
_cache
->
clear
();
}
}
virtual
void
onFlush
(
std
::
shared_ptr
<
packet_list
>
,
bool
key_pos
)
=
0
;
virtual
void
onFlush
(
std
::
shared_ptr
<
packet_list
>
,
bool
key_pos
)
=
0
;
private
:
private
:
void
flushAll
()
{
void
flushAll
()
{
if
(
_cache
->
empty
())
{
if
(
_cache
->
empty
())
{
return
;
return
;
}
}
onFlush
(
std
::
move
(
_cache
),
_key_pos
);
onFlush
(
std
::
move
(
_cache
),
_key_pos
);
_cache
=
std
::
make_shared
<
packet_list
>
();
_cache
=
std
::
make_shared
<
packet_list
>
();
_key_pos
=
false
;
_key_pos
=
false
;
}
}
private
:
private
:
bool
_key_pos
=
false
;
bool
_key_pos
=
false
;
policy
_policy
;
policy
_policy
;
std
::
shared_ptr
<
packet_list
>
_cache
;
std
::
shared_ptr
<
packet_list
>
_cache
;
};
};
}
/* namespace mediakit */
}
/* namespace mediakit */
#endif //ZLMEDIAKIT_MEDIASOURCE_H
#endif //ZLMEDIAKIT_MEDIASOURCE_H
\ No newline at end of file
src/Common/MultiMediaSourceMuxer.cpp
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#include <math.h>
#include <math.h>
#include "Common/config.h"
#include "Common/config.h"
#include "MultiMediaSourceMuxer.h"
#include "MultiMediaSourceMuxer.h"
namespace
mediakit
{
namespace
mediakit
{
///////////////////////////////MultiMuxerPrivate//////////////////////////////////
///////////////////////////////MultiMuxerPrivate//////////////////////////////////
MultiMuxerPrivate
::~
MultiMuxerPrivate
()
{}
MultiMuxerPrivate
::~
MultiMuxerPrivate
()
{}
MultiMuxerPrivate
::
MultiMuxerPrivate
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
float
dur_sec
,
MultiMuxerPrivate
::
MultiMuxerPrivate
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
float
dur_sec
,
bool
enable_rtsp
,
bool
enable_rtmp
,
bool
enable_hls
,
bool
enable_mp4
)
{
bool
enable_rtsp
,
bool
enable_rtmp
,
bool
enable_hls
,
bool
enable_mp4
)
{
_stream_url
=
vhost
+
" "
+
app
+
" "
+
stream
;
_stream_url
=
vhost
+
" "
+
app
+
" "
+
stream
;
if
(
enable_rtmp
)
{
if
(
enable_rtmp
)
{
_rtmp
=
std
::
make_shared
<
RtmpMediaSourceMuxer
>
(
vhost
,
app
,
stream
,
std
::
make_shared
<
TitleMeta
>
(
dur_sec
));
_rtmp
=
std
::
make_shared
<
RtmpMediaSourceMuxer
>
(
vhost
,
app
,
stream
,
std
::
make_shared
<
TitleMeta
>
(
dur_sec
));
}
}
if
(
enable_rtsp
)
{
if
(
enable_rtsp
)
{
_rtsp
=
std
::
make_shared
<
RtspMediaSourceMuxer
>
(
vhost
,
app
,
stream
,
std
::
make_shared
<
TitleSdp
>
(
dur_sec
));
_rtsp
=
std
::
make_shared
<
RtspMediaSourceMuxer
>
(
vhost
,
app
,
stream
,
std
::
make_shared
<
TitleSdp
>
(
dur_sec
));
}
}
if
(
enable_hls
)
{
if
(
enable_hls
)
{
_hls
=
dynamic_pointer_cast
<
HlsRecorder
>
(
Recorder
::
createRecorder
(
Recorder
::
type_hls
,
vhost
,
app
,
stream
));
_hls
=
dynamic_pointer_cast
<
HlsRecorder
>
(
Recorder
::
createRecorder
(
Recorder
::
type_hls
,
vhost
,
app
,
stream
));
}
}
if
(
enable_mp4
)
{
if
(
enable_mp4
)
{
_mp4
=
Recorder
::
createRecorder
(
Recorder
::
type_mp4
,
vhost
,
app
,
stream
);
_mp4
=
Recorder
::
createRecorder
(
Recorder
::
type_mp4
,
vhost
,
app
,
stream
);
}
}
_ts
=
std
::
make_shared
<
TSMediaSourceMuxer
>
(
vhost
,
app
,
stream
);
_ts
=
std
::
make_shared
<
TSMediaSourceMuxer
>
(
vhost
,
app
,
stream
);
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
_fmp4
=
std
::
make_shared
<
FMP4MediaSourceMuxer
>
(
vhost
,
app
,
stream
);
_fmp4
=
std
::
make_shared
<
FMP4MediaSourceMuxer
>
(
vhost
,
app
,
stream
);
#endif
#endif
}
}
void
MultiMuxerPrivate
::
resetTracks
()
{
void
MultiMuxerPrivate
::
resetTracks
()
{
if
(
_rtmp
)
{
if
(
_rtmp
)
{
_rtmp
->
resetTracks
();
_rtmp
->
resetTracks
();
}
}
if
(
_rtsp
)
{
if
(
_rtsp
)
{
_rtsp
->
resetTracks
();
_rtsp
->
resetTracks
();
}
}
if
(
_ts
)
{
if
(
_ts
)
{
_ts
->
resetTracks
();
_ts
->
resetTracks
();
}
}
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
if
(
_fmp4
)
{
if
(
_fmp4
)
{
_fmp4
->
resetTracks
();
_fmp4
->
resetTracks
();
}
}
#endif
#endif
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
auto
hls
=
_hls
;
auto
hls
=
_hls
;
if
(
hls
)
{
if
(
hls
)
{
hls
->
resetTracks
();
hls
->
resetTracks
();
}
}
auto
mp4
=
_mp4
;
auto
mp4
=
_mp4
;
if
(
mp4
)
{
if
(
mp4
)
{
mp4
->
resetTracks
();
mp4
->
resetTracks
();
}
}
}
}
void
MultiMuxerPrivate
::
setMediaListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
)
{
void
MultiMuxerPrivate
::
setMediaListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
)
{
_listener
=
listener
;
_listener
=
listener
;
if
(
_rtmp
)
{
if
(
_rtmp
)
{
_rtmp
->
setListener
(
listener
);
_rtmp
->
setListener
(
listener
);
}
}
if
(
_rtsp
)
{
if
(
_rtsp
)
{
_rtsp
->
setListener
(
listener
);
_rtsp
->
setListener
(
listener
);
}
}
if
(
_ts
)
{
if
(
_ts
)
{
_ts
->
setListener
(
listener
);
_ts
->
setListener
(
listener
);
}
}
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
if
(
_fmp4
)
{
if
(
_fmp4
)
{
_fmp4
->
setListener
(
listener
);
_fmp4
->
setListener
(
listener
);
}
}
#endif
#endif
auto
hls
=
_hls
;
auto
hls
=
_hls
;
if
(
hls
)
{
if
(
hls
)
{
hls
->
setListener
(
listener
);
hls
->
setListener
(
listener
);
}
}
}
}
int
MultiMuxerPrivate
::
totalReaderCount
()
const
{
int
MultiMuxerPrivate
::
totalReaderCount
()
const
{
auto
hls
=
_hls
;
auto
hls
=
_hls
;
return
(
_rtsp
?
_rtsp
->
readerCount
()
:
0
)
+
return
(
_rtsp
?
_rtsp
->
readerCount
()
:
0
)
+
(
_rtmp
?
_rtmp
->
readerCount
()
:
0
)
+
(
_rtmp
?
_rtmp
->
readerCount
()
:
0
)
+
(
_ts
?
_ts
->
readerCount
()
:
0
)
+
(
_ts
?
_ts
->
readerCount
()
:
0
)
+
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
(
_fmp4
?
_fmp4
->
readerCount
()
:
0
)
+
(
_fmp4
?
_fmp4
->
readerCount
()
:
0
)
+
#endif
#endif
(
hls
?
hls
->
readerCount
()
:
0
);
(
hls
?
hls
->
readerCount
()
:
0
);
}
}
static
std
::
shared_ptr
<
MediaSinkInterface
>
makeRecorder
(
const
vector
<
Track
::
Ptr
>
&
tracks
,
Recorder
::
type
type
,
const
string
&
custom_path
,
MediaSource
&
sender
){
static
std
::
shared_ptr
<
MediaSinkInterface
>
makeRecorder
(
const
vector
<
Track
::
Ptr
>
&
tracks
,
Recorder
::
type
type
,
const
string
&
custom_path
,
MediaSource
&
sender
){
auto
recorder
=
Recorder
::
createRecorder
(
type
,
sender
.
getVhost
(),
sender
.
getApp
(),
sender
.
getId
(),
custom_path
);
auto
recorder
=
Recorder
::
createRecorder
(
type
,
sender
.
getVhost
(),
sender
.
getApp
(),
sender
.
getId
(),
custom_path
);
for
(
auto
&
track
:
tracks
)
{
for
(
auto
&
track
:
tracks
)
{
recorder
->
addTrack
(
track
);
recorder
->
addTrack
(
track
);
}
}
return
recorder
;
return
recorder
;
}
}
//此函数可能跨线程调用
//此函数可能跨线程调用
bool
MultiMuxerPrivate
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
){
bool
MultiMuxerPrivate
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
){
switch
(
type
)
{
switch
(
type
)
{
case
Recorder
:
:
type_hls
:
{
case
Recorder
:
:
type_hls
:
{
if
(
start
&&
!
_hls
)
{
if
(
start
&&
!
_hls
)
{
//开始录制
//开始录制
auto
hls
=
dynamic_pointer_cast
<
HlsRecorder
>
(
makeRecorder
(
getTracks
(
true
),
type
,
custom_path
,
sender
));
auto
hls
=
dynamic_pointer_cast
<
HlsRecorder
>
(
makeRecorder
(
getTracks
(
true
),
type
,
custom_path
,
sender
));
if
(
hls
)
{
if
(
hls
)
{
//设置HlsMediaSource的事件监听器
//设置HlsMediaSource的事件监听器
hls
->
setListener
(
_listener
);
hls
->
setListener
(
_listener
);
}
}
_hls
=
hls
;
_hls
=
hls
;
}
else
if
(
!
start
&&
_hls
)
{
}
else
if
(
!
start
&&
_hls
)
{
//停止录制
//停止录制
_hls
=
nullptr
;
_hls
=
nullptr
;
}
}
return
true
;
return
true
;
}
}
case
Recorder
:
:
type_mp4
:
{
case
Recorder
:
:
type_mp4
:
{
if
(
start
&&
!
_mp4
)
{
if
(
start
&&
!
_mp4
)
{
//开始录制
//开始录制
_mp4
=
makeRecorder
(
getTracks
(
true
),
type
,
custom_path
,
sender
);
_mp4
=
makeRecorder
(
getTracks
(
true
),
type
,
custom_path
,
sender
);
}
else
if
(
!
start
&&
_mp4
)
{
}
else
if
(
!
start
&&
_mp4
)
{
//停止录制
//停止录制
_mp4
=
nullptr
;
_mp4
=
nullptr
;
}
}
return
true
;
return
true
;
}
}
default
:
return
false
;
default
:
return
false
;
}
}
}
}
//此函数可能跨线程调用
//此函数可能跨线程调用
bool
MultiMuxerPrivate
::
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
){
bool
MultiMuxerPrivate
::
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
){
switch
(
type
){
switch
(
type
){
case
Recorder
:
:
type_hls
:
case
Recorder
:
:
type_hls
:
return
_hls
?
true
:
false
;
return
_hls
?
true
:
false
;
case
Recorder
:
:
type_mp4
:
case
Recorder
:
:
type_mp4
:
return
_mp4
?
true
:
false
;
return
_mp4
?
true
:
false
;
default
:
default
:
return
false
;
return
false
;
}
}
}
}
void
MultiMuxerPrivate
::
setTimeStamp
(
uint32_t
stamp
)
{
void
MultiMuxerPrivate
::
setTimeStamp
(
uint32_t
stamp
)
{
if
(
_rtmp
)
{
if
(
_rtmp
)
{
_rtmp
->
setTimeStamp
(
stamp
);
_rtmp
->
setTimeStamp
(
stamp
);
}
}
if
(
_rtsp
)
{
if
(
_rtsp
)
{
_rtsp
->
setTimeStamp
(
stamp
);
_rtsp
->
setTimeStamp
(
stamp
);
}
}
}
}
void
MultiMuxerPrivate
::
setTrackListener
(
Listener
*
listener
)
{
void
MultiMuxerPrivate
::
setTrackListener
(
Listener
*
listener
)
{
_track_listener
=
listener
;
_track_listener
=
listener
;
}
}
void
MultiMuxerPrivate
::
onTrackReady
(
const
Track
::
Ptr
&
track
)
{
void
MultiMuxerPrivate
::
onTrackReady
(
const
Track
::
Ptr
&
track
)
{
if
(
_rtmp
)
{
if
(
_rtmp
)
{
_rtmp
->
addTrack
(
track
);
_rtmp
->
addTrack
(
track
);
}
}
if
(
_rtsp
)
{
if
(
_rtsp
)
{
_rtsp
->
addTrack
(
track
);
_rtsp
->
addTrack
(
track
);
}
}
if
(
_ts
)
{
if
(
_ts
)
{
_ts
->
addTrack
(
track
);
_ts
->
addTrack
(
track
);
}
}
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
if
(
_fmp4
)
{
if
(
_fmp4
)
{
_fmp4
->
addTrack
(
track
);
_fmp4
->
addTrack
(
track
);
}
}
#endif
#endif
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
auto
hls
=
_hls
;
auto
hls
=
_hls
;
if
(
hls
)
{
if
(
hls
)
{
hls
->
addTrack
(
track
);
hls
->
addTrack
(
track
);
}
}
auto
mp4
=
_mp4
;
auto
mp4
=
_mp4
;
if
(
mp4
)
{
if
(
mp4
)
{
mp4
->
addTrack
(
track
);
mp4
->
addTrack
(
track
);
}
}
}
}
bool
MultiMuxerPrivate
::
isEnabled
(){
bool
MultiMuxerPrivate
::
isEnabled
(){
auto
hls
=
_hls
;
auto
hls
=
_hls
;
return
(
_rtmp
?
_rtmp
->
isEnabled
()
:
false
)
||
return
(
_rtmp
?
_rtmp
->
isEnabled
()
:
false
)
||
(
_rtsp
?
_rtsp
->
isEnabled
()
:
false
)
||
(
_rtsp
?
_rtsp
->
isEnabled
()
:
false
)
||
(
_ts
?
_ts
->
isEnabled
()
:
false
)
||
(
_ts
?
_ts
->
isEnabled
()
:
false
)
||
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
(
_fmp4
?
_fmp4
->
isEnabled
()
:
false
)
||
(
_fmp4
?
_fmp4
->
isEnabled
()
:
false
)
||
#endif
#endif
(
hls
?
hls
->
isEnabled
()
:
false
)
||
_mp4
;
(
hls
?
hls
->
isEnabled
()
:
false
)
||
_mp4
;
}
}
void
MultiMuxerPrivate
::
onTrackFrame
(
const
Frame
::
Ptr
&
frame
)
{
void
MultiMuxerPrivate
::
onTrackFrame
(
const
Frame
::
Ptr
&
frame
)
{
if
(
_rtmp
)
{
if
(
_rtmp
)
{
_rtmp
->
inputFrame
(
frame
);
_rtmp
->
inputFrame
(
frame
);
}
}
if
(
_rtsp
)
{
if
(
_rtsp
)
{
_rtsp
->
inputFrame
(
frame
);
_rtsp
->
inputFrame
(
frame
);
}
}
if
(
_ts
)
{
if
(
_ts
)
{
_ts
->
inputFrame
(
frame
);
_ts
->
inputFrame
(
frame
);
}
}
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
if
(
_fmp4
)
{
if
(
_fmp4
)
{
_fmp4
->
inputFrame
(
frame
);
_fmp4
->
inputFrame
(
frame
);
}
}
#endif
#endif
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
//此处使用智能指针拷贝来确保线程安全,比互斥锁性能更优
//此处使用智能指针拷贝来确保线程安全,比互斥锁性能更优
auto
hls
=
_hls
;
auto
hls
=
_hls
;
if
(
hls
)
{
if
(
hls
)
{
hls
->
inputFrame
(
frame
);
hls
->
inputFrame
(
frame
);
}
}
auto
mp4
=
_mp4
;
auto
mp4
=
_mp4
;
if
(
mp4
)
{
if
(
mp4
)
{
mp4
->
inputFrame
(
frame
);
mp4
->
inputFrame
(
frame
);
}
}
}
}
static
string
getTrackInfoStr
(
const
TrackSource
*
track_src
){
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
)
{
auto
codec_type
=
track
->
getTrackType
();
auto
codec_type
=
track
->
getTrackType
();
codec_info
<<
track
->
getCodecName
();
codec_info
<<
track
->
getCodecName
();
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
);
codec_info
<<
"["
codec_info
<<
"["
<<
audio_track
->
getAudioSampleRate
()
<<
"/"
<<
audio_track
->
getAudioSampleRate
()
<<
"/"
<<
audio_track
->
getAudioChannel
()
<<
"/"
<<
audio_track
->
getAudioChannel
()
<<
"/"
<<
audio_track
->
getAudioSampleBit
()
<<
"] "
;
<<
audio_track
->
getAudioSampleBit
()
<<
"] "
;
break
;
break
;
}
}
case
TrackVideo
:
{
case
TrackVideo
:
{
auto
video_track
=
dynamic_pointer_cast
<
VideoTrack
>
(
track
);
auto
video_track
=
dynamic_pointer_cast
<
VideoTrack
>
(
track
);
codec_info
<<
"["
codec_info
<<
"["
<<
video_track
->
getVideoWidth
()
<<
"/"
<<
video_track
->
getVideoWidth
()
<<
"/"
<<
video_track
->
getVideoHeight
()
<<
"/"
<<
video_track
->
getVideoHeight
()
<<
"/"
<<
round
(
video_track
->
getVideoFps
())
<<
"] "
;
<<
round
(
video_track
->
getVideoFps
())
<<
"] "
;
break
;
break
;
}
}
default
:
default
:
break
;
break
;
}
}
}
}
return
codec_info
;
return
codec_info
;
}
}
void
MultiMuxerPrivate
::
onAllTrackReady
()
{
void
MultiMuxerPrivate
::
onAllTrackReady
()
{
if
(
_rtmp
)
{
if
(
_rtmp
)
{
_rtmp
->
onAllTrackReady
();
_rtmp
->
onAllTrackReady
();
}
}
if
(
_rtsp
)
{
if
(
_rtsp
)
{
_rtsp
->
onAllTrackReady
();
_rtsp
->
onAllTrackReady
();
}
}
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
if
(
_fmp4
)
{
if
(
_fmp4
)
{
_fmp4
->
onAllTrackReady
();
_fmp4
->
onAllTrackReady
();
}
}
#endif
#endif
if
(
_track_listener
)
{
if
(
_track_listener
)
{
_track_listener
->
onAllTrackReady
();
_track_listener
->
onAllTrackReady
();
}
}
InfoL
<<
"stream: "
<<
_stream_url
<<
" , codec info: "
<<
getTrackInfoStr
(
this
);
InfoL
<<
"stream: "
<<
_stream_url
<<
" , codec info: "
<<
getTrackInfoStr
(
this
);
}
}
///////////////////////////////MultiMediaSourceMuxer//////////////////////////////////
///////////////////////////////MultiMediaSourceMuxer//////////////////////////////////
MultiMediaSourceMuxer
::~
MultiMediaSourceMuxer
()
{}
MultiMediaSourceMuxer
::~
MultiMediaSourceMuxer
()
{}
MultiMediaSourceMuxer
::
MultiMediaSourceMuxer
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
float
dur_sec
,
MultiMediaSourceMuxer
::
MultiMediaSourceMuxer
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
float
dur_sec
,
bool
enable_rtsp
,
bool
enable_rtmp
,
bool
enable_hls
,
bool
enable_mp4
)
{
bool
enable_rtsp
,
bool
enable_rtmp
,
bool
enable_hls
,
bool
enable_mp4
)
{
_muxer
.
reset
(
new
MultiMuxerPrivate
(
vhost
,
app
,
stream
,
dur_sec
,
enable_rtsp
,
enable_rtmp
,
enable_hls
,
enable_mp4
));
_muxer
.
reset
(
new
MultiMuxerPrivate
(
vhost
,
app
,
stream
,
dur_sec
,
enable_rtsp
,
enable_rtmp
,
enable_hls
,
enable_mp4
));
_muxer
->
setTrackListener
(
this
);
_muxer
->
setTrackListener
(
this
);
}
}
void
MultiMediaSourceMuxer
::
setMediaListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
)
{
void
MultiMediaSourceMuxer
::
setMediaListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
)
{
setDelegate
(
listener
);
setDelegate
(
listener
);
//拦截事件
//拦截事件
_muxer
->
setMediaListener
(
shared_from_this
());
_muxer
->
setMediaListener
(
shared_from_this
());
}
}
void
MultiMediaSourceMuxer
::
setTrackListener
(
const
std
::
weak_ptr
<
MultiMuxerPrivate
::
Listener
>
&
listener
)
{
void
MultiMediaSourceMuxer
::
setTrackListener
(
const
std
::
weak_ptr
<
MultiMuxerPrivate
::
Listener
>
&
listener
)
{
_track_listener
=
listener
;
_track_listener
=
listener
;
}
}
int
MultiMediaSourceMuxer
::
totalReaderCount
()
const
{
int
MultiMediaSourceMuxer
::
totalReaderCount
()
const
{
return
_muxer
->
totalReaderCount
();
return
_muxer
->
totalReaderCount
();
}
}
void
MultiMediaSourceMuxer
::
setTimeStamp
(
uint32_t
stamp
)
{
void
MultiMediaSourceMuxer
::
setTimeStamp
(
uint32_t
stamp
)
{
_muxer
->
setTimeStamp
(
stamp
);
_muxer
->
setTimeStamp
(
stamp
);
}
}
vector
<
Track
::
Ptr
>
MultiMediaSourceMuxer
::
getTracks
(
MediaSource
&
sender
,
bool
trackReady
)
const
{
vector
<
Track
::
Ptr
>
MultiMediaSourceMuxer
::
getTracks
(
MediaSource
&
sender
,
bool
trackReady
)
const
{
return
_muxer
->
getTracks
(
trackReady
);
return
_muxer
->
getTracks
(
trackReady
);
}
}
int
MultiMediaSourceMuxer
::
totalReaderCount
(
MediaSource
&
sender
)
{
int
MultiMediaSourceMuxer
::
totalReaderCount
(
MediaSource
&
sender
)
{
auto
listener
=
getDelegate
();
auto
listener
=
getDelegate
();
if
(
!
listener
)
{
if
(
!
listener
)
{
return
totalReaderCount
();
return
totalReaderCount
();
}
}
return
listener
->
totalReaderCount
(
sender
);
return
listener
->
totalReaderCount
(
sender
);
}
}
bool
MultiMediaSourceMuxer
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
bool
MultiMediaSourceMuxer
::
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
{
return
_muxer
->
setupRecord
(
sender
,
type
,
start
,
custom_path
);
return
_muxer
->
setupRecord
(
sender
,
type
,
start
,
custom_path
);
}
}
bool
MultiMediaSourceMuxer
::
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
bool
MultiMediaSourceMuxer
::
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
{
return
_muxer
->
isRecording
(
sender
,
type
);
return
_muxer
->
isRecording
(
sender
,
type
);
}
}
void
MultiMediaSourceMuxer
::
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
){
void
MultiMediaSourceMuxer
::
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
){
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
RtpSender
::
Ptr
rtp_sender
=
std
::
make_shared
<
RtpSender
>
(
atoi
(
ssrc
.
data
()));
RtpSender
::
Ptr
rtp_sender
=
std
::
make_shared
<
RtpSender
>
(
atoi
(
ssrc
.
data
()));
weak_ptr
<
MultiMediaSourceMuxer
>
weak_self
=
shared_from_this
();
weak_ptr
<
MultiMediaSourceMuxer
>
weak_self
=
shared_from_this
();
rtp_sender
->
startSend
(
dst_url
,
dst_port
,
is_udp
,
[
weak_self
,
rtp_sender
,
cb
](
const
SockException
&
ex
)
{
rtp_sender
->
startSend
(
dst_url
,
dst_port
,
is_udp
,
src_port
,
[
weak_self
,
rtp_sender
,
cb
,
ssrc
](
const
SockException
&
ex
)
{
cb
(
ex
);
cb
(
ex
);
auto
strong_self
=
weak_self
.
lock
();
auto
strong_self
=
weak_self
.
lock
();
if
(
!
strong_self
||
ex
)
{
if
(
!
strong_self
||
ex
)
{
return
;
return
;
}
}
for
(
auto
&
track
:
strong_self
->
_muxer
->
getTracks
(
false
))
{
for
(
auto
&
track
:
strong_self
->
_muxer
->
getTracks
(
false
))
{
rtp_sender
->
addTrack
(
track
);
rtp_sender
->
addTrack
(
track
);
}
}
rtp_sender
->
addTrackCompleted
();
rtp_sender
->
addTrackCompleted
();
strong_self
->
_rtp_sender
=
rtp_sender
;
strong_self
->
_rtp_sender
[
ssrc
]
=
rtp_sender
;
});
});
#else
#else
cb
(
SockException
(
Err_other
,
"该功能未启用,编译时请打开ENABLE_RTPPROXY宏"
));
cb
(
SockException
(
Err_other
,
"该功能未启用,编译时请打开ENABLE_RTPPROXY宏"
));
#endif//ENABLE_RTPPROXY
#endif//ENABLE_RTPPROXY
}
}
bool
MultiMediaSourceMuxer
::
stopSendRtp
(
MediaSource
&
sender
){
bool
MultiMediaSourceMuxer
::
stopSendRtp
(
MediaSource
&
sender
,
const
string
&
ssrc
){
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
if
(
_rtp_sender
)
{
map
<
string
,
RtpSender
::
Ptr
>::
iterator
ite
=
_rtp_sender
.
find
(
ssrc
);
_rtp_sender
=
nullptr
;
if
(
ite
!=
_rtp_sender
.
end
())
return
true
;
{
}
ite
->
second
=
nullptr
;
#endif//ENABLE_RTPPROXY
_rtp_sender
.
erase
(
ite
);
return
false
;
return
true
;
}
}
#endif//ENABLE_RTPPROXY
void
MultiMediaSourceMuxer
::
addTrack
(
const
Track
::
Ptr
&
track
)
{
return
false
;
_muxer
->
addTrack
(
track
);
}
}
void
MultiMediaSourceMuxer
::
addTrack
(
const
Track
::
Ptr
&
track
)
{
void
MultiMediaSourceMuxer
::
addTrackCompleted
()
{
_muxer
->
addTrack
(
track
);
_muxer
->
addTrackCompleted
();
}
}
void
MultiMediaSourceMuxer
::
addTrackCompleted
()
{
void
MultiMediaSourceMuxer
::
onAllTrackReady
(){
_muxer
->
addTrackCompleted
();
_muxer
->
setMediaListener
(
shared_from_this
());
}
auto
listener
=
_track_listener
.
lock
();
if
(
listener
){
void
MultiMediaSourceMuxer
::
onAllTrackReady
(){
listener
->
onAllTrackReady
();
_muxer
->
setMediaListener
(
shared_from_this
());
}
auto
listener
=
_track_listener
.
lock
();
}
if
(
listener
){
listener
->
onAllTrackReady
();
void
MultiMediaSourceMuxer
::
resetTracks
()
{
}
_muxer
->
resetTracks
();
}
}
void
MultiMediaSourceMuxer
::
resetTracks
()
{
//该类实现frame级别的时间戳覆盖
_muxer
->
resetTracks
();
class
FrameModifyStamp
:
public
Frame
{
}
public
:
typedef
std
::
shared_ptr
<
FrameModifyStamp
>
Ptr
;
//该类实现frame级别的时间戳覆盖
FrameModifyStamp
(
const
Frame
::
Ptr
&
frame
,
Stamp
&
stamp
){
class
FrameModifyStamp
:
public
Frame
{
_frame
=
frame
;
public
:
//覆盖时间戳
typedef
std
::
shared_ptr
<
FrameModifyStamp
>
Ptr
;
stamp
.
revise
(
frame
->
dts
(),
frame
->
pts
(),
_dts
,
_pts
,
true
);
FrameModifyStamp
(
const
Frame
::
Ptr
&
frame
,
Stamp
&
stamp
){
}
_frame
=
frame
;
~
FrameModifyStamp
()
override
{}
//覆盖时间戳
stamp
.
revise
(
frame
->
dts
(),
frame
->
pts
(),
_dts
,
_pts
,
true
);
uint32_t
dts
()
const
override
{
}
return
_dts
;
~
FrameModifyStamp
()
override
{}
}
uint32_t
dts
()
const
override
{
uint32_t
pts
()
const
override
{
return
_dts
;
return
_pts
;
}
}
uint32_t
pts
()
const
override
{
uint32_t
prefixSize
()
const
override
{
return
_pts
;
return
_frame
->
prefixSize
();
}
}
uint32_t
prefixSize
()
const
override
{
bool
keyFrame
()
const
override
{
return
_frame
->
prefixSize
();
return
_frame
->
keyFrame
();
}
}
bool
keyFrame
()
const
override
{
bool
configFrame
()
const
override
{
return
_frame
->
keyFrame
();
return
_frame
->
configFrame
();
}
}
bool
configFrame
()
const
override
{
bool
cacheAble
()
const
override
{
return
_frame
->
configFrame
();
return
_frame
->
cacheAble
();
}
}
bool
cacheAble
()
const
override
{
char
*
data
()
const
override
{
return
_frame
->
cacheAble
();
return
_frame
->
data
();
}
}
char
*
data
()
const
override
{
uint32_t
size
()
const
override
{
return
_frame
->
data
();
return
_frame
->
size
();
}
}
uint32_t
size
()
const
override
{
CodecId
getCodecId
()
const
override
{
return
_frame
->
size
();
return
_frame
->
getCodecId
();
}
}
private
:
CodecId
getCodecId
()
const
override
{
int64_t
_dts
;
return
_frame
->
getCodecId
();
int64_t
_pts
;
}
Frame
::
Ptr
_frame
;
private
:
};
int64_t
_dts
;
int64_t
_pts
;
void
MultiMediaSourceMuxer
::
inputFrame
(
const
Frame
::
Ptr
&
frame_in
)
{
Frame
::
Ptr
_frame
;
GET_CONFIG
(
bool
,
modify_stamp
,
General
::
kModifyStamp
);
};
auto
frame
=
frame_in
;
if
(
modify_stamp
)
{
void
MultiMediaSourceMuxer
::
inputFrame
(
const
Frame
::
Ptr
&
frame_in
)
{
//开启了时间戳覆盖
GET_CONFIG
(
bool
,
modify_stamp
,
General
::
kModifyStamp
);
frame
=
std
::
make_shared
<
FrameModifyStamp
>
(
frame
,
_stamp
[
frame
->
getTrackType
()]);
auto
frame
=
frame_in
;
}
if
(
modify_stamp
)
{
_muxer
->
inputFrame
(
frame
);
//开启了时间戳覆盖
frame
=
std
::
make_shared
<
FrameModifyStamp
>
(
frame
,
_stamp
[
frame
->
getTrackType
()]);
#if defined(ENABLE_RTPPROXY)
}
auto
rtp_sender
=
_rtp_sender
;
_muxer
->
inputFrame
(
frame
);
if
(
rtp_sender
)
{
rtp_sender
->
inputFrame
(
frame
);
#if defined(ENABLE_RTPPROXY)
}
map
<
string
,
RtpSender
::
Ptr
>::
iterator
ite
=
_rtp_sender
.
begin
();
#endif //ENABLE_RTPPROXY
while
(
ite
!=
_rtp_sender
.
end
())
{
}
if
(
ite
->
second
)
{
bool
MultiMediaSourceMuxer
::
isEnabled
(){
ite
->
second
->
inputFrame
(
frame
);
GET_CONFIG
(
uint32_t
,
stream_none_reader_delay_ms
,
General
::
kStreamNoneReaderDelayMS
);
}
if
(
!
_is_enable
||
_last_check
.
elapsedTime
()
>
stream_none_reader_delay_ms
)
{
ite
++
;
//无人观看时,每次检查是否真的无人观看
}
//有人观看时,则延迟一定时间检查一遍是否无人观看了(节省性能)
#endif //ENABLE_RTPPROXY
#if defined(ENABLE_RTPPROXY)
_is_enable
=
(
_muxer
->
isEnabled
()
||
_rtp_sender
);
}
#else
_is_enable
=
_muxer
->
isEnabled
();
bool
MultiMediaSourceMuxer
::
isEnabled
(){
#endif //ENABLE_RTPPROXY
GET_CONFIG
(
uint32_t
,
stream_none_reader_delay_ms
,
General
::
kStreamNoneReaderDelayMS
);
if
(
_is_enable
)
{
if
(
!
_is_enable
||
_last_check
.
elapsedTime
()
>
stream_none_reader_delay_ms
)
{
//无人观看时,不刷新计时器,因为无人观看时每次都会检查一遍,所以刷新计数器无意义且浪费cpu
//无人观看时,每次检查是否真的无人观看
_last_check
.
resetTime
();
//有人观看时,则延迟一定时间检查一遍是否无人观看了(节省性能)
}
#if defined(ENABLE_RTPPROXY)
}
_is_enable
=
(
_muxer
->
isEnabled
()
||
_rtp_sender
.
size
());
return
_is_enable
;
#else
}
_is_enable
=
_muxer
->
isEnabled
();
#endif //ENABLE_RTPPROXY
if
(
_is_enable
)
{
}
//namespace mediakit
//无人观看时,不刷新计时器,因为无人观看时每次都会检查一遍,所以刷新计数器无意义且浪费cpu
_last_check
.
resetTime
();
}
}
return
_is_enable
;
}
}
//namespace mediakit
src/Common/MultiMediaSourceMuxer.h
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#ifndef ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#ifndef ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#define ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#define ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#include "Common/Stamp.h"
#include "Common/Stamp.h"
#include "Rtp/RtpSender.h"
#include "Rtp/RtpSender.h"
#include "Record/Recorder.h"
#include "Record/Recorder.h"
#include "Record/HlsRecorder.h"
#include "Record/HlsRecorder.h"
#include "Record/HlsMediaSource.h"
#include "Record/HlsMediaSource.h"
#include "Rtsp/RtspMediaSourceMuxer.h"
#include "Rtsp/RtspMediaSourceMuxer.h"
#include "Rtmp/RtmpMediaSourceMuxer.h"
#include "Rtmp/RtmpMediaSourceMuxer.h"
#include "TS/TSMediaSourceMuxer.h"
#include "TS/TSMediaSourceMuxer.h"
#include "FMP4/FMP4MediaSourceMuxer.h"
#include "FMP4/FMP4MediaSourceMuxer.h"
namespace
mediakit
{
namespace
mediakit
{
class
MultiMuxerPrivate
:
public
MediaSink
,
public
std
::
enable_shared_from_this
<
MultiMuxerPrivate
>
{
class
MultiMuxerPrivate
:
public
MediaSink
,
public
std
::
enable_shared_from_this
<
MultiMuxerPrivate
>
{
public
:
public
:
friend
class
MultiMediaSourceMuxer
;
friend
class
MultiMediaSourceMuxer
;
typedef
std
::
shared_ptr
<
MultiMuxerPrivate
>
Ptr
;
typedef
std
::
shared_ptr
<
MultiMuxerPrivate
>
Ptr
;
class
Listener
{
class
Listener
{
public
:
public
:
Listener
()
=
default
;
Listener
()
=
default
;
virtual
~
Listener
()
=
default
;
virtual
~
Listener
()
=
default
;
virtual
void
onAllTrackReady
()
=
0
;
virtual
void
onAllTrackReady
()
=
0
;
};
};
~
MultiMuxerPrivate
()
override
;
~
MultiMuxerPrivate
()
override
;
private
:
private
:
MultiMuxerPrivate
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
float
dur_sec
,
MultiMuxerPrivate
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
float
dur_sec
,
bool
enable_rtsp
,
bool
enable_rtmp
,
bool
enable_hls
,
bool
enable_mp4
);
bool
enable_rtsp
,
bool
enable_rtmp
,
bool
enable_hls
,
bool
enable_mp4
);
void
resetTracks
()
override
;
void
resetTracks
()
override
;
void
setMediaListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
);
void
setMediaListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
);
int
totalReaderCount
()
const
;
int
totalReaderCount
()
const
;
void
setTimeStamp
(
uint32_t
stamp
);
void
setTimeStamp
(
uint32_t
stamp
);
void
setTrackListener
(
Listener
*
listener
);
void
setTrackListener
(
Listener
*
listener
);
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
);
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
);
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
);
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
);
bool
isEnabled
();
bool
isEnabled
();
void
onTrackReady
(
const
Track
::
Ptr
&
track
)
override
;
void
onTrackReady
(
const
Track
::
Ptr
&
track
)
override
;
void
onTrackFrame
(
const
Frame
::
Ptr
&
frame
)
override
;
void
onTrackFrame
(
const
Frame
::
Ptr
&
frame
)
override
;
void
onAllTrackReady
()
override
;
void
onAllTrackReady
()
override
;
private
:
private
:
string
_stream_url
;
string
_stream_url
;
Listener
*
_track_listener
=
nullptr
;
Listener
*
_track_listener
=
nullptr
;
RtmpMediaSourceMuxer
::
Ptr
_rtmp
;
RtmpMediaSourceMuxer
::
Ptr
_rtmp
;
RtspMediaSourceMuxer
::
Ptr
_rtsp
;
RtspMediaSourceMuxer
::
Ptr
_rtsp
;
HlsRecorder
::
Ptr
_hls
;
HlsRecorder
::
Ptr
_hls
;
MediaSinkInterface
::
Ptr
_mp4
;
MediaSinkInterface
::
Ptr
_mp4
;
TSMediaSourceMuxer
::
Ptr
_ts
;
TSMediaSourceMuxer
::
Ptr
_ts
;
#if defined(ENABLE_MP4)
#if defined(ENABLE_MP4)
FMP4MediaSourceMuxer
::
Ptr
_fmp4
;
FMP4MediaSourceMuxer
::
Ptr
_fmp4
;
#endif
#endif
std
::
weak_ptr
<
MediaSourceEvent
>
_listener
;
std
::
weak_ptr
<
MediaSourceEvent
>
_listener
;
};
};
class
MultiMediaSourceMuxer
:
public
MediaSourceEventInterceptor
,
public
MediaSinkInterface
,
public
MultiMuxerPrivate
::
Listener
,
public
std
::
enable_shared_from_this
<
MultiMediaSourceMuxer
>
{
class
MultiMediaSourceMuxer
:
public
MediaSourceEventInterceptor
,
public
MediaSinkInterface
,
public
MultiMuxerPrivate
::
Listener
,
public
std
::
enable_shared_from_this
<
MultiMediaSourceMuxer
>
{
public
:
public
:
typedef
MultiMuxerPrivate
::
Listener
Listener
;
typedef
MultiMuxerPrivate
::
Listener
Listener
;
typedef
std
::
shared_ptr
<
MultiMediaSourceMuxer
>
Ptr
;
typedef
std
::
shared_ptr
<
MultiMediaSourceMuxer
>
Ptr
;
~
MultiMediaSourceMuxer
()
override
;
~
MultiMediaSourceMuxer
()
override
;
MultiMediaSourceMuxer
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
float
dur_sec
=
0
.
0
,
MultiMediaSourceMuxer
(
const
string
&
vhost
,
const
string
&
app
,
const
string
&
stream
,
float
dur_sec
=
0
.
0
,
bool
enable_rtsp
=
true
,
bool
enable_rtmp
=
true
,
bool
enable_hls
=
true
,
bool
enable_mp4
=
false
);
bool
enable_rtsp
=
true
,
bool
enable_rtmp
=
true
,
bool
enable_hls
=
true
,
bool
enable_mp4
=
false
);
/**
/**
* 设置事件监听器
* 设置事件监听器
* @param listener 监听器
* @param listener 监听器
*/
*/
void
setMediaListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
);
void
setMediaListener
(
const
std
::
weak_ptr
<
MediaSourceEvent
>
&
listener
);
/**
/**
* 随着Track就绪事件监听器
* 随着Track就绪事件监听器
* @param listener 事件监听器
* @param listener 事件监听器
*/
*/
void
setTrackListener
(
const
std
::
weak_ptr
<
MultiMuxerPrivate
::
Listener
>
&
listener
);
void
setTrackListener
(
const
std
::
weak_ptr
<
MultiMuxerPrivate
::
Listener
>
&
listener
);
/**
/**
* 返回总的消费者个数
* 返回总的消费者个数
*/
*/
int
totalReaderCount
()
const
;
int
totalReaderCount
()
const
;
/**
/**
* 判断是否生效(是否正在转其他协议)
* 判断是否生效(是否正在转其他协议)
*/
*/
bool
isEnabled
();
bool
isEnabled
();
/**
/**
* 设置MediaSource时间戳
* 设置MediaSource时间戳
* @param stamp 时间戳
* @param stamp 时间戳
*/
*/
void
setTimeStamp
(
uint32_t
stamp
);
void
setTimeStamp
(
uint32_t
stamp
);
/////////////////////////////////MediaSourceEvent override/////////////////////////////////
/////////////////////////////////MediaSourceEvent override/////////////////////////////////
/**
/**
* 获取所有Track
* 获取所有Track
* @param trackReady 是否筛选过滤未就绪的track
* @param trackReady 是否筛选过滤未就绪的track
* @return 所有Track
* @return 所有Track
*/
*/
vector
<
Track
::
Ptr
>
getTracks
(
MediaSource
&
sender
,
bool
trackReady
=
true
)
const
override
;
vector
<
Track
::
Ptr
>
getTracks
(
MediaSource
&
sender
,
bool
trackReady
=
true
)
const
override
;
/**
/**
* 观看总人数
* 观看总人数
* @param sender 事件发送者
* @param sender 事件发送者
* @return 观看总人数
* @return 观看总人数
*/
*/
int
totalReaderCount
(
MediaSource
&
sender
)
override
;
int
totalReaderCount
(
MediaSource
&
sender
)
override
;
/**
/**
* 设置录制状态
* 设置录制状态
* @param type 录制类型
* @param type 录制类型
* @param start 开始或停止
* @param start 开始或停止
* @param custom_path 开启录制时,指定自定义路径
* @param custom_path 开启录制时,指定自定义路径
* @return 是否设置成功
* @return 是否设置成功
*/
*/
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
override
;
bool
setupRecord
(
MediaSource
&
sender
,
Recorder
::
type
type
,
bool
start
,
const
string
&
custom_path
)
override
;
/**
/**
* 获取录制状态
* 获取录制状态
* @param type 录制类型
* @param type 录制类型
* @return 录制状态
* @return 录制状态
*/
*/
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
override
;
bool
isRecording
(
MediaSource
&
sender
,
Recorder
::
type
type
)
override
;
/**
/**
* 开始发送ps-rtp流
* 开始发送ps-rtp流
* @param dst_url 目标ip或域名
* @param dst_url 目标ip或域名
* @param dst_port 目标端口
* @param dst_port 目标端口
* @param ssrc rtp的ssrc
* @param ssrc rtp的ssrc
* @param is_udp 是否为udp
* @param is_udp 是否为udp
* @param cb 启动成功或失败回调
* @param cb 启动成功或失败回调
*/
*/
void
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
)
override
;
void
startSendRtp
(
MediaSource
&
sender
,
const
string
&
dst_url
,
uint16_t
dst_port
,
const
string
&
ssrc
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
)
override
;
/**
/**
* 停止ps-rtp发送
* 停止ps-rtp发送
* @return 是否成功
* @return 是否成功
*/
*/
bool
stopSendRtp
(
MediaSource
&
sender
)
override
;
bool
stopSendRtp
(
MediaSource
&
sender
,
const
string
&
ssrc
)
override
;
/////////////////////////////////MediaSinkInterface override/////////////////////////////////
/////////////////////////////////MediaSinkInterface override/////////////////////////////////
/**
/**
* 添加track,内部会调用Track的clone方法
* 添加track,内部会调用Track的clone方法
* 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系
* 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系
* @param track 添加音频或视频轨道
* @param track 添加音频或视频轨道
*/
*/
void
addTrack
(
const
Track
::
Ptr
&
track
)
override
;
void
addTrack
(
const
Track
::
Ptr
&
track
)
override
;
/**
/**
* 添加track完毕
* 添加track完毕
*/
*/
void
addTrackCompleted
()
override
;
void
addTrackCompleted
()
override
;
/**
/**
* 重置track
* 重置track
*/
*/
void
resetTracks
()
override
;
void
resetTracks
()
override
;
/**
/**
* 写入帧数据
* 写入帧数据
* @param frame 帧
* @param frame 帧
*/
*/
void
inputFrame
(
const
Frame
::
Ptr
&
frame
)
override
;
void
inputFrame
(
const
Frame
::
Ptr
&
frame
)
override
;
/////////////////////////////////MultiMuxerPrivate::Listener override/////////////////////////////////
/////////////////////////////////MultiMuxerPrivate::Listener override/////////////////////////////////
/**
/**
* 所有track全部就绪
* 所有track全部就绪
*/
*/
void
onAllTrackReady
()
override
;
void
onAllTrackReady
()
override
;
private
:
private
:
bool
_is_enable
=
false
;
bool
_is_enable
=
false
;
Ticker
_last_check
;
Ticker
_last_check
;
Stamp
_stamp
[
2
];
Stamp
_stamp
[
2
];
MultiMuxerPrivate
::
Ptr
_muxer
;
MultiMuxerPrivate
::
Ptr
_muxer
;
std
::
weak_ptr
<
MultiMuxerPrivate
::
Listener
>
_track_listener
;
std
::
weak_ptr
<
MultiMuxerPrivate
::
Listener
>
_track_listener
;
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
RtpSender
::
Ptr
_rtp_sender
;
map
<
string
,
RtpSender
::
Ptr
>
_rtp_sender
;
#endif //ENABLE_RTPPROXY
#endif //ENABLE_RTPPROXY
};
};
}
//namespace mediakit
}
//namespace mediakit
#endif //ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#endif //ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
src/Rtp/RtpSender.cpp
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
#include "RtpSender.h"
#include "RtpSender.h"
#include "Rtsp/RtspSession.h"
#include "Rtsp/RtspSession.h"
#include "Thread/WorkThreadPool.h"
#include "Thread/WorkThreadPool.h"
#include "RtpCache.h"
#include "RtpCache.h"
namespace
mediakit
{
namespace
mediakit
{
RtpSender
::
RtpSender
(
uint32_t
ssrc
,
uint8_t
payload_type
)
{
RtpSender
::
RtpSender
(
uint32_t
ssrc
,
uint8_t
payload_type
)
{
_poller
=
EventPollerPool
::
Instance
().
getPoller
();
_poller
=
EventPollerPool
::
Instance
().
getPoller
();
_interface
=
std
::
make_shared
<
RtpCachePS
>
([
this
](
std
::
shared_ptr
<
List
<
Buffer
::
Ptr
>
>
list
)
{
_interface
=
std
::
make_shared
<
RtpCachePS
>
([
this
](
std
::
shared_ptr
<
List
<
Buffer
::
Ptr
>
>
list
)
{
onFlushRtpList
(
std
::
move
(
list
));
onFlushRtpList
(
std
::
move
(
list
));
},
ssrc
,
payload_type
);
},
ssrc
,
payload_type
);
}
}
RtpSender
::~
RtpSender
()
{
RtpSender
::~
RtpSender
()
{
}
}
void
RtpSender
::
startSend
(
const
string
&
dst_url
,
uint16_t
dst_port
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
){
void
RtpSender
::
startSend
(
const
string
&
dst_url
,
uint16_t
dst_port
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
){
_is_udp
=
is_udp
;
_is_udp
=
is_udp
;
_socket
=
Socket
::
createSocket
(
_poller
,
false
);
_socket
=
Socket
::
createSocket
(
_poller
,
false
);
_dst_url
=
dst_url
;
_dst_url
=
dst_url
;
_dst_port
=
dst_port
;
_dst_port
=
dst_port
;
weak_ptr
<
RtpSender
>
weak_self
=
shared_from_this
();
_src_port
=
src_port
;
if
(
is_udp
)
{
weak_ptr
<
RtpSender
>
weak_self
=
shared_from_this
();
_socket
->
bindUdpSock
(
0
);
if
(
is_udp
)
{
auto
poller
=
_poller
;
_socket
->
bindUdpSock
(
src_port
);
WorkThreadPool
::
Instance
().
getPoller
()
->
async
([
cb
,
dst_url
,
dst_port
,
weak_self
,
poller
]()
{
auto
poller
=
_poller
;
struct
sockaddr
addr
;
WorkThreadPool
::
Instance
().
getPoller
()
->
async
([
cb
,
dst_url
,
dst_port
,
weak_self
,
poller
]()
{
//切换线程目的是为了dns解析放在后台线程执行
struct
sockaddr
addr
;
if
(
!
SockUtil
::
getDomainIP
(
dst_url
.
data
(),
dst_port
,
addr
))
{
//切换线程目的是为了dns解析放在后台线程执行
poller
->
async
([
dst_url
,
cb
]()
{
if
(
!
SockUtil
::
getDomainIP
(
dst_url
.
data
(),
dst_port
,
addr
))
{
//切回自己的线程
poller
->
async
([
dst_url
,
cb
]()
{
cb
(
SockException
(
Err_dns
,
StrPrinter
<<
"dns解析域名失败:"
<<
dst_url
));
//切回自己的线程
});
cb
(
SockException
(
Err_dns
,
StrPrinter
<<
"dns解析域名失败:"
<<
dst_url
));
return
;
});
}
return
;
}
//dns解析成功
poller
->
async
([
addr
,
weak_self
,
cb
]()
{
//dns解析成功
//切回自己的线程
poller
->
async
([
addr
,
weak_self
,
cb
]()
{
cb
(
SockException
());
//切回自己的线程
auto
strong_self
=
weak_self
.
lock
();
cb
(
SockException
());
if
(
strong_self
)
{
auto
strong_self
=
weak_self
.
lock
();
strong_self
->
_socket
->
setSendPeerAddr
(
&
addr
);
if
(
strong_self
)
{
strong_self
->
onConnect
();
strong_self
->
_socket
->
setSendPeerAddr
(
&
addr
);
}
strong_self
->
onConnect
();
});
}
});
});
}
else
{
});
_socket
->
connect
(
dst_url
,
dst_port
,
[
cb
,
weak_self
](
const
SockException
&
err
)
{
}
else
{
cb
(
err
);
_socket
->
connect
(
dst_url
,
dst_port
,
[
cb
,
weak_self
](
const
SockException
&
err
)
{
auto
strong_self
=
weak_self
.
lock
();
cb
(
err
);
if
(
strong_self
&&
!
err
)
{
auto
strong_self
=
weak_self
.
lock
();
//tcp连接成功
if
(
strong_self
&&
!
err
)
{
strong_self
->
onConnect
();
//tcp连接成功
}
strong_self
->
onConnect
();
});
}
}
},
5.0
F
,
"0.0.0.0"
,
src_port
);
}
}
}
void
RtpSender
::
onConnect
(){
_is_connect
=
true
;
void
RtpSender
::
onConnect
(){
//加大发送缓存,防止udp丢包之类的问题
_is_connect
=
true
;
SockUtil
::
setSendBuf
(
_socket
->
rawFD
(),
4
*
1024
*
1024
);
//加大发送缓存,防止udp丢包之类的问题
if
(
!
_is_udp
)
{
SockUtil
::
setSendBuf
(
_socket
->
rawFD
(),
4
*
1024
*
1024
);
//关闭tcp no_delay并开启MSG_MORE, 提高发送性能
if
(
!
_is_udp
)
{
SockUtil
::
setNoDelay
(
_socket
->
rawFD
(),
false
);
//关闭tcp no_delay并开启MSG_MORE, 提高发送性能
_socket
->
setSendFlags
(
SOCKET_DEFAULE_FLAGS
|
FLAG_MORE
);
SockUtil
::
setNoDelay
(
_socket
->
rawFD
(),
false
);
}
_socket
->
setSendFlags
(
SOCKET_DEFAULE_FLAGS
|
FLAG_MORE
);
//连接建立成功事件
}
weak_ptr
<
RtpSender
>
weak_self
=
shared_from_this
();
//连接建立成功事件
_socket
->
setOnErr
([
weak_self
](
const
SockException
&
err
)
{
weak_ptr
<
RtpSender
>
weak_self
=
shared_from_this
();
auto
strong_self
=
weak_self
.
lock
();
_socket
->
setOnErr
([
weak_self
](
const
SockException
&
err
)
{
if
(
strong_self
)
{
auto
strong_self
=
weak_self
.
lock
();
strong_self
->
onErr
(
err
);
if
(
strong_self
)
{
}
strong_self
->
onErr
(
err
);
});
}
InfoL
<<
"开始发送 rtp:"
<<
_socket
->
get_peer_ip
()
<<
":"
<<
_socket
->
get_peer_port
()
<<
", 是否为udp方式:"
<<
_is_udp
;
});
}
InfoL
<<
"开始发送 rtp:"
<<
_socket
->
get_peer_ip
()
<<
":"
<<
_socket
->
get_peer_port
()
<<
", 是否为udp方式:"
<<
_is_udp
;
}
void
RtpSender
::
addTrack
(
const
Track
::
Ptr
&
track
){
_interface
->
addTrack
(
track
);
void
RtpSender
::
addTrack
(
const
Track
::
Ptr
&
track
){
}
_interface
->
addTrack
(
track
);
}
void
RtpSender
::
addTrackCompleted
(){
_interface
->
addTrackCompleted
();
void
RtpSender
::
addTrackCompleted
(){
}
_interface
->
addTrackCompleted
();
}
void
RtpSender
::
resetTracks
(){
_interface
->
resetTracks
();
void
RtpSender
::
resetTracks
(){
}
_interface
->
resetTracks
();
}
//此函数在其他线程执行
void
RtpSender
::
inputFrame
(
const
Frame
::
Ptr
&
frame
)
{
//此函数在其他线程执行
if
(
_is_connect
)
{
void
RtpSender
::
inputFrame
(
const
Frame
::
Ptr
&
frame
)
{
//连接成功后才做实质操作(节省cpu资源)
if
(
_is_connect
)
{
_interface
->
inputFrame
(
frame
);
//连接成功后才做实质操作(节省cpu资源)
}
_interface
->
inputFrame
(
frame
);
}
}
}
//此函数在其他线程执行
void
RtpSender
::
onFlushRtpList
(
shared_ptr
<
List
<
Buffer
::
Ptr
>
>
rtp_list
)
{
//此函数在其他线程执行
if
(
!
_is_connect
){
void
RtpSender
::
onFlushRtpList
(
shared_ptr
<
List
<
Buffer
::
Ptr
>
>
rtp_list
)
{
//连接成功后才能发送数据
if
(
!
_is_connect
){
return
;
//连接成功后才能发送数据
}
return
;
}
auto
is_udp
=
_is_udp
;
auto
socket
=
_socket
;
auto
is_udp
=
_is_udp
;
_poller
->
async
([
rtp_list
,
is_udp
,
socket
]()
{
auto
socket
=
_socket
;
int
i
=
0
;
_poller
->
async
([
rtp_list
,
is_udp
,
socket
]()
{
int
size
=
rtp_list
->
size
();
int
i
=
0
;
rtp_list
->
for_each
([
&
](
Buffer
::
Ptr
&
packet
)
{
int
size
=
rtp_list
->
size
();
if
(
is_udp
)
{
rtp_list
->
for_each
([
&
](
Buffer
::
Ptr
&
packet
)
{
//udp模式,rtp over tcp前4个字节可以忽略
if
(
is_udp
)
{
socket
->
send
(
std
::
make_shared
<
BufferRtp
>
(
std
::
move
(
packet
),
4
),
nullptr
,
0
,
++
i
==
size
);
//udp模式,rtp over tcp前4个字节可以忽略
}
else
{
socket
->
send
(
std
::
make_shared
<
BufferRtp
>
(
std
::
move
(
packet
),
4
),
nullptr
,
0
,
++
i
==
size
);
//tcp模式, rtp over tcp前2个字节可以忽略,只保留后续rtp长度的2个字节
}
else
{
socket
->
send
(
std
::
make_shared
<
BufferRtp
>
(
std
::
move
(
packet
),
2
),
nullptr
,
0
,
++
i
==
size
);
//tcp模式, rtp over tcp前2个字节可以忽略,只保留后续rtp长度的2个字节
}
socket
->
send
(
std
::
make_shared
<
BufferRtp
>
(
std
::
move
(
packet
),
2
),
nullptr
,
0
,
++
i
==
size
);
});
}
});
});
}
});
}
void
RtpSender
::
onErr
(
const
SockException
&
ex
,
bool
is_connect
)
{
_is_connect
=
false
;
void
RtpSender
::
onErr
(
const
SockException
&
ex
,
bool
is_connect
)
{
_is_connect
=
false
;
//监听socket断开事件,方便重连
if
(
is_connect
)
{
//监听socket断开事件,方便重连
WarnL
<<
"重连"
<<
_dst_url
<<
":"
<<
_dst_port
<<
"失败, 原因为:"
<<
ex
.
what
();
if
(
is_connect
)
{
}
else
{
WarnL
<<
"重连"
<<
_dst_url
<<
":"
<<
_dst_port
<<
"失败, 原因为:"
<<
ex
.
what
();
WarnL
<<
"停止发送 rtp:"
<<
_dst_url
<<
":"
<<
_dst_port
<<
", 原因为:"
<<
ex
.
what
();
}
else
{
}
WarnL
<<
"停止发送 rtp:"
<<
_dst_url
<<
":"
<<
_dst_port
<<
", 原因为:"
<<
ex
.
what
();
}
weak_ptr
<
RtpSender
>
weak_self
=
shared_from_this
();
_connect_timer
=
std
::
make_shared
<
Timer
>
(
10.0
,
[
weak_self
]()
{
weak_ptr
<
RtpSender
>
weak_self
=
shared_from_this
();
auto
strong_self
=
weak_self
.
lock
();
_connect_timer
=
std
::
make_shared
<
Timer
>
(
10.0
,
[
weak_self
]()
{
if
(
!
strong_self
)
{
auto
strong_self
=
weak_self
.
lock
();
return
false
;
if
(
!
strong_self
)
{
}
return
false
;
strong_self
->
startSend
(
strong_self
->
_dst_url
,
strong_self
->
_dst_port
,
strong_self
->
_is_udp
,
[
weak_self
](
const
SockException
&
ex
){
}
auto
strong_self
=
weak_self
.
lock
();
strong_self
->
startSend
(
strong_self
->
_dst_url
,
strong_self
->
_dst_port
,
strong_self
->
_is_udp
,
strong_self
->
_src_port
,
[
weak_self
](
const
SockException
&
ex
){
if
(
strong_self
&&
ex
)
{
auto
strong_self
=
weak_self
.
lock
();
//连接失败且本对象未销毁,那么重试连接
if
(
strong_self
&&
ex
)
{
strong_self
->
onErr
(
ex
,
true
);
//连接失败且本对象未销毁,那么重试连接
}
strong_self
->
onErr
(
ex
,
true
);
});
}
return
false
;
});
},
_poller
);
return
false
;
}
},
_poller
);
}
}
//namespace mediakit
}
//namespace mediakit
#endif// defined(ENABLE_RTPPROXY)
#endif// defined(ENABLE_RTPPROXY)
\ No newline at end of file
src/Rtp/RtpSender.h
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#ifndef ZLMEDIAKIT_RTPSENDER_H
#ifndef ZLMEDIAKIT_RTPSENDER_H
#define ZLMEDIAKIT_RTPSENDER_H
#define ZLMEDIAKIT_RTPSENDER_H
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
#include "PSEncoder.h"
#include "PSEncoder.h"
#include "Extension/CommonRtp.h"
#include "Extension/CommonRtp.h"
namespace
mediakit
{
namespace
mediakit
{
//rtp发送客户端,支持发送GB28181协议
//rtp发送客户端,支持发送GB28181协议
class
RtpSender
:
public
MediaSinkInterface
,
public
std
::
enable_shared_from_this
<
RtpSender
>
{
class
RtpSender
:
public
MediaSinkInterface
,
public
std
::
enable_shared_from_this
<
RtpSender
>
{
public
:
public
:
typedef
std
::
shared_ptr
<
RtpSender
>
Ptr
;
typedef
std
::
shared_ptr
<
RtpSender
>
Ptr
;
~
RtpSender
()
override
;
~
RtpSender
()
override
;
/**
/**
* 构造函数,创建GB28181 RTP发送客户端
* 构造函数,创建GB28181 RTP发送客户端
* @param ssrc rtp的ssrc
* @param ssrc rtp的ssrc
* @param payload_type 国标中ps-rtp的pt一般为96
* @param payload_type 国标中ps-rtp的pt一般为96
*/
*/
RtpSender
(
uint32_t
ssrc
,
uint8_t
payload_type
=
96
);
RtpSender
(
uint32_t
ssrc
,
uint8_t
payload_type
=
96
);
/**
/**
* 开始发送ps-rtp包
* 开始发送ps-rtp包
* @param dst_url 目标ip或域名
* @param dst_url 目标ip或域名
* @param dst_port 目标端口
* @param dst_port 目标端口
* @param is_udp 是否采用udp方式发送rtp
* @param is_udp 是否采用udp方式发送rtp
* @param cb 连接目标端口是否成功的回调
* @param cb 连接目标端口是否成功的回调
*/
*/
void
startSend
(
const
string
&
dst_url
,
uint16_t
dst_port
,
bool
is_udp
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
);
void
startSend
(
const
string
&
dst_url
,
uint16_t
dst_port
,
bool
is_udp
,
uint16_t
src_port
,
const
function
<
void
(
const
SockException
&
ex
)
>
&
cb
);
/**
/**
* 输入帧数据
* 输入帧数据
*/
*/
void
inputFrame
(
const
Frame
::
Ptr
&
frame
)
override
;
void
inputFrame
(
const
Frame
::
Ptr
&
frame
)
override
;
/**
/**
* 添加track,内部会调用Track的clone方法
* 添加track,内部会调用Track的clone方法
* 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系
* 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系
* @param track
* @param track
*/
*/
virtual
void
addTrack
(
const
Track
::
Ptr
&
track
)
override
;
virtual
void
addTrack
(
const
Track
::
Ptr
&
track
)
override
;
/**
/**
* 添加所有Track完毕
* 添加所有Track完毕
*/
*/
virtual
void
addTrackCompleted
()
override
;
virtual
void
addTrackCompleted
()
override
;
/**
/**
* 重置track
* 重置track
*/
*/
virtual
void
resetTracks
()
override
;
virtual
void
resetTracks
()
override
;
private
:
private
:
//合并写输出
//合并写输出
void
onFlushRtpList
(
std
::
shared_ptr
<
List
<
Buffer
::
Ptr
>
>
rtp_list
);
void
onFlushRtpList
(
std
::
shared_ptr
<
List
<
Buffer
::
Ptr
>
>
rtp_list
);
//udp/tcp连接成功回调
//udp/tcp连接成功回调
void
onConnect
();
void
onConnect
();
//异常断开socket事件
//异常断开socket事件
void
onErr
(
const
SockException
&
ex
,
bool
is_connect
=
false
);
void
onErr
(
const
SockException
&
ex
,
bool
is_connect
=
false
);
private
:
private
:
bool
_is_udp
;
bool
_is_udp
;
bool
_is_connect
=
false
;
bool
_is_connect
=
false
;
string
_dst_url
;
string
_dst_url
;
uint16_t
_dst_port
;
uint16_t
_dst_port
;
Socket
::
Ptr
_socket
;
uint16_t
_src_port
;
EventPoller
::
Ptr
_poller
;
Socket
::
Ptr
_socket
;
Timer
::
Ptr
_connect_timer
;
EventPoller
::
Ptr
_poller
;
MediaSinkInterface
::
Ptr
_interface
;
Timer
::
Ptr
_connect_timer
;
};
MediaSinkInterface
::
Ptr
_interface
;
};
}
//namespace mediakit
#endif// defined(ENABLE_RTPPROXY)
}
//namespace mediakit
#endif //ZLMEDIAKIT_RTPSENDER_H
#endif// defined(ENABLE_RTPPROXY)
#endif //ZLMEDIAKIT_RTPSENDER_H
src/Rtp/RtpServer.cpp
查看文件 @
50927548
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
*
* Use of this source code is governed by MIT license that can be found in the
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
* may be found in the AUTHORS file in the root of the source tree.
*/
*/
#if defined(ENABLE_RTPPROXY)
#if defined(ENABLE_RTPPROXY)
#include "RtpServer.h"
#include "RtpServer.h"
#include "RtpSelector.h"
#include "RtpSelector.h"
namespace
mediakit
{
namespace
mediakit
{
RtpServer
::
RtpServer
()
{
RtpServer
::
RtpServer
()
{
}
}
RtpServer
::~
RtpServer
()
{
RtpServer
::~
RtpServer
()
{
if
(
_on_clearup
){
if
(
_on_clearup
){
_on_clearup
();
_on_clearup
();
}
}
}
}
void
RtpServer
::
start
(
uint16_t
local_port
,
const
string
&
stream_id
,
bool
enable_tcp
,
const
char
*
local_ip
)
{
void
RtpServer
::
start
(
uint16_t
local_port
,
const
string
&
stream_id
,
bool
enable_tcp
,
const
char
*
local_ip
)
{
//创建udp服务器
//创建udp服务器
Socket
::
Ptr
udp_server
=
Socket
::
createSocket
(
nullptr
,
false
);
Socket
::
Ptr
udp_server
=
Socket
::
createSocket
(
nullptr
,
false
);
if
(
local_port
==
0
)
{
if
(
local_port
==
0
)
{
//随机端口,rtp端口采用偶数
//随机端口,rtp端口采用偶数
Socket
::
Ptr
rtcp_server
=
Socket
::
createSocket
(
nullptr
,
false
);
Socket
::
Ptr
rtcp_server
=
Socket
::
createSocket
(
nullptr
,
false
);
auto
pair
=
std
::
make_pair
(
udp_server
,
rtcp_server
);
auto
pair
=
std
::
make_pair
(
udp_server
,
rtcp_server
);
makeSockPair
(
pair
,
local_ip
);
makeSockPair
(
pair
,
local_ip
);
//取偶数端口
//取偶数端口
udp_server
=
pair
.
first
;
udp_server
=
pair
.
first
;
}
else
if
(
!
udp_server
->
bindUdpSock
(
local_port
,
local_ip
))
{
}
else
if
(
!
udp_server
->
bindUdpSock
(
local_port
,
local_ip
))
{
//用户指定端口
//用户指定端口
throw
std
::
runtime_error
(
StrPrinter
<<
"bindUdpSock on "
<<
local_ip
<<
":"
<<
local_port
<<
" failed:"
<<
get_uv_errmsg
(
true
));
throw
std
::
runtime_error
(
StrPrinter
<<
"bindUdpSock on "
<<
local_ip
<<
":"
<<
local_port
<<
" failed:"
<<
get_uv_errmsg
(
true
));
}
}
//设置udp socket读缓存
//设置udp socket读缓存
SockUtil
::
setRecvBuf
(
udp_server
->
rawFD
(),
4
*
1024
*
1024
);
SockUtil
::
setRecvBuf
(
udp_server
->
rawFD
(),
4
*
1024
*
1024
);
TcpServer
::
Ptr
tcp_server
;
TcpServer
::
Ptr
tcp_server
;
if
(
enable_tcp
)
{
if
(
enable_tcp
)
{
//创建tcp服务器
//创建tcp服务器
tcp_server
=
std
::
make_shared
<
TcpServer
>
(
udp_server
->
getPoller
());
tcp_server
=
std
::
make_shared
<
TcpServer
>
(
udp_server
->
getPoller
());
(
*
tcp_server
)[
RtpSession
::
kStreamID
]
=
stream_id
;
(
*
tcp_server
)[
RtpSession
::
kStreamID
]
=
stream_id
;
tcp_server
->
start
<
RtpSession
>
(
udp_server
->
get_local_port
(),
local_ip
);
tcp_server
->
start
<
RtpSession
>
(
udp_server
->
get_local_port
(),
local_ip
);
}
}
RtpProcess
::
Ptr
process
;
RtpProcess
::
Ptr
process
;
if
(
!
stream_id
.
empty
())
{
if
(
!
stream_id
.
empty
())
{
//指定了流id,那么一个端口一个流(不管是否包含多个ssrc的多个流,绑定rtp源后,会筛选掉ip端口不匹配的流)
//指定了流id,那么一个端口一个流(不管是否包含多个ssrc的多个流,绑定rtp源后,会筛选掉ip端口不匹配的流)
process
=
RtpSelector
::
Instance
().
getProcess
(
stream_id
,
true
);
process
=
RtpSelector
::
Instance
().
getProcess
(
stream_id
,
true
);
udp_server
->
setOnRead
([
udp_server
,
process
](
const
Buffer
::
Ptr
&
buf
,
struct
sockaddr
*
addr
,
int
)
{
//udp_server->setOnRead([udp_server, process](const Buffer::Ptr &buf, struct sockaddr *addr, int) {
process
->
inputRtp
(
true
,
udp_server
,
buf
->
data
(),
buf
->
size
(),
addr
);
// process->inputRtp(true, udp_server, buf->data(), buf->size(), addr);
});
//});
}
else
{
weak_ptr
<
Socket
>
weak_sock
=
udp_server
;
//未指定流id,一个端口多个流,通过ssrc来分流
udp_server
->
setOnRead
([
weak_sock
,
process
](
const
Buffer
::
Ptr
&
buf
,
struct
sockaddr
*
addr
,
int
)
{
auto
&
ref
=
RtpSelector
::
Instance
();
process
->
inputRtp
(
true
,
weak_sock
.
lock
(),
buf
->
data
(),
buf
->
size
(),
addr
);
udp_server
->
setOnRead
([
&
ref
,
udp_server
](
const
Buffer
::
Ptr
&
buf
,
struct
sockaddr
*
addr
,
int
)
{
});
ref
.
inputRtp
(
udp_server
,
buf
->
data
(),
buf
->
size
(),
addr
);
}
else
{
});
//未指定流id,一个端口多个流,通过ssrc来分流
}
auto
&
ref
=
RtpSelector
::
Instance
();
//udp_server->setOnRead([&ref, udp_server](const Buffer::Ptr &buf, struct sockaddr *addr, int) {
_on_clearup
=
[
udp_server
,
process
,
stream_id
]()
{
// ref.inputRtp(udp_server, buf->data(), buf->size(), addr);
//去除循环引用
//});
udp_server
->
setOnRead
(
nullptr
);
weak_ptr
<
Socket
>
weak_sock
=
udp_server
;
if
(
process
)
{
udp_server
->
setOnRead
([
&
ref
,
weak_sock
](
const
Buffer
::
Ptr
&
buf
,
struct
sockaddr
*
addr
,
int
)
{
//删除rtp处理器
ref
.
inputRtp
(
weak_sock
.
lock
(),
buf
->
data
(),
buf
->
size
(),
addr
);
RtpSelector
::
Instance
().
delProcess
(
stream_id
,
process
.
get
());
});
}
}
};
_on_clearup
=
[
udp_server
,
process
,
stream_id
]()
{
_tcp_server
=
tcp_server
;
//去除循环引用
_udp_server
=
udp_server
;
//udp_server->setOnRead(nullptr);
_rtp_process
=
process
;
if
(
process
)
{
}
//删除rtp处理器
RtpSelector
::
Instance
().
delProcess
(
stream_id
,
process
.
get
());
void
RtpServer
::
setOnDetach
(
const
function
<
void
()
>
&
cb
){
}
if
(
_rtp_process
){
};
_rtp_process
->
setOnDetach
(
cb
);
}
_tcp_server
=
tcp_server
;
}
_udp_server
=
udp_server
;
_rtp_process
=
process
;
EventPoller
::
Ptr
RtpServer
::
getPoller
()
{
}
return
_udp_server
->
getPoller
();
}
void
RtpServer
::
setOnDetach
(
const
function
<
void
()
>
&
cb
){
if
(
_rtp_process
){
uint16_t
RtpServer
::
getPort
()
{
_rtp_process
->
setOnDetach
(
cb
);
return
_udp_server
?
_udp_server
->
get_local_port
()
:
0
;
}
}
}
}
//namespace mediakit
EventPoller
::
Ptr
RtpServer
::
getPoller
()
{
return
_udp_server
->
getPoller
();
}
uint16_t
RtpServer
::
getPort
()
{
return
_udp_server
?
_udp_server
->
get_local_port
()
:
0
;
}
}
//namespace mediakit
#endif//defined(ENABLE_RTPPROXY)
#endif//defined(ENABLE_RTPPROXY)
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论