2022-07-15 20:01:00 +08:00
/*
2023-12-09 16:23:51 +08:00
* Copyright ( c ) 2016 - present The ZLMediaKit project authors . All Rights Reserved .
2019-06-11 09:25:54 +08:00
*
2023-12-09 16:23:51 +08:00
* This file is part of ZLMediaKit ( https : //github.com/ZLMediaKit/ZLMediaKit).
2019-06-11 09:25:54 +08:00
*
2023-12-09 16:23:51 +08:00
* Use of this source code is governed by MIT - like license that can be found in the
2020-04-04 20:30:09 +08:00
* 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 .
2019-06-11 09:25:54 +08:00
*/
2024-06-19 14:06:02 +08:00
# include <exception>
2020-06-10 10:33:48 +08:00
# include <sys/stat.h>
2020-12-06 22:36:44 +08:00
# include <math.h>
2019-05-20 11:22:59 +08:00
# include <signal.h>
2023-11-24 10:44:08 +08:00
# ifdef _WIN32
# include <io.h>
# include <iostream>
# include <tchar.h>
# endif // _WIN32
2019-05-20 11:22:59 +08:00
# include <functional>
# include <unordered_map>
2024-01-26 10:26:30 +08:00
# include <regex>
2023-11-24 10:44:08 +08:00
# include "Util/MD5.h"
2019-05-20 11:22:59 +08:00
# include "Util/util.h"
2023-11-24 10:44:08 +08:00
# include "Util/File.h"
2019-05-20 11:22:59 +08:00
# include "Util/logger.h"
# include "Util/onceToken.h"
# include "Util/NoticeCenter.h"
2023-11-24 10:44:08 +08:00
# include "Network/TcpServer.h"
# include "Network/UdpServer.h"
# include "Thread/WorkThreadPool.h"
2019-05-21 09:26:24 +08:00
# ifdef ENABLE_MYSQL
2019-05-20 11:22:59 +08:00
# include "Util/SqlPool.h"
2019-05-21 09:26:24 +08:00
# endif //ENABLE_MYSQL
2023-11-24 10:44:08 +08:00
# include "WebApi.h"
# include "WebHook.h"
# include "FFmpegSource.h"
2019-05-20 11:22:59 +08:00
# include "Common/config.h"
# include "Common/MediaSource.h"
# include "Http/HttpSession.h"
2023-11-24 10:44:08 +08:00
# include "Http/HttpRequester.h"
2019-05-20 16:26:04 +08:00
# include "Player/PlayerProxy.h"
2021-06-16 19:40:08 +08:00
# include "Pusher/PusherProxy.h"
2024-06-09 10:52:10 +08:00
# include "Rtp/RtpProcess.h"
2023-11-24 10:44:08 +08:00
# include "Record/MP4Reader.h"
2020-07-02 22:23:43 +08:00
# if defined(ENABLE_RTPPROXY)
# include "Rtp/RtpServer.h"
# endif
2023-11-24 10:44:08 +08:00
2021-03-24 16:52:41 +08:00
# ifdef ENABLE_WEBRTC
2021-10-15 16:27:17 +08:00
# include "../webrtc/WebRtcPlayer.h"
# include "../webrtc/WebRtcPusher.h"
2021-10-16 10:52:28 +08:00
# include "../webrtc/WebRtcEchoTest.h"
2021-03-24 16:52:41 +08:00
# endif
2021-12-19 17:39:50 +08:00
2022-08-12 18:09:44 +08:00
# if defined(ENABLE_VERSION)
2024-03-24 17:18:18 +08:00
# include "ZLMVersion.h"
2022-08-12 18:09:44 +08:00
# endif
2024-04-13 20:35:59 +08:00
# if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_X264) && defined (ENABLE_FFMPEG)
2024-03-16 22:56:32 +08:00
# include "VideoStack.h"
# endif
2022-02-02 20:34:50 +08:00
using namespace std ;
using namespace Json ;
2019-05-20 11:22:59 +08:00
using namespace toolkit ;
using namespace mediakit ;
namespace API {
# define API_FIELD "api."
2019-06-24 14:51:49 +08:00
const string kApiDebug = API_FIELD " apiDebug " ;
const string kSecret = API_FIELD " secret " ;
2020-05-09 00:06:36 +08:00
const string kSnapRoot = API_FIELD " snapRoot " ;
2020-06-10 10:33:48 +08:00
const string kDefaultSnap = API_FIELD " defaultSnap " ;
2023-12-02 15:58:40 +08:00
const string kDownloadRoot = API_FIELD " downloadRoot " ;
2019-05-20 17:12:00 +08:00
2019-05-20 11:22:59 +08:00
static onceToken token ( [ ] ( ) {
2019-05-20 16:26:04 +08:00
mINI : : Instance ( ) [ kApiDebug ] = " 1 " ;
2019-05-20 17:12:00 +08:00
mINI : : Instance ( ) [ kSecret ] = " 035c73f7-bb6b-4889-a715-d9eb2d1925cc " ;
2020-05-09 00:06:36 +08:00
mINI : : Instance ( ) [ kSnapRoot ] = " ./www/snap/ " ;
2020-06-10 10:33:48 +08:00
mINI : : Instance ( ) [ kDefaultSnap ] = " ./www/logo.png " ;
2023-12-02 15:58:40 +08:00
mINI : : Instance ( ) [ kDownloadRoot ] = " ./www " ;
2019-05-20 11:22:59 +08:00
} ) ;
} //namespace API
2020-12-27 22:14:59 +08:00
using HttpApi = function < void ( const Parser & parser , const HttpSession : : HttpResponseInvoker & invoker , SockInfo & sender ) > ;
2024-09-19 14:53:50 +08:00
// http api列表 [AUTO-TRANSLATED:a05e9d9d]
// http api list
2023-12-02 14:56:59 +08:00
static map < string , HttpApi , StrCaseCompare > s_map_api ;
2020-01-17 11:44:20 +08:00
2020-12-06 21:50:41 +08:00
static void responseApi ( const Json : : Value & res , const HttpSession : : HttpResponseInvoker & invoker ) {
GET_CONFIG ( string , charSet , Http : : kCharSet ) ;
HttpSession : : KeyValue headerOut ;
headerOut [ " Content-Type " ] = string ( " application/json; charset= " ) + charSet ;
2021-01-02 21:24:06 +08:00
invoker ( 200 , headerOut , res . toStyledString ( ) ) ;
2020-12-06 21:50:41 +08:00
} ;
static void responseApi ( int code , const string & msg , const HttpSession : : HttpResponseInvoker & invoker ) {
Json : : Value res ;
res [ " code " ] = code ;
res [ " msg " ] = msg ;
responseApi ( res , invoker ) ;
}
static ApiArgsType getAllArgs ( const Parser & parser ) ;
2020-12-27 22:14:59 +08:00
static HttpApi toApi ( const function < void ( API_ARGS_MAP_ASYNC ) > & cb ) {
2020-12-06 21:50:41 +08:00
return [ cb ] ( const Parser & parser , const HttpSession : : HttpResponseInvoker & invoker , SockInfo & sender ) {
GET_CONFIG ( string , charSet , Http : : kCharSet ) ;
HttpSession : : KeyValue headerOut ;
headerOut [ " Content-Type " ] = string ( " application/json; charset= " ) + charSet ;
Json : : Value val ;
val [ " code " ] = API : : Success ;
2024-09-19 14:53:50 +08:00
// 参数解析成map [AUTO-TRANSLATED:20e11ff3]
// Parse parameters into a map
2020-12-06 21:50:41 +08:00
auto args = getAllArgs ( parser ) ;
2024-03-23 22:46:30 +08:00
cb ( sender , headerOut , ArgsMap ( parser , args ) , val , invoker ) ;
2020-12-06 21:50:41 +08:00
} ;
}
2020-12-27 22:14:59 +08:00
static HttpApi toApi ( const function < void ( API_ARGS_MAP ) > & cb ) {
return toApi ( [ cb ] ( API_ARGS_MAP_ASYNC ) {
cb ( API_ARGS_VALUE ) ;
2021-01-02 21:24:06 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2020-01-17 11:44:20 +08:00
} ) ;
}
2020-12-27 22:14:59 +08:00
static HttpApi toApi ( const function < void ( API_ARGS_JSON_ASYNC ) > & cb ) {
return [ cb ] ( const Parser & parser , const HttpSession : : HttpResponseInvoker & invoker , SockInfo & sender ) {
GET_CONFIG ( string , charSet , Http : : kCharSet ) ;
HttpSession : : KeyValue headerOut ;
headerOut [ " Content-Type " ] = string ( " application/json; charset= " ) + charSet ;
2021-08-12 16:07:31 +08:00
Json : : Value val ;
val [ " code " ] = API : : Success ;
2020-12-27 22:14:59 +08:00
if ( parser [ " Content-Type " ] . find ( " application/json " ) = = string : : npos ) {
throw InvalidArgsException ( " 该接口只支持json格式的请求 " ) ;
}
2024-09-19 14:53:50 +08:00
// 参数解析成json对象然后处理 [AUTO-TRANSLATED:6f23397b]
// Parse parameters into a JSON object and then process
2021-08-12 16:07:31 +08:00
Json : : Value args ;
2020-12-27 22:14:59 +08:00
Json : : Reader reader ;
2023-06-10 11:04:52 +08:00
reader . parse ( parser . content ( ) , args ) ;
2020-12-27 22:14:59 +08:00
2024-03-23 22:46:30 +08:00
cb ( sender , headerOut , ArgsJson ( parser , args ) , val , invoker ) ;
2020-12-27 22:14:59 +08:00
} ;
}
static HttpApi toApi ( const function < void ( API_ARGS_JSON ) > & cb ) {
return toApi ( [ cb ] ( API_ARGS_JSON_ASYNC ) {
2021-08-12 16:07:31 +08:00
cb ( API_ARGS_VALUE ) ;
2021-01-02 21:24:06 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2020-12-27 22:14:59 +08:00
} ) ;
}
2021-03-31 17:15:26 +08:00
static HttpApi toApi ( const function < void ( API_ARGS_STRING_ASYNC ) > & cb ) {
return [ cb ] ( const Parser & parser , const HttpSession : : HttpResponseInvoker & invoker , SockInfo & sender ) {
GET_CONFIG ( string , charSet , Http : : kCharSet ) ;
HttpSession : : KeyValue headerOut ;
headerOut [ " Content-Type " ] = string ( " application/json; charset= " ) + charSet ;
Json : : Value val ;
val [ " code " ] = API : : Success ;
2024-03-23 22:46:30 +08:00
cb ( sender , headerOut , ArgsString ( parser , ( string & ) parser . content ( ) ) , val , invoker ) ;
2021-03-31 17:15:26 +08:00
} ;
}
static HttpApi toApi ( const function < void ( API_ARGS_STRING ) > & cb ) {
return toApi ( [ cb ] ( API_ARGS_STRING_ASYNC ) {
cb ( API_ARGS_VALUE ) ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
}
2020-12-27 22:14:59 +08:00
void api_regist ( const string & api_path , const function < void ( API_ARGS_MAP ) > & func ) {
s_map_api . emplace ( api_path , toApi ( func ) ) ;
}
void api_regist ( const string & api_path , const function < void ( API_ARGS_MAP_ASYNC ) > & func ) {
s_map_api . emplace ( api_path , toApi ( func ) ) ;
}
void api_regist ( const string & api_path , const function < void ( API_ARGS_JSON ) > & func ) {
s_map_api . emplace ( api_path , toApi ( func ) ) ;
2020-01-17 11:44:20 +08:00
}
2019-05-20 11:22:59 +08:00
2020-12-27 22:14:59 +08:00
void api_regist ( const string & api_path , const function < void ( API_ARGS_JSON_ASYNC ) > & func ) {
s_map_api . emplace ( api_path , toApi ( func ) ) ;
}
2020-12-06 21:50:41 +08:00
2021-03-31 17:15:26 +08:00
void api_regist ( const string & api_path , const function < void ( API_ARGS_STRING ) > & func ) {
s_map_api . emplace ( api_path , toApi ( func ) ) ;
}
void api_regist ( const string & api_path , const function < void ( API_ARGS_STRING_ASYNC ) > & func ) {
s_map_api . emplace ( api_path , toApi ( func ) ) ;
}
2024-09-19 14:53:50 +08:00
// 获取HTTP请求中url参数、content参数 [AUTO-TRANSLATED:d161a1e1]
// Get URL parameters and content parameters from the HTTP request
2019-05-20 16:26:04 +08:00
static ApiArgsType getAllArgs ( const Parser & parser ) {
ApiArgsType allArgs ;
2019-12-29 10:49:04 +08:00
if ( parser [ " Content-Type " ] . find ( " application/x-www-form-urlencoded " ) = = 0 ) {
2023-06-10 11:04:52 +08:00
auto contentArgs = parser . parseArgs ( parser . content ( ) ) ;
2019-05-20 11:22:59 +08:00
for ( auto & pr : contentArgs ) {
2024-04-20 22:25:21 +08:00
allArgs [ pr . first ] = strCoding : : UrlDecodeComponent ( pr . second ) ;
2019-05-20 11:22:59 +08:00
}
2019-12-29 10:49:04 +08:00
} else if ( parser [ " Content-Type " ] . find ( " application/json " ) = = 0 ) {
2019-05-20 16:26:04 +08:00
try {
2023-06-10 11:04:52 +08:00
stringstream ss ( parser . content ( ) ) ;
2019-05-20 16:26:04 +08:00
Value jsonArgs ;
ss > > jsonArgs ;
auto keys = jsonArgs . getMemberNames ( ) ;
2019-12-29 10:49:04 +08:00
for ( auto key = keys . begin ( ) ; key ! = keys . end ( ) ; + + key ) {
2019-05-20 16:26:04 +08:00
allArgs [ * key ] = jsonArgs [ * key ] . asString ( ) ;
}
2019-12-29 10:49:04 +08:00
} catch ( std : : exception & ex ) {
2019-05-20 16:26:04 +08:00
WarnL < < ex . what ( ) ;
2019-05-20 11:22:59 +08:00
}
2019-12-29 10:49:04 +08:00
} else if ( ! parser [ " Content-Type " ] . empty ( ) ) {
2019-05-20 16:26:04 +08:00
WarnL < < " invalid Content-Type: " < < parser [ " Content-Type " ] ;
2019-05-20 11:22:59 +08:00
}
2019-05-20 16:26:04 +08:00
2019-12-29 10:49:04 +08:00
for ( auto & pr : parser . getUrlArgs ( ) ) {
allArgs [ pr . first ] = pr . second ;
2019-05-20 16:26:04 +08:00
}
2020-09-21 14:32:56 +08:00
return allArgs ;
2019-05-20 11:22:59 +08:00
}
2021-02-21 21:28:17 +08:00
extern uint64_t getTotalMemUsage ( ) ;
2021-12-29 20:48:15 +08:00
extern uint64_t getTotalMemBlock ( ) ;
2021-12-27 17:40:15 +08:00
extern uint64_t getThisThreadMemUsage ( ) ;
2021-12-29 20:48:15 +08:00
extern uint64_t getThisThreadMemBlock ( ) ;
extern std : : vector < size_t > getBlockTypeSize ( ) ;
extern uint64_t getTotalMemBlockByType ( int type ) ;
extern uint64_t getThisThreadMemBlockByType ( int type ) ;
2021-02-21 21:28:17 +08:00
2022-07-29 16:23:35 +08:00
static void * web_api_tag = nullptr ;
2019-05-20 11:22:59 +08:00
static inline void addHttpListener ( ) {
2019-05-28 17:14:36 +08:00
GET_CONFIG ( bool , api_debug , API : : kApiDebug ) ;
2024-09-19 14:53:50 +08:00
// 注册监听kBroadcastHttpRequest事件 [AUTO-TRANSLATED:4af22c90]
// Register to listen for the kBroadcastHttpRequest event
2022-07-29 16:23:35 +08:00
NoticeCenter : : Instance ( ) . addListener ( & web_api_tag , Broadcast : : kBroadcastHttpRequest , [ ] ( BroadcastHttpRequestArgs ) {
2023-06-10 11:04:52 +08:00
auto it = s_map_api . find ( parser . url ( ) ) ;
2019-05-20 11:22:59 +08:00
if ( it = = s_map_api . end ( ) ) {
return ;
}
2024-09-19 14:53:50 +08:00
// 该api已被消费 [AUTO-TRANSLATED:db0872fc]
// This API has been consumed
2019-05-20 11:22:59 +08:00
consumed = true ;
2020-12-06 21:50:41 +08:00
2019-05-20 17:34:39 +08:00
if ( api_debug ) {
2021-08-12 21:02:07 +08:00
auto newInvoker = [ invoker , parser ] ( int code , const HttpSession : : KeyValue & headerOut , const HttpBody : : Ptr & body ) {
2024-09-19 14:53:50 +08:00
// body默认为空 [AUTO-TRANSLATED:4fd4ecc8]
// The body is empty by default
2021-01-19 16:05:38 +08:00
ssize_t size = 0 ;
2019-10-28 17:23:16 +08:00
if ( body & & body - > remainSize ( ) ) {
2024-09-19 14:53:50 +08:00
// 有body, 获取body大小 [AUTO-TRANSLATED:ab1c417d]
// If there is a body, get the body size
2019-10-28 17:23:16 +08:00
size = body - > remainSize ( ) ;
}
2019-05-20 17:34:39 +08:00
2023-07-02 12:45:07 +08:00
LogContextCapture log ( getLogger ( ) , toolkit : : LDebug , __FILE__ , " http api debug " , __LINE__ ) ;
2023-06-10 11:04:52 +08:00
log < < " \r \n # request: \r \n " < < parser . method ( ) < < " " < < parser . fullUrl ( ) < < " \r \n " ;
2021-08-12 21:02:07 +08:00
log < < " # header: \r \n " ;
for ( auto & pr : parser . getHeader ( ) ) {
log < < pr . first < < " : " < < pr . second < < " \r \n " ;
}
2023-06-10 11:04:52 +08:00
auto & content = parser . content ( ) ;
2021-08-12 21:02:07 +08:00
log < < " # content: \r \n " < < ( content . size ( ) > 4 * 1024 ? content . substr ( 0 , 4 * 1024 ) : content ) < < " \r \n " ;
2021-08-23 11:00:20 +08:00
if ( size > 0 & & size < 4 * 1024 ) {
2021-08-12 21:02:07 +08:00
auto response = body - > readData ( size ) ;
log < < " # response: \r \n " < < response - > data ( ) < < " \r \n " ;
invoker ( code , headerOut , response ) ;
2020-12-06 21:50:41 +08:00
} else {
2021-08-12 21:02:07 +08:00
log < < " # response size: " < < size < < " \r \n " ;
2021-01-02 21:24:06 +08:00
invoker ( code , headerOut , body ) ;
2019-10-28 17:23:16 +08:00
}
2019-05-20 17:34:39 +08:00
} ;
2020-12-06 21:50:41 +08:00
( ( HttpSession : : HttpResponseInvoker & ) invoker ) = newInvoker ;
2019-05-20 17:34:39 +08:00
}
2019-05-20 11:22:59 +08:00
2019-05-20 17:34:39 +08:00
try {
2020-12-06 21:50:41 +08:00
it - > second ( parser , invoker , sender ) ;
} catch ( ApiRetException & ex ) {
responseApi ( ex . code ( ) , ex . what ( ) , invoker ) ;
2024-05-09 18:07:08 +08:00
auto helper = static_cast < SocketHelper & > ( sender ) . shared_from_this ( ) ;
helper - > getPoller ( ) - > async ( [ helper , ex ] ( ) { helper - > shutdown ( SockException ( Err_shutdown , ex . what ( ) ) ) ; } , false ) ;
2019-05-21 09:26:24 +08:00
}
# ifdef ENABLE_MYSQL
catch ( SqlException & ex ) {
2020-12-06 21:50:41 +08:00
responseApi ( API : : SqlFailed , StrPrinter < < " 操作数据库失败: " < < ex . what ( ) < < " : " < < ex . getSql ( ) , invoker ) ;
2019-05-21 09:26:24 +08:00
}
# endif // ENABLE_MYSQL
catch ( std : : exception & ex ) {
2020-12-06 21:50:41 +08:00
responseApi ( API : : Exception , ex . what ( ) , invoker ) ;
2019-05-20 17:34:39 +08:00
}
2019-05-20 11:22:59 +08:00
} ) ;
}
2024-03-10 16:31:20 +08:00
template < typename Type >
class ServiceController {
public :
using Pointer = std : : shared_ptr < Type > ;
std : : unordered_map < std : : string , Pointer > _map ;
mutable std : : recursive_mutex _mtx ;
void clear ( ) {
decltype ( _map ) copy ;
{
std : : lock_guard < std : : recursive_mutex > lck ( _mtx ) ;
copy . swap ( _map ) ;
}
}
size_t erase ( const std : : string & key ) {
std : : lock_guard < std : : recursive_mutex > lck ( _mtx ) ;
return _map . erase ( key ) ;
}
2024-06-01 18:14:52 +08:00
size_t size ( ) {
std : : lock_guard < std : : recursive_mutex > lck ( _mtx ) ;
return _map . size ( ) ;
}
2024-03-10 16:31:20 +08:00
Pointer find ( const std : : string & key ) const {
std : : lock_guard < std : : recursive_mutex > lck ( _mtx ) ;
auto it = _map . find ( key ) ;
if ( it = = _map . end ( ) ) {
return nullptr ;
}
return it - > second ;
}
2024-11-01 10:47:18 +08:00
void for_each ( const std : : function < void ( const std : : string & , const Pointer & ) > & cb ) {
std : : lock_guard < std : : recursive_mutex > lck ( _mtx ) ;
auto it = _map . begin ( ) ;
while ( it ! = _map . end ( ) ) {
cb ( it - > first , it - > second ) ;
it + + ;
}
}
2024-03-10 16:31:20 +08:00
template < class . . . _Args >
Pointer make ( const std : : string & key , _Args & & . . . __args ) {
// assert(!find(key));
auto server = std : : make_shared < Type > ( std : : forward < _Args > ( __args ) . . . ) ;
std : : lock_guard < std : : recursive_mutex > lck ( _mtx ) ;
auto it = _map . emplace ( key , server ) ;
assert ( it . second ) ;
return server ;
}
template < class . . . _Args >
Pointer makeWithAction ( const std : : string & key , function < void ( Pointer ) > action , _Args & & . . . __args ) {
// assert(!find(key));
auto server = std : : make_shared < Type > ( std : : forward < _Args > ( __args ) . . . ) ;
action ( server ) ;
std : : lock_guard < std : : recursive_mutex > lck ( _mtx ) ;
auto it = _map . emplace ( key , server ) ;
assert ( it . second ) ;
return server ;
}
} ;
2024-09-19 14:53:50 +08:00
// 拉流代理器列表 [AUTO-TRANSLATED:6dcfb11f]
// Pull stream proxy list
2024-03-10 16:31:20 +08:00
static ServiceController < PlayerProxy > s_player_proxy ;
2019-05-20 17:03:04 +08:00
2024-09-19 14:53:50 +08:00
// 推流代理器列表 [AUTO-TRANSLATED:539a1bcf]
// Push stream proxy list
2024-03-10 16:31:20 +08:00
static ServiceController < PusherProxy > s_pusher_proxy ;
2021-06-16 19:40:08 +08:00
2024-09-19 14:53:50 +08:00
// FFmpeg拉流代理器列表 [AUTO-TRANSLATED:4bdedf10]
// FFmpeg pull stream proxy list
2024-03-10 16:31:20 +08:00
static ServiceController < FFmpegSource > s_ffmpeg_src ;
2019-06-06 18:28:33 +08:00
2020-07-02 22:23:43 +08:00
# if defined(ENABLE_RTPPROXY)
2024-09-19 14:53:50 +08:00
// rtp服务器列表 [AUTO-TRANSLATED:2e362a8c]
// RTP server list
2024-03-10 16:31:20 +08:00
static ServiceController < RtpServer > s_rtp_server ;
2020-07-02 22:23:43 +08:00
# endif
2021-06-17 10:41:26 +08:00
static inline string getPusherKey ( const string & schema , const string & vhost , const string & app , const string & stream ,
const string & dst_url ) {
return schema + " / " + vhost + " / " + app + " / " + stream + " / " + MD5 ( dst_url ) . hexdigest ( ) ;
}
2022-09-07 11:06:39 +08:00
static void fillSockInfo ( Value & val , SockInfo * info ) {
val [ " peer_ip " ] = info - > get_peer_ip ( ) ;
val [ " peer_port " ] = info - > get_peer_port ( ) ;
val [ " local_port " ] = info - > get_local_port ( ) ;
val [ " local_ip " ] = info - > get_local_ip ( ) ;
val [ " identifier " ] = info - > getIdentifier ( ) ;
}
2023-06-17 10:28:01 +08:00
void dumpMediaTuple ( const MediaTuple & tuple , Json : : Value & item ) {
item [ VHOST_KEY ] = tuple . vhost ;
item [ " app " ] = tuple . app ;
item [ " stream " ] = tuple . stream ;
2024-03-30 14:46:39 +08:00
item [ " params " ] = tuple . params ;
2023-06-17 10:28:01 +08:00
}
2024-11-01 10:47:18 +08:00
Value ToJson ( const PusherProxy : : Ptr & p ) {
Value item ;
item [ " url " ] = p - > getUrl ( ) ;
item [ " status " ] = p - > getStatus ( ) ;
item [ " liveSecs " ] = p - > getLiveSecs ( ) ;
item [ " rePublishCount " ] = p - > getRePublishCount ( ) ;
if ( auto src = p - > getSrc ( ) ) {
dumpMediaTuple ( src - > getMediaTuple ( ) , item [ " src " ] ) ;
}
return item ;
}
Value ToJson ( const PlayerProxy : : Ptr & p ) {
Value item ;
item [ " url " ] = p - > getUrl ( ) ;
item [ " status " ] = p - > getStatus ( ) ;
item [ " liveSecs " ] = p - > getLiveSecs ( ) ;
item [ " rePullCount " ] = p - > getRePullCount ( ) ;
item [ " totalReaderCount " ] = p - > totalReaderCount ( ) ;
dumpMediaTuple ( p - > getMediaTuple ( ) , item [ " src " ] ) ;
return item ;
}
2021-04-08 17:34:13 +08:00
Value makeMediaSourceJson ( MediaSource & media ) {
Value item ;
item [ " schema " ] = media . getSchema ( ) ;
2023-06-17 10:28:01 +08:00
dumpMediaTuple ( media . getMediaTuple ( ) , item ) ;
2021-04-08 17:34:13 +08:00
item [ " createStamp " ] = ( Json : : UInt64 ) media . getCreateStamp ( ) ;
item [ " aliveSecond " ] = ( Json : : UInt64 ) media . getAliveSecond ( ) ;
item [ " bytesSpeed " ] = media . getBytesSpeed ( ) ;
item [ " readerCount " ] = media . readerCount ( ) ;
item [ " totalReaderCount " ] = media . totalReaderCount ( ) ;
item [ " originType " ] = ( int ) media . getOriginType ( ) ;
item [ " originTypeStr " ] = getOriginTypeString ( media . getOriginType ( ) ) ;
item [ " originUrl " ] = media . getOriginUrl ( ) ;
2021-05-22 09:31:59 +08:00
item [ " isRecordingMP4 " ] = media . isRecording ( Recorder : : type_mp4 ) ;
item [ " isRecordingHLS " ] = media . isRecording ( Recorder : : type_hls ) ;
2021-04-08 17:34:13 +08:00
auto originSock = media . getOriginSock ( ) ;
if ( originSock ) {
2022-09-07 11:06:39 +08:00
fillSockInfo ( item [ " originSock " ] , originSock . get ( ) ) ;
2021-04-08 17:34:13 +08:00
} else {
item [ " originSock " ] = Json : : nullValue ;
}
2024-09-19 14:53:50 +08:00
// getLossRate有线程安全问题; 使用getMediaInfo接口才能获取丢包率; getMediaList接口将忽略丢包率 [AUTO-TRANSLATED:b2e927c6]
// getLossRate has thread safety issues; use the getMediaInfo interface to get the packet loss rate; the getMediaList interface will ignore the packet loss rate
2022-11-26 10:14:37 +08:00
auto current_thread = false ;
try { current_thread = media . getOwnerPoller ( ) - > isCurrentThread ( ) ; } catch ( . . . ) { }
2022-09-03 16:47:37 +08:00
float last_loss = - 1 ;
2021-04-11 11:39:49 +08:00
for ( auto & track : media . getTracks ( false ) ) {
2021-04-08 17:34:13 +08:00
Value obj ;
auto codec_type = track - > getTrackType ( ) ;
obj [ " codec_id " ] = track - > getCodecId ( ) ;
obj [ " codec_id_name " ] = track - > getCodecName ( ) ;
obj [ " ready " ] = track - > ready ( ) ;
obj [ " codec_type " ] = codec_type ;
2022-06-11 14:45:56 +08:00
if ( current_thread ) {
2024-09-19 14:53:50 +08:00
// rtp推流只有一个统计器, 但是可能有多个track, 如果短时间多次获取间隔丢包率, 第二次会获取为-1 [AUTO-TRANSLATED:5bfbc951]
// RTP push stream has only one statistics, but may have multiple tracks. If you get the interval packet loss rate multiple times in a short time, the second time will get -1
2022-09-03 16:47:37 +08:00
auto loss = media . getLossRate ( codec_type ) ;
if ( loss = = - 1 ) {
loss = last_loss ;
} else {
last_loss = loss ;
}
obj [ " loss " ] = loss ;
2022-06-11 14:45:56 +08:00
}
2023-02-05 22:04:14 +08:00
obj [ " frames " ] = track - > getFrames ( ) ;
2023-10-28 11:09:31 +08:00
obj [ " duration " ] = track - > getDuration ( ) ;
2021-04-08 17:34:13 +08:00
switch ( codec_type ) {
case TrackAudio : {
auto audio_track = dynamic_pointer_cast < AudioTrack > ( track ) ;
obj [ " sample_rate " ] = audio_track - > getAudioSampleRate ( ) ;
obj [ " channels " ] = audio_track - > getAudioChannel ( ) ;
obj [ " sample_bit " ] = audio_track - > getAudioSampleBit ( ) ;
break ;
}
case TrackVideo : {
auto video_track = dynamic_pointer_cast < VideoTrack > ( track ) ;
obj [ " width " ] = video_track - > getVideoWidth ( ) ;
obj [ " height " ] = video_track - > getVideoHeight ( ) ;
2023-02-05 22:04:14 +08:00
obj [ " key_frames " ] = video_track - > getVideoKeyFrames ( ) ;
新增支持获取gop大小与间隔信息: #1570
getMediaList/getMediaInfo接口、on_media_changed hook新增支持字段如下:
{
"codec_id" : 0,
"codec_id_name" : "H264",
"codec_type" : 0,
"fps" : 0.0,
"frames" : 1119, #累计接收帧数,不包含sei/aud/sps/pps等不能解码的帧
"gop_interval_ms" : 1993, #gop间隔时间,单位毫秒
"gop_size" : 60, #gop大小,单位帧数
"height" : 556,
"key_frames" : 21, #累计接收关键帧数
"ready" : true,
"width" : 990
}
2023-02-20 16:10:22 +08:00
int gop_size = video_track - > getVideoGopSize ( ) ;
int gop_interval_ms = video_track - > getVideoGopInterval ( ) ;
float fps = video_track - > getVideoFps ( ) ;
2023-03-23 18:14:28 +08:00
if ( fps < = 1 & & gop_interval_ms ) {
新增支持获取gop大小与间隔信息: #1570
getMediaList/getMediaInfo接口、on_media_changed hook新增支持字段如下:
{
"codec_id" : 0,
"codec_id_name" : "H264",
"codec_type" : 0,
"fps" : 0.0,
"frames" : 1119, #累计接收帧数,不包含sei/aud/sps/pps等不能解码的帧
"gop_interval_ms" : 1993, #gop间隔时间,单位毫秒
"gop_size" : 60, #gop大小,单位帧数
"height" : 556,
"key_frames" : 21, #累计接收关键帧数
"ready" : true,
"width" : 990
}
2023-02-20 16:10:22 +08:00
fps = gop_size * 1000.0 / gop_interval_ms ;
}
obj [ " fps " ] = round ( fps ) ;
obj [ " gop_size " ] = gop_size ;
obj [ " gop_interval_ms " ] = gop_interval_ms ;
2021-04-08 17:34:13 +08:00
break ;
}
default :
break ;
}
item [ " tracks " ] . append ( obj ) ;
}
return item ;
}
2022-07-24 22:30:59 +08:00
# if defined(ENABLE_RTPPROXY)
2024-07-09 10:42:10 +08:00
uint16_t openRtpServer ( uint16_t local_port , const mediakit : : MediaTuple & tuple , int tcp_mode , const string & local_ip , bool re_use_port , uint32_t ssrc , int only_track , bool multiplex ) {
auto key = tuple . shortUrl ( ) ;
if ( s_rtp_server . find ( key ) ) {
2024-09-19 14:53:50 +08:00
// 为了防止RtpProcess所有权限混乱的问题, 不允许重复添加相同的key [AUTO-TRANSLATED:06c7b14c]
// To prevent the problem of all permissions being messed up in RtpProcess, duplicate keys are not allowed to be added
2022-06-22 10:31:53 +08:00
return 0 ;
}
2024-07-09 10:42:10 +08:00
auto server = s_rtp_server . makeWithAction ( key , [ & ] ( RtpServer : : Ptr server ) {
2024-08-01 11:03:26 +08:00
server - > start ( local_port , local_ip . c_str ( ) , tuple , ( RtpServer : : TcpMode ) tcp_mode , re_use_port , ssrc , only_track , multiplex ) ;
2024-03-10 16:31:20 +08:00
} ) ;
2024-07-09 10:42:10 +08:00
server - > setOnDetach ( [ key ] ( const SockException & ex ) {
2024-09-19 14:53:50 +08:00
// 设置rtp超时移除事件 [AUTO-TRANSLATED:98d42cf3]
// Set RTP timeout removal event
2024-07-09 10:42:10 +08:00
s_rtp_server . erase ( key ) ;
2022-10-30 21:36:35 +08:00
} ) ;
2022-06-22 10:31:53 +08:00
2024-09-19 14:53:50 +08:00
// 回复json [AUTO-TRANSLATED:0c443c6a]
// Reply JSON
2022-06-22 10:31:53 +08:00
return server - > getPort ( ) ;
}
2022-07-24 22:30:59 +08:00
# endif
2022-06-22 10:31:53 +08:00
2021-12-27 17:40:15 +08:00
void getStatisticJson ( const function < void ( Value & val ) > & cb ) {
auto obj = std : : make_shared < Value > ( objectValue ) ;
auto & val = * obj ;
2021-08-21 19:11:20 +08:00
val [ " MediaSource " ] = ( Json : : UInt64 ) ( ObjectStatistic < MediaSource > : : count ( ) ) ;
val [ " MultiMediaSourceMuxer " ] = ( Json : : UInt64 ) ( ObjectStatistic < MultiMediaSourceMuxer > : : count ( ) ) ;
val [ " TcpServer " ] = ( Json : : UInt64 ) ( ObjectStatistic < TcpServer > : : count ( ) ) ;
val [ " TcpSession " ] = ( Json : : UInt64 ) ( ObjectStatistic < TcpSession > : : count ( ) ) ;
val [ " UdpServer " ] = ( Json : : UInt64 ) ( ObjectStatistic < UdpServer > : : count ( ) ) ;
val [ " UdpSession " ] = ( Json : : UInt64 ) ( ObjectStatistic < UdpSession > : : count ( ) ) ;
val [ " TcpClient " ] = ( Json : : UInt64 ) ( ObjectStatistic < TcpClient > : : count ( ) ) ;
val [ " Socket " ] = ( Json : : UInt64 ) ( ObjectStatistic < Socket > : : count ( ) ) ;
val [ " FrameImp " ] = ( Json : : UInt64 ) ( ObjectStatistic < FrameImp > : : count ( ) ) ;
val [ " Frame " ] = ( Json : : UInt64 ) ( ObjectStatistic < Frame > : : count ( ) ) ;
val [ " Buffer " ] = ( Json : : UInt64 ) ( ObjectStatistic < Buffer > : : count ( ) ) ;
val [ " BufferRaw " ] = ( Json : : UInt64 ) ( ObjectStatistic < BufferRaw > : : count ( ) ) ;
val [ " BufferLikeString " ] = ( Json : : UInt64 ) ( ObjectStatistic < BufferLikeString > : : count ( ) ) ;
val [ " BufferList " ] = ( Json : : UInt64 ) ( ObjectStatistic < BufferList > : : count ( ) ) ;
val [ " RtpPacket " ] = ( Json : : UInt64 ) ( ObjectStatistic < RtpPacket > : : count ( ) ) ;
val [ " RtmpPacket " ] = ( Json : : UInt64 ) ( ObjectStatistic < RtmpPacket > : : count ( ) ) ;
# ifdef ENABLE_MEM_DEBUG
auto bytes = getTotalMemUsage ( ) ;
2021-12-29 20:48:15 +08:00
val [ " totalMemUsage " ] = ( Json : : UInt64 ) bytes ;
val [ " totalMemUsageMB " ] = ( int ) ( bytes / 1024 / 1024 ) ;
val [ " totalMemBlock " ] = ( Json : : UInt64 ) getTotalMemBlock ( ) ;
static auto block_type_size = getBlockTypeSize ( ) ;
{
int i = 0 ;
string str ;
size_t last = 0 ;
for ( auto sz : block_type_size ) {
str . append ( to_string ( last ) + " ~ " + to_string ( sz ) + " : " + to_string ( getTotalMemBlockByType ( i + + ) ) + " ; " ) ;
last = sz ;
}
str . pop_back ( ) ;
val [ " totalMemBlockTypeCount " ] = str ;
}
2021-12-27 17:40:15 +08:00
auto thread_size = EventPollerPool : : Instance ( ) . getExecutorSize ( ) + WorkThreadPool : : Instance ( ) . getExecutorSize ( ) ;
std : : shared_ptr < vector < Value > > thread_mem_info = std : : make_shared < vector < Value > > ( thread_size ) ;
2021-12-29 20:48:15 +08:00
shared_ptr < void > finished ( nullptr , [ thread_mem_info , cb , obj ] ( void * ) {
2021-12-27 17:40:15 +08:00
for ( auto & val : * thread_mem_info ) {
( * obj ) [ " threadMem " ] . append ( val ) ;
}
2024-09-19 14:53:50 +08:00
// 触发回调 [AUTO-TRANSLATED:08ea452d]
// Trigger callback
2021-12-27 17:40:15 +08:00
cb ( * obj ) ;
} ) ;
auto pos = 0 ;
2021-12-29 20:48:15 +08:00
auto lam0 = [ & ] ( TaskExecutor & executor ) {
2021-12-27 17:40:15 +08:00
auto & val = ( * thread_mem_info ) [ pos + + ] ;
2021-12-29 20:48:15 +08:00
executor . async ( [ finished , & val ] ( ) {
2021-12-27 17:40:15 +08:00
auto bytes = getThisThreadMemUsage ( ) ;
val [ " threadName " ] = getThreadName ( ) ;
val [ " threadMemUsage " ] = ( Json : : UInt64 ) bytes ;
2021-12-29 20:48:15 +08:00
val [ " threadMemUsageMB " ] = ( Json : : UInt64 ) ( bytes / 1024 / 1024 ) ;
val [ " threadMemBlock " ] = ( Json : : UInt64 ) getThisThreadMemBlock ( ) ;
{
int i = 0 ;
string str ;
size_t last = 0 ;
for ( auto sz : block_type_size ) {
str . append ( to_string ( last ) + " ~ " + to_string ( sz ) + " : " + to_string ( getThisThreadMemBlockByType ( i + + ) ) + " ; " ) ;
last = sz ;
}
str . pop_back ( ) ;
val [ " threadMemBlockTypeCount " ] = str ;
}
2021-12-27 17:40:15 +08:00
} ) ;
} ;
2021-12-29 20:48:15 +08:00
auto lam1 = [ lam0 ] ( const TaskExecutor : : Ptr & executor ) {
lam0 ( * executor ) ;
} ;
EventPollerPool : : Instance ( ) . for_each ( lam1 ) ;
WorkThreadPool : : Instance ( ) . for_each ( lam1 ) ;
2021-12-27 17:40:15 +08:00
# else
cb ( * obj ) ;
2021-08-21 19:11:20 +08:00
# endif
}
2024-07-14 09:32:41 +08:00
void addStreamProxy ( const MediaTuple & tuple , const string & url , int retry_count ,
2023-12-01 14:33:07 +08:00
const ProtocolOption & option , int rtp_type , float timeout_sec , const mINI & args ,
2022-01-12 16:45:47 +08:00
const function < void ( const SockException & ex , const string & key ) > & cb ) {
2024-07-14 09:32:41 +08:00
auto key = tuple . shortUrl ( ) ;
2024-03-10 16:31:20 +08:00
if ( s_player_proxy . find ( key ) ) {
2024-09-19 14:53:50 +08:00
// 已经在拉流了 [AUTO-TRANSLATED:e06c57d7]
// Already pulling stream
2023-08-26 23:02:19 +08:00
cb ( SockException ( Err_other , " This stream already exists " ) , key ) ;
2022-01-12 16:45:47 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// 添加拉流代理 [AUTO-TRANSLATED:aa516f44]
// Add pull stream proxy
2024-07-14 09:32:41 +08:00
auto player = s_player_proxy . make ( key , tuple , option , retry_count ) ;
2022-01-12 16:45:47 +08:00
2024-09-19 14:53:50 +08:00
// 先透传拷贝参数 [AUTO-TRANSLATED:22b5605e]
// First pass-through copy parameters
2024-03-22 20:34:07 +08:00
for ( auto & pr : args ) {
( * player ) [ pr . first ] = pr . second ;
}
2023-12-07 22:05:20 +08:00
2024-09-19 14:53:50 +08:00
// 指定RTP over TCP(播放rtsp时有效) [AUTO-TRANSLATED:1a062656]
// Specify RTP over TCP (effective when playing RTSP)
2022-02-02 20:34:50 +08:00
( * player ) [ Client : : kRtpType ] = rtp_type ;
2022-01-12 16:45:47 +08:00
2024-03-10 16:31:20 +08:00
if ( timeout_sec > 0.1f ) {
2024-09-19 14:53:50 +08:00
// 播放握手超时时间 [AUTO-TRANSLATED:5a29ae1f]
// Play handshake timeout
2022-02-02 20:34:50 +08:00
( * player ) [ Client : : kTimeoutMS ] = timeout_sec * 1000 ;
2022-01-12 16:45:47 +08:00
}
2024-09-19 14:53:50 +08:00
// 开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试 [AUTO-TRANSLATED:ac8499e5]
// Start playing. If playback fails or is stopped, it will automatically retry several times, by default it will retry indefinitely
2022-01-12 16:45:47 +08:00
player - > setPlayCallbackOnce ( [ cb , key ] ( const SockException & ex ) {
if ( ex ) {
2024-03-10 16:31:20 +08:00
s_player_proxy . erase ( key ) ;
2022-01-12 16:45:47 +08:00
}
cb ( ex , key ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 被主动关闭拉流 [AUTO-TRANSLATED:41a19476]
// The pull stream was actively closed
2022-01-12 16:45:47 +08:00
player - > setOnClose ( [ key ] ( const SockException & ex ) {
2024-03-10 16:31:20 +08:00
s_player_proxy . erase ( key ) ;
2022-01-12 16:45:47 +08:00
} ) ;
player - > play ( url ) ;
} ;
2024-03-10 16:31:20 +08:00
void addStreamPusherProxy ( const string & schema ,
const string & vhost ,
const string & app ,
const string & stream ,
const string & url ,
int retry_count ,
int rtp_type ,
float timeout_sec ,
const function < void ( const SockException & ex , const string & key ) > & cb ) {
auto key = getPusherKey ( schema , vhost , app , stream , url ) ;
auto src = MediaSource : : find ( schema , vhost , app , stream ) ;
if ( ! src ) {
cb ( SockException ( Err_other , " can not find the source stream " ) , key ) ;
return ;
}
if ( s_pusher_proxy . find ( key ) ) {
2024-09-19 14:53:50 +08:00
// 已经在推流了 [AUTO-TRANSLATED:81fcd202]
// Already pushing stream
2024-03-10 16:31:20 +08:00
cb ( SockException ( Err_success ) , key ) ;
return ;
}
2024-09-19 14:53:50 +08:00
// 添加推流代理 [AUTO-TRANSLATED:f9dbc76d]
// Add push stream proxy
2024-03-10 16:31:20 +08:00
auto pusher = s_pusher_proxy . make ( key , src , retry_count ) ;
2024-09-19 14:53:50 +08:00
// 指定RTP over TCP(播放rtsp时有效) [AUTO-TRANSLATED:1a062656]
// Specify RTP over TCP (effective when playing RTSP)
2024-03-10 16:31:20 +08:00
pusher - > emplace ( Client : : kRtpType , rtp_type ) ;
if ( timeout_sec > 0.1f ) {
2024-09-19 14:53:50 +08:00
// 推流握手超时时间 [AUTO-TRANSLATED:00762fc1]
// Push stream handshake timeout
2024-03-10 16:31:20 +08:00
pusher - > emplace ( Client : : kTimeoutMS , timeout_sec * 1000 ) ;
}
2024-09-19 14:53:50 +08:00
// 开始推流,如果推流失败或者推流中止,将会自动重试若干次,默认一直重试 [AUTO-TRANSLATED:c8b95088]
// Start pushing stream. If the push stream fails or is stopped, it will automatically retry several times, by default it will retry indefinitely
2024-03-10 16:31:20 +08:00
pusher - > setPushCallbackOnce ( [ cb , key , url ] ( const SockException & ex ) {
if ( ex ) {
WarnL < < " Push " < < url < < " failed, key: " < < key < < " , err: " < < ex ;
s_pusher_proxy . erase ( key ) ;
}
cb ( ex , key ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 被主动关闭推流 [AUTO-TRANSLATED:bf216f82]
// Stream closed actively
2024-03-10 16:31:20 +08:00
pusher - > setOnClose ( [ key , url ] ( const SockException & ex ) {
WarnL < < " Push " < < url < < " failed, key: " < < key < < " , err: " < < ex ;
s_pusher_proxy . erase ( key ) ;
} ) ;
pusher - > publish ( url ) ;
}
2022-03-12 15:07:01 +08:00
2019-05-27 15:09:44 +08:00
/**
* 安 装 api接口
* 所 有 api都支持GET和POST两种方式
* POST方式参数支持application / json和application / x - www - form - urlencoded方式
2024-09-19 14:53:50 +08:00
* Install api interface
* All apis support GET and POST methods
* POST method parameters support application / json and application / x - www - form - urlencoded methods
* [ AUTO - TRANSLATED : 62e68 c43 ]
2019-05-27 15:09:44 +08:00
*/
2019-05-20 11:22:59 +08:00
void installWebApi ( ) {
addHttpListener ( ) ;
2019-05-28 17:14:36 +08:00
GET_CONFIG ( string , api_secret , API : : kSecret ) ;
2019-05-20 17:12:00 +08:00
2024-09-19 14:53:50 +08:00
// 获取线程负载 [AUTO-TRANSLATED:3b0ece5c]
// Get thread load
// 测试url http://127.0.0.1/index/api/getThreadsLoad [AUTO-TRANSLATED:de1c93e7]
// Test url http://127.0.0.1/index/api/getThreadsLoad
2023-07-26 17:18:33 +08:00
api_regist ( " /index/api/getThreadsLoad " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
2019-05-20 11:22:59 +08:00
EventPollerPool : : Instance ( ) . getExecutorDelay ( [ invoker , headerOut ] ( const vector < int > & vecDelay ) {
Value val ;
auto vec = EventPollerPool : : Instance ( ) . getExecutorLoad ( ) ;
2019-05-28 09:38:15 +08:00
int i = API : : Success ;
2019-05-20 11:22:59 +08:00
for ( auto load : vec ) {
Value obj ( objectValue ) ;
obj [ " load " ] = load ;
2019-10-24 11:21:55 +08:00
obj [ " delay " ] = vecDelay [ i + + ] ;
val [ " data " ] . append ( obj ) ;
}
2019-11-13 14:33:19 +08:00
val [ " code " ] = API : : Success ;
2021-01-02 21:24:06 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2019-10-24 11:21:55 +08:00
} ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 获取后台工作线程负载 [AUTO-TRANSLATED:6166e265]
// Get background worker thread load
// 测试url http://127.0.0.1/index/api/getWorkThreadsLoad [AUTO-TRANSLATED:209a8bc1]
// Test url http://127.0.0.1/index/api/getWorkThreadsLoad
2023-07-26 17:18:33 +08:00
api_regist ( " /index/api/getWorkThreadsLoad " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
2019-10-24 11:21:55 +08:00
WorkThreadPool : : Instance ( ) . getExecutorDelay ( [ invoker , headerOut ] ( const vector < int > & vecDelay ) {
Value val ;
auto vec = WorkThreadPool : : Instance ( ) . getExecutorLoad ( ) ;
int i = 0 ;
for ( auto load : vec ) {
Value obj ( objectValue ) ;
obj [ " load " ] = load ;
2019-05-20 11:22:59 +08:00
obj [ " delay " ] = vecDelay [ i + + ] ;
val [ " data " ] . append ( obj ) ;
}
2019-11-13 14:33:19 +08:00
val [ " code " ] = API : : Success ;
2021-01-02 21:24:06 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2019-05-20 11:22:59 +08:00
} ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 获取服务器配置 [AUTO-TRANSLATED:7dd2f3da]
// Get server configuration
// 测试url http://127.0.0.1/index/api/getServerConfig [AUTO-TRANSLATED:59cd0d71]
// Test url http://127.0.0.1/index/api/getServerConfig
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/getServerConfig " , [ ] ( API_ARGS_MAP ) {
2019-05-20 17:12:00 +08:00
CHECK_SECRET ( ) ;
2019-05-20 11:22:59 +08:00
Value obj ;
for ( auto & pr : mINI : : Instance ( ) ) {
obj [ pr . first ] = ( string & ) pr . second ;
}
val [ " data " ] . append ( obj ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 设置服务器配置 [AUTO-TRANSLATED:3de7bd37]
// Set server configuration
// 测试url(比如关闭http api调试) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0 [AUTO-TRANSLATED:9471d218]
// Test url (e.g. disable http api debugging) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0
// 你也可以通过http post方式传参, 可以通过application/x-www-form-urlencoded或application/json方式传参 [AUTO-TRANSLATED:d493a7c0]
// You can also pass parameters through http post method, you can pass parameters through application/x-www-form-urlencoded or application/json methods
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/setServerConfig " , [ ] ( API_ARGS_MAP ) {
2019-05-20 17:12:00 +08:00
CHECK_SECRET ( ) ;
2019-05-20 11:22:59 +08:00
auto & ini = mINI : : Instance ( ) ;
2019-05-28 09:38:15 +08:00
int changed = API : : Success ;
2024-03-23 22:46:30 +08:00
for ( auto & pr : allArgs . args ) {
2019-05-20 11:22:59 +08:00
if ( ini . find ( pr . first ) = = ini . end ( ) ) {
2021-06-08 14:49:32 +08:00
# if 1
2024-09-19 14:53:50 +08:00
// 没有这个key [AUTO-TRANSLATED:d6855e02]
// This key does not exist
2021-06-08 14:49:32 +08:00
continue ;
# else
2024-09-19 14:53:50 +08:00
// 新增配置选项,为了动态添加多个ffmpeg cmd 模板 [AUTO-TRANSLATED:0f977fcd]
// Add configuration options to dynamically add multiple ffmpeg cmd templates
2021-06-04 18:06:40 +08:00
ini [ pr . first ] = pr . second ;
2024-09-19 14:53:50 +08:00
// 防止changed变化 [AUTO-TRANSLATED:f8ad7e59]
// Prevent changed changes
2021-06-08 14:49:32 +08:00
continue ;
# endif
2019-05-20 11:22:59 +08:00
}
2023-07-26 16:40:10 +08:00
if ( pr . first = = FFmpeg : : kBin ) {
WarnL < < " Configuration named " < < FFmpeg : : kBin < < " is not allowed to be set by setServerConfig api. " ;
continue ;
}
2019-05-20 11:22:59 +08:00
if ( ini [ pr . first ] = = pr . second ) {
continue ;
}
ini [ pr . first ] = pr . second ;
2024-09-19 14:53:50 +08:00
// 替换成功 [AUTO-TRANSLATED:b5d4fec1]
// Replacement successful
2019-05-20 11:22:59 +08:00
+ + changed ;
}
if ( changed > 0 ) {
2023-09-02 10:52:07 +08:00
NOTICE_EMIT ( BroadcastReloadConfigArgs , Broadcast : : kBroadcastReloadConfig ) ;
2019-11-04 09:21:11 +08:00
ini . dumpFile ( g_ini_file ) ;
2019-05-20 11:22:59 +08:00
}
val [ " changed " ] = changed ;
} ) ;
2019-05-27 15:09:44 +08:00
2020-12-27 22:14:59 +08:00
static auto s_get_api_list = [ ] ( API_ARGS_MAP ) {
2019-05-20 17:12:00 +08:00
CHECK_SECRET ( ) ;
2019-05-20 11:22:59 +08:00
for ( auto & pr : s_map_api ) {
val [ " data " ] . append ( pr . first ) ;
}
2020-01-17 11:44:20 +08:00
} ;
2024-09-19 14:53:50 +08:00
// 获取服务器api列表 [AUTO-TRANSLATED:e4c0dd9d]
// Get server api list
// 测试url http://127.0.0.1/index/api/getApiList [AUTO-TRANSLATED:df09e368]
// Test url http://127.0.0.1/index/api/getApiList
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/getApiList " , [ ] ( API_ARGS_MAP ) {
s_get_api_list ( API_ARGS_VALUE ) ;
2020-01-17 11:44:20 +08:00
} ) ;
2024-09-19 14:53:50 +08:00
// 获取服务器api列表 [AUTO-TRANSLATED:e4c0dd9d]
// Get server api list
// 测试url http://127.0.0.1/index/ [AUTO-TRANSLATED:76934dd3]
// Test url http://127.0.0.1/index/
2020-12-27 22:14:59 +08:00
api_regist ( " /index/ " , [ ] ( API_ARGS_MAP ) {
s_get_api_list ( API_ARGS_VALUE ) ;
2019-05-20 11:22:59 +08:00
} ) ;
2019-06-15 17:07:10 +08:00
# if !defined(_WIN32)
2024-09-19 14:53:50 +08:00
// 重启服务器,只有Daemon方式才能重启, 否则是直接关闭! [AUTO-TRANSLATED:9d8a1c32]
// Restart server, only Daemon mode can restart, otherwise it will be closed directly!
// 测试url http://127.0.0.1/index/api/restartServer [AUTO-TRANSLATED:8beaaa8a]
// Test url http://127.0.0.1/index/api/restartServer
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/restartServer " , [ ] ( API_ARGS_MAP ) {
2019-05-20 17:12:00 +08:00
CHECK_SECRET ( ) ;
2019-05-20 11:22:59 +08:00
EventPollerPool : : Instance ( ) . getPoller ( ) - > doDelayTask ( 1000 , [ ] ( ) {
2024-09-19 14:53:50 +08:00
// 尝试正常退出 [AUTO-TRANSLATED:93828d0f]
// Try to exit normally
2019-05-20 11:22:59 +08:00
: : kill ( getpid ( ) , SIGINT ) ;
2024-09-19 14:53:50 +08:00
// 3秒后强制退出 [AUTO-TRANSLATED:fdc82920]
// Force exit after 3 seconds
2019-05-20 11:22:59 +08:00
EventPollerPool : : Instance ( ) . getPoller ( ) - > doDelayTask ( 3000 , [ ] ( ) {
exit ( 0 ) ;
return 0 ;
} ) ;
return 0 ;
} ) ;
2023-12-02 15:02:00 +08:00
val [ " msg " ] = " MediaServer will reboot in on 1 second " ;
2019-05-20 11:22:59 +08:00
} ) ;
2021-12-19 17:39:50 +08:00
# else
2024-09-19 14:53:50 +08:00
// 增加Windows下的重启代码 [AUTO-TRANSLATED:dcba12d5]
// Add restart code for Windows
2021-12-19 17:39:50 +08:00
api_regist ( " /index/api/restartServer " , [ ] ( API_ARGS_MAP ) {
CHECK_SECRET ( ) ;
2024-09-19 14:53:50 +08:00
// 创建重启批处理脚本文件 [AUTO-TRANSLATED:cc18c259]
// Create a restart batch script file
2021-12-19 17:39:50 +08:00
FILE * pf ;
errno_t err = : : _wfopen_s ( & pf , L " RestartServer.cmd " , L " w " ) ; //“w”如果该文件存在, 其内容将被覆盖
if ( err = = 0 ) {
char szExeName [ 1024 ] ;
char drive [ _MAX_DRIVE ] = { 0 } ;
char dir [ _MAX_DIR ] = { 0 } ;
char fname [ _MAX_FNAME ] = { 0 } ;
char ext [ _MAX_EXT ] = { 0 } ;
char exeName [ _MAX_FNAME ] = { 0 } ;
GetModuleFileNameA ( NULL , szExeName , 1024 ) ; //获取进程的全路径
_splitpath ( szExeName , drive , dir , fname , ext ) ;
strcpy ( exeName , fname ) ;
strcat ( exeName , ext ) ;
fprintf ( pf , " @echo off \n taskkill /f /im %s \n start \" \" \" %s \" \n del %%0 " , exeName , szExeName ) ;
fclose ( pf ) ;
2024-09-19 14:53:50 +08:00
// 1秒后执行创建的批处理脚本 [AUTO-TRANSLATED:596dbca9]
// Execute the created batch script after 1 second
2021-12-19 17:39:50 +08:00
EventPollerPool : : Instance ( ) . getPoller ( ) - > doDelayTask ( 1000 , [ ] ( ) {
STARTUPINFO si ;
PROCESS_INFORMATION pi ;
ZeroMemory ( & si , sizeof si ) ;
ZeroMemory ( & pi , sizeof pi ) ;
si . cb = sizeof si ;
si . dwFlags = STARTF_USESHOWWINDOW ;
si . wShowWindow = SW_HIDE ;
TCHAR winSysDir [ 1024 ] ;
ZeroMemory ( winSysDir , sizeof winSysDir ) ;
GetSystemDirectory ( winSysDir , 1024 ) ;
TCHAR appName [ 1024 ] ;
ZeroMemory ( appName , sizeof appName ) ;
_stprintf ( appName , " %s \\ cmd.exe " , winSysDir ) ;
BOOL bRet = CreateProcess ( appName , " /c RestartServer.cmd " , NULL , NULL , FALSE , 0 , NULL , NULL , & si , & pi ) ;
if ( bRet = = FALSE ) {
int err = GetLastError ( ) ;
cout < < endl < < " 无法执行重启操作,错误代码: " < < err < < endl ;
}
WaitForSingleObject ( pi . hProcess , INFINITE ) ;
CloseHandle ( pi . hProcess ) ;
CloseHandle ( pi . hThread ) ;
return 0 ;
} ) ;
val [ " msg " ] = " 服务器将在一秒后自动重启 " ;
} else {
val [ " msg " ] = " 创建重启脚本文件失败 " ;
val [ " code " ] = API : : OtherFailed ;
}
} ) ;
2019-06-15 17:07:10 +08:00
# endif //#if !defined(_WIN32)
2019-05-20 11:22:59 +08:00
2024-09-19 14:53:50 +08:00
// 获取流列表,可选筛选参数 [AUTO-TRANSLATED:68ffc6b6]
// Get stream list, optional filtering parameters
// 测试url0(获取所有流) http://127.0.0.1/index/api/getMediaList [AUTO-TRANSLATED:434652ea]
// Test url0 (get all streams) http://127.0.0.1/index/api/getMediaList
// 测试url1(获取虚拟主机为"__defaultVost__"的流) http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__ [AUTO-TRANSLATED:5d9bd1ee]
// Test url1 (get streams with virtual host "__defaultVost__") http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__
// 测试url2(获取rtsp类型的流) http://127.0.0.1/index/api/getMediaList?schema=rtsp [AUTO-TRANSLATED:21c2c15d]
// Test url2 (get rtsp type streams) http://127.0.0.1/index/api/getMediaList?schema=rtsp
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/getMediaList " , [ ] ( API_ARGS_MAP ) {
2019-05-20 17:12:00 +08:00
CHECK_SECRET ( ) ;
2024-09-19 14:53:50 +08:00
// 获取所有MediaSource列表 [AUTO-TRANSLATED:7bf16dc2]
// Get all MediaSource lists
2021-06-30 21:06:29 +08:00
MediaSource : : for_each_media ( [ & ] ( const MediaSource : : Ptr & media ) {
2021-04-08 17:34:13 +08:00
val [ " data " ] . append ( makeMediaSourceJson ( * media ) ) ;
2021-06-30 21:06:29 +08:00
} , allArgs [ " schema " ] , allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
2019-05-20 11:22:59 +08:00
} ) ;
2024-09-19 14:53:50 +08:00
// 测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs [AUTO-TRANSLATED:126a75e8]
// Test url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/isMediaOnline " , [ ] ( API_ARGS_MAP ) {
2019-11-13 14:26:12 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " schema " , " vhost " , " app " , " stream " ) ;
2020-05-26 10:11:58 +08:00
val [ " online " ] = ( bool ) ( MediaSource : : find ( allArgs [ " schema " ] , allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ) ;
2019-11-13 14:26:12 +08:00
} ) ;
2024-09-19 14:53:50 +08:00
// 获取媒体流播放器列表 [AUTO-TRANSLATED:bcadf31c]
// Get media stream player list
// 测试url http://127.0.0.1/index/api/getMediaPlayerList?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs [AUTO-TRANSLATED:2aab7522]
// Test url http://127.0.0.1/index/api/getMediaPlayerList?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
2022-08-30 21:05:19 +08:00
api_regist ( " /index/api/getMediaPlayerList " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " schema " , " vhost " , " app " , " stream " ) ;
auto src = MediaSource : : find ( allArgs [ " schema " ] , allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
if ( ! src ) {
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
}
src - > getPlayerList (
2023-09-02 12:06:35 +08:00
[ = ] ( const std : : list < toolkit : : Any > & info_list ) mutable {
2022-08-30 21:05:19 +08:00
val [ " code " ] = API : : Success ;
auto & data = val [ " data " ] ;
2022-09-01 17:45:06 +08:00
data = Value ( arrayValue ) ;
2022-08-30 21:05:19 +08:00
for ( auto & info : info_list ) {
2023-09-02 12:06:35 +08:00
auto & obj = info . get < Value > ( ) ;
data . append ( std : : move ( obj ) ) ;
2022-08-30 21:05:19 +08:00
}
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ,
2023-09-02 12:06:35 +08:00
[ ] ( toolkit : : Any & & info ) - > toolkit : : Any {
2022-08-30 21:05:19 +08:00
auto obj = std : : make_shared < Value > ( ) ;
2023-09-02 12:06:35 +08:00
auto & sock = info . get < SockInfo > ( ) ;
fillSockInfo ( * obj , & sock ) ;
( * obj ) [ " typeid " ] = toolkit : : demangle ( typeid ( sock ) . name ( ) ) ;
toolkit : : Any ret ;
ret . set ( obj ) ;
return ret ;
2022-08-30 21:05:19 +08:00
} ) ;
} ) ;
2023-09-02 12:53:56 +08:00
api_regist ( " /index/api/broadcastMessage " , [ ] ( API_ARGS_MAP ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " schema " , " vhost " , " app " , " stream " , " msg " ) ;
auto src = MediaSource : : find ( allArgs [ " schema " ] , allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
if ( ! src ) {
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
}
Any any ;
Buffer : : Ptr buffer = std : : make_shared < BufferLikeString > ( allArgs [ " msg " ] ) ;
any . set ( std : : move ( buffer ) ) ;
src - > broadcastMessage ( any ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs [AUTO-TRANSLATED:9402e811]
// Test url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
2022-06-11 14:17:43 +08:00
api_regist ( " /index/api/getMediaInfo " , [ ] ( API_ARGS_MAP_ASYNC ) {
2019-12-13 13:35:27 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " schema " , " vhost " , " app " , " stream " ) ;
2020-05-26 10:11:58 +08:00
auto src = MediaSource : : find ( allArgs [ " schema " ] , allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
2019-12-13 13:35:27 +08:00
if ( ! src ) {
2022-06-11 14:17:43 +08:00
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
2019-12-13 13:35:27 +08:00
}
2022-06-11 14:17:43 +08:00
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
auto val = makeMediaSourceJson ( * src ) ;
val [ " code " ] = API : : Success ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2019-12-13 13:35:27 +08:00
} ) ;
2024-09-19 14:53:50 +08:00
// 主动关断流,包括关断拉流、推流 [AUTO-TRANSLATED:80506955]
// Actively close the stream, including closing the pull stream and push stream
// 测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 [AUTO-TRANSLATED:c3831592]
// Test url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
2022-06-11 14:17:43 +08:00
api_regist ( " /index/api/close_stream " , [ ] ( API_ARGS_MAP_ASYNC ) {
2019-05-20 17:12:00 +08:00
CHECK_SECRET ( ) ;
2019-05-20 16:53:29 +08:00
CHECK_ARGS ( " schema " , " vhost " , " app " , " stream " ) ;
2024-09-19 14:53:50 +08:00
// 踢掉推流器 [AUTO-TRANSLATED:61e39b14]
// Kick out the pusher
2019-05-20 11:22:59 +08:00
auto src = MediaSource : : find ( allArgs [ " schema " ] ,
allArgs [ " vhost " ] ,
allArgs [ " app " ] ,
allArgs [ " stream " ] ) ;
2022-06-11 14:17:43 +08:00
if ( ! src ) {
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
}
bool force = allArgs [ " force " ] . as < bool > ( ) ;
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
bool flag = src - > close ( force ) ;
2019-11-18 16:28:56 +08:00
val [ " result " ] = flag ? 0 : - 1 ;
2019-05-27 14:15:21 +08:00
val [ " msg " ] = flag ? " success " : " close failed " ;
2020-10-24 23:31:40 +08:00
val [ " code " ] = flag ? API : : Success : API : : OtherFailed ;
2022-06-14 16:29:04 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2022-06-11 14:17:43 +08:00
} ) ;
2019-05-20 11:22:59 +08:00
} ) ;
2024-09-19 14:53:50 +08:00
// 批量主动关断流,包括关断拉流、推流 [AUTO-TRANSLATED:5d180cd8]
// Batch actively close the stream, including closing the pull stream and push stream
// 测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 [AUTO-TRANSLATED:786933db]
// Test url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/close_streams " , [ ] ( API_ARGS_MAP ) {
2019-11-18 10:46:59 +08:00
CHECK_SECRET ( ) ;
2024-09-19 14:53:50 +08:00
// 筛选命中个数 [AUTO-TRANSLATED:6db1e8c7]
// Filter hit count
2019-11-18 10:46:59 +08:00
int count_hit = 0 ;
int count_closed = 0 ;
2019-11-19 10:55:44 +08:00
list < MediaSource : : Ptr > media_list ;
2021-06-30 21:06:29 +08:00
MediaSource : : for_each_media ( [ & ] ( const MediaSource : : Ptr & media ) {
2019-11-18 10:46:59 +08:00
+ + count_hit ;
2019-11-19 10:55:44 +08:00
media_list . emplace_back ( media ) ;
2021-06-30 21:06:29 +08:00
} , allArgs [ " schema " ] , allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
2019-11-19 10:55:44 +08:00
bool force = allArgs [ " force " ] . as < bool > ( ) ;
2022-06-11 14:17:43 +08:00
for ( auto & media : media_list ) {
if ( media - > close ( force ) ) {
2019-11-18 10:46:59 +08:00
+ + count_closed ;
}
2019-11-19 10:55:44 +08:00
}
2019-11-18 10:46:59 +08:00
val [ " count_hit " ] = count_hit ;
val [ " count_closed " ] = count_closed ;
} ) ;
2024-09-19 14:53:50 +08:00
// 获取所有Session列表信息 [AUTO-TRANSLATED:e785052d]
// Get all Session list information
// 可以根据本地端口和远端ip来筛选 [AUTO-TRANSLATED:4d4c9d61]
// You can filter by local port and remote ip
// 测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935 [AUTO-TRANSLATED:ef845193]
// Test url (filter tcp session under a certain port) http://127.0.0.1/index/api/getAllSession?local_port=1935
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/getAllSession " , [ ] ( API_ARGS_MAP ) {
2019-05-27 15:09:44 +08:00
CHECK_SECRET ( ) ;
Value jsession ;
uint16_t local_port = allArgs [ " local_port " ] . as < uint16_t > ( ) ;
2021-08-12 16:07:31 +08:00
string peer_ip = allArgs [ " peer_ip " ] ;
2019-05-27 15:09:44 +08:00
2021-06-08 11:29:32 +08:00
SessionMap : : Instance ( ) . for_each_session ( [ & ] ( const string & id , const Session : : Ptr & session ) {
2019-11-18 16:16:56 +08:00
if ( local_port ! = 0 & & local_port ! = session - > get_local_port ( ) ) {
2019-05-27 15:09:44 +08:00
return ;
}
if ( ! peer_ip . empty ( ) & & peer_ip ! = session - > get_peer_ip ( ) ) {
return ;
}
2022-09-07 11:06:39 +08:00
fillSockInfo ( jsession , session . get ( ) ) ;
2019-05-27 15:09:44 +08:00
jsession [ " id " ] = id ;
2022-06-11 15:03:28 +08:00
jsession [ " typeid " ] = toolkit : : demangle ( typeid ( * session ) . name ( ) ) ;
2019-05-27 15:09:44 +08:00
val [ " data " ] . append ( jsession ) ;
} ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 断开tcp连接, 比如说可以断开rtsp、rtmp播放器等 [AUTO-TRANSLATED:9147ffec]
// Disconnect the tcp connection, for example, you can disconnect the rtsp, rtmp player, etc.
// 测试url http://127.0.0.1/index/api/kick_session?id=123456 [AUTO-TRANSLATED:c2880cb5]
// Test url http://127.0.0.1/index/api/kick_session?id=123456
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/kick_session " , [ ] ( API_ARGS_MAP ) {
2019-05-20 17:12:00 +08:00
CHECK_SECRET ( ) ;
2019-05-20 16:53:29 +08:00
CHECK_ARGS ( " id " ) ;
2024-09-19 14:53:50 +08:00
// 踢掉tcp会话 [AUTO-TRANSLATED:f6f318bd]
// Kick out the tcp session
2019-05-28 09:38:15 +08:00
auto session = SessionMap : : Instance ( ) . get ( allArgs [ " id " ] ) ;
2019-05-20 11:22:59 +08:00
if ( ! session ) {
2019-11-13 14:33:19 +08:00
throw ApiRetException ( " can not find the target " , API : : OtherFailed ) ;
2019-05-20 11:22:59 +08:00
}
session - > safeShutdown ( ) ;
} ) ;
2019-11-18 16:34:39 +08:00
2024-09-19 14:53:50 +08:00
// 批量断开tcp连接, 比如说可以断开rtsp、rtmp播放器等 [AUTO-TRANSLATED:fef59eb8]
// Batch disconnect tcp connections, for example, you can disconnect rtsp, rtmp players, etc.
// 测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935 [AUTO-TRANSLATED:5891b482]
// Test url http://127.0.0.1/index/api/kick_sessions?local_port=1935
2023-12-17 18:13:51 +08:00
api_regist ( " /index/api/kick_sessions " , [ ] ( API_ARGS_MAP ) {
2019-11-18 16:34:39 +08:00
CHECK_SECRET ( ) ;
uint16_t local_port = allArgs [ " local_port " ] . as < uint16_t > ( ) ;
2021-08-12 16:07:31 +08:00
string peer_ip = allArgs [ " peer_ip " ] ;
2021-01-17 18:31:50 +08:00
size_t count_hit = 0 ;
2019-11-18 16:34:39 +08:00
2021-06-08 11:29:32 +08:00
list < Session : : Ptr > session_list ;
2023-12-17 18:13:51 +08:00
SessionMap : : Instance ( ) . for_each_session ( [ & ] ( const string & id , const Session : : Ptr & session ) {
if ( local_port ! = 0 & & local_port ! = session - > get_local_port ( ) ) {
2019-11-18 16:34:39 +08:00
return ;
}
2023-12-17 18:13:51 +08:00
if ( ! peer_ip . empty ( ) & & peer_ip ! = session - > get_peer_ip ( ) ) {
return ;
}
if ( session - > getIdentifier ( ) = = sender . getIdentifier ( ) ) {
2024-09-19 14:53:50 +08:00
// 忽略本http链接 [AUTO-TRANSLATED:9fb4bf76]
// Ignore this http link
2019-11-18 16:34:39 +08:00
return ;
}
2019-11-19 10:55:44 +08:00
session_list . emplace_back ( session ) ;
2019-11-18 16:34:39 +08:00
+ + count_hit ;
} ) ;
2019-11-19 10:55:44 +08:00
2023-12-17 18:13:51 +08:00
for ( auto & session : session_list ) {
2019-11-19 10:55:44 +08:00
session - > safeShutdown ( ) ;
}
2019-11-18 16:34:39 +08:00
val [ " count_hit " ] = ( Json : : UInt64 ) count_hit ;
} ) ;
2024-09-19 14:53:50 +08:00
// 动态添加rtsp/rtmp推流代理 [AUTO-TRANSLATED:2eb09bc9]
// Dynamically add rtsp/rtmp push stream proxy
// 测试url http://127.0.0.1/index/api/addStreamPusherProxy?schema=rtmp&vhost=__defaultVhost__&app=proxy&stream=0&dst_url=rtmp://127.0.0.1/live/obs [AUTO-TRANSLATED:25d7d4b0]
// Test url http://127.0.0.1/index/api/addStreamPusherProxy?schema=rtmp&vhost=__defaultVhost__&app=proxy&stream=0&dst_url=rtmp://127.0.0.1/live/obs
2021-06-16 19:40:08 +08:00
api_regist ( " /index/api/addStreamPusherProxy " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
2021-06-17 10:12:34 +08:00
CHECK_ARGS ( " schema " , " vhost " , " app " , " stream " , " dst_url " ) ;
auto dst_url = allArgs [ " dst_url " ] ;
2023-04-01 23:54:11 +08:00
auto retry_count = allArgs [ " retry_count " ] . empty ( ) ? - 1 : allArgs [ " retry_count " ] . as < int > ( ) ;
2021-06-16 19:40:08 +08:00
addStreamPusherProxy ( allArgs [ " schema " ] ,
allArgs [ " vhost " ] ,
allArgs [ " app " ] ,
allArgs [ " stream " ] ,
2021-06-17 10:12:34 +08:00
allArgs [ " dst_url " ] ,
2023-03-22 15:52:56 +08:00
retry_count ,
2021-06-17 10:41:26 +08:00
allArgs [ " rtp_type " ] ,
allArgs [ " timeout_sec " ] ,
2021-06-17 10:12:34 +08:00
[ invoker , val , headerOut , dst_url ] ( const SockException & ex , const string & key ) mutable {
if ( ex ) {
val [ " code " ] = API : : OtherFailed ;
val [ " msg " ] = ex . what ( ) ;
} else {
val [ " data " ] [ " key " ] = key ;
InfoL < < " Publish success, please play with player: " < < dst_url ;
2021-06-16 19:40:08 +08:00
}
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 关闭推流代理 [AUTO-TRANSLATED:91602b75]
// Close the push stream proxy
// 测试url http://127.0.0.1/index/api/delStreamPusherProxy?key=__defaultVhost__/proxy/0 [AUTO-TRANSLATED:2671206c]
// Test url http://127.0.0.1/index/api/delStreamPusherProxy?key=__defaultVhost__/proxy/0
2021-06-17 10:12:34 +08:00
api_regist ( " /index/api/delStreamPusherProxy " , [ ] ( API_ARGS_MAP ) {
2021-06-16 19:40:08 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " key " ) ;
2024-03-10 16:31:20 +08:00
val [ " data " ] [ " flag " ] = s_pusher_proxy . erase ( allArgs [ " key " ] ) = = 1 ;
2021-06-16 19:40:08 +08:00
} ) ;
2024-11-01 10:47:18 +08:00
api_regist ( " /index/api/listStreamPusherProxy " , [ ] ( API_ARGS_MAP ) {
CHECK_SECRET ( ) ;
s_pusher_proxy . for_each ( [ & val ] ( const std : : string & key , const PusherProxy : : Ptr & p ) {
Json : : Value item = ToJson ( p ) ;
item [ " key " ] = key ;
val [ " data " ] . append ( item ) ;
} ) ;
} ) ;
api_regist ( " /index/api/listStreamProxy " , [ ] ( API_ARGS_MAP ) {
CHECK_SECRET ( ) ;
s_player_proxy . for_each ( [ & val ] ( const std : : string & key , const PlayerProxy : : Ptr & p ) {
Json : : Value item = ToJson ( p ) ;
item [ " key " ] = key ;
val [ " data " ] . append ( item ) ;
} ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 动态添加rtsp/rtmp拉流代理 [AUTO-TRANSLATED:2616537c]
// Dynamically add rtsp/rtmp pull stream proxy
// 测试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 [AUTO-TRANSLATED:71ddce15]
// Test 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
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/addStreamProxy " , [ ] ( API_ARGS_MAP_ASYNC ) {
2019-05-27 23:07:12 +08:00
CHECK_SECRET ( ) ;
2020-09-20 09:26:00 +08:00
CHECK_ARGS ( " vhost " , " app " , " stream " , " url " ) ;
2022-03-12 15:07:01 +08:00
2023-12-01 14:33:07 +08:00
mINI args ;
2024-03-23 22:46:30 +08:00
for ( auto & pr : allArgs . args ) {
2023-12-01 14:33:07 +08:00
args . emplace ( pr . first , pr . second ) ;
}
2022-09-16 23:30:55 +08:00
ProtocolOption option ( allArgs ) ;
2023-03-22 15:52:56 +08:00
auto retry_count = allArgs [ " retry_count " ] . empty ( ) ? - 1 : allArgs [ " retry_count " ] . as < int > ( ) ;
2024-07-14 09:32:41 +08:00
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
auto tuple = MediaTuple { vhost , allArgs [ " app " ] , allArgs [ " stream " ] , " " } ;
addStreamProxy ( tuple ,
2019-05-27 23:07:12 +08:00
allArgs [ " url " ] ,
2023-03-22 15:52:56 +08:00
retry_count ,
2022-03-12 13:19:21 +08:00
option ,
2019-05-27 23:07:12 +08:00
allArgs [ " rtp_type " ] ,
2021-04-30 18:01:48 +08:00
allArgs [ " timeout_sec " ] ,
2023-12-01 14:33:07 +08:00
args ,
2021-03-14 10:29:17 +08:00
[ invoker , val , headerOut ] ( const SockException & ex , const string & key ) mutable {
if ( ex ) {
val [ " code " ] = API : : OtherFailed ;
val [ " msg " ] = ex . what ( ) ;
} else {
val [ " data " ] [ " key " ] = key ;
2019-05-27 23:07:12 +08:00
}
2021-01-02 21:24:06 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2019-05-27 23:07:12 +08:00
} ) ;
2019-05-20 16:26:04 +08:00
} ) ;
2024-09-19 14:53:50 +08:00
// 关闭拉流代理 [AUTO-TRANSLATED:5204f128]
// Close the pull stream proxy
// 测试url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0 [AUTO-TRANSLATED:2b0903ef]
// Test url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/delStreamProxy " , [ ] ( API_ARGS_MAP ) {
2019-05-20 17:12:00 +08:00
CHECK_SECRET ( ) ;
2019-05-20 18:17:53 +08:00
CHECK_ARGS ( " key " ) ;
2024-03-10 16:31:20 +08:00
val [ " data " ] [ " flag " ] = s_player_proxy . erase ( allArgs [ " key " ] ) = = 1 ;
2019-05-20 16:26:04 +08:00
} ) ;
2021-01-23 09:42:15 +08:00
static auto addFFmpegSource = [ ] ( const string & ffmpeg_cmd_key ,
const string & src_url ,
2019-06-06 18:28:33 +08:00
const string & dst_url ,
int timeout_ms ,
2020-12-20 20:25:44 +08:00
bool enable_hls ,
bool enable_mp4 ,
const function < void ( const SockException & ex , const string & key ) > & cb ) {
2019-06-06 18:28:33 +08:00
auto key = MD5 ( dst_url ) . hexdigest ( ) ;
2024-03-10 16:31:20 +08:00
if ( s_ffmpeg_src . find ( key ) ) {
2024-09-19 14:53:50 +08:00
// 已经在拉流了 [AUTO-TRANSLATED:e06c57d7]
// Already pulling
2020-12-20 20:25:44 +08:00
cb ( SockException ( Err_success ) , key ) ;
2019-06-06 18:28:33 +08:00
return ;
}
2024-03-10 16:31:20 +08:00
auto ffmpeg = s_ffmpeg_src . make ( key ) ;
2019-06-06 18:28:33 +08:00
2020-12-20 20:25:44 +08:00
ffmpeg - > setOnClose ( [ key ] ( ) {
2024-03-10 16:31:20 +08:00
s_ffmpeg_src . erase ( key ) ;
2019-06-06 18:28:33 +08:00
} ) ;
2021-01-17 20:15:08 +08:00
ffmpeg - > setupRecordFlag ( enable_hls , enable_mp4 ) ;
2021-01-23 09:42:15 +08:00
ffmpeg - > play ( ffmpeg_cmd_key , src_url , dst_url , timeout_ms , [ cb , key ] ( const SockException & ex ) {
2020-12-20 20:25:44 +08:00
if ( ex ) {
2024-03-10 16:31:20 +08:00
s_ffmpeg_src . erase ( key ) ;
2019-06-06 18:28:33 +08:00
}
2020-12-20 20:25:44 +08:00
cb ( ex , key ) ;
2019-06-06 18:28:33 +08:00
} ) ;
} ;
2024-09-19 14:53:50 +08:00
// 动态添加rtsp/rtmp拉流代理 [AUTO-TRANSLATED:2616537c]
// Dynamically add rtsp/rtmp pull stream proxy
// 测试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 [AUTO-TRANSLATED:501cdd89]
// // Test 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
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/addFFmpegSource " , [ ] ( API_ARGS_MAP_ASYNC ) {
2019-06-06 18:28:33 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " src_url " , " dst_url " , " timeout_ms " ) ;
auto src_url = allArgs [ " src_url " ] ;
auto dst_url = allArgs [ " dst_url " ] ;
int timeout_ms = allArgs [ " timeout_ms " ] ;
2020-12-20 20:25:44 +08:00
auto enable_hls = allArgs [ " enable_hls " ] . as < int > ( ) ;
auto enable_mp4 = allArgs [ " enable_mp4 " ] . as < int > ( ) ;
2019-06-06 18:28:33 +08:00
2021-01-23 09:42:15 +08:00
addFFmpegSource ( allArgs [ " ffmpeg_cmd_key " ] , src_url , dst_url , timeout_ms , enable_hls , enable_mp4 ,
2021-03-14 10:29:17 +08:00
[ invoker , val , headerOut ] ( const SockException & ex , const string & key ) mutable {
2020-12-20 20:25:44 +08:00
if ( ex ) {
2021-03-14 10:29:17 +08:00
val [ " code " ] = API : : OtherFailed ;
val [ " msg " ] = ex . what ( ) ;
2020-12-20 20:25:44 +08:00
} else {
2021-03-14 10:29:17 +08:00
val [ " data " ] [ " key " ] = key ;
2019-06-06 18:28:33 +08:00
}
2021-01-02 21:24:06 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2019-06-06 18:28:33 +08:00
} ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 关闭拉流代理 [AUTO-TRANSLATED:5204f128]
// Close the pull stream proxy
// 测试url http://127.0.0.1/index/api/delFFmepgSource?key=key [AUTO-TRANSLATED:ed6fa147]
// Test url http://127.0.0.1/index/api/delFFmepgSource?key=key
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/delFFmpegSource " , [ ] ( API_ARGS_MAP ) {
2019-06-06 18:28:33 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " key " ) ;
2024-03-10 16:31:20 +08:00
val [ " data " ] [ " flag " ] = s_ffmpeg_src . erase ( allArgs [ " key " ] ) = = 1 ;
2019-06-06 18:28:33 +08:00
} ) ;
2024-11-01 10:47:18 +08:00
api_regist ( " /index/api/listFFmpegSource " , [ ] ( API_ARGS_MAP ) {
CHECK_SECRET ( ) ;
s_ffmpeg_src . for_each ( [ & val ] ( const std : : string & key , const FFmpegSource : : Ptr & src ) {
Json : : Value item ;
item [ " src_url " ] = src - > getSrcUrl ( ) ;
item [ " dst_url " ] = src - > getDstUrl ( ) ;
item [ " cmd " ] = src - > getCmd ( ) ;
item [ " ffmpeg_cmd_key " ] = src - > getCmdKey ( ) ;
item [ " key " ] = key ;
val [ " data " ] . append ( item ) ;
} ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 新增http api下载可执行程序文件接口 [AUTO-TRANSLATED:d6e44e84]
// Add a new http api to download executable files
// 测试url http://127.0.0.1/index/api/downloadBin [AUTO-TRANSLATED:9525e834]
// Test url http://127.0.0.1/index/api/downloadBin
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/downloadBin " , [ ] ( API_ARGS_MAP_ASYNC ) {
2019-10-28 17:23:16 +08:00
CHECK_SECRET ( ) ;
2024-03-23 22:46:30 +08:00
invoker . responseFile ( allArgs . parser . getHeader ( ) , StrCaseMap ( ) , exePath ( ) ) ;
2019-10-28 17:23:16 +08:00
} ) ;
2019-12-06 11:54:10 +08:00
# if defined(ENABLE_RTPPROXY)
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/getRtpInfo " , [ ] ( API_ARGS_MAP ) {
2019-12-05 19:53:55 +08:00
CHECK_SECRET ( ) ;
2020-07-07 10:01:12 +08:00
CHECK_ARGS ( " stream_id " ) ;
2024-07-09 10:42:10 +08:00
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
std : : string app = kRtpAppName ;
if ( ! allArgs [ " app " ] . empty ( ) ) {
app = allArgs [ " app " ] ;
}
auto src = MediaSource : : find ( vhost , app , allArgs [ " stream_id " ] ) ;
2024-06-09 10:52:10 +08:00
auto process = src ? src - > getRtpProcess ( ) : nullptr ;
2020-07-07 10:01:12 +08:00
if ( ! process ) {
2019-12-05 19:53:55 +08:00
val [ " exist " ] = false ;
return ;
}
val [ " exist " ] = true ;
2022-09-07 11:06:39 +08:00
fillSockInfo ( val , process . get ( ) ) ;
2019-12-05 19:53:55 +08:00
} ) ;
2020-07-02 22:23:43 +08:00
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/openRtpServer " , [ ] ( API_ARGS_MAP ) {
2020-07-02 22:23:43 +08:00
CHECK_SECRET ( ) ;
2022-09-09 10:56:28 +08:00
CHECK_ARGS ( " port " , " stream_id " ) ;
2024-07-09 10:42:10 +08:00
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
std : : string app = kRtpAppName ;
if ( ! allArgs [ " app " ] . empty ( ) ) {
app = allArgs [ " app " ] ;
}
2020-07-09 10:38:57 +08:00
auto stream_id = allArgs [ " stream_id " ] ;
2024-07-09 10:42:10 +08:00
auto tuple = MediaTuple { vhost , app , stream_id , " " } ;
2022-09-09 10:56:28 +08:00
auto tcp_mode = allArgs [ " tcp_mode " ] . as < int > ( ) ;
if ( allArgs [ " enable_tcp " ] . as < int > ( ) & & ! tcp_mode ) {
2024-09-19 14:53:50 +08:00
// 兼容老版本请求, 新版本去除enable_tcp参数并新增tcp_mode参数 [AUTO-TRANSLATED:3b6a5ab5]
// Compatible with old version requests, the new version removes the enable_tcp parameter and adds the tcp_mode parameter
2022-09-09 10:56:28 +08:00
tcp_mode = 1 ;
}
2024-03-05 17:06:31 +08:00
auto only_track = allArgs [ " only_track " ] . as < int > ( ) ;
if ( allArgs [ " only_audio " ] . as < bool > ( ) ) {
2024-09-19 14:53:50 +08:00
// 兼容老版本请求, 新版本去除only_audio参数并新增only_track参数 [AUTO-TRANSLATED:a7a40942]
// Compatible with old version requests, the new version removes the only_audio parameter and adds the only_track parameter
2024-03-05 17:06:31 +08:00
only_track = 1 ;
}
2024-08-01 11:03:26 +08:00
GET_CONFIG ( std : : string , local_ip , General : : kListenIP )
2024-01-18 10:59:09 +08:00
if ( ! allArgs [ " local_ip " ] . empty ( ) ) {
local_ip = allArgs [ " local_ip " ] ;
}
2024-07-09 10:42:10 +08:00
auto port = openRtpServer ( allArgs [ " port " ] , tuple , tcp_mode , local_ip , allArgs [ " re_use_port " ] . as < bool > ( ) ,
2024-03-05 17:06:31 +08:00
allArgs [ " ssrc " ] . as < uint32_t > ( ) , only_track ) ;
2022-09-09 10:56:28 +08:00
if ( port = = 0 ) {
2024-07-09 10:42:10 +08:00
throw InvalidArgsException ( " This stream already exists " ) ;
2020-07-09 10:57:17 +08:00
}
2024-09-19 14:53:50 +08:00
// 回复json [AUTO-TRANSLATED:0c443c6a]
// Reply json
2022-06-22 10:31:53 +08:00
val [ " port " ] = port ;
2020-07-02 22:23:43 +08:00
} ) ;
2023-11-09 20:36:51 +08:00
2024-07-09 10:42:10 +08:00
api_regist ( " /index/api/openRtpServerMultiplex " , [ ] ( API_ARGS_MAP ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " port " , " stream_id " ) ;
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
std : : string app = kRtpAppName ;
if ( ! allArgs [ " app " ] . empty ( ) ) {
app = allArgs [ " app " ] ;
}
auto stream_id = allArgs [ " stream_id " ] ;
auto tuple = MediaTuple { vhost , app , stream_id , " " } ;
auto tcp_mode = allArgs [ " tcp_mode " ] . as < int > ( ) ;
if ( allArgs [ " enable_tcp " ] . as < int > ( ) & & ! tcp_mode ) {
2024-09-19 14:53:50 +08:00
// 兼容老版本请求, 新版本去除enable_tcp参数并新增tcp_mode参数 [AUTO-TRANSLATED:b5f8f5df]
// Compatible with old version requests, the new version removes the enable_tcp parameter and adds the tcp_mode parameter
2024-07-09 10:42:10 +08:00
tcp_mode = 1 ;
}
auto only_track = allArgs [ " only_track " ] . as < int > ( ) ;
if ( allArgs [ " only_audio " ] . as < bool > ( ) ) {
2024-09-19 14:53:50 +08:00
// 兼容老版本请求, 新版本去除only_audio参数并新增only_track参数 [AUTO-TRANSLATED:a7a40942]
// Compatible with old version requests, the new version removes the only_audio parameter and adds the only_track parameter
2024-07-09 10:42:10 +08:00
only_track = 1 ;
}
std : : string local_ip = " :: " ;
if ( ! allArgs [ " local_ip " ] . empty ( ) ) {
local_ip = allArgs [ " local_ip " ] ;
}
auto port = openRtpServer ( allArgs [ " port " ] , tuple , tcp_mode , local_ip , true , 0 , only_track , true ) ;
if ( port = = 0 ) {
throw InvalidArgsException ( " This stream already exists " ) ;
}
2024-09-19 14:53:50 +08:00
// 回复json [AUTO-TRANSLATED:e80815cd]
// Reply json
2024-07-09 10:42:10 +08:00
val [ " port " ] = port ;
} ) ;
2020-07-02 22:23:43 +08:00
2022-09-09 10:56:28 +08:00
api_regist ( " /index/api/connectRtpServer " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " stream_id " , " dst_url " , " dst_port " ) ;
2024-03-10 16:31:20 +08:00
auto cb = [ val , headerOut , invoker ] ( const SockException & ex ) mutable {
if ( ex ) {
val [ " code " ] = API : : OtherFailed ;
val [ " msg " ] = ex . what ( ) ;
}
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ;
2024-07-09 10:42:10 +08:00
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
std : : string app = kRtpAppName ;
if ( ! allArgs [ " app " ] . empty ( ) ) {
app = allArgs [ " app " ] ;
}
auto stream_id = allArgs [ " stream_id " ] ;
auto tuple = MediaTuple { vhost , app , stream_id , " " } ;
auto server = s_rtp_server . find ( tuple . shortUrl ( ) ) ;
2024-03-10 16:31:20 +08:00
if ( ! server ) {
2024-07-09 10:42:10 +08:00
cb ( SockException ( Err_other , " can not find the stream " ) ) ;
2024-03-10 16:31:20 +08:00
return ;
}
server - > connectToServer ( allArgs [ " dst_url " ] , allArgs [ " dst_port " ] , cb ) ;
2022-09-09 10:56:28 +08:00
} ) ;
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/closeRtpServer " , [ ] ( API_ARGS_MAP ) {
2020-07-02 22:23:43 +08:00
CHECK_SECRET ( ) ;
2020-07-09 10:38:57 +08:00
CHECK_ARGS ( " stream_id " ) ;
2020-07-02 22:23:43 +08:00
2024-07-09 10:42:10 +08:00
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
std : : string app = kRtpAppName ;
if ( ! allArgs [ " app " ] . empty ( ) ) {
app = allArgs [ " app " ] ;
}
auto stream_id = allArgs [ " stream_id " ] ;
auto tuple = MediaTuple { vhost , app , stream_id , " " } ;
if ( s_rtp_server . erase ( tuple . shortUrl ( ) ) = = 0 ) {
2020-08-08 12:20:44 +08:00
val [ " hit " ] = 0 ;
return ;
}
val [ " hit " ] = 1 ;
2020-07-02 22:23:43 +08:00
} ) ;
2023-04-21 23:08:48 +08:00
api_regist ( " /index/api/updateRtpServerSSRC " , [ ] ( API_ARGS_MAP ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " stream_id " , " ssrc " ) ;
2024-07-09 10:42:10 +08:00
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
std : : string app = kRtpAppName ;
if ( ! allArgs [ " app " ] . empty ( ) ) {
app = allArgs [ " app " ] ;
}
auto stream_id = allArgs [ " stream_id " ] ;
auto tuple = MediaTuple { vhost , app , stream_id , " " } ;
auto server = s_rtp_server . find ( tuple . shortUrl ( ) ) ;
2024-03-10 16:31:20 +08:00
if ( ! server ) {
2023-04-21 23:08:48 +08:00
throw ApiRetException ( " RtpServer not found by stream_id " , API : : NotFound ) ;
}
2024-03-10 16:31:20 +08:00
server - > updateSSRC ( allArgs [ " ssrc " ] ) ;
2023-04-21 23:08:48 +08:00
} ) ;
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/listRtpServer " , [ ] ( API_ARGS_MAP ) {
2020-07-02 22:26:38 +08:00
CHECK_SECRET ( ) ;
2024-03-10 16:31:20 +08:00
std : : lock_guard < std : : recursive_mutex > lck ( s_rtp_server . _mtx ) ;
for ( auto & pr : s_rtp_server . _map ) {
2024-07-09 10:42:10 +08:00
auto vec = split ( pr . first , " / " ) ;
2020-07-09 10:38:57 +08:00
Value obj ;
2024-07-09 10:42:10 +08:00
obj [ " vhost " ] = vec [ 0 ] ;
obj [ " app " ] = vec [ 1 ] ;
obj [ " stream_id " ] = vec [ 2 ] ;
2024-11-01 10:47:18 +08:00
auto & rtps = pr . second ;
obj [ " port " ] = rtps - > getPort ( ) ;
obj [ " ssrc " ] = rtps - > getSSRC ( ) ;
obj [ " tcp_mode " ] = rtps - > getTcpMode ( ) ;
obj [ " only_track " ] = rtps - > getOnlyTrack ( ) ;
2020-07-09 10:38:57 +08:00
val [ " data " ] . append ( obj ) ;
2020-07-02 22:26:38 +08:00
}
} ) ;
2020-07-02 22:23:43 +08:00
2024-07-28 22:55:33 +08:00
static auto start_send_rtp = [ ] ( bool passive , API_ARGS_MAP_ASYNC ) {
2021-10-21 10:21:52 +08:00
auto src = MediaSource : : find ( allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] , allArgs [ " from_mp4 " ] . as < int > ( ) ) ;
2021-08-12 16:07:31 +08:00
if ( ! src ) {
2022-06-11 14:17:43 +08:00
throw ApiRetException ( " can not find the source stream " , API : : NotFound ) ;
2020-09-06 17:56:05 +08:00
}
2024-04-10 13:14:53 +08:00
auto type = allArgs [ " type " ] . empty ( ) ? ( int ) MediaSourceEvent : : SendRtpArgs : : kRtpPS : allArgs [ " type " ] . as < int > ( ) ;
2024-03-02 16:53:53 +08:00
if ( ! allArgs [ " use_ps " ] . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 兼容之前的use_ps参数 [AUTO-TRANSLATED:0193f489]
// Compatible with the previous use_ps parameter
2024-03-10 16:17:29 +08:00
type = allArgs [ " use_ps " ] . as < int > ( ) ;
2024-03-02 16:53:53 +08:00
}
2022-04-03 18:25:36 +08:00
MediaSourceEvent : : SendRtpArgs args ;
2024-07-28 22:55:33 +08:00
if ( passive ) {
args . con_type = allArgs [ " is_udp " ] . as < bool > ( ) ? mediakit : : MediaSourceEvent : : SendRtpArgs : : kUdpPassive : mediakit : : MediaSourceEvent : : SendRtpArgs : : kTcpPassive ;
} else {
args . con_type = allArgs [ " is_udp " ] . as < bool > ( ) ? mediakit : : MediaSourceEvent : : SendRtpArgs : : kUdpActive : mediakit : : MediaSourceEvent : : SendRtpArgs : : kTcpActive ;
}
2022-04-03 18:25:36 +08:00
args . dst_url = allArgs [ " dst_url " ] ;
args . dst_port = allArgs [ " dst_port " ] ;
2023-11-07 23:38:58 +08:00
args . ssrc_multi_send = allArgs [ " ssrc_multi_send " ] . empty ( ) ? false : allArgs [ " ssrc_multi_send " ] . as < bool > ( ) ;
2022-04-03 18:25:36 +08:00
args . ssrc = allArgs [ " ssrc " ] ;
args . src_port = allArgs [ " src_port " ] ;
args . pt = allArgs [ " pt " ] . empty ( ) ? 96 : allArgs [ " pt " ] . as < int > ( ) ;
2024-07-28 22:55:33 +08:00
args . data_type = ( MediaSourceEvent : : SendRtpArgs : : DataType ) type ;
2022-08-20 12:48:27 +08:00
args . only_audio = allArgs [ " only_audio " ] . as < bool > ( ) ;
args . udp_rtcp_timeout = allArgs [ " udp_rtcp_timeout " ] ;
2023-01-07 22:36:30 +08:00
args . recv_stream_id = allArgs [ " recv_stream_id " ] ;
2024-07-28 22:55:33 +08:00
args . close_delay_ms = allArgs [ " close_delay_ms " ] ;
2024-09-19 14:53:50 +08:00
// 记录发送流的app和vhost [AUTO-TRANSLATED:ee1b41d5]
// Record the app and vhost of the sending stream
2024-08-22 19:50:29 +08:00
args . recv_stream_app = allArgs [ " app " ] ;
2024-08-28 12:28:01 +08:00
args . recv_stream_vhost = allArgs [ " vhost " ] ;
2022-06-11 14:17:43 +08:00
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
2024-07-28 22:55:33 +08:00
try {
src - > startSendRtp ( args , [ val , headerOut , invoker ] ( uint16_t local_port , const SockException & ex ) mutable {
if ( ex ) {
val [ " code " ] = API : : OtherFailed ;
val [ " msg " ] = ex . what ( ) ;
}
val [ " local_port " ] = local_port ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
} catch ( std : : exception & ex ) {
val [ " code " ] = API : : Exception ;
val [ " msg " ] = ex . what ( ) ;
2022-06-11 14:17:43 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2024-07-28 22:55:33 +08:00
}
2022-04-03 18:25:36 +08:00
} ) ;
2024-07-28 22:55:33 +08:00
} ;
api_regist ( " /index/api/startSendRtp " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " vhost " , " app " , " stream " , " ssrc " , " dst_url " , " dst_port " , " is_udp " ) ;
start_send_rtp ( false , API_ARGS_VALUE , invoker ) ;
} ) ;
api_regist ( " /index/api/startSendRtpPassive " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " vhost " , " app " , " stream " , " ssrc " ) ;
start_send_rtp ( true , API_ARGS_VALUE , invoker ) ;
2020-09-06 17:56:05 +08:00
} ) ;
2024-11-09 19:17:54 +08:00
api_regist ( " /index/api/startSendRtpTalk " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " vhost " , " app " , " stream " , " ssrc " , " recv_stream_id " ) ;
auto src = MediaSource : : find ( allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] , allArgs [ " from_mp4 " ] . as < int > ( ) ) ;
if ( ! src ) {
throw ApiRetException ( " can not find the source stream " , API : : NotFound ) ;
}
MediaSourceEvent : : SendRtpArgs args ;
args . con_type = mediakit : : MediaSourceEvent : : SendRtpArgs : : kVoiceTalk ;
args . ssrc = allArgs [ " ssrc " ] ;
args . pt = allArgs [ " pt " ] . empty ( ) ? 96 : allArgs [ " pt " ] . as < int > ( ) ;
args . data_type = allArgs [ " type " ] . empty ( ) ? MediaSourceEvent : : SendRtpArgs : : kRtpPS : ( MediaSourceEvent : : SendRtpArgs : : DataType ) ( allArgs [ " type " ] . as < int > ( ) ) ;
args . only_audio = allArgs [ " only_audio " ] . as < bool > ( ) ;
args . recv_stream_id = allArgs [ " recv_stream_id " ] ;
args . recv_stream_app = allArgs [ " app " ] ;
args . recv_stream_vhost = allArgs [ " vhost " ] ;
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
try {
src - > startSendRtp ( args , [ val , headerOut , invoker ] ( uint16_t local_port , const SockException & ex ) mutable {
if ( ex ) {
val [ " code " ] = API : : OtherFailed ;
val [ " msg " ] = ex . what ( ) ;
}
val [ " local_port " ] = local_port ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
} catch ( std : : exception & ex ) {
val [ " code " ] = API : : Exception ;
val [ " msg " ] = ex . what ( ) ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
}
} ) ;
} ) ;
2024-04-21 11:31:43 +08:00
api_regist ( " /index/api/listRtpSender " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " vhost " , " app " , " stream " ) ;
auto src = MediaSource : : find ( allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
if ( ! src ) {
throw ApiRetException ( " can not find the source stream " , API : : NotFound ) ;
}
auto muxer = src - > getMuxer ( ) ;
CHECK ( muxer , " get muxer from media source failed " ) ;
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
muxer - > forEachRtpSender ( [ & ] ( const std : : string & ssrc ) mutable {
val [ " data " ] . append ( ssrc ) ;
} ) ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
} ) ;
2022-06-11 14:17:43 +08:00
api_regist ( " /index/api/stopSendRtp " , [ ] ( API_ARGS_MAP_ASYNC ) {
2020-09-06 17:56:05 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " vhost " , " app " , " stream " ) ;
2020-09-06 18:19:54 +08:00
auto src = MediaSource : : find ( allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
2020-09-06 17:56:05 +08:00
if ( ! src ) {
2022-06-11 14:17:43 +08:00
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
2020-09-06 17:56:05 +08:00
}
2022-06-11 14:17:43 +08:00
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
2024-09-19 14:53:50 +08:00
// ssrc如果为空, 关闭全部 [AUTO-TRANSLATED:e0955dab]
// If ssrc is empty, close all
2022-06-11 14:17:43 +08:00
if ( ! src - > stopSendRtp ( allArgs [ " ssrc " ] ) ) {
val [ " code " ] = API : : OtherFailed ;
val [ " msg " ] = " stopSendRtp failed " ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
return ;
}
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2020-09-06 17:56:05 +08:00
} ) ;
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/pauseRtpCheck " , [ ] ( API_ARGS_MAP ) {
2020-12-25 16:05:38 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " stream_id " ) ;
2024-07-09 10:42:10 +08:00
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
std : : string app = kRtpAppName ;
if ( ! allArgs [ " app " ] . empty ( ) ) {
app = allArgs [ " app " ] ;
}
2024-09-19 14:53:50 +08:00
// 只是暂停流的检查, 流媒体服务器做为流负载服务, 收流就转发, RTSP/RTMP有自己暂停协议 [AUTO-TRANSLATED:dda6ee31]
// Only pause the stream check, the media server acts as a stream load balancing service, receiving the stream and forwarding it, RTSP/RTMP has its own pause protocol
2024-07-09 10:42:10 +08:00
auto src = MediaSource : : find ( vhost , app , allArgs [ " stream_id " ] ) ;
2024-06-09 10:52:10 +08:00
auto process = src ? src - > getRtpProcess ( ) : nullptr ;
if ( process ) {
process - > setStopCheckRtp ( true ) ;
2021-03-16 11:30:51 +08:00
} else {
val [ " code " ] = API : : NotFound ;
2020-12-25 16:05:38 +08:00
}
} ) ;
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/resumeRtpCheck " , [ ] ( API_ARGS_MAP ) {
2020-12-25 16:05:38 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " stream_id " ) ;
2024-07-09 10:42:10 +08:00
std : : string vhost = DEFAULT_VHOST ;
if ( ! allArgs [ " vhost " ] . empty ( ) ) {
vhost = allArgs [ " vhost " ] ;
}
std : : string app = kRtpAppName ;
if ( ! allArgs [ " app " ] . empty ( ) ) {
app = allArgs [ " app " ] ;
}
auto src = MediaSource : : find ( vhost , app , allArgs [ " stream_id " ] ) ;
2024-06-09 10:52:10 +08:00
auto process = src ? src - > getRtpProcess ( ) : nullptr ;
if ( process ) {
process - > setStopCheckRtp ( false ) ;
2021-03-16 11:30:51 +08:00
} else {
val [ " code " ] = API : : NotFound ;
2020-12-25 16:05:38 +08:00
}
} ) ;
2020-09-06 17:56:05 +08:00
2019-12-06 11:54:10 +08:00
# endif //ENABLE_RTPPROXY
2019-12-05 19:53:55 +08:00
2024-09-19 14:53:50 +08:00
// 开始录制hls或MP4 [AUTO-TRANSLATED:0818775e]
// Start recording hls or MP4
2022-06-11 14:17:43 +08:00
api_regist ( " /index/api/startRecord " , [ ] ( API_ARGS_MAP_ASYNC ) {
2020-04-05 09:26:29 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " type " , " vhost " , " app " , " stream " ) ;
2022-06-11 14:17:43 +08:00
auto src = MediaSource : : find ( allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
if ( ! src ) {
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
}
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
auto result = src - > setupRecord ( ( Recorder : : type ) allArgs [ " type " ] . as < int > ( ) , true , allArgs [ " customized_path " ] , allArgs [ " max_second " ] . as < size_t > ( ) ) ;
val [ " result " ] = result ;
val [ " code " ] = result ? API : : Success : API : : OtherFailed ;
val [ " msg " ] = result ? " success " : " start record failed " ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2020-04-05 09:26:29 +08:00
} ) ;
2023-04-01 23:54:11 +08:00
2024-09-19 14:53:50 +08:00
// 设置录像流播放速度 [AUTO-TRANSLATED:a8d82298]
// Set the playback speed of the recording stream
2022-06-11 14:17:43 +08:00
api_regist ( " /index/api/setRecordSpeed " , [ ] ( API_ARGS_MAP_ASYNC ) {
2021-10-25 15:13:21 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " schema " , " vhost " , " app " , " stream " , " speed " ) ;
auto src = MediaSource : : find ( allArgs [ " schema " ] ,
allArgs [ " vhost " ] ,
allArgs [ " app " ] ,
allArgs [ " stream " ] ) ;
2022-06-11 14:17:43 +08:00
if ( ! src ) {
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
}
auto speed = allArgs [ " speed " ] . as < float > ( ) ;
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
bool flag = src - > speed ( speed ) ;
2021-10-25 15:13:21 +08:00
val [ " result " ] = flag ? 0 : - 1 ;
val [ " msg " ] = flag ? " success " : " set failed " ;
val [ " code " ] = flag ? API : : Success : API : : OtherFailed ;
2022-06-11 14:17:43 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2021-10-25 15:13:21 +08:00
} ) ;
2022-06-11 14:17:43 +08:00
api_regist ( " /index/api/seekRecordStamp " , [ ] ( API_ARGS_MAP_ASYNC ) {
2021-10-25 15:13:21 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " schema " , " vhost " , " app " , " stream " , " stamp " ) ;
auto src = MediaSource : : find ( allArgs [ " schema " ] ,
allArgs [ " vhost " ] ,
allArgs [ " app " ] ,
allArgs [ " stream " ] ) ;
2022-06-11 14:17:43 +08:00
if ( ! src ) {
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
}
auto stamp = allArgs [ " stamp " ] . as < size_t > ( ) ;
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
bool flag = src - > seekTo ( stamp ) ;
2021-10-25 15:13:21 +08:00
val [ " result " ] = flag ? 0 : - 1 ;
val [ " msg " ] = flag ? " success " : " seek failed " ;
val [ " code " ] = flag ? API : : Success : API : : OtherFailed ;
2022-06-11 14:17:43 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2021-10-25 15:13:21 +08:00
} ) ;
2020-04-05 09:26:29 +08:00
2024-09-19 14:53:50 +08:00
// 停止录制hls或MP4 [AUTO-TRANSLATED:24d11a0c]
// Stop recording hls or MP4
2022-06-11 14:17:43 +08:00
api_regist ( " /index/api/stopRecord " , [ ] ( API_ARGS_MAP_ASYNC ) {
2020-04-05 09:26:29 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " type " , " vhost " , " app " , " stream " ) ;
2022-06-11 14:17:43 +08:00
auto src = MediaSource : : find ( allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
if ( ! src ) {
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
}
auto type = ( Recorder : : type ) allArgs [ " type " ] . as < int > ( ) ;
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
auto result = src - > setupRecord ( type , false , " " , 0 ) ;
val [ " result " ] = result ;
val [ " code " ] = result ? API : : Success : API : : OtherFailed ;
val [ " msg " ] = result ? " success " : " stop record failed " ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2020-04-05 09:26:29 +08:00
} ) ;
2024-09-19 14:53:50 +08:00
// 获取hls或MP4录制状态 [AUTO-TRANSLATED:a08a2f1a]
// Get the recording status of hls or MP4
2022-06-11 14:17:43 +08:00
api_regist ( " /index/api/isRecording " , [ ] ( API_ARGS_MAP_ASYNC ) {
2020-04-05 09:26:29 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " type " , " vhost " , " app " , " stream " ) ;
2022-06-11 14:17:43 +08:00
auto src = MediaSource : : find ( allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
if ( ! src ) {
throw ApiRetException ( " can not find the stream " , API : : NotFound ) ;
}
auto type = ( Recorder : : type ) allArgs [ " type " ] . as < int > ( ) ;
src - > getOwnerPoller ( ) - > async ( [ = ] ( ) mutable {
val [ " status " ] = src - > isRecording ( type ) ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2020-04-05 09:26:29 +08:00
} ) ;
2023-04-01 23:54:11 +08:00
2023-05-03 18:52:11 +08:00
api_regist ( " /index/api/getProxyPusherInfo " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " key " ) ;
2024-03-10 16:31:20 +08:00
auto pusher = s_pusher_proxy . find ( allArgs [ " key " ] ) ;
if ( ! pusher ) {
2023-05-03 18:52:11 +08:00
throw ApiRetException ( " can not find pusher " , API : : NotFound ) ;
}
2024-11-01 10:47:18 +08:00
val [ " data " ] = ToJson ( pusher ) ;
2023-05-03 18:52:11 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
api_regist ( " /index/api/getProxyInfo " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " key " ) ;
2024-03-10 16:31:20 +08:00
auto proxy = s_player_proxy . find ( allArgs [ " key " ] ) ;
if ( ! proxy ) {
2023-05-03 18:52:11 +08:00
throw ApiRetException ( " can not find the proxy " , API : : NotFound ) ;
}
2024-11-01 10:47:18 +08:00
val [ " data " ] = ToJson ( proxy ) ;
2023-05-03 18:52:11 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2024-09-19 14:53:50 +08:00
// 删除录像文件夹 [AUTO-TRANSLATED:821aed07]
// Delete the recording folder
2022-08-04 10:15:07 +08:00
// http://127.0.0.1/index/api/deleteRecordDirectroy?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01-01
2022-08-05 16:01:31 +08:00
api_regist ( " /index/api/deleteRecordDirectory " , [ ] ( API_ARGS_MAP ) {
2022-08-04 10:15:07 +08:00
CHECK_SECRET ( ) ;
2023-10-28 10:22:43 +08:00
CHECK_ARGS ( " vhost " , " app " , " stream " , " period " ) ;
2024-03-30 14:59:28 +08:00
auto tuple = MediaTuple { allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] , " " } ;
2023-05-25 16:23:24 +08:00
auto record_path = Recorder : : getRecordPath ( Recorder : : type_mp4 , tuple , allArgs [ " customized_path " ] ) ;
2022-08-04 10:15:07 +08:00
auto period = allArgs [ " period " ] ;
record_path = record_path + period + " / " ;
2024-05-15 11:30:16 +08:00
bool recording = false ;
2023-10-28 10:22:43 +08:00
auto name = allArgs [ " name " ] ;
if ( ! name . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 删除指定文件 [AUTO-TRANSLATED:e8ee7bfa]
// Delete the specified file
2023-10-28 10:22:43 +08:00
record_path + = name ;
2024-05-15 11:30:16 +08:00
} else {
2024-09-19 14:53:50 +08:00
// 删除文件夹,先判断该流是否正在录制中 [AUTO-TRANSLATED:9f124786]
// Delete the folder, first check if the stream is being recorded
2023-12-02 15:22:16 +08:00
auto src = MediaSource : : find ( allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] ) ;
if ( src & & src - > isRecording ( Recorder : : type_mp4 ) ) {
recording = true ;
}
2022-08-04 10:15:07 +08:00
}
2022-08-05 16:01:31 +08:00
val [ " path " ] = record_path ;
2023-12-02 15:22:16 +08:00
if ( ! recording ) {
2023-12-02 21:12:26 +08:00
val [ " code " ] = File : : delete_file ( record_path , true ) ;
2023-12-02 15:22:16 +08:00
return ;
}
File : : scanDir ( record_path , [ ] ( const string & path , bool is_dir ) {
if ( is_dir ) {
return true ;
}
2023-12-02 21:12:26 +08:00
if ( path . find ( " /. " ) = = std : : string : : npos ) {
2023-12-02 19:49:28 +08:00
File : : delete_file ( path ) ;
2023-12-02 15:22:16 +08:00
} else {
TraceL < < " Ignore tmp mp4 file: " < < path ;
}
return true ;
2023-12-02 21:12:26 +08:00
} , true , true ) ;
File : : deleteEmptyDir ( record_path ) ;
2022-08-04 10:15:07 +08:00
} ) ;
2023-04-01 23:54:11 +08:00
2024-09-19 14:53:50 +08:00
// 获取录像文件夹列表或mp4文件列表 [AUTO-TRANSLATED:f7e299bc]
// Get the list of recording folders or mp4 files
2023-12-02 14:56:59 +08:00
//http://127.0.0.1/index/api/getMP4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01
api_regist ( " /index/api/getMP4RecordFile " , [ ] ( API_ARGS_MAP ) {
2020-02-01 23:26:33 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " vhost " , " app " , " stream " ) ;
2024-03-30 14:59:28 +08:00
auto tuple = MediaTuple { allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] , " " } ;
2023-05-25 16:23:24 +08:00
auto record_path = Recorder : : getRecordPath ( Recorder : : type_mp4 , tuple , allArgs [ " customized_path " ] ) ;
2020-02-01 23:26:33 +08:00
auto period = allArgs [ " period " ] ;
2024-09-19 14:53:50 +08:00
// 判断是获取mp4文件列表还是获取文件夹列表 [AUTO-TRANSLATED:b9c86d2f]
// Determine whether to get the mp4 file list or the folder list
2020-02-01 23:26:33 +08:00
bool search_mp4 = period . size ( ) = = sizeof ( " 2020-02-01 " ) - 1 ;
if ( search_mp4 ) {
record_path = record_path + period + " / " ;
}
Json : : Value paths ( arrayValue ) ;
2024-09-19 14:53:50 +08:00
// 这是筛选日期,获取文件夹列表 [AUTO-TRANSLATED:786fa49d]
// This is to filter the date and get the folder list
2020-02-01 23:26:33 +08:00
File : : scanDir ( record_path , [ & ] ( const string & path , bool isDir ) {
2021-01-17 18:31:50 +08:00
auto pos = path . rfind ( ' / ' ) ;
2020-02-01 23:26:33 +08:00
if ( pos ! = string : : npos ) {
string relative_path = path . substr ( pos + 1 ) ;
if ( search_mp4 ) {
if ( ! isDir ) {
2024-09-19 14:53:50 +08:00
// 我们只收集mp4文件, 对文件夹不感兴趣 [AUTO-TRANSLATED:254d9f25]
// We only collect mp4 files, we are not interested in folders
2020-02-01 23:26:33 +08:00
paths . append ( relative_path ) ;
}
} else if ( isDir & & relative_path . find ( period ) = = 0 ) {
2024-09-19 14:53:50 +08:00
// 匹配到对应日期的文件夹 [AUTO-TRANSLATED:cd3d10b9]
// Match the folder for the corresponding date
2020-02-01 23:26:33 +08:00
paths . append ( relative_path ) ;
}
}
return true ;
} , false ) ;
2020-01-19 14:54:31 +08:00
2020-02-01 23:26:33 +08:00
val [ " data " ] [ " rootPath " ] = record_path ;
val [ " data " ] [ " paths " ] = paths ;
2020-03-20 11:51:24 +08:00
} ) ;
2020-01-19 14:54:31 +08:00
2020-06-10 10:50:37 +08:00
static auto responseSnap = [ ] ( const string & snap_path ,
const HttpSession : : KeyValue & headerIn ,
2022-02-24 11:28:48 +08:00
const HttpSession : : HttpResponseInvoker & invoker ,
const string & err_msg = " " ) {
static bool s_snap_success_once = false ;
2020-06-10 10:50:37 +08:00
StrCaseMap headerOut ;
GET_CONFIG ( string , defaultSnap , API : : kDefaultSnap ) ;
2023-12-02 19:49:28 +08:00
if ( ! File : : fileSize ( snap_path ) ) {
2022-02-24 11:28:48 +08:00
if ( ! err_msg . empty ( ) & & ( ! s_snap_success_once | | defaultSnap . empty ( ) ) ) {
2024-09-19 14:53:50 +08:00
// 重来没截图成功过或者默认截图图片为空, 那么直接返回FFmpeg错误日志 [AUTO-TRANSLATED:5bde510f]
// If the screenshot has never been successful or the default screenshot image is empty, then directly return the FFmpeg error log
2022-02-24 11:28:48 +08:00
headerOut [ " Content-Type " ] = HttpFileManager : : getContentType ( " .txt " ) ;
invoker . responseFile ( headerIn , headerOut , err_msg , false , false ) ;
return ;
}
2024-09-19 14:53:50 +08:00
// 截图成功过一次,那么认为配置无错误,截图失败时,返回预设默认图片 [AUTO-TRANSLATED:ffe4d807]
// If the screenshot has been successful once, then it is considered that the configuration is error-free, and when the screenshot fails, the preset default image is returned
2022-02-24 11:28:48 +08:00
const_cast < string & > ( snap_path ) = File : : absolutePath ( " " , defaultSnap ) ;
2020-06-10 10:50:37 +08:00
headerOut [ " Content-Type " ] = HttpFileManager : : getContentType ( snap_path . data ( ) ) ;
} else {
2022-02-24 11:28:48 +08:00
s_snap_success_once = true ;
2024-09-19 14:53:50 +08:00
// 之前生成的截图文件, 我们默认为jpeg格式 [AUTO-TRANSLATED:5cc5c1ff]
// The previously generated screenshot file, we default to jpeg format
2020-06-10 10:50:37 +08:00
headerOut [ " Content-Type " ] = HttpFileManager : : getContentType ( " .jpeg " ) ;
}
2024-09-19 14:53:50 +08:00
// 返回图片给http客户端 [AUTO-TRANSLATED:58a1f64e]
// Return image to http client
2020-06-10 10:50:37 +08:00
invoker . responseFile ( headerIn , headerOut , snap_path ) ;
} ;
2020-05-09 00:06:36 +08:00
2024-09-19 14:53:50 +08:00
// 获取截图缓存或者实时截图 [AUTO-TRANSLATED:78e2fe1e]
// Get screenshot cache or real-time screenshot
2020-05-09 00:06:36 +08:00
//http://127.0.0.1/index/api/getSnap?url=rtmp://127.0.0.1/record/robot.mp4&timeout_sec=10&expire_sec=3
2020-12-27 22:14:59 +08:00
api_regist ( " /index/api/getSnap " , [ ] ( API_ARGS_MAP_ASYNC ) {
2020-05-09 00:06:36 +08:00
CHECK_SECRET ( ) ;
CHECK_ARGS ( " url " , " timeout_sec " , " expire_sec " ) ;
2020-06-10 10:50:37 +08:00
GET_CONFIG ( string , snap_root , API : : kSnapRoot ) ;
2020-09-26 10:07:22 +08:00
bool have_old_snap = false , res_old_snap = false ;
2020-05-09 00:06:36 +08:00
int expire_sec = allArgs [ " expire_sec " ] ;
2020-05-09 09:29:45 +08:00
auto scan_path = File : : absolutePath ( MD5 ( allArgs [ " url " ] ) . hexdigest ( ) , snap_root ) + " / " ;
2020-09-26 10:07:22 +08:00
string new_snap = StrPrinter < < scan_path < < time ( NULL ) < < " .jpeg " ;
2020-05-09 09:29:45 +08:00
File : : scanDir ( scan_path , [ & ] ( const string & path , bool isDir ) {
2020-09-26 10:07:22 +08:00
if ( isDir | | ! end_with ( path , " .jpeg " ) ) {
2024-09-19 14:53:50 +08:00
// 忽略文件夹或其他类型的文件 [AUTO-TRANSLATED:3ecffcae]
// Ignore folders or other types of files
2020-05-09 09:29:45 +08:00
return true ;
2020-05-09 00:06:36 +08:00
}
2020-05-09 09:29:45 +08:00
2024-09-19 14:53:50 +08:00
// 找到截图 [AUTO-TRANSLATED:b784cfec]
// Find screenshot
2023-06-10 12:28:49 +08:00
auto tm = findSubString ( path . data ( ) + scan_path . size ( ) , nullptr , " .jpeg " ) ;
2020-05-09 09:29:45 +08:00
if ( atoll ( tm . data ( ) ) + expire_sec < time ( NULL ) ) {
2024-09-19 14:53:50 +08:00
// 截图已经过期,改名,以便再次请求时,可以返回老截图 [AUTO-TRANSLATED:94fac79b]
// Screenshot has expired, rename it so that it can be returned when requested again
2020-09-26 10:07:22 +08:00
rename ( path . data ( ) , new_snap . data ( ) ) ;
have_old_snap = true ;
2020-05-09 09:29:45 +08:00
return true ;
}
2024-09-19 14:53:50 +08:00
// 截图存在,且未过期,那么返回之 [AUTO-TRANSLATED:6f53d3d1]
// Screenshot exists and has not expired, so return it
2020-09-26 10:07:22 +08:00
res_old_snap = true ;
2024-03-23 22:46:30 +08:00
responseSnap ( path , allArgs . parser . getHeader ( ) , invoker ) ;
2024-09-19 14:53:50 +08:00
// 中断遍历 [AUTO-TRANSLATED:7893aab3]
// Interrupt traversal
2020-05-09 09:29:45 +08:00
return false ;
2020-05-09 00:06:36 +08:00
} ) ;
2020-09-26 10:07:22 +08:00
if ( res_old_snap ) {
2024-09-19 14:53:50 +08:00
// 已经回复了旧的截图 [AUTO-TRANSLATED:9051a3e6]
// Old screenshot has been replied
2020-06-10 10:33:48 +08:00
return ;
2020-05-09 00:06:36 +08:00
}
2024-09-19 14:53:50 +08:00
// 无截图或者截图已经过期 [AUTO-TRANSLATED:89c46415]
// No screenshot or screenshot has expired
2020-09-26 10:07:22 +08:00
if ( ! have_old_snap ) {
2024-09-19 14:53:50 +08:00
// 无过期截图,生成一个空文件,目的是顺便创建文件夹路径 [AUTO-TRANSLATED:bdbfdbcb]
// No expired screenshot, generate an empty file, the purpose is to create the folder path by the way
// 同时防止在FFmpeg生成截图途中不停的尝试调用该api多次启动FFmpeg进程 [AUTO-TRANSLATED:a04e1ee2]
// At the same time, prevent the FFmpeg process from being started multiple times by continuously trying to call this API during the FFmpeg screenshot generation process
2023-12-02 19:49:28 +08:00
auto file = File : : create_file ( new_snap , " wb " ) ;
2020-09-26 10:07:22 +08:00
if ( file ) {
fclose ( file ) ;
}
2020-05-09 09:39:36 +08:00
}
2024-09-19 14:53:50 +08:00
// 启动FFmpeg进程, 开始截图, 生成临时文件, 截图成功后替换为正式文件 [AUTO-TRANSLATED:7d589e3f]
// Start the FFmpeg process, start taking screenshots, generate temporary files, replace them with formal files after successful screenshots
2020-09-26 10:07:22 +08:00
auto new_snap_tmp = new_snap + " .tmp " ;
2022-02-24 11:28:48 +08:00
FFmpegSnap : : makeSnap ( allArgs [ " url " ] , new_snap_tmp , allArgs [ " timeout_sec " ] , [ invoker , allArgs , new_snap , new_snap_tmp ] ( bool success , const string & err_msg ) {
2020-09-26 10:07:22 +08:00
if ( ! success ) {
2024-09-19 14:53:50 +08:00
// 生成截图失败,可能残留空文件 [AUTO-TRANSLATED:c96a4468]
// Screenshot generation failed, there may be residual empty files
2023-12-02 19:49:28 +08:00
File : : delete_file ( new_snap_tmp ) ;
2020-09-26 10:07:22 +08:00
} else {
2024-09-19 14:53:50 +08:00
// 临时文件改成正式文件 [AUTO-TRANSLATED:eca24dfd]
// Temporary file changed to formal file
2023-12-02 19:49:28 +08:00
File : : delete_file ( new_snap ) ;
2020-09-26 10:07:22 +08:00
rename ( new_snap_tmp . data ( ) , new_snap . data ( ) ) ;
2020-05-09 00:06:36 +08:00
}
2024-03-23 22:46:30 +08:00
responseSnap ( new_snap , allArgs . parser . getHeader ( ) , invoker , err_msg ) ;
2020-05-09 00:06:36 +08:00
} ) ;
} ) ;
2021-12-27 17:40:15 +08:00
api_regist ( " /index/api/getStatistic " , [ ] ( API_ARGS_MAP_ASYNC ) {
2021-01-23 09:44:37 +08:00
CHECK_SECRET ( ) ;
2021-12-27 17:40:15 +08:00
getStatisticJson ( [ headerOut , val , invoker ] ( const Value & data ) mutable {
val [ " data " ] = data ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
2021-01-23 09:44:37 +08:00
} ) ;
2021-03-24 16:52:41 +08:00
# ifdef ENABLE_WEBRTC
2021-10-19 15:23:12 +08:00
class WebRtcArgsImp : public WebRtcArgs {
public :
2024-03-23 22:46:30 +08:00
WebRtcArgsImp ( const ArgsString & args , std : : string session_id )
2022-03-27 21:12:19 +08:00
: _args ( args )
, _session_id ( std : : move ( session_id ) ) { }
2021-10-19 15:23:12 +08:00
~ WebRtcArgsImp ( ) override = default ;
2024-05-23 17:36:56 +08:00
toolkit : : variant operator [ ] ( const string & key ) const override {
2021-10-19 15:23:12 +08:00
if ( key = = " url " ) {
return getUrl ( ) ;
2021-04-04 23:20:10 +08:00
}
2021-10-19 15:23:12 +08:00
return _args [ key ] ;
2021-04-04 23:20:10 +08:00
}
2021-10-19 15:23:12 +08:00
private :
string getUrl ( ) const {
auto & allArgs = _args ;
2021-10-16 17:07:21 +08:00
CHECK_ARGS ( " app " , " stream " ) ;
2024-08-29 10:58:51 +08:00
string auth = _args [ " Authorization " ] ; // Authorization Bearer
return StrPrinter < < " rtc:// " < < _args [ " Host " ] < < " / " < < _args [ " app " ] < < " / " < < _args [ " stream " ] < < " ? "
< < _args . parser . params ( ) + " &session= " + _session_id + ( auth . empty ( ) ? " " : ( " &Authorization= " + auth ) ) ;
2021-04-04 23:20:10 +08:00
}
2021-10-19 15:23:12 +08:00
private :
2024-03-23 22:46:30 +08:00
ArgsString _args ;
2022-03-27 21:12:19 +08:00
std : : string _session_id ;
2021-10-19 15:23:12 +08:00
} ;
2021-10-16 10:52:28 +08:00
2021-10-19 15:23:12 +08:00
api_regist ( " /index/api/webrtc " , [ ] ( API_ARGS_STRING_ASYNC ) {
CHECK_ARGS ( " type " ) ;
auto type = allArgs [ " type " ] ;
2024-03-23 22:46:30 +08:00
auto offer = allArgs . args ;
2021-10-19 15:23:12 +08:00
CHECK ( ! offer . empty ( ) , " http body(webrtc offer sdp) is empty " ) ;
2024-03-23 22:46:30 +08:00
auto & session = static_cast < Session & > ( sender ) ;
2023-12-10 10:21:21 +08:00
auto args = std : : make_shared < WebRtcArgsImp > ( allArgs , sender . getIdentifier ( ) ) ;
2024-03-23 22:52:40 +08:00
WebRtcPluginManager : : Instance ( ) . negotiateSdp ( session , type , * args , [ invoker , val , offer , headerOut ] ( const WebRtcInterface & exchanger ) mutable {
2024-03-23 22:46:30 +08:00
auto & handler = const_cast < WebRtcInterface & > ( exchanger ) ;
2021-10-19 15:23:12 +08:00
try {
2024-03-23 22:46:30 +08:00
val [ " sdp " ] = handler . getAnswerSdp ( offer ) ;
2021-10-19 15:23:12 +08:00
val [ " id " ] = exchanger . getIdentifier ( ) ;
val [ " type " ] = " answer " ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} catch ( std : : exception & ex ) {
val [ " code " ] = API : : Exception ;
val [ " msg " ] = ex . what ( ) ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
}
} ) ;
2021-03-24 16:52:41 +08:00
} ) ;
2023-04-08 21:28:17 +08:00
2023-04-28 22:06:46 +08:00
static constexpr char delete_webrtc_url [ ] = " /index/api/delete_webrtc " ;
2023-04-08 21:28:17 +08:00
static auto whip_whep_func = [ ] ( const char * type , API_ARGS_STRING_ASYNC ) {
2024-03-23 22:46:30 +08:00
auto offer = allArgs . args ;
2023-04-08 21:28:17 +08:00
CHECK ( ! offer . empty ( ) , " http body(webrtc offer sdp) is empty " ) ;
2023-04-28 22:06:46 +08:00
auto & session = static_cast < Session & > ( sender ) ;
2024-03-23 22:46:30 +08:00
auto location = std : : string ( session . overSsl ( ) ? " https:// " : " http:// " ) + allArgs [ " host " ] + delete_webrtc_url ;
2023-12-10 10:21:21 +08:00
auto args = std : : make_shared < WebRtcArgsImp > ( allArgs , sender . getIdentifier ( ) ) ;
2024-03-23 22:52:40 +08:00
WebRtcPluginManager : : Instance ( ) . negotiateSdp ( session , type , * args , [ invoker , offer , headerOut , location ] ( const WebRtcInterface & exchanger ) mutable {
2024-03-23 22:46:30 +08:00
auto & handler = const_cast < WebRtcInterface & > ( exchanger ) ;
try {
2024-09-19 14:53:50 +08:00
// 设置返回类型 [AUTO-TRANSLATED:ffc2a31a]
// Set return type
2024-03-23 22:46:30 +08:00
headerOut [ " Content-Type " ] = " application/sdp " ;
headerOut [ " Location " ] = location + " ?id= " + exchanger . getIdentifier ( ) + " &token= " + exchanger . deleteRandStr ( ) ;
invoker ( 201 , headerOut , handler . getAnswerSdp ( offer ) ) ;
} catch ( std : : exception & ex ) {
headerOut [ " Content-Type " ] = " text/plain " ;
invoker ( 406 , headerOut , ex . what ( ) ) ;
}
} ) ;
2023-04-08 21:28:17 +08:00
} ;
api_regist ( " /index/api/whip " , [ ] ( API_ARGS_STRING_ASYNC ) { whip_whep_func ( " push " , API_ARGS_VALUE , invoker ) ; } ) ;
api_regist ( " /index/api/whep " , [ ] ( API_ARGS_STRING_ASYNC ) { whip_whep_func ( " play " , API_ARGS_VALUE , invoker ) ; } ) ;
2023-04-28 22:06:46 +08:00
api_regist ( delete_webrtc_url , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_ARGS ( " id " , " token " ) ;
2024-03-23 22:46:30 +08:00
CHECK ( allArgs . parser . method ( ) = = " DELETE " , " http method is not DELETE: " + allArgs . parser . method ( ) ) ;
2023-04-28 22:06:46 +08:00
auto obj = WebRtcTransportManager : : Instance ( ) . getItem ( allArgs [ " id " ] ) ;
if ( ! obj ) {
invoker ( 404 , headerOut , " id not found " ) ;
return ;
}
if ( obj - > deleteRandStr ( ) ! = allArgs [ " token " ] ) {
invoker ( 401 , headerOut , " token incorrect " ) ;
return ;
}
obj - > safeShutdown ( SockException ( Err_shutdown , " deleted by http api " ) ) ;
invoker ( 200 , headerOut , " " ) ;
} ) ;
2021-03-24 16:52:41 +08:00
# endif
2022-08-12 18:09:44 +08:00
# if defined(ENABLE_VERSION)
api_regist ( " /index/api/version " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
Value ver ;
ver [ " buildTime " ] = BUILD_TIME ;
ver [ " branchName " ] = BRANCH_NAME ;
ver [ " commitHash " ] = COMMIT_HASH ;
val [ " data " ] = ver ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
# endif
2024-07-09 10:43:34 +08:00
# if ENABLE_MP4
2023-11-24 10:44:08 +08:00
api_regist ( " /index/api/loadMP4File " , [ ] ( API_ARGS_MAP ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " vhost " , " app " , " stream " , " file_path " ) ;
ProtocolOption option ;
2024-09-19 14:53:50 +08:00
// mp4支持多track [AUTO-TRANSLATED:b9688762]
// mp4 supports multiple tracks
2023-12-09 22:34:22 +08:00
option . max_track = 16 ;
2024-09-19 14:53:50 +08:00
// 默认解复用mp4不生成mp4 [AUTO-TRANSLATED:11f2dcee]
// By default, demultiplexing mp4 does not generate mp4
2023-11-24 10:44:08 +08:00
option . enable_mp4 = false ;
2024-09-19 14:53:50 +08:00
// 但是如果参数明确指定开启mp4, 那么也允许之 [AUTO-TRANSLATED:b143a9e3]
// But if the parameter explicitly specifies to enable mp4, then it is also allowed
2023-11-24 10:44:08 +08:00
option . load ( allArgs ) ;
2024-09-19 14:53:50 +08:00
// 强制无人观看时自动关闭 [AUTO-TRANSLATED:f7c85948]
// Force automatic shutdown when no one is watching
2023-11-24 10:44:08 +08:00
option . auto_close = true ;
2024-07-14 09:32:41 +08:00
auto tuple = MediaTuple { allArgs [ " vhost " ] , allArgs [ " app " ] , allArgs [ " stream " ] , " " } ;
auto reader = std : : make_shared < MP4Reader > ( tuple , allArgs [ " file_path " ] , option ) ;
2024-09-19 14:53:50 +08:00
// sample_ms设置为0, 从配置文件加载; file_repeat可以指定, 如果配置文件也指定循环解复用, 那么强制开启 [AUTO-TRANSLATED:23e826b4]
// sample_ms is set to 0, loaded from the configuration file; file_repeat can be specified, if the configuration file also specifies loop demultiplexing, then force it to be enabled
2023-11-24 10:44:08 +08:00
reader - > startReadMP4 ( 0 , true , allArgs [ " file_repeat " ] ) ;
} ) ;
2024-07-09 10:43:34 +08:00
# endif
2023-12-02 15:58:40 +08:00
GET_CONFIG_FUNC ( std : : set < std : : string > , download_roots , API : : kDownloadRoot , [ ] ( const string & str ) - > std : : set < std : : string > {
std : : set < std : : string > ret ;
auto vec = toolkit : : split ( str , " ; " ) ;
for ( auto & item : vec ) {
2024-03-15 22:24:45 +08:00
auto root = File : : absolutePath ( " " , item , true ) ;
2023-12-02 15:58:40 +08:00
ret . emplace ( std : : move ( root ) ) ;
}
return ret ;
} ) ;
api_regist ( " /index/api/downloadFile " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_ARGS ( " file_path " ) ;
auto file_path = allArgs [ " file_path " ] ;
if ( file_path . find ( " .. " ) ! = std : : string : : npos ) {
invoker ( 401 , StrCaseMap { } , " You can not access parent directory " ) ;
return ;
}
bool safe = false ;
for ( auto & root : download_roots ) {
if ( start_with ( file_path , root ) ) {
safe = true ;
break ;
}
}
if ( ! safe ) {
invoker ( 401 , StrCaseMap { } , " You can not download files outside the root directory " ) ;
return ;
}
2024-09-19 14:53:50 +08:00
// 通过on_http_access完成文件下载鉴权, 请务必确认访问鉴权url参数以及访问文件路径是否合法 [AUTO-TRANSLATED:73507988]
// File download authentication is completed through on_http_access. Please make sure that the access authentication URL parameters and the access file path are legal
2023-12-02 15:58:40 +08:00
HttpSession : : HttpAccessPathInvoker file_invoker = [ allArgs , invoker ] ( const string & err_msg , const string & cookie_path_in , int life_second ) mutable {
if ( ! err_msg . empty ( ) ) {
invoker ( 401 , StrCaseMap { } , err_msg ) ;
} else {
StrCaseMap res_header ;
auto save_name = allArgs [ " save_name " ] ;
if ( ! save_name . empty ( ) ) {
res_header . emplace ( " Content-Disposition " , " attachment;filename= \" " + save_name + " \" " ) ;
}
2024-03-23 22:46:30 +08:00
invoker . responseFile ( allArgs . parser . getHeader ( ) , res_header , allArgs [ " file_path " ] ) ;
2023-12-02 15:58:40 +08:00
}
} ;
2024-03-23 22:46:30 +08:00
bool flag = NOTICE_EMIT ( BroadcastHttpAccessArgs , Broadcast : : kBroadcastHttpAccess , allArgs . parser , file_path , false , file_invoker , sender ) ;
2023-12-02 15:58:40 +08:00
if ( ! flag ) {
2024-09-19 14:53:50 +08:00
// 文件下载鉴权事件无人监听,不允许下载 [AUTO-TRANSLATED:5e02f0ce]
// No one is listening to the file download authentication event, download is not allowed
2023-12-02 15:58:40 +08:00
invoker ( 401 , StrCaseMap { } , " None http access event listener " ) ;
}
} ) ;
2024-03-16 22:56:32 +08:00
2024-04-13 20:35:59 +08:00
# if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_X264) && defined(ENABLE_FFMPEG)
2024-03-16 22:56:32 +08:00
VideoStackManager : : Instance ( ) . loadBgImg ( " novideo.yuv " ) ;
NoticeCenter : : Instance ( ) . addListener ( nullptr , Broadcast : : kBroadcastStreamNoneReader , [ ] ( BroadcastStreamNoneReaderArgs ) {
auto id = sender . getMediaTuple ( ) . stream ;
VideoStackManager : : Instance ( ) . stopVideoStack ( id ) ;
} ) ;
api_regist ( " /index/api/stack/start " , [ ] ( API_ARGS_JSON_ASYNC ) {
CHECK_SECRET ( ) ;
2024-06-19 14:06:02 +08:00
int ret = 0 ;
try {
ret = VideoStackManager : : Instance ( ) . startVideoStack ( allArgs . args ) ;
val [ " code " ] = ret ;
val [ " msg " ] = ret ? " failed " : " success " ;
} catch ( const std : : exception & e ) {
val [ " code " ] = - 1 ;
val [ " msg " ] = e . what ( ) ;
}
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
} ) ;
api_regist ( " /index/api/stack/reset " , [ ] ( API_ARGS_JSON_ASYNC ) {
CHECK_SECRET ( ) ;
int ret = 0 ;
try {
auto ret = VideoStackManager : : Instance ( ) . resetVideoStack ( allArgs . args ) ;
val [ " code " ] = ret ;
val [ " msg " ] = ret ? " failed " : " success " ;
} catch ( const std : : exception & e ) {
val [ " code " ] = - 1 ;
val [ " msg " ] = e . what ( ) ;
}
2024-03-23 20:47:38 +08:00
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2024-03-16 22:56:32 +08:00
} ) ;
api_regist ( " /index/api/stack/stop " , [ ] ( API_ARGS_MAP_ASYNC ) {
CHECK_SECRET ( ) ;
CHECK_ARGS ( " id " ) ;
auto ret = VideoStackManager : : Instance ( ) . stopVideoStack ( allArgs [ " id " ] ) ;
2024-03-23 20:47:38 +08:00
val [ " code " ] = ret ;
val [ " msg " ] = ret ? " failed " : " success " ;
invoker ( 200 , headerOut , val . toStyledString ( ) ) ;
2024-03-16 22:56:32 +08:00
} ) ;
# endif
2019-05-20 17:03:04 +08:00
}
2019-05-20 16:26:04 +08:00
2019-05-20 17:03:04 +08:00
void unInstallWebApi ( ) {
2024-03-10 16:31:20 +08:00
s_player_proxy . clear ( ) ;
s_ffmpeg_src . clear ( ) ;
s_pusher_proxy . clear ( ) ;
2020-07-02 22:23:43 +08:00
# if defined(ENABLE_RTPPROXY)
2024-03-10 16:31:20 +08:00
s_rtp_server . clear ( ) ;
2020-07-02 22:23:43 +08:00
# endif
2024-06-19 14:06:02 +08:00
# if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_FFMPEG) && defined(ENABLE_X264)
VideoStackManager : : Instance ( ) . clear ( ) ;
# endif
2024-03-10 16:31:20 +08:00
2022-07-29 16:23:35 +08:00
NoticeCenter : : Instance ( ) . delListener ( & web_api_tag ) ;
2021-10-25 15:13:21 +08:00
}