Merge branch 'master' of github.com:ZLMediaKit/ZLMediaKit into feature/transcode2

# Conflicts:
#	src/Common/MediaSource.h
#	src/Common/config.h
#	src/Rtsp/RtspMediaSourceImp.h
#	src/Rtsp/RtspMediaSourceMuxer.h
#	src/Rtsp/RtspMuxer.cpp
#	webrtc/WebRtcTransport.cpp
This commit is contained in:
cqm 2024-09-22 11:40:39 +08:00
commit 89e21038c2
283 changed files with 10913 additions and 3055 deletions

@ -1 +1 @@
Subproject commit a6e30e41f0c52f9d36c41eb79ac69b50020a6ac9
Subproject commit ac6ae2d76cb7463243ade44e6aa75a552e82e5c9

View File

@ -44,53 +44,76 @@
extern "C" {
#endif
//输出日志到shell
// 输出日志到shell [AUTO-TRANSLATED:6523242b]
// cpp
// Output log to shell
#define LOG_CONSOLE (1 << 0)
//输出日志到文件
// 输出日志到文件 [AUTO-TRANSLATED:8ffaf1e0]
// Output log to file
#define LOG_FILE (1 << 1)
//输出日志到回调函数(mk_events::on_mk_log)
// 输出日志到回调函数(mk_events::on_mk_log) [AUTO-TRANSLATED:616561c1]
// Output log to callback function (mk_events::on_mk_log)
#define LOG_CALLBACK (1 << 2)
//向下兼容
// 向下兼容 [AUTO-TRANSLATED:5b800712]
// Downward compatibility
#define mk_env_init1 mk_env_init2
//回调user_data回调函数
// 回调user_data回调函数 [AUTO-TRANSLATED:ced626fb]
// Callback user_data callback function
typedef void(API_CALL *on_user_data_free)(void *user_data);
typedef struct {
// 线程数
// 线程数 [AUTO-TRANSLATED:f7fc7650]
// Number of threads
int thread_num;
// 日志级别,支持0~4
// 日志级别,支持0~4 [AUTO-TRANSLATED:f4d77bb5]
// Log level, supports 0~4
int log_level;
//控制日志输出的掩模请查看LOG_CONSOLE、LOG_FILE、LOG_CALLBACK等宏
// 控制日志输出的掩模请查看LOG_CONSOLE、LOG_FILE、LOG_CALLBACK等宏 [AUTO-TRANSLATED:71de1d10]
// Control the mask of log output, please refer to LOG_CONSOLE, LOG_FILE, LOG_CALLBACK macros
int log_mask;
//文件日志保存路径,路径可以不存在(内部可以创建文件夹)设置为NULL关闭日志输出至文件
// 文件日志保存路径,路径可以不存在(内部可以创建文件夹)设置为NULL关闭日志输出至文件 [AUTO-TRANSLATED:d0989d3c]
// File log save path, the path can be non-existent (folders can be created internally), set to NULL to disable log output to file
const char *log_file_path;
//文件日志保存天数,设置为0关闭日志文件
// 文件日志保存天数,设置为0关闭日志文件 [AUTO-TRANSLATED:04253cb0]
// File log save days, set to 0 to disable log file
int log_file_days;
// 配置文件是内容还是路径
// 配置文件是内容还是路径 [AUTO-TRANSLATED:b946f030]
// Is the configuration file content or path
int ini_is_path;
// 配置文件内容或路径可以为NULL,如果该文件不存在,那么将导出默认配置至该文件
// 配置文件内容或路径可以为NULL,如果该文件不存在,那么将导出默认配置至该文件 [AUTO-TRANSLATED:aeaa4583]
// Configuration file content or path, can be NULL, if the file does not exist, then the default configuration will be exported to the file
const char *ini;
// ssl证书是内容还是路径
// ssl证书是内容还是路径 [AUTO-TRANSLATED:820671ab]
// Is the ssl certificate content or path
int ssl_is_path;
// ssl证书内容或路径可以为NULL
// ssl证书内容或路径可以为NULL [AUTO-TRANSLATED:c32fffb6]
// ssl certificate content or path, can be NULL
const char *ssl;
// 证书密码可以为NULL
// 证书密码可以为NULL [AUTO-TRANSLATED:b8c9c173]
// Certificate password, can be NULL
const char *ssl_pwd;
} mk_config;
/**
*
* @param cfg
* Initialize the environment, you need to call this function before calling this library
* @param cfg Library running related parameters
* [AUTO-TRANSLATED:58d6d220]
*/
API_EXPORT void API_CALL mk_env_init(const mk_config *cfg);
/**
* main函数退出时调用
* Close all servers, please call this function when exiting the main function
* [AUTO-TRANSLATED:f1148928]
*/
API_EXPORT void API_CALL mk_stop_all_server();
@ -106,6 +129,19 @@ API_EXPORT void API_CALL mk_stop_all_server();
* @param ssl_is_path ssl证书是内容还是路径
* @param ssl ssl证书内容或路径NULL
* @param ssl_pwd NULL
* mk_env_init version of basic type parameters, for easy calling by other languages
* @param thread_num Number of threads
* @param log_level Log level, supports 0~4
* @param log_mask Log output mode mask, please refer to LOG_CONSOLE, LOG_FILE, LOG_CALLBACK macros
* @param log_file_path File log save path, the path can be non-existent (folders can be created internally), set to NULL to disable log output to file
* @param log_file_days File log save days, set to 0 to disable log file
* @param ini_is_path Is the configuration file content or path
* @param ini Configuration file content or path, can be NULL, if the file does not exist, then the default configuration will be exported to the file
* @param ssl_is_path Is the ssl certificate content or path
* @param ssl ssl certificate content or path, can be NULL
* @param ssl_pwd Certificate password, can be NULL
* [AUTO-TRANSLATED:12901102]
*/
API_EXPORT void API_CALL mk_env_init2(int thread_num,
int log_level,
@ -122,6 +158,11 @@ API_EXPORT void API_CALL mk_env_init2(int thread_num,
*
* @param file_max_size (MB)
* @param file_max_count
* Set the log file
* @param file_max_size Single slice file size (MB)
* @param file_max_count Number of slice files
* [AUTO-TRANSLATED:59204140]
*/
API_EXPORT void API_CALL mk_set_log(int file_max_size, int file_max_count);
@ -130,6 +171,12 @@ API_EXPORT void API_CALL mk_set_log(int file_max_size, int file_max_count);
* @deprecated 使mk_ini_set_option替代
* @param key
* @param val
* Set the configuration item
* @deprecated Please use mk_ini_set_option instead
* @param key Configuration item name
* @param val Configuration item value
* [AUTO-TRANSLATED:93d02c07]
*/
API_EXPORT void API_CALL mk_set_option(const char *key, const char *val);
@ -137,6 +184,11 @@ API_EXPORT void API_CALL mk_set_option(const char *key, const char *val);
*
* @deprecated 使mk_ini_get_option替代
* @param key
* Get the value of the configuration item
* @deprecated Please use mk_ini_get_option instead
* @param key Configuration item name
* [AUTO-TRANSLATED:6222a231]
*/
API_EXPORT const char * API_CALL mk_get_option(const char *key);
@ -146,6 +198,12 @@ API_EXPORT const char * API_CALL mk_get_option(const char *key);
* @param port htt监听端口800
* @param ssl ssl类型服务器
* @return 0:,0:
* Create http[s] server
* @param port htt listening port, recommended 80, pass in 0 to randomly allocate
* @param ssl Whether it is an ssl type server
* @return 0: failure, non-0: port number
* [AUTO-TRANSLATED:4ca78101]
*/
API_EXPORT uint16_t API_CALL mk_http_server_start(uint16_t port, int ssl);
@ -154,6 +212,12 @@ API_EXPORT uint16_t API_CALL mk_http_server_start(uint16_t port, int ssl);
* @param port rtsp监听端口5540
* @param ssl ssl类型服务器
* @return 0:,0:
* Create rtsp[s] server
* @param port rtsp listening port, recommended 554, pass in 0 to randomly allocate
* @param ssl Whether it is an ssl type server
* @return 0: failure, non-0: port number
* [AUTO-TRANSLATED:3d984d90]
*/
API_EXPORT uint16_t API_CALL mk_rtsp_server_start(uint16_t port, int ssl);
@ -162,6 +226,12 @@ API_EXPORT uint16_t API_CALL mk_rtsp_server_start(uint16_t port, int ssl);
* @param port rtmp监听端口19350
* @param ssl ssl类型服务器
* @return 0:,0:
* Create rtmp[s] server
* @param port rtmp listening port, recommended 1935, pass in 0 to randomly allocate
* @param ssl Whether it is an ssl type server
* @return 0: failure, non-0: port number
* [AUTO-TRANSLATED:ed841271]
*/
API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl);
@ -169,6 +239,11 @@ API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl);
* rtp服务器
* @param port rtp监听端口(udp/tcp)
* @return 0:,0:
* Create rtp server
* @param port rtp listening port (including udp/tcp)
* @return 0: failure, non-0: port number
* [AUTO-TRANSLATED:f49af495]
*/
API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port);
@ -176,10 +251,16 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port);
* rtc服务器
* @param port rtc监听端口
* @return 0:,0:
* Create rtc server
* @param port rtc listening port
* @return 0: failure, non-0: port number
* [AUTO-TRANSLATED:df151854]
*/
API_EXPORT uint16_t API_CALL mk_rtc_server_start(uint16_t port);
//获取webrtc answer sdp回调函数
// 获取webrtc answer sdp回调函数 [AUTO-TRANSLATED:10c93fa9]
// Get webrtc answer sdp callback function
typedef void(API_CALL *on_mk_webrtc_get_answer_sdp)(void *user_data, const char *answer, const char *err);
/**
@ -189,6 +270,14 @@ typedef void(API_CALL *on_mk_webrtc_get_answer_sdp)(void *user_data, const char
* @param type webrtc插件类型echo,play,push
* @param offer webrtc offer sdp
* @param url rtc url, rtc://__defaultVhost/app/stream?key1=val1&key2=val2
* webrtc exchange sdp, generate answer sdp based on offer sdp
* @param user_data Callback user pointer
* @param cb Callback function
* @param type webrtc plugin type, supports echo, play, push
* @param offer webrtc offer sdp
* @param url rtc url, for example rtc://__defaultVhost/app/stream?key1=val1&key2=val2
* [AUTO-TRANSLATED:ea79659b]
*/
API_EXPORT void API_CALL mk_webrtc_get_answer_sdp(void *user_data, on_mk_webrtc_get_answer_sdp cb, const char *type,
const char *offer, const char *url);
@ -200,6 +289,11 @@ API_EXPORT void API_CALL mk_webrtc_get_answer_sdp2(void *user_data, on_user_data
* srt服务器
* @param port srt监听端口
* @return 0:,0:
* Create srt server
* @param port srt listening port
* @return 0: failure, non-0: port number
* [AUTO-TRANSLATED:250984c0]
*/
API_EXPORT uint16_t API_CALL mk_srt_server_start(uint16_t port);
@ -208,6 +302,12 @@ API_EXPORT uint16_t API_CALL mk_srt_server_start(uint16_t port);
* shell服务器
* @param port shell监听端口
* @return 0:,0:
* Create shell server
* @param port shell listening port
* @return 0: failure, non-0: port number
* [AUTO-TRANSLATED:66ec9a2a]
*/
API_EXPORT uint16_t API_CALL mk_shell_server_start(uint16_t port);

View File

@ -23,6 +23,11 @@ typedef struct {
* MediaSource事件广播
* @param regist 10
* @param sender MediaSource对象
* Register or unregister MediaSource event broadcast
* @param regist Register as 1, unregister as 0
* @param sender The MediaSource object
* [AUTO-TRANSLATED:d440a47c]
*/
void (API_CALL *on_mk_media_changed)(int regist,
const mk_media_source sender);
@ -33,6 +38,13 @@ typedef struct {
* @param url_info url相关信息
* @param invoker invoker返回鉴权结果
* @param sender tcp客户端相关信息
* Receive rtsp/rtmp push stream event broadcast, control push stream authentication through this event
* @see mk_publish_auth_invoker_do
* @param url_info Push stream url related information
* @param invoker Execute invoker to return authentication result
* @param sender The tcp client related information
* [AUTO-TRANSLATED:2a607577]
*/
void (API_CALL *on_mk_media_publish)(const mk_media_info url_info,
const mk_publish_auth_invoker invoker,
@ -44,6 +56,13 @@ typedef struct {
* @param url_info url相关信息
* @param invoker invoker返回鉴权结果
* @param sender
* Play rtsp/rtmp/http-flv/hls event broadcast, control playback authentication through this event
* @see mk_auth_invoker_do
* @param url_info Play url related information
* @param invoker Execute invoker to return authentication result
* @param sender Play client related information
* [AUTO-TRANSLATED:817c964d]
*/
void (API_CALL *on_mk_media_play)(const mk_media_info url_info,
const mk_auth_invoker invoker,
@ -55,6 +74,13 @@ typedef struct {
* @param sender
* @return 1
* 0
* This event will be broadcast after the stream is not found. Please pull the stream or other methods to generate the stream after listening to this event, so that you can pull the stream on demand.
* @param url_info Play url related information
* @param sender Play client related information
* @return 1 Close directly
* 0 Wait for stream registration
* [AUTO-TRANSLATED:468e7356]
*/
int (API_CALL *on_mk_media_not_found)(const mk_media_info url_info,
const mk_sock_info sender);
@ -62,6 +88,10 @@ typedef struct {
/**
*
* @param sender MediaSource对象
* Triggered when a stream is not consumed by anyone, the purpose is to achieve business logic such as actively disconnecting the pull stream when no one is watching
* @param sender The MediaSource object
* [AUTO-TRANSLATED:348078cb]
*/
void (API_CALL *on_mk_media_no_reader)(const mk_media_source sender);
@ -71,6 +101,13 @@ typedef struct {
* @param invoker invoker返回http回复
* @param consumed 1
* @param sender http客户端相关信息
* Receive http api request broadcast (including GET/POST)
* @param parser Http request content object
* @param invoker Execute this invoker to return http reply
* @param consumed Set to 1 if we want to handle this event
* @param sender Http client related information
* [AUTO-TRANSLATED:eb15bc74]
*/
void (API_CALL *on_mk_http_request)(const mk_parser parser,
const mk_http_response_invoker invoker,
@ -84,6 +121,14 @@ typedef struct {
* @param is_dir path是否为文件夹
* @param invoker invoker返回本次访问文件的结果
* @param sender http客户端相关信息
* In the http file server, receive the broadcast of http access to files or directories, control the access permission of the http directory through this event
* @param parser Http request content object
* @param path File absolute path
* @param is_dir Whether path is a folder
* @param invoker Execute invoker to return the result of accessing the file this time
* @param sender Http client related information
* [AUTO-TRANSLATED:c49b1702]
*/
void (API_CALL *on_mk_http_access)(const mk_parser parser,
const char *path,
@ -97,6 +142,13 @@ typedef struct {
* @param parser http请求内容对象
* @param path ,
* @param sender http客户端相关信息
* In the http file server, receive the broadcast before http access to files or directories, you can control the mapping of http url to file path through this event
* By overriding the path parameter in this event, you can achieve the purpose of selecting different http root directories according to virtual hosts or apps
* @param parser Http request content object
* @param path File absolute path, overriding it can redirect to other files
* @param sender Http client related information
* [AUTO-TRANSLATED:8b167279]
*/
void (API_CALL *on_mk_http_before_access)(const mk_parser parser,
char *path,
@ -107,6 +159,12 @@ typedef struct {
* @param url_info rtsp url相关信息
* @param invoker invoker返回是否需要rtsp专属认证
* @param sender rtsp客户端相关信息
* Does this rtsp stream need authentication? If so, call invoker and pass in realm, otherwise pass in empty realm
* @param url_info Request rtsp url related information
* @param invoker Execute invoker to return whether rtsp exclusive authentication is required
* @param sender Rtsp client related information
* [AUTO-TRANSLATED:9bd81de9]
*/
void (API_CALL *on_mk_rtsp_get_realm)(const mk_media_info url_info,
const mk_rtsp_get_realm_invoker invoker,
@ -121,6 +179,16 @@ typedef struct {
* @param must_no_encrypt true(base64认证方式),
* @param invoker invoker返回rtsp专属认证的密码
* @param sender rtsp客户端信息
* Request authentication user password event, user_name is the username, must_no_encrypt if true, then you must provide plain text password (because it is base64 authentication method at this time), otherwise it will lead to authentication failure
* After getting the password, please call invoker and input the corresponding type of password and password type, invoker will match the password when executing
* @param url_info Request rtsp url related information
* @param realm Rtsp authentication realm
* @param user_name Rtsp authentication username
* @param must_no_encrypt If true, then you must provide plain text password (because it is base64 authentication method at this time), otherwise it will lead to authentication failure
* @param invoker Execute invoker to return the password of rtsp exclusive authentication
* @param sender Rtsp client information
* [AUTO-TRANSLATED:833da340]
*/
void (API_CALL *on_mk_rtsp_auth)(const mk_media_info url_info,
const char *realm,
@ -131,16 +199,25 @@ typedef struct {
/**
* mp4分片文件成功后广播
* Broadcast after recording mp4 fragment file successfully
* [AUTO-TRANSLATED:eef1d414]
*/
void (API_CALL *on_mk_record_mp4)(const mk_record_info mp4);
/**
* ts分片文件成功后广播
* Broadcast after recording ts fragment file successfully
* [AUTO-TRANSLATED:b91dc9fa]
*/
void (API_CALL *on_mk_record_ts)(const mk_record_info ts);
/**
* shell登录鉴权
* Shell login authentication
* [AUTO-TRANSLATED:95784c94]
*/
void (API_CALL *on_mk_shell_login)(const char *user_name,
const char *passwd,
@ -153,6 +230,13 @@ typedef struct {
* @param total_bytes
* @param total_seconds tcp会话时长
* @param is_player
* Stop rtsp/rtmp/http-flv session after traffic reporting event broadcast
* @param url_info Play url related information
* @param total_bytes Total traffic consumed up and down, unit bytes
* @param total_seconds The duration of this tcp session, unit seconds
* @param is_player Whether the client is a player
* [AUTO-TRANSLATED:d81d1fc3]
*/
void (API_CALL *on_mk_flow_report)(const mk_media_info url_info,
size_t total_bytes,
@ -168,6 +252,14 @@ typedef struct {
* @param line
* @param function
* @param message
* Log output broadcast
* @param level Log level
* @param file Source file name
* @param line Source file line
* @param function Source file function name
* @param message Log content
* [AUTO-TRANSLATED:5aa5cb8f]
*/
void (API_CALL *on_mk_log)(int level, const char *file, int line, const char *function, const char *message);
@ -179,12 +271,25 @@ typedef struct {
* @param ssrc ssrc的10进制打印atoi转换为整型
* @param err
* @param msg
* Send rtp stream failure callback, applicable to rtp sending triggered by mk_media_source_start_send_rtp/mk_media_start_send_rtp interface
* @param vhost Virtual host
* @param app Application name
* @param stream Stream id
* @param ssrc Ssrc's decimal print, convert to integer through atoi
* @param err Error code
* @param msg Error message
* [AUTO-TRANSLATED:c956e89b]
*/
void (API_CALL *on_mk_media_send_rtp_stop)(const char *vhost, const char *app, const char *stream, const char *ssrc, int err, const char *msg);
/**
* rtc sctp连接中///
* @param rtc_transport
* Rtc sctp connection in/complete/failure/close callback
* @param rtc_transport Data channel object
* [AUTO-TRANSLATED:5455fb76]
*/
void (API_CALL *on_mk_rtc_sctp_connecting)(mk_rtc_transport rtc_transport);
void (API_CALL *on_mk_rtc_sctp_connected)(mk_rtc_transport rtc_transport);
@ -196,6 +301,12 @@ typedef struct {
* @param rtc_transport
* @param msg
* @param len
* Rtc data channel send data callback
* @param rtc_transport Data channel object
* @param msg Data
* @param len Data length
* [AUTO-TRANSLATED:42f75e55]
*/
void (API_CALL *on_mk_rtc_sctp_send)(mk_rtc_transport rtc_transport, const uint8_t *msg, size_t len);
@ -206,6 +317,14 @@ typedef struct {
* @param ppid id
* @param msg
* @param len
* Rtc data channel receive data callback
* @param rtc_transport Data channel object
* @param streamId Stream id
* @param ppid Protocol id
* @param msg Data
* @param len Data length
* [AUTO-TRANSLATED:3abda838]
*/
void (API_CALL *on_mk_rtc_sctp_received)(mk_rtc_transport rtc_transport, uint16_t streamId, uint32_t ppid, const uint8_t *msg, size_t len);
@ -215,6 +334,10 @@ typedef struct {
/**
* ZLMediaKit里面的事件
* @param events ,null以便取消监听
* Listen to events in ZLMediaKit
* @param events The structure of each event, this object will be copied again internally, it can be set to null to cancel listening
* [AUTO-TRANSLATED:d3418bc6]
*/
API_EXPORT void API_CALL mk_events_listen(const mk_events *events);

View File

@ -19,30 +19,42 @@ extern "C" {
#endif
///////////////////////////////////////////RecordInfo/////////////////////////////////////////////
//RecordInfo对象的C映射
// RecordInfo对象的C映射 [AUTO-TRANSLATED:2c7825a6]
// RecordInfo object's C mapping
typedef struct mk_record_info_t *mk_record_info;
// GMT 标准时间,单位秒
// GMT 标准时间,单位秒 [AUTO-TRANSLATED:3b827274]
// GMT standard time, unit is seconds
API_EXPORT uint64_t API_CALL mk_record_info_get_start_time(const mk_record_info ctx);
// 录像长度,单位秒
// 录像长度,单位秒 [AUTO-TRANSLATED:1dceac0c]
// Recording length, unit is seconds
API_EXPORT float API_CALL mk_record_info_get_time_len(const mk_record_info ctx);
// 文件大小,单位 BYTE
// 文件大小,单位 BYTE [AUTO-TRANSLATED:add20c50]
// File size, unit is BYTE
API_EXPORT size_t API_CALL mk_record_info_get_file_size(const mk_record_info ctx);
// 文件路径
// 文件路径 [AUTO-TRANSLATED:c5246c5d]
// File path
API_EXPORT const char *API_CALL mk_record_info_get_file_path(const mk_record_info ctx);
// 文件名称
// 文件名称 [AUTO-TRANSLATED:b5d7c753]
// File name
API_EXPORT const char *API_CALL mk_record_info_get_file_name(const mk_record_info ctx);
// 文件夹路径
// 文件夹路径 [AUTO-TRANSLATED:0e5c9d26]
// Folder path
API_EXPORT const char *API_CALL mk_record_info_get_folder(const mk_record_info ctx);
// 播放路径
// 播放路径 [AUTO-TRANSLATED:9b5c99f8]
// Playback path
API_EXPORT const char *API_CALL mk_record_info_get_url(const mk_record_info ctx);
// 应用名称
// 应用名称 [AUTO-TRANSLATED:2aa47ea2]
// Application name
API_EXPORT const char *API_CALL mk_record_info_get_vhost(const mk_record_info ctx);
// 流 ID
// 流 ID [AUTO-TRANSLATED:4bbe1cbe]
// Stream ID
API_EXPORT const char *API_CALL mk_record_info_get_app(const mk_record_info ctx);
// 虚拟主机
// 虚拟主机 [AUTO-TRANSLATED:aaae9cfe]
// Virtual host
API_EXPORT const char *API_CALL mk_record_info_get_stream(const mk_record_info ctx);
//// 下面宏保障用户代码兼容性, 二进制abi不兼容用户需要重新编译链接 /////
// // 下面宏保障用户代码兼容性, 二进制abi不兼容用户需要重新编译链接 ///// [AUTO-TRANSLATED:e532a596]
// // The following macros ensure user code compatibility, binary abi is incompatible, users need to recompile and link /////
#define mk_mp4_info mk_record_info
#define mk_mp4_info_get_start_time mk_record_info_get_start_time
#define mk_mp4_info_get_time_len mk_record_info_get_time_len
@ -56,29 +68,40 @@ API_EXPORT const char *API_CALL mk_record_info_get_stream(const mk_record_info c
#define mk_mp4_info_get_stream mk_record_info_get_stream
///////////////////////////////////////////Parser/////////////////////////////////////////////
//Parser对象的C映射
// Parser对象的C映射 [AUTO-TRANSLATED:6ceb91d6]
// Parser object's C mapping
typedef struct mk_parser_t *mk_parser;
//Parser对象中Headers foreach回调
// Parser对象中Headers foreach回调 [AUTO-TRANSLATED:4e0a6646]
// Parser object's Headers foreach callback
typedef void(API_CALL *on_mk_parser_header_cb)(void *user_data, const char *key, const char *val);
//Parser::Method(),获取命令字譬如GET/POST
// Parser::Method(),获取命令字譬如GET/POST [AUTO-TRANSLATED:904ebe57]
// Parser::Method(), get the command word, such as GET/POST
API_EXPORT const char* API_CALL mk_parser_get_method(const mk_parser ctx);
//Parser::Url(),获取HTTP的访问url(不包括?后面的参数)
// Parser::Url(),获取HTTP的访问url(不包括?后面的参数) [AUTO-TRANSLATED:75f320c5]
// Parser::Url(), get the HTTP access url (excluding the parameters after ?)
API_EXPORT const char* API_CALL mk_parser_get_url(const mk_parser ctx);
//Parser::Params(),?后面的参数字符串
// Parser::Params(),?后面的参数字符串 [AUTO-TRANSLATED:3745fec0]
// Parser::Params(), the parameter string after ?
API_EXPORT const char* API_CALL mk_parser_get_url_params(const mk_parser ctx);
//Parser::getUrlArgs()["key"],获取?后面的参数中的特定参数
// Parser::getUrlArgs()["key"],获取?后面的参数中的特定参数 [AUTO-TRANSLATED:425e4b61]
// Parser::getUrlArgs()["key"], get the specific parameter in the parameters after ?
API_EXPORT const char* API_CALL mk_parser_get_url_param(const mk_parser ctx,const char *key);
//Parser::Tail(),获取协议相关信息,譬如 HTTP/1.1
// Parser::Tail(),获取协议相关信息,譬如 HTTP/1.1 [AUTO-TRANSLATED:786534b6]
// Parser::Tail(), get protocol related information, such as HTTP/1.1
API_EXPORT const char* API_CALL mk_parser_get_tail(const mk_parser ctx);
//Parser::getValues()["key"],获取HTTP头中特定字段
// Parser::getValues()["key"],获取HTTP头中特定字段 [AUTO-TRANSLATED:1c210637]
// Parser::getValues()["key"], get the specific field in the HTTP header
API_EXPORT const char* API_CALL mk_parser_get_header(const mk_parser ctx,const char *key);
//Parser::Content(),获取HTTP body
// Parser::Content(),获取HTTP body [AUTO-TRANSLATED:fb05b253]
// Parser::Content(), get the HTTP body
API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, size_t *length);
//循环获取所有header
// 循环获取所有header [AUTO-TRANSLATED:9fd7571a]
// Loop to get all headers
API_EXPORT void API_CALL mk_parser_headers_for_each(const mk_parser ctx, on_mk_parser_header_cb cb, void *user_data);
///////////////////////////////////////////MediaInfo/////////////////////////////////////////////
//MediaInfo对象的C映射
// MediaInfo对象的C映射 [AUTO-TRANSLATED:f9649086]
// MediaInfo object's C mapping
typedef struct mk_media_info_t *mk_media_info;
//MediaInfo::param_strs
API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx);
@ -97,9 +120,11 @@ API_EXPORT uint16_t API_CALL mk_media_info_get_port(const mk_media_info ctx);
///////////////////////////////////////////MediaSource/////////////////////////////////////////////
//MediaSource对象的C映射
// MediaSource对象的C映射 [AUTO-TRANSLATED:feb50a09]
// MediaSource object's C mapping
typedef struct mk_media_source_t *mk_media_source;
//查找MediaSource的回调函数
// 查找MediaSource的回调函数 [AUTO-TRANSLATED:e8b54cf9]
// Callback function to find MediaSource
typedef void(API_CALL *on_mk_media_source_find_cb)(void *user_data, const mk_media_source ctx);
//MediaSource::getSchema()
@ -126,7 +151,8 @@ API_EXPORT int API_CALL mk_media_source_broadcast_msg(const mk_media_source ctx,
API_EXPORT const char* API_CALL mk_media_source_get_origin_url(const mk_media_source ctx);
// MediaSource::getOriginType()
API_EXPORT int API_CALL mk_media_source_get_origin_type(const mk_media_source ctx);
// MediaSource::getOriginTypeStr(), 使用后请用mk_free释放返回值
// MediaSource::getOriginTypeStr(), 使用后请用mk_free释放返回值 [AUTO-TRANSLATED:d612ec22]
// MediaSource::getOriginTypeStr(), please use mk_free to release the return value after use
API_EXPORT const char *API_CALL mk_media_source_get_origin_type_str(const mk_media_source ctx);
// MediaSource::getCreateStamp()
API_EXPORT uint64_t API_CALL mk_media_source_get_create_stamp(const mk_media_source ctx);
@ -149,6 +175,20 @@ API_EXPORT uint64_t API_CALL mk_media_source_get_alive_second(const mk_media_sou
* @param ctx
* @param force
* @return 01
* Live sources are called MediaSource in ZLMediaKit,
* Currently, there are 3 types, namely RtmpMediaSource, RtspMediaSource, HlsMediaSource
* The source is generated in both passive and active ways:
* Passive ways are rtsp/rtmp/rtp push stream, mp4 on-demand
* Active ways include objects created by mk_media_create (DevChannel), objects created by mk_proxy_player_create (PlayerProxy)
* You don't need to do anything for passive ways, ZLMediaKit has already adapted the MediaSource::close() event by default, which will close the live stream
* For active ways, you need to set the callback of this event, you need to choose to delete the object yourself
* You can set the callback through the mk_proxy_player_set_on_close and mk_media_set_on_close functions,
* Please delete the object in the callback to complete the media closure, otherwise why call the mk_media_source_close function?
* @param ctx object
* @param force Whether to force closure, if forced closure, it will be closed even if someone is watching
* @return 0 means failure, 1 means success
* [AUTO-TRANSLATED:9415a405]
*/
API_EXPORT int API_CALL mk_media_source_close(const mk_media_source ctx,int force);
//MediaSource::seekTo()
@ -156,13 +196,20 @@ API_EXPORT int API_CALL mk_media_source_seek_to(const mk_media_source ctx,uint32
/**
* rtp推流成功与否的回调()
* Callback for whether rtp push stream is successful or not (after the first success, it will keep retrying)
* [AUTO-TRANSLATED:7e00f7fb]
*/
typedef void(API_CALL *on_mk_media_source_send_rtp_result)(void *user_data, uint16_t local_port, int err, const char *msg);
// MediaSource::startSendRtp,请参考mk_media_start_send_rtp,注意ctx参数类型不一样
// MediaSource::startSendRtp,请参考mk_media_start_send_rtp,注意ctx参数类型不一样 [AUTO-TRANSLATED:515ab2e3]
// MediaSource::startSendRtp, please refer to mk_media_start_send_rtp, note that the ctx parameter type is different
API_EXPORT void API_CALL mk_media_source_start_send_rtp(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_source_send_rtp_result cb, void *user_data);
API_EXPORT void API_CALL mk_media_source_start_send_rtp2(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_source_send_rtp_result cb, void *user_data, on_user_data_free user_data_free);
//MediaSource::stopSendRtp请参考mk_media_stop_send_rtp,注意ctx参数类型不一样
API_EXPORT void API_CALL mk_media_source_start_send_rtp3(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, mk_ini options, on_mk_media_source_send_rtp_result cb, void *user_data);
API_EXPORT void API_CALL mk_media_source_start_send_rtp4(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, mk_ini options, on_mk_media_source_send_rtp_result cb,void *user_data, on_user_data_free user_data_free);
// MediaSource::stopSendRtp请参考mk_media_stop_send_rtp,注意ctx参数类型不一样 [AUTO-TRANSLATED:415fe454]
// MediaSource::stopSendRtp, please refer to mk_media_stop_send_rtp, note that the ctx parameter type is different
API_EXPORT int API_CALL mk_media_source_stop_send_rtp(const mk_media_source ctx);
//MediaSource::find()
@ -184,18 +231,29 @@ API_EXPORT void API_CALL mk_media_source_for_each(void *user_data, on_mk_media_s
const char *vhost, const char *app, const char *stream);
///////////////////////////////////////////HttpBody/////////////////////////////////////////////
// HttpBody对象的C映射 [AUTO-TRANSLATED:d8025ad9]
// cpp
//HttpBody对象的C映射
typedef struct mk_http_body_t *mk_http_body;
/**
* HttpStringBody
* @param str
* @param len 0strlen获取
* Generate HttpStringBody
* @param str String pointer
* @param len String length, if it is 0, use strlen to get it
* [AUTO-TRANSLATED:7f828392]
*/
API_EXPORT mk_http_body API_CALL mk_http_body_from_string(const char *str,size_t len);
/**
* HttpBufferBody
* @param buffer mk_buffer对象
* Generate HttpBufferBody
* @param buffer mk_buffer object
* [AUTO-TRANSLATED:2d31a2e4]
*/
API_EXPORT mk_http_body API_CALL mk_http_body_from_buffer(mk_buffer buffer);
@ -203,6 +261,10 @@ API_EXPORT mk_http_body API_CALL mk_http_body_from_buffer(mk_buffer buffer);
/**
* HttpFileBody
* @param file_path
* Generate HttpFileBody
* @param file_path File full path
* [AUTO-TRANSLATED:4823ab7f]
*/
API_EXPORT mk_http_body API_CALL mk_http_body_from_file(const char *file_path);
@ -210,16 +272,25 @@ API_EXPORT mk_http_body API_CALL mk_http_body_from_file(const char *file_path);
* HttpMultiFormBody
* @param key_val key-value
* @param file_path
* Generate HttpMultiFormBody
* @param key_val Parameter key-value
* @param file_path File full path
* [AUTO-TRANSLATED:17976911]
*/
API_EXPORT mk_http_body API_CALL mk_http_body_from_multi_form(const char *key_val[],const char *file_path);
/**
* HttpBody
* Destroy HttpBody
* [AUTO-TRANSLATED:a1169b76]
*/
API_EXPORT void API_CALL mk_http_body_release(mk_http_body ctx);
///////////////////////////////////////////HttpResponseInvoker/////////////////////////////////////////////
//HttpSession::HttpResponseInvoker对象的C映射
// HttpSession::HttpResponseInvoker对象的C映射 [AUTO-TRANSLATED:89287e03]
// HttpSession::HttpResponseInvoker对象的C映射
typedef struct mk_http_response_invoker_t *mk_http_response_invoker;
/**
@ -227,6 +298,12 @@ typedef struct mk_http_response_invoker_t *mk_http_response_invoker;
* @param response_code 200
* @param response_header http头 {"Content-Type","text/html",NULL} NULL结尾
* @param response_body body对象
* HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body);
* @param response_code For example 200
* @param response_header The returned http header, for example {"Content-Type","text/html",NULL} must end with NULL
* @param response_body Body object
* [AUTO-TRANSLATED:e006685a]
*/
API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invoker ctx,
int response_code,
@ -238,6 +315,12 @@ API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invo
* @param response_code 200
* @param response_header http头 {"Content-Type","text/html",NULL} NULL结尾
* @param response_content content部分
* HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const string &body);
* @param response_code For example 200
* @param response_header The returned http header, for example {"Content-Type","text/html",NULL} must end with NULL
* @param response_content The returned content part, for example a web page content
* [AUTO-TRANSLATED:0c3cf577]
*/
API_EXPORT void API_CALL mk_http_response_invoker_do_string(const mk_http_response_invoker ctx,
int response_code,
@ -248,6 +331,12 @@ API_EXPORT void API_CALL mk_http_response_invoker_do_string(const mk_http_respon
* @param request_parser mk_parser对象http头中的Range字段fseek然后再发送文件部分片段
* @param response_header http头 {"Content-Type","text/html",NULL} NULL结尾
* @param response_file_path content部分/path/to/html/file
* HttpSession::HttpResponseInvoker(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const string &filePath);
* @param request_parser The mk_parser object in the request event, used to extract the Range field in the http header, use this field to fseek first and then send the file part fragment
* @param response_header The returned http header, for example {"Content-Type","text/html",NULL} must end with NULL
* @param response_file_path The returned content part, for example /path/to/html/file
* [AUTO-TRANSLATED:8ed9ed9e]
*/
API_EXPORT void API_CALL mk_http_response_invoker_do_file(const mk_http_response_invoker ctx,
const mk_parser request_parser,
@ -256,16 +345,24 @@ API_EXPORT void API_CALL mk_http_response_invoker_do_file(const mk_http_response
/**
* mk_http_response_invoker对象线mk_http_response_invoker_do
* mk_http_response_invoker_do
* Clone the mk_http_response_invoker object, by cloning the object to a heap object, you can achieve cross-thread asynchronous execution of mk_http_response_invoker_do
* If you execute mk_http_response_invoker_do synchronously, then there is no need to clone the object
* [AUTO-TRANSLATED:54c98395]
*/
API_EXPORT mk_http_response_invoker API_CALL mk_http_response_invoker_clone(const mk_http_response_invoker ctx);
/**
*
* Destroy the cloned object on the heap
* [AUTO-TRANSLATED:16c6a29b]
*/
API_EXPORT void API_CALL mk_http_response_invoker_clone_release(const mk_http_response_invoker ctx);
///////////////////////////////////////////HttpAccessPathInvoker/////////////////////////////////////////////
//HttpSession::HttpAccessPathInvoker对象的C映射
// HttpSession::HttpAccessPathInvoker对象的C映射 [AUTO-TRANSLATED:beb105f4]
// HttpSession::HttpAccessPathInvoker对象的C映射
typedef struct mk_http_access_path_invoker_t *mk_http_access_path_invoker;
/**
@ -273,6 +370,12 @@ typedef struct mk_http_access_path_invoker_t *mk_http_access_path_invoker;
* @param err_msg ,null
* @param access_path 访,null
* @param cookie_life_second cookie有效期
* HttpSession::HttpAccessPathInvoker(const string &errMsg,const string &accessPath, int cookieLifeSecond);
* @param err_msg If it is empty, it means that the authentication is passed, otherwise it is an error prompt, it can be null
* @param access_path The root directory to run or prohibit access, it can be null
* @param cookie_life_second Authentication cookie validity period
*
* [AUTO-TRANSLATED:105c76c4]
**/
API_EXPORT void API_CALL mk_http_access_path_invoker_do(const mk_http_access_path_invoker ctx,
const char *err_msg,
@ -282,20 +385,32 @@ API_EXPORT void API_CALL mk_http_access_path_invoker_do(const mk_http_access_pat
/**
* mk_http_access_path_invoker对象线mk_http_access_path_invoker_do
* mk_http_access_path_invoker_do
* Clone the mk_http_access_path_invoker object, by cloning the object to a heap object, you can achieve cross-thread asynchronous execution of mk_http_access_path_invoker_do
* If you execute mk_http_access_path_invoker_do synchronously, then there is no need to clone the object
* [AUTO-TRANSLATED:ad2a71e4]
*/
API_EXPORT mk_http_access_path_invoker API_CALL mk_http_access_path_invoker_clone(const mk_http_access_path_invoker ctx);
/**
*
* Destroy the cloned object on the heap
* [AUTO-TRANSLATED:16c6a29b]
*/
API_EXPORT void API_CALL mk_http_access_path_invoker_clone_release(const mk_http_access_path_invoker ctx);
///////////////////////////////////////////RtspSession::onGetRealm/////////////////////////////////////////////
//RtspSession::onGetRealm对象的C映射
// RtspSession::onGetRealm对象的C映射 [AUTO-TRANSLATED:2355d645]
// RtspSession::onGetRealm对象的C映射
typedef struct mk_rtsp_get_realm_invoker_t *mk_rtsp_get_realm_invoker;
/**
* RtspSession::onGetRealm
* @param realm rtsp流是否需要开启rtsp专属鉴权null或空字符串则不鉴权
* Execute RtspSession::onGetRealm
* @param realm Whether this rtsp stream needs to enable rtsp exclusive authentication, to null or empty string does not authenticate
* [AUTO-TRANSLATED:ed88a88b]
*/
API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_do(const mk_rtsp_get_realm_invoker ctx,
const char *realm);
@ -303,22 +418,35 @@ API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_do(const mk_rtsp_get_realm_in
/**
* mk_rtsp_get_realm_invoker对象线mk_rtsp_get_realm_invoker_do
* mk_rtsp_get_realm_invoker_do
* Clone the mk_rtsp_get_realm_invoker object, by cloning the object to a heap object, you can achieve cross-thread asynchronous execution of mk_rtsp_get_realm_invoker_do
* If you execute mk_rtsp_get_realm_invoker_do synchronously, then there is no need to clone the object
* [AUTO-TRANSLATED:15fa6e77]
*/
API_EXPORT mk_rtsp_get_realm_invoker API_CALL mk_rtsp_get_realm_invoker_clone(const mk_rtsp_get_realm_invoker ctx);
/**
*
* Destroy the cloned object on the heap
* [AUTO-TRANSLATED:16c6a29b]
*/
API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_clone_release(const mk_rtsp_get_realm_invoker ctx);
///////////////////////////////////////////RtspSession::onAuth/////////////////////////////////////////////
//RtspSession::onAuth对象的C映射
// RtspSession::onAuth对象的C映射 [AUTO-TRANSLATED:f3a1ebb7]
// RtspSession::onAuth对象的C映射
typedef struct mk_rtsp_auth_invoker_t *mk_rtsp_auth_invoker;
/**
* RtspSession::onAuth
* @param encrypted true是则表明是md5加密的密码, md5密码者则会导致认证失败
* @param pwd_or_md5 md5加密的密码
* Execute RtspSession::onAuth
* @param encrypted If true, it means that the password is md5 encrypted, otherwise it is plain text password, if you provide md5 password when requesting plain text password, it will cause authentication failure
* @param pwd_or_md5 Plain text password or md5 encrypted password
* [AUTO-TRANSLATED:f7152252]
*/
API_EXPORT void API_CALL mk_rtsp_auth_invoker_do(const mk_rtsp_auth_invoker ctx,
int encrypted,
@ -327,16 +455,24 @@ API_EXPORT void API_CALL mk_rtsp_auth_invoker_do(const mk_rtsp_auth_invoker ctx,
/**
* mk_rtsp_auth_invoker对象线mk_rtsp_auth_invoker_do
* mk_rtsp_auth_invoker_do
* Clone the mk_rtsp_auth_invoker object, by cloning the object to a heap object, you can achieve cross-thread asynchronous execution of mk_rtsp_auth_invoker_do
* If you execute mk_rtsp_auth_invoker_do synchronously, then there is no need to clone the object
* [AUTO-TRANSLATED:505859bd]
*/
API_EXPORT mk_rtsp_auth_invoker API_CALL mk_rtsp_auth_invoker_clone(const mk_rtsp_auth_invoker ctx);
/**
*
* Destroy the cloned object on the heap
* [AUTO-TRANSLATED:16c6a29b]
*/
API_EXPORT void API_CALL mk_rtsp_auth_invoker_clone_release(const mk_rtsp_auth_invoker ctx);
///////////////////////////////////////////Broadcast::PublishAuthInvoker/////////////////////////////////////////////
//Broadcast::PublishAuthInvoker对象的C映射
// Broadcast::PublishAuthInvoker对象的C映射 [AUTO-TRANSLATED:0eb37ee6]
// Broadcast::PublishAuthInvoker对象的C映射
typedef struct mk_publish_auth_invoker_t *mk_publish_auth_invoker;
/**
@ -344,6 +480,12 @@ typedef struct mk_publish_auth_invoker_t *mk_publish_auth_invoker;
* @param err_msg null则代表鉴权成功
* @param enable_hls hls
* @param enable_mp4 MP4录制
* Execute Broadcast::PublishAuthInvoker
* @param err_msg Empty or null means authentication success
* @param enable_hls Whether to allow hls conversion
* @param enable_mp4 Whether to allow MP4 recording
* [AUTO-TRANSLATED:ee8fb2b4]
*/
API_EXPORT void API_CALL mk_publish_auth_invoker_do(const mk_publish_auth_invoker ctx,
const char *err_msg,
@ -355,37 +497,58 @@ API_EXPORT void API_CALL mk_publish_auth_invoker_do2(const mk_publish_auth_invok
/**
* mk_publish_auth_invoker对象线mk_publish_auth_invoker_do
* mk_publish_auth_invoker_do
* Clone the mk_publish_auth_invoker object, by cloning the object to a heap object, you can achieve cross-thread asynchronous execution of mk_publish_auth_invoker_do
* If you execute mk_publish_auth_invoker_do synchronously, then there is no need to clone the object
* [AUTO-TRANSLATED:03357111]
*/
API_EXPORT mk_publish_auth_invoker API_CALL mk_publish_auth_invoker_clone(const mk_publish_auth_invoker ctx);
/**
*
* Destroy the cloned object on the heap
* [AUTO-TRANSLATED:16c6a29b]
*/
API_EXPORT void API_CALL mk_publish_auth_invoker_clone_release(const mk_publish_auth_invoker ctx);
///////////////////////////////////////////Broadcast::AuthInvoker/////////////////////////////////////////////
//Broadcast::AuthInvoker对象的C映射
// Broadcast::AuthInvoker对象的C映射 [AUTO-TRANSLATED:08f4186e]
// Broadcast::AuthInvoker对象的C映射
typedef struct mk_auth_invoker_t *mk_auth_invoker;
/**
* Broadcast::AuthInvoker
* @param err_msg null则代表鉴权成功
* Execute Broadcast::AuthInvoker
* @param err_msg Empty or null means authentication success
* [AUTO-TRANSLATED:7215fd9a]
*/
API_EXPORT void API_CALL mk_auth_invoker_do(const mk_auth_invoker ctx, const char *err_msg);
/**
* mk_auth_invoker对象线mk_auth_invoker_do
* mk_auth_invoker_do
* Clone the mk_auth_invoker object. By cloning the object to a heap object, we can achieve asynchronous execution of mk_auth_invoker_do across threads.
* If mk_auth_invoker_do is executed synchronously, there is no need to clone the object.
* [AUTO-TRANSLATED:2430560d]
*/
API_EXPORT mk_auth_invoker API_CALL mk_auth_invoker_clone(const mk_auth_invoker ctx);
/**
*
* Destroy the cloned object on the heap.
* [AUTO-TRANSLATED:16c6a29b]
*/
API_EXPORT void API_CALL mk_auth_invoker_clone_release(const mk_auth_invoker ctx);
///////////////////////////////////////////WebRtcTransport/////////////////////////////////////////////
//WebRtcTransport对象的C映射
// WebRtcTransport对象的C映射 [AUTO-TRANSLATED:20b208cc]
// C mapping of the WebRtcTransport object
typedef struct mk_rtc_transport_t *mk_rtc_transport;
/**
@ -395,6 +558,15 @@ typedef struct mk_rtc_transport_t *mk_rtc_transport;
* @param ppid id
* @param msg
* @param len
* Send rtc data channel
* @param ctx Data channel object
* @param streamId Stream id
* @param ppid Protocol id
* @param msg Data
* @param len Data length
* [AUTO-TRANSLATED:a0ce3c9e]
*/
API_EXPORT void API_CALL mk_rtc_send_datachannel(const mk_rtc_transport ctx, uint16_t streamId, uint32_t ppid, const char* msg, size_t len);

View File

@ -17,16 +17,22 @@
extern "C" {
#endif
// 是否为关键帧 [AUTO-TRANSLATED:b999067c]
// cpp
//是否为关键帧
#define MK_FRAME_FLAG_IS_KEY (1 << 0)
//是否为配置帧(sps/pps/vps等)
// 是否为配置帧(sps/pps/vps等) [AUTO-TRANSLATED:cf504832]
// 是否为配置帧(sps/pps/vps等)
#define MK_FRAME_FLAG_IS_CONFIG (1 << 1)
//是否可丢弃的帧(sei/aud)
// 是否可丢弃的帧(sei/aud) [AUTO-TRANSLATED:6481fe69]
// 是否可丢弃的帧(sei/aud)
#define MK_FRAME_FLAG_DROP_ABLE (1 << 2)
//是否不可单独解码的帧(多slice的非vcl帧)
// 是否不可单独解码的帧(多slice的非vcl帧) [AUTO-TRANSLATED:cb4da662]
// 是否不可单独解码的帧(多slice的非vcl帧)
#define MK_FRAME_FLAG_NOT_DECODE_ABLE (1 << 3)
//codec id常量定义
// codec id常量定义 [AUTO-TRANSLATED:dbc838b6]
// codec id常量定义
API_EXPORT extern const int MKCodecH264;
API_EXPORT extern const int MKCodecH265;
API_EXPORT extern const int MKCodecAAC;
@ -41,6 +47,7 @@ API_EXPORT extern const int MKCodecJPEG;
typedef struct mk_frame_t *mk_frame;
// 用户自定义free回调函数 [AUTO-TRANSLATED:dc96ff2d]
// 用户自定义free回调函数
typedef void(API_CALL *on_mk_frame_data_release)(void *user_data, char *ptr);
@ -54,6 +61,17 @@ typedef void(API_CALL *on_mk_frame_data_release)(void *user_data, char *ptr);
* @param cb data指针free释放回调,
* @param user_data data指针free释放回调用户指针
* @return frame对象引用
* Create a frame object and return its reference.
* @param codec_id Encoding and decoding type, please refer to MKCodecXXX definition.
* @param dts Decoding timestamp, in milliseconds.
* @param pts Display timestamp, in milliseconds.
* @param data Single frame data.
* @param size Single frame data length.
* @param cb data pointer free release callback, if empty, the data will be copied internally.
* @param user_data data pointer free release callback user pointer.
* @return frame object reference.
* [AUTO-TRANSLATED:0481221b]
*/
API_EXPORT mk_frame API_CALL mk_frame_create(int codec_id, uint64_t dts, uint64_t pts, const char *data, size_t size,
on_mk_frame_data_release cb, void *user_data);
@ -62,6 +80,10 @@ API_EXPORT mk_frame API_CALL mk_frame_create2(int codec_id, uint64_t dts, uint64
/**
* frame对象
* @param frame
* Decrement the reference of the frame object.
* @param frame Frame object reference.
* [AUTO-TRANSLATED:53b5a750]
*/
API_EXPORT void API_CALL mk_frame_unref(mk_frame frame);
@ -69,51 +91,83 @@ API_EXPORT void API_CALL mk_frame_unref(mk_frame frame);
* frame对象
* @param frame frame对象
* @return
* Reference the frame object.
* @param frame The referenced frame object.
* @return New object reference.
* [AUTO-TRANSLATED:f772813d]
*/
API_EXPORT mk_frame API_CALL mk_frame_ref(mk_frame frame);
/**
* frame codec类型MKCodecXXX定义
* Get the frame encoding codec type, please refer to MKCodecXXX definition.
* [AUTO-TRANSLATED:b3a34bb8]
*/
API_EXPORT int API_CALL mk_frame_codec_id(mk_frame frame);
/**
* codec名称
* Get the frame encoding codec name.
* [AUTO-TRANSLATED:6c3129d7]
*/
API_EXPORT const char* API_CALL mk_frame_codec_name(mk_frame frame);
/**
*
* Whether the frame is video.
* [AUTO-TRANSLATED:c43dbd4e]
*/
API_EXPORT int API_CALL mk_frame_is_video(mk_frame frame);
/**
*
* Get the frame data pointer.
* [AUTO-TRANSLATED:bf454f3b]
*/
API_EXPORT const char* API_CALL mk_frame_get_data(mk_frame frame);
/**
*
* Get the length of the frame data pointer.
* [AUTO-TRANSLATED:8a76acf2]
*/
API_EXPORT size_t API_CALL mk_frame_get_data_size(mk_frame frame);
/**
* H264/H265前缀一般是0x00 00 00 01,4
* Return the length of the frame data prefix, for example, the H264/H265 prefix is generally 0x00 00 00 01, then this function returns 4.
* [AUTO-TRANSLATED:352c7cfc]
*/
API_EXPORT size_t API_CALL mk_frame_get_data_prefix_size(mk_frame frame);
/**
*
* Get the decoding timestamp, in milliseconds.
* [AUTO-TRANSLATED:049f1339]
*/
API_EXPORT uint64_t API_CALL mk_frame_get_dts(mk_frame frame);
/**
*
* Get the display timestamp, in milliseconds.
* [AUTO-TRANSLATED:4ab081a7]
*/
API_EXPORT uint64_t API_CALL mk_frame_get_pts(mk_frame frame);
/**
* flag MK_FRAME_FLAG
* Get the frame flag, please refer to MK_FRAME_FLAG.
* [AUTO-TRANSLATED:6fdf971c]
*/
API_EXPORT uint32_t API_CALL mk_frame_get_flags(mk_frame frame);
@ -126,18 +180,31 @@ typedef struct mk_frame_merger_t *mk_frame_merger;
*
* @param type 0: none, 1: h264_prefix/AnnexB(0x 00 00 00 01), 2: mp4_nal_size(avcC)
* @return
* Create a frame merger.
* @param type Starting header type, 0: none, 1: h264_prefix/AnnexB(0x 00 00 00 01), 2: mp4_nal_size(avcC)
* @return Frame merger.
* [AUTO-TRANSLATED:385eedd7]
*/
API_EXPORT mk_frame_merger API_CALL mk_frame_merger_create(int type);
/**
*
* @param ctx
* Destroy the frame merger.
* @param ctx Object pointer.
* [AUTO-TRANSLATED:0c9aad7b]
*/
API_EXPORT void API_CALL mk_frame_merger_release(mk_frame_merger ctx);
/**
* merger对象缓冲便
* @param ctx
* Clear the merger object buffer for reuse.
* @param ctx Object pointer.
* [AUTO-TRANSLATED:6b1d2209]
*/
API_EXPORT void API_CALL mk_frame_merger_clear(mk_frame_merger ctx);
@ -148,6 +215,14 @@ API_EXPORT void API_CALL mk_frame_merger_clear(mk_frame_merger ctx);
* @param pts
* @param buffer buffer对象
* @param have_key_frame
* Frame merging callback function.
* @param user_data User data pointer.
* @param dts Decoding timestamp.
* @param pts Display timestamp.
* @param buffer Merged data buffer object.
* @param have_key_frame Whether the merged data contains a key frame.
* [AUTO-TRANSLATED:ff78df4f]
*/
typedef void(API_CALL *on_mk_frame_merger)(void *user_data, uint64_t dts, uint64_t pts, mk_buffer buffer, int have_key_frame);
@ -157,12 +232,23 @@ typedef void(API_CALL *on_mk_frame_merger)(void *user_data, uint64_t dts, uint64
* @param frame
* @param cb
* @param user_data
* Input frame to the merger object and merge.
* @param ctx Object pointer.
* @param frame Frame data.
* @param cb Frame merging callback function.
* @param user_data Frame merging callback function user data pointer.
* [AUTO-TRANSLATED:83aa1436]
*/
API_EXPORT void API_CALL mk_frame_merger_input(mk_frame_merger ctx, mk_frame frame, on_mk_frame_merger cb, void *user_data);
/**
* flush merger对象缓冲api前需要确保先调用mk_frame_merger_input函数并且回调参数有效
* @param ctx
* Force flush the merger object buffer. Before calling this API, make sure to call the mk_frame_merger_input function first and the callback parameters are valid.
* @param ctx Object pointer.
* [AUTO-TRANSLATED:42bb104c]
*/
API_EXPORT void API_CALL mk_frame_merger_flush(mk_frame_merger ctx);
@ -178,6 +264,15 @@ typedef struct mk_mpeg_muxer_t *mk_mpeg_muxer;
* @param size
* @param timestamp
* @param key_pos
* mpeg-ps/ts packer output callback function.
* @param user_data User data pointer set during callback.
* @param muxer Object.
* @param frame Frame data.
* @param size Frame data length.
* @param timestamp Timestamp.
* @param key_pos Whether it is a key frame.
* [AUTO-TRANSLATED:14c103a2]
*/
typedef void(API_CALL *on_mk_mpeg_muxer_frame)(void *user_data, mk_mpeg_muxer muxer, const char *frame, size_t size, uint64_t timestamp, int key_pos);
@ -187,12 +282,23 @@ typedef void(API_CALL *on_mk_mpeg_muxer_frame)(void *user_data, mk_mpeg_muxer mu
* @param user_data
* @param is_ps ps
* @return
* mpeg-ps/ts packer.
* @param cb Packing callback function.
* @param user_data Callback user data pointer.
* @param is_ps Whether it is ps.
* @return Packer object.
* [AUTO-TRANSLATED:6526b871]
*/
API_EXPORT mk_mpeg_muxer API_CALL mk_mpeg_muxer_create(on_mk_mpeg_muxer_frame cb, void *user_data, int is_ps);
/**
* mpeg-ps/ts
* @param ctx
* Delete the mpeg-ps/ts packer.
* @param ctx Packer.
* [AUTO-TRANSLATED:0b533391]
*/
API_EXPORT void API_CALL mk_mpeg_muxer_release(mk_mpeg_muxer ctx);
@ -200,6 +306,11 @@ API_EXPORT void API_CALL mk_mpeg_muxer_release(mk_mpeg_muxer ctx);
* track
* @param ctx mk_mpeg_muxer对象
* @param track mk_track对象
* Add audio/video track.
* @param ctx mk_mpeg_muxer object.
* @param track mk_track object, audio/video track.
* [AUTO-TRANSLATED:f2082619]
*/
API_EXPORT void API_CALL mk_mpeg_muxer_init_track(mk_mpeg_muxer ctx, void* track);
@ -208,6 +319,12 @@ API_EXPORT void API_CALL mk_mpeg_muxer_init_track(mk_mpeg_muxer ctx, void* track
* track()ZLMediaKit不知道后续是否还要添加track3
* Track类型便(3)
* @param ctx
* Call this function after the track is initialized.
* In the case of a single track (only audio or video), because ZLMediaKit does not know whether to add more tracks later, it will wait for an additional 3 seconds.
* If the generated stream is a single Track type, please call this function to speed up the stream generation. Of course, if you don't call this function, the impact is not big (it will wait for an additional 3 seconds).
* @param ctx Object pointer.
* [AUTO-TRANSLATED:f40d41cb]
*/
API_EXPORT void API_CALL mk_mpeg_muxer_init_complete(mk_mpeg_muxer ctx);
@ -216,6 +333,13 @@ API_EXPORT void API_CALL mk_mpeg_muxer_init_complete(mk_mpeg_muxer ctx);
* @param ctx mk_mpeg_muxer对象
* @param frame
* @return 10
* Input frame object.
* @param ctx mk_mpeg_muxer object.
* @param frame Frame object.
* @return 1 means success, 0 means failure.
* [AUTO-TRANSLATED:46523906]
*/
API_EXPORT int API_CALL mk_mpeg_muxer_input_frame(mk_mpeg_muxer ctx, mk_frame frame);

View File

@ -25,6 +25,13 @@ typedef struct mk_h264_splitter_t *mk_h264_splitter;
* @param splitter
* @param frame
* @param size
* h264 frame splitter output callback function
* @param user_data user data pointer set when setting the callback
* @param splitter object
* @param frame frame data
* @param size frame data length
* [AUTO-TRANSLATED:3e4e4dfa]
*/
typedef void(API_CALL *on_mk_h264_splitter_frame)(void *user_data, mk_h264_splitter splitter, const char *frame, int size);
@ -34,6 +41,13 @@ typedef void(API_CALL *on_mk_h264_splitter_frame)(void *user_data, mk_h264_split
* @param user_data
* @param is_h265 265
* @return
* Create h264 frame splitter
* @param cb frame splitting callback function
* @param user_data callback user data pointer
* @param is_h265 whether it is 265
* @return frame splitter object
* [AUTO-TRANSLATED:6e06f68d]
*/
API_EXPORT mk_h264_splitter API_CALL mk_h264_splitter_create(on_mk_h264_splitter_frame cb, void *user_data, int is_h265);
API_EXPORT mk_h264_splitter API_CALL mk_h264_splitter_create2(on_mk_h264_splitter_frame cb, void *user_data, on_user_data_free user_data_free, int is_h265);
@ -41,6 +55,10 @@ API_EXPORT mk_h264_splitter API_CALL mk_h264_splitter_create2(on_mk_h264_splitte
/**
* h264分帧器
* @param ctx
* Delete h264 frame splitter
* @param ctx frame splitter
* [AUTO-TRANSLATED:e69bb6dd]
*/
API_EXPORT void API_CALL mk_h264_splitter_release(mk_h264_splitter ctx);
@ -49,6 +67,13 @@ API_EXPORT void API_CALL mk_h264_splitter_release(mk_h264_splitter ctx);
* @param ctx
* @param data h264/h265数据
* @param size
* Input data and split frames
* @param ctx frame splitter
* @param data h264/h265 data
* @param size data length
* [AUTO-TRANSLATED:c6b93aed]
*/
API_EXPORT void API_CALL mk_h264_splitter_input_data(mk_h264_splitter ctx, const char *data, int size);

View File

@ -27,18 +27,32 @@ typedef struct mk_http_downloader_t *mk_http_downloader;
* @param code 0
* @param err_msg
* @param file_path
* @param user_data User data pointer
* @param code Error code, 0 represents success
* @param err_msg Error message
* @param file_path File save path
* [AUTO-TRANSLATED:8f8ed7ef]
*/
typedef void(API_CALL *on_mk_download_complete)(void *user_data, int code, const char *err_msg, const char *file_path);
/**
* http[s]
* @return
* Create http[s] downloader
* @return Downloader pointer
* [AUTO-TRANSLATED:93112194]
*/
API_EXPORT mk_http_downloader API_CALL mk_http_downloader_create();
/**
* http[s]
* @param ctx
* Destroy http[s] downloader
* @param ctx Downloader pointer
* [AUTO-TRANSLATED:8378a5a7]
*/
API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx);
@ -49,6 +63,14 @@ API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx);
* @param file
* @param cb
* @param user_data
* Start http[s] download
* @param ctx Downloader pointer
* @param url http[s] download url
* @param file File save path
* @param cb Callback function
* @param user_data User data pointer
* [AUTO-TRANSLATED:8a2acf02]
*/
API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data);
API_EXPORT void API_CALL mk_http_downloader_start2(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data, on_user_data_free user_data_free);
@ -66,16 +88,33 @@ typedef struct mk_http_requester_t *mk_http_requester;
* @param user_data
* @param code 0
* @param err_msg
* Http request result callback
* When code == 0, it means that the current http session is complete (http response has been received)
* Users should get the mk_http_requester object through user_data
* Then get the relevant response data through functions such as mk_http_requester_get_response
* At the end of the callback, the object should be destroyed by calling the mk_http_requester_release function
* Or reuse the object after calling the mk_http_requester_clear function
* @param user_data User data pointer
* @param code Error code, 0 represents success
* @param err_msg Error message
* [AUTO-TRANSLATED:d24408ce]
*/
typedef void(API_CALL *on_mk_http_requester_complete)(void *user_data, int code, const char *err_msg);
/**
* HttpRequester
* Create HttpRequester
* [AUTO-TRANSLATED:fa182fbc]
*/
API_EXPORT mk_http_requester API_CALL mk_http_requester_create();
/**
* mk_http_requester对象时才需要用到此方法
* This method is only needed when reusing the mk_http_requester object
* [AUTO-TRANSLATED:6854d97f]
*/
API_EXPORT void API_CALL mk_http_requester_clear(mk_http_requester ctx);
@ -83,17 +122,29 @@ API_EXPORT void API_CALL mk_http_requester_clear(mk_http_requester ctx);
* HttpRequester
* mk_http_requester_start函数且正在等待http回复
* mk_http_requester_release方法取消本次http请求
* Destroy HttpRequester
* If the mk_http_requester_start function is called and is waiting for the http response,
* You can also call the mk_http_requester_release method to cancel the current http request
* [AUTO-TRANSLATED:5f533e28]
*/
API_EXPORT void API_CALL mk_http_requester_release(mk_http_requester ctx);
/**
* HTTP方法GET/POST
* Set HTTP method, such as GET/POST
* [AUTO-TRANSLATED:d4b641f1]
*/
API_EXPORT void API_CALL mk_http_requester_set_method(mk_http_requester ctx,const char *method);
/**
* HTTP头
* @param header {"Content-Type","text/html",NULL} NULL结尾
* Batch set HTTP headers
* @param header For example, {"Content-Type","text/html",NULL} must end with NULL
* [AUTO-TRANSLATED:65124347]
*/
API_EXPORT void API_CALL mk_http_requester_set_header(mk_http_requester ctx, const char *header[]);
@ -102,18 +153,32 @@ API_EXPORT void API_CALL mk_http_requester_set_header(mk_http_requester ctx, con
* @param key Content-Type
* @param value text/html
* @param force key
* Add HTTP header
* @param key For example, Content-Type
* @param value For example, text/html
* @param force If the key already exists, whether to force replacement
* [AUTO-TRANSLATED:79d32682]
*/
API_EXPORT void API_CALL mk_http_requester_add_header(mk_http_requester ctx,const char *key,const char *value,int force);
/**
*
* @param body mk_http_body对象mk_http_body_from_string等函数生成使mk_http_body_release释放之
* Set message body,
* @param body mk_http_body object, generated by functions such as mk_http_body_from_string, please call mk_http_body_release to release it after use
* [AUTO-TRANSLATED:85d0f139]
*/
API_EXPORT void API_CALL mk_http_requester_set_body(mk_http_requester ctx, mk_http_body body);
/**
* HTTP回复后可调用该方法获取状态码
* @return 200 OK
* You can call this method to get the status code after receiving the HTTP response
* @return For example, 200 OK
* [AUTO-TRANSLATED:7757b21a]
*/
API_EXPORT const char* API_CALL mk_http_requester_get_response_status(mk_http_requester ctx);
@ -121,6 +186,11 @@ API_EXPORT const char* API_CALL mk_http_requester_get_response_status(mk_http_re
* HTTP回复后可调用该方法获取响应HTTP头
* @param key HTTP头键名
* @return HTTP头键值
* You can call this method to get the response HTTP header after receiving the HTTP response
* @param key HTTP header key name
* @return HTTP header key value
* [AUTO-TRANSLATED:10f8ae74]
*/
API_EXPORT const char* API_CALL mk_http_requester_get_response_header(mk_http_requester ctx,const char *key);
@ -128,12 +198,21 @@ API_EXPORT const char* API_CALL mk_http_requester_get_response_header(mk_http_re
* HTTP回复后可调用该方法获取响应HTTP body
* @param length body长度,null
* @return body指针
* You can call this method to get the response HTTP body after receiving the HTTP response
* @param length Return body length, can be null
* @return Body pointer
* [AUTO-TRANSLATED:764dbb38]
*/
API_EXPORT const char* API_CALL mk_http_requester_get_response_body(mk_http_requester ctx, size_t *length);
/**
* HTTP回复后可调用该方法获取响应
* @return
* You can call this method to get the response after receiving the HTTP response
* @return Response object
* [AUTO-TRANSLATED:3800b175]
*/
API_EXPORT mk_parser API_CALL mk_http_requester_get_response(mk_http_requester ctx);
@ -141,6 +220,11 @@ API_EXPORT mk_parser API_CALL mk_http_requester_get_response(mk_http_requester c
*
* @param cb
* @param user_data
* Set callback function
* @param cb Callback function, cannot be empty
* @param user_data User data pointer
* [AUTO-TRANSLATED:f04412b8]
*/
API_EXPORT void API_CALL mk_http_requester_set_cb(mk_http_requester ctx,on_mk_http_requester_complete cb, void *user_data);
API_EXPORT void API_CALL mk_http_requester_set_cb2(mk_http_requester ctx,on_mk_http_requester_complete cb, void *user_data, on_user_data_free user_data_free);
@ -149,6 +233,11 @@ API_EXPORT void API_CALL mk_http_requester_set_cb2(mk_http_requester ctx,on_mk_h
* url请求
* @param url urlhttp/https
* @param timeout_second
* Start url request
* @param url Request url, supports http/https
* @param timeout_second Maximum timeout time
* [AUTO-TRANSLATED:36986fec]
*/
API_EXPORT void API_CALL mk_http_requester_start(mk_http_requester ctx,const char *url, float timeout_second);

View File

@ -33,6 +33,16 @@ typedef struct mk_media_t *mk_media;
* @param hls_enabled hls
* @param mp4_enabled mp4
* @return
* Create a media source
* @param vhost Virtual host name, generally __defaultVhost__
* @param app Application name, recommended as live
* @param stream Stream id, such as camera
* @param duration Duration (in seconds), 0 for live broadcast
* @param hls_enabled Whether to generate hls
* @param mp4_enabled Whether to generate mp4
* @return Object pointer
* [AUTO-TRANSLATED:b5124a1e]
*/
API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, const char *stream,
float duration, int hls_enabled, int mp4_enabled);
@ -45,12 +55,25 @@ API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app,
* @param duration ()0
* @param option ProtocolOption相关配置
* @return
* Create a media source
* @param vhost Virtual host name, generally __defaultVhost__
* @param app Application name, recommended as live
* @param stream Stream id, such as camera
* @param duration Duration (in seconds), 0 for live broadcast
* @param option ProtocolOption related configuration
* @return Object pointer
* [AUTO-TRANSLATED:870d86b0]
*/
API_EXPORT mk_media API_CALL mk_media_create2(const char *vhost, const char *app, const char *stream, float duration, mk_ini option);
/**
*
* @param ctx
* Destroy the media source
* @param ctx Object pointer
* [AUTO-TRANSLATED:a63ad166]
*/
API_EXPORT void API_CALL mk_media_release(mk_media ctx);
@ -58,6 +81,11 @@ API_EXPORT void API_CALL mk_media_release(mk_media ctx);
* track
* @param ctx mk_media对象
* @param track mk_track对象
* Add audio and video tracks
* @param ctx mk_media object
* @param track mk_track object, audio and video track
* [AUTO-TRANSLATED:0e4ebe8d]
*/
API_EXPORT void API_CALL mk_media_init_track(mk_media ctx, mk_track track);
@ -73,6 +101,19 @@ API_EXPORT void API_CALL mk_media_init_track(mk_media ctx, mk_track track);
* @param height
* @param fps fps
* @return 10
* Add video track, please use mk_media_init_track method
* @param ctx Object pointer
* @param codec_id 0:CodecH264/1:CodecH265
* @param width Video width; Valid only during encoding
* @param height Video height; Valid only during encoding
* @param fps Video fps; Valid only during encoding
* @param bit_rate Video bitrate, unit bps; Valid only during encoding
* @param width Video width
* @param height Video height
* @param fps Video fps
* @return 1 for success, 0 for failure
* [AUTO-TRANSLATED:c6944851]
*/
API_EXPORT int API_CALL mk_media_init_video(mk_media ctx, int codec_id, int width, int height, float fps, int bit_rate);
@ -84,6 +125,15 @@ API_EXPORT int API_CALL mk_media_init_video(mk_media ctx, int codec_id, int widt
* @param sample_bit 16
* @param sample_rate
* @return 10
* Add audio track, please use mk_media_init_track method
* @param ctx Object pointer
* @param codec_id 2:CodecAAC/3:CodecG711A/4:CodecG711U/5:OPUS
* @param channel Number of channels
* @param sample_bit Sampling bit, only supports 16
* @param sample_rate Sampling rate
* @return 1 for success, 0 for failure
* [AUTO-TRANSLATED:5c5c7c7a]
*/
API_EXPORT int API_CALL mk_media_init_audio(mk_media ctx, int codec_id, int sample_rate, int channels, int sample_bit);
@ -92,6 +142,12 @@ API_EXPORT int API_CALL mk_media_init_audio(mk_media ctx, int codec_id, int samp
* track()ZLMediaKit不知道后续是否还要添加track3
* Track类型便(3)
* @param ctx
* Call this function after h264/h265/aac initialization,
* In single track (only audio or video), because ZLMediaKit does not know whether to add more tracks later, it will wait for 3 seconds.
* If the generated stream is a single Track type, please call this function to speed up the stream generation speed. Of course, if you do not call this function, the impact is not big (it will wait for 3 seconds).
* @param ctx Object pointer
* [AUTO-TRANSLATED:cd2bee12]
*/
API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx);
@ -100,6 +156,12 @@ API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx);
* @param ctx mk_media对象
* @param frame
* @return 10
* Input frame object
* @param ctx mk_media object
* @param frame Frame object
* @return 1 for success, 0 for failure
* [AUTO-TRANSLATED:9f6ca231]
*/
API_EXPORT int API_CALL mk_media_input_frame(mk_media ctx, mk_frame frame);
@ -111,6 +173,15 @@ API_EXPORT int API_CALL mk_media_input_frame(mk_media ctx, mk_frame frame);
* @param dts
* @param pts
* @return 10
* Input single frame H264 video, the starting byte of the frame can be 00 00 01, 00 00 00 01, please use mk_media_input_frame method
* @param ctx Object pointer
* @param data Single frame H264 data
* @param len Number of bytes of single frame H264 data
* @param dts Decode timestamp, unit milliseconds
* @param pts Play timestamp, unit milliseconds
* @return 1 for success, 0 for failure
* [AUTO-TRANSLATED:3b96ace8]
*/
API_EXPORT int API_CALL mk_media_input_h264(mk_media ctx, const void *data, int len, uint64_t dts, uint64_t pts);
@ -122,6 +193,15 @@ API_EXPORT int API_CALL mk_media_input_h264(mk_media ctx, const void *data, int
* @param dts
* @param pts
* @return 10
* Input single frame H265 video, the starting byte of the frame can be 00 00 01, 00 00 00 01, please use mk_media_input_frame method
* @param ctx Object pointer
* @param data Single frame H265 data
* @param len Number of bytes of single frame H265 data
* @param dts Decode timestamp, unit milliseconds
* @param pts Play timestamp, unit milliseconds
* @return 1 for success, 0 for failure
* [AUTO-TRANSLATED:884739ba]
*/
API_EXPORT int API_CALL mk_media_input_h265(mk_media ctx, const void *data, int len, uint64_t dts, uint64_t pts);
@ -131,6 +211,13 @@ API_EXPORT int API_CALL mk_media_input_h265(mk_media ctx, const void *data, int
* @param yuv yuv420p数据
* @param linesize yuv420p linesize
* @param cts
* Input YUV video data
* @param ctx Object pointer
* @param yuv yuv420p data
* @param linesize yuv420p linesize
* @param cts Video capture timestamp, unit milliseconds
* [AUTO-TRANSLATED:9c97805c]
*/
API_EXPORT void API_CALL mk_media_input_yuv(mk_media ctx, const char *yuv[3], int linesize[3], uint64_t cts);
@ -142,6 +229,15 @@ API_EXPORT void API_CALL mk_media_input_yuv(mk_media ctx, const char *yuv[3], in
* @param dts
* @param adts adts头null
* @return 10
* Input single frame AAC audio (specify adts header separately), please use mk_media_input_frame method
* @param ctx Object pointer
* @param data Single frame AAC data without adts header, adts header 7 bytes
* @param len Number of bytes of single frame AAC data
* @param dts Timestamp, milliseconds
* @param adts adts header, can be null
* @return 1 for success, 0 for failure
* [AUTO-TRANSLATED:11e0503d]
*/
API_EXPORT int API_CALL mk_media_input_aac(mk_media ctx, const void *data, int len, uint64_t dts, void *adts);
@ -152,6 +248,14 @@ API_EXPORT int API_CALL mk_media_input_aac(mk_media ctx, const void *data, int l
* @param len PCM数据字节数
* @param dts
* @return 10
* Input single frame PCM audio, this function is valid only when ENABLE_FAAC is compiled
* @param ctx Object pointer
* @param data Single frame PCM data
* @param len Number of bytes of single frame PCM data
* @param dts Timestamp, milliseconds
* @return 1 for success, 0 for failure
* [AUTO-TRANSLATED:70f7488b]
*/
API_EXPORT int API_CALL mk_media_input_pcm(mk_media ctx, void *data, int len, uint64_t pts);
@ -162,6 +266,14 @@ API_EXPORT int API_CALL mk_media_input_pcm(mk_media ctx, void *data, int len, ui
* @param len
* @param dts
* @return 10
* Input single frame OPUS/G711 audio frame, please use mk_media_input_frame method
* @param ctx Object pointer
* @param data Single frame audio data
* @param len Number of bytes of single frame audio data
* @param dts Timestamp, milliseconds
* @return 1 for success, 0 for failure
* [AUTO-TRANSLATED:4ffeabd6]
*/
API_EXPORT int API_CALL mk_media_input_audio(mk_media ctx, const void* data, int len, uint64_t dts);
@ -171,6 +283,13 @@ API_EXPORT int API_CALL mk_media_input_audio(mk_media ctx, const void* data, int
* mk_media_release函数并且释放其他资源
* mk_media_release函数MediaSource.close()
* @param user_data mk_media_set_on_close函数设置
* MediaSource.close() callback event
* When you choose to close an associated MediaSource, it will eventually trigger this callback
* You should call mk_media_release function and release other resources through this event
* If you do not call mk_media_release function, then the MediaSource.close() operation will be invalid
* @param user_data User data pointer, set by mk_media_set_on_close function
* [AUTO-TRANSLATED:20191b2d]
*/
typedef void(API_CALL *on_mk_media_close)(void *user_data);
@ -181,6 +300,14 @@ typedef void(API_CALL *on_mk_media_close)(void *user_data);
* @param ctx
* @param cb
* @param user_data
* Listen to MediaSource.close() event
* When you choose to close an associated MediaSource, it will eventually trigger this callback
* You should call mk_media_release function and release other resources through this event
* @param ctx Object pointer
* @param cb Callback pointer
* @param user_data User data pointer
* [AUTO-TRANSLATED:35d9db20]
*/
API_EXPORT void API_CALL mk_media_set_on_close(mk_media ctx, on_mk_media_close cb, void *user_data);
API_EXPORT void API_CALL mk_media_set_on_close2(mk_media ctx, on_mk_media_close cb, void *user_data, on_user_data_free user_data_free);
@ -190,6 +317,12 @@ API_EXPORT void API_CALL mk_media_set_on_close2(mk_media ctx, on_mk_media_close
* @param user_data ,mk_media_set_on_seek设置
* @param stamp_ms seek至的时间轴位置
* @return 1seek请求0
* Triggered when the client receives a seek request
* @param user_data User data pointer, set by mk_media_set_on_seek
* @param stamp_ms Seek to the timeline position, unit milliseconds
* @return 1 means the seek request will be processed, 0 means the request will be ignored
* [AUTO-TRANSLATED:c3301852]
*/
typedef int(API_CALL *on_mk_media_seek)(void *user_data,uint32_t stamp_ms);
@ -197,6 +330,11 @@ typedef int(API_CALL *on_mk_media_seek)(void *user_data,uint32_t stamp_ms);
* pause或resume请求时触发该回调
* @param user_data ,mk_media_set_on_pause设置
* @param pause 1:, 0:
* Triggered when the client receives a pause or resume request
* @param user_data User data pointer, set by mk_media_set_on_pause
* @param pause 1: pause, 0: resume
* [AUTO-TRANSLATED:4f8aa828]
*/
typedef int(API_CALL* on_mk_media_pause)(void* user_data, int pause);
@ -204,6 +342,11 @@ typedef int(API_CALL* on_mk_media_pause)(void* user_data, int pause);
* speed请求时触发该回调
* @param user_data ,mk_media_set_on_pause设置
* @param speed 0.5 1.0 2.0
* Triggered when the client receives a speed request
* @param user_data User data pointer, set by mk_media_set_on_pause
* @param speed 0.5 1.0 2.0
* [AUTO-TRANSLATED:51bd090d]
*/
typedef int(API_CALL* on_mk_media_speed)(void* user_data, float speed);
@ -212,6 +355,12 @@ typedef int(API_CALL* on_mk_media_speed)(void* user_data, float speed);
* @param ctx
* @param cb
* @param user_data
* Listen to player seek request event
* @param ctx Object pointer
* @param cb Callback pointer
* @param user_data User data pointer
* [AUTO-TRANSLATED:50c723d0]
*/
API_EXPORT void API_CALL mk_media_set_on_seek(mk_media ctx, on_mk_media_seek cb, void *user_data);
API_EXPORT void API_CALL mk_media_set_on_seek2(mk_media ctx, on_mk_media_seek cb, void *user_data, on_user_data_free user_data_free);
@ -221,6 +370,12 @@ API_EXPORT void API_CALL mk_media_set_on_seek2(mk_media ctx, on_mk_media_seek cb
* @param ctx
* @param cb
* @param user_data
* Listen to player pause request event
* @param ctx Object pointer
* @param cb Callback pointer
* @param user_data User data pointer
* [AUTO-TRANSLATED:bd6e9068]
*/
API_EXPORT void API_CALL mk_media_set_on_pause(mk_media ctx, on_mk_media_pause cb, void *user_data);
API_EXPORT void API_CALL mk_media_set_on_pause2(mk_media ctx, on_mk_media_pause cb, void *user_data, on_user_data_free user_data_free);
@ -230,6 +385,12 @@ API_EXPORT void API_CALL mk_media_set_on_pause2(mk_media ctx, on_mk_media_pause
* @param ctx
* @param cb
* @param user_data
* Listen to player pause request event
* @param ctx Object pointer
* @param cb Callback pointer
* @param user_data User data pointer
* [AUTO-TRANSLATED:bd6e9068]
*/
API_EXPORT void API_CALL mk_media_set_on_speed(mk_media ctx, on_mk_media_speed cb, void *user_data);
API_EXPORT void API_CALL mk_media_set_on_speed2(mk_media ctx, on_mk_media_speed cb, void *user_data, on_user_data_free user_data_free);
@ -238,6 +399,11 @@ API_EXPORT void API_CALL mk_media_set_on_speed2(mk_media ctx, on_mk_media_speed
*
* @param ctx
* @return
* Get the total number of viewers
* @param ctx Object pointer
* @return Number of viewers
* [AUTO-TRANSLATED:56635caf]
*/
API_EXPORT int API_CALL mk_media_total_reader_count(mk_media ctx);
@ -246,6 +412,12 @@ API_EXPORT int API_CALL mk_media_total_reader_count(mk_media ctx);
* @param user_data
* @param sender MediaSource对象
* @param regist 10
* MediaSource registration or deregistration event
* @param user_data User data pointer set when setting the callback
* @param sender Generated MediaSource object
* @param regist 1 for registration event, 0 for deregistration event
* [AUTO-TRANSLATED:4585bbef]
*/
typedef void(API_CALL *on_mk_media_source_regist)(void *user_data, mk_media_source sender, int regist);
@ -254,12 +426,21 @@ typedef void(API_CALL *on_mk_media_source_regist)(void *user_data, mk_media_sour
* @param ctx
* @param cb
* @param user_data
* Set MediaSource registration or deregistration event callback function
* @param ctx Object pointer
* @param cb Callback pointer
* @param user_data User data pointer
* [AUTO-TRANSLATED:1c3b45be]
*/
API_EXPORT void API_CALL mk_media_set_on_regist(mk_media ctx, on_mk_media_source_regist cb, void *user_data);
API_EXPORT void API_CALL mk_media_set_on_regist2(mk_media ctx, on_mk_media_source_regist cb, void *user_data, on_user_data_free user_data_free);
/**
* rtp推流成功与否的回调()
* Callback for whether rtp streaming is successful or not (after the first success, it will retry continuously)
* [AUTO-TRANSLATED:7e00f7fb]
*/
typedef on_mk_media_source_send_rtp_result on_mk_media_send_rtp_result;
@ -270,22 +451,46 @@ typedef on_mk_media_source_send_rtp_result on_mk_media_send_rtp_result;
* @param dst_port
* @param ssrc rtp的ssrc10
* @param con_type 0: tcp主动1udp主动2tcp被动3udp被动
* @param options
* @param cb
* @param user_data
* Start sending a ps-rtp stream (distinguished by ssrc), this api is thread-safe
* @param ctx Object pointer
* @param dst_url Target ip or domain name
* @param dst_port Target port
* @param ssrc rtp's ssrc, 10-base string print
* @param con_type 0: tcp active, 1: udp active, 2: tcp passive, 3: udp passive
* @param options Options
* @param cb Start success or failure callback
* @param user_data Callback user pointer
* [AUTO-TRANSLATED:dbf694a0]
*/
API_EXPORT void API_CALL mk_media_start_send_rtp(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_send_rtp_result cb, void *user_data);
API_EXPORT void API_CALL mk_media_start_send_rtp2(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_send_rtp_result cb, void *user_data, on_user_data_free user_data_free);
API_EXPORT void API_CALL mk_media_start_send_rtp3(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, mk_ini options, on_mk_media_send_rtp_result cb, void *user_data);
API_EXPORT void API_CALL mk_media_start_send_rtp4(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, mk_ini options, on_mk_media_send_rtp_result cb, void *user_data,on_user_data_free user_data_free);
/**
* ps-rtp发送api线程安全
* @param ctx
* @param ssrc rtp的ssrc10null或空字符串rtp推流
* Stop a certain route or all ps-rtp sending, this api is thread-safe
* @param ctx Object pointer
* @param ssrc rtp's ssrc, 10-base string print, if it is null or empty string, stop all rtp streaming
* [AUTO-TRANSLATED:6fb2b1df]
*/
API_EXPORT void API_CALL mk_media_stop_send_rtp(mk_media ctx, const char *ssrc);
/**
* 线
* @param ctx
* Get the belonging thread
* @param ctx Object pointer
* [AUTO-TRANSLATED:85a157e8]
*/
API_EXPORT mk_thread API_CALL mk_media_get_owner_thread(mk_media ctx);

View File

@ -28,6 +28,14 @@ typedef struct mk_player_t *mk_player;
* @param err_msg
* @param tracks track列表
* @param track_count track个数
* Callback for playback result or playback interruption event
* @param user_data User data pointer
* @param err_code Error code, 0 for success
* @param err_msg Error message
* @param tracks Track list
* @param track_count Number of tracks
* [AUTO-TRANSLATED:38d4c546]
*/
typedef void(API_CALL *on_mk_play_event)(void *user_data, int err_code, const char *err_msg, mk_track tracks[],
int track_count);
@ -35,12 +43,20 @@ typedef void(API_CALL *on_mk_play_event)(void *user_data, int err_code, const ch
/**
* ,rtmp[s]/rtsp[s]
* @return
* Create a player that supports rtmp[s]/rtsp[s]
* @return Player pointer
* [AUTO-TRANSLATED:509f9a50]
*/
API_EXPORT mk_player API_CALL mk_player_create();
/**
*
* @param ctx
* Destroy the player
* @param ctx Player pointer
* [AUTO-TRANSLATED:2448eb93]
*/
API_EXPORT void API_CALL mk_player_release(mk_player ctx);
@ -49,6 +65,12 @@ API_EXPORT void API_CALL mk_player_release(mk_player ctx);
* @param ctx
* @param key , net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/wait_track_ready
* @param val ,string
* Set player configuration options
* @param ctx Player pointer
* @param key Configuration key, supports net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/wait_track_ready
* @param val Configuration value, if it is an integer, it needs to be converted to a string
* [AUTO-TRANSLATED:12650e9f]
*/
API_EXPORT void API_CALL mk_player_set_option(mk_player ctx, const char *key, const char *val);
@ -56,6 +78,11 @@ API_EXPORT void API_CALL mk_player_set_option(mk_player ctx, const char *key, co
* url
* @param ctx
* @param url rtsp[s]/rtmp[s] url
* Start playing the url
* @param ctx Player pointer
* @param url rtsp[s]/rtmp[s] url
* [AUTO-TRANSLATED:dbec813f]
*/
API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url);
@ -63,6 +90,11 @@ API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url);
*
* @param ctx
* @param pause 1:0
* Pause or resume playback, only useful for on-demand
* @param ctx Player pointer
* @param pause 1: Pause playback, 0: Resume playback
* [AUTO-TRANSLATED:28eee990]
*/
API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause);
@ -70,6 +102,11 @@ API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause);
*
* @param ctx
* @param speed 0.5 1.0 2.0
* Playback at a multiple, only useful for on-demand
* @param ctx Player pointer
* @param speed 0.5 1.0 2.0
* [AUTO-TRANSLATED:95249ade]
*/
API_EXPORT void API_CALL mk_player_speed(mk_player ctx, float speed);
@ -77,6 +114,11 @@ API_EXPORT void API_CALL mk_player_speed(mk_player ctx, float speed);
*
* @param ctx
* @param progress 0.01.0
* Set the on-demand progress bar
* @param ctx Object pointer
* @param progress Value range is 0.01.0
* [AUTO-TRANSLATED:cede3a8f]
*/
API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress);
@ -84,6 +126,11 @@ API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress);
*
* @param ctx
* @param seek_pos
* Set the on-demand progress bar
* @param ctx Object pointer
* @param seek_pos Value range is the increment relative to the start time, unit is seconds
* [AUTO-TRANSLATED:cddea627]
*/
API_EXPORT void API_CALL mk_player_seekto_pos(mk_player ctx, int seek_pos);
@ -92,6 +139,12 @@ API_EXPORT void API_CALL mk_player_seekto_pos(mk_player ctx, int seek_pos);
* @param ctx
* @param cb ,null立即取消回调
* @param user_data
* Set the player to enable playback result callback function
* @param ctx Player pointer
* @param cb Callback function pointer, set null to immediately cancel the callback
* @param user_data User data pointer
* [AUTO-TRANSLATED:1c2daeaf]
*/
API_EXPORT void API_CALL mk_player_set_on_result(mk_player ctx, on_mk_play_event cb, void *user_data);
API_EXPORT void API_CALL mk_player_set_on_result2(mk_player ctx, on_mk_play_event cb, void *user_data, on_user_data_free user_data_free);
@ -101,24 +154,43 @@ API_EXPORT void API_CALL mk_player_set_on_result2(mk_player ctx, on_mk_play_even
* @param ctx
* @param cb ,null立即取消回调
* @param user_data
* Set the callback for playback being abnormally interrupted
* @param ctx Player pointer
* @param cb Callback function pointer, set null to immediately cancel the callback
* @param user_data User data pointer
///////////////////////////Audio and video related information interfaces are only valid after the playback success callback is triggered///////////////////////////////
* [AUTO-TRANSLATED:18f58697]
*/
API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_event cb, void *user_data);
API_EXPORT void API_CALL mk_player_set_on_shutdown2(mk_player ctx, on_mk_play_event cb, void *user_data, on_user_data_free user_data_free);
///////////////////////////获取音视频相关信息接口在播放成功回调触发后才有效///////////////////////////////
// /////////////////////////获取音视频相关信息接口在播放成功回调触发后才有效/////////////////////////////// [AUTO-TRANSLATED:4b53d8ff]
// * Get the duration of the on-demand program, if it is live, return 0, otherwise return the number of seconds
/**
* 0
* Get the on-demand playback progress, value range is 0.01.0
* [AUTO-TRANSLATED:522140b7]
*/
API_EXPORT float API_CALL mk_player_duration(mk_player ctx);
/**
* 0.01.0
* Get the on-demand playback progress position, value range is the increment relative to the start time, unit is seconds
* [AUTO-TRANSLATED:921795a0]
*/
API_EXPORT float API_CALL mk_player_progress(mk_player ctx);
/**
*
* Get the packet loss rate, valid for rtsp
* @param ctx Object pointer
* @param track_type 0: Video, 1: Audio
* [AUTO-TRANSLATED:058e5089]
*/
API_EXPORT int API_CALL mk_player_progress_pos(mk_player ctx);

View File

@ -29,6 +29,16 @@ typedef struct mk_proxy_player_t *mk_proxy_player;
* @param hls_enabled hls
* @param mp4_enabled mp4
* @return
* Create a proxy player
* @param vhost Virtual host name, generally __defaultVhost__
* @param app Application name
* @param stream Stream name
* @param rtp_type rtsp playback method: RTP_TCP = 0, RTP_UDP = 1, RTP_MULTICAST = 2
* @param hls_enabled Whether to generate hls
* @param mp4_enabled Whether to generate mp4
* @return Object pointer
* [AUTO-TRANSLATED:1d4f13f4]
*/
API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *vhost, const char *app, const char *stream, int hls_enabled, int mp4_enabled);
@ -40,6 +50,14 @@ API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *vhost, co
* @param stream
* @param option ProtocolOption相关配置
* @return
* Create a proxy player
* @param vhost Virtual host name, generally __defaultVhost__
* @param app Application name
* @param stream Stream name
* @param option ProtocolOption related configuration
* @return Object pointer
* [AUTO-TRANSLATED:4c6208df]
*/
API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create2(const char *vhost, const char *app, const char *stream, mk_ini option);
@ -54,6 +72,17 @@ API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create2(const char *vhost, c
* @param mp4_enabled mp4
* @param retry_count <0
* @return
* Create a proxy player
* @param vhost Virtual host name, generally __defaultVhost__
* @param app Application name
* @param stream Stream name
* @param rtp_type rtsp playback method: RTP_TCP = 0, RTP_UDP = 1, RTP_MULTICAST = 2
* @param hls_enabled Whether to generate hls
* @param mp4_enabled Whether to generate mp4
* @param retry_count Retry count, when <0 retry infinitely
* @return Object pointer
* [AUTO-TRANSLATED:e25286c3]
*/
API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create3(const char *vhost, const char *app, const char *stream, int hls_enabled, int mp4_enabled, int retry_count);
@ -66,6 +95,15 @@ API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create3(const char *vhost, c
* @param option ProtocolOption相关配置
* @param retry_count <0
* @return
* Create a proxy player
* @param vhost Virtual host name, generally __defaultVhost__
* @param app Application name
* @param stream Stream name
* @param option ProtocolOption related configuration
* @param retry_count Retry count, when <0 retry infinitely
* @return Object pointer
* [AUTO-TRANSLATED:2cb296d1]
*/
API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create4(const char *vhost, const char *app, const char *stream, mk_ini option, int retry_count);
@ -73,6 +111,10 @@ API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create4(const char *vhost, c
/**
*
* @param ctx
* Destroy the proxy player
* @param ctx Object pointer
* [AUTO-TRANSLATED:fe451691]
*/
API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx);
@ -81,6 +123,12 @@ API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx);
* @param ctx
* @param key , net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/rtsp_speed
* @param val ,string
* Set proxy player configuration options
* @param ctx Proxy player pointer
* @param key Configuration item key, supports net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/rtsp_speed
* @param val Configuration item value, if it is an integer, it needs to be converted to a unified string
* [AUTO-TRANSLATED:78938fba]
*/
API_EXPORT void API_CALL mk_proxy_player_set_option(mk_proxy_player ctx, const char *key, const char *val);
@ -88,6 +136,11 @@ API_EXPORT void API_CALL mk_proxy_player_set_option(mk_proxy_player ctx, const c
*
* @param ctx
* @param url url,rtsp/rtmp
* Start playback
* @param ctx Object pointer
* @param url Playback url, supports rtsp/rtmp
* [AUTO-TRANSLATED:9597bafb]
*/
API_EXPORT void API_CALL mk_proxy_player_play(mk_proxy_player ctx, const char *url);
@ -97,9 +150,17 @@ API_EXPORT void API_CALL mk_proxy_player_play(mk_proxy_player ctx, const char *u
* mk_proxy_player_release函数并且释放其他资源
* mk_proxy_player_release函数MediaSource.close()
* @param user_data mk_proxy_player_set_on_close函数设置
* MediaSource.close() callback event
* When you choose to close an associated MediaSource, it will eventually trigger this callback
* You should call mk_proxy_player_release function through this event and release other resources
* If you do not call mk_proxy_player_release function, then MediaSource.close() operation will be invalid
* @param user_data User data pointer, set by mk_proxy_player_set_on_close function
* [AUTO-TRANSLATED:c99b6bfd]
*/
typedef void(API_CALL *on_mk_proxy_player_cb)(void *user_data, int err, const char *what, int sys_err);
// 保持兼容
// 保持兼容 [AUTO-TRANSLATED:94139ca7]
// Keep compatible
#define on_mk_proxy_player_close on_mk_proxy_player_cb
/**
@ -109,6 +170,14 @@ typedef void(API_CALL *on_mk_proxy_player_cb)(void *user_data, int err, const ch
* @param ctx
* @param cb
* @param user_data
* Listen for MediaSource.close() event
* When you choose to close an associated MediaSource, it will eventually trigger this callback
* You should call mk_proxy_player_release function through this event and release other resources
* @param ctx Object pointer
* @param cb Callback pointer
* @param user_data User data pointer
* [AUTO-TRANSLATED:174060d4]
*/
API_EXPORT void API_CALL mk_proxy_player_set_on_close(mk_proxy_player ctx, on_mk_proxy_player_cb cb, void *user_data);
API_EXPORT void API_CALL mk_proxy_player_set_on_close2(mk_proxy_player ctx, on_mk_proxy_player_cb cb, void *user_data, on_user_data_free user_data_free);
@ -119,6 +188,13 @@ API_EXPORT void API_CALL mk_proxy_player_set_on_close2(mk_proxy_player ctx, on_m
* @param cb
* @param user_data
* @param user_data_free
* Set the proxy's first playback result callback. If the first playback fails, it can be considered a startup failure.
* @param ctx Object pointer
* @param cb Callback pointer
* @param user_data User data pointer
* @param user_data_free User data release callback
* [AUTO-TRANSLATED:1f34852a]
*/
API_EXPORT void API_CALL mk_proxy_player_set_on_play_result(mk_proxy_player ctx, on_mk_proxy_player_cb cb, void *user_data, on_user_data_free user_data_free);
@ -126,6 +202,11 @@ API_EXPORT void API_CALL mk_proxy_player_set_on_play_result(mk_proxy_player ctx,
*
* @param ctx
* @return
* Get the total number of viewers
* @param ctx Object pointer
* @return Number of viewers
* [AUTO-TRANSLATED:56635caf]
*/
API_EXPORT int API_CALL mk_proxy_player_total_reader_count(mk_proxy_player ctx);

View File

@ -25,6 +25,12 @@ typedef struct mk_pusher_t *mk_pusher;
* @param user_data
* @param err_code 0
* @param err_msg
* Callback for streaming result or streaming interruption event
* @param user_data User data pointer
* @param err_code Error code, 0 for success
* @param err_msg Error message
* [AUTO-TRANSLATED:6e7d5c79]
*/
typedef void(API_CALL *on_mk_push_event)(void *user_data,int err_code,const char *err_msg);
@ -38,6 +44,17 @@ typedef void(API_CALL *on_mk_push_event)(void *user_data,int err_code,const char
* @param app MediaSource对象的应用名live
* @param stream MediaSource对象的流id
* @return
* Bind the MediaSource object and create an rtmp[s]/rtsp[s] pusher
* MediaSource is generated by mk_media_create or mk_proxy_player_create or streaming
* The MediaSource object must be registered
*
* @param schema Protocol to which the bound MediaSource object belongs, supporting rtsp/rtmp
* @param vhost Virtual host of the bound MediaSource object, generally __defaultVhost__
* @param app Application name of the bound MediaSource object, generally live
* @param stream Stream id of the bound MediaSource object
* @return Object pointer
* [AUTO-TRANSLATED:9366fdbc]
*/
API_EXPORT mk_pusher API_CALL mk_pusher_create(const char *schema,const char *vhost,const char *app, const char *stream);
@ -48,12 +65,24 @@ API_EXPORT mk_pusher API_CALL mk_pusher_create(const char *schema,const char *vh
*
* @param src MediaSource对象
* @return
* Bind the MediaSource object and create an rtmp[s]/rtsp[s] pusher
* MediaSource is generated by mk_media_create or mk_proxy_player_create or streaming
* The MediaSource object must be registered
*
* @param src MediaSource object
* @return Object pointer
* [AUTO-TRANSLATED:34ca024a]
*/
API_EXPORT mk_pusher API_CALL mk_pusher_create_src(mk_media_source src);
/**
*
* @param ctx
* Release the pusher
* @param ctx Pusher pointer
* [AUTO-TRANSLATED:55fd6b8b]
*/
API_EXPORT void API_CALL mk_pusher_release(mk_pusher ctx);
@ -62,6 +91,12 @@ API_EXPORT void API_CALL mk_pusher_release(mk_pusher ctx);
* @param ctx
* @param key , net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms
* @param val ,string
* Set the pusher configuration options
* @param ctx Pusher pointer
* @param key Configuration item key, supports net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms
* @param val Configuration item value, if it is an integer, it needs to be converted to a unified string
* [AUTO-TRANSLATED:0e3ce06d]
*/
API_EXPORT void API_CALL mk_pusher_set_option(mk_pusher ctx, const char *key, const char *val);
@ -69,6 +104,11 @@ API_EXPORT void API_CALL mk_pusher_set_option(mk_pusher ctx, const char *key, co
*
* @param ctx
* @param url rtsp[s]/rtmp[s]
* Start streaming
* @param ctx Pusher pointer
* @param url Streaming address, supports rtsp[s]/rtmp[s]
* [AUTO-TRANSLATED:45c0a836]
*/
API_EXPORT void API_CALL mk_pusher_publish(mk_pusher ctx,const char *url);
@ -77,6 +117,12 @@ API_EXPORT void API_CALL mk_pusher_publish(mk_pusher ctx,const char *url);
* @param ctx
* @param cb ,null
* @param user_data
* Set the pusher streaming result callback function
* @param ctx Pusher pointer
* @param cb Callback function pointer, must not be null
* @param user_data User data pointer
* [AUTO-TRANSLATED:a45fb6e4]
*/
API_EXPORT void API_CALL mk_pusher_set_on_result(mk_pusher ctx, on_mk_push_event cb, void *user_data);
API_EXPORT void API_CALL mk_pusher_set_on_result2(mk_pusher ctx, on_mk_push_event cb, void *user_data, on_user_data_free user_data_free);
@ -86,6 +132,12 @@ API_EXPORT void API_CALL mk_pusher_set_on_result2(mk_pusher ctx, on_mk_push_even
* @param ctx
* @param cb ,null
* @param user_data
* Set the callback for the streaming being abnormally interrupted
* @param ctx Pusher pointer
* @param cb Callback function pointer, must not be null
* @param user_data User data pointer
* [AUTO-TRANSLATED:9e11a215]
*/
API_EXPORT void API_CALL mk_pusher_set_on_shutdown(mk_pusher ctx, on_mk_push_event cb, void *user_data);
API_EXPORT void API_CALL mk_pusher_set_on_shutdown2(mk_pusher ctx, on_mk_push_event cb, void *user_data, on_user_data_free user_data_free);

View File

@ -17,19 +17,28 @@
extern "C" {
#endif
///////////////////////////////////////////flv录制/////////////////////////////////////////////
// /////////////////////////////////////////flv录制///////////////////////////////////////////// [AUTO-TRANSLATED:a084663f]
// /////////////////////////////////////////flv录制/////////////////////////////////////////////
typedef struct mk_flv_recorder_t *mk_flv_recorder;
/**
* flv录制器
* @return
* Create flv recorder
* @return
* [AUTO-TRANSLATED:7582cde1]
*/
API_EXPORT mk_flv_recorder API_CALL mk_flv_recorder_create();
/**
* flv录制器
* @param ctx
* Release flv recorder
* @param ctx
* [AUTO-TRANSLATED:c33c76bb]
*/
API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx);
@ -41,10 +50,20 @@ API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx);
* @param stream RtmpMediaSource的 stream名
* @param file_path
* @return 0:-1:,RtmpMediaSource不存在
* Start recording flv
* @param ctx flv recorder
* @param vhost virtual host
* @param app app name of the bound RtmpMediaSource
* @param stream stream name of the bound RtmpMediaSource
* @param file_path file storage address
* @return 0: start exceeds, -1: failure, file opening fails or the RtmpMediaSource does not exist
* [AUTO-TRANSLATED:194cf3de]
*/
API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *vhost, const char *app, const char *stream, const char *file_path);
///////////////////////////////////////////hls/mp4录制/////////////////////////////////////////////
// /////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// [AUTO-TRANSLATED:99c61c68]
// /////////////////////////////////////////hls/mp4录制/////////////////////////////////////////////
/**
*
@ -53,6 +72,14 @@ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *v
* @param app
* @param stream id
* @return ,0:, 1:
* Get recording status
* @param type 0: hls, 1: MP4
* @param vhost virtual host
* @param app application name
* @param stream stream id
* @return recording status, 0: not recording, 1: recording
* [AUTO-TRANSLATED:0b1d374a]
*/
API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, const char *app, const char *stream);
@ -65,6 +92,16 @@ API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, co
* @param customized_path null则自动生成
* @param max_second mp4录制最大切片时间0
* @return 10
* Start recording
* @param type 0: hls-ts, 1: MP4, 2: hls-fmp4, 3: http-fmp4, 4: http-ts
* @param vhost virtual host
* @param app application name
* @param stream stream id
* @param customized_path custom directory for saving recording files, defaults to empty or null, automatically generated
* @param max_second maximum slice time for mp4 recording, in seconds, set to 0 to use the configuration file configuration
* @return 1 represents success, 0 represents failure
* [AUTO-TRANSLATED:0a1c8c3e]
*/
API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream, const char *customized_path, size_t max_second);
@ -75,6 +112,15 @@ API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const cha
* @param app
* @param stream id
* @return 1:0
* Stop recording
* @param type 0: hls-ts, 1: MP4, 2: hls-fmp4, 3: http-fmp4, 4: http-ts
* @param vhost virtual host
* @param app application name
* @param stream stream id
* @return 1: success, 0: failure
* [AUTO-TRANSLATED:df1638e7]
*/
API_EXPORT int API_CALL mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream);

View File

@ -22,12 +22,22 @@ typedef struct mk_rtp_server_t *mk_rtp_server;
* @param tcp_mode tcp模式(0: 1: 2: )
* @param stream_id id
* @return
* Create GB28181 RTP server
* @param port Listening port, 0 for random
* @param tcp_mode tcp mode (0: not listening to port 1: listening to port 2: actively connect to the server)
* @param stream_id Stream id bound to this port
* @return
* [AUTO-TRANSLATED:0c5fd548]
*/
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int tcp_mode, const char *stream_id);
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create2(uint16_t port, int tcp_mode, const char *vhost, const char *app, const char *stream_id);
/**
* TCP
* Callback for whether the connection to the server is successful in TCP active mode
* [AUTO-TRANSLATED:752e915a]
*/
typedef void(API_CALL *on_mk_rtp_server_connected)(void *user_data, int err, const char *what, int sys_err);
@ -39,6 +49,15 @@ typedef void(API_CALL *on_mk_rtp_server_connected)(void *user_data, int err, con
* @param cb
* @param user_data
* @return
* Connect to the server in TCP active mode
* @param @param ctx Server object
* @param dst_url Server address
* @param dst_port Server port
* @param cb Callback for whether the connection to the server is successful
* @param user_data User data pointer
* @return
* [AUTO-TRANSLATED:e827d45a]
*/
API_EXPORT void API_CALL mk_rtp_server_connect(mk_rtp_server ctx, const char *dst_url, uint16_t dst_port, on_mk_rtp_server_connected cb, void *user_data);
API_EXPORT void API_CALL mk_rtp_server_connect2(mk_rtp_server ctx, const char *dst_url, uint16_t dst_port, on_mk_rtp_server_connected cb, void *user_data, on_user_data_free user_data_free);
@ -46,6 +65,10 @@ API_EXPORT void API_CALL mk_rtp_server_connect2(mk_rtp_server ctx, const char *d
/**
* GB28181 RTP
* @param ctx
* Destroy GB28181 RTP server
* @param ctx Server object
* [AUTO-TRANSLATED:828e02f0]
*/
API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx);
@ -53,12 +76,21 @@ API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx);
*
* @param ctx
* @return
* Get the local listening port number
* @param ctx Server object
* @return Port number
* [AUTO-TRANSLATED:90fe5d22]
*/
API_EXPORT uint16_t API_CALL mk_rtp_server_port(mk_rtp_server ctx);
/**
* GB28181 RTP
* @param user_data
* Triggered when the GB28181 RTP server receives a stream timeout
* @param user_data User data pointer
* [AUTO-TRANSLATED:04d56f24]
*/
typedef void(API_CALL *on_mk_rtp_server_detach)(void *user_data);
@ -67,6 +99,13 @@ typedef void(API_CALL *on_mk_rtp_server_detach)(void *user_data);
* @param ctx
* @param cb
* @param user_data
* Listen for B28181 RTP server receiving stream timeout events
* @param ctx Server object
* @param cb Callback function
* @param user_data Callback function user data pointer
* [AUTO-TRANSLATED:a88c239f]
*/
API_EXPORT void API_CALL mk_rtp_server_set_on_detach(mk_rtp_server ctx, on_mk_rtp_server_detach cb, void *user_data);
API_EXPORT void API_CALL mk_rtp_server_set_on_detach2(mk_rtp_server ctx, on_mk_rtp_server_detach cb, void *user_data, on_user_data_free user_data_free);

View File

@ -29,6 +29,14 @@ typedef void(API_CALL *on_mk_buffer_free)(void *user_data, void *data);
* @param cb free回调函数
* @param user_data free回调函数on_mk_buffer_free第一个参数
* @return buffer对象
* Create a buffer object
* @param data Data pointer
* @param len Data length
* @param cb Data pointer free callback function. This parameter is set to null, the data will be copied internally
* @param user_data The first parameter of the data pointer free callback function on_mk_buffer_free
* @return buffer object
* [AUTO-TRANSLATED:933f9ad8]
*/
API_EXPORT mk_buffer API_CALL mk_buffer_from_char(const char *data, size_t len, on_mk_buffer_free cb, void *user_data);
API_EXPORT mk_buffer API_CALL mk_buffer_from_char2(const char *data, size_t len, on_mk_buffer_free cb, void *user_data, on_user_data_free user_data_free);
@ -38,7 +46,8 @@ API_EXPORT const char* API_CALL mk_buffer_get_data(mk_buffer buffer);
API_EXPORT size_t API_CALL mk_buffer_get_size(mk_buffer buffer);
///////////////////////////////////////////SockInfo/////////////////////////////////////////////
//SockInfo对象的C映射
// SockInfo对象的C映射 [AUTO-TRANSLATED:6bc64f0f]
// C mapping of SockInfo object
typedef struct mk_sock_info_t *mk_sock_info;
//SockInfo::get_peer_ip()
@ -52,24 +61,28 @@ API_EXPORT uint16_t API_CALL mk_sock_info_local_port(const mk_sock_info ctx);
#ifndef SOCK_INFO_API_RENAME
#define SOCK_INFO_API_RENAME
//mk_tcp_session对象转换成mk_sock_info对象后再获取网络相关信息
// mk_tcp_session对象转换成mk_sock_info对象后再获取网络相关信息 [AUTO-TRANSLATED:bd727f26]
// Get network information after converting mk_tcp_session object to mk_sock_info object
#define mk_tcp_session_peer_ip(x,buf) mk_sock_info_peer_ip(mk_tcp_session_get_sock_info(x),buf)
#define mk_tcp_session_local_ip(x,buf) mk_sock_info_local_ip(mk_tcp_session_get_sock_info(x),buf)
#define mk_tcp_session_peer_port(x) mk_sock_info_peer_port(mk_tcp_session_get_sock_info(x))
#define mk_tcp_session_local_port(x) mk_sock_info_local_port(mk_tcp_session_get_sock_info(x))
//mk_tcp_client对象转换成mk_sock_info对象后再获取网络相关信息
// mk_tcp_client对象转换成mk_sock_info对象后再获取网络相关信息 [AUTO-TRANSLATED:e642eaac]
// Get network information after converting mk_tcp_client object to mk_sock_info object
#define mk_tcp_client_peer_ip(x,buf) mk_sock_info_peer_ip(mk_tcp_client_get_sock_info(x),buf)
#define mk_tcp_client_local_ip(x,buf) mk_sock_info_local_ip(mk_tcp_client_get_sock_info(x),buf)
#define mk_tcp_client_peer_port(x) mk_sock_info_peer_port(mk_tcp_client_get_sock_info(x))
#define mk_tcp_client_local_port(x) mk_sock_info_local_port(mk_tcp_client_get_sock_info(x))
#endif
///////////////////////////////////////////TcpSession/////////////////////////////////////////////
//TcpSession对象的C映射
// TcpSession对象的C映射 [AUTO-TRANSLATED:df12e714]
// C mapping of TcpSession object
typedef struct mk_tcp_session_t *mk_tcp_session;
typedef struct mk_tcp_session_ref_t *mk_tcp_session_ref;
//获取基类指针以便获取其网络相关信息
// 获取基类指针以便获取其网络相关信息 [AUTO-TRANSLATED:725bdbd0]
// Get the base class pointer to get its network information
API_EXPORT mk_sock_info API_CALL mk_tcp_session_get_sock_info(const mk_tcp_session ctx);
//TcpSession::safeShutdown()
@ -78,24 +91,34 @@ API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int er
API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx, const char *data, size_t len);
API_EXPORT void API_CALL mk_tcp_session_send_buffer(const mk_tcp_session ctx, mk_buffer buffer);
//切换到该对象所在线程后再TcpSession::send()
// 切换到该对象所在线程后再TcpSession::send() [AUTO-TRANSLATED:8a8f72ac]
// Switch to the thread where the object is located, then TcpSession::send()
API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx, const char *data, size_t len);
API_EXPORT void API_CALL mk_tcp_session_send_buffer_safe(const mk_tcp_session ctx, mk_buffer buffer);
//创建mk_tcp_session的强引用
// 创建mk_tcp_session的强引用 [AUTO-TRANSLATED:9dff998d]
// Create a strong reference to mk_tcp_session
API_EXPORT mk_tcp_session_ref API_CALL mk_tcp_session_ref_from(const mk_tcp_session ctx);
//删除mk_tcp_session的强引用
// 删除mk_tcp_session的强引用 [AUTO-TRANSLATED:583848b0]
// Delete the strong reference to mk_tcp_session
API_EXPORT void mk_tcp_session_ref_release(const mk_tcp_session_ref ref);
//根据强引用获取mk_tcp_session
// 根据强引用获取mk_tcp_session [AUTO-TRANSLATED:4bf1f7a0]
// Get mk_tcp_session according to the strong reference
API_EXPORT mk_tcp_session mk_tcp_session_from_ref(const mk_tcp_session_ref ref);
///////////////////////////////////////////自定义tcp服务/////////////////////////////////////////////
// /////////////////////////////////////////自定义tcp服务///////////////////////////////////////////// [AUTO-TRANSLATED:25f12aa6]
// /////////////////////////////////////////Custom tcp service/////////////////////////////////////////////
typedef struct {
/**
* mk_tcp_session创建对象
* @param server_port
* @param session
* Receive mk_tcp_session create object
* @param server_port Server port number
* @param session Session processing object
* [AUTO-TRANSLATED:58183e28]
*/
void (API_CALL *on_mk_tcp_session_create)(uint16_t server_port,mk_tcp_session session);
@ -104,6 +127,12 @@ typedef struct {
* @param server_port
* @param session
* @param buffer
* Receive data sent by the client
* @param server_port Server port number
* @param session Session processing object
* @param buffer Data
* [AUTO-TRANSLATED:ff2132fa]
*/
void (API_CALL *on_mk_tcp_session_data)(uint16_t server_port,mk_tcp_session session, mk_buffer buffer);
@ -111,6 +140,11 @@ typedef struct {
* 2
* @param server_port
* @param session
* Timer every 2 seconds, used to manage timeout tasks
* @param server_port Server port number
* @param session Session processing object
* [AUTO-TRANSLATED:5d36ea59]
*/
void (API_CALL *on_mk_tcp_session_manager)(uint16_t server_port,mk_tcp_session session);
@ -120,19 +154,30 @@ typedef struct {
* @param session
* @param code
* @param msg
* Generally triggered by client disconnecting tcp
* @param server_port Server port number
* @param session Session processing object
* @param code Error code
* @param msg Error message
* [AUTO-TRANSLATED:a2e6ce8b]
*/
void (API_CALL *on_mk_tcp_session_disconnect)(uint16_t server_port,mk_tcp_session session,int code,const char *msg);
} mk_tcp_session_events;
typedef enum {
//普通的tcp
// 普通的tcp [AUTO-TRANSLATED:b4035d33]
// Ordinary tcp
mk_type_tcp = 0,
//ssl类型的tcp
// ssl类型的tcp [AUTO-TRANSLATED:88792584]
// ssl type tcp
mk_type_ssl = 1,
//基于websocket的连接
// 基于websocket的连接 [AUTO-TRANSLATED:01f3223d]
// Websocket based connection
mk_type_ws = 2,
//基于ssl websocket的连接
// 基于ssl websocket的连接 [AUTO-TRANSLATED:86411da9]
// Ssl websocket based connection
mk_type_wss = 3
}mk_tcp_type;
@ -141,6 +186,12 @@ typedef enum {
* mk_tcp_server_server_start启动的服务类型有效
* @param session
* @param user_data
* Attach user data to the tcp session object
* This function is only valid for services started by mk_tcp_server_server_start
* @param session Session object
* @param user_data User data pointer
* [AUTO-TRANSLATED:272bd460]
*/
API_EXPORT void API_CALL mk_tcp_session_set_user_data(mk_tcp_session session, void *user_data);
API_EXPORT void API_CALL mk_tcp_session_set_user_data2(mk_tcp_session session, void *user_data, on_user_data_free user_data_free);
@ -150,6 +201,12 @@ API_EXPORT void API_CALL mk_tcp_session_set_user_data2(mk_tcp_session session, v
* mk_tcp_server_server_start启动的服务类型有效
* @param session tcp会话对象
* @return
* Get the user data attached to the tcp session object
* This function is only valid for services started by mk_tcp_server_server_start
* @param session Tcp session object
* @return User data pointer
* [AUTO-TRANSLATED:8047a5a4]
*/
API_EXPORT void* API_CALL mk_tcp_session_get_user_data(mk_tcp_session session);
@ -157,19 +214,29 @@ API_EXPORT void* API_CALL mk_tcp_session_get_user_data(mk_tcp_session session);
* tcp服务器
* @param port 0
* @param type
* Start tcp server
* @param port Listening port number, 0 is random
* @param type Server type
* [AUTO-TRANSLATED:ad27f0ed]
*/
API_EXPORT uint16_t API_CALL mk_tcp_server_start(uint16_t port, mk_tcp_type type);
/**
* tcp服务器事件
* Listen for tcp server events
* [AUTO-TRANSLATED:7ca82a56]
*/
API_EXPORT void API_CALL mk_tcp_server_events_listen(const mk_tcp_session_events *events);
///////////////////////////////////////////自定义tcp客户端/////////////////////////////////////////////
// /////////////////////////////////////////自定义tcp客户端///////////////////////////////////////////// [AUTO-TRANSLATED:e5ae198e]
// /////////////////////////////////////////Custom tcp client/////////////////////////////////////////////
typedef struct mk_tcp_client_t *mk_tcp_client;
//获取基类指针以便获取其网络相关信息
// 获取基类指针以便获取其网络相关信息 [AUTO-TRANSLATED:725bdbd0]
// Get the base class pointer to get its network information
API_EXPORT mk_sock_info API_CALL mk_tcp_client_get_sock_info(const mk_tcp_client ctx);
typedef struct {
@ -178,6 +245,12 @@ typedef struct {
* @param client tcp客户端
* @param code 0
* @param msg
* Tcp client connects to server successfully or fails callback
* @param client Tcp client
* @param code 0 for successful connection, otherwise for failure reason
* @param msg Connection failure error message
* [AUTO-TRANSLATED:2b38e72a]
*/
void (API_CALL *on_mk_tcp_client_connect)(mk_tcp_client client,int code,const char *msg);
@ -187,6 +260,13 @@ typedef struct {
* @param client tcp客户端
* @param code
* @param msg
* Tcp client disconnects from tcp server callback
* Generally caused by eof event
* @param client Tcp client
* @param code Error code
* @param msg Error message
* [AUTO-TRANSLATED:5658c1c5]
*/
void (API_CALL *on_mk_tcp_client_disconnect)(mk_tcp_client client,int code,const char *msg);
@ -194,12 +274,21 @@ typedef struct {
* tcp服务器发来的数据
* @param client tcp客户端
* @param buffer
* Receive data sent by the tcp server
* @param client Tcp client
* @param buffer Data
* [AUTO-TRANSLATED:4a225334]
*/
void (API_CALL *on_mk_tcp_client_data)(mk_tcp_client client, mk_buffer buffer);
/**
* 2
* @param client tcp客户端
* Timer every 2 seconds, used to manage timeout tasks
* @param client Tcp client
* [AUTO-TRANSLATED:05f637ef]
*/
void (API_CALL *on_mk_tcp_client_manager)(mk_tcp_client client);
} mk_tcp_client_events;
@ -210,12 +299,23 @@ typedef struct {
* @param user_data
* @param type
* @return
* Create tcp client
* @param events Callback function structure
* @param user_data User data pointer
* @param type Client type
* @return Client object
* [AUTO-TRANSLATED:01191226]
*/
API_EXPORT mk_tcp_client API_CALL mk_tcp_client_create(mk_tcp_client_events *events, mk_tcp_type type);
/**
* tcp客户端
* @param ctx
* Release the tcp client
* @param ctx Client object
* [AUTO-TRANSLATED:b3a75d23]
*/
API_EXPORT void API_CALL mk_tcp_client_release(mk_tcp_client ctx);
@ -225,6 +325,13 @@ API_EXPORT void API_CALL mk_tcp_client_release(mk_tcp_client ctx);
* @param host ip或域名
* @param port
* @param time_out_sec
* Initiate connection
* @param ctx Client object
* @param host Server ip or domain name
* @param port Server port number
* @param time_out_sec Timeout time
* [AUTO-TRANSLATED:dd45e0fa]
*/
API_EXPORT void API_CALL mk_tcp_client_connect(mk_tcp_client ctx, const char *host, uint16_t port, float time_out_sec);
@ -234,6 +341,13 @@ API_EXPORT void API_CALL mk_tcp_client_connect(mk_tcp_client ctx, const char *ho
* @param ctx
* @param data
* @param len 0strlen获取
* Non-thread-safe data sending
* Developers can call this function if they can ensure that it is within the network thread of this object
* @param ctx Client object
* @param data Data pointer
* @param len Data length, 0 means get it by strlen internally
* [AUTO-TRANSLATED:a5e2ac86]
*/
API_EXPORT void API_CALL mk_tcp_client_send(mk_tcp_client ctx, const char *data, int len);
API_EXPORT void API_CALL mk_tcp_client_send_buffer(mk_tcp_client ctx, mk_buffer buffer);
@ -243,6 +357,12 @@ API_EXPORT void API_CALL mk_tcp_client_send_buffer(mk_tcp_client ctx, mk_buffer
* @param ctx
* @param data
* @param len 0strlen获取
* Send data after switching to the network thread of this object
* @param ctx Client object
* @param data Data pointer
* @param len Data length, 0 means get it by strlen internally
* [AUTO-TRANSLATED:95dc75f8]
*/
API_EXPORT void API_CALL mk_tcp_client_send_safe(mk_tcp_client ctx, const char *data, int len);
API_EXPORT void API_CALL mk_tcp_client_send_buffer_safe(mk_tcp_client ctx, mk_buffer buffer);
@ -251,6 +371,11 @@ API_EXPORT void API_CALL mk_tcp_client_send_buffer_safe(mk_tcp_client ctx, mk_bu
*
* @param ctx
* @param user_data
* Client attaches user data
* @param ctx Client object
* @param user_data User data pointer
* [AUTO-TRANSLATED:a9d4840b]
*/
API_EXPORT void API_CALL mk_tcp_client_set_user_data(mk_tcp_client ctx, void *user_data);
API_EXPORT void API_CALL mk_tcp_client_set_user_data2(mk_tcp_client ctx, void *user_data, on_user_data_free user_data_free);
@ -259,6 +384,12 @@ API_EXPORT void API_CALL mk_tcp_client_set_user_data2(mk_tcp_client ctx, void *u
*
* @param ctx
* @return
* Get the user data attached to the client object
* @param ctx Client object
* @return User data pointer
* [AUTO-TRANSLATED:7f74985b]
*/
API_EXPORT void* API_CALL mk_tcp_client_get_user_data(mk_tcp_client ctx);

View File

@ -19,13 +19,19 @@
extern "C" {
#endif
///////////////////////////////////////////事件线程/////////////////////////////////////////////
// /////////////////////////////////////////事件线程///////////////////////////////////////////// [AUTO-TRANSLATED:6d564b3b]
// /////////////////////////////////////////事件线程/////////////////////////////////////////////
typedef struct mk_thread_t *mk_thread;
/**
* tcp会话对象所在事件线程
* @param ctx tcp会话对象
* @return 线
* Get the event thread where the tcp session object is located
* @param ctx tcp session object
* @return The event thread where the object is located
* [AUTO-TRANSLATED:17da57ec]
*/
API_EXPORT mk_thread API_CALL mk_thread_from_tcp_session(mk_tcp_session ctx);
@ -33,6 +39,11 @@ API_EXPORT mk_thread API_CALL mk_thread_from_tcp_session(mk_tcp_session ctx);
* tcp客户端对象所在事件线程
* @param ctx tcp客户端
* @return 线
* Get the event thread where the tcp client object is located
* @param ctx tcp client
* @return The event thread where the object is located
* [AUTO-TRANSLATED:15d4174b]
*/
API_EXPORT mk_thread API_CALL mk_thread_from_tcp_client(mk_tcp_client ctx);
@ -41,6 +52,12 @@ API_EXPORT mk_thread API_CALL mk_thread_from_tcp_client(mk_tcp_client ctx);
* 线线
* 线io事件线程
* @return 线
* Get an event thread randomly from the event thread pool according to the load balancing algorithm
* If this function is executed within the event thread, it will return the current event thread
* Event thread refers to timer, network io event thread
* @return Event thread
* [AUTO-TRANSLATED:5da37e1f]
*/
API_EXPORT mk_thread API_CALL mk_thread_from_pool();
@ -49,6 +66,12 @@ API_EXPORT mk_thread API_CALL mk_thread_from_pool();
* 线线
* ZLMediaKit中后台线程用于dns解析mp4点播时的文件解复用
* @return 线
* Get a thread randomly from the background thread pool according to the load balancing algorithm
* Background threads are essentially the same as event threads, but they have lower priority and can execute short-term blocking tasks
* Background threads in ZLMediaKit are used for dns resolution, file demultiplexing during mp4 on-demand
* @return Background thread
* [AUTO-TRANSLATED:3b552537]
*/
API_EXPORT mk_thread API_CALL mk_thread_from_pool_work();
@ -60,6 +83,13 @@ typedef struct mk_thread_pool_t *mk_thread_pool;
* @param n_thread 线0cpu个数
* @param priority 线PRIORITY_LOWEST = 0,PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH, PRIORITY_HIGHEST
* @return 线
* Create a thread pool
* @param name Thread pool name, for debugging
* @param n_thread Number of threads, 0 for the number of cpus
* @param priority Thread priority, divided into PRIORITY_LOWEST = 0,PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH, PRIORITY_HIGHEST
* @return Thread pool
* [AUTO-TRANSLATED:177acea2]
*/
API_EXPORT mk_thread_pool API_CALL mk_thread_pool_create(const char *name, size_t n_thread, int priority);
@ -67,6 +97,11 @@ API_EXPORT mk_thread_pool API_CALL mk_thread_pool_create(const char *name, size_
* 线
* @param pool 线
* @return 0:
* Destroy the thread pool
* @param pool Thread pool
* @return 0: Success
* [AUTO-TRANSLATED:1f1b3582]
*/
API_EXPORT int API_CALL mk_thread_pool_release(mk_thread_pool pool);
@ -74,10 +109,16 @@ API_EXPORT int API_CALL mk_thread_pool_release(mk_thread_pool pool);
* 线线
* @param pool 线
* @return 线
* Get a thread from the thread pool
* @param pool Thread pool
* @return Thread
* [AUTO-TRANSLATED:f47de48e]
*/
API_EXPORT mk_thread API_CALL mk_thread_from_thread_pool(mk_thread_pool pool);
///////////////////////////////////////////线程切换/////////////////////////////////////////////
// /////////////////////////////////////////线程切换///////////////////////////////////////////// [AUTO-TRANSLATED:5fc795bf]
// /////////////////////////////////////////线程切换/////////////////////////////////////////////
typedef void (API_CALL *on_mk_async)(void *user_data);
/**
@ -85,6 +126,12 @@ typedef void (API_CALL *on_mk_async)(void *user_data);
* @param ctx 线
* @param cb
* @param user_data
* Switch to the event thread and execute asynchronously
* @param ctx Event thread
* @param cb Callback function
* @param user_data User data pointer
* [AUTO-TRANSLATED:55773ed5]
*/
API_EXPORT void API_CALL mk_async_do(mk_thread ctx, on_mk_async cb, void *user_data);
API_EXPORT void API_CALL mk_async_do2(mk_thread ctx, on_mk_async cb, void *user_data, on_user_data_free user_data_free);
@ -95,6 +142,13 @@ API_EXPORT void API_CALL mk_async_do2(mk_thread ctx, on_mk_async cb, void *user_
* @param ms
* @param cb
* @param user_data
* Switch to the event thread and execute with delay
* @param ctx Event thread
* @param ms Delay time, in milliseconds
* @param cb Callback function
* @param user_data User data pointer
* [AUTO-TRANSLATED:35dfdb0e]
*/
API_EXPORT void API_CALL mk_async_do_delay(mk_thread ctx, size_t ms, on_mk_async cb, void *user_data);
API_EXPORT void API_CALL mk_async_do_delay2(mk_thread ctx, size_t ms, on_mk_async cb, void *user_data, on_user_data_free user_data_free);
@ -104,15 +158,26 @@ API_EXPORT void API_CALL mk_async_do_delay2(mk_thread ctx, size_t ms, on_mk_asyn
* @param ctx 线
* @param cb
* @param user_data
* Switch to the event thread and execute synchronously
* @param ctx Event thread
* @param cb Callback function
* @param user_data User data pointer
* [AUTO-TRANSLATED:1326dfb2]
*/
API_EXPORT void API_CALL mk_sync_do(mk_thread ctx, on_mk_async cb, void *user_data);
///////////////////////////////////////////定时器/////////////////////////////////////////////
// /////////////////////////////////////////定时器///////////////////////////////////////////// [AUTO-TRANSLATED:7f76781c]
// /////////////////////////////////////////定时器/////////////////////////////////////////////
typedef struct mk_timer_t *mk_timer;
/**
*
* @return ()0
* Timer trigger event
* @return Next trigger delay (in milliseconds), return 0 to stop repeating
* [AUTO-TRANSLATED:f8846f56]
*/
typedef uint64_t (API_CALL *on_mk_timer)(void *user_data);
@ -123,6 +188,14 @@ typedef uint64_t (API_CALL *on_mk_timer)(void *user_data);
* @param cb
* @param user_data
* @return
* Create a timer
* @param ctx Thread object
* @param delay_ms Execution delay, in milliseconds
* @param cb Callback function
* @param user_data User data pointer
* @return Timer object
* [AUTO-TRANSLATED:2d47864a]
*/
API_EXPORT mk_timer API_CALL mk_timer_create(mk_thread ctx, uint64_t delay_ms, on_mk_timer cb, void *user_data);
API_EXPORT mk_timer API_CALL mk_timer_create2(mk_thread ctx, uint64_t delay_ms, on_mk_timer cb, void *user_data, on_user_data_free user_data_free);
@ -130,31 +203,49 @@ API_EXPORT mk_timer API_CALL mk_timer_create2(mk_thread ctx, uint64_t delay_ms,
/**
*
* @param ctx
* Destroy and cancel the timer
* @param ctx Timer object
* [AUTO-TRANSLATED:3fdb8534]
*/
API_EXPORT void API_CALL mk_timer_release(mk_timer ctx);
///////////////////////////////////////////信号量/////////////////////////////////////////////
// /////////////////////////////////////////信号量///////////////////////////////////////////// [AUTO-TRANSLATED:f41da57a]
// /////////////////////////////////////////信号量/////////////////////////////////////////////
typedef struct mk_sem_t *mk_sem;
/**
*
* Create a semaphore
* [AUTO-TRANSLATED:dcd83058]
*/
API_EXPORT mk_sem API_CALL mk_sem_create();
/**
*
* Destroy the semaphore
* [AUTO-TRANSLATED:b298797b]
*/
API_EXPORT void API_CALL mk_sem_release(mk_sem sem);
/**
* n
* Increase the semaphore by n
* [AUTO-TRANSLATED:1f455c5d]
*/
API_EXPORT void API_CALL mk_sem_post(mk_sem sem, size_t n);
/**
* 1
* @param sem
* Decrease the semaphore by 1
* @param sem
* [AUTO-TRANSLATED:626595d8]
*/
API_EXPORT void API_CALL mk_sem_wait(mk_sem sem);

View File

@ -18,12 +18,15 @@
extern "C" {
#endif
//音视频轨道
// 音视频轨道 [AUTO-TRANSLATED:cec3b225]
// Audio and video track
typedef struct mk_track_t *mk_track;
//输出frame回调
// 输出frame回调 [AUTO-TRANSLATED:4daee75b]
// Output frame callback
typedef void(API_CALL *on_mk_frame_out)(void *user_data, mk_frame frame);
//track创建参数
// track创建参数 [AUTO-TRANSLATED:31a3c487]
// Track creation parameters
typedef union {
struct {
int width;
@ -42,12 +45,22 @@ typedef union {
* @param codec_id MKCodecXXX
* @param args
* @return track对象引用
* Create a track object reference
* @param codec_id Please refer to the MKCodecXXX constant definition
* @param args Video or audio parameters
* @return Track object reference
* [AUTO-TRANSLATED:d53f3578]
*/
API_EXPORT mk_track API_CALL mk_track_create(int codec_id, codec_args *args);
/**
* track对象
* @param track track对象
* Decrement the reference count of the track object
* @param track Track object
* [AUTO-TRANSLATED:50d6180e]
*/
API_EXPORT void API_CALL mk_track_unref(mk_track track);
@ -55,36 +68,59 @@ API_EXPORT void API_CALL mk_track_unref(mk_track track);
* track对象
* @param track track对象
* @return track引用对象
* Increment the reference count of the track object
* @param track Track object
* @return New track reference object
* [AUTO-TRANSLATED:6492cbb1]
*/
API_EXPORT mk_track API_CALL mk_track_ref(mk_track track);
/**
* track codec类型MKCodecXXX定义
* Get the track encoding codec type, please refer to the MKCodecXXX definition
* [AUTO-TRANSLATED:f90ed835]
*/
API_EXPORT int API_CALL mk_track_codec_id(mk_track track);
/**
* codec名称
* Get the encoding codec name
* [AUTO-TRANSLATED:f46d430e]
*/
API_EXPORT const char* API_CALL mk_track_codec_name(mk_track track);
/**
*
* Get the bitrate information
* [AUTO-TRANSLATED:de8b48fe]
*/
API_EXPORT int API_CALL mk_track_bit_rate(mk_track track);
/**
* 1: 0
* Get whether the track is ready, 1: ready, 0: not ready
* [AUTO-TRANSLATED:926d1a1a]
*/
API_EXPORT int API_CALL mk_track_ready(mk_track track);
/**
*
* Get the cumulative frame count
* [AUTO-TRANSLATED:c30a45c6]
*/
API_EXPORT uint64_t API_CALL mk_track_frames(mk_track track);
/**
*
* Get the time, in milliseconds
* [AUTO-TRANSLATED:37b0e1f9]
*/
API_EXPORT uint64_t API_CALL mk_track_duration(mk_track track);
@ -93,6 +129,12 @@ API_EXPORT uint64_t API_CALL mk_track_duration(mk_track track);
* @param track track对象
* @param cb frame输出回调
* @param user_data frame输出回调用户指针参数
* Listen for frame output events
* @param track Track object
* @param cb Frame output callback
* @param user_data Frame output callback user pointer parameter
* [AUTO-TRANSLATED:5cbd8347]
*/
API_EXPORT void *API_CALL mk_track_add_delegate(mk_track track, on_mk_frame_out cb, void *user_data);
API_EXPORT void *API_CALL mk_track_add_delegate2(mk_track track, on_mk_frame_out cb, void *user_data, on_user_data_free user_data_free);
@ -101,61 +143,99 @@ API_EXPORT void *API_CALL mk_track_add_delegate2(mk_track track, on_mk_frame_out
* frame输出事件监听
* @param track track对象
* @param tag mk_track_add_delegate返回值
* Cancel the frame output event listener
* @param track Track object
* @param tag Return value of mk_track_add_delegate
* [AUTO-TRANSLATED:83a9fd9f]
*/
API_EXPORT void API_CALL mk_track_del_delegate(mk_track track, void *tag);
/**
* frame到trackapi
* Input frame to track, you usually don't need to call this api
* [AUTO-TRANSLATED:ca3b03e8]
*/
API_EXPORT void API_CALL mk_track_input_frame(mk_track track, mk_frame frame);
/**
* track是否为视频
* Whether the track is video
* [AUTO-TRANSLATED:22573187]
*/
API_EXPORT int API_CALL mk_track_is_video(mk_track track);
/**
*
* Get the video width
* [AUTO-TRANSLATED:06a849c6]
*/
API_EXPORT int API_CALL mk_track_video_width(mk_track track);
/**
*
* Get the video height
* [AUTO-TRANSLATED:27b5ed6e]
*/
API_EXPORT int API_CALL mk_track_video_height(mk_track track);
/**
*
* Get the video frame rate
* [AUTO-TRANSLATED:3c19a388]
*/
API_EXPORT int API_CALL mk_track_video_fps(mk_track track);
/**
*
* Get the cumulative number of video keyframes
* [AUTO-TRANSLATED:0e70e666]
*/
API_EXPORT uint64_t API_CALL mk_track_video_key_frames(mk_track track);
/**
* GOP关键帧间隔
* Get the video GOP keyframe interval
* [AUTO-TRANSLATED:ea8d3729]
*/
API_EXPORT int API_CALL mk_track_video_gop_size(mk_track track);
/**
* ()
* Get the cumulative video keyframe interval (milliseconds)
* [AUTO-TRANSLATED:194b1e80]
*/
API_EXPORT int API_CALL mk_track_video_gop_interval_ms(mk_track track);
/**
*
* Get the audio sample rate
* [AUTO-TRANSLATED:bf0e046b]
*/
API_EXPORT int API_CALL mk_track_audio_sample_rate(mk_track track);
/**
*
* Get the number of audio channels
* [AUTO-TRANSLATED:ccb5d776]
*/
API_EXPORT int API_CALL mk_track_audio_channel(mk_track track);
/**
* 16bit
* Get the audio bit depth, usually 16bit
* [AUTO-TRANSLATED:11e36409]
*/
API_EXPORT int API_CALL mk_track_audio_sample_bit(mk_track track);

View File

@ -19,17 +19,24 @@
extern "C" {
#endif
//解码器对象
// 解码器对象 [AUTO-TRANSLATED:14f75955]
// cpp
// Decoder object
typedef struct mk_decoder_t *mk_decoder;
//解码后的frame
// 解码后的frame [AUTO-TRANSLATED:acb96f85]
// Decoded frame
typedef struct mk_frame_pix_t *mk_frame_pix;
//SwsContext的包装
// SwsContext的包装 [AUTO-TRANSLATED:4f7ae38f]
// SwsContext wrapper
typedef struct mk_swscale_t *mk_swscale;
//FFmpeg原始解码帧对象
// FFmpeg原始解码帧对象 [AUTO-TRANSLATED:ed99009b]
// FFmpeg original decoded frame object
typedef struct AVFrame AVFrame;
//FFmpeg编解码器对象
// FFmpeg编解码器对象 [AUTO-TRANSLATED:12b26186]
// FFmpeg codec object
typedef struct AVCodecContext AVCodecContext;
//解码输出回调
// 解码输出回调 [AUTO-TRANSLATED:1a380eed]
// Decode output callback
typedef void(API_CALL *on_mk_decode)(void *user_data, mk_frame_pix frame);
/**
@ -37,6 +44,12 @@ typedef void(API_CALL *on_mk_decode)(void *user_data, mk_frame_pix frame);
* @param track track对象
* @param thread_num 线0
* @return NULL代表失败
* Create decoder
* @param track track object
* @param thread_num Number of decoding threads, 0 for automatic
* @return Returns the decoder object, NULL indicates failure
* [AUTO-TRANSLATED:d01b3192]
*/
API_EXPORT mk_decoder API_CALL mk_decoder_create(mk_track track, int thread_num);
@ -47,6 +60,14 @@ API_EXPORT mk_decoder API_CALL mk_decoder_create(mk_track track, int thread_num)
* @param codec_name_list ffmpeg codec name列表NULL结尾{"libopenh264", "h264_nvdec", NULL};
* ;codec不存在mk_track_codec_id类型不匹配时使codec列表
* @return NULL代表失败
* Create decoder
* @param track track object
* @param thread_num Number of decoding threads, 0 for automatic
* @param codec_name_list Preferred ffmpeg codec name list, ending with NULL, for example: {"libopenh264", "h264_nvdec", NULL};
* The higher the priority in the array, the higher the priority; if the specified codec does not exist, or does not match the mk_track_codec_id type, the internal default codec list will be used
* @return Returns the decoder object, NULL indicates failure
* [AUTO-TRANSLATED:078aba31]
*/
API_EXPORT mk_decoder API_CALL mk_decoder_create2(mk_track track, int thread_num, const char *codec_name_list[]);
@ -54,6 +75,11 @@ API_EXPORT mk_decoder API_CALL mk_decoder_create2(mk_track track, int thread_num
*
* @param ctx
* @param flush_frame
* Destroy decoder
* @param ctx Decoder object
* @param flush_frame Whether to wait for all frames to be decoded successfully
* [AUTO-TRANSLATED:1a4d9663]
*/
API_EXPORT void API_CALL mk_decoder_release(mk_decoder ctx, int flush_frame);
@ -63,11 +89,21 @@ API_EXPORT void API_CALL mk_decoder_release(mk_decoder ctx, int flush_frame);
* @param frame
* @param async
* @param enable_merge slice合并输入到解码器才能解码
* Decode audio and video frames
* @param ctx Decoder
* @param frame Frame object
* @param async Whether to decode asynchronously
* @param enable_merge Whether to merge frame decoding, in some cases, it is necessary to merge slices with the same timestamp into the decoder before decoding
* [AUTO-TRANSLATED:87df4c4d]
*/
API_EXPORT void API_CALL mk_decoder_decode(mk_decoder ctx, mk_frame frame, int async, int enable_merge);
/**
*
* Set the maximum frame cache backlog limit for asynchronous decoding
* [AUTO-TRANSLATED:1e3e413d]
*/
API_EXPORT void API_CALL mk_decoder_set_max_async_frame_size(mk_decoder ctx, size_t size);
@ -76,6 +112,12 @@ API_EXPORT void API_CALL mk_decoder_set_max_async_frame_size(mk_decoder ctx, siz
* @param ctx
* @param cb
* @param user_data
* Set decode output callback
* @param ctx Decoder
* @param cb Callback function
* @param user_data User pointer parameter of the callback function
* [AUTO-TRANSLATED:a90f8764]
*/
API_EXPORT void API_CALL mk_decoder_set_cb(mk_decoder ctx, on_mk_decode cb, void *user_data);
API_EXPORT void API_CALL mk_decoder_set_cb2(mk_decoder ctx, on_mk_decode cb, void *user_data, on_user_data_free user_data_free);
@ -83,6 +125,10 @@ API_EXPORT void API_CALL mk_decoder_set_cb2(mk_decoder ctx, on_mk_decode cb, voi
/**
* FFmpeg原始AVCodecContext对象
* @param ctx
* Get the FFmpeg original AVCodecContext object
* @param ctx Decoder
* [AUTO-TRANSLATED:73ed5496]
*/
API_EXPORT const AVCodecContext* API_CALL mk_decoder_get_context(mk_decoder ctx);
@ -92,12 +138,21 @@ API_EXPORT const AVCodecContext* API_CALL mk_decoder_get_context(mk_decoder ctx)
* mk_frame_pix新引用
* @param frame
* @return
* Create a new reference to the mk_frame_pix decoding frame
* @param frame Original reference
* @return New reference
* [AUTO-TRANSLATED:ca58ab5d]
*/
API_EXPORT mk_frame_pix API_CALL mk_frame_pix_ref(mk_frame_pix frame);
/**
* mk_frame_pix减引用
* @param frame
* Decrease the reference of the decoding frame mk_frame_pix
* @param frame Original reference
* [AUTO-TRANSLATED:1581d0a9]
*/
API_EXPORT void API_CALL mk_frame_pix_unref(mk_frame_pix frame);
@ -105,6 +160,11 @@ API_EXPORT void API_CALL mk_frame_pix_unref(mk_frame_pix frame);
* FFmpeg AVFrame转换为mk_frame_pix
* @param frame FFmpeg AVFrame
* @return mk_frame_pix对象
* Convert from FFmpeg AVFrame to mk_frame_pix
* @param frame FFmpeg AVFrame
* @return mk_frame_pix object
* [AUTO-TRANSLATED:adfb43d5]
*/
API_EXPORT mk_frame_pix API_CALL mk_frame_pix_from_av_frame(AVFrame *frame);
@ -114,6 +174,13 @@ API_EXPORT mk_frame_pix API_CALL mk_frame_pix_from_av_frame(AVFrame *frame);
* @param line_size line size
* @param plane
* @return mk_frame_pix对象
* Create a mk_frame_pix object without memory copy
* @param plane_data Multiple plane data, get its data pointer through mk_buffer_get_data
* @param line_size Plane data line size
* @param plane Number of data planes
* @return mk_frame_pix object
* [AUTO-TRANSLATED:b720d2e2]
*/
API_EXPORT mk_frame_pix API_CALL mk_frame_pix_from_buffer(mk_buffer plane_data[], int line_size[], int plane);
@ -121,6 +188,11 @@ API_EXPORT mk_frame_pix API_CALL mk_frame_pix_from_buffer(mk_buffer plane_data[]
* FFmpeg AVFrame对象
* @param frame mk_frame_pix
* @return FFmpeg AVFrame对象
* Get the FFmpeg AVFrame object
* @param frame Decoded frame mk_frame_pix
* @return FFmpeg AVFrame object
* [AUTO-TRANSLATED:03142bdc]
*/
API_EXPORT AVFrame* API_CALL mk_frame_pix_get_av_frame(mk_frame_pix frame);
@ -132,12 +204,23 @@ API_EXPORT AVFrame* API_CALL mk_frame_pix_get_av_frame(mk_frame_pix frame);
* @param width 0
* @param height 0
* @return SwsContext wrapper
* Create an instance of the ffmpeg SwsContext wrapper
* @param output AVPixelFormat type, AV_PIX_FMT_BGR24==3
* @param width Target width, set to 0, then it is the same as the input
* @param height Target height, set to 0, then it is the same as the input
* @return SwsContext wrapper instance
* [AUTO-TRANSLATED:417474cb]
*/
API_EXPORT mk_swscale mk_swscale_create(int output, int width, int height);
/**
* ffmpeg SwsContext wrapper实例
* @param ctx SwsContext wrapper实例
* Release the ffmpeg SwsContext wrapper instance
* @param ctx SwsContext wrapper instance
* [AUTO-TRANSLATED:8eaaea2f]
*/
API_EXPORT void mk_swscale_release(mk_swscale ctx);
@ -147,6 +230,13 @@ API_EXPORT void mk_swscale_release(mk_swscale ctx);
* @param frame pix frame
* @param out
* @return sws_scale()the height of the output slice
* Use SwsContext to convert pix format
* @param ctx SwsContext wrapper instance
* @param frame pix frame
* @param out Data pointer to store the converted data, the user needs to ensure that the application is applied in advance and the size is sufficient
* @return sws_scale() return value: the height of the output slice
* [AUTO-TRANSLATED:3018afe4]
*/
API_EXPORT int mk_swscale_input_frame(mk_swscale ctx, mk_frame_pix frame, uint8_t *out);
@ -155,6 +245,13 @@ API_EXPORT int mk_swscale_input_frame(mk_swscale ctx, mk_frame_pix frame, uint8_
* @param ctx SwsContext wrapper实例
* @param frame pix frame
* @return pix frame对象使mk_frame_pix_unref销毁
* Use SwsContext to convert pix format
* @param ctx SwsContext wrapper instance
* @param frame pix frame
* @return New pix frame object, needs to be destroyed using mk_frame_pix_unref
* [AUTO-TRANSLATED:5b4e98a3]
*/
API_EXPORT mk_frame_pix mk_swscale_input_frame2(mk_swscale ctx, mk_frame_pix frame);

View File

@ -20,12 +20,19 @@ extern "C" {
/**
* mk api内部malloc的资源
* Release resources allocated by mk api internally
* [AUTO-TRANSLATED:92ecfef5]
*/
API_EXPORT void API_CALL mk_free(void *ptr);
/**
*
* @return 使mk_free
* Get the path of the executable file of this program
* @return File path, need to be mk_free after use
* [AUTO-TRANSLATED:5f1fae7f]
*/
API_EXPORT char* API_CALL mk_util_get_exe_path();
@ -33,12 +40,21 @@ API_EXPORT char* API_CALL mk_util_get_exe_path();
*
* @param relative_path ,null
* @return 使mk_free
* Get the absolute path of the file in the same directory as the executable file of this program
* @param relative_path The path of the file in the same directory, can be null
* @return File path, need to be mk_free after use
* [AUTO-TRANSLATED:80442e8e]
*/
API_EXPORT char* API_CALL mk_util_get_exe_dir(const char *relative_path);
/**
* unix标准的系统时间戳
* @return
* Get the Unix standard system timestamp
* @return Current system timestamp
* [AUTO-TRANSLATED:feddaa5b]
*/
API_EXPORT uint64_t API_CALL mk_util_get_current_millisecond();
@ -46,6 +62,11 @@ API_EXPORT uint64_t API_CALL mk_util_get_current_millisecond();
*
* @param fmt %Y-%m-%d %H:%M:%S
* @return 使mk_free
* Get the time string
* @param fmt Time format, such as %Y-%m-%d %H:%M:%S
* @return Time string, need to be mk_free after use
* [AUTO-TRANSLATED:c5a6c984]
*/
API_EXPORT char* API_CALL mk_util_get_current_time_string(const char *fmt);
@ -54,6 +75,12 @@ API_EXPORT char* API_CALL mk_util_get_current_time_string(const char *fmt);
* @param buf
* @param len
* @return 使mk_free
* Print binary data as string
* @param buf Binary data
* @param len Data length
* @return Printable debug information, need to be mk_free after use
* [AUTO-TRANSLATED:5b76b3a5]
*/
API_EXPORT char* API_CALL mk_util_hex_dump(const void *buf, int len);
@ -62,12 +89,19 @@ typedef struct mk_ini_t *mk_ini;
/**
* ini配置对象
* Create ini configuration object
* [AUTO-TRANSLATED:b8ce40cc]
*/
API_EXPORT mk_ini API_CALL mk_ini_create();
/**
* ini配置
* @return ini配置mk_ini_release释放它
* Return the global default ini configuration
* @return Global default ini configuration, do not use mk_ini_release to release it
* [AUTO-TRANSLATED:057ea031]
*/
API_EXPORT mk_ini API_CALL mk_ini_default();
@ -75,6 +109,11 @@ API_EXPORT mk_ini API_CALL mk_ini_default();
* ini配置文件内容
* @param ini ini对象
* @param str
* Load ini configuration file content
* @param ini Ini object
* @param str Configuration file content
* [AUTO-TRANSLATED:b9366be5]
*/
API_EXPORT void API_CALL mk_ini_load_string(mk_ini ini, const char *str);
@ -82,11 +121,19 @@ API_EXPORT void API_CALL mk_ini_load_string(mk_ini ini, const char *str);
* ini配置文件
* @param ini ini对象
* @param file
* Load ini configuration file
* @param ini Ini object
* @param file Configuration file path
* [AUTO-TRANSLATED:688e0471]
*/
API_EXPORT void API_CALL mk_ini_load_file(mk_ini ini, const char *file);
/**
* ini配置对象
* Destroy ini configuration object
* [AUTO-TRANSLATED:b6286ab8]
*/
API_EXPORT void API_CALL mk_ini_release(mk_ini ini);
@ -95,6 +142,12 @@ API_EXPORT void API_CALL mk_ini_release(mk_ini ini);
* @param ini
* @param key field.key
* @param value
* Add or overwrite configuration item
* @param ini Configuration object
* @param key Configuration name, two-part, such as: field.key
* @param value Configuration value
* [AUTO-TRANSLATED:1b2c8bfa]
*/
API_EXPORT void API_CALL mk_ini_set_option(mk_ini ini, const char *key, const char *value);
API_EXPORT void API_CALL mk_ini_set_option_int(mk_ini ini, const char *key, int value);
@ -104,6 +157,12 @@ API_EXPORT void API_CALL mk_ini_set_option_int(mk_ini ini, const char *key, int
* @param ini
* @param key field.key
* @return NULL
* Get configuration item
* @param ini Configuration object
* @param key Configuration name, two-part, such as: field.key
* @return NULL if the configuration does not exist, otherwise return the configuration value
* [AUTO-TRANSLATED:4df4bc65]
*/
API_EXPORT const char *API_CALL mk_ini_get_option(mk_ini ini, const char *key);
@ -112,6 +171,12 @@ API_EXPORT const char *API_CALL mk_ini_get_option(mk_ini ini, const char *key);
* @param ini
* @param key field.key
* @return 1: 0:
* Delete configuration item
* @param ini Configuration object
* @param key Configuration name, two-part, such as: field.key
* @return 1: Success, 0: The configuration does not exist
* [AUTO-TRANSLATED:dbbbdca3]
*/
API_EXPORT int API_CALL mk_ini_del_option(mk_ini ini, const char *key);
@ -119,6 +184,11 @@ API_EXPORT int API_CALL mk_ini_del_option(mk_ini ini, const char *key);
*
* @param ini
* @return mk_free
* Export to configuration file content
* @param ini Configuration object
* @return Configuration file content string, need to be mk_free after use
* [AUTO-TRANSLATED:94620b68]
*/
API_EXPORT char *API_CALL mk_ini_dump_string(mk_ini ini);
@ -126,19 +196,30 @@ API_EXPORT char *API_CALL mk_ini_dump_string(mk_ini ini);
*
* @param ini
* @param file
* Export configuration file to file
* @param ini Configuration object
* @param file Configuration file path
* [AUTO-TRANSLATED:8fac58af]
*/
API_EXPORT void API_CALL mk_ini_dump_file(mk_ini ini, const char *file);
///////////////////////////////////////////统计/////////////////////////////////////////////
// /////////////////////////////////////////统计///////////////////////////////////////////// [AUTO-TRANSLATED:964becb9]
// /////////////////////////////////////////统计/////////////////////////////////////////////
typedef void(API_CALL *on_mk_get_statistic_cb)(void *user_data, mk_ini ini);
/**
*
* @param ini
* Get memory data statistics
* @param ini Store statistical results
* [AUTO-TRANSLATED:48d8035c]
*/
API_EXPORT void API_CALL mk_get_statistic(on_mk_get_statistic_cb cb, void *user_data, on_user_data_free free_cb);
///////////////////////////////////////////日志/////////////////////////////////////////////
// /////////////////////////////////////////日志///////////////////////////////////////////// [AUTO-TRANSLATED:b1bd4de8]
// /////////////////////////////////////////日志/////////////////////////////////////////////
/**
*
@ -148,10 +229,20 @@ API_EXPORT void API_CALL mk_get_statistic(on_mk_get_statistic_cb cb, void *user_
* @param line __LINE__
* @param fmt printf类型的格式控制字符串
* @param ...
* Print log
* @param level Log level, support 0~4
* @param file __FILE__
* @param function __FUNCTION__
* @param line __LINE__
* @param fmt printf type format control string
* @param ... Variable length parameters
* [AUTO-TRANSLATED:f19956e7]
*/
API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...);
// 以下宏可以替换printf使用
// 以下宏可以替换printf使用 [AUTO-TRANSLATED:73b59437]
// The following macros can replace printf
#define log_printf(lev, ...) mk_log_printf(lev, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define log_trace(...) log_printf(0, ##__VA_ARGS__)
#define log_debug(...) log_printf(1, ##__VA_ARGS__)

View File

@ -93,20 +93,24 @@ API_EXPORT void API_CALL mk_env_init2(int thread_num,
int ssl_is_path,
const char *ssl,
const char *ssl_pwd) {
//确保只初始化一次
// 确保只初始化一次 [AUTO-TRANSLATED:e4b32b0f]
// Ensure initialization only happens once
static onceToken token([&]() {
if (log_mask & LOG_CONSOLE) {
//控制台日志
// 控制台日志 [AUTO-TRANSLATED:5c00e83f]
// Console log
Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", (LogLevel) log_level));
}
if (log_mask & LOG_CALLBACK) {
//广播日志
// 广播日志 [AUTO-TRANSLATED:67556df8]
// Broadcast log
Logger::Instance().add(std::make_shared<EventChannel>("EventChannel", (LogLevel) log_level));
}
if (log_mask & LOG_FILE) {
//日志文件
// 日志文件 [AUTO-TRANSLATED:afacc934]
// Log file
auto channel = std::make_shared<FileChannel>("FileChannel",
log_file_path ? File::absolutePath("", log_file_path) :
exeDir() + "log/", (LogLevel) log_level);
@ -114,15 +118,18 @@ API_EXPORT void API_CALL mk_env_init2(int thread_num,
Logger::Instance().add(channel);
}
//异步日志线程
// 异步日志线程 [AUTO-TRANSLATED:1cc193a1]
// Asynchronous log thread
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
//设置线程数
// 设置线程数 [AUTO-TRANSLATED:22ec5cc9]
// Set thread count
EventPollerPool::setPoolSize(thread_num);
WorkThreadPool::setPoolSize(thread_num);
if (ini && ini[0]) {
//设置配置文件
// 设置配置文件 [AUTO-TRANSLATED:2216856d]
// Set configuration file
if (ini_is_path) {
try {
mINI::Instance().parseFile(ini);
@ -136,7 +143,8 @@ API_EXPORT void API_CALL mk_env_init2(int thread_num,
}
if (ssl && ssl[0]) {
//设置ssl证书
// 设置ssl证书 [AUTO-TRANSLATED:e441027c]
// Set SSL certificate
SSL_Initor::Instance().loadCertificate(ssl, true, ssl_pwd ? ssl_pwd : "", ssl_is_path);
}
});
@ -157,7 +165,8 @@ API_EXPORT void API_CALL mk_set_option(const char *key, const char *val) {
return;
}
mINI::Instance()[key] = val;
//广播配置文件热加载
// 广播配置文件热加载 [AUTO-TRANSLATED:7ae561f3]
// Broadcast configuration file hot reload
NOTICE_EMIT(BroadcastReloadConfigArgs, Broadcast::kBroadcastReloadConfig);
}
@ -226,7 +235,8 @@ API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl) {
API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port){
#ifdef ENABLE_RTPPROXY
try {
//创建rtp 服务器
// 创建rtp 服务器 [AUTO-TRANSLATED:480fda83]
// Create RTP server
rtpServer = std::make_shared<RtpServer>();
rtpServer->start(port);
return rtpServer->getPort();
@ -244,7 +254,8 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port){
API_EXPORT uint16_t API_CALL mk_rtc_server_start(uint16_t port) {
#ifdef ENABLE_WEBRTC
try {
//创建rtc udp服务器
// 创建rtc udp服务器 [AUTO-TRANSLATED:9287972e]
// Create RTC UDP server
rtcServer_udp = std::make_shared<UdpServer>();
rtcServer_udp->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
if (!buf) {
@ -252,13 +263,15 @@ API_EXPORT uint16_t API_CALL mk_rtc_server_start(uint16_t port) {
}
auto new_poller = WebRtcSession::queryPoller(buf);
if (!new_poller) {
//该数据对应的webrtc对象未找到丢弃之
// 该数据对应的webrtc对象未找到丢弃之 [AUTO-TRANSLATED:d401f8cb]
// The WebRTC object corresponding to this data was not found, discard it
return Socket::Ptr();
}
return Socket::createSocket(new_poller, false);
});
rtcServer_udp->start<WebRtcSession>(port);
//创建rtc tcp服务器
// 创建rtc tcp服务器 [AUTO-TRANSLATED:1eefd92f]
// Create RTC TCP server
rtcServer_tcp = std::make_shared<TcpServer>();
rtcServer_tcp->start<WebRtcSession>(rtcServer_udp->getPort());
return rtcServer_udp->getPort();
@ -328,7 +341,8 @@ API_EXPORT uint16_t API_CALL mk_srt_server_start(uint16_t port) {
}
auto new_poller = SRT::SrtSession::queryPoller(buf);
if (!new_poller) {
//握手第一阶段
// 握手第一阶段 [AUTO-TRANSLATED:6b3abcd4]
// Handshake stage one
return Socket::createSocket(poller, false);
}
return Socket::createSocket(new_poller, false);

View File

@ -228,7 +228,8 @@ API_EXPORT mk_track API_CALL mk_media_source_get_track(const mk_media_source ctx
API_EXPORT float API_CALL mk_media_source_get_track_loss(const mk_media_source ctx, const mk_track track) {
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
// rtp推流只有一个统计器但是可能有多个track如果短时间多次获取间隔丢包率第二次会获取为-1
// rtp推流只有一个统计器但是可能有多个track如果短时间多次获取间隔丢包率第二次会获取为-1 [AUTO-TRANSLATED:b30fec2c]
// RTP streaming has only one statistics object, but there may be multiple tracks. If the packet loss rate is obtained multiple times in a short period, the second time will be obtained as -1
return src->getLossRate((*((Track::Ptr *)track))->getTrackType());
}
@ -307,13 +308,53 @@ API_EXPORT void API_CALL mk_media_source_start_send_rtp2(const mk_media_source c
args.dst_url = dst_url;
args.dst_port = dst_port;
args.ssrc = ssrc;
args.close_delay_ms = 30 * 1000;
args.con_type = (mediakit::MediaSourceEvent::SendRtpArgs::ConType)con_type;
std::shared_ptr<void> ptr(user_data, user_data_free ? user_data_free : [](void *) {});
src->startSendRtp(args, [cb, ptr](uint16_t local_port, const SockException &ex){
if (cb) {
cb(ptr.get(), local_port, ex.getErrCode(), ex.what());
}
src->getOwnerPoller()->async([=]() mutable {
src->startSendRtp(args, [cb, ptr](uint16_t local_port, const SockException &ex) {
if (cb) {
cb(ptr.get(), local_port, ex.getErrCode(), ex.what());
}
});
});
}
API_EXPORT void API_CALL mk_media_source_start_send_rtp3(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, mk_ini options, on_mk_media_source_send_rtp_result cb,void *user_data) {
mk_media_source_start_send_rtp4(ctx, dst_url, dst_port, ssrc, con_type, options, cb, user_data, nullptr);
}
API_EXPORT void API_CALL mk_media_source_start_send_rtp4(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, mk_ini options, on_mk_media_source_send_rtp_result cb,void *user_data, on_user_data_free user_data_free) {
assert(ctx && dst_url && ssrc);
MediaSource *src = (MediaSource *)ctx;
MediaSourceEvent::SendRtpArgs args;
args.dst_url = dst_url;
args.dst_port = dst_port;
args.ssrc = ssrc;
args.con_type = (mediakit::MediaSourceEvent::SendRtpArgs::ConType)con_type;
auto ini_ptr = (mINI *)options;
args.src_port = (*ini_ptr)["src_port"].empty() ? 0 : (*ini_ptr)["src_port"].as<int>();
args.ssrc_multi_send = (*ini_ptr)["ssrc_multi_send"].empty() ? false : (*ini_ptr)["ssrc_multi_send"].as<bool>();
args.pt = (*ini_ptr)["pt"].empty() ? 96 : (*ini_ptr)["pt"].as<int>();
args.data_type = (*ini_ptr)["data_type"].empty() ? MediaSourceEvent::SendRtpArgs::DataType::kRtpPS:(MediaSourceEvent::SendRtpArgs::DataType)(*ini_ptr)["data_type"].as<int>();
args.only_audio = (*ini_ptr)["only_audio"].empty() ? false : (*ini_ptr)["only_audio"].as<bool>();
args.udp_rtcp_timeout = (*ini_ptr)["udp_rtcp_timeout"].empty() ? false : (*ini_ptr)["udp_rtcp_timeout"].as<bool>();
args.recv_stream_id = (*ini_ptr)["recv_stream_id"];
args.recv_stream_app = src->getMediaTuple().app.c_str();
args.recv_stream_vhost = src->getMediaTuple().vhost.c_str();
args.close_delay_ms = (*ini_ptr)["close_delay_ms"].empty() ? 0 : (*ini_ptr)["close_delay_ms"].as<int>();
args.rtcp_timeout_ms = (*ini_ptr)["rtcp_timeout_ms"].empty() ? 30000 : (*ini_ptr)["rtcp_timeout_ms"].as<int>();
args.rtcp_send_interval_ms = (*ini_ptr)["rtcp_send_interval_ms"].empty() ? 5000 : (*ini_ptr)["rtcp_send_interval_ms"].as<int>();
std::shared_ptr<void> ptr(
user_data, user_data_free ? user_data_free : [](void *) {});
src->getOwnerPoller()->async([=]() mutable {
src->startSendRtp(args, [cb, ptr](uint16_t local_port, const SockException &ex) {
if (cb) {
cb(ptr.get(), local_port, ex.getErrCode(), ex.what());
}
});
});
}
@ -568,7 +609,8 @@ API_EXPORT void API_CALL mk_rtc_send_datachannel(const mk_rtc_transport ctx, uin
std::string msg_str(msg, len);
std::weak_ptr<WebRtcTransport> weak_trans = transport->shared_from_this();
transport->getPoller()->async([streamId, ppid, msg_str, weak_trans]() {
// 切换线程后再操作
// 切换线程后再操作 [AUTO-TRANSLATED:12d77fca]
// Operate after switching threads
if (auto trans = weak_trans.lock()) {
trans->sendDatachannel(streamId, ppid, msg_str.c_str(), msg_str.size());
}

View File

@ -45,12 +45,14 @@ H264Splitter::~H264Splitter() {
ssize_t H264Splitter::onRecvHeader(const char *data, size_t len) {
auto frame = Factory::getFrameFromPtr(_h265 ? CodecH265 : CodecH264, (char *)data, len, 0, 0);
if (_have_decode_frame && (frame->decodeAble() || frame->configFrame())) {
// 缓存中存在可解码帧且下一帧是可解码帧或者配置帧那么flush缓存
// 缓存中存在可解码帧且下一帧是可解码帧或者配置帧那么flush缓存 [AUTO-TRANSLATED:2c52093b]
// Flush the cache if there are decodable frames in the cache and the next frame is a decodable frame or a configuration frame.
_cb(_buffer.data(), _buffer.size());
_buffer.assign(data, len);
_have_decode_frame = frame->decodeAble();
} else {
// 还需要缓存
// 还需要缓存 [AUTO-TRANSLATED:a4dc19cb]
// Still need to cache
_buffer.append(data, len);
_have_decode_frame = _have_decode_frame || frame->decodeAble();
}
@ -59,10 +61,12 @@ ssize_t H264Splitter::onRecvHeader(const char *data, size_t len) {
const char *H264Splitter::onSearchPacketTail(const char *data, size_t len) {
for (size_t i = 2; len > 2 && i < len - 2; ++i) {
//判断0x00 00 01
// 判断0x00 00 01 [AUTO-TRANSLATED:afa3d4c2]
// Determine if it is 0x00 00 01
if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
if (i > 0 && data[i - 1] == 0) {
//找到0x00 00 00 01
// 找到0x00 00 00 01 [AUTO-TRANSLATED:96a10021]
// Find 0x00 00 00 01
return data + i - 1;
}
return data + i;

View File

@ -22,7 +22,8 @@ public:
using Ptr = std::shared_ptr<MediaHelper>;
MediaHelper(const char *vhost, const char *app, const char *stream, float duration, const ProtocolOption &option) {
_poller = EventPollerPool::Instance().getPoller();
// 在poller线程中创建DevChannel(MultiMediaSourceMuxer)对象,确保严格的线程安全限制
// 在poller线程中创建DevChannel(MultiMediaSourceMuxer)对象,确保严格的线程安全限制 [AUTO-TRANSLATED:d5063d7a]
// Create a DevChannel (MultiMediaSourceMuxer) object in the poller thread to ensure strict thread safety restrictions
auto tuple = MediaTuple{vhost, app, stream};
_poller->sync([&]() { _channel = std::make_shared<DevChannel>(tuple, duration, option); });
}
@ -59,14 +60,17 @@ public:
}
protected:
// 通知其停止推流
// 通知其停止推流 [AUTO-TRANSLATED:d69d10d8]
// Notify it to stop streaming
bool close(MediaSource &sender) override {
if (!_on_close) {
//未设置回调,没法关闭
// 未设置回调,没法关闭 [AUTO-TRANSLATED:2c1423fe]
// No callback is set, so it cannot be closed
WarnL << "请使用mk_media_set_on_close函数设置回调函数!";
return false;
}
//请在回调中调用mk_media_release函数释放资源,否则MediaSource::close()操作不会生效
// 请在回调中调用mk_media_release函数释放资源,否则MediaSource::close()操作不会生效 [AUTO-TRANSLATED:da067eb0]
// Please call the mk_media_release function to release resources in the callback, otherwise the MediaSource::close() operation will not take effect
_on_close(_on_close_data.get());
WarnL << "close media: " << sender.getUrl();
return true;
@ -79,7 +83,8 @@ protected:
return _on_seek(_on_seek_data.get(), stamp);
}
// 通知暂停或恢复
// 通知暂停或恢复 [AUTO-TRANSLATED:ee3c219f]
// Notify pause or resume
bool pause(MediaSource &sender, bool pause) override {
if (!_on_pause) {
return false;
@ -87,7 +92,8 @@ protected:
return _on_pause(_on_pause_data.get(), pause);
}
//通知倍数播放
// 通知倍数播放 [AUTO-TRANSLATED:12e66e3f]
// Notify playback speed
bool speed(MediaSource &sender, float speed) override {
if (!_on_speed) {
return false;
@ -295,9 +301,11 @@ API_EXPORT void API_CALL mk_media_start_send_rtp2(mk_media ctx, const char *dst_
args.dst_url = dst_url;
args.dst_port = dst_port;
args.ssrc = ssrc;
args.close_delay_ms = 30 * 1000;
args.con_type = (mediakit::MediaSourceEvent::SendRtpArgs::ConType)con_type;
// sender参数无用
// sender参数无用 [AUTO-TRANSLATED:21590ae5]
// The sender parameter is useless
auto ref = *obj;
std::shared_ptr<void> ptr(user_data, user_data_free ? user_data_free : [](void *) {});
(*obj)->getChannel()->getOwnerPoller(MediaSource::NullMediaSource())->async([args, ref, cb, ptr]() {
@ -309,10 +317,51 @@ API_EXPORT void API_CALL mk_media_start_send_rtp2(mk_media ctx, const char *dst_
});
}
API_EXPORT void API_CALL mk_media_start_send_rtp3(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, mk_ini options, on_mk_media_send_rtp_result cb, void *user_data) {
mk_media_start_send_rtp4(ctx, dst_url, dst_port, ssrc, con_type,options, cb, user_data, nullptr);
}
API_EXPORT void API_CALL mk_media_start_send_rtp4(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, mk_ini options, on_mk_media_send_rtp_result cb, void *user_data,on_user_data_free user_data_free) {
assert(ctx && dst_url && ssrc);
MediaHelper::Ptr *obj = (MediaHelper::Ptr *)ctx;
MediaSourceEvent::SendRtpArgs args;
args.dst_url = dst_url;
args.dst_port = dst_port;
args.ssrc = ssrc;
args.con_type = (mediakit::MediaSourceEvent::SendRtpArgs::ConType)con_type;
auto ini_ptr = (mINI *)options;
args.src_port = (*ini_ptr)["src_port"].empty() ? 0 : (*ini_ptr)["src_port"].as<int>();
args.ssrc_multi_send = (*ini_ptr)["ssrc_multi_send"].empty() ? false : (*ini_ptr)["ssrc_multi_send"].as<bool>();
args.pt = (*ini_ptr)["pt"].empty() ? 96 : (*ini_ptr)["pt"].as<int>();
args.data_type = (*ini_ptr)["data_type"].empty() ? MediaSourceEvent::SendRtpArgs::DataType::kRtpPS
: (MediaSourceEvent::SendRtpArgs::DataType)(*ini_ptr)["data_type"].as<int>();
args.only_audio = (*ini_ptr)["only_audio"].empty() ? false : (*ini_ptr)["only_audio"].as<bool>();
args.udp_rtcp_timeout = (*ini_ptr)["udp_rtcp_timeout"].empty() ? false : (*ini_ptr)["udp_rtcp_timeout"].as<bool>();
args.recv_stream_id =(*ini_ptr)["recv_stream_id"];
args.recv_stream_app =obj->get()->getChannel()->getMediaTuple().app.c_str();
args.recv_stream_vhost = obj->get()->getChannel()->getMediaTuple().vhost.c_str();
args.close_delay_ms = (*ini_ptr)["close_delay_ms"].empty() ? 30000 : (*ini_ptr)["close_delay_ms"].as<int>();
args.rtcp_timeout_ms = (*ini_ptr)["rtcp_timeout_ms"].empty() ? 30000 : (*ini_ptr)["rtcp_timeout_ms"].as<int>();
args.rtcp_send_interval_ms = (*ini_ptr)["rtcp_send_interval_ms"].empty() ? 5000 : (*ini_ptr)["rtcp_send_interval_ms"].as<int>();
// sender参数无用 [AUTO-TRANSLATED:21590ae5]
// The sender parameter is useless
auto ref = *obj;
std::shared_ptr<void> ptr(
user_data, user_data_free ? user_data_free : [](void *) {});
(*obj)->getChannel()->getOwnerPoller(MediaSource::NullMediaSource())->async([args, ref, cb, ptr]() {
ref->getChannel()->startSendRtp(MediaSource::NullMediaSource(), args, [cb, ptr](uint16_t local_port, const SockException &ex) {
if (cb) {
cb(ptr.get(), local_port, ex.getErrCode(), ex.what());
}
});
});
}
API_EXPORT void API_CALL mk_media_stop_send_rtp(mk_media ctx, const char *ssrc) {
assert(ctx);
MediaHelper::Ptr *obj = (MediaHelper::Ptr *)ctx;
// sender参数无用
// sender参数无用 [AUTO-TRANSLATED:21590ae5]
// The sender parameter is useless
auto ref = *obj;
string ssrc_str = ssrc ? ssrc : "";
(*obj)->getChannel()->getOwnerPoller(MediaSource::NullMediaSource())->async([ref, ssrc_str]() {

View File

@ -57,14 +57,16 @@ public:
void onEvent(bool is_shutdown, const SockException &ex){
lock_guard<recursive_mutex> lck(_mtx);
if (is_shutdown) {
//播放中断
// 播放中断 [AUTO-TRANSLATED:9b841156]
// Play interrupted
if (_on_shutdown) {
_on_shutdown(_on_shutdown_data.get(), ex.getErrCode(), ex.what(), nullptr, 0);
}
return;
}
//播放结果
// 播放结果 [AUTO-TRANSLATED:15f1e8fe]
// Play result
if (_on_play) {
auto cpp_tracks = _player->getTracks(false);
mk_track tracks[TrackMax] = {nullptr};
@ -118,7 +120,8 @@ API_EXPORT void API_CALL mk_player_set_option(mk_player ctx,const char* key,cons
auto player = obj.getPlayer();
string key_str(key), val_str(val);
player->getPoller()->async([key_str,val_str,player](){
//切换线程后再操作
// 切换线程后再操作 [AUTO-TRANSLATED:b8f01c71]
// Operate after switching threads
(*player)[key_str] = val_str;
});
}
@ -128,7 +131,8 @@ API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url) {
auto player = obj.getPlayer();
string url_str(url);
player->getPoller()->async([url_str,player](){
//切换线程后再操作
// 切换线程后再操作 [AUTO-TRANSLATED:b8f01c71]
// Operate after switching threads
player->play(url_str);
});
}
@ -138,7 +142,8 @@ API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause) {
MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx);
auto player = obj.getPlayer();
player->getPoller()->async([pause,player](){
//切换线程后再操作
// 切换线程后再操作 [AUTO-TRANSLATED:b8f01c71]
// Operate after switching threads
player->pause(pause);
});
}
@ -148,7 +153,8 @@ API_EXPORT void API_CALL mk_player_speed(mk_player ctx, float speed) {
MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *) ctx);
auto player = obj.getPlayer();
player->getPoller()->async([speed, player]() {
//切换线程后再操作
// 切换线程后再操作 [AUTO-TRANSLATED:b8f01c71]
// Operate after switching threads
player->speed(speed);
});
}
@ -158,7 +164,8 @@ API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress) {
MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx);
auto player = obj.getPlayer();
player->getPoller()->async([progress,player](){
//切换线程后再操作
// 切换线程后再操作 [AUTO-TRANSLATED:b8f01c71]
// Operate after switching threads
player->seekTo(progress);
});
}
@ -168,7 +175,8 @@ API_EXPORT void API_CALL mk_player_seekto_pos(mk_player ctx, int seek_pos) {
MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *) ctx);
auto player = obj.getPlayer();
player->getPoller()->async([seek_pos, player]() {
//切换线程后再操作
// 切换线程后再操作 [AUTO-TRANSLATED:b8f01c71]
// Operate after switching threads
player->seekTo((uint32_t) seek_pos);
});
}

View File

@ -53,7 +53,8 @@ API_EXPORT void API_CALL mk_proxy_player_set_option(mk_proxy_player ctx, const c
PlayerProxy::Ptr &obj = *((PlayerProxy::Ptr *) ctx);
std::string key_str(key), val_str(val);
obj->getPoller()->async([obj,key_str,val_str](){
//切换线程再操作
// 切换线程再操作 [AUTO-TRANSLATED:b78259f9]
// Switch threads and then operate
(*obj)[key_str] = val_str;
});
}
@ -63,7 +64,8 @@ API_EXPORT void API_CALL mk_proxy_player_play(mk_proxy_player ctx, const char *u
PlayerProxy::Ptr &obj = *((PlayerProxy::Ptr *) ctx);
std::string url_str(url);
obj->getPoller()->async([obj,url_str](){
//切换线程再操作
// 切换线程再操作 [AUTO-TRANSLATED:b78259f9]
// Switch threads and then operate
obj->play(url_str);
});
}
@ -77,7 +79,8 @@ API_EXPORT void API_CALL mk_proxy_player_set_on_close2(mk_proxy_player ctx, on_m
PlayerProxy::Ptr &obj = *((PlayerProxy::Ptr *)ctx);
std::shared_ptr<void> ptr(user_data, user_data_free ? user_data_free : [](void *) {});
obj->getPoller()->async([obj, cb, ptr]() {
// 切换线程再操作
// 切换线程再操作 [AUTO-TRANSLATED:bae49fee]
// Switch threads and then operate
obj->setOnClose([cb, ptr](const SockException &ex) {
if (cb) {
cb(ptr.get(), ex.getErrCode(), ex.what(), ex.getCustomCode());
@ -91,7 +94,8 @@ API_EXPORT void API_CALL mk_proxy_player_set_on_play_result(mk_proxy_player ctx,
PlayerProxy::Ptr &obj = *((PlayerProxy::Ptr *)ctx);
std::shared_ptr<void> ptr(user_data, user_data_free ? user_data_free : [](void *) {});
obj->getPoller()->async([obj, cb, ptr]() {
// 切换线程再操作
// 切换线程再操作 [AUTO-TRANSLATED:bae49fee]
// Switch threads and then operate
obj->setPlayCallbackOnce([cb, ptr](const SockException &ex) {
if (cb) {
cb(ptr.get(), ex.getErrCode(), ex.what(), ex.getCustomCode());

View File

@ -39,7 +39,8 @@ API_EXPORT void API_CALL mk_pusher_set_option(mk_pusher ctx, const char *key, co
MediaPusher::Ptr &obj = *((MediaPusher::Ptr *)ctx);
std::string key_str(key), val_str(val);
obj->getPoller()->async([obj,key_str,val_str](){
//切换线程再操作
// 切换线程再操作 [AUTO-TRANSLATED:b78259f9]
// Switch threads and then operate
(*obj)[key_str] = val_str;
});
}
@ -49,7 +50,8 @@ API_EXPORT void API_CALL mk_pusher_publish(mk_pusher ctx,const char *url){
MediaPusher::Ptr &obj = *((MediaPusher::Ptr *)ctx);
std::string url_str(url);
obj->getPoller()->async([obj,url_str](){
//切换线程再操作
// 切换线程再操作 [AUTO-TRANSLATED:b78259f9]
// Switch threads and then operate
obj->publish(url_str);
});
}
@ -63,7 +65,8 @@ API_EXPORT void API_CALL mk_pusher_set_on_result2(mk_pusher ctx, on_mk_push_even
MediaPusher::Ptr &obj = *((MediaPusher::Ptr *)ctx);
std::shared_ptr<void> ptr(user_data, user_data_free ? user_data_free : [](void *) {});
obj->getPoller()->async([obj, cb, ptr]() {
// 切换线程再操作
// 切换线程再操作 [AUTO-TRANSLATED:bae49fee]
// Switch threads and then operate
obj->setOnPublished([cb, ptr](const SockException &ex) { cb(ptr.get(), ex.getErrCode(), ex.what()); });
});
}
@ -77,7 +80,8 @@ API_EXPORT void API_CALL mk_pusher_set_on_shutdown2(mk_pusher ctx, on_mk_push_ev
MediaPusher::Ptr &obj = *((MediaPusher::Ptr *)ctx);
std::shared_ptr<void> ptr(user_data, user_data_free ? user_data_free : [](void *) {});
obj->getPoller()->async([obj, cb, ptr]() {
// 切换线程再操作
// 切换线程再操作 [AUTO-TRANSLATED:bae49fee]
// Switch threads and then operate
obj->setOnShutdown([cb, ptr](const SockException &ex) { cb(ptr.get(), ex.getErrCode(), ex.what()); });
});
}

View File

@ -37,7 +37,8 @@ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *v
}
}
///////////////////////////////////////////hls/mp4录制/////////////////////////////////////////////
// /////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// [AUTO-TRANSLATED:99c61c68]
// /////////////////////////////////////////hls/mp4 recording/////////////////////////////////////////////
static inline bool isRecording(Recorder::type type, const string &vhost, const string &app, const string &stream_id){
auto src = MediaSource::find(vhost, app, stream_id);

View File

@ -242,11 +242,13 @@ API_EXPORT uint16_t API_CALL mk_tcp_server_start(uint16_t port, mk_tcp_type type
s_tcp_server[type]->start<SessionWithSSL<SessionForC> >(port);
break;
case mk_type_ws:
//此处你也可以修改WebSocketHeader::BINARY
// 此处你也可以修改WebSocketHeader::BINARY [AUTO-TRANSLATED:706abaab]
// You can also modify WebSocketHeader::BINARY here
s_tcp_server[type]->start<WebSocketSession<SessionForC, HttpSession, WebSocketHeader::TEXT> >(port);
break;
case mk_type_wss:
//此处你也可以修改WebSocketHeader::BINARY
// 此处你也可以修改WebSocketHeader::BINARY [AUTO-TRANSLATED:706abaab]
// You can also modify WebSocketHeader::BINARY here
s_tcp_server[type]->start<WebSocketSession<SessionForC, HttpsSession, WebSocketHeader::TEXT> >(port);
break;
default:
@ -307,10 +309,12 @@ TcpClientForC::Ptr *mk_tcp_client_create_l(mk_tcp_client_events *events, mk_tcp_
case mk_type_ssl:
return (TcpClientForC::Ptr *)new std::shared_ptr<SessionWithSSL<TcpClientForC> >(new SessionWithSSL<TcpClientForC>(events));
case mk_type_ws:
//此处你也可以修改WebSocketHeader::BINARY
// 此处你也可以修改WebSocketHeader::BINARY [AUTO-TRANSLATED:706abaab]
// You can also modify WebSocketHeader::BINARY here
return (TcpClientForC::Ptr *)new std::shared_ptr<WebSocketClient<TcpClientForC, WebSocketHeader::TEXT, false> >(new WebSocketClient<TcpClientForC, WebSocketHeader::TEXT, false>(events));
case mk_type_wss:
//此处你也可以修改WebSocketHeader::BINARY
// 此处你也可以修改WebSocketHeader::BINARY [AUTO-TRANSLATED:706abaab]
// You can also modify WebSocketHeader::BINARY
return (TcpClientForC::Ptr *)new std::shared_ptr<WebSocketClient<TcpClientForC, WebSocketHeader::TEXT, true> >(new WebSocketClient<TcpClientForC, WebSocketHeader::TEXT, true>(events));
default:
return nullptr;

View File

@ -134,7 +134,8 @@ API_EXPORT void API_CALL mk_timer_release(mk_timer ctx){
class WorkThreadPoolForC : public TaskExecutorGetterImp {
public:
WorkThreadPoolForC(const char *name, size_t n_thread, int priority) {
//最低优先级
// 最低优先级 [AUTO-TRANSLATED:cd1f0dbc]
// Lowest priority
addPoller(name, n_thread, (ThreadPool::Priority) priority, false);
}

View File

@ -63,7 +63,8 @@ API_EXPORT mk_ini API_CALL mk_ini_default() {
static void emit_ini_file_reload(mk_ini ini) {
if (ini == mk_ini_default()) {
// 广播配置文件热加载
// 广播配置文件热加载 [AUTO-TRANSLATED:86a0c1be]
// Broadcast configuration hot reload
NOTICE_EMIT(BroadcastReloadConfigArgs, Broadcast::kBroadcastReloadConfig);
}
}
@ -223,7 +224,8 @@ API_EXPORT void API_CALL mk_get_statistic(on_mk_get_statistic_cb func, void *use
(*obj).emplace(prefix + pr.first, std::move(pr.second));
}
}
// 触发回调
// 触发回调 [AUTO-TRANSLATED:ae2ff258]
// Trigger callback
cb(*obj);
});

View File

@ -36,7 +36,8 @@ static void on_h264_frame(void *user_data, mk_h264_splitter splitter, const char
}
//按照json转义规则转义webrtc answer sdp
// 按照json转义规则转义webrtc answer sdp [AUTO-TRANSLATED:b9373d68]
// Escape the webrtc answer sdp according to json escape rules
static char *escape_string(const char *ptr){
char *escaped = malloc(2 * strlen(ptr));
char *ptr_escaped = escaped;
@ -108,8 +109,16 @@ static void on_mk_webrtc_get_answer_sdp_func(void *user_data, const char *answer
* @param invoker invoker返回http回复
* @param consumed 1
* @param sender http客户端相关信息
* Receive http api request broadcast (including GET/POST)
* @param parser http request content object
* @param invoker Execute this invoker to return http reply
* @param consumed Set to 1 if we want to handle this event
* @param sender http client related information
* [AUTO-TRANSLATED:39435e52]
*/
//测试url : http://127.0.0.1/api/test
// 测试url : http://127.0.0.1/api/test [AUTO-TRANSLATED:4776d7a3]
// Test url: http://127.0.0.1/api/test
void API_CALL on_mk_http_request(const mk_parser parser,
const mk_http_response_invoker invoker,
int *consumed,
@ -119,7 +128,8 @@ void API_CALL on_mk_http_request(const mk_parser parser,
*consumed = 1;
if (strcmp(url, "/index/api/webrtc") == 0) {
//拦截api: /index/api/webrtc
// 拦截api: /index/api/webrtc [AUTO-TRANSLATED:1db21d1c]
// Intercept api: /index/api/webrtc
char rtc_url[1024];
snprintf(rtc_url, sizeof(rtc_url), "rtc://%s/%s/%s?%s", mk_parser_get_header(parser, "Host"),
mk_parser_get_url_param(parser, "app"), mk_parser_get_url_param(parser, "stream"),
@ -179,7 +189,8 @@ int main(int argc, char *argv[]) {
}
mk_media media = mk_media_create("__defaultVhost__", "live", "test", 0, 0, 0);
//h264的codec
// h264的codec [AUTO-TRANSLATED:940c6a32]
// h264 codec
//mk_media_init_video(media, 0, 0, 0, 0, 2 * 104 * 1024);
codec_args v_args = {0};
mk_track v_track = mk_track_create(MKCodecH264, &v_args);
@ -187,7 +198,8 @@ int main(int argc, char *argv[]) {
mk_media_init_complete(media);
mk_track_unref(v_track);
//创建h264分帧器
// 创建h264分帧器 [AUTO-TRANSLATED:5775837d]
// Create h264 frame splitter
mk_h264_splitter splitter = mk_h264_splitter_create(on_h264_frame, media, 0);
signal(SIGINT, s_on_exit);// 设置退出信号
@ -197,7 +209,8 @@ int main(int argc, char *argv[]) {
if (size > 0) {
mk_h264_splitter_input_data(splitter, buf, size);
} else {
//文件读完了,重新开始
// 文件读完了,重新开始 [AUTO-TRANSLATED:035fb238]
// File read finished, start again
fseek(fp, 0, SEEK_SET);
}
}

View File

@ -69,23 +69,27 @@ void API_CALL on_regist(void *user_data, mk_media_source sender, int regist) {
Context *ptr = (Context *)user_data;
const char *schema = mk_media_source_get_schema(sender);
if (strstr(ptr->url, schema) != ptr->url) {
// 协议匹配失败
// 协议匹配失败 [AUTO-TRANSLATED:436784d0]
// Protocol matching failed
return;
}
if (!regist) {
// 注销
// 注销 [AUTO-TRANSLATED:ebc5be28]
// Log out
if (ptr->pusher) {
mk_pusher_release(ptr->pusher);
ptr->pusher = NULL;
}
} else {
// 注册
// 注册 [AUTO-TRANSLATED:e2df30a6]
// Register
if (!ptr->pusher) {
ptr->pusher = mk_pusher_create_src(sender);
mk_pusher_set_on_result2(ptr->pusher, on_push_result, ptr, NULL);
mk_pusher_set_on_shutdown2(ptr->pusher, on_push_shutdown, ptr, NULL);
// 开始推流
// 开始推流 [AUTO-TRANSLATED:df3972ff]
// Start streaming
mk_pusher_publish(ptr->pusher, ptr->url);
}
}
@ -115,7 +119,8 @@ int main(int argc, char *argv[]) {
}
mk_media media = mk_media_create("__defaultVhost__", "live", "test", 0, 0, 0);
// h264的codec
// h264的codec [AUTO-TRANSLATED:e840179e]
// h264 codec
codec_args v_args = { 0 };
mk_track v_track = mk_track_create(MKCodecH264, &v_args);
mk_media_init_track(media, v_track);
@ -128,7 +133,8 @@ int main(int argc, char *argv[]) {
mk_media_set_on_regist2(media, on_regist, ctx, release_context);
// 创建h264分帧器
// 创建h264分帧器 [AUTO-TRANSLATED:72254159]
// Create h264 frame splitter
mk_h264_splitter splitter = mk_h264_splitter_create(on_h264_frame, media, 0);
signal(SIGINT, s_on_exit); // 设置退出信号
signal(SIGTERM, s_on_exit); // 设置退出信号
@ -139,7 +145,8 @@ int main(int argc, char *argv[]) {
if (size > 0) {
mk_h264_splitter_input_data(splitter, buf, size);
} else {
// 文件读完了,重新开始
// 文件读完了,重新开始 [AUTO-TRANSLATED:ffffe75c]
// File read finished, start again
fseek(fp, 0, SEEK_SET);
}
}

View File

@ -52,7 +52,8 @@ int main(int argc, char *argv[]) {
mk_http_requester_set_cb(requester, on_requester_complete, &ctx);
mk_http_requester_start(requester, "http://www.baidu.com/baidu", 10);
//等待http请求完毕
// 等待http请求完毕 [AUTO-TRANSLATED:0c1d7dea]
// Wait for the HTTP request to complete
mk_sem_wait(sem);
mk_sem_release(sem);

View File

@ -33,14 +33,16 @@ void API_CALL on_frame_decode(void *user_data, mk_frame_pix frame) {
int align = 32;
size_t pixel_size = 3;
size_t raw_linesize = w * pixel_size;
// 对齐后的宽度
// 对齐后的宽度 [AUTO-TRANSLATED:f9bfe888]
// Aligned width
size_t aligned_linesize = (raw_linesize + align - 1) & ~(align - 1);
size_t total_size = aligned_linesize * h;
uint8_t* brg24 = malloc(total_size);
mk_swscale_input_frame(ctx->swscale, frame, brg24);
free(brg24);
#else
//todo 此处转换为opencv对象
// todo 此处转换为opencv对象 [AUTO-TRANSLATED:37358ee1]
// todo Convert to opencv object here
cv::Mat *mat = new cv::Mat();
mat->create(h, w, CV_8UC3);
mk_swscale_input_frame(ctx->swscale, frame, (uint8_t *) mat->data);
@ -60,7 +62,8 @@ void API_CALL on_mk_play_event_func(void *user_data, int err_code, const char *e
log_info("got video track: %s", mk_track_codec_name(tracks[i]));
ctx->video_decoder = mk_decoder_create(tracks[i], 0);
mk_decoder_set_cb(ctx->video_decoder, on_frame_decode, user_data);
//监听track数据回调
// 监听track数据回调 [AUTO-TRANSLATED:8295ebf6]
// Listen to track data callback
mk_track_add_delegate(tracks[i], on_track_frame_out, user_data);
}
}

View File

@ -65,7 +65,8 @@ void API_CALL on_mk_media_source_regist_func(void *user_data, mk_media_source se
Context *ctx = (Context *) user_data;
const char *schema = mk_media_source_get_schema(sender);
if (strncmp(schema, ctx->push_url, strlen(schema)) == 0) {
//判断是否为推流协议相关的流注册或注销事件
// 判断是否为推流协议相关的流注册或注销事件 [AUTO-TRANSLATED:00a88a17]
// Determine if it is a stream registration or deregistration event related to the streaming protocol
release_pusher(&(ctx->pusher));
if (regist) {
ctx->pusher = mk_pusher_create_src(sender);
@ -131,17 +132,20 @@ int main(int argc, char *argv[]){
return -1;
}
//可以通过
// 可以通过 [AUTO-TRANSLATED:9a320d61]
// Can be achieved through
//rtmp://127.0.0.1/live/test
//rtsp://127.0.0.1/live/test
//播放mk_media的数据
// 播放mk_media的数据 [AUTO-TRANSLATED:623dc58f]
// Playing the data of mk_media
mk_rtsp_server_start(554, 0);
mk_rtmp_server_start(1935, 0);
Context *ctx = (Context *) malloc(sizeof(Context));
memset(ctx, 0, sizeof(Context));
//推流给自己测试,当然也可以推流给其他服务器测试
// 推流给自己测试,当然也可以推流给其他服务器测试 [AUTO-TRANSLATED:616e4dc2]
// Stream to yourself for testing, of course, you can also stream to other servers for testing
context_start(ctx, argv[1], argv[2]);
log_info("enter any key to exit");

View File

@ -17,6 +17,11 @@
* MediaSource事件广播
* @param regist 10
* @param sender MediaSource对象
* Register or unregister MediaSource event broadcast
* @param regist Register as 1, unregister as 0
* @param sender The MediaSource object
* [AUTO-TRANSLATED:8bb75918]
*/
void API_CALL on_mk_media_changed(int regist,
const mk_media_source sender) {
@ -34,6 +39,13 @@ void API_CALL on_mk_media_changed(int regist,
* @param url_info url相关信息
* @param invoker invoker返回鉴权结果
* @param sender tcp客户端相关信息
* Receive rtsp/rtmp push stream event broadcast, control push stream authentication through this event
* @see mk_publish_auth_invoker_do
* @param url_info Push stream url related information
* @param invoker Execute invoker to return authentication result
* @param sender The tcp client related information
* [AUTO-TRANSLATED:72053c81]
*/
void API_CALL on_mk_media_publish(const mk_media_info url_info,
const mk_publish_auth_invoker invoker,
@ -52,7 +64,8 @@ void API_CALL on_mk_media_publish(const mk_media_info url_info,
mk_media_info_get_stream(url_info),
mk_media_info_get_params(url_info));
//允许推流并且允许转hls/mp4
// 允许推流并且允许转hls/mp4 [AUTO-TRANSLATED:4b2d0e4e]
// Allow push stream, and allow to convert to hls/mp4
mk_publish_auth_invoker_do(invoker, NULL, 1, 1);
}
@ -62,6 +75,13 @@ void API_CALL on_mk_media_publish(const mk_media_info url_info,
* @param url_info url相关信息
* @param invoker invoker返回鉴权结果
* @param sender
* Play rtsp/rtmp/http-flv/hls event broadcast, control playback authentication through this event
* @see mk_auth_invoker_do
* @param url_info Play url related information
* @param invoker Execute invoker to return authentication result
* @param sender Play client related information
* [AUTO-TRANSLATED:fc351667]
*/
void API_CALL on_mk_media_play(const mk_media_info url_info,
const mk_auth_invoker invoker,
@ -81,7 +101,8 @@ void API_CALL on_mk_media_play(const mk_media_info url_info,
mk_media_info_get_stream(url_info),
mk_media_info_get_params(url_info));
//允许播放
// 允许播放 [AUTO-TRANSLATED:b940f821]
// Allow playback
mk_auth_invoker_do(invoker, NULL);
}
@ -91,6 +112,13 @@ void API_CALL on_mk_media_play(const mk_media_info url_info,
* @param sender
* @return 1
* 0
* This event will be broadcast after the stream is not found. Please pull the stream or other methods to generate the stream after listening to this event, so that you can pull the stream on demand
* @param url_info Play url related information
* @param sender Play client related information
* @return 1 Close directly
* 0 Wait for stream registration
* [AUTO-TRANSLATED:71caa7d8]
*/
int API_CALL on_mk_media_not_found(const mk_media_info url_info,
const mk_sock_info sender) {
@ -113,6 +141,10 @@ int API_CALL on_mk_media_not_found(const mk_media_info url_info,
/**
*
* @param sender MediaSource对象
* Triggered when no one consumes a certain stream, the purpose is to achieve business logic such as actively disconnecting the pull stream when no one is watching
* @param sender The MediaSource object
* [AUTO-TRANSLATED:d2881c87]
*/
void API_CALL on_mk_media_no_reader(const mk_media_source sender) {
log_printf(LOG_LEV,
@ -123,7 +155,8 @@ void API_CALL on_mk_media_no_reader(const mk_media_source sender) {
mk_media_source_get_stream(sender));
}
//按照json转义规则转义webrtc answer sdp
// 按照json转义规则转义webrtc answer sdp [AUTO-TRANSLATED:b9373d68]
// Escape webrtc answer sdp according to json escape rules
static char *escape_string(const char *ptr){
char *escaped = malloc(2 * strlen(ptr));
char *ptr_escaped = escaped;
@ -203,8 +236,16 @@ void API_CALL on_get_statistic_cb(void *user_data, mk_ini ini) {
* @param invoker invoker返回http回复
* @param consumed 1
* @param sender http客户端相关信息
* Receive http api request broadcast (including GET/POST)
* @param parser Http request content object
* @param invoker Execute this invoker to return http reply
* @param consumed Set to 1 if we want to handle this event
* @param sender Http client related information
* [AUTO-TRANSLATED:39435e52]
*/
//测试url : http://127.0.0.1/api/test
// 测试url : http://127.0.0.1/api/test [AUTO-TRANSLATED:4776d7a3]
// Test url : http://127.0.0.1/api/test
void API_CALL on_mk_http_request(const mk_parser parser,
const mk_http_response_invoker invoker,
int *consumed,
@ -230,7 +271,8 @@ void API_CALL on_mk_http_request(const mk_parser parser,
const char *url = mk_parser_get_url(parser);
*consumed = 1;
//拦截api: /api/test
// 拦截api: /api/test [AUTO-TRANSLATED:262baadf]
// Intercept api: /api/test
if (strcmp(url, "/api/test") == 0) {
const char *response_header[] = { "Content-Type", "text/html", NULL };
const char *content = "<html>"
@ -247,7 +289,8 @@ void API_CALL on_mk_http_request(const mk_parser parser,
mk_http_response_invoker_do(invoker, 200, response_header, body);
mk_http_body_release(body);
} else if (strcmp(url, "/index/api/webrtc") == 0) {
//拦截api: /index/api/webrtc
// 拦截api: /index/api/webrtc [AUTO-TRANSLATED:1db21d1c]
// Intercept api: /index/api/webrtc
char rtc_url[1024];
snprintf(rtc_url, sizeof(rtc_url), "rtc://%s/%s/%s?%s", mk_parser_get_header(parser, "Host"),
mk_parser_get_url_param(parser, "app"), mk_parser_get_url_param(parser, "stream"),
@ -256,7 +299,8 @@ void API_CALL on_mk_http_request(const mk_parser parser,
mk_webrtc_get_answer_sdp(mk_http_response_invoker_clone(invoker), on_mk_webrtc_get_answer_sdp_func,
mk_parser_get_url_param(parser, "type"), mk_parser_get_content(parser, NULL), rtc_url);
} else if (strcmp(url, "/index/api/getStatistic") == 0) {
//拦截api: /index/api/webrtc
// 拦截api: /index/api/webrtc [AUTO-TRANSLATED:1db21d1c]
// Intercept api: /index/api/webrtc
mk_get_statistic(on_get_statistic_cb, mk_http_response_invoker_clone(invoker), (on_user_data_free) mk_http_response_invoker_clone_release);
} else {
*consumed = 0;
@ -271,6 +315,14 @@ void API_CALL on_mk_http_request(const mk_parser parser,
* @param is_dir path是否为文件夹
* @param invoker invoker返回本次访问文件的结果
* @param sender http客户端相关信息
* In the http file server, receive the broadcast of http access to files or directories, control the access permission of http directory through this event
* @param parser Http request content object
* @param path File absolute path
* @param is_dir Whether path is a folder
* @param invoker Execute invoker to return the result of accessing the file this time
* @param sender Http client related information
* [AUTO-TRANSLATED:2db5fc1e]
*/
void API_CALL on_mk_http_access(const mk_parser parser,
const char *path,
@ -296,7 +348,8 @@ void API_CALL on_mk_http_access(const mk_parser parser,
mk_parser_get_header(parser,"User-Agent"),
mk_parser_get_content(parser,NULL));
//有访问权限,每次访问文件都需要鉴权
// 有访问权限,每次访问文件都需要鉴权 [AUTO-TRANSLATED:6ade8f21]
// Has access permission, each access to the file needs authentication
mk_http_access_path_invoker_do(invoker, NULL, NULL, 0);
}
@ -306,6 +359,13 @@ void API_CALL on_mk_http_access(const mk_parser parser,
* @param parser http请求内容对象
* @param path ,
* @param sender http客户端相关信息
* In the http file server, receive the broadcast before http access to files or directories, through this event you can control the mapping of http url to file path
* By overriding the path parameter in this event, you can achieve the purpose of selecting different http root directories according to virtual hosts or apps
* @param parser Http request content object
* @param path File absolute path, override it to redirect to other files
* @param sender Http client related information
* [AUTO-TRANSLATED:e166f6cb]
*/
void API_CALL on_mk_http_before_access(const mk_parser parser,
char *path,
@ -328,7 +388,8 @@ void API_CALL on_mk_http_before_access(const mk_parser parser,
mk_parser_get_tail(parser),
mk_parser_get_header(parser, "User-Agent"),
mk_parser_get_content(parser,NULL));
//覆盖path的值可以重定向文件
// 覆盖path的值可以重定向文件 [AUTO-TRANSLATED:7b03ed02]
// Overriding the value of path can redirect files
}
/**
@ -336,6 +397,12 @@ void API_CALL on_mk_http_before_access(const mk_parser parser,
* @param url_info rtsp url相关信息
* @param invoker invoker返回是否需要rtsp专属认证
* @param sender rtsp客户端相关信息
* Does this rtsp stream need authentication? If so, call invoker and pass in realm, otherwise pass in empty realm
* @param url_info Request rtsp url related information
* @param invoker Execute invoker to return whether rtsp exclusive authentication is required
* @param sender Rtsp client related information
* [AUTO-TRANSLATED:3308f34e]
*/
void API_CALL on_mk_rtsp_get_realm(const mk_media_info url_info,
const mk_rtsp_get_realm_invoker invoker,
@ -354,7 +421,8 @@ void API_CALL on_mk_rtsp_get_realm(const mk_media_info url_info,
mk_media_info_get_stream(url_info),
mk_media_info_get_params(url_info));
//rtsp播放默认鉴权
// rtsp播放默认鉴权 [AUTO-TRANSLATED:3a820721]
// Rtsp playback default authentication
mk_rtsp_get_realm_invoker_do(invoker, "zlmediakit");
}
@ -367,6 +435,16 @@ void API_CALL on_mk_rtsp_get_realm(const mk_media_info url_info,
* @param must_no_encrypt 1(base64认证方式),
* @param invoker invoker返回rtsp专属认证的密码
* @param sender rtsp客户端信息
* Request authentication user password event, user_name is the username, must_no_encrypt if it is 1, then you must provide plain text password (because it is base64 authentication method at this time), otherwise it will lead to authentication failure
* After getting the password, please call invoker and enter the corresponding type of password and password type, invoker will match the password when executing
* @param url_info Request rtsp url related information
* @param realm Rtsp authentication realm
* @param user_name Rtsp authentication username
* @param must_no_encrypt If it is 1, then you must provide plain text password (because it is base64 authentication method at this time), otherwise it will lead to authentication failure
* @param invoker Execute invoker to return the password of rtsp exclusive authentication
* @param sender Rtsp client information
* [AUTO-TRANSLATED:28391926]
*/
void API_CALL on_mk_rtsp_auth(const mk_media_info url_info,
const char *realm,
@ -391,12 +469,16 @@ void API_CALL on_mk_rtsp_auth(const mk_media_info url_info,
mk_media_info_get_params(url_info),
realm,user_name,(int)must_no_encrypt);
//rtsp播放用户名跟密码一致
// rtsp播放用户名跟密码一致 [AUTO-TRANSLATED:31cc5970]
// Rtsp playback username and password are consistent
mk_rtsp_auth_invoker_do(invoker,0,user_name);
}
/**
* mp4分片文件成功后广播
* Broadcast after recording mp4 fragment file successfully
* [AUTO-TRANSLATED:0fdeba0d]
*/
void API_CALL on_mk_record_mp4(const mk_record_info mp4) {
log_printf(LOG_LEV,
@ -424,6 +506,9 @@ void API_CALL on_mk_record_mp4(const mk_record_info mp4) {
/**
* shell登录鉴权
* Shell login authentication
* [AUTO-TRANSLATED:cc20f95e]
*/
void API_CALL on_mk_shell_login(const char *user_name,
const char *passwd,
@ -438,7 +523,8 @@ void API_CALL on_mk_shell_login(const char *user_name,
mk_sock_info_peer_ip(sender,ip + 32),
mk_sock_info_peer_port(sender),
user_name, passwd);
//允许登录shell
// 允许登录shell [AUTO-TRANSLATED:64a9b374]
// Allow login shell
mk_auth_invoker_do(invoker, NULL);
}
@ -450,6 +536,15 @@ void API_CALL on_mk_shell_login(const char *user_name,
* @param is_player
* @param peer_ip ip
* @param peer_port
* Stop rtsp/rtmp/http-flv session after traffic report event broadcast
* @param url_info Play url related information
* @param total_bytes Total traffic consumed up and down, unit is byte
* @param total_seconds The duration of this tcp session, unit is second
* @param is_player Whether the client is a player
* @param peer_ip Client ip
* @param peer_port Client port number
* [AUTO-TRANSLATED:6757a1c3]
*/
void API_CALL on_mk_flow_report(const mk_media_info url_info,
size_t total_bytes,

View File

@ -14,18 +14,24 @@
#include "mk_mediakit.h"
#define LOG_LEV 4
//修改此宏,可以选择协议类型
// 修改此宏,可以选择协议类型 [AUTO-TRANSLATED:7a3e6a3a]
// Modify this macro to choose the protocol type
#define TCP_TYPE mk_type_ws
////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct {
mk_tcp_session _session;
//下面你可以夹杂你的私货数据
// 下面你可以夹杂你的私货数据 [AUTO-TRANSLATED:99451203]
// You can insert your private data below
char your_some_useful_data[1024];
} tcp_session_user_data;
/**
* tcp客户端连接服务器时触发
* @param session
* Triggered when the tcp client connects to the server
* @param session Session processing object
* [AUTO-TRANSLATED:0030d1a6]
*/
void API_CALL on_mk_tcp_session_create(uint16_t server_port,mk_tcp_session session){
char ip[64];
@ -40,6 +46,12 @@ void API_CALL on_mk_tcp_session_create(uint16_t server_port,mk_tcp_session sessi
* @param session
* @param data
* @param len
* Receive data sent from the tcp client
* @param session Session processing object
* @param data Data pointer
* @param len Data length
* [AUTO-TRANSLATED:f8f01265]
*/
void API_CALL on_mk_tcp_session_data(uint16_t server_port,mk_tcp_session session, mk_buffer buffer){
char ip[64];
@ -55,6 +67,10 @@ void API_CALL on_mk_tcp_session_data(uint16_t server_port,mk_tcp_session session
/**
* 2
* @param session
* Timer every 2 seconds, used to manage timeout tasks
* @param session Session processing object
* [AUTO-TRANSLATED:f664481f]
*/
void API_CALL on_mk_tcp_session_manager(uint16_t server_port,mk_tcp_session session){
char ip[64];
@ -67,6 +83,13 @@ void API_CALL on_mk_tcp_session_manager(uint16_t server_port,mk_tcp_session sess
* @param session
* @param code
* @param msg
* Generally triggered by the client disconnecting tcp
* You can call the mk_tcp_session_send_safe function in this event
* @param session Session processing object
* @param code Error code
* @param msg Error message
* [AUTO-TRANSLATED:62628263]
*/
void API_CALL on_mk_tcp_session_disconnect(uint16_t server_port,mk_tcp_session session,int code,const char *msg){
char ip[64];
@ -78,7 +101,8 @@ void API_CALL on_mk_tcp_session_disconnect(uint16_t server_port,mk_tcp_session s
////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct {
mk_tcp_client client;
//下面你可以夹杂你的私货数据
// 下面你可以夹杂你的私货数据 [AUTO-TRANSLATED:99451203]
// You can insert your private data below
char your_some_useful_data[1024];
int count;
} tcp_client_user_data;
@ -88,11 +112,18 @@ typedef struct {
* @param client tcp客户端
* @param code 0
* @param msg
* Callback for successful or failed connection of tcp client to server
* @param client Tcp client
* @param code 0 for successful connection, otherwise the reason for failure
* @param msg Connection failure error message
* [AUTO-TRANSLATED:0737893b]
*/
void API_CALL on_mk_tcp_client_connect(mk_tcp_client client,int code,const char *msg){
log_printf(LOG_LEV,"connect result:%d %s",code,msg);
if(code == 0){
//连接上后我们发送一个hello world测试数据
// 连接上后我们发送一个hello world测试数据 [AUTO-TRANSLATED:9eb05433]
// After connecting, we send a hello world test data
mk_tcp_client_send(client,"hello world",0);
}else{
tcp_client_user_data *user_data = mk_tcp_client_get_user_data(client);
@ -107,10 +138,18 @@ void API_CALL on_mk_tcp_client_connect(mk_tcp_client client,int code,const char
* @param client tcp客户端
* @param code
* @param msg
* Callback for disconnection between tcp client and tcp server
* Generally caused by eof event
* @param client Tcp client
* @param code Error code
* @param msg Error message
* [AUTO-TRANSLATED:9cfff388]
*/
void API_CALL on_mk_tcp_client_disconnect(mk_tcp_client client,int code,const char *msg){
log_printf(LOG_LEV,"disconnect:%d %s",code,msg);
//服务器主动断开了,我们销毁对象
// 服务器主动断开了,我们销毁对象 [AUTO-TRANSLATED:4b142ba4]
// The server actively disconnected, we destroy the object
tcp_client_user_data *user_data = mk_tcp_client_get_user_data(client);
mk_tcp_client_release(client);
free(user_data);
@ -121,6 +160,12 @@ void API_CALL on_mk_tcp_client_disconnect(mk_tcp_client client,int code,const ch
* @param client tcp客户端
* @param data
* @param len
* Receive data sent from the tcp server
* @param client Tcp client
* @param data Data pointer
* @param len Data length
* [AUTO-TRANSLATED:d152eff2]
*/
void API_CALL on_mk_tcp_client_data(mk_tcp_client client, mk_buffer buffer){
log_printf(LOG_LEV, "data[%d]:%s", mk_buffer_get_size(buffer), mk_buffer_get_data(buffer));
@ -129,6 +174,10 @@ void API_CALL on_mk_tcp_client_data(mk_tcp_client client, mk_buffer buffer){
/**
* 2
* @param client tcp客户端
* Timer every 2 seconds, used to manage timeout tasks
* @param client Tcp client
* [AUTO-TRANSLATED:ba842db3]
*/
void API_CALL on_mk_tcp_client_manager(mk_tcp_client client){
tcp_client_user_data *user_data = mk_tcp_client_get_user_data(client);
@ -137,7 +186,8 @@ void API_CALL on_mk_tcp_client_manager(mk_tcp_client client){
mk_tcp_client_send(client,buf,0);
if(++user_data->count == 5){
//发送5遍后主动释放对象
// 发送5遍后主动释放对象 [AUTO-TRANSLATED:43ce72ca]
// Release the object after sending 5 times
mk_tcp_client_release(client);
free(user_data);
}
@ -171,7 +221,8 @@ void test_client(){
mk_tcp_client_set_user_data(client,user_data);
mk_tcp_client_connect(client, "121.40.165.18", 8800, 3);
//你可以连接127.0.0.1 80测试
// 你可以连接127.0.0.1 80测试 [AUTO-TRANSLATED:68544f6e]
// You can connect to 127.0.0.1 80 to test
// mk_tcp_client_connect(client, "127.0.0.1", 80, 3);
}

View File

@ -133,6 +133,9 @@ mediaServerId=your_server_id
#最多等待未初始化的Track时间单位毫秒超时之后会忽略未初始化的Track
wait_track_ready_ms=10000
#最多等待音频Track收到数据时间单位毫秒超时且完全没收到音频数据忽略音频Track
#加快某些带封装的流metadata说明有音频但是实际上没有的流ready时间比如很多厂商的GB28181 PS
wait_audio_track_data_ms=1000
#如果流只有单Track最多等待若干毫秒超时后未收到其他Track的数据则认为是单Track
#如果协议元数据有声明特定track数那么无此等待时间
wait_add_track_ms=3000

View File

@ -37,14 +37,18 @@ public:
unsigned int channel_configuration; // 3 uimsbf 表示声道数
unsigned int original; // 1 bslbf
unsigned int home; // 1 bslbf
// 下面的为改变的参数即每一帧都不同
// 下面的为改变的参数即每一帧都不同 [AUTO-TRANSLATED:481aa349]
// The following are the parameters that change in each frame
unsigned int copyright_identification_bit; // 1 bslbf
unsigned int copyright_identification_start; // 1 bslbf
unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block
unsigned int adts_buffer_fullness; // 11 bslbf 0x7FF 说明是码率可变的码流
// no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.
// 所以说number_of_raw_data_blocks_in_frame == 0
// 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
// no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. [AUTO-TRANSLATED:3e975531]
// no_raw_data_blocks_in_frame indicates that there are number_of_raw_data_blocks_in_frame + 1 AAC raw frames in the ADTS frame.
// 所以说number_of_raw_data_blocks_in_frame == 0 [AUTO-TRANSLATED:1b8e9697]
// So number_of_raw_data_blocks_in_frame == 0
// 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) [AUTO-TRANSLATED:4a09d783]
// means that there is one AAC data block in the ADTS frame, not that there is none. (An AAC raw frame contains 1024 samples and related data over a period of time)
unsigned int no_raw_data_blocks_in_frame; // 2 uimsfb
};
@ -203,6 +207,9 @@ bool parseAacConfig(const string &config, int &samplerate, int &channels) {
/**
* aac类型SDP
* aac type SDP
* [AUTO-TRANSLATED:c06f00b1]
*/
class AACSdp : public Sdp {
public:
@ -213,6 +220,14 @@ public:
* @param sample_rate
* @param channels
* @param bitrate
* Constructor
* @param aac_cfg aac two-byte configuration description
* @param payload_type rtp payload type
* @param sample_rate audio sampling rate
* @param channels number of channels
* @param bitrate bitrate
* [AUTO-TRANSLATED:6fe1f3b2]
*/
AACSdp(const string &aac_cfg, int payload_type, int sample_rate, int channels, int bitrate)
: Sdp(sample_rate, payload_type) {
@ -286,7 +301,8 @@ int AACTrack::getAudioChannel() const {
static Frame::Ptr addADTSHeader(const Frame::Ptr &frame_in, const std::string &aac_config) {
auto frame = FrameImp::create();
frame->_codec_id = CodecAAC;
// 生成adts头
// 生成adts头 [AUTO-TRANSLATED:c285b9b0]
// Generate adts header
char adts_header[32] = { 0 };
auto size = dumpAacConfig(aac_config, frame_in->size(), (uint8_t *)adts_header, sizeof(adts_header));
CHECK(size > 0, "Invalid adts config");
@ -305,7 +321,8 @@ bool AACTrack::inputFrame(const Frame::Ptr &frame) {
}
bool ret = false;
//有adts头尝试分帧
// 有adts头尝试分帧 [AUTO-TRANSLATED:f691c4ce]
// There is an adts header, try to frame
int64_t dts = frame->dts();
int64_t pts = frame->pts();
@ -337,13 +354,15 @@ bool AACTrack::inputFrame(const Frame::Ptr &frame) {
bool AACTrack::inputFrame_l(const Frame::Ptr &frame) {
if (_cfg.empty() && frame->prefixSize()) {
// 未获取到aac_cfg信息根据7个字节的adts头生成aac config
// 未获取到aac_cfg信息根据7个字节的adts头生成aac config [AUTO-TRANSLATED:1b80f562]
// Unable to get aac_cfg information, generate aac config based on the 7-byte adts header
_cfg = makeAacConfig((uint8_t *)(frame->data()), frame->prefixSize());
update();
}
if (frame->size() > frame->prefixSize()) {
// 除adts头外有实际负载
// 除adts头外有实际负载 [AUTO-TRANSLATED:5b7c088e]
// There is an actual payload besides the adts header
return AudioTrack::inputFrame(frame);
}
return false;
@ -394,7 +413,8 @@ Track::Ptr getTrackBySdp(const SdpTrack::Ptr &track) {
aac_cfg_str = findSubString(track->_fmtp.data(), "config=", nullptr);
}
if (aac_cfg_str.empty()) {
// 如果sdp中获取不到aac config信息那么在rtp也无法获取那么忽略该Track
// 如果sdp中获取不到aac config信息那么在rtp也无法获取那么忽略该Track [AUTO-TRANSLATED:995bc20d]
// If aac config information cannot be obtained from sdp, then it cannot be obtained from rtp either, so ignore this Track
return nullptr;
}
string aac_cfg;

View File

@ -19,6 +19,9 @@ namespace mediakit{
/**
* aac音频通道
* AAC audio channel
* [AUTO-TRANSLATED:0d58b638]
*/
class AACTrack : public AudioTrack {
public:
@ -28,6 +31,10 @@ public:
AACTrack(int samplerate, int channel, int profile=1);
/**
* aac extra data
* Construct object through AAC extra data
* [AUTO-TRANSLATED:1fa035c8]
*/
AACTrack(const std::string &aac_cfg);

View File

@ -18,6 +18,9 @@
namespace mediakit {
/**
* aac Rtmp转adts类
* aac Rtmp to adts class
* [AUTO-TRANSLATED:8b262ddb]
*/
class AACRtmpDecoder : public RtmpCodec {
public:
@ -28,12 +31,19 @@ public:
/**
* Rtmp并解码
* @param rtmp Rtmp数据包
* Input Rtmp and decode
* @param rtmp Rtmp data packet
* [AUTO-TRANSLATED:43b1eae8]
*/
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
};
/**
* aac adts转Rtmp类
* aac adts to Rtmp class
* [AUTO-TRANSLATED:2d9c53dd]
*/
class AACRtmpEncoder : public RtmpCodec {
public:
@ -44,17 +54,31 @@ public:
* track不为空且包含adts头相关信息
* inputFrame时可以不输入adts头
* @param track
* Constructor, track can be empty, in which case the adts header is input when inputFrame is called
* If track is not empty and contains adts header related information,
* then the adts header can be omitted when inputFrame is called
* @param track
* [AUTO-TRANSLATED:fcf8f765]
*/
AACRtmpEncoder(const Track::Ptr &track) : RtmpCodec(track) {}
/**
* aac adts头
* @param frame aac数据
* Input aac data, can be without adts header
* @param frame aac data
* [AUTO-TRANSLATED:d9f4131a]
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* config包
* Generate config package
* [AUTO-TRANSLATED:8f851364]
*/
void makeConfigPacket() override;

View File

@ -47,7 +47,8 @@ AACRtpDecoder::AACRtpDecoder() {
}
void AACRtpDecoder::obtainFrame() {
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
// 从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 [AUTO-TRANSLATED:f85fe201]
// Re-apply the object from the cache pool to prevent overwriting the object that has been written to the ring buffer
_frame = FrameImp::create();
_frame->_codec_id = CodecAAC;
}
@ -55,45 +56,56 @@ void AACRtpDecoder::obtainFrame() {
bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
auto payload_size = rtp->getPayloadSize();
if (payload_size <= 0) {
// 无实际负载
// 无实际负载 [AUTO-TRANSLATED:2267e6ac]
// No actual load
return false;
}
auto stamp = rtp->getStampMS();
// rtp数据开始部分
// rtp数据开始部分 [AUTO-TRANSLATED:f22ebdb9]
// Start of rtp data
auto ptr = rtp->getPayload();
// rtp数据末尾
// rtp数据末尾 [AUTO-TRANSLATED:ee108f2b]
// End of rtp data
auto end = ptr + payload_size;
// 首2字节表示Au-Header的个数单位bit所以除以16得到Au-Header个数
// 首2字节表示Au-Header的个数单位bit所以除以16得到Au-Header个数 [AUTO-TRANSLATED:c7175051]
// The first 2 bytes represent the number of Au-Headers, in bits, so divide by 16 to get the number of Au-Headers
auto au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4;
if (!au_header_count) {
// 问题issue: https://github.com/ZLMediaKit/ZLMediaKit/issues/1869
// 问题issue: https://github.com/ZLMediaKit/ZLMediaKit/issues/1869 [AUTO-TRANSLATED:14be1ff8]
// Issue: https://github.com/ZLMediaKit/ZLMediaKit/issues/1869
WarnL << "invalid aac rtp au_header_count";
return false;
}
// 记录au_header起始指针
// 记录au_header起始指针 [AUTO-TRANSLATED:b9083b72]
// Record the starting pointer of au_header
auto au_header_ptr = ptr + 2;
ptr = au_header_ptr + au_header_count * 2;
if (end < ptr) {
// 数据不够
// 数据不够 [AUTO-TRANSLATED:830a2785]
// Not enough data
return false;
}
if (!_last_dts) {
// 记录第一个时间戳
// 记录第一个时间戳 [AUTO-TRANSLATED:2e85b398]
// Record the first timestamp
_last_dts = stamp;
}
// 每个audio unit时间戳增量
// 每个audio unit时间戳增量 [AUTO-TRANSLATED:0345240c]
// Timestamp increment for each audio unit
auto dts_inc = static_cast<int64_t>(stamp - _last_dts) / au_header_count;
if (dts_inc < 0 || dts_inc > 100) {
// 时间戳增量异常,忽略
// 时间戳增量异常,忽略 [AUTO-TRANSLATED:d2750ef8]
// Timestamp increment anomaly, ignore
dts_inc = 0;
}
for (auto i = 0u; i < (size_t)au_header_count; ++i) {
// 之后的2字节是AU_HEADER,其中高13位表示一帧AAC负载的字节长度低3位无用
// 之后的2字节是AU_HEADER,其中高13位表示一帧AAC负载的字节长度低3位无用 [AUTO-TRANSLATED:404eb444]
// The following 2 bytes are AU_HEADER, where the high 13 bits represent the byte length of one frame of AAC payload, and the low 3 bits are useless
auto size = ((au_header_ptr[0] << 8) | au_header_ptr[1]) >> 3;
auto len = std::min<int>(size, end - ptr);
if (len <= 0) {
@ -104,12 +116,14 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
au_header_ptr += 2;
if (_frame->size() >= (size_t)size) {
// 设置当前audio unit时间戳
// 设置当前audio unit时间戳 [AUTO-TRANSLATED:eee18d6e]
// Set the current audio unit timestamp
_frame->_dts = _last_dts + i * dts_inc;
flushData();
}
}
// 记录上次时间戳
// 记录上次时间戳 [AUTO-TRANSLATED:a830d69f]
// Record the last timestamp
_last_dts = stamp;
return false;
}
@ -117,7 +131,8 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
void AACRtpDecoder::flushData() {
auto ptr = reinterpret_cast<const uint8_t *>(_frame->data());
if ((ptr[0] == 0xFF && (ptr[1] & 0xF0) == 0xF0) && _frame->size() > ADTS_HEADER_LEN) {
// adts头打入了rtp包不符合规范兼容EasyPusher的bug
// adts头打入了rtp包不符合规范兼容EasyPusher的bug [AUTO-TRANSLATED:203a5ee9]
// The adts header is inserted into the rtp packet, which is not compliant with the specification, compatible with the bug of EasyPusher
_frame->_prefix_size = ADTS_HEADER_LEN;
}
RtpCodec::inputFrame(_frame);

View File

@ -17,6 +17,9 @@
namespace mediakit {
/**
* aac rtp转adts类
* aac rtp to adts class
* [AUTO-TRANSLATED:8ff7580f]
*/
class AACRtpDecoder : public RtpCodec {
public:
@ -28,6 +31,11 @@ public:
* rtp并解码
* @param rtp rtp数据包
* @param key_pos false,
* input rtp and decode
* @param rtp rtp data packet
* @param key_pos this parameter is internally forced to false, please ignore it
* [AUTO-TRANSLATED:2993fcbe]
*/
bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override;
@ -43,6 +51,9 @@ private:
/**
* aac adts转rtp类
* aac adts to rtp class
* [AUTO-TRANSLATED:1ed889e2]
*/
class AACRtpEncoder : public RtpCodec {
public:
@ -51,6 +62,11 @@ public:
/**
* aac dats头
* @param frame dats头的aac数据
* input aac data, must have dats header
* @param frame aac data with dats header
* [AUTO-TRANSLATED:459bba30]
*/
bool inputFrame(const Frame::Ptr &frame) override;

View File

@ -21,6 +21,9 @@ namespace mediakit {
/**
* G711类型SDP
* G711 type SDP
* [AUTO-TRANSLATED:ea72d60a]
*/
class G711Sdp : public Sdp {
public:
@ -31,6 +34,14 @@ public:
* @param sample_rate
* @param channels
* @param bitrate
* G711 sampling rate is fixed at 8000
* @param codecId G711A G711U
* @param payload_type rtp payload type
* @param sample_rate audio sampling rate
* @param channels number of channels
* @param bitrate bitrate
* [AUTO-TRANSLATED:5ea4b771]
*/
G711Sdp(CodecId codecId, int payload_type, int sample_rate, int channels, int bitrate)
: Sdp(sample_rate, payload_type) {
@ -136,7 +147,8 @@ RtpCodec::Ptr getRtpDecoderByCodecIdU() {
RtmpCodec::Ptr getRtmpEncoderByTrack(const Track::Ptr &track) {
auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
if (audio_track->getAudioSampleRate() != 8000 || audio_track->getAudioChannel() != 1 || audio_track->getAudioSampleBit() != 16) {
//rtmp对g711只支持8000/1/16规格但是ZLMediaKit可以解析其他规格的G711
// rtmp对g711只支持8000/1/16规格但是ZLMediaKit可以解析其他规格的G711 [AUTO-TRANSLATED:0ddeaafe]
// rtmp only supports 8000/1/16 specifications for g711, but ZLMediaKit can parse other specifications of G711
WarnL << "RTMP only support G711 with 8000/1/16, now is"
<< audio_track->getAudioSampleRate() << "/"
<< audio_track->getAudioChannel() << "/"

View File

@ -18,6 +18,10 @@ namespace mediakit{
/**
* G711音频通道
* G711 audio channel
* [AUTO-TRANSLATED:57f8bc08]
*/
class G711Track : public AudioTrackImp{
public:

View File

@ -16,7 +16,8 @@ void G711RtpEncoder::setOpt(int opt, const toolkit::Any &param) {
WarnL << "set g711 rtp encoder duration ms failed for " << dur;
return;
}
// 向上 20ms 取整
// 向上 20ms 取整 [AUTO-TRANSLATED:b8a9e39e]
// Round up to the nearest 20ms
_pkt_dur_ms = (dur + 19) / 20 * 20;
}
}

View File

@ -19,6 +19,9 @@ namespace mediakit {
/**
* G711 rtp编码类
* G711 rtp encoding class
* [AUTO-TRANSLATED:92aa6cf3]
*/
class G711RtpEncoder : public RtpCodec {
public:
@ -28,11 +31,20 @@ public:
*
* @param codec
* @param channels
* Constructor
* @param codec Encoding type
* @param channels Number of channels
* [AUTO-TRANSLATED:dbbd593e]
*/
G711RtpEncoder(CodecId codec, uint32_t channels);
/**
* rtp
* Input frame data and encode it into rtp
* [AUTO-TRANSLATED:02bc9009]
*/
bool inputFrame(const Frame::Ptr &frame) override;

View File

@ -67,24 +67,31 @@ void splitH264(
while (true) {
auto next_start = memfind(start, end - start, "\x00\x00\x01", 3);
if (next_start) {
//找到下一帧
// 找到下一帧 [AUTO-TRANSLATED:7161f54a]
// Find the next frame
if (*(next_start - 1) == 0x00) {
//这个是00 00 00 01开头
// 这个是00 00 00 01开头 [AUTO-TRANSLATED:b0d79e9e]
// This starts with 00 00 00 01
next_start -= 1;
next_prefix = 4;
} else {
//这个是00 00 01开头
// 这个是00 00 01开头 [AUTO-TRANSLATED:18ae81d8]
// This starts with 00 00 01
next_prefix = 3;
}
//记得加上本帧prefix长度
// 记得加上本帧prefix长度 [AUTO-TRANSLATED:8bde5d52]
// Remember to add the prefix length of this frame
cb(start - prefix, next_start - start + prefix, prefix);
//搜索下一帧末尾的起始位置
// 搜索下一帧末尾的起始位置 [AUTO-TRANSLATED:8976b719]
// Search for the starting position of the end of the next frame
start = next_start + next_prefix;
//记录下一帧的prefix长度
// 记录下一帧的prefix长度 [AUTO-TRANSLATED:756aee4e]
// Record the prefix length of the next frame
prefix = next_prefix;
continue;
}
//未找到下一帧,这是最后一帧
// 未找到下一帧,这是最后一帧 [AUTO-TRANSLATED:58365453]
// The next frame was not found, this is the last frame
cb(start - prefix, end - start + prefix, prefix);
break;
}
@ -96,17 +103,20 @@ size_t prefixSize(const char *ptr, size_t len) {
}
if (ptr[0] != 0x00 || ptr[1] != 0x00) {
//不是0x00 00开头
// 不是0x00 00开头 [AUTO-TRANSLATED:c406f0da]
// Not 0x00 00 at the beginning
return 0;
}
if (ptr[2] == 0x00 && ptr[3] == 0x01) {
//是0x00 00 00 01
// 是0x00 00 00 01 [AUTO-TRANSLATED:70caae72]
// It is 0x00 00 00 01
return 4;
}
if (ptr[2] == 0x01) {
//是0x00 00 01
// 是0x00 00 01 [AUTO-TRANSLATED:78b4a3c9]
// It is 0x00 00 01
return 3;
}
return 0;
@ -148,7 +158,8 @@ bool H264Track::inputFrame(const Frame::Ptr &frame) {
return inputFrame_l(frame);
}
//非I/B/P帧情况下split一下防止多个帧粘合在一起
// 非I/B/P帧情况下split一下防止多个帧粘合在一起 [AUTO-TRANSLATED:b69c6e75]
// In the case of non-I/B/P frames, split it to prevent multiple frames from sticking together
bool ret = false;
splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, size_t len, size_t prefix) {
H264FrameInternal::Ptr sub_frame = std::make_shared<H264FrameInternal>(frame, (char *)ptr, len, prefix);
@ -267,13 +278,15 @@ bool H264Track::inputFrame_l(const Frame::Ptr &frame) {
break;
}
default:
// 避免识别不出关键帧
// 避免识别不出关键帧 [AUTO-TRANSLATED:8eb84679]
// Avoid not being able to recognize keyframes
if (_latest_is_config_frame && !frame->dropAble()) {
if (!frame->keyFrame()) {
const_cast<Frame::Ptr &>(frame) = std::make_shared<FrameCacheAble>(frame, true);
}
}
// 判断是否是I帧, 并且如果是,那判断前面是否插入过config帧, 如果插入过就不插入了
// 判断是否是I帧, 并且如果是,那判断前面是否插入过config帧, 如果插入过就不插入了 [AUTO-TRANSLATED:40733cd8]
// Determine if it is an I frame, and if it is, determine if a config frame has been inserted before, and if it has been inserted, do not insert it
if (frame->keyFrame() && !_latest_is_config_frame) {
insertConfigFrame(frame);
}
@ -313,6 +326,11 @@ public:
Single NAI Unit Mode = 0. // Single NAI mode (Only nals from 1-23 are allowed)
Non Interleaved Mode = 1// Non-interleaved Mode: 1-2324 (STAP-A)28 (FU-A) are allowed
Interleaved Mode = 2, // 25 (STAP-B)26 (MTAP16)27 (MTAP24)28 (EU-A)and 29 (EU-B) are allowed.
Single NAI Unit Mode = 0. // Single NAI mode (Only nals from 1-23 are allowed)
Non Interleaved Mode = 1// Non-interleaved Mode: 1-2324 (STAP-A)28 (FU-A) are allowed
Interleaved Mode = 2, // 25 (STAP-B)26 (MTAP16)27 (MTAP24)28 (EU-A)and 29 (EU-B) are allowed.
*
* [AUTO-TRANSLATED:6166738f]
**/
GET_CONFIG(bool, h264_stap_a, Rtp::kH264StapA);
_printer << "a=fmtp:" << payload_type << " packetization-mode=" << h264_stap_a << "; profile-level-id=";
@ -365,7 +383,8 @@ Track::Ptr getTrackBySdp(const SdpTrack::Ptr &track) {
auto sps = decodeBase64(base64_SPS);
auto pps = decodeBase64(base64_PPS);
if (sps.empty() || pps.empty()) {
//如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps
// 如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps [AUTO-TRANSLATED:60f03d45]
// If there is no sps/pps in the sdp, then it may be possible to recover the sps/pps in the subsequent rtp
return std::make_shared<H264Track>();
}
return std::make_shared<H264Track>(sps, pps, 0, 0);

View File

@ -68,24 +68,35 @@ public:
bool decodeAble() const override {
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
auto type = H264_TYPE(*nal_ptr);
//多slice情况下, first_mb_in_slice 表示其为一帧的开始
// 多slice情况下, first_mb_in_slice 表示其为一帧的开始 [AUTO-TRANSLATED:80e88e88]
// // In the case of multiple slices, first_mb_in_slice indicates the start of a frame
return type >= NAL_B_P && type <= NAL_IDR && (nal_ptr[1] & 0x80);
}
};
/**
* 264
* 264 frame class
* [AUTO-TRANSLATED:342ccb1e]
*/
using H264Frame = H264FrameHelper<FrameImp>;
/**
* H264类
* Frame类
* H264 class that prevents memory copying
* Users can quickly wrap a pointer into a Frame class without copying using this type
* [AUTO-TRANSLATED:ff9be1c8]
*/
using H264FrameNoCacheAble = H264FrameHelper<FrameFromPtr>;
/**
* 264
* 264 video channel
* [AUTO-TRANSLATED:6936e76d]
*/
class H264Track : public VideoTrack {
public:
@ -94,6 +105,10 @@ public:
/**
* sps pps构造h264类型的媒体
* inputFrame中获取sps pps
* Construct a media of h264 type without specifying sps pps
* Get sps pps in the subsequent inputFrame
* [AUTO-TRANSLATED:84d01c7f]
*/
H264Track() = default;
@ -103,6 +118,14 @@ public:
* @param pps pps帧数据
* @param sps_prefix_len 264340x00 00 00 01
* @param pps_prefix_len 264340x00 00 00 01
* Construct a media of h264 type
* @param sps sps frame data
* @param pps pps frame data
* @param sps_prefix_len 264 header length, can be 3 or 4 bytes, generally 0x00 00 00 01
* @param pps_prefix_len 264 header length, can be 3 or 4 bytes, generally 0x00 00 00 01
* [AUTO-TRANSLATED:702c1433]
*/
H264Track(const std::string &sps, const std::string &pps, int sps_prefix_len = 4, int pps_prefix_len = 4);

View File

@ -62,7 +62,8 @@ void H264RtmpEncoder::flush() {
bool H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
if (!_rtmp_packet) {
_rtmp_packet = RtmpPacket::create();
//flags/not config/cts预占位
// flags/not config/cts预占位 [AUTO-TRANSLATED:7effb692]
// flags/not config/cts placeholder
_rtmp_packet->buffer.resize(5);
}
@ -78,7 +79,8 @@ bool H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
_rtmp_packet->chunk_id = CHUNK_VIDEO;
_rtmp_packet->stream_index = STREAM_MEDIA;
_rtmp_packet->type_id = MSG_VIDEO;
// 输出rtmp packet
// 输出rtmp packet [AUTO-TRANSLATED:d72e89a7]
// Output rtmp packet
RtmpCodec::inputRtmp(_rtmp_packet);
_rtmp_packet = nullptr;
}, &_rtmp_packet->buffer);

View File

@ -19,6 +19,10 @@ namespace mediakit {
/**
* h264 Rtmp解码类
* h264 over rtmp h264-Frame
* h264 Rtmp decoder class
* Demultiplex h264-Frame from h264 over rtmp
* [AUTO-TRANSLATED:4908a1f3]
*/
class H264RtmpDecoder : public RtmpCodec {
public:
@ -29,6 +33,10 @@ public:
/**
* 264 Rtmp包
* @param rtmp Rtmp包
* Input 264 Rtmp package
* @param rtmp Rtmp package
* [AUTO-TRANSLATED:06f3e94c]
*/
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
@ -39,6 +47,9 @@ private:
/**
* 264 Rtmp打包类
* 264 Rtmp packaging class
* [AUTO-TRANSLATED:e5bc7c66]
*/
class H264RtmpEncoder : public RtmpCodec {
public:
@ -49,22 +60,39 @@ public:
* track不为空且包含sps pps信息
* inputFrame时可以不输入sps pps
* @param track
* Constructor, track can be empty, in which case sps pps is input when inputFrame
* If track is not empty and contains sps pps information,
* then sps pps can be omitted when inputFrame
* @param track
* [AUTO-TRANSLATED:e61fdfed]
*/
H264RtmpEncoder(const Track::Ptr &track) : RtmpCodec(track) {}
/**
* 264sps pps
* @param frame
* Input 264 frame, sps pps can be omitted
* @param frame Frame data
* [AUTO-TRANSLATED:caefd055]
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* frame缓存
* Flush all frame cache output
* [AUTO-TRANSLATED:adaea568]
*/
void flush() override;
/**
* config包
* Generate config package
* [AUTO-TRANSLATED:8f851364]
*/
void makeConfigPacket() override;

View File

@ -51,7 +51,9 @@ bool H264RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
WarnL << "start drop h264 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString();
}
_last_seq = seq;
// 确保有sps rtp的时候gop从sps开始否则从关键帧开始
// 确保有sps rtp的时候gop从sps开始否则从关键帧开始 [AUTO-TRANSLATED:115ae07c]
// cpp
// Ensure that when there is sps rtp, the gop starts from sps; otherwise, it starts from the key frame
return _is_gop && !last_is_gop;
}
@ -70,6 +72,23 @@ Table 1. Summary of NAL unit types and their payload structures
28 FU-A Fragmentation unit 5.8
29 FU-B Fragmentation unit 5.8
30-31 undefined -
/*
RTF3984 Section 5.2 Common Structure of the RTP Payload Format
Table 1. Summary of NAL unit types and their payload structures
Type Packet Type name Section
---------------------------------------------------------
0 undefined -
1-23 NAL unit Single NAL unit packet per H.264 5.6
24 STAP-A Single-time aggregation packet 5.7.1
25 STAP-B Single-time aggregation packet 5.7.1
26 MTAP16 Multi-time aggregation packet 5.7.2
27 MTAP24 Multi-time aggregation packet 5.7.2
28 FU-A Fragmentation unit 5.8
29 FU-B Fragmentation unit 5.8
30-31 undefined -
* [AUTO-TRANSLATED:57545317]
*/
bool H264RtpDecoder::singleFrame(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssize_t size, uint64_t stamp){
@ -82,7 +101,8 @@ bool H264RtpDecoder::singleFrame(const RtpPacket::Ptr &rtp, const uint8_t *ptr,
}
bool H264RtpDecoder::unpackStapA(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssize_t size, uint64_t stamp) {
//STAP-A 单一时间的组合包
// STAP-A 单一时间的组合包 [AUTO-TRANSLATED:cfa62307]
// STAP-A single-time aggregation packet
auto have_key_frame = false;
auto end = ptr + size;
while (ptr + 2 < end) {
@ -105,7 +125,8 @@ bool H264RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz
auto nal_suffix = *ptr & (~0x1F);
FuFlags *fu = (FuFlags *) (ptr + 1);
if (fu->start_bit) {
//该帧的第一个rtp包
// 该帧的第一个rtp包 [AUTO-TRANSLATED:a9581a23]
// The first rtp packet of this frame
_frame->_buffer.assign("\x00\x00\x00\x01", 4);
_frame->_buffer.push_back(nal_suffix | fu->nal_type);
_frame->_pts = stamp;
@ -113,28 +134,34 @@ bool H264RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz
}
if (_fu_dropped) {
//该帧不完整
// 该帧不完整 [AUTO-TRANSLATED:6bd7eca7]
// This frame is incomplete
return false;
}
if (!fu->start_bit && seq != (uint16_t) (_last_seq + 1)) {
//中间的或末尾的rtp包其seq必须连续否则说明rtp丢包那么该帧不完整必须得丢弃
// 中间的或末尾的rtp包其seq必须连续否则说明rtp丢包那么该帧不完整必须得丢弃 [AUTO-TRANSLATED:6953b332]
// The middle or end rtp packet, its seq must be continuous, otherwise it indicates that the rtp packet is lost, then the frame is incomplete and must be discarded
_fu_dropped = true;
_frame->_buffer.clear();
return false;
}
//后面追加数据
// 后面追加数据 [AUTO-TRANSLATED:248516e9]
// Append data
_frame->_buffer.append((char *) ptr + 2, size - 2);
if (!fu->end_bit) {
//非末尾包
// 非末尾包 [AUTO-TRANSLATED:2e43ac3c]
// Not the end packet
return fu->start_bit ? (_frame->keyFrame() || _frame->configFrame()) : false;
}
//确保下一次fu必须收到第一个包
// 确保下一次fu必须收到第一个包 [AUTO-TRANSLATED:491d81ec]
// Ensure that the next fu must receive the first packet
_fu_dropped = true;
//该帧最后一个rtp包,输出frame
// 该帧最后一个rtp包,输出frame [AUTO-TRANSLATED:a648aaa5]
// The last rtp packet of this frame, output frame
outputFrame(rtp, _frame);
return false;
}
@ -142,7 +169,8 @@ bool H264RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz
bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
auto payload_size = rtp->getPayloadSize();
if (payload_size <= 0) {
//无实际负载
// 无实际负载 [AUTO-TRANSLATED:305af48f]
// No actual payload
return false;
}
auto frame = rtp->getPayload();
@ -173,10 +201,12 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
void H264RtpDecoder::outputFrame(const RtpPacket::Ptr &rtp, const H264Frame::Ptr &frame) {
if (frame->dropAble()) {
//不参与dts生成
// 不参与dts生成 [AUTO-TRANSLATED:dff3b747]
// Not involved in dts generation
frame->_dts = frame->_pts;
} else {
//rtsp没有dts那么根据pts排序算法生成dts
// rtsp没有dts那么根据pts排序算法生成dts [AUTO-TRANSLATED:f37c17f3]
// Rtsp does not have dts, so dts is generated according to the pts sorting algorithm
_dts_generator.getDts(frame->_pts, frame->_dts);
}
@ -196,17 +226,20 @@ void H264RtpEncoder::insertConfigFrame(uint64_t pts){
if (!_sps || !_pps) {
return;
}
//gop缓存从sps开始sps、pps后面还有时间戳相同的关键帧所以mark bit为false
// gop缓存从sps开始sps、pps后面还有时间戳相同的关键帧所以mark bit为false [AUTO-TRANSLATED:e8dcff77]
// The gop cache starts from sps, sps, pps and then there are key frames with the same timestamp, so the mark bit is false
packRtp(_sps->data() + _sps->prefixSize(), _sps->size() - _sps->prefixSize(), pts, false, true);
packRtp(_pps->data() + _pps->prefixSize(), _pps->size() - _pps->prefixSize(), pts, false, false);
}
void H264RtpEncoder::packRtp(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
if (len + 3 <= getRtpInfo().getMaxSize()) {
// 采用STAP-A/Single NAL unit packet per H.264 模式
// 采用STAP-A/Single NAL unit packet per H.264 模式 [AUTO-TRANSLATED:1a719984]
// Use STAP-A/Single NAL unit packet per H.264 mode
packRtpSmallFrame(ptr, len, pts, is_mark, gop_pos);
} else {
//STAP-A模式打包会大于MTU,所以采用FU-A模式
// STAP-A模式打包会大于MTU,所以采用FU-A模式 [AUTO-TRANSLATED:f3923abc]
// STAP-A mode packaging will be larger than MTU, so FU-A mode is used
packRtpFu(ptr, len, pts, is_mark, gop_pos);
}
}
@ -214,12 +247,14 @@ void H264RtpEncoder::packRtp(const char *ptr, size_t len, uint64_t pts, bool is_
void H264RtpEncoder::packRtpFu(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
auto packet_size = getRtpInfo().getMaxSize() - 2;
if (len <= packet_size + 1) {
// 小于FU-A打包最小字节长度要求采用STAP-A/Single NAL unit packet per H.264 模式
// 小于FU-A打包最小字节长度要求采用STAP-A/Single NAL unit packet per H.264 模式 [AUTO-TRANSLATED:b83bb4d1]
// Less than the minimum byte length requirement for FU-A packaging, use STAP-A/Single NAL unit packet per H.264 mode
packRtpSmallFrame(ptr, len, pts, is_mark, gop_pos);
return;
}
//末尾5bit为nalu type固定为28(FU-A)
// 末尾5bit为nalu type固定为28(FU-A) [AUTO-TRANSLATED:6293f1a9]
// The last 5 bits are the nalu type, fixed to 28 (FU-A)
auto fu_char_0 = (ptr[0] & (~0x1F)) | 28;
auto fu_char_1 = H264_TYPE(ptr[0]);
FuFlags *fu_flags = (FuFlags *) (&fu_char_1);
@ -233,17 +268,23 @@ void H264RtpEncoder::packRtpFu(const char *ptr, size_t len, uint64_t pts, bool i
fu_flags->end_bit = 1;
}
//传入nullptr先不做payload的内存拷贝
// 传入nullptr先不做payload的内存拷贝 [AUTO-TRANSLATED:1858cf77]
// Pass in nullptr first, do not copy the payload memory
auto rtp = getRtpInfo().makeRtp(TrackVideo, nullptr, packet_size + 2, fu_flags->end_bit && is_mark, pts);
//rtp payload 负载部分
// rtp payload 负载部分 [AUTO-TRANSLATED:aecf73cc]
// rtp payload load part
uint8_t *payload = rtp->getPayload();
//FU-A 第1个字节
// FU-A 第1个字节 [AUTO-TRANSLATED:b5558495]
// FU-A first byte
payload[0] = fu_char_0;
//FU-A 第2个字节
// FU-A 第2个字节 [AUTO-TRANSLATED:6b4540bb]
// FU-A second byte
payload[1] = fu_char_1;
//H264 数据
// H264 数据 [AUTO-TRANSLATED:79204239]
// H264 data
memcpy(payload + 2, (uint8_t *) ptr + offset, packet_size);
//输入到rtp环形缓存
// 输入到rtp环形缓存 [AUTO-TRANSLATED:5208ef90]
// Input to the rtp ring buffer
RtpCodec::inputRtp(rtp, gop_pos);
offset += packet_size;
@ -261,7 +302,8 @@ void H264RtpEncoder::packRtpSmallFrame(const char *data, size_t len, uint64_t pt
}
void H264RtpEncoder::packRtpStapA(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
// 如果帧长度不超过mtu,为了兼容性 webrtc采用STAP-A模式打包
// 如果帧长度不超过mtu,为了兼容性 webrtc采用STAP-A模式打包 [AUTO-TRANSLATED:a091199c]
// If the frame length does not exceed mtu, for compatibility with webrtc, use STAP-A mode packaging
auto rtp = getRtpInfo().makeRtp(TrackVideo, nullptr, len + 3, is_mark, pts);
uint8_t *payload = rtp->getPayload();
//STAP-A
@ -274,7 +316,8 @@ void H264RtpEncoder::packRtpStapA(const char *ptr, size_t len, uint64_t pts, boo
}
void H264RtpEncoder::packRtpSingleNalu(const char *data, size_t len, uint64_t pts, bool is_mark, bool gop_pos) {
// Single NAL unit packet per H.264 模式
// Single NAL unit packet per H.264 模式 [AUTO-TRANSLATED:9332a8e4]
// Single NAL unit packet per H.264 mode
RtpCodec::inputRtp(getRtpInfo().makeRtp(TrackVideo, data, len, is_mark, pts), gop_pos);
}
@ -300,7 +343,8 @@ bool H264RtpEncoder::inputFrame(const Frame::Ptr &frame) {
inputFrame_l(frame, true);
} else {
if (_last_frame) {
//如果时间戳发生了变化那么markbit才置true
// 如果时间戳发生了变化那么markbit才置true [AUTO-TRANSLATED:19b68429]
// If the timestamp changes, then the markbit is set to true
inputFrame_l(_last_frame, _last_frame->pts() != frame->pts());
}
_last_frame = Frame::getCacheAbleFrame(frame);
@ -310,7 +354,8 @@ bool H264RtpEncoder::inputFrame(const Frame::Ptr &frame) {
void H264RtpEncoder::flush() {
if (_last_frame) {
// 如果时间戳发生了变化那么markbit才置true
// 如果时间戳发生了变化那么markbit才置true [AUTO-TRANSLATED:6b1d0fe0]
// If the timestamp changes, then the markbit is set to true
inputFrame_l(_last_frame, true);
_last_frame = nullptr;
}
@ -318,7 +363,8 @@ void H264RtpEncoder::flush() {
bool H264RtpEncoder::inputFrame_l(const Frame::Ptr &frame, bool is_mark){
if (frame->keyFrame()) {
//保证每一个关键帧前都有SPS与PPS
// 保证每一个关键帧前都有SPS与PPS [AUTO-TRANSLATED:9d1a9d5e]
// Ensure that there are SPS and PPS before each key frame
insertConfigFrame(frame->pts());
}
packRtp(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize(), frame->pts(), is_mark, false);

View File

@ -22,6 +22,11 @@ namespace mediakit {
* h264 rtp解码类
* h264 over rtsp-rtp h264-Frame
* rfc3984
* h264 rtp decoder class
* Demultiplex h264-Frame from h264 over rtsp-rtp
* rfc3984
* [AUTO-TRANSLATED:84b4831b]
*/
class H264RtpDecoder : public RtpCodec{
public:
@ -33,6 +38,11 @@ public:
* 264 rtp包
* @param rtp rtp包
* @param key_pos
* Input 264 rtp packet
* @param rtp rtp packet
* @param key_pos This parameter is ignored
* [AUTO-TRANSLATED:a9ed29db]
*/
bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = true) override;
@ -56,6 +66,9 @@ private:
/**
* 264 rtp打包类
* 264 rtp packaging class
* [AUTO-TRANSLATED:baed5b50]
*/
class H264RtpEncoder : public RtpCodec {
public:
@ -64,11 +77,19 @@ public:
/**
* 264
* @param frame
* Input 264 frame
* @param frame Frame data, required
* [AUTO-TRANSLATED:1190bc60]
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* frame缓存
* Flush all frame buffers in the output
* [AUTO-TRANSLATED:adaea568]
*/
void flush() override;
@ -87,6 +108,6 @@ private:
Frame::Ptr _last_frame;
};
}//namespace mediakit{
}//namespace mediakit
#endif //ZLMEDIAKIT_H264RTPCODEC_H

View File

@ -106,30 +106,35 @@ bool H265Track::inputFrame(const Frame::Ptr &frame) {
}
bool H265Track::inputFrame_l(const Frame::Ptr &frame) {
if (frame->keyFrame()) {
insertConfigFrame(frame);
_is_idr = true;
return VideoTrack::inputFrame(frame);
}
_is_idr = false;
int type = H265_TYPE(frame->data()[frame->prefixSize()]);
bool ret = true;
//非idr帧
switch (H265_TYPE( frame->data()[frame->prefixSize()])) {
switch (type) {
case H265Frame::NAL_VPS: {
_vps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
_latest_is_config_frame = true;
ret = VideoTrack::inputFrame(frame);
break;
}
case H265Frame::NAL_SPS: {
_sps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
_latest_is_config_frame = true;
ret = VideoTrack::inputFrame(frame);
break;
}
case H265Frame::NAL_PPS: {
_pps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
_latest_is_config_frame = true;
ret = VideoTrack::inputFrame(frame);
break;
}
default: {
// 判断是否是I帧, 并且如果是,那判断前面是否插入过config帧, 如果插入过就不插入了
if (frame->keyFrame() && !_latest_is_config_frame) {
insertConfigFrame(frame);
}
if (!frame->dropAble()) {
_latest_is_config_frame = false;
}
ret = VideoTrack::inputFrame(frame);
break;
}
@ -199,9 +204,6 @@ Track::Ptr H265Track::clone() const {
}
void H265Track::insertConfigFrame(const Frame::Ptr &frame) {
if (_is_idr) {
return;
}
if (!_vps.empty()) {
VideoTrack::inputFrame(createConfigFrame<H265Frame>(_vps, frame->dts(), frame->getIndex()));
}
@ -217,6 +219,9 @@ void H265Track::insertConfigFrame(const Frame::Ptr &frame) {
/**
* h265类型sdp
* h265 type sdp
* [AUTO-TRANSLATED:4418a7df]
*/
class H265Sdp : public Sdp {
public:
@ -226,9 +231,17 @@ public:
* @param pps 265 pps,0x00000001
* @param payload_type rtp payload type 96
* @param bitrate
* Constructor
* @param sps 265 sps, without 0x00000001 header
* @param pps 265 pps, without 0x00000001 header
* @param payload_type rtp payload type, default 96
* @param bitrate Bitrate
* [AUTO-TRANSLATED:93f4ec48]
*/
H265Sdp(const string &strVPS, const string &strSPS, const string &strPPS, int payload_type, int bitrate) : Sdp(90000, payload_type) {
//视频通道
// 视频通道 [AUTO-TRANSLATED:642ca881]
// Video channel
_printer << "m=video 0 RTP/AVP " << payload_type << "\r\n";
if (bitrate) {
_printer << "b=AS:" << bitrate << "\r\n";
@ -274,7 +287,8 @@ Track::Ptr getTrackBySdp(const SdpTrack::Ptr &track) {
auto sps = decodeBase64(map["sprop-sps"]);
auto pps = decodeBase64(map["sprop-pps"]);
if (sps.empty() || pps.empty()) {
// 如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps
// 如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps [AUTO-TRANSLATED:9300510b]
// If there is no sps/pps in the sdp, then it may be possible to recover sps/pps from the subsequent rtp
return std::make_shared<H265Track>();
}
return std::make_shared<H265Track>(vps, sps, pps, 0, 0, 0);

View File

@ -65,7 +65,8 @@ public:
bool keyFrame() const override {
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
auto type = H265_TYPE(*nal_ptr);
// 参考自FFmpeg: IRAP VCL NAL unit types span the range
// 参考自FFmpeg: IRAP VCL NAL unit types span the range [AUTO-TRANSLATED:45413c06]
// Referenced from FFmpeg: IRAP VCL NAL unit types span the range
// [BLA_W_LP (16), RSV_IRAP_VCL23 (23)].
return (type >= NAL_BLA_W_LP && type <= NAL_RSV_IRAP_VCL23) && decodeAble() ;
}
@ -93,24 +94,35 @@ public:
bool decodeAble() const override {
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
auto type = H265_TYPE(*nal_ptr);
//多slice情况下, first_slice_segment_in_pic_flag 表示其为一帧的开始
// 多slice情况下, first_slice_segment_in_pic_flag 表示其为一帧的开始 [AUTO-TRANSLATED:0427551b]
// In the case of multiple slices, first_slice_segment_in_pic_flag indicates the beginning of a frame
return type >= NAL_TRAIL_N && type <= NAL_RSV_IRAP_VCL23 && (nal_ptr[2] & 0x80);
}
};
/**
* 265
* 265 frame class
* [AUTO-TRANSLATED:9141a4be]
*/
using H265Frame = H265FrameHelper<FrameImp>;
/**
* H265类
* Frame类
* H265 class to prevent memory copying
* Users can quickly wrap a pointer into a Frame class without copying through this type
* [AUTO-TRANSLATED:44bde991]
*/
using H265FrameNoCacheAble = H265FrameHelper<FrameFromPtr>;
/**
* 265
* 265 video channel
* [AUTO-TRANSLATED:27c65a36]
*/
class H265Track : public VideoTrack {
public:
@ -119,6 +131,10 @@ public:
/**
* sps pps构造h265类型的媒体
* inputFrame中获取sps pps
* Construct a h265 media without specifying sps pps
* Get sps pps in the subsequent inputFrame
* [AUTO-TRANSLATED:bf86e048]
*/
H265Track() = default;
@ -130,6 +146,16 @@ public:
* @param vps_prefix_len 265340x00 00 00 01
* @param sps_prefix_len 265340x00 00 00 01
* @param pps_prefix_len 265340x00 00 00 01
* Construct a h265 media
* @param vps vps frame data
* @param sps sps frame data
* @param pps pps frame data
* @param vps_prefix_len 265 header length, can be 3 or 4 bytes, generally 0x00 00 00 01
* @param sps_prefix_len 265 header length, can be 3 or 4 bytes, generally 0x00 00 00 01
* @param pps_prefix_len 265 header length, can be 3 or 4 bytes, generally 0x00 00 00 01
* [AUTO-TRANSLATED:a8c42d9f]
*/
H265Track(const std::string &vps,const std::string &sps, const std::string &pps,int vps_prefix_len = 4, int sps_prefix_len = 4, int pps_prefix_len = 4);
@ -151,7 +177,7 @@ private:
void insertConfigFrame(const Frame::Ptr &frame);
private:
bool _is_idr = false;
bool _latest_is_config_frame = false;
int _width = 0;
int _height = 0;
float _fps = 0;

View File

@ -22,12 +22,14 @@ namespace mediakit {
void H265RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
if (_info.codec == CodecInvalid) {
// 先判断是否为增强型rtmp
// 先判断是否为增强型rtmp [AUTO-TRANSLATED:86c4f86a]
// First, determine if it is an enhanced rtmp
parseVideoRtmpPacket((uint8_t *)pkt->data(), pkt->size(), &_info);
}
if (_info.is_enhanced) {
// 增强型rtmp
// 增强型rtmp [AUTO-TRANSLATED:d7d72114]
// Enhanced rtmp
parseVideoRtmpPacket((uint8_t *)pkt->data(), pkt->size(), &_info);
if (!_info.is_enhanced || _info.codec != CodecH265) {
throw std::invalid_argument("Invalid enhanced-rtmp hevc packet!");
@ -61,7 +63,8 @@ void H265RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
return;
}
// 国内扩展(12) H265 rtmp
// 国内扩展(12) H265 rtmp [AUTO-TRANSLATED:ba272139]
// Domestic extension (12) H265 rtmp
if (pkt->isConfigFrame()) {
CHECK_RET(pkt->size() > 5);
getTrack()->setExtraData((uint8_t *)pkt->data() + 5, pkt->size() - 5);
@ -133,7 +136,8 @@ bool H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
_rtmp_packet->chunk_id = CHUNK_VIDEO;
_rtmp_packet->stream_index = STREAM_MEDIA;
_rtmp_packet->type_id = MSG_VIDEO;
// 输出rtmp packet
// 输出rtmp packet [AUTO-TRANSLATED:d72e89a7]
// Output rtmp packet
RtmpCodec::inputRtmp(_rtmp_packet);
_rtmp_packet = nullptr;
}, &_rtmp_packet->buffer);

View File

@ -19,6 +19,10 @@ namespace mediakit {
/**
* h265 Rtmp解码类
* h265 over rtmp h265-Frame
* h265 Rtmp decoder class
* Demultiplex h265-Frame from h265 over rtmp
* [AUTO-TRANSLATED:2768e4bd]
*/
class H265RtmpDecoder : public RtmpCodec {
public:
@ -29,6 +33,10 @@ public:
/**
* 265 Rtmp包
* @param rtmp Rtmp包
* Input 265 Rtmp packet
* @param rtmp Rtmp packet
* [AUTO-TRANSLATED:63dbe33f]
*/
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
@ -42,6 +50,9 @@ protected:
/**
* 265 Rtmp打包类
* 265 Rtmp packaging class
* [AUTO-TRANSLATED:5891c800]
*/
class H265RtmpEncoder : public RtmpCodec {
public:
@ -52,22 +63,39 @@ public:
* track不为空且包含sps pps信息
* inputFrame时可以不输入sps pps
* @param track
* Constructor, track can be empty, in which case sps pps is input when inputFrame
* If track is not empty and contains sps pps information,
* Then sps pps can be omitted when inputFrame
* @param track
* [AUTO-TRANSLATED:e61fdfed]
*/
H265RtmpEncoder(const Track::Ptr &track) : RtmpCodec(track) {}
/**
* 265sps pps
* @param frame
* Input 265 frame, sps pps can be omitted
* @param frame Frame data
* [AUTO-TRANSLATED:7d1be1e7]
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* frame缓存
* Flush all frame cache output
* [AUTO-TRANSLATED:adaea568]
*/
void flush() override;
/**
* config包
* Generate config packet
* [AUTO-TRANSLATED:8f851364]
*/
void makeConfigPacket() override;

View File

@ -13,7 +13,8 @@
namespace mediakit{
//https://datatracker.ietf.org/doc/rfc7798/
//H265 nalu 头两个字节的定义
// H265 nalu 头两个字节的定义 [AUTO-TRANSLATED:d896dd59]
// H265 nalu header definition of the first two bytes
/*
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
@ -71,7 +72,8 @@ H265Frame::Ptr H265RtpDecoder::obtainFrame() {
*/
bool H265RtpDecoder::unpackAp(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssize_t size, uint64_t stamp){
bool have_key_frame = false;
//忽略PayloadHdr
// 忽略PayloadHdr [AUTO-TRANSLATED:9868ddb5]
// Ignore PayloadHdr
CHECK_SIZE(size, 2, have_key_frame);
ptr += 2;
size -= 2;
@ -125,7 +127,8 @@ bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz
auto e_bit = (ptr[2] >> 6) & 0x01;
auto type = ptr[2] & 0x3f;
if (s_bit) {
//该帧的第一个rtp包
// 该帧的第一个rtp包 [AUTO-TRANSLATED:a9581a23]
// The first rtp packet of this frame
_frame->_buffer.assign("\x00\x00\x00\x01", 4);
_frame->_buffer.push_back((type << 1) | (ptr[0] & 0x81));
_frame->_buffer.push_back(ptr[1]);
@ -134,22 +137,26 @@ bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz
}
if (_fu_dropped) {
//该帧不完整
// 该帧不完整 [AUTO-TRANSLATED:6bd7eca7]
// This frame is incomplete
return false;
}
if (!s_bit && seq != (uint16_t) (_last_seq + 1)) {
//中间的或末尾的rtp包其seq必须连续否则说明rtp丢包那么该帧不完整必须得丢弃
// 中间的或末尾的rtp包其seq必须连续否则说明rtp丢包那么该帧不完整必须得丢弃 [AUTO-TRANSLATED:6953b332]
// The middle or end rtp packet, its seq must be continuous, otherwise it means rtp packet loss, then this frame is incomplete and must be discarded
_fu_dropped = true;
_frame->_buffer.clear();
return false;
}
//跳过PayloadHdr + FU header
// 跳过PayloadHdr + FU header [AUTO-TRANSLATED:51ec6760]
// Skip PayloadHdr + FU header
ptr += 3;
size -= 3;
if (_using_donl_field) {
//DONL确保不少于2个字节
// DONL确保不少于2个字节 [AUTO-TRANSLATED:7e72ecc1]
// DONL must be no less than 2 bytes
CHECK_SIZE(size, 2, false);
uint16_t donl = AV_RB16(ptr);
size -= 2;
@ -158,17 +165,21 @@ bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz
CHECK_SIZE(size, 1, false);
//后面追加数据
// 后面追加数据 [AUTO-TRANSLATED:248516e9]
// Append data later
_frame->_buffer.append((char *) ptr, size);
if (!e_bit) {
//非末尾包
// 非末尾包 [AUTO-TRANSLATED:2e43ac3c]
// Non-end packet
return s_bit ? (_frame->keyFrame() || _frame->configFrame()) : false;
}
//确保下一次fu必须收到第一个包
// 确保下一次fu必须收到第一个包 [AUTO-TRANSLATED:491d81ec]
// Ensure that the next fu must receive the first packet
_fu_dropped = true;
//该帧最后一个rtp包
// 该帧最后一个rtp包 [AUTO-TRANSLATED:ea395f0e]
// The last rtp packet of this frame
outputFrame(rtp, _frame);
return false;
}
@ -182,14 +193,16 @@ bool H265RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool) {
WarnL << "start drop h265 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString();
}
_last_seq = seq;
// 确保有sps rtp的时候gop从sps开始否则从关键帧开始
// 确保有sps rtp的时候gop从sps开始否则从关键帧开始 [AUTO-TRANSLATED:115ae07c]
// Ensure that when there is sps rtp, gop starts from sps; otherwise, it starts from the key frame
return _is_gop && !last_is_gop;
}
bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
auto payload_size = rtp->getPayloadSize();
if (payload_size <= 0) {
//无实际负载
// 无实际负载 [AUTO-TRANSLATED:305af48f]
// No actual payload
return false;
}
auto frame = rtp->getPayload();
@ -229,10 +242,12 @@ bool H265RtpDecoder::singleFrame(const RtpPacket::Ptr &rtp, const uint8_t *ptr,
void H265RtpDecoder::outputFrame(const RtpPacket::Ptr &rtp, const H265Frame::Ptr &frame) {
if (frame->dropAble()) {
//不参与dts生成
// 不参与dts生成 [AUTO-TRANSLATED:dff3b747]
// Not involved in dts generation
frame->_dts = frame->_pts;
} else {
//rtsp没有dts那么根据pts排序算法生成dts
// rtsp没有dts那么根据pts排序算法生成dts [AUTO-TRANSLATED:f37c17f3]
// rtsp does not have dts, so dts is generated according to the pts sorting algorithm
_dts_generator.getDts(frame->_pts, frame->_dts);
}
@ -270,19 +285,26 @@ void H265RtpEncoder::packRtpFu(const char *ptr, size_t len, uint64_t pts, bool i
}
{
// 传入nullptr先不做payload的内存拷贝
// 传入nullptr先不做payload的内存拷贝 [AUTO-TRANSLATED:7ed49f0a]
// Pass in nullptr first, do not copy the payload memory
auto rtp = getRtpInfo().makeRtp(TrackVideo, nullptr, max_size + 3, mark_bit, pts);
// rtp payload 负载部分
// rtp payload 负载部分 [AUTO-TRANSLATED:03a5ef9b]
// rtp payload load part
uint8_t *payload = rtp->getPayload();
// FU 第1个字节表明为FU
// FU 第1个字节表明为FU [AUTO-TRANSLATED:9cf07fda]
// FU first byte, indicating FU
payload[0] = 49 << 1;
// FU 第2个字节貌似固定为1
// FU 第2个字节貌似固定为1 [AUTO-TRANSLATED:77983091]
// FU second byte seems to be fixed to 1
payload[1] = ptr[1]; // 1;
// FU 第3个字节
// FU 第3个字节 [AUTO-TRANSLATED:c627abd0]
// FU third byte
payload[2] = s_e_flags;
// H265 数据
// H265 数据 [AUTO-TRANSLATED:a2c3135f]
// H265 data
memcpy(payload + 3, ptr + offset, max_size);
// 输入到rtp环形缓存
// 输入到rtp环形缓存 [AUTO-TRANSLATED:6bafd42b]
// Input to rtp ring buffer
RtpCodec::inputRtp(rtp, fu_start && gop_pos);
}
@ -296,7 +318,8 @@ void H265RtpEncoder::packRtp(const char *ptr, size_t len, uint64_t pts, bool is_
//signal-nalu
RtpCodec::inputRtp(getRtpInfo().makeRtp(TrackVideo, ptr, len, is_mark, pts), gop_pos);
} else {
//FU-A模式
// FU-A模式 [AUTO-TRANSLATED:a273a49c]
// FU-A mode
packRtpFu(ptr, len, pts, is_mark, gop_pos);
}
}
@ -305,7 +328,8 @@ void H265RtpEncoder::insertConfigFrame(uint64_t pts){
WarnL<<" not ok";
return;
}
//gop缓存从vps 开始vps ,sps、pps后面还有时间戳相同的关键帧所以mark bit为false
// gop缓存从vps 开始vps ,sps、pps后面还有时间戳相同的关键帧所以mark bit为false [AUTO-TRANSLATED:2534b06f]
// gop cache starts from vps, vps, sps, pps followed by key frames with the same timestamp, so mark bit is false
packRtp(_vps->data() + _vps->prefixSize(), _vps->size() - _vps->prefixSize(), pts, false, true);
packRtp(_sps->data() + _sps->prefixSize(), _sps->size() - _sps->prefixSize(), pts, false, false);
packRtp(_pps->data() + _pps->prefixSize(), _pps->size() - _pps->prefixSize(), pts, false, false);
@ -313,7 +337,8 @@ void H265RtpEncoder::insertConfigFrame(uint64_t pts){
}
bool H265RtpEncoder::inputFrame_l(const Frame::Ptr &frame, bool is_mark){
if (frame->keyFrame()) {
//保证每一个关键帧前都有SPS PPS VPS
// 保证每一个关键帧前都有SPS PPS VPS [AUTO-TRANSLATED:9189f8d7]
// Ensure that there are SPS PPS VPS before each key frame
insertConfigFrame(frame->pts());
}
packRtp(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize(), frame->pts(), is_mark, false);
@ -347,7 +372,8 @@ bool H265RtpEncoder::inputFrame(const Frame::Ptr &frame) {
inputFrame_l(frame, true);
} else {
if (_last_frame) {
//如果时间戳发生了变化那么markbit才置true
// 如果时间戳发生了变化那么markbit才置true [AUTO-TRANSLATED:19b68429]
// If the timestamp changes, then markbit is set to true
inputFrame_l(_last_frame, _last_frame->pts() != frame->pts());
}
_last_frame = Frame::getCacheAbleFrame(frame);
@ -357,7 +383,8 @@ bool H265RtpEncoder::inputFrame(const Frame::Ptr &frame) {
void H265RtpEncoder::flush() {
if (_last_frame) {
// 如果时间戳发生了变化那么markbit才置true
// 如果时间戳发生了变化那么markbit才置true [AUTO-TRANSLATED:6b1d0fe0]
// If the timestamp changes, then markbit is set to true
inputFrame_l(_last_frame, true);
_last_frame = nullptr;
}

View File

@ -22,6 +22,11 @@ namespace mediakit {
* h265 rtp解码类
* h265 over rtsp-rtp h265-Frame
* H265-over-RTPdraft-ietf-payload-rtp-h265-07.pdf
* h265 rtp decoder class
* Demultiplex h265-Frame from h265 over rtsp-rtp
* Draft (H265-over-RTP) draft-ietf-payload-rtp-h265-07.pdf
* [AUTO-TRANSLATED:24e7e278]
*/
class H265RtpDecoder : public RtpCodec {
public:
@ -33,6 +38,11 @@ public:
* 265 rtp包
* @param rtp rtp包
* @param key_pos
* Input 265 rtp packet
* @param rtp rtp packet
* @param key_pos This parameter is ignored
* [AUTO-TRANSLATED:35e8fa1d]
*/
bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = true) override;
@ -57,6 +67,9 @@ private:
/**
* 265 rtp打包类
* 265 rtp packer class
* [AUTO-TRANSLATED:4b3f96fe]
*/
class H265RtpEncoder : public RtpCodec {
public:
@ -65,11 +78,19 @@ public:
/**
* 265
* @param frame
* Input 265 frame
* @param frame Frame data, required
* [AUTO-TRANSLATED:48454707]
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* frame缓存
* Flush all frame cache in output
* [AUTO-TRANSLATED:adaea568]
*/
void flush() override;
@ -85,6 +106,6 @@ private:
Frame::Ptr _last_frame;
};
}//namespace mediakit{
}//namespace mediakit
#endif //ZLMEDIAKIT_H265RTPCODEC_H

View File

@ -42,11 +42,16 @@ public:
/**
* JPEG/MJPEG帧
* @param pix_type pixel format type; AV_PIX_FMT_YUVJ422P || (AVCOL_RANGE_JPEG && AV_PIX_FMT_YUV422P) : 1; AV_PIX_FMT_YUVJ420P || (AVCOL_RANGE_JPEG && AV_PIX_FMT_YUV420P) : 0
* JPEG/MJPEG frame
* @param pix_type pixel format type; AV_PIX_FMT_YUVJ422P || (AVCOL_RANGE_JPEG && AV_PIX_FMT_YUV422P) : 1; AV_PIX_FMT_YUVJ420P || (AVCOL_RANGE_JPEG && AV_PIX_FMT_YUV420P) : 0
* [AUTO-TRANSLATED:d746e541]
*/
template <typename... ARGS>
JPEGFrame(uint8_t pix_type, ARGS &&...args) : Parent(std::forward<ARGS>(args)...) {
_pix_type = pix_type;
// JFIF头固定20个字节长度
// JFIF头固定20个字节长度 [AUTO-TRANSLATED:bd63b447]
// JFIF header is fixed at 20 bytes in length
CHECK(this->size() > kJFIFSize);
}
size_t prefixSize() const override { return 0; }

View File

@ -632,7 +632,8 @@ void JPEGRtpEncoder::rtpSendJpeg(const uint8_t *buf, int size, uint64_t pts, uin
for (j = 0; j < tables; j++)
qtables[nb_qtables + j] = buf + i + 5 + j * 65;
nb_qtables += tables;
// 大致忽略DQT/qtable所占字节数提高搜寻速度
// 大致忽略DQT/qtable所占字节数提高搜寻速度 [AUTO-TRANSLATED:63423997]
// Roughly ignore the number of bytes occupied by DQT/qtable to improve search speed
i += tables << 6;
} else if (buf[i + 1] == SOF0) {
if (buf[i + 14] != 17 || buf[i + 17] != 17) {
@ -642,7 +643,8 @@ void JPEGRtpEncoder::rtpSendJpeg(const uint8_t *buf, int size, uint64_t pts, uin
}
h = (buf[i + 5] * 256 + buf[i + 6]) / 8;
w = (buf[i + 7] * 256 + buf[i + 8]) / 8;
// 大致忽略SOF0所占字节数提高搜寻速度
// 大致忽略SOF0所占字节数提高搜寻速度 [AUTO-TRANSLATED:438cbf70]
// Roughly ignore the number of bytes occupied by SOF0 to improve search speed
i += 16;
} else if (buf[i + 1] == DHT) {
int dht_size = AV_RB16(&buf[i + 2]);
@ -811,7 +813,8 @@ bool JPEGRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool) {
auto seq = rtp->getSeq();
auto marker = rtp->getHeader()->mark;
if (size <= 0) {
//无实际负载
// 无实际负载 [AUTO-TRANSLATED:305af48f]
// No actual load
return false;
}
@ -829,7 +832,8 @@ bool JPEGRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool) {
////////////////////////////////////////////////////////////////////////
bool JPEGRtpEncoder::inputFrame(const Frame::Ptr &frame) {
// JFIF头固定20个字节长度
// JFIF头固定20个字节长度 [AUTO-TRANSLATED:bd63b447]
// JFIF header is fixed at 20 bytes in length
auto ptr = (uint8_t *)frame->data() + frame->prefixSize() + JPEGFrameImp::kJFIFSize;
auto len = frame->size() - frame->prefixSize() - JPEGFrameImp::kJFIFSize;
auto pts = frame->pts();

View File

@ -19,6 +19,9 @@ struct PayloadContext {
/**
* rtp解码类
* General rtp decoding class
* [AUTO-TRANSLATED:41b57089]
*/
class JPEGRtpDecoder : public RtpCodec {
public:
@ -30,6 +33,12 @@ public:
* rtp并解码
* @param rtp rtp数据包
* @param key_pos false,
* Input rtp and decode
* @param rtp rtp data packet
* @param key_pos This parameter is internally forced to false, please ignore it
* [AUTO-TRANSLATED:2993fcbe]
*/
bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override;

View File

@ -20,6 +20,9 @@ namespace mediakit {
/**
* L16类型SDP
* L16 type SDP
* [AUTO-TRANSLATED:11b1196d]
*/
class L16Sdp : public Sdp {
public:
@ -29,6 +32,14 @@ public:
* @param channels
* @param sample_rate
* @param bitrate
* L16 sampling bit width is fixed to 16 bits
* @param payload_type rtp payload type
* @param channels number of channels
* @param sample_rate audio sampling rate
* @param bitrate bitrate
* [AUTO-TRANSLATED:7a08a400]
*/
L16Sdp(int payload_type, int sample_rate, int channels, int bitrate) : Sdp(sample_rate, payload_type) {
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";

View File

@ -18,6 +18,10 @@ namespace mediakit {
/**
* L16音频通道
* L16 audio channel
* [AUTO-TRANSLATED:7a4b086f]
*/
class L16Track : public AudioTrackImp{
public:

View File

@ -20,6 +20,9 @@ namespace mediakit {
/**
* Opus类型SDP
* Opus type SDP
* [AUTO-TRANSLATED:6c0a72ed]
*/
class OpusSdp : public Sdp {
public:
@ -29,6 +32,14 @@ public:
* @param sample_rate
* @param channels
* @param bitrate
* Construct opus sdp
* @param payload_type rtp payload type
* @param sample_rate audio sample rate
* @param channels number of channels
* @param bitrate bitrate
* [AUTO-TRANSLATED:40713e9d]
*/
OpusSdp(int payload_type, int sample_rate, int channels, int bitrate) : Sdp(sample_rate, payload_type) {
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";

View File

@ -18,6 +18,9 @@ namespace mediakit {
/**
* Opus帧音频通道
* Opus frame audio channel
* [AUTO-TRANSLATED:522e95da]
*/
class OpusTrack : public AudioTrackImp{
public:
@ -25,11 +28,13 @@ public:
OpusTrack() : AudioTrackImp(CodecOpus,48000,2,16){}
private:
//克隆该Track
// 克隆该Track [AUTO-TRANSLATED:9a15682a]
// Clone this Track
Track::Ptr clone() const override {
return std::make_shared<OpusTrack>(*this);
}
//生成sdp
// 生成sdp [AUTO-TRANSLATED:663a9367]
// Generate sdp
Sdp::Ptr getSdp(uint8_t payload_type) const override ;
};

View File

@ -2155,7 +2155,8 @@ exit:
void h264GetWidthHeight(T_SPS *ptSps, int *piWidth, int *piHeight)
{
// ¿í¸ß¼ÆË㹫ʽ
// ¿í¸ß¼ÆË㹫ʽ [AUTO-TRANSLATED:e2d61727]
// What is the frame rate
int iCodeWidth = 0;
int iCodedHeight = 0;
iCodeWidth = 16 * ptSps->iMbWidth;
@ -2278,9 +2279,43 @@ void h265GeFramerate(T_HEVCVPS *ptVps, T_HEVCSPS *ptSps,float *pfFramerate)
*pfFramerate = (float)(ptSps->tVui.u32VuiTimeScale) / (float)(ptSps->tVui.u32VuiNumUnitsInTick);
}
else{
//vps sps可能不包含帧率
// vps sps可能不包含帧率 [AUTO-TRANSLATED:15424320]
// vps sps may not contain frame rate
*pfFramerate = 0.0F;
RPT(RPT_WRN, "frame rate:0");
}
}
int h265ParsePps(T_GetBitContext *ptGetBitContext, T_HEVC_PPS *ptPps)
{
int iRet = 0;
ptPps->pps_pic_parameter_set_id = parseUe(ptGetBitContext);
ptPps->pps_seq_parameter_set_id = parseUe(ptGetBitContext);
ptPps->dependent_slice_segments_enabled_flag = getOneBit(ptGetBitContext);
ptPps->output_flag_present_flag = getOneBit(ptGetBitContext);
ptPps->num_extra_slice_header_bits = getBits(ptGetBitContext, 3);
ptPps->sign_data_hiding_enabled_flag = getOneBit(ptGetBitContext);
ptPps->cabac_init_present_flag = getOneBit(ptGetBitContext);
ptPps->num_ref_idx_l0_default_active_minus1 = parseUe(ptGetBitContext);
ptPps->num_ref_idx_l1_default_active_minus1 = parseUe(ptGetBitContext);
ptPps->init_qp_minus26 = parseSe(ptGetBitContext);
ptPps->constrained_intra_pred_flag = getOneBit(ptGetBitContext);
ptPps->transform_skip_enabled_flag = getOneBit(ptGetBitContext);
ptPps->cu_qp_delta_enabled_flag = getOneBit(ptGetBitContext);
if (ptPps->cu_qp_delta_enabled_flag) {
ptPps->diff_cu_qp_delta_depth = parseUe(ptGetBitContext);
}
ptPps->pps_cb_qp_offset = parseSe(ptGetBitContext);
ptPps->pps_cr_qp_offset = parseSe(ptGetBitContext);
ptPps->pps_slice_chroma_qp_offsets_present_flag = getOneBit(ptGetBitContext);
ptPps->weighted_pred_flag = getOneBit(ptGetBitContext);
ptPps->weighted_bipred_flag = getOneBit(ptGetBitContext);
ptPps->transquant_bypass_enabled_flag = getOneBit(ptGetBitContext);
ptPps->tiles_enabled_flag = getOneBit(ptGetBitContext);
ptPps->entropy_coding_sync_enabled_flag = getOneBit(ptGetBitContext);
return iRet;
}

View File

@ -144,6 +144,11 @@ typedef struct T_AVRational{
/***
* Sequence parameter set
* ¿É²Î¿¼H264±ê×¼µÚ7½ÚºÍ¸½Â¼D E
/***
* Sequence parameter set
* H.264 sequence parameter set, version 7 and above, D E
* [AUTO-TRANSLATED:bd590cb8]
*/
#define Extended_SAR 255
@ -437,6 +442,62 @@ typedef struct T_HEVCSPS {
int iVuiPresent;
}T_HEVCSPS;
typedef struct {
int pps_pic_parameter_set_id;
int pps_seq_parameter_set_id;
int dependent_slice_segments_enabled_flag;
int output_flag_present_flag;
int num_extra_slice_header_bits;
int sign_data_hiding_enabled_flag;
int cabac_init_present_flag;
int num_ref_idx_l0_default_active_minus1;
int num_ref_idx_l1_default_active_minus1;
int init_qp_minus26;
int constrained_intra_pred_flag;
int transform_skip_enabled_flag;
int cu_qp_delta_enabled_flag;
int diff_cu_qp_delta_depth;
int pps_cb_qp_offset;
int pps_cr_qp_offset;
int pps_slice_chroma_qp_offsets_present_flag;
int weighted_pred_flag;
int weighted_bipred_flag;
int transquant_bypass_enabled_flag;
int tiles_enabled_flag;
int entropy_coding_sync_enabled_flag;
int uniform_spacing_flag;
int loop_filter_across_tiles_enabled_flag;
int pps_loop_filter_across_slices_enabled_flag;
int deblocking_filter_control_present_flag;
int deblocking_filter_override_enabled_flag;
int pps_deblocking_filter_disabled_flag;
int pps_beta_offset_div2;
int pps_tc_offset_div2;
int pps_scaling_list_data_present_flag;
int lists_modification_present_flag;
int log2_parallel_merge_level_minus2;
int slice_segment_header_extension_present_flag;
int pps_extension_present_flag;
int pps_range_extension_flag;
int pps_multilayer_extension_flag;
int pps_3d_extension_flag;
int pps_scc_extension_flag;
int pps_extension_4bits;
// PPS range extension fields
int log2_max_transform_skip_block_size_minus2;
int cross_component_prediction_enabled_flag;
int chroma_qp_offset_list_enabled_flag;
int diff_cu_chroma_qp_offset_depth;
int chroma_qp_offset_list_len_minus1;
int cb_qp_offset_list[6];
int cr_qp_offset_list[6];
int log2_sao_offset_scale_luma;
int log2_sao_offset_scale_chroma;
// 可以根据需要添加更多字段 [AUTO-TRANSLATED:57b9a7c1]
// You can add more fields as needed
} T_HEVC_PPS;
typedef struct T_GetBitContext{
uint8_t *pu8Buf; // buf
@ -446,7 +507,7 @@ typedef struct T_GetBitContext{
int iCurBitPos; // current bit position
}T_GetBitContext;
int h265ParsePps(T_GetBitContext *ptGetBitContext, T_HEVC_PPS *ptPps);
int h264DecSeqParameterSet(void *pvBuf, T_SPS *ptSps);
int h265DecSeqParameterSet( void *pvBufSrc, T_HEVCSPS *ptSps );
int h265DecVideoParameterSet( void *pvBufSrc, T_HEVCVPS *ptVps );

15
golang/tester/sdk_test.go Normal file
View File

@ -0,0 +1,15 @@
package tester
import (
"testing"
"time"
"zlmediakit/zlmediakit"
)
func TestServer(t *testing.T) {
zlmediakit.MK_env_init2(0, zlmediakit.LTrace, zlmediakit.LOG_CONSOLE|zlmediakit.LOG_CALLBACK|zlmediakit.LOG_FILE,
"log", 7, true, "../../conf/config.ini", true, "../../default.pem", "")
zlmediakit.MK_http_server_start(80, false)
zlmediakit.MK_http_server_start(443, true)
time.Sleep(1000 * time.Second)
}

View File

@ -0,0 +1,51 @@
package zlmediakit
/*
#include "mk_mediakit.h"
#cgo CFLAGS: -I../../api/include
#cgo LDFLAGS: -L../../release/linux/Debug/ -lmk_api
*/
import "C"
type LOG_MASK int
type LOG_LEVEL int
const (
LOG_CONSOLE LOG_MASK = 1 << 0
LOG_FILE LOG_MASK = 1 << 1
LOG_CALLBACK LOG_MASK = 2 << 1
)
const (
LTrace LOG_LEVEL = 0
LDebug LOG_LEVEL = 1
LInfo LOG_LEVEL = 2
LWarn LOG_LEVEL = 3
LError LOG_LEVEL = 4
)
func btoi(b bool) int {
if b {
return 1
}
return 0
}
func MK_env_init2(thread_num int, log_level LOG_LEVEL, log_mask LOG_MASK, log_file_path string, log_file_days int, ini_is_path bool, ini string, ssl_is_path bool, ssl string, ssl_pwd string) {
// 调用 C SDK 的函数
C.mk_env_init2(C.int(thread_num), C.int(log_level), C.int(log_mask), C.CString(log_file_path), C.int(log_file_days), C.int(btoi(ini_is_path)), C.CString(ini), C.int(btoi(ssl_is_path)), C.CString(ssl), C.CString(ssl_pwd))
}
func MK_stop_all_server() {
// 调用 C SDK 的函数
C.mk_stop_all_server()
}
func MK_set_log(file_max_size, file_max_count int) {
// 调用 C SDK 的函数
C.mk_set_log(C.int(file_max_size), C.int(file_max_count))
}
func MK_http_server_start(port uint16, ssl bool) {
C.mk_http_server_start(C.ushort(port), C.int(btoi(ssl)))
}

View File

@ -35,9 +35,11 @@ onceToken token([]() {
#else
string ffmpeg_bin = trim(System::execute("which ffmpeg"));
#endif
//默认ffmpeg命令路径为环境变量中路径
// 默认ffmpeg命令路径为环境变量中路径 [AUTO-TRANSLATED:40c35597]
// Default ffmpeg command path is the path in the environment variable
mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin;
//ffmpeg日志保存路径
// ffmpeg日志保存路径 [AUTO-TRANSLATED:e455732d]
// ffmpeg log save path
mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log";
mINI::Instance()[kCmd] = "%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kSnap] = "%s -i %s -y -f mjpeg -frames:v 1 -an %s";
@ -104,7 +106,8 @@ void FFmpegSource::play(const string &ffmpeg_cmd_key, const string &src_url, con
InfoL << cmd;
if (is_local_ip(_media_info.host)) {
// 推流给自己的,通过判断流是否注册上来判断是否正常
// 推流给自己的,通过判断流是否注册上来判断是否正常 [AUTO-TRANSLATED:423f2be6]
// Push stream to yourself, judge whether the stream is registered to determine whether it is normal
if (_media_info.schema != RTSP_SCHEMA && _media_info.schema != RTMP_SCHEMA) {
cb(SockException(Err_other, "本服务只支持rtmp/rtsp推流"));
return;
@ -113,41 +116,50 @@ void FFmpegSource::play(const string &ffmpeg_cmd_key, const string &src_url, con
findAsync(timeout_ms, [cb, weakSelf, timeout_ms](const MediaSource::Ptr &src) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
// 自己已经销毁
// 自己已经销毁 [AUTO-TRANSLATED:3d45c3b0]
// Self has been destroyed
return;
}
if (src) {
// 推流给自己成功
// 推流给自己成功 [AUTO-TRANSLATED:65dba71b]
// Push stream to yourself successfully
cb(SockException());
strongSelf->onGetMediaSource(src);
strongSelf->startTimer(timeout_ms);
return;
}
//推流失败
// 推流失败 [AUTO-TRANSLATED:4d8d226a]
// Push stream failed
if (!strongSelf->_process.wait(false)) {
// ffmpeg进程已经退出
// ffmpeg进程已经退出 [AUTO-TRANSLATED:04193893]
// ffmpeg process has exited
cb(SockException(Err_other, StrPrinter << "ffmpeg已经退出,exit code = " << strongSelf->_process.exit_code()));
return;
}
// ffmpeg进程还在线但是等待推流超时
// ffmpeg进程还在线但是等待推流超时 [AUTO-TRANSLATED:9f71f17b]
// ffmpeg process is still online, but waiting for the stream to timeout
cb(SockException(Err_other, "等待超时"));
});
} else{
//推流给其他服务器的通过判断FFmpeg进程是否在线判断是否成功
// 推流给其他服务器的通过判断FFmpeg进程是否在线判断是否成功 [AUTO-TRANSLATED:9b963da5]
// Push stream to other servers, judge whether it is successful by judging whether the FFmpeg process is online
weak_ptr<FFmpegSource> weakSelf = shared_from_this();
_timer = std::make_shared<Timer>(timeout_ms / 1000.0f, [weakSelf, cb, timeout_ms]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
// 自身已经销毁
// 自身已经销毁 [AUTO-TRANSLATED:5f954f8a]
// Self has been destroyed
return false;
}
// FFmpeg还在线那么我们认为推流成功
// FFmpeg还在线那么我们认为推流成功 [AUTO-TRANSLATED:4330df49]
// FFmpeg is still online, so we think the push stream is successful
if (strongSelf->_process.wait(false)) {
cb(SockException());
strongSelf->startTimer(timeout_ms);
return false;
}
// ffmpeg进程已经退出
// ffmpeg进程已经退出 [AUTO-TRANSLATED:04193893]
// ffmpeg process has exited
cb(SockException(Err_other, StrPrinter << "ffmpeg已经退出,exit code = " << strongSelf->_process.exit_code()));
return false;
}, _poller);
@ -162,9 +174,11 @@ void FFmpegSource::findAsync(int maxWaitMS, const function<void(const MediaSourc
}
void *listener_tag = this;
// 若干秒后执行等待媒体注册超时回调
// 若干秒后执行等待媒体注册超时回调 [AUTO-TRANSLATED:71010a04]
// Execute the media registration timeout callback after a few seconds
auto onRegistTimeout = _poller->doDelayTask(maxWaitMS, [cb, listener_tag]() {
// 取消监听该事件
// 取消监听该事件 [AUTO-TRANSLATED:31297323]
// Cancel listening to this event
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
cb(nullptr);
return 0;
@ -174,7 +188,8 @@ void FFmpegSource::findAsync(int maxWaitMS, const function<void(const MediaSourc
auto onRegist = [listener_tag, weakSelf, cb, onRegistTimeout](BroadcastMediaChangedArgs) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
// 本身已经销毁,取消延时任务
// 本身已经销毁,取消延时任务 [AUTO-TRANSLATED:cc2e420f]
// Self has been destroyed, cancel the delayed task
onRegistTimeout->cancel();
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
return;
@ -182,29 +197,38 @@ void FFmpegSource::findAsync(int maxWaitMS, const function<void(const MediaSourc
if (!bRegist || sender.getSchema() != strongSelf->_media_info.schema ||
!equalMediaTuple(sender.getMediaTuple(), strongSelf->_media_info)) {
// 不是自己感兴趣的事件,忽略之
// 不是自己感兴趣的事件,忽略之 [AUTO-TRANSLATED:f61f5668]
// Not an event of interest, ignore it
return;
}
// 查找的流终于注册上了;取消延时任务,防止多次回调
// 查找的流终于注册上了;取消延时任务,防止多次回调 [AUTO-TRANSLATED:66fc5abf]
// The stream you are looking for is finally registered; cancel the delayed task to prevent multiple callbacks
onRegistTimeout->cancel();
// 取消事件监听
// 取消事件监听 [AUTO-TRANSLATED:c722acb6]
// Cancel event listening
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
// 切换到自己的线程再回复
// 切换到自己的线程再回复 [AUTO-TRANSLATED:3b630c64]
// Switch to your own thread and then reply
strongSelf->_poller->async([weakSelf, cb]() {
if (auto strongSelf = weakSelf.lock()) {
// 再找一遍媒体源,一般能找到
// 再找一遍媒体源,一般能找到 [AUTO-TRANSLATED:f0b81977]
// Find the media source again, usually you can find it
strongSelf->findAsync(0, cb);
}
}, false);
};
// 监听媒体注册事件
// 监听媒体注册事件 [AUTO-TRANSLATED:ea3e763b]
// Listen to media registration events
NoticeCenter::Instance().addListener(listener_tag, Broadcast::kBroadcastMediaChanged, onRegist);
}
/**
* 线
* Check if the media is online regularly
* [AUTO-TRANSLATED:11bae8ab]
*/
void FFmpegSource::startTimer(int timeout_ms) {
weak_ptr<FFmpegSource> weakSelf = shared_from_this();
@ -212,54 +236,66 @@ void FFmpegSource::startTimer(int timeout_ms) {
_timer = std::make_shared<Timer>(1.0f, [weakSelf, timeout_ms]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
//自身已经销毁
// 自身已经销毁 [AUTO-TRANSLATED:5a02ef8b]
// Self has been destroyed
return false;
}
bool needRestart = ffmpeg_restart_sec > 0 && strongSelf->_replay_ticker.elapsedTime() > ffmpeg_restart_sec * 1000;
if (is_local_ip(strongSelf->_media_info.host)) {
// 推流给自己的我们通过检查是否已经注册来判断FFmpeg是否工作正常
// 推流给自己的我们通过检查是否已经注册来判断FFmpeg是否工作正常 [AUTO-TRANSLATED:9a441d38]
// Push stream to yourself, we judge whether FFmpeg is working properly by checking whether it has been registered
strongSelf->findAsync(0, [&](const MediaSource::Ptr &src) {
// 同步查找流
// 同步查找流 [AUTO-TRANSLATED:97048f1e]
// Synchronously find the stream
if (!src || needRestart) {
if (needRestart) {
strongSelf->_replay_ticker.resetTime();
if (strongSelf->_process.wait(false)) {
// FFmpeg进程还在运行超时就关闭它
// FFmpeg进程还在运行超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, timeout and close it
strongSelf->_process.kill(2000);
}
InfoL << "FFmpeg即将重启, 将会继续拉流 " << strongSelf->_src_url;
}
// 流不在线,重新拉流, 这里原先是10秒超时实际发现10秒不够改成20秒了
// 流不在线,重新拉流, 这里原先是10秒超时实际发现10秒不够改成20秒了 [AUTO-TRANSLATED:10e8c704]
// The stream is not online, re-pull the stream, here the original timeout was 10 seconds, but it was found that 10 seconds was not enough, so it was changed to 20 seconds
if (strongSelf->_replay_ticker.elapsedTime() > 20 * 1000) {
// 上次重试时间超过10秒那么再重试FFmpeg拉流
// 上次重试时间超过10秒那么再重试FFmpeg拉流 [AUTO-TRANSLATED:b308095a]
// The last retry time exceeds 10 seconds, then retry FFmpeg to pull the stream
strongSelf->_replay_ticker.resetTime();
strongSelf->play(strongSelf->_ffmpeg_cmd_key, strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [](const SockException &) {});
}
}
});
} else {
// 推流给其他服务器的我们通过判断FFmpeg进程是否在线如果FFmpeg推流中断那么它应该会自动退出
// 推流给其他服务器的我们通过判断FFmpeg进程是否在线如果FFmpeg推流中断那么它应该会自动退出 [AUTO-TRANSLATED:82da3ea5]
// Push stream to other servers, we judge whether the FFmpeg process is online, if FFmpeg push stream is interrupted, then it should exit automatically
if (!strongSelf->_process.wait(false) || needRestart) {
if (needRestart) {
strongSelf->_replay_ticker.resetTime();
if (strongSelf->_process.wait(false)) {
// FFmpeg进程还在运行超时就关闭它
// FFmpeg进程还在运行超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, timeout and close it
strongSelf->_process.kill(2000);
}
InfoL << "FFmpeg即将重启, 将会继续拉流 " << strongSelf->_src_url;
}
// ffmpeg不在线重新拉流
// ffmpeg不在线重新拉流 [AUTO-TRANSLATED:aa958c43]
// ffmpeg is not online, re-pull the stream
strongSelf->play(strongSelf->_ffmpeg_cmd_key, strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [weakSelf](const SockException &ex) {
if (!ex) {
// 没有错误
// 没有错误 [AUTO-TRANSLATED:037ae0ca]
// No error
return;
}
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
// 自身已经销毁
// 自身已经销毁 [AUTO-TRANSLATED:5f954f8a]
// Self has been destroyed
return;
}
// 上次重试时间超过10秒那么再重试FFmpeg拉流
// 上次重试时间超过10秒那么再重试FFmpeg拉流 [AUTO-TRANSLATED:b308095a]
// Retry FFmpeg stream pulling if the last retry time is over 10 seconds
strongSelf->startTimer(10 * 1000);
});
}
@ -275,10 +311,12 @@ void FFmpegSource::setOnClose(const function<void()> &cb){
bool FFmpegSource::close(MediaSource &sender) {
auto listener = getDelegate();
if (listener && !listener->close(sender)) {
//关闭失败
// 关闭失败 [AUTO-TRANSLATED:83f07dba]
// Close failed
return false;
}
//该流无人观看,我们停止吧
// 该流无人观看,我们停止吧 [AUTO-TRANSLATED:43999b39]
// No one is watching this stream, let's stop it
if (_onClose) {
_onClose();
}
@ -297,7 +335,8 @@ void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) {
auto muxer = src->getMuxer();
auto listener = muxer ? muxer->getDelegate() : nullptr;
if (listener && listener.get() != this) {
//防止多次进入onGetMediaSource函数导致无限递归调用的bug
// 防止多次进入onGetMediaSource函数导致无限递归调用的bug [AUTO-TRANSLATED:ceadb9c7]
// Prevent the bug of infinite recursive calls caused by entering the onGetMediaSource function multiple times
setDelegate(listener);
muxer->setDelegate(shared_from_this());
if (_enable_hls) {
@ -317,7 +356,8 @@ void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float
WorkThreadPool::Instance().getPoller()->async([timeout_sec, play_url, save_path, cb, ticker]() {
auto elapsed_ms = ticker.elapsedTime();
if (elapsed_ms > timeout_sec * 1000) {
// 超时,后台线程负载太高,当代太久才启动该任务
// 超时,后台线程负载太高,当代太久才启动该任务 [AUTO-TRANSLATED:815606d6]
// Timeout, the background thread load is too high, it takes too long to start this task
cb(false, "wait work poller schedule snap task timeout");
return;
}
@ -328,21 +368,26 @@ void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float
auto log_file = ffmpeg_log.empty() ? ffmpeg_log : File::absolutePath("", ffmpeg_log);
process->run(cmd, log_file);
//定时器延时应该减去后台任务启动的延时
// 定时器延时应该减去后台任务启动的延时 [AUTO-TRANSLATED:7d224687]
// The timer delay should be reduced by the delay of the background task startup
auto delayTask = EventPollerPool::Instance().getPoller()->doDelayTask(
(uint64_t)(timeout_sec * 1000 - elapsed_ms), [process, cb, log_file, save_path]() {
if (process->wait(false)) {
// FFmpeg进程还在运行超时就关闭它
// FFmpeg进程还在运行超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, close it if it times out
process->kill(2000);
}
return 0;
});
// 等待FFmpeg进程退出
// 等待FFmpeg进程退出 [AUTO-TRANSLATED:0a179187]
// Wait for the FFmpeg process to exit
process->wait(true);
// FFmpeg进程退出了可以取消定时器了
// FFmpeg进程退出了可以取消定时器了 [AUTO-TRANSLATED:c8a4b513]
// The FFmpeg process has exited, the timer can be canceled
delayTask->cancel();
// 执行回调函数
// 执行回调函数 [AUTO-TRANSLATED:7309a900]
// Execute the callback function
bool success = process->exit_code() == 0 && File::fileSize(save_path);
cb(success, (!success && !log_file.empty()) ? File::loadFile(log_file) : "");
});

View File

@ -26,11 +26,16 @@ namespace FFmpeg {
class FFmpegSnap {
public:
using onSnap = std::function<void(bool success, const std::string &err_msg)>;
/// 创建截图
/// \param play_url 播放url地址只要FFmpeg支持即可
/// \param save_path 截图jpeg文件保存路径
/// \param timeout_sec 生成截图超时时间(防止阻塞太久)
/// \param cb 生成截图成功与否回调
// / 创建截图 [AUTO-TRANSLATED:6d334c49]
// / Create a screenshot
// / \param play_url 播放url地址只要FFmpeg支持即可 [AUTO-TRANSLATED:609d4de4]
// / \param play_url The playback URL address, as long as FFmpeg supports it
// / \param save_path 截图jpeg文件保存路径 [AUTO-TRANSLATED:0fc0ac0d]
// / \param save_path The path to save the screenshot JPEG file
// / \param timeout_sec 生成截图超时时间(防止阻塞太久) [AUTO-TRANSLATED:0dcc0095]
// / \param timeout_sec Timeout for generating the screenshot (to prevent blocking for too long)
// / \param cb 生成截图成功与否回调 [AUTO-TRANSLATED:5b4b93c9]
// / \param cb Callback for whether the screenshot was generated successfully
static void makeSnap(const std::string &play_url, const std::string &save_path, float timeout_sec, const onSnap &cb);
private:
@ -48,6 +53,9 @@ public:
/**
*
* Set the active close callback
* [AUTO-TRANSLATED:2134a5b3]
*/
void setOnClose(const std::function<void()> &cb);
@ -58,6 +66,14 @@ public:
* @param dst_url FFmpeg推流地址
* @param timeout_ms
* @param cb
* Start playing the URL
* @param ffmpeg_cmd_key FFmpeg stream command configuration item key, users can set multiple command parameter templates in the configuration file at the same time
* @param src_url FFmpeg stream address
* @param dst_url FFmpeg push stream address
* @param timeout_ms Timeout for waiting for the result, in milliseconds
* @param cb Success or failure callback
* [AUTO-TRANSLATED:2c35789e]
*/
void play(const std::string &ffmpeg_cmd_key, const std::string &src_url, const std::string &dst_url, int timeout_ms, const onPlay &cb);
@ -65,6 +81,11 @@ public:
*
* @param enable_hls hls直播或录制
* @param enable_mp4 mp4
* Set recording
* @param enable_hls Whether to enable HLS live streaming or recording
* @param enable_mp4 Whether to record MP4
* [AUTO-TRANSLATED:9f28d5c2]
*/
void setupRecordFlag(bool enable_hls, bool enable_mp4);
@ -74,11 +95,14 @@ private:
void onGetMediaSource(const mediakit::MediaSource::Ptr &src);
///////MediaSourceEvent override///////
// 关闭
// 关闭 [AUTO-TRANSLATED:92392f02]
// Close
bool close(mediakit::MediaSource &sender) override;
// 获取媒体源类型
// 获取媒体源类型 [AUTO-TRANSLATED:34290a69]
// Get the media source type
mediakit::MediaOriginType getOriginType(mediakit::MediaSource &sender) const override;
//获取媒体源url或者文件路径
// 获取媒体源url或者文件路径 [AUTO-TRANSLATED:d6d885b8]
// Get the media source URL or file path
std::string getOriginUrl(mediakit::MediaSource &sender) const override;
private:

View File

@ -35,12 +35,15 @@ using namespace toolkit;
#ifndef _WIN32
static void setupChildProcess() {
//取消cpu亲和性设置防止FFmpeg进程cpu占用率不能超过100%的问题
// 取消cpu亲和性设置防止FFmpeg进程cpu占用率不能超过100%的问题 [AUTO-TRANSLATED:d168129d]
// Cancel CPU affinity settings to prevent FFmpeg process from exceeding 100% CPU usage.
setThreadAffinity(-1);
//子进程关闭core文件生成
// 子进程关闭core文件生成 [AUTO-TRANSLATED:721d2925]
// Disable core file generation for child processes.
struct rlimit rlim = { 0, 0 };
setrlimit(RLIMIT_CORE, &rlim);
//子进程恢复默认信号处理
// 子进程恢复默认信号处理 [AUTO-TRANSLATED:1bc7387b]
// Restore default signal handling for child processes.
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
@ -52,7 +55,8 @@ static int runChildProcess(string cmd, string log_file) {
setupChildProcess();
if (log_file.empty()) {
//未指定子进程日志文件时,重定向至/dev/null
// 未指定子进程日志文件时,重定向至/dev/null [AUTO-TRANSLATED:dd0c9853]
// Redirect child process logs to /dev/null if no log file is specified.
log_file = "/dev/null";
} else {
log_file = StrPrinter << log_file << "." << getpid();
@ -64,7 +68,8 @@ static int runChildProcess(string cmd, string log_file) {
open("/dev/null", O_RDONLY, 0666); /* will be fd 0 (STDIN_FILENO) */
}
//重定向shell日志至文件
// 重定向shell日志至文件 [AUTO-TRANSLATED:3480502b]
// Redirect shell logs to file.
auto fp = File::create_file(log_file, "ab");
if (!fp) {
fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
@ -77,7 +82,8 @@ static int runChildProcess(string cmd, string log_file) {
if (dup2(log_fd, STDERR_FILENO) < 0) {
fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
}
// 关闭日志文件
// 关闭日志文件 [AUTO-TRANSLATED:9fb6e256]
// Close log file.
::fclose(fp);
}
fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data());
@ -114,13 +120,15 @@ void Process::run(const string &cmd, string log_file) {
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
if (log_file.empty()) {
//未指定子进程日志文件时,重定向至/dev/null
// 未指定子进程日志文件时,重定向至/dev/null [AUTO-TRANSLATED:dd0c9853]
// Redirect child process logs to /dev/null if no log file is specified.
log_file = "NUL";
} else {
log_file = StrPrinter << log_file << "." << getCurrentMillisecond();
}
//重定向shell日志至文件
// 重定向shell日志至文件 [AUTO-TRANSLATED:3480502b]
// Redirect shell logs to file.
auto fp = File::create_file(log_file, "ab");
if (!fp) {
fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
@ -137,7 +145,8 @@ void Process::run(const string &cmd, string log_file) {
LPTSTR lpDir = const_cast<char *>(cmd.data());
if (CreateProcess(NULL, lpDir, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
//下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程
// 下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程 [AUTO-TRANSLATED:d50fada2]
// The following two lines close the handle and break the relationship between the current process and the new process. Otherwise, the TerminateProcess function may accidentally close the child process.
CloseHandle(pi.hThread);
_pid = pi.dwProcessId;
_handle = pi.hProcess;
@ -165,12 +174,14 @@ void Process::run(const string &cmd, string log_file) {
throw std::runtime_error(StrPrinter << "fork child process failed, cmd: " << cmd << ",err:" << get_uv_errmsg());
}
if (_pid == 0) {
//子进程
// 子进程 [AUTO-TRANSLATED:3f793797]
// Child process.
exit(runChildProcess(cmd, log_file));
}
#endif
if (log_file.empty()) {
//未指定子进程日志文件时,重定向至/dev/null
// 未指定子进程日志文件时,重定向至/dev/null [AUTO-TRANSLATED:dd0c9853]
// Redirect child process logs to /dev/null if no log file is specified.
log_file = "/dev/null";
} else {
log_file = StrPrinter << log_file << "." << _pid;
@ -185,6 +196,13 @@ void Process::run(const string &cmd, string log_file) {
* @param exit_code_ptr
* @param block
* @return
* Get the process's alive status.
* @param pid Process ID
* @param exit_code_ptr Process return code
* @param block Whether to block and wait
* @return Whether the process is still running
* [AUTO-TRANSLATED:ef80ff17]
*/
static bool s_wait(pid_t pid, void *handle, int *exit_code_ptr, bool block) {
if (pid <= 0) {
@ -193,14 +211,16 @@ static bool s_wait(pid_t pid, void *handle, int *exit_code_ptr, bool block) {
#ifdef _WIN32
DWORD code = 0;
if (block) {
//一直等待
// 一直等待 [AUTO-TRANSLATED:ca8a5e14]
// Wait indefinitely.
code = WaitForSingleObject(handle, INFINITE);
} else {
code = WaitForSingleObject(handle, 0);
}
if (code == WAIT_FAILED || code == WAIT_OBJECT_0) {
//子进程已经退出了,获取子进程退出代码
// 子进程已经退出了,获取子进程退出代码 [AUTO-TRANSLATED:c39663b2]
// The child process has exited, get the child process exit code.
DWORD exitCode = 0;
if (exit_code_ptr && GetExitCodeProcess(handle, &exitCode)) {
*exit_code_ptr = exitCode;
@ -209,10 +229,12 @@ static bool s_wait(pid_t pid, void *handle, int *exit_code_ptr, bool block) {
}
if (code == WAIT_TIMEOUT) {
//子进程还在线
// 子进程还在线 [AUTO-TRANSLATED:3d32ef56]
// The child process is still online.
return true;
}
//不太可能运行到此处
// 不太可能运行到此处 [AUTO-TRANSLATED:b1fde65d]
// It is unlikely to run to this point.
WarnL << "WaitForSingleObject ret:" << code;
return false;
#else
@ -268,42 +290,52 @@ bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent) {
static void s_kill(pid_t pid, void *handle, int max_delay, bool force) {
if (pid <= 0) {
// pid无效
// pid无效 [AUTO-TRANSLATED:6665d7a0]
// Invalid pid.
return;
}
#ifdef _WIN32
// windows下目前没有比较好的手段往子进程发送SIGTERM或信号
//所以杀死子进程的方式全部强制为立即关闭
// windows下目前没有比较好的手段往子进程发送SIGTERM或信号 [AUTO-TRANSLATED:cd32ad25]
// Currently, there is no good way to send SIGTERM or signals to child processes under Windows.
// 所以杀死子进程的方式全部强制为立即关闭 [AUTO-TRANSLATED:2fc31f2b]
// Therefore, all methods of killing child processes are forced to be immediate closure.
force = true;
if (force) {
//强制关闭子进程
// 强制关闭子进程 [AUTO-TRANSLATED:f3c712f6]
// Force close the child process.
TerminateProcess(handle, 0);
} else {
//非强制关闭发送Ctr+C信号
// 非强制关闭发送Ctr+C信号 [AUTO-TRANSLATED:fe9bf53f]
// Non-forced closure, send Ctrl+C signal.
signalCtrl(pid, CTRL_C_EVENT);
}
#else
if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) {
//进程可能已经退出了
// 进程可能已经退出了 [AUTO-TRANSLATED:682a8b61]
// The process may have already exited.
WarnL << "kill process " << pid << " failed:" << get_uv_errmsg();
return;
}
#endif // _WIN32
if (force) {
//发送SIGKILL信号后阻塞等待退出
// 发送SIGKILL信号后阻塞等待退出 [AUTO-TRANSLATED:4fc03dae]
// After sending the SIGKILL signal, block and wait for exit.
s_wait(pid, handle, nullptr, true);
DebugL << "force kill " << pid << " success!";
return;
}
//发送SIGTERM信号后2秒后检查子进程是否已经退出
// 发送SIGTERM信号后2秒后检查子进程是否已经退出 [AUTO-TRANSLATED:b9878b28]
// After sending the SIGTERM signal, check if the child process has exited after 2 seconds.
EventPollerPool::Instance().getPoller()->doDelayTask(max_delay, [pid, handle]() {
if (!s_wait(pid, handle, nullptr, false)) {
//进程已经退出了
// 进程已经退出了 [AUTO-TRANSLATED:ad02bb63]
// The process has exited.
return 0;
}
//进程还在运行
// 进程还在运行 [AUTO-TRANSLATED:b1aa9ba4]
// The process is still running.
WarnL << "process still working,force kill it:" << pid;
s_kill(pid, handle, 0, true);
return 0;

View File

@ -134,17 +134,20 @@ void System::startDaemon(bool &kill_parent_if_failed) {
pid = fork();
if (pid == -1) {
WarnL << "fork失败:" << get_uv_errmsg();
//休眠1秒再试
// 休眠1秒再试 [AUTO-TRANSLATED:00e5d7bf]
// Sleep for 1 second and try again
sleep(1);
continue;
}
if (pid == 0) {
//子进程
// 子进程 [AUTO-TRANSLATED:3f793797]
// Child process
return;
}
//父进程,监视子进程是否退出
// 父进程,监视子进程是否退出 [AUTO-TRANSLATED:0e13a34d]
// Parent process, monitor whether the child process exits
DebugL << "启动子进程:" << pid;
signal(SIGINT, [](int) {
WarnL << "收到主动退出信号,关闭父进程与子进程";
@ -162,9 +165,11 @@ void System::startDaemon(bool &kill_parent_if_failed) {
int status = 0;
if (waitpid(pid, &status, 0) >= 0) {
WarnL << "子进程退出";
//休眠3秒再启动子进程
// 休眠3秒再启动子进程 [AUTO-TRANSLATED:608448bd]
// Sleep for 3 seconds and then start the child process
sleep(3);
//重启子进程,如果子进程重启失败,那么不应该杀掉守护进程,这样守护进程可以一直尝试重启子进程
// 重启子进程,如果子进程重启失败,那么不应该杀掉守护进程,这样守护进程可以一直尝试重启子进程 [AUTO-TRANSLATED:0a336b0a]
// Restart the child process. If the child process fails to restart, the daemon process should not be killed. This allows the daemon process to continuously attempt to restart the child process.
kill_parent_if_failed = false;
break;
}
@ -204,7 +209,8 @@ void System::systemSetup(){
#ifndef ANDROID
signal(SIGSEGV, sig_crash);
signal(SIGABRT, sig_crash);
//忽略挂起信号
// 忽略挂起信号 [AUTO-TRANSLATED:73e71e54]
// Ignore the hang up signal
signal(SIGHUP, SIG_IGN);
#endif// ANDROID
#endif//!defined(_WIN32)

View File

@ -80,14 +80,17 @@ void Channel::copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr&
memcpy(buf->get()->data[0] + buf->get()->linesize[0] * (i + p->posY) + p->posX,
_tmp->get()->data[0] + _tmp->get()->linesize[0] * i, _tmp->get()->width);
}
// 确保height为奇数时也能正确的复制到最后一行uv数据
// 确保height为奇数时也能正确的复制到最后一行uv数据 [AUTO-TRANSLATED:69895ea5]
// Ensure that the uv data can be copied to the last line correctly when height is odd
for (int i = 0; i < (p->height + 1) / 2; i++) {
// U平面
// U平面 [AUTO-TRANSLATED:8b73dc2d]
// U plane
memcpy(buf->get()->data[1] + buf->get()->linesize[1] * (i + p->posY / 2) +
p->posX / 2,
_tmp->get()->data[1] + _tmp->get()->linesize[1] * i, _tmp->get()->width / 2);
// V平面
// V平面 [AUTO-TRANSLATED:8fa72cc7]
// V plane
memcpy(buf->get()->data[2] + buf->get()->linesize[2] * (i + p->posY / 2) +
p->posX / 2,
_tmp->get()->data[2] + _tmp->get()->linesize[2] * i, _tmp->get()->width / 2);
@ -95,7 +98,8 @@ void Channel::copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr&
break;
}
case AV_PIX_FMT_NV12: {
// TODO: 待实现
// TODO: 待实现 [AUTO-TRANSLATED:247ec1df]
// TODO: To be implemented
break;
}
@ -110,7 +114,8 @@ void StackPlayer::addChannel(const std::weak_ptr<Channel>& chn) {
void StackPlayer::play() {
auto url = _url;
// 创建拉流 解码对象
// 创建拉流 解码对象 [AUTO-TRANSLATED:9267c5dc]
// Create a pull stream decoding object
_player = std::make_shared<mediakit::MediaPlayer>();
std::weak_ptr<mediakit::MediaPlayer> weakPlayer = _player;
@ -127,7 +132,8 @@ void StackPlayer::play() {
if (!self) { return; }
if (!ex) {
// 取消定时器
// 取消定时器 [AUTO-TRANSLATED:41ff7c9a]
// Cancel the timer
self->_timer.reset();
self->_failedCount = 0;
@ -141,7 +147,8 @@ void StackPlayer::play() {
// auto audioTrack = std::dynamic_pointer_cast<mediakit::AudioTrack>(strongPlayer->getTrack(mediakit::TrackAudio, false));
if (videoTrack) {
// TODO:添加使用显卡还是cpu解码的判断逻辑
// TODO:添加使用显卡还是cpu解码的判断逻辑 [AUTO-TRANSLATED:44bef37a]
// TODO: Add logic to determine whether to use GPU or CPU decoding
auto decoder = std::make_shared<mediakit::FFmpegDecoder>(
videoTrack, 0, std::vector<std::string>{"h264", "hevc"});
@ -227,7 +234,8 @@ VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelForm
info.iBitRate = _bitRate;
_dev->initVideo(info);
// dev->initAudio(); //TODO:音频
// dev->initAudio(); //TODO:音频 [AUTO-TRANSLATED:adc5658b]
// dev->initAudio(); //TODO: Audio
_dev->addTrackCompleted();
_isExit = false;
@ -276,7 +284,8 @@ void VideoStack::start() {
}
void VideoStack::initBgColor() {
// 填充底色
// 填充底色 [AUTO-TRANSLATED:ee9bbd46]
// Fill the background color
auto R = 20;
auto G = 20;
auto B = 20;
@ -402,11 +411,13 @@ Params VideoStackManager::parseParams(const Json::Value& json, std::string& id,
float gapv = json["gapv"].asFloat();// 垂直间距
float gaph = json["gaph"].asFloat();// 水平间距
// 单个间距
// 单个间距 [AUTO-TRANSLATED:e1b9b5b6]
// Single spacing
int gaphPix = static_cast<int>(round(width * gaph));
int gapvPix = static_cast<int>(round(height * gapv));
// 根据间距计算格子宽高
// 根据间距计算格子宽高 [AUTO-TRANSLATED:b9972498]
// Calculate the width and height of the grid according to the spacing
int gridWidth = cols > 1 ? (width - gaphPix * (cols - 1)) / cols : width;
int gridHeight = rows > 1 ? (height - gapvPix * (rows - 1)) / rows : height;
@ -427,7 +438,8 @@ Params VideoStackManager::parseParams(const Json::Value& json, std::string& id,
}
}
// 判断是否需要合并格子 (焦点屏)
// 判断是否需要合并格子 (焦点屏) [AUTO-TRANSLATED:bfa14430]
// Determine whether to merge grids (focus screen)
if (json.isMember("span") && json["span"].isArray() && json["span"].size() > 0) {
for (const auto& subArray : json["span"]) {
if (!subArray.isArray() || subArray.size() != 2) {

View File

@ -98,7 +98,8 @@ private:
std::string _url;
mediakit::MediaPlayer::Ptr _player;
// 用于断线重连
// 用于断线重连 [AUTO-TRANSLATED:18fd242a]
// Used for disconnection and reconnection
toolkit::Timer::Ptr _timer;
int _failedCount = 0;
@ -145,13 +146,16 @@ private:
class VideoStackManager {
public:
// 创建拼接流
// 创建拼接流 [AUTO-TRANSLATED:ebb3a8ec]
// Create a concatenated stream
int startVideoStack(const Json::Value& json);
// 停止拼接流
// 停止拼接流 [AUTO-TRANSLATED:a46f341f]
// Stop the concatenated stream
int stopVideoStack(const std::string& id);
// 可以在不断流的情况下,修改拼接流的配置(实现切换拼接屏内容)
// 可以在不断流的情况下,修改拼接流的配置(实现切换拼接屏内容) [AUTO-TRANSLATED:f9b59b6b]
// You can modify the configuration of the concatenated stream (to switch the content of the concatenated screen) without stopping the stream
int resetVideoStack(const Json::Value& json);
public:

View File

@ -90,7 +90,8 @@ static onceToken token([]() {
}//namespace API
using HttpApi = function<void(const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender)>;
//http api列表
// http api列表 [AUTO-TRANSLATED:a05e9d9d]
// http api list
static map<string, HttpApi, StrCaseCompare> s_map_api;
static void responseApi(const Json::Value &res, const HttpSession::HttpResponseInvoker &invoker){
@ -118,7 +119,8 @@ static HttpApi toApi(const function<void(API_ARGS_MAP_ASYNC)> &cb) {
Json::Value val;
val["code"] = API::Success;
//参数解析成map
// 参数解析成map [AUTO-TRANSLATED:20e11ff3]
// Parse parameters into a map
auto args = getAllArgs(parser);
cb(sender, headerOut, ArgsMap(parser, args), val, invoker);
};
@ -143,7 +145,8 @@ static HttpApi toApi(const function<void(API_ARGS_JSON_ASYNC)> &cb) {
if (parser["Content-Type"].find("application/json") == string::npos) {
throw InvalidArgsException("该接口只支持json格式的请求");
}
//参数解析成json对象然后处理
// 参数解析成json对象然后处理 [AUTO-TRANSLATED:6f23397b]
// Parse parameters into a JSON object and then process
Json::Value args;
Json::Reader reader;
reader.parse(parser.content(), args);
@ -203,7 +206,8 @@ void api_regist(const string &api_path, const function<void(API_ARGS_STRING_ASYN
s_map_api.emplace(api_path, toApi(func));
}
//获取HTTP请求中url参数、content参数
// 获取HTTP请求中url参数、content参数 [AUTO-TRANSLATED:d161a1e1]
// Get URL parameters and content parameters from the HTTP request
static ApiArgsType getAllArgs(const Parser &parser) {
ApiArgsType allArgs;
if (parser["Content-Type"].find("application/x-www-form-urlencoded") == 0) {
@ -245,21 +249,25 @@ static void *web_api_tag = nullptr;
static inline void addHttpListener(){
GET_CONFIG(bool, api_debug, API::kApiDebug);
//注册监听kBroadcastHttpRequest事件
// 注册监听kBroadcastHttpRequest事件 [AUTO-TRANSLATED:4af22c90]
// Register to listen for the kBroadcastHttpRequest event
NoticeCenter::Instance().addListener(&web_api_tag, Broadcast::kBroadcastHttpRequest, [](BroadcastHttpRequestArgs) {
auto it = s_map_api.find(parser.url());
if (it == s_map_api.end()) {
return;
}
//该api已被消费
// 该api已被消费 [AUTO-TRANSLATED:db0872fc]
// This API has been consumed
consumed = true;
if(api_debug){
auto newInvoker = [invoker, parser](int code, const HttpSession::KeyValue &headerOut, const HttpBody::Ptr &body) {
//body默认为空
// body默认为空 [AUTO-TRANSLATED:4fd4ecc8]
// The body is empty by default
ssize_t size = 0;
if (body && body->remainSize()) {
//有body获取body大小
// 有body获取body大小 [AUTO-TRANSLATED:ab1c417d]
// If there is a body, get the body size
size = body->remainSize();
}
@ -362,17 +370,21 @@ public:
}
};
//拉流代理器列表
// 拉流代理器列表 [AUTO-TRANSLATED:6dcfb11f]
// Pull stream proxy list
static ServiceController<PlayerProxy> s_player_proxy;
//推流代理器列表
// 推流代理器列表 [AUTO-TRANSLATED:539a1bcf]
// Push stream proxy list
static ServiceController<PusherProxy> s_pusher_proxy;
//FFmpeg拉流代理器列表
// FFmpeg拉流代理器列表 [AUTO-TRANSLATED:4bdedf10]
// FFmpeg pull stream proxy list
static ServiceController<FFmpegSource> s_ffmpeg_src;
#if defined(ENABLE_RTPPROXY)
//rtp服务器列表
// rtp服务器列表 [AUTO-TRANSLATED:2e362a8c]
// RTP server list
static ServiceController<RtpServer> s_rtp_server;
#endif
@ -418,7 +430,8 @@ Value makeMediaSourceJson(MediaSource &media){
item["originSock"] = Json::nullValue;
}
//getLossRate有线程安全问题使用getMediaInfo接口才能获取丢包率getMediaList接口将忽略丢包率
// 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
auto current_thread = false;
try { current_thread = media.getOwnerPoller()->isCurrentThread();} catch (...) {}
float last_loss = -1;
@ -430,7 +443,8 @@ Value makeMediaSourceJson(MediaSource &media){
obj["ready"] = track->ready();
obj["codec_type"] = codec_type;
if (current_thread) {
//rtp推流只有一个统计器但是可能有多个track如果短时间多次获取间隔丢包率第二次会获取为-1
// 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
auto loss = media.getLossRate(codec_type);
if (loss == -1) {
loss = last_loss;
@ -477,7 +491,8 @@ Value makeMediaSourceJson(MediaSource &media){
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)) {
//为了防止RtpProcess所有权限混乱的问题不允许重复添加相同的key
// 为了防止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
return 0;
}
@ -485,11 +500,13 @@ uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, i
server->start(local_port, local_ip.c_str(), tuple, (RtpServer::TcpMode)tcp_mode, re_use_port, ssrc, only_track, multiplex);
});
server->setOnDetach([key](const SockException &ex) {
//设置rtp超时移除事件
// 设置rtp超时移除事件 [AUTO-TRANSLATED:98d42cf3]
// Set RTP timeout removal event
s_rtp_server.erase(key);
});
//回复json
// 回复json [AUTO-TRANSLATED:0c443c6a]
// Reply JSON
return server->getPort();
}
@ -543,7 +560,8 @@ void getStatisticJson(const function<void(Value &val)> &cb) {
for (auto &val : *thread_mem_info) {
(*obj)["threadMem"].append(val);
}
//触发回调
// 触发回调 [AUTO-TRANSLATED:08ea452d]
// Trigger callback
cb(*obj);
});
@ -584,27 +602,33 @@ void addStreamProxy(const MediaTuple &tuple, const string &url, int retry_count,
const function<void(const SockException &ex, const string &key)> &cb) {
auto key = tuple.shortUrl();
if (s_player_proxy.find(key)) {
//已经在拉流了
// 已经在拉流了 [AUTO-TRANSLATED:e06c57d7]
// Already pulling stream
cb(SockException(Err_other, "This stream already exists"), key);
return;
}
//添加拉流代理
// 添加拉流代理 [AUTO-TRANSLATED:aa516f44]
// Add pull stream proxy
auto player = s_player_proxy.make(key, tuple, option, retry_count);
// 先透传拷贝参数
// 先透传拷贝参数 [AUTO-TRANSLATED:22b5605e]
// First pass-through copy parameters
for (auto &pr : args) {
(*player)[pr.first] = pr.second;
}
//指定RTP over TCP(播放rtsp时有效)
// 指定RTP over TCP(播放rtsp时有效) [AUTO-TRANSLATED:1a062656]
// Specify RTP over TCP (effective when playing RTSP)
(*player)[Client::kRtpType] = rtp_type;
if (timeout_sec > 0.1f) {
//播放握手超时时间
// 播放握手超时时间 [AUTO-TRANSLATED:5a29ae1f]
// Play handshake timeout
(*player)[Client::kTimeoutMS] = timeout_sec * 1000;
}
//开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试
// 开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试 [AUTO-TRANSLATED:ac8499e5]
// Start playing. If playback fails or is stopped, it will automatically retry several times, by default it will retry indefinitely
player->setPlayCallbackOnce([cb, key](const SockException &ex) {
if (ex) {
s_player_proxy.erase(key);
@ -612,7 +636,8 @@ void addStreamProxy(const MediaTuple &tuple, const string &url, int retry_count,
cb(ex, key);
});
//被主动关闭拉流
// 被主动关闭拉流 [AUTO-TRANSLATED:41a19476]
// The pull stream was actively closed
player->setOnClose([key](const SockException &ex) {
s_player_proxy.erase(key);
});
@ -636,23 +661,28 @@ void addStreamPusherProxy(const string &schema,
return;
}
if (s_pusher_proxy.find(key)) {
//已经在推流了
// 已经在推流了 [AUTO-TRANSLATED:81fcd202]
// Already pushing stream
cb(SockException(Err_success), key);
return;
}
//添加推流代理
// 添加推流代理 [AUTO-TRANSLATED:f9dbc76d]
// Add push stream proxy
auto pusher = s_pusher_proxy.make(key, src, retry_count);
//指定RTP over TCP(播放rtsp时有效)
// 指定RTP over TCP(播放rtsp时有效) [AUTO-TRANSLATED:1a062656]
// Specify RTP over TCP (effective when playing RTSP)
pusher->emplace(Client::kRtpType, rtp_type);
if (timeout_sec > 0.1f) {
//推流握手超时时间
// 推流握手超时时间 [AUTO-TRANSLATED:00762fc1]
// Push stream handshake timeout
pusher->emplace(Client::kTimeoutMS, timeout_sec * 1000);
}
//开始推流,如果推流失败或者推流中止,将会自动重试若干次,默认一直重试
// 开始推流,如果推流失败或者推流中止,将会自动重试若干次,默认一直重试 [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
pusher->setPushCallbackOnce([cb, key, url](const SockException &ex) {
if (ex) {
WarnL << "Push " << url << " failed, key: " << key << ", err: " << ex;
@ -661,7 +691,8 @@ void addStreamPusherProxy(const string &schema,
cb(ex, key);
});
//被主动关闭推流
// 被主动关闭推流 [AUTO-TRANSLATED:bf216f82]
// Stream closed actively
pusher->setOnClose([key, url](const SockException &ex) {
WarnL << "Push " << url << " failed, key: " << key << ", err: " << ex;
s_pusher_proxy.erase(key);
@ -674,13 +705,20 @@ void addStreamPusherProxy(const string &schema,
* api接口
* api都支持GET和POST两种方式
* POST方式参数支持application/json和application/x-www-form-urlencoded方式
* 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:62e68c43]
*/
void installWebApi() {
addHttpListener();
GET_CONFIG(string,api_secret,API::kSecret);
//获取线程负载
//测试url http://127.0.0.1/index/api/getThreadsLoad
// 获取线程负载 [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
api_regist("/index/api/getThreadsLoad", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
EventPollerPool::Instance().getExecutorDelay([invoker, headerOut](const vector<int> &vecDelay) {
@ -698,8 +736,10 @@ void installWebApi() {
});
});
//获取后台工作线程负载
//测试url http://127.0.0.1/index/api/getWorkThreadsLoad
// 获取后台工作线程负载 [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
api_regist("/index/api/getWorkThreadsLoad", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
WorkThreadPool::Instance().getExecutorDelay([invoker, headerOut](const vector<int> &vecDelay) {
@ -717,8 +757,10 @@ void installWebApi() {
});
});
//获取服务器配置
//测试url http://127.0.0.1/index/api/getServerConfig
// 获取服务器配置 [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
api_regist("/index/api/getServerConfig",[](API_ARGS_MAP){
CHECK_SECRET();
Value obj;
@ -728,9 +770,12 @@ void installWebApi() {
val["data"].append(obj);
});
//设置服务器配置
//测试url(比如关闭http api调试) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0
//你也可以通过http post方式传参可以通过application/x-www-form-urlencoded或application/json方式传参
// 设置服务器配置 [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
api_regist("/index/api/setServerConfig",[](API_ARGS_MAP){
CHECK_SECRET();
auto &ini = mINI::Instance();
@ -738,12 +783,15 @@ void installWebApi() {
for (auto &pr : allArgs.args) {
if (ini.find(pr.first) == ini.end()) {
#if 1
//没有这个key
// 没有这个key [AUTO-TRANSLATED:d6855e02]
// This key does not exist
continue;
#else
// 新增配置选项,为了动态添加多个ffmpeg cmd 模板
// 新增配置选项,为了动态添加多个ffmpeg cmd 模板 [AUTO-TRANSLATED:0f977fcd]
// Add configuration options to dynamically add multiple ffmpeg cmd templates
ini[pr.first] = pr.second;
// 防止changed变化
// 防止changed变化 [AUTO-TRANSLATED:f8ad7e59]
// Prevent changed changes
continue;
#endif
}
@ -755,7 +803,8 @@ void installWebApi() {
continue;
}
ini[pr.first] = pr.second;
//替换成功
// 替换成功 [AUTO-TRANSLATED:b5d4fec1]
// Replacement successful
++changed;
}
if (changed > 0) {
@ -773,28 +822,36 @@ void installWebApi() {
}
};
//获取服务器api列表
//测试url http://127.0.0.1/index/api/getApiList
// 获取服务器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
api_regist("/index/api/getApiList",[](API_ARGS_MAP){
s_get_api_list(API_ARGS_VALUE);
});
//获取服务器api列表
//测试url http://127.0.0.1/index/
// 获取服务器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/
api_regist("/index/",[](API_ARGS_MAP){
s_get_api_list(API_ARGS_VALUE);
});
#if !defined(_WIN32)
//重启服务器,只有Daemon方式才能重启否则是直接关闭
//测试url http://127.0.0.1/index/api/restartServer
// 重启服务器,只有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
api_regist("/index/api/restartServer",[](API_ARGS_MAP){
CHECK_SECRET();
EventPollerPool::Instance().getPoller()->doDelayTask(1000,[](){
//尝试正常退出
// 尝试正常退出 [AUTO-TRANSLATED:93828d0f]
// Try to exit normally
::kill(getpid(), SIGINT);
//3秒后强制退出
// 3秒后强制退出 [AUTO-TRANSLATED:fdc82920]
// Force exit after 3 seconds
EventPollerPool::Instance().getPoller()->doDelayTask(3000,[](){
exit(0);
return 0;
@ -805,10 +862,12 @@ void installWebApi() {
val["msg"] = "MediaServer will reboot in on 1 second";
});
#else
//增加Windows下的重启代码
// 增加Windows下的重启代码 [AUTO-TRANSLATED:dcba12d5]
// Add restart code for Windows
api_regist("/index/api/restartServer", [](API_ARGS_MAP) {
CHECK_SECRET();
//创建重启批处理脚本文件
// 创建重启批处理脚本文件 [AUTO-TRANSLATED:cc18c259]
// Create a restart batch script file
FILE *pf;
errno_t err = ::_wfopen_s(&pf, L"RestartServer.cmd", L"w"); //“w”如果该文件存在其内容将被覆盖
if (err == 0) {
@ -824,7 +883,8 @@ void installWebApi() {
strcat(exeName, ext);
fprintf(pf, "@echo off\ntaskkill /f /im %s\nstart \"\" \"%s\"\ndel %%0", exeName, szExeName);
fclose(pf);
// 1秒后执行创建的批处理脚本
// 1秒后执行创建的批处理脚本 [AUTO-TRANSLATED:596dbca9]
// Execute the created batch script after 1 second
EventPollerPool::Instance().getPoller()->doDelayTask(1000, []() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
@ -859,27 +919,35 @@ void installWebApi() {
});
#endif//#if !defined(_WIN32)
//获取流列表,可选筛选参数
//测试url0(获取所有流) http://127.0.0.1/index/api/getMediaList
//测试url1(获取虚拟主机为"__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: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
api_regist("/index/api/getMediaList",[](API_ARGS_MAP){
CHECK_SECRET();
//获取所有MediaSource列表
// 获取所有MediaSource列表 [AUTO-TRANSLATED:7bf16dc2]
// Get all MediaSource lists
MediaSource::for_each_media([&](const MediaSource::Ptr &media) {
val["data"].append(makeMediaSourceJson(*media));
}, allArgs["schema"], allArgs["vhost"], allArgs["app"], allArgs["stream"]);
});
//测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
// 测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs [AUTO-TRANSLATED:126a75e8]
// Test url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
api_regist("/index/api/isMediaOnline",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("schema","vhost","app","stream");
val["online"] = (bool) (MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"]));
});
//获取媒体流播放器列表
//测试url http://127.0.0.1/index/api/getMediaPlayerList?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
// 获取媒体流播放器列表 [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
api_regist("/index/api/getMediaPlayerList",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("schema", "vhost", "app", "stream");
@ -922,7 +990,8 @@ void installWebApi() {
src->broadcastMessage(any);
});
//测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
// 测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs [AUTO-TRANSLATED:9402e811]
// Test url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
api_regist("/index/api/getMediaInfo",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("schema","vhost","app","stream");
@ -937,12 +1006,15 @@ void installWebApi() {
});
});
//主动关断流,包括关断拉流、推流
//测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
// 主动关断流,包括关断拉流、推流 [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
api_regist("/index/api/close_stream",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("schema","vhost","app","stream");
//踢掉推流器
// 踢掉推流器 [AUTO-TRANSLATED:61e39b14]
// Kick out the pusher
auto src = MediaSource::find(allArgs["schema"],
allArgs["vhost"],
allArgs["app"],
@ -961,11 +1033,14 @@ void installWebApi() {
});
});
//批量主动关断流,包括关断拉流、推流
//测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
// 批量主动关断流,包括关断拉流、推流 [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
api_regist("/index/api/close_streams",[](API_ARGS_MAP){
CHECK_SECRET();
//筛选命中个数
// 筛选命中个数 [AUTO-TRANSLATED:6db1e8c7]
// Filter hit count
int count_hit = 0;
int count_closed = 0;
list<MediaSource::Ptr> media_list;
@ -984,9 +1059,12 @@ void installWebApi() {
val["count_closed"] = count_closed;
});
//获取所有Session列表信息
//可以根据本地端口和远端ip来筛选
//测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935
// 获取所有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
api_regist("/index/api/getAllSession",[](API_ARGS_MAP){
CHECK_SECRET();
Value jsession;
@ -1007,12 +1085,15 @@ void installWebApi() {
});
});
//断开tcp连接比如说可以断开rtsp、rtmp播放器等
//测试url http://127.0.0.1/index/api/kick_session?id=123456
// 断开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
api_regist("/index/api/kick_session",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("id");
//踢掉tcp会话
// 踢掉tcp会话 [AUTO-TRANSLATED:f6f318bd]
// Kick out the tcp session
auto session = SessionMap::Instance().get(allArgs["id"]);
if(!session){
throw ApiRetException("can not find the target",API::OtherFailed);
@ -1021,8 +1102,10 @@ void installWebApi() {
});
//批量断开tcp连接比如说可以断开rtsp、rtmp播放器等
//测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935
// 批量断开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
api_regist("/index/api/kick_sessions", [](API_ARGS_MAP) {
CHECK_SECRET();
uint16_t local_port = allArgs["local_port"].as<uint16_t>();
@ -1038,7 +1121,8 @@ void installWebApi() {
return;
}
if (session->getIdentifier() == sender.getIdentifier()) {
// 忽略本http链接
// 忽略本http链接 [AUTO-TRANSLATED:9fb4bf76]
// Ignore this http link
return;
}
session_list.emplace_back(session);
@ -1051,8 +1135,10 @@ void installWebApi() {
val["count_hit"] = (Json::UInt64)count_hit;
});
//动态添加rtsp/rtmp推流代理
//测试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
// 动态添加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
api_regist("/index/api/addStreamPusherProxy", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
CHECK_ARGS("schema", "vhost", "app", "stream", "dst_url");
@ -1078,16 +1164,20 @@ void installWebApi() {
});
});
//关闭推流代理
//测试url http://127.0.0.1/index/api/delStreamPusherProxy?key=__defaultVhost__/proxy/0
// 关闭推流代理 [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
api_regist("/index/api/delStreamPusherProxy", [](API_ARGS_MAP) {
CHECK_SECRET();
CHECK_ARGS("key");
val["data"]["flag"] = s_pusher_proxy.erase(allArgs["key"]) == 1;
});
//动态添加rtsp/rtmp拉流代理
//测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs
// 动态添加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
api_regist("/index/api/addStreamProxy",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("vhost","app","stream","url");
@ -1123,8 +1213,10 @@ void installWebApi() {
});
});
//关闭拉流代理
//测试url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0
// 关闭拉流代理 [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
api_regist("/index/api/delStreamProxy",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("key");
@ -1140,7 +1232,8 @@ void installWebApi() {
const function<void(const SockException &ex, const string &key)> &cb) {
auto key = MD5(dst_url).hexdigest();
if (s_ffmpeg_src.find(key)) {
//已经在拉流了
// 已经在拉流了 [AUTO-TRANSLATED:e06c57d7]
// Already pulling
cb(SockException(Err_success), key);
return;
}
@ -1159,8 +1252,10 @@ void installWebApi() {
});
};
//动态添加rtsp/rtmp拉流代理
//测试url http://127.0.0.1/index/api/addFFmpegSource?src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000
// 动态添加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
api_regist("/index/api/addFFmpegSource",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("src_url","dst_url","timeout_ms");
@ -1182,16 +1277,20 @@ void installWebApi() {
});
});
//关闭拉流代理
//测试url http://127.0.0.1/index/api/delFFmepgSource?key=key
// 关闭拉流代理 [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
api_regist("/index/api/delFFmpegSource",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("key");
val["data"]["flag"] = s_ffmpeg_src.erase(allArgs["key"]) == 1;
});
//新增http api下载可执行程序文件接口
//测试url http://127.0.0.1/index/api/downloadBin
// 新增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
api_regist("/index/api/downloadBin",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
invoker.responseFile(allArgs.parser.getHeader(), StrCaseMap(), exePath());
@ -1234,12 +1333,14 @@ void installWebApi() {
auto tuple = MediaTuple { vhost, app, stream_id, "" };
auto tcp_mode = allArgs["tcp_mode"].as<int>();
if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
//兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数
// 兼容老版本请求新版本去除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
tcp_mode = 1;
}
auto only_track = allArgs["only_track"].as<int>();
if (allArgs["only_audio"].as<bool>()) {
// 兼容老版本请求新版本去除only_audio参数并新增only_track参数
// 兼容老版本请求新版本去除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
only_track = 1;
}
GET_CONFIG(std::string, local_ip, General::kListenIP)
@ -1251,7 +1352,8 @@ void installWebApi() {
if (port == 0) {
throw InvalidArgsException("This stream already exists");
}
//回复json
// 回复json [AUTO-TRANSLATED:0c443c6a]
// Reply json
val["port"] = port;
});
@ -1270,12 +1372,14 @@ void installWebApi() {
auto tuple = MediaTuple { vhost, app, stream_id, "" };
auto tcp_mode = allArgs["tcp_mode"].as<int>();
if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
// 兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数
// 兼容老版本请求新版本去除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
tcp_mode = 1;
}
auto only_track = allArgs["only_track"].as<int>();
if (allArgs["only_audio"].as<bool>()) {
// 兼容老版本请求新版本去除only_audio参数并新增only_track参数
// 兼容老版本请求新版本去除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
only_track = 1;
}
std::string local_ip = "::";
@ -1287,7 +1391,8 @@ void installWebApi() {
if (port == 0) {
throw InvalidArgsException("This stream already exists");
}
// 回复json
// 回复json [AUTO-TRANSLATED:e80815cd]
// Reply json
val["port"] = port;
});
@ -1384,7 +1489,8 @@ void installWebApi() {
}
auto type = allArgs["type"].empty() ? (int)MediaSourceEvent::SendRtpArgs::kRtpPS : allArgs["type"].as<int>();
if (!allArgs["use_ps"].empty()) {
// 兼容之前的use_ps参数
// 兼容之前的use_ps参数 [AUTO-TRANSLATED:0193f489]
// Compatible with the previous use_ps parameter
type = allArgs["use_ps"].as<int>();
}
MediaSourceEvent::SendRtpArgs args;
@ -1404,6 +1510,10 @@ void installWebApi() {
args.udp_rtcp_timeout = allArgs["udp_rtcp_timeout"];
args.recv_stream_id = allArgs["recv_stream_id"];
args.close_delay_ms = allArgs["close_delay_ms"];
// 记录发送流的app和vhost [AUTO-TRANSLATED:ee1b41d5]
// Record the app and vhost of the sending stream
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 {
@ -1464,7 +1574,8 @@ void installWebApi() {
}
src->getOwnerPoller()->async([=]() mutable {
// ssrc如果为空关闭全部
// ssrc如果为空关闭全部 [AUTO-TRANSLATED:e0955dab]
// If ssrc is empty, close all
if (!src->stopSendRtp(allArgs["ssrc"])) {
val["code"] = API::OtherFailed;
val["msg"] = "stopSendRtp failed";
@ -1486,7 +1597,8 @@ void installWebApi() {
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
//只是暂停流的检查流媒体服务器做为流负载服务收流就转发RTSP/RTMP有自己暂停协议
// 只是暂停流的检查流媒体服务器做为流负载服务收流就转发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
auto src = MediaSource::find(vhost, app, allArgs["stream_id"]);
auto process = src ? src->getRtpProcess() : nullptr;
if (process) {
@ -1518,7 +1630,8 @@ void installWebApi() {
#endif//ENABLE_RTPPROXY
// 开始录制hls或MP4
// 开始录制hls或MP4 [AUTO-TRANSLATED:0818775e]
// Start recording hls or MP4
api_regist("/index/api/startRecord",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("type","vhost","app","stream");
@ -1537,7 +1650,8 @@ void installWebApi() {
});
});
//设置录像流播放速度
// 设置录像流播放速度 [AUTO-TRANSLATED:a8d82298]
// Set the playback speed of the recording stream
api_regist("/index/api/setRecordSpeed", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
CHECK_ARGS("schema", "vhost", "app", "stream", "speed");
@ -1580,7 +1694,8 @@ void installWebApi() {
});
});
// 停止录制hls或MP4
// 停止录制hls或MP4 [AUTO-TRANSLATED:24d11a0c]
// Stop recording hls or MP4
api_regist("/index/api/stopRecord",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("type","vhost","app","stream");
@ -1600,7 +1715,8 @@ void installWebApi() {
});
});
// 获取hls或MP4录制状态
// 获取hls或MP4录制状态 [AUTO-TRANSLATED:a08a2f1a]
// Get the recording status of hls or MP4
api_regist("/index/api/isRecording",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("type","vhost","app","stream");
@ -1645,7 +1761,8 @@ void installWebApi() {
invoker(200, headerOut, val.toStyledString());
});
// 删除录像文件夹
// 删除录像文件夹 [AUTO-TRANSLATED:821aed07]
// Delete the recording folder
// http://127.0.0.1/index/api/deleteRecordDirectroy?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01-01
api_regist("/index/api/deleteRecordDirectory", [](API_ARGS_MAP) {
CHECK_SECRET();
@ -1658,10 +1775,12 @@ void installWebApi() {
bool recording = false;
auto name = allArgs["name"];
if (!name.empty()) {
// 删除指定文件
// 删除指定文件 [AUTO-TRANSLATED:e8ee7bfa]
// Delete the specified file
record_path += name;
} else {
// 删除文件夹,先判断该流是否正在录制中
// 删除文件夹,先判断该流是否正在录制中 [AUTO-TRANSLATED:9f124786]
// Delete the folder, first check if the stream is being recorded
auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]);
if (src && src->isRecording(Recorder::type_mp4)) {
recording = true;
@ -1686,7 +1805,8 @@ void installWebApi() {
File::deleteEmptyDir(record_path);
});
//获取录像文件夹列表或mp4文件列表
// 获取录像文件夹列表或mp4文件列表 [AUTO-TRANSLATED:f7e299bc]
// Get the list of recording folders or mp4 files
//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){
CHECK_SECRET();
@ -1695,25 +1815,29 @@ void installWebApi() {
auto record_path = Recorder::getRecordPath(Recorder::type_mp4, tuple, allArgs["customized_path"]);
auto period = allArgs["period"];
//判断是获取mp4文件列表还是获取文件夹列表
// 判断是获取mp4文件列表还是获取文件夹列表 [AUTO-TRANSLATED:b9c86d2f]
// Determine whether to get the mp4 file list or the folder list
bool search_mp4 = period.size() == sizeof("2020-02-01") - 1;
if (search_mp4) {
record_path = record_path + period + "/";
}
Json::Value paths(arrayValue);
//这是筛选日期,获取文件夹列表
// 这是筛选日期,获取文件夹列表 [AUTO-TRANSLATED:786fa49d]
// This is to filter the date and get the folder list
File::scanDir(record_path, [&](const string &path, bool isDir) {
auto pos = path.rfind('/');
if (pos != string::npos) {
string relative_path = path.substr(pos + 1);
if (search_mp4) {
if (!isDir) {
//我们只收集mp4文件对文件夹不感兴趣
// 我们只收集mp4文件对文件夹不感兴趣 [AUTO-TRANSLATED:254d9f25]
// We only collect mp4 files, we are not interested in folders
paths.append(relative_path);
}
} else if (isDir && relative_path.find(period) == 0) {
//匹配到对应日期的文件夹
// 匹配到对应日期的文件夹 [AUTO-TRANSLATED:cd3d10b9]
// Match the folder for the corresponding date
paths.append(relative_path);
}
}
@ -1733,24 +1857,29 @@ void installWebApi() {
GET_CONFIG(string, defaultSnap, API::kDefaultSnap);
if (!File::fileSize(snap_path)) {
if (!err_msg.empty() && (!s_snap_success_once || defaultSnap.empty())) {
//重来没截图成功过或者默认截图图片为空那么直接返回FFmpeg错误日志
// 重来没截图成功过或者默认截图图片为空那么直接返回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
headerOut["Content-Type"] = HttpFileManager::getContentType(".txt");
invoker.responseFile(headerIn, headerOut, err_msg, false, false);
return;
}
//截图成功过一次,那么认为配置无错误,截图失败时,返回预设默认图片
// 截图成功过一次,那么认为配置无错误,截图失败时,返回预设默认图片 [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
const_cast<string &>(snap_path) = File::absolutePath("", defaultSnap);
headerOut["Content-Type"] = HttpFileManager::getContentType(snap_path.data());
} else {
s_snap_success_once = true;
//之前生成的截图文件我们默认为jpeg格式
// 之前生成的截图文件我们默认为jpeg格式 [AUTO-TRANSLATED:5cc5c1ff]
// The previously generated screenshot file, we default to jpeg format
headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg");
}
//返回图片给http客户端
// 返回图片给http客户端 [AUTO-TRANSLATED:58a1f64e]
// Return image to http client
invoker.responseFile(headerIn, headerOut, snap_path);
};
//获取截图缓存或者实时截图
// 获取截图缓存或者实时截图 [AUTO-TRANSLATED:78e2fe1e]
// Get screenshot cache or real-time screenshot
//http://127.0.0.1/index/api/getSnap?url=rtmp://127.0.0.1/record/robot.mp4&timeout_sec=10&expire_sec=3
api_regist("/index/api/getSnap", [](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
@ -1764,49 +1893,61 @@ void installWebApi() {
File::scanDir(scan_path, [&](const string &path, bool isDir) {
if (isDir || !end_with(path, ".jpeg")) {
//忽略文件夹或其他类型的文件
// 忽略文件夹或其他类型的文件 [AUTO-TRANSLATED:3ecffcae]
// Ignore folders or other types of files
return true;
}
//找到截图
// 找到截图 [AUTO-TRANSLATED:b784cfec]
// Find screenshot
auto tm = findSubString(path.data() + scan_path.size(), nullptr, ".jpeg");
if (atoll(tm.data()) + expire_sec < time(NULL)) {
//截图已经过期,改名,以便再次请求时,可以返回老截图
// 截图已经过期,改名,以便再次请求时,可以返回老截图 [AUTO-TRANSLATED:94fac79b]
// Screenshot has expired, rename it so that it can be returned when requested again
rename(path.data(), new_snap.data());
have_old_snap = true;
return true;
}
//截图存在,且未过期,那么返回之
// 截图存在,且未过期,那么返回之 [AUTO-TRANSLATED:6f53d3d1]
// Screenshot exists and has not expired, so return it
res_old_snap = true;
responseSnap(path, allArgs.parser.getHeader(), invoker);
//中断遍历
// 中断遍历 [AUTO-TRANSLATED:7893aab3]
// Interrupt traversal
return false;
});
if (res_old_snap) {
//已经回复了旧的截图
// 已经回复了旧的截图 [AUTO-TRANSLATED:9051a3e6]
// Old screenshot has been replied
return;
}
//无截图或者截图已经过期
// 无截图或者截图已经过期 [AUTO-TRANSLATED:89c46415]
// No screenshot or screenshot has expired
if (!have_old_snap) {
//无过期截图,生成一个空文件,目的是顺便创建文件夹路径
//同时防止在FFmpeg生成截图途中不停的尝试调用该api多次启动FFmpeg进程
// 无过期截图,生成一个空文件,目的是顺便创建文件夹路径 [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
auto file = File::create_file(new_snap, "wb");
if (file) {
fclose(file);
}
}
//启动FFmpeg进程开始截图生成临时文件截图成功后替换为正式文件
// 启动FFmpeg进程开始截图生成临时文件截图成功后替换为正式文件 [AUTO-TRANSLATED:7d589e3f]
// Start the FFmpeg process, start taking screenshots, generate temporary files, replace them with formal files after successful screenshots
auto new_snap_tmp = new_snap + ".tmp";
FFmpegSnap::makeSnap(allArgs["url"], new_snap_tmp, allArgs["timeout_sec"], [invoker, allArgs, new_snap, new_snap_tmp](bool success, const string &err_msg) {
if (!success) {
//生成截图失败,可能残留空文件
// 生成截图失败,可能残留空文件 [AUTO-TRANSLATED:c96a4468]
// Screenshot generation failed, there may be residual empty files
File::delete_file(new_snap_tmp);
} else {
//临时文件改成正式文件
// 临时文件改成正式文件 [AUTO-TRANSLATED:eca24dfd]
// Temporary file changed to formal file
File::delete_file(new_snap);
rename(new_snap_tmp.data(), new_snap.data());
}
@ -1842,8 +1983,9 @@ void installWebApi() {
auto &allArgs = _args;
CHECK_ARGS("app", "stream");
return StrPrinter << "rtc://" << _args["Host"] << "/" << _args["app"] << "/"
<< _args["stream"] << "?" << _args.parser.params() + "&session=" + _session_id;
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));
}
private:
@ -1885,7 +2027,8 @@ void installWebApi() {
WebRtcPluginManager::Instance().negotiateSdp(session, type, *args, [invoker, offer, headerOut, location](const WebRtcInterface &exchanger) mutable {
auto &handler = const_cast<WebRtcInterface &>(exchanger);
try {
// 设置返回类型
// 设置返回类型 [AUTO-TRANSLATED:ffc2a31a]
// Set return type
headerOut["Content-Type"] = "application/sdp";
headerOut["Location"] = location + "?id=" + exchanger.getIdentifier() + "&token=" + exchanger.deleteRandStr();
invoker(201, headerOut, handler.getAnswerSdp(offer));
@ -1934,17 +2077,22 @@ void installWebApi() {
CHECK_ARGS("vhost", "app", "stream", "file_path");
ProtocolOption option;
// mp4支持多track
// mp4支持多track [AUTO-TRANSLATED:b9688762]
// mp4 supports multiple tracks
option.max_track = 16;
// 默认解复用mp4不生成mp4
// 默认解复用mp4不生成mp4 [AUTO-TRANSLATED:11f2dcee]
// By default, demultiplexing mp4 does not generate mp4
option.enable_mp4 = false;
// 但是如果参数明确指定开启mp4, 那么也允许之
// 但是如果参数明确指定开启mp4, 那么也允许之 [AUTO-TRANSLATED:b143a9e3]
// But if the parameter explicitly specifies to enable mp4, then it is also allowed
option.load(allArgs);
// 强制无人观看时自动关闭
// 强制无人观看时自动关闭 [AUTO-TRANSLATED:f7c85948]
// Force automatic shutdown when no one is watching
option.auto_close = true;
auto tuple = MediaTuple{allArgs["vhost"], allArgs["app"], allArgs["stream"], ""};
auto reader = std::make_shared<MP4Reader>(tuple, allArgs["file_path"], option);
// sample_ms设置为0从配置文件加载file_repeat可以指定如果配置文件也指定循环解复用那么强制开启
// 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
reader->startReadMP4(0, true, allArgs["file_repeat"]);
});
#endif
@ -1979,7 +2127,8 @@ void installWebApi() {
return;
}
// 通过on_http_access完成文件下载鉴权请务必确认访问鉴权url参数以及访问文件路径是否合法
// 通过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
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);
@ -1995,7 +2144,8 @@ void installWebApi() {
bool flag = NOTICE_EMIT(BroadcastHttpAccessArgs, Broadcast::kBroadcastHttpAccess, allArgs.parser, file_path, false, file_invoker, sender);
if (!flag) {
// 文件下载鉴权事件无人监听,不允许下载
// 文件下载鉴权事件无人监听,不允许下载 [AUTO-TRANSLATED:5e02f0ce]
// No one is listening to the file download authentication event, download is not allowed
invoker(401, StrCaseMap {}, "None http access event listener");
}
});

View File

@ -19,16 +19,19 @@
#include "Http/HttpSession.h"
#include "Common/MultiMediaSourceMuxer.h"
//配置文件路径
// 配置文件路径 [AUTO-TRANSLATED:8a373c2f]
// Configuration file path
extern std::string g_ini_file;
namespace mediakit {
////////////RTSP服务器配置///////////
// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981]
// //////////RTSP server configuration///////////
namespace Rtsp {
extern const std::string kPort;
} //namespace Rtsp
////////////RTMP服务器配置///////////
// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f]
// //////////RTMP server configuration///////////
namespace Rtmp {
extern const std::string kPort;
} //namespace RTMP
@ -153,19 +156,25 @@ using ArgsString = HttpAllArgs<std::string>;
#define API_ARGS_STRING_ASYNC API_ARGS_STRING, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_VALUE sender, headerOut, allArgs, val
//注册http请求参数是map<string, variant, StrCaseCompare>类型的http api
// 注册http请求参数是map<string, variant, StrCaseCompare>类型的http api [AUTO-TRANSLATED:8a273897]
// Register http request parameters as map<string, variant, StrCaseCompare> type http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP)> &func);
//注册http请求参数是map<string, variant, StrCaseCompare>类型,但是可以异步回复的的http api
// 注册http请求参数是map<string, variant, StrCaseCompare>类型,但是可以异步回复的的http api [AUTO-TRANSLATED:9da5d5f5]
// Register http request parameters as map<string, variant, StrCaseCompare> type, but can be replied asynchronously http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP_ASYNC)> &func);
//注册http请求参数是Json::Value类型的http api(可以支持多级嵌套的json参数对象)
// 注册http请求参数是Json::Value类型的http api(可以支持多级嵌套的json参数对象) [AUTO-TRANSLATED:c4794456]
// Register http request parameters as Json::Value type http api (can support multi-level nested json parameter objects)
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_JSON)> &func);
//注册http请求参数是Json::Value类型但是可以异步回复的的http api
// 注册http请求参数是Json::Value类型但是可以异步回复的的http api [AUTO-TRANSLATED:742e57fd]
// Register http request parameters as Json::Value type, but can be replied asynchronously http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_JSON_ASYNC)> &func);
//注册http请求参数是http原始请求信息的http api
// 注册http请求参数是http原始请求信息的http api [AUTO-TRANSLATED:72d3fe93]
// Register http request parameters as http original request information http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING)> &func);
//注册http请求参数是http原始请求信息的异步回复的http api
// 注册http请求参数是http原始请求信息的异步回复的http api [AUTO-TRANSLATED:49feefa8]
// Register http request parameters as http original request information asynchronous reply http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING_ASYNC)> &func);
template<typename Args, typename First>
@ -178,14 +187,17 @@ bool checkArgs(Args &args, const First &first, const KeyTypes &...keys) {
return checkArgs(args, first) && checkArgs(args, keys...);
}
//检查http url中或body中或http header参数是否为空的宏
// 检查http url中或body中或http header参数是否为空的宏 [AUTO-TRANSLATED:9de001a4]
// Check whether the http url, body or http header parameters are empty
#define CHECK_ARGS(...) \
if(!checkArgs(allArgs,##__VA_ARGS__)){ \
throw InvalidArgsException("Required parameter missed: " #__VA_ARGS__); \
}
// 检查http参数中是否附带secret密钥的宏127.0.0.1的ip不检查密钥
// 同时检测是否在ip白名单内
// 检查http参数中是否附带secret密钥的宏127.0.0.1的ip不检查密钥 [AUTO-TRANSLATED:7546956c]
// Check whether the http parameters contain the secret key, the ip of 127.0.0.1 does not check the key
// 同时检测是否在ip白名单内 [AUTO-TRANSLATED:d12f963d]
// Check whether it is in the ip whitelist at the same time
#define CHECK_SECRET() \
do { \
auto ip = sender.get_peer_ip(); \

View File

@ -56,7 +56,8 @@ const string kRetryDelay = HOOK_FIELD "retry_delay";
static onceToken token([]() {
mINI::Instance()[kEnable] = false;
mINI::Instance()[kTimeoutSec] = 10;
// 默认hook地址设置为空采用默认行为(例如不鉴权)
// 默认hook地址设置为空采用默认行为(例如不鉴权) [AUTO-TRANSLATED:0e38bc3c]
// Default hook address is set to empty, using default behavior (e.g. no authentication)
mINI::Instance()[kOnPublish] = "";
mINI::Instance()[kOnPlay] = "";
mINI::Instance()[kOnFlowReport] = "";
@ -134,7 +135,8 @@ static void parse_http_response(const SockException &ex, const Parser &res, cons
fun(result, "", should_retry);
} catch (std::exception &ex) {
auto errStr = StrPrinter << "[do hook invoker failed]:" << ex.what() << endl;
// 如果还是抛异常,那么再上抛异常
// 如果还是抛异常,那么再上抛异常 [AUTO-TRANSLATED:cc66ff58]
// If an exception is still thrown, then re-throw the exception
fun(Json::nullValue, errStr, should_retry);
}
}
@ -190,7 +192,8 @@ void do_http_hook(const string &url, const ArgsType &body, const function<void(c
onceToken token(nullptr, [&]() mutable { requester.reset(); });
parse_http_response(ex, res, [&](const Value &obj, const string &err, bool should_retry) {
if (!err.empty()) {
// hook失败
// hook失败 [AUTO-TRANSLATED:68231f46]
// Hook failed
WarnL << "hook " << url << " " << ticker.elapsedTime() << "ms,failed" << err << ":" << bodyStr;
if (retry-- > 0 && should_retry) {
@ -198,12 +201,14 @@ void do_http_hook(const string &url, const ArgsType &body, const function<void(c
do_http_hook(url, body, func, retry);
return 0;
});
// 重试不需要触发回调
// 重试不需要触发回调 [AUTO-TRANSLATED:41917311]
// Retry does not need to trigger callback
return;
}
} else if (ticker.elapsedTime() > 500) {
// hook成功但是hook响应超过500ms打印警告日志
// hook成功但是hook响应超过500ms打印警告日志 [AUTO-TRANSLATED:e03557aa]
// Hook succeeded, but hook response exceeded 500ms, print warning log
DebugL << "hook " << url << " " << ticker.elapsedTime() << "ms,success:" << bodyStr;
}
@ -240,7 +245,8 @@ static void reportServerStarted() {
for (auto &pr : mINI::Instance()) {
body[pr.first] = (string &)pr.second;
}
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_server_started, body, nullptr);
}
@ -252,11 +258,13 @@ static void reportServerExited() {
}
const ArgsType body;
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_server_exited, body, nullptr);
}
// 服务器定时保活定时器
// 服务器定时保活定时器 [AUTO-TRANSLATED:2bb39e50]
// Server keep-alive timer
static Timer::Ptr g_keepalive_timer;
static void reportServerKeepalive() {
GET_CONFIG(bool, hook_enable, Hook::kEnable);
@ -270,7 +278,8 @@ static void reportServerKeepalive() {
getStatisticJson([](const Value &data) mutable {
ArgsType body;
body["data"] = data;
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_server_keepalive, body, nullptr);
});
return true;
@ -285,7 +294,8 @@ static string getPullUrl(const string &origin_fmt, const MediaInfo &info) {
WarnL << "get origin url failed, origin_fmt:" << origin_fmt;
return "";
}
// 告知源站这是来自边沿站的拉流请求,如果未找到流请立即返回拉流失败
// 告知源站这是来自边沿站的拉流请求,如果未找到流请立即返回拉流失败 [AUTO-TRANSLATED:adf0d210]
// Inform the origin station that this is a pull stream request from the edge station, if the stream is not found, please return the pull stream failure immediately
return string(url) + '?' + kEdgeServerParam + '&' + VHOST_KEY + '=' + info.vhost + '&' + info.params;
}
@ -305,9 +315,11 @@ static void pullStreamFromOrigin(const vector<string> &urls, size_t index, size_
if (!ex) {
return;
}
// 拉流失败
// 拉流失败 [AUTO-TRANSLATED:6d52eb25]
// Pull stream failed
if (++failed_cnt == urls.size()) {
// 已经重试所有源站了
// 已经重试所有源站了 [AUTO-TRANSLATED:b3b384a8]
// All origin stations have been retried
WarnL << "pull stream from origin final failed: " << url;
closePlayer();
return;
@ -342,20 +354,24 @@ void installWebHook() {
invoker("", ProtocolOption());
return;
}
// 异步执行该hook api防止阻塞NoticeCenter
// 异步执行该hook api防止阻塞NoticeCenter [AUTO-TRANSLATED:783f64c1]
// Asynchronously execute this hook api to prevent blocking NoticeCenter
auto body = make_json(args);
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
body["originType"] = (int)type;
body["originTypeStr"] = getOriginTypeString(type);
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_publish, body, [invoker](const Value &obj, const string &err) mutable {
if (err.empty()) {
// 推流鉴权成功
// 推流鉴权成功 [AUTO-TRANSLATED:e4285dab]
// Push stream authentication succeeded
invoker(err, ProtocolOption(jsonToMini(obj)));
} else {
// 推流鉴权失败
// 推流鉴权失败 [AUTO-TRANSLATED:780430e0]
// Push stream authentication failed
invoker(err, ProtocolOption());
}
});
@ -371,7 +387,8 @@ void installWebHook() {
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_play, body, [invoker](const Value &obj, const string &err) { invoker(err); });
});
@ -387,17 +404,20 @@ void installWebHook() {
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_flowreport, body, nullptr);
});
static const string unAuthedRealm = "unAuthedRealm";
// 监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问
// 监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问 [AUTO-TRANSLATED:00dc9fa3]
// Listen to the kBroadcastOnGetRtspRealm event to determine whether the rtsp link needs authentication (traditional rtsp authentication scheme) to access
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastOnGetRtspRealm, [](BroadcastOnGetRtspRealmArgs) {
GET_CONFIG(string, hook_rtsp_realm, Hook::kOnRtspRealm);
if (!hook_enable || hook_rtsp_realm.empty()) {
// 无需认证
// 无需认证 [AUTO-TRANSLATED:77728e07]
// No authentication required
invoker("");
return;
}
@ -405,10 +425,12 @@ void installWebHook() {
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_rtsp_realm, body, [invoker](const Value &obj, const string &err) {
if (!err.empty()) {
// 如果接口访问失败那么该rtsp流认证失败
// 如果接口访问失败那么该rtsp流认证失败 [AUTO-TRANSLATED:81b19b72]
// If the interface access fails, then the rtsp stream authentication fails
invoker(unAuthedRealm);
return;
}
@ -416,11 +438,13 @@ void installWebHook() {
});
});
// 监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码
// 监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码 [AUTO-TRANSLATED:bcf1754e]
// Listen to the kBroadcastOnRtspAuth event to return the correct rtsp authentication username and password
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastOnRtspAuth, [](BroadcastOnRtspAuthArgs) {
GET_CONFIG(string, hook_rtsp_auth, Hook::kOnRtspAuth);
if (unAuthedRealm == realm || !hook_enable || hook_rtsp_auth.empty()) {
// 认证失败
// 认证失败 [AUTO-TRANSLATED:70cf56ff]
// Authentication failed
invoker(false, makeRandStr(12));
return;
}
@ -431,10 +455,12 @@ void installWebHook() {
body["user_name"] = user_name;
body["must_no_encrypt"] = must_no_encrypt;
body["realm"] = realm;
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_rtsp_auth, body, [invoker](const Value &obj, const string &err) {
if (!err.empty()) {
// 认证失败
// 认证失败 [AUTO-TRANSLATED:70cf56ff]
// Authentication failed
invoker(false, makeRandStr(12));
return;
}
@ -442,7 +468,8 @@ void installWebHook() {
});
});
// 监听rtsp、rtmp源注册或注销事件
// 监听rtsp、rtmp源注册或注销事件 [AUTO-TRANSLATED:6396afa8]
// Listen to rtsp, rtmp source registration or deregistration events
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastMediaChanged, [](BroadcastMediaChangedArgs) {
GET_CONFIG(string, hook_stream_changed, Hook::kOnStreamChanged);
if (!hook_enable || hook_stream_changed.empty()) {
@ -460,7 +487,8 @@ void installWebHook() {
return ret;
});
if (!stream_changed_set.empty() && stream_changed_set.find(sender.getSchema()) == stream_changed_set.end()) {
// 该协议注册注销事件被忽略
// 该协议注册注销事件被忽略 [AUTO-TRANSLATED:87299c9d]
// This protocol registration deregistration event is ignored
return;
}
@ -473,7 +501,8 @@ void installWebHook() {
dumpMediaTuple(sender.getMediaTuple(), body);
body["regist"] = bRegist;
}
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_stream_changed, body, nullptr);
});
@ -488,10 +517,12 @@ void installWebHook() {
return ret;
});
// 监听播放失败(未找到特定的流)事件
// 监听播放失败(未找到特定的流)事件 [AUTO-TRANSLATED:ca8cc9ba]
// Listen to playback failure (specific stream not found) event
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastNotFoundStream, [](BroadcastNotFoundStreamArgs) {
if (!origin_urls.empty()) {
// 设置了源站,那么尝试溯源
// 设置了源站,那么尝试溯源 [AUTO-TRANSLATED:541a4ced]
// If the source station is set, then try to trace the source
static atomic<uint8_t> s_index { 0 };
pullStreamFromOrigin(origin_urls, s_index.load(), 0, args, closePlayer);
++s_index;
@ -499,7 +530,8 @@ void installWebHook() {
}
if (start_with(args.params, kEdgeServerParam)) {
// 源站收到来自边沿站的溯源请求,流不存在时立即返回拉流失败
// 源站收到来自边沿站的溯源请求,流不存在时立即返回拉流失败 [AUTO-TRANSLATED:5bd04a34]
// The source station receives a trace request from the edge station, and immediately returns a pull stream failure if the stream does not exist
closePlayer();
return;
}
@ -513,7 +545,8 @@ void installWebHook() {
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// Hook回复立即关闭流
// Hook回复立即关闭流 [AUTO-TRANSLATED:2dcf7bd6]
// Hook reply immediately closes the stream
auto res_cb = [closePlayer](const Value &res, const string &err) {
bool flag = res["close"].asBool();
if (flag) {
@ -521,7 +554,8 @@ void installWebHook() {
}
};
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_stream_not_found, body, res_cb);
});
@ -539,13 +573,15 @@ void installWebHook() {
};
#ifdef ENABLE_MP4
// 录制mp4文件成功后广播
// 录制mp4文件成功后广播 [AUTO-TRANSLATED:479ec954]
// Broadcast after recording the mp4 file successfully
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastRecordMP4, [](BroadcastRecordMP4Args) {
GET_CONFIG(string, hook_record_mp4, Hook::kOnRecordMp4);
if (!hook_enable || hook_record_mp4.empty()) {
return;
}
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_record_mp4, getRecordInfo(info), nullptr);
});
#endif // ENABLE_MP4
@ -555,7 +591,8 @@ void installWebHook() {
if (!hook_enable || hook_record_ts.empty()) {
return;
}
// 执行 hook
// 执行 hook [AUTO-TRANSLATED:d9d66f75]
// Execute hook
do_http_hook(hook_record_ts, getRecordInfo(info), nullptr);
});
@ -572,13 +609,15 @@ void installWebHook() {
body["user_name"] = user_name;
body["passwd"] = passwd;
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_shell_login, body, [invoker](const Value &, const string &err) { invoker(err); });
});
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastStreamNoneReader, [](BroadcastStreamNoneReaderArgs) {
if (!origin_urls.empty() && sender.getOriginType() == MediaOriginType::pull) {
// 边沿站无人观看时如果是拉流的则立即停止溯源
// 边沿站无人观看时如果是拉流的则立即停止溯源 [AUTO-TRANSLATED:a1429c77]
// If no one is watching at the edge station, stop tracing immediately if it is pulling
sender.close(false);
WarnL << "无人观看主动关闭流:" << sender.getOriginUrl();
return;
@ -592,7 +631,8 @@ void installWebHook() {
body["schema"] = sender.getSchema();
dumpMediaTuple(sender.getMediaTuple(), body);
weak_ptr<MediaSource> weakSrc = sender.shared_from_this();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_stream_none_reader, body, [weakSrc](const Value &obj, const string &err) {
bool flag = obj["close"].asBool();
auto strongSrc = weakSrc.lock();
@ -618,7 +658,8 @@ void installWebHook() {
body["originUrl"] = sender.getOriginUrl(MediaSource::NullMediaSource());
body["msg"] = ex.what();
body["err"] = ex.getErrCode();
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_send_rtp_stopped, body, nullptr);
});
@ -629,19 +670,36 @@ void installWebHook() {
* 3cookie标记是否有权限访问文件
* 4cookie中记录的url参数是否跟本次url参数一致
* 5kBroadcastHttpAccess事件
* kBroadcastHttpAccess event trigger mechanism
* 1. Find the cookie according to the http request header, and find it to enter step 3
* 2. Find the cookie according to the http url parameter, if the cookie is still not found, enter step 5
* 3. The cookie marks whether it has permission to access the file, if it has permission, return the file directly
* 4. Whether the url parameter recorded in the cookie is consistent with the current url parameter, if it is consistent, return the client error code directly
* 5. Trigger the kBroadcastHttpAccess event
* [AUTO-TRANSLATED:ecd819e5]
*/
// 开发者应该通过该事件判定http客户端是否有权限访问http服务器上的特定文件
// ZLMediaKit会记录本次鉴权的结果至cookie
// 如果鉴权成功在cookie有效期内那么下次客户端再访问授权目录时ZLMediaKit会直接返回文件
// 如果鉴权失败在cookie有效期内如果http url参数不变(否则会立即再次触发鉴权事件)ZLMediaKit会直接返回错误码
// 如果用户客户端不支持cookie那么ZLMediaKit会根据url参数查找cookie并追踪用户
// 如果没有url参数客户端又不支持cookie那么会根据ip和端口追踪用户
// 追踪用户的目的是为了缓存上次鉴权结果,减少鉴权次数,提高性能
// 开发者应该通过该事件判定http客户端是否有权限访问http服务器上的特定文件 [AUTO-TRANSLATED:938b8bc5]
// Developers should use this event to determine whether the http client has permission to access specific files on the http server
// ZLMediaKit会记录本次鉴权的结果至cookie [AUTO-TRANSLATED:b051ea2e]
// ZLMediaKit will record the result of this authentication to the cookie
// 如果鉴权成功在cookie有效期内那么下次客户端再访问授权目录时ZLMediaKit会直接返回文件 [AUTO-TRANSLATED:be12a468]
// If the authentication is successful, within the validity period of the cookie, the next time the client accesses the authorized directory, ZLMediaKit will return the file directly
// 如果鉴权失败在cookie有效期内如果http url参数不变(否则会立即再次触发鉴权事件)ZLMediaKit会直接返回错误码 [AUTO-TRANSLATED:6396137d]
// If the authentication fails, within the validity period of the cookie, if the http url parameter remains unchanged (otherwise the authentication event will be triggered immediately), ZLMediaKit will return the error code directly
// 如果用户客户端不支持cookie那么ZLMediaKit会根据url参数查找cookie并追踪用户 [AUTO-TRANSLATED:6fd2e366]
// If the user client does not support cookies, then ZLMediaKit will find the cookie according to the url parameter and track the user,
// 如果没有url参数客户端又不支持cookie那么会根据ip和端口追踪用户 [AUTO-TRANSLATED:85a780ea]
// If there is no url parameter and the client does not support cookies, then the user will be tracked according to the ip and port
// 追踪用户的目的是为了缓存上次鉴权结果,减少鉴权次数,提高性能 [AUTO-TRANSLATED:22827145]
// The purpose of tracking users is to cache the last authentication result, reduce the number of authentication times, and improve performance
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastHttpAccess, [](BroadcastHttpAccessArgs) {
GET_CONFIG(string, hook_http_access, Hook::kOnHttpAccess);
if (!hook_enable || hook_http_access.empty()) {
// 未开启http文件访问鉴权那么允许访问但是每次访问都要鉴权
// 因为后续随时都可能开启鉴权(重载配置文件后可能重新开启鉴权)
// 未开启http文件访问鉴权那么允许访问但是每次访问都要鉴权 [AUTO-TRANSLATED:deb3a0ae]
// If http file access authentication is not enabled, then access is allowed, but authentication is required for each access;
// 因为后续随时都可能开启鉴权(重载配置文件后可能重新开启鉴权) [AUTO-TRANSLATED:a090bf06]
// Because authentication may be enabled at any time in the future (authentication may be re-enabled after reloading the configuration file)
if (!HttpFileManager::isIPAllowed(sender.get_peer_ip())) {
invoker("Your ip is not allowed to access the service.", "", 0);
} else {
@ -660,16 +718,21 @@ void installWebHook() {
for (auto &pr : parser.getHeader()) {
body[string("header.") + pr.first] = pr.second;
}
// 执行hook
// 执行hook [AUTO-TRANSLATED:1df68201]
// Execute hook
do_http_hook(hook_http_access, body, [invoker](const Value &obj, const string &err) {
if (!err.empty()) {
// 如果接口访问失败那么仅限本次没有访问http服务器的权限
// 如果接口访问失败那么仅限本次没有访问http服务器的权限 [AUTO-TRANSLATED:f8afd1fd]
// If the interface access fails, then only this time does not have permission to access the http server
invoker(err, "", 0);
return;
}
// err参数代表不能访问的原因空则代表可以访问
// path参数是该客户端能访问或被禁止的顶端目录如果path为空字符串则表述为当前目录
// second参数规定该cookie超时时间如果second为0本次鉴权结果不缓存
// err参数代表不能访问的原因空则代表可以访问 [AUTO-TRANSLATED:87dd19b9]
// The err parameter represents the reason why it cannot be accessed, empty means it can be accessed
// path参数是该客户端能访问或被禁止的顶端目录如果path为空字符串则表述为当前目录 [AUTO-TRANSLATED:b883a448]
// The path parameter is the top directory that this client can access or is prohibited, if path is an empty string, it means the current directory
// second参数规定该cookie超时时间如果second为0本次鉴权结果不缓存 [AUTO-TRANSLATED:1a0b9eb1]
// The second parameter specifies the timeout time of this cookie, if second is 0, the result of this authentication will not be cached
invoker(obj["err"].asString(), obj["path"].asString(), obj["second"].asInt());
});
});
@ -691,10 +754,12 @@ void installWebHook() {
do_http_hook(rtp_server_timeout, body);
});
// 汇报服务器重新启动
// 汇报服务器重新启动 [AUTO-TRANSLATED:bd7d83df]
// Report server restart
reportServerStarted();
// 定时上报保活
// 定时上报保活 [AUTO-TRANSLATED:bd2364a0]
// Report keep-alive regularly
reportServerKeepalive();
}

View File

@ -15,7 +15,8 @@
#include <functional>
#include "json/json.h"
//支持json或urlencoded方式传输参数
// 支持json或urlencoded方式传输参数 [AUTO-TRANSLATED:0e14d484]
// // Support json or urlencoded way to transmit parameters
#define JSON_ARGS
#ifdef JSON_ARGS
@ -25,7 +26,8 @@ typedef mediakit::HttpArgs ArgsType;
#endif
namespace Hook {
//web hook回复最大超时时间
// web hook回复最大超时时间 [AUTO-TRANSLATED:9a059363]
// Maximum timeout for web hook reply
extern const std::string kTimeoutSec;
}//namespace Hook
@ -37,6 +39,13 @@ void onProcessExited();
* @param url
* @param body body
* @param func
* Trigger http hook request
* @param url Request address
* @param body Request body
* @param func Callback
* [AUTO-TRANSLATED:8ffdd09b]
*/
void do_http_hook(const std::string &url, const ArgsType &body, const std::function<void(const Json::Value &, const std::string &)> &func = nullptr);
#endif //ZLMEDIAKIT_WEBHOOK_H

View File

@ -50,7 +50,8 @@ using namespace toolkit;
using namespace mediakit;
namespace mediakit {
////////////HTTP配置///////////
// //////////HTTP配置/////////// [AUTO-TRANSLATED:a281d694]
// //////////HTTP configuration///////////
namespace Http {
#define HTTP_FIELD "http."
const string kPort = HTTP_FIELD"port";
@ -61,7 +62,8 @@ onceToken token1([](){
},nullptr);
}//namespace Http
////////////SHELL配置///////////
// //////////SHELL配置/////////// [AUTO-TRANSLATED:f023ec45]
// //////////SHELL configuration///////////
namespace Shell {
#define SHELL_FIELD "shell."
const string kPort = SHELL_FIELD"port";
@ -70,7 +72,8 @@ onceToken token1([](){
},nullptr);
} //namespace Shell
////////////RTSP服务器配置///////////
// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981]
// //////////RTSP server configuration///////////
namespace Rtsp {
#define RTSP_FIELD "rtsp."
const string kPort = RTSP_FIELD"port";
@ -82,7 +85,8 @@ onceToken token1([](){
} //namespace Rtsp
////////////RTMP服务器配置///////////
// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f]
// //////////RTMP server configuration///////////
namespace Rtmp {
#define RTMP_FIELD "rtmp."
const string kPort = RTMP_FIELD"port";
@ -93,7 +97,8 @@ onceToken token1([](){
},nullptr);
} //namespace RTMP
////////////Rtp代理相关配置///////////
// //////////Rtp代理相关配置/////////// [AUTO-TRANSLATED:7b285587]
// //////////Rtp proxy related configuration///////////
namespace RtpProxy {
#define RTP_PROXY_FIELD "rtp_proxy."
const string kPort = RTP_PROXY_FIELD"port";
@ -171,7 +176,8 @@ public:
#if defined(ENABLE_VERSION)
(*_parser) << Option('v', "version", Option::ArgNone, nullptr, false, "显示版本号",
[](const std::shared_ptr<ostream> &stream, const string &arg) -> bool {
//版本信息
// 版本信息 [AUTO-TRANSLATED:d4cc59b2]
// Version information
*stream << "编译日期: " << BUILD_TIME << std::endl;
*stream << "代码日期: " << COMMIT_TIME << std::endl;
*stream << "当前git分支: " << BRANCH_NAME << std::endl;
@ -205,7 +211,8 @@ public:
}
};
//全局变量在WebApi中用于保存配置文件用
// 全局变量在WebApi中用于保存配置文件用 [AUTO-TRANSLATED:6d5585ca]
// Global variable, used in WebApi to save configuration files
string g_ini_file;
int start_main(int argc,char *argv[]) {
@ -228,11 +235,13 @@ int start_main(int argc,char *argv[]) {
int threads = cmd_main["threads"];
bool affinity = cmd_main["affinity"];
//设置日志
// 设置日志 [AUTO-TRANSLATED:50372045]
// Set log
Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
#if !defined(ANDROID)
auto fileChannel = std::make_shared<FileChannel>("FileChannel", cmd_main["log-dir"], logLevel);
// 日志最多保存天数
// 日志最多保存天数 [AUTO-TRANSLATED:9bfa8a9a]
// Maximum number of days to save logs
fileChannel->setMaxDay(cmd_main["max_day"]);
fileChannel->setFileMaxCount(cmd_main["log-slice"]);
fileChannel->setFileMaxSize(cmd_main["log-size"]);
@ -243,24 +252,29 @@ int start_main(int argc,char *argv[]) {
pid_t pid = getpid();
bool kill_parent_if_failed = true;
if (bDaemon) {
//启动守护进程
// 启动守护进程 [AUTO-TRANSLATED:33b2c5be]
// Start daemon process
System::startDaemon(kill_parent_if_failed);
}
//开启崩溃捕获等
// 开启崩溃捕获等 [AUTO-TRANSLATED:9c7c759c]
// Enable crash capture, etc.
System::systemSetup();
#endif//!defined(_WIN32)
//启动异步日志线程
// 启动异步日志线程 [AUTO-TRANSLATED:c93cc6f4]
// Start asynchronous log thread
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
InfoL << kServerName;
//加载配置文件,如果配置文件不存在就创建一个
// 加载配置文件,如果配置文件不存在就创建一个 [AUTO-TRANSLATED:761e7479]
// Load configuration file, create one if it doesn't exist
loadIniConfig(g_ini_file.data());
auto &secret = mINI::Instance()[API::kSecret];
if (secret == "035c73f7-bb6b-4889-a715-d9eb2d1925cc" || secret.empty()) {
// 使用默认secret被禁止启动
// 使用默认secret被禁止启动 [AUTO-TRANSLATED:6295164b]
// Starting with the default secret is prohibited
secret = makeRandStr(32, true);
mINI::Instance().dumpFile(g_ini_file);
WarnL << "The " << API::kSecret << " is invalid, modified it to: " << secret
@ -268,13 +282,16 @@ int start_main(int argc,char *argv[]) {
}
if (!File::is_dir(ssl_file)) {
// 不是文件夹,加载证书,证书包含公钥和私钥
// 不是文件夹,加载证书,证书包含公钥和私钥 [AUTO-TRANSLATED:5d3a5e49]
// Not a folder, load certificate, certificate contains public key and private key
SSL_Initor::Instance().loadCertificate(ssl_file.data());
} else {
//加载文件夹下的所有证书
// 加载文件夹下的所有证书 [AUTO-TRANSLATED:0e1f9b20]
// Load all certificates under the folder
File::scanDir(ssl_file,[](const string &path, bool isDir){
if (!isDir) {
// 最后的一个证书会当做默认证书(客户端ssl握手时未指定主机)
// 最后的一个证书会当做默认证书(客户端ssl握手时未指定主机) [AUTO-TRANSLATED:b242685c]
// The last certificate will be used as the default certificate (client ssl handshake does not specify the host)
SSL_Initor::Instance().loadCertificate(path.data());
}
return true;
@ -291,37 +308,46 @@ int start_main(int argc,char *argv[]) {
uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
uint16_t rtpPort = mINI::Instance()[RtpProxy::kPort];
//设置poller线程数和cpu亲和性,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
//如果需要调用getSnap和addFFmpegSource接口可以关闭cpu亲和性
// 设置poller线程数和cpu亲和性,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效 [AUTO-TRANSLATED:7f03a1e5]
// Set the number of poller threads and CPU affinity. This function must be called before using ZLToolKit network related objects to take effect.
// 如果需要调用getSnap和addFFmpegSource接口可以关闭cpu亲和性 [AUTO-TRANSLATED:7629f7bc]
// If you need to call the getSnap and addFFmpegSource interfaces, you can turn off CPU affinity
EventPollerPool::setPoolSize(threads);
WorkThreadPool::setPoolSize(threads);
EventPollerPool::enableCpuAffinity(affinity);
//简单的telnet服务器可用于服务器调试但是不能使用23端口否则telnet上了莫名其妙的现象
//测试方法:telnet 127.0.0.1 9000
// 简单的telnet服务器可用于服务器调试但是不能使用23端口否则telnet上了莫名其妙的现象 [AUTO-TRANSLATED:f9324c6e]
// Simple telnet server, can be used for server debugging, but cannot use port 23, otherwise telnet will have inexplicable phenomena
// 测试方法:telnet 127.0.0.1 9000 [AUTO-TRANSLATED:de0ac883]
// Test method: telnet 127.0.0.1 9000
auto shellSrv = std::make_shared<TcpServer>();
//rtsp[s]服务器, 可用于诸如亚马逊echo show这样的设备访问
// rtsp[s]服务器, 可用于诸如亚马逊echo show这样的设备访问 [AUTO-TRANSLATED:f28e54f7]
// rtsp[s] server, can be used for devices such as Amazon Echo Show to access
auto rtspSrv = std::make_shared<TcpServer>();
auto rtspSSLSrv = std::make_shared<TcpServer>();
//rtmp[s]服务器
// rtmp[s]服务器 [AUTO-TRANSLATED:3ac98bf5]
// rtmp[s] server
auto rtmpSrv = std::make_shared<TcpServer>();
auto rtmpsSrv = std::make_shared<TcpServer>();
//http[s]服务器
// http[s]服务器 [AUTO-TRANSLATED:5bbc8735]
// http[s] server
auto httpSrv = std::make_shared<TcpServer>();
auto httpsSrv = std::make_shared<TcpServer>();
#if defined(ENABLE_RTPPROXY)
//GB28181 rtp推流端口支持UDP/TCP
// GB28181 rtp推流端口支持UDP/TCP [AUTO-TRANSLATED:8a9b2872]
// GB28181 rtp push stream port, supports UDP/TCP
auto rtpServer = std::make_shared<RtpServer>();
#endif//defined(ENABLE_RTPPROXY)
#if defined(ENABLE_WEBRTC)
auto rtcSrv_tcp = std::make_shared<TcpServer>();
//webrtc udp服务器
// webrtc udp服务器 [AUTO-TRANSLATED:157a64e5]
// webrtc udp server
auto rtcSrv_udp = std::make_shared<UdpServer>();
rtcSrv_udp->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
if (!buf) {
@ -329,7 +355,8 @@ int start_main(int argc,char *argv[]) {
}
auto new_poller = WebRtcSession::queryPoller(buf);
if (!new_poller) {
//该数据对应的webrtc对象未找到丢弃之
// 该数据对应的webrtc对象未找到丢弃之 [AUTO-TRANSLATED:d401f8cb]
// The webrtc object corresponding to this data is not found, discard it
return Socket::Ptr();
}
return Socket::createSocket(new_poller, false);
@ -347,7 +374,8 @@ int start_main(int argc,char *argv[]) {
}
auto new_poller = SRT::SrtSession::queryPoller(buf);
if (!new_poller) {
//握手第一阶段
// 握手第一阶段 [AUTO-TRANSLATED:6b3abcd4]
// Handshake phase one
return Socket::createSocket(poller, false);
}
return Socket::createSocket(new_poller, false);
@ -362,31 +390,40 @@ int start_main(int argc,char *argv[]) {
InfoL << "已启动http hook 接口";
try {
//rtsp服务器端口默认554
// rtsp服务器端口默认554 [AUTO-TRANSLATED:07937d81]
// rtsp server, default port 554
if (rtspPort) { rtspSrv->start<RtspSession>(rtspPort, listen_ip); }
//rtsps服务器端口默认322
// rtsps服务器端口默认322 [AUTO-TRANSLATED:e8a9fd71]
// rtsps server, default port 322
if (rtspsPort) { rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort, listen_ip); }
//rtmp服务器端口默认1935
// rtmp服务器端口默认1935 [AUTO-TRANSLATED:58324c74]
// rtmp server, default port 1935
if (rtmpPort) { rtmpSrv->start<RtmpSession>(rtmpPort, listen_ip); }
//rtmps服务器端口默认19350
// rtmps服务器端口默认19350 [AUTO-TRANSLATED:c565ff4e]
// rtmps server, default port 19350
if (rtmpsPort) { rtmpsSrv->start<RtmpSessionWithSSL>(rtmpsPort, listen_ip); }
//http服务器端口默认80
// http服务器端口默认80 [AUTO-TRANSLATED:8899e852]
// http server, default port 80
if (httpPort) { httpSrv->start<HttpSession>(httpPort, listen_ip); }
//https服务器端口默认443
// https服务器端口默认443 [AUTO-TRANSLATED:24999616]
// https server, default port 443
if (httpsPort) { httpsSrv->start<HttpsSession>(httpsPort, listen_ip); }
//telnet远程调试服务器
// telnet远程调试服务器 [AUTO-TRANSLATED:577cb7cf]
// telnet remote debug server
if (shellPort) { shellSrv->start<ShellSession>(shellPort, listen_ip); }
#if defined(ENABLE_RTPPROXY)
//创建rtp服务器
// 创建rtp服务器 [AUTO-TRANSLATED:873f7f52]
// create rtp server
if (rtpPort) { rtpServer->start(rtpPort, listen_ip.c_str()); }
#endif//defined(ENABLE_RTPPROXY)
#if defined(ENABLE_WEBRTC)
//webrtc udp服务器
// webrtc udp服务器 [AUTO-TRANSLATED:157a64e5]
// webrtc udp server
if (rtcPort) { rtcSrv_udp->start<WebRtcSession>(rtcPort, listen_ip);}
if (rtcTcpPort) { rtcSrv_tcp->start<WebRtcSession>(rtcTcpPort, listen_ip);}
@ -394,7 +431,8 @@ int start_main(int argc,char *argv[]) {
#endif//defined(ENABLE_WEBRTC)
#if defined(ENABLE_SRT)
// srt udp服务器
// srt udp服务器 [AUTO-TRANSLATED:06911727]
// srt udp server
if (srtPort) { srtSrv->start<SRT::SrtSession>(srtPort, listen_ip); }
#endif//defined(ENABLE_SRT)
@ -403,14 +441,16 @@ int start_main(int argc,char *argv[]) {
sleep(1);
#if !defined(_WIN32)
if (pid != getpid() && kill_parent_if_failed) {
//杀掉守护进程
// 杀掉守护进程 [AUTO-TRANSLATED:bee035e9]
// kill the daemon process
kill(pid, SIGINT);
}
#endif
return -1;
}
//设置退出信号处理函数
// 设置退出信号处理函数 [AUTO-TRANSLATED:4f047770]
// set exit signal handler
static semaphore sem;
signal(SIGINT, [](int) {
InfoL << "SIGINT:exit";
@ -433,7 +473,8 @@ int start_main(int argc,char *argv[]) {
unInstallWebHook();
onProcessExited();
//休眠1秒再退出防止资源释放顺序错误
// 休眠1秒再退出防止资源释放顺序错误 [AUTO-TRANSLATED:1b11a74f]
// sleep for 1 second before exiting, to prevent resource release order errors
InfoL << "程序退出中,请等待...";
sleep(1);
InfoL << "程序退出完毕!";

View File

@ -19,7 +19,8 @@ namespace mediakit {
H264Encoder::H264Encoder() {}
H264Encoder::~H264Encoder() {
//* 清除图像区域
// * 清除图像区域 [AUTO-TRANSLATED:6b316309]
// * Clear image area
if (_pPicIn) {
delete _pPicIn;
_pPicIn = nullptr;
@ -29,7 +30,8 @@ H264Encoder::~H264Encoder() {
_pPicOut = nullptr;
}
//* 关闭编码器句柄
// * 关闭编码器句柄 [AUTO-TRANSLATED:bf4a14e5]
// * Close encoder handle
if (_pX264Handle) {
x264_encoder_close(_pX264Handle);
_pX264Handle = nullptr;
@ -206,6 +208,178 @@ Value的值就是fps。
* i.e. when an x264_param_t is passed to x264_t in an x264_picture_t or in zones.
* Not used when x264_encoder_reconfig is called directly.
void (*param_free)( void* );
/*typedef struct x264_param_t
{
CPU flag bit
unsigned int cpu;
int i_threads; Parallel encoding of multiple frames
int b_deterministic; Whether to allow non-deterministic thread optimization
int i_sync_lookahead; Thread pre-buffering
Video attributes
int i_width; Width
int i_height; Height
int i_csp; CSP of the encoded bitstream, only supports i420, color space setting
int i_level_idc; Setting of the level value
int i_frame_total; Total number of encoded frames, default 0
Vui parameter set video availability information video standardization options
struct
{
they will be reduced to be 0 < x <= 65535 and prime
int i_sar_height;
int i_sar_width; Set aspect ratio
int i_overscan; 0=undef, 1=no overscan, 2=overscan Overscan lines, default "undef" (not set), options: show (watch) / crop (remove)
See the following value h264 appendix E
Int i_vidformat; Video format, default "undef", component/pal/ntsc/secam/mac/undef
int b_fullrange; Specify full range samples setting, default "off", options: off/on
int i_colorprim; Original chroma format, default "undef", options: undef/bt709/bt470m/bt470bg, smpte170m/smpte240m/film
int i_transfer; Conversion method, default "undef", options: undef/bt709/bt470m/bt470bg/linear,log100/log316/smpte170m/smpte240m
int i_colmatrix; Chroma matrix setting, default "undef", undef/bt709/fcc/bt470bg, smpte170m/smpte240m/GBR/YCgCo
int i_chroma_loc; both top & bottom chroma samples specified, range 0~5, default 0
} vui;
int i_fps_num;
int i_fps_den;
These two parameters are determined by the fps frame rate, the assignment process is as follows:
{ float fps;
if( sscanf( value, "%d/%d", &p->i_fps_num, &p->i_fps_den ) == 2 )
;
else if( sscanf( value, "%f", &fps ) )
{
p->i_fps_num = (int)(fps * 1000 + .5);
p->i_fps_den = 1000;
}
else
b_error = 1;
}
The value of Value is fps.
Stream parameters
int i_frame_reference; Maximum number of reference frames
int i_keyint_max; Set IDR keyframe at this interval
int i_keyint_min; Scene switching less than this value encodes as I, not IDR.
int i_scenecut_threshold; How actively to insert additional I frames
int i_bframe; Number of P frames between two related images
int i_bframe_adaptive; Adaptive B frame determination
int i_bframe_bias; Control the insertion of B frame determination, range -100~+100, the higher the easier it is to insert B frame, default 0
int b_bframe_pyramid; Allow some B to be reference frames
Parameters required for deblocking filter
int b_deblocking_filter;
int i_deblocking_filter_alphac0; [-6, 6] -6 light filter, 6 strong
int i_deblocking_filter_beta; [-6, 6] idem
Entropy coding
int b_cabac;
int i_cabac_init_idc;
int b_interlaced; Interlaced scanning
Quantization
int i_cqm_preset; Custom quantization matrix (CQM), initialize quantization mode to flat
char *psz_cqm_file; JM format reads external quantization matrix file in JM format, automatically ignores other cqm options
uint8_t cqm_4iy[16]; used only if i_cqm_preset == X264_CQM_CUSTOM
uint8_t cqm_4ic[16];
uint8_t cqm_4py[16];
uint8_t cqm_4pc[16];
uint8_t cqm_8iy[64];
uint8_t cqm_8py[64];
Log
void (*pf_log)( void *, int i_level, const char *psz, va_list );
void *p_log_private;
int i_log_level;
int b_visualize;
char *psz_dump_yuv; Name of the reconstructed frame
Encoding analysis parameters
struct
{
unsigned int intra; Inter-frame partition
unsigned int inter; Intra-frame partition
int b_transform_8x8; Inter-frame partition
int b_weighted_bipred; Implicit weighting for b frames
int i_direct_mv_pred; Time-space motion prediction
int i_chroma_qp_offset; Chroma quantization step offset
int i_me_method; Motion estimation algorithm (X264_ME_*)
int i_me_range; Integer pixel motion estimation search range (from predicted mv)
int i_mv_range; Maximum length of motion vector (in pixels). -1 = auto, based on level
int i_mv_range_thread; Minimum space between threads. -1 = auto, based on number of threads.
int i_subpel_refine; Sub-pixel motion estimation quality
int b_chroma_me; Sub-pixel chroma motion estimation and mode selection for P frames
int b_mixed_references; Allow each macroblock partition in the P frame to have its own reference number
int i_trellis; Trellis quantization, find the appropriate quantization value for each 8x8 block, requires CABAC, default 0 0: off 1: use only at the end of encoding 2: always use
int b_fast_pskip; Fast P frame skip detection
int b_dct_decimate; Transform parameter domain in P-frames
int i_noise_reduction; Adaptive pseudo-blind area
float f_psy_rd; Psy RD strength
float f_psy_trellis; Psy trellis strength
int b_psy; Toggle all psy optimizations
, the size of the invalid area used in luminance quantization
int i_luma_deadzone[2]; {Inter-frame, Intra-frame}
int b_psnr; Calculate and print PSNR information
int b_ssim; Calculate and print SSIM information
} analyse;
Bitrate control parameters
struct
{
int i_rc_method; X264_RC_*
int i_qp_constant; 0-51
int i_qp_min; Minimum quantization value allowed
int i_qp_max; Maximum quantization value allowed
int i_qp_step; Maximum quantization step between frames
int i_bitrate; Set average bitrate
float f_rf_constant; 1pass VBR, nominal QP
float f_rate_tolerance;
int i_vbv_max_bitrate; In average bitrate mode, the maximum instantaneous bitrate, default 0 (same as -B setting)
int i_vbv_buffer_size; Size of the bitrate control buffer, unit kbit, default 0
float f_vbv_buffer_init; <=1: fraction of buffer_size. >1: kbit bitrate control buffer data retention maximum data amount ratio to buffer size, range 0~1.0, default 0.9
float f_ip_factor;
float f_pb_factor;
int i_aq_mode; psy adaptive QP. (X264_AQ_*)
float f_aq_strength;
int b_mb_tree; Macroblock-tree ratecontrol.
int i_lookahead;
2pass multiple compression bitrate control
int b_stat_write; Enable stat writing in psz_stat_out
char *psz_stat_out;
int b_stat_read; Read stat from psz_stat_in and use it
char *psz_stat_in;
2pass params (same as ffmpeg ones)
float f_qcompress; 0.0 => cbr, 1.0 => constant qp
float f_qblur; Quantization blur over time
float f_complexity_blur; Complexity blur over time
x264_zone_t *zones; Bitrate control coverage
int i_zones; number of zone_t's
char *psz_zones; Another way to specify the zone
} rc;
Muxing parameters
int b_aud; Generate access unit delimiter
int b_repeat_headers; Place SPS/PPS before each keyframe
int i_sps_id; SPS and PPS id number
Slice (like strip) parameters
int i_slice_max_size; Maximum number of bytes per slice, including expected NAL overhead.
int i_slice_max_mbs; Maximum number of macroblocks per slice, overwrite i_slice_count
int i_slice_count; Number of strips per frame: Set rectangular strips.
Optional callback for freeing this x264_param_t when it is done being used.
* Only used when the x264_param_t sits in memory for an indefinite period of time,
* i.e. when an x264_param_t is passed to x264_t in an x264_picture_t or in zones.
* Not used when x264_encoder_reconfig is called directly.
void (*param_free)( void* );
} x264_param_t;
* [AUTO-TRANSLATED:b730fe72]
} x264_param_t;*/
bool H264Encoder::init(int iWidth, int iHeight, int iFps, int iBitRate) {
@ -213,8 +387,10 @@ bool H264Encoder::init(int iWidth, int iHeight, int iFps, int iBitRate) {
return true;
}
x264_param_t X264Param, *pX264Param = &X264Param;
//* 配置参数
//* 使用默认参数
// * 配置参数 [AUTO-TRANSLATED:1629898c]
// * Configure parameters
// * 使用默认参数 [AUTO-TRANSLATED:db7658e2]
// * Use default parameters
x264_param_default_preset(pX264Param, "ultrafast", "zerolatency");
//* cpuFlags
@ -233,13 +409,18 @@ bool H264Encoder::init(int iWidth, int iHeight, int iFps, int iBitRate) {
pX264Param->rc.f_qcompress = 0.6;//ffmpeg:qcompress 量化器压缩比率0-1.越小则比特率越区域固定,但是越高越使量化器参数越固定
pX264Param->analyse.i_me_range = 16; //ffmpeg:me_range 运动侦测的半径
pX264Param->i_frame_reference = 3; //ffmpeg:refsB和P帧向前预测参考的帧数。取值范围1-16。
//该值不影响解码的速度,但是越大解码
//所需的内存越大。这个值在一般情况下
//越大效果越好但是超过6以后效果就
//不明显了。
// 该值不影响解码的速度,但是越大解码 [AUTO-TRANSLATED:23eb12ae]
// This value does not affect the decoding speed, but the larger the decoding
// 所需的内存越大。这个值在一般情况下 [AUTO-TRANSLATED:3ff4f036]
// The more memory required. This value is generally
// 越大效果越好但是超过6以后效果就 [AUTO-TRANSLATED:3252f33a]
// The better the effect, but the effect is not obvious after 6
// 不明显了。 [AUTO-TRANSLATED:d654ba67]
// It's not obvious.
pX264Param->analyse.i_trellis = 1; //ffmpeg:trellis
//pX264Param->analyse.i_me_method=X264_ME_DIA;//ffmpeg:me_method ME_ZERO 运动侦测的方式
// pX264Param->analyse.i_me_method=X264_ME_DIA;//ffmpeg:me_method ME_ZERO 运动侦测的方式 [AUTO-TRANSLATED:24c6240d]
// pX264Param->analyse.i_me_method=X264_ME_DIA;//ffmpeg:me_method ME_ZERO Motion detection method
pX264Param->rc.f_qblur = 0.5; //ffmpeg:qblur
//* bitstream parameters
@ -262,6 +443,27 @@ bool H264Encoder::init(int iWidth, int iHeight, int iFps, int iBitRate) {
GOP的最后一帧5B帧
open-GOP,P帧就是close-GOP
B帧压缩性能好于P帧open-GOP在编码性能上稍微优于close-GOP
/*open-GOP
open-GOP only appears when the bitstream contains B frames.
A frame in a GOP needs to rely on some frames in the previous GOP when decoding,
This GOP is called open-GOP.
Some decoders do not fully support open-GOP bitstreams,
For example, Blu-ray decoders, so open-GOP is disabled by default in x264.
For the decoding end, if the received bitstream is as follows: I0 B0 B1 P0 B2 B3... This is an open-GOP bitstream (I frame followed by B frame).
Therefore, the decoding of B0 B1 needs to use the data of the GOP before I0, and the dts of B0 B1 is less than that of I0.
If the bitstream is as follows: I0 P0 B0 B1 P1 B2 B3... This is a close-GOP bitstream,
The decoding of all frames after I0 does not depend on the frames before I0, and the dts of all frames after I0 is greater than that of I0.
If the bitstream is IDR0 B0 B1 P0 B2 B3... then this GOP is close-GOP, although the dst of B0, B1 is smaller than that of IDR0,
But both the encoder and decoder refresh the reference buffer, B0, B1 cannot refer to the forward GOP frame.
For the encoding end, if the encoding frame type is determined as follows: ...P0 B1 B2 P3 B4 B5 I6 This will output an open-Gop bitstream (P0 P3 B1 B2 I6 B4 B5...),
The decoding of B4 B5 depends on P3.
If the encoding frame type is determined as follows...P0 B1 B2 P3 B4 P5 I6, then this will not output an open-GOP bitstream (P0 P3 B1 B2 P5 B4 I6...).
The difference between the two is whether the 5th frame before I6 is set to B frame or P frame,
If the last frame of a GOP (the 5th frame in the example above) is set to B frame,
This bitstream is open-GOP, and setting it to P frame is close-GOP.
Since B frames have better compression performance than P frames, open-GOP performs slightly better than close-GOP in terms of encoding performance,
But for compatibility and less trouble, it's better to turn off opne-GOP.
* [AUTO-TRANSLATED:6ccfc922]
opne-GOP关闭的好*/
pX264Param->b_open_gop = 0;
pX264Param->i_bframe = 0; //最大B帧数.
@ -282,11 +484,14 @@ bool H264Encoder::init(int iWidth, int iHeight, int iFps, int iBitRate) {
pX264Param->b_annexb = 1; //1前面为0x00000001,0为nal长度
pX264Param->b_repeat_headers = 1; //关键帧前面是否放sps跟pps帧0 否 1
//* 设置Profile.使用baseline
// * 设置Profile.使用baseline [AUTO-TRANSLATED:c451b8a5]
// * Set Profile. Use baseline
x264_param_apply_profile(pX264Param, "high");
//* 打开编码器句柄,通过x264_encoder_parameters得到设置给X264
//* 的参数.通过x264_encoder_reconfig更新X264的参数
// * 打开编码器句柄,通过x264_encoder_parameters得到设置给X264 [AUTO-TRANSLATED:e52faa11]
// * Open encoder handle, get the settings for X264 through x264_encoder_parameters
// * 的参数.通过x264_encoder_reconfig更新X264的参数 [AUTO-TRANSLATED:83b929df]
// * Parameters. Update the parameters of X264 through x264_encoder_reconfig
_pX264Handle = x264_encoder_open(pX264Param);
if (!_pX264Handle) {
return false;

View File

@ -97,7 +97,8 @@ static bool checkIfSupportedNvidia_l() {
bool find_driver = false;
File::scanDir("/dev", [&](const string &path, bool is_dir) {
if (!is_dir && start_with(path, "/dev/nvidia")) {
//找到nvidia的驱动
// 找到nvidia的驱动 [AUTO-TRANSLATED:5b87bf81]
// Find the Nvidia driver
find_driver = true;
return false;
}
@ -412,7 +413,8 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std:
throw std::runtime_error("创建解码器失败");
}
//保存AVFrame的引用
// 保存AVFrame的引用 [AUTO-TRANSLATED:2df53d07]
// Save the AVFrame reference
#ifdef FF_API_OLD_ENCDEC
_context->refcounted_frames = 1;
#endif
@ -452,7 +454,8 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std:
_context->flags |= AV_CODEC_FLAG_TRUNCATED;
_do_merger = false;
} else {
// 此时业务层应该需要合帧
// 此时业务层应该需要合帧 [AUTO-TRANSLATED:8dea0fff]
// The business layer should need to merge frames at this time
_do_merger = true;
}
#endif
@ -460,13 +463,15 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std:
int ret = avcodec_open2(_context.get(), codec, &dict);
av_dict_free(&dict);
if (ret >= 0) {
//成功
// 成功 [AUTO-TRANSLATED:7d878ca9]
// Success
InfoL << "打开解码器成功:" << codec->name;
break;
}
if (codec_default && codec_default != codec) {
//硬件编解码器打开失败,尝试软件的
// 硬件编解码器打开失败,尝试软件的 [AUTO-TRANSLATED:060200f4]
// Hardware codec failed to open, try software codec
WarnL << "打开解码器" << codec->name << "失败,原因是:" << ffmpeg_err(ret) << ", 再尝试打开解码器" << codec_default->name;
codec = codec_default;
continue;
@ -514,7 +519,8 @@ bool FFmpegDecoder::inputFrame_l(const Frame::Ptr &frame, bool live, bool enable
bool FFmpegDecoder::inputFrame(const Frame::Ptr &frame, bool live, bool async, bool enable_merge) {
if (async && !TaskManager::isEnabled() && getContext()->codec_type == AVMEDIA_TYPE_VIDEO) {
//开启异步编码,且为视频,尝试启动异步解码线程
// 开启异步编码,且为视频,尝试启动异步解码线程 [AUTO-TRANSLATED:17a68fc6]
// Enable asynchronous encoding, and it is video, try to start asynchronous decoding thread
startThread("decoder thread");
}
@ -525,7 +531,8 @@ bool FFmpegDecoder::inputFrame(const Frame::Ptr &frame, bool live, bool async, b
auto frame_cache = Frame::getCacheAbleFrame(frame);
return addDecodeTask(frame->keyFrame(), [this, live, frame_cache, enable_merge]() {
inputFrame_l(frame_cache, live, enable_merge);
//此处模拟解码太慢导致的主动丢帧
// 此处模拟解码太慢导致的主动丢帧 [AUTO-TRANSLATED:fc8bea8a]
// Here simulates decoding too slow, resulting in active frame dropping
//usleep(100 * 1000);
});
}
@ -561,7 +568,8 @@ bool FFmpegDecoder::decodeFrame(const char *data, size_t size, uint64_t dts, uin
break;
}
if (live && pts - out_frame->get()->pts > MAX_DELAY_SECOND * 1000 && _ticker.createdTime() > 10 * 1000) {
//后面的帧才忽略,防止Track无法ready
// 后面的帧才忽略,防止Track无法ready [AUTO-TRANSLATED:23f1a7c9]
// The following frames are ignored to prevent the Track from being ready
WarnL << "解码时,忽略" << MAX_DELAY_SECOND << "秒前的数据:" << pts << " " << out_frame->get()->pts;
continue;
}
@ -676,7 +684,8 @@ FFmpegFrame::Ptr FFmpegSwr::inputFrame(const FFmpegFrame::Ptr &frame) {
frame->get()->channels == _target_channels &&
frame->get()->channel_layout == (uint64_t)_target_channel_layout &&
frame->get()->sample_rate == _target_samplerate) {
//不转格式
// 不转格式 [AUTO-TRANSLATED:31dc6ae1]
// Do not convert format
return frame;
}
if (!_ctx) {
@ -738,11 +747,13 @@ FFmpegFrame::Ptr FFmpegSws::inputFrame(const FFmpegFrame::Ptr &frame, int &ret,
auto target_width = _target_width ? _target_width : frame->get()->width;
auto target_height = _target_height ? _target_height : frame->get()->height;
if (frame->get()->format == _target_format && frame->get()->width == target_width && frame->get()->height == target_height) {
//不转格式
// 不转格式 [AUTO-TRANSLATED:31dc6ae1]
// Do not convert format
return frame;
}
if (_ctx && (_src_width != frame->get()->width || _src_height != frame->get()->height || _src_format != (enum AVPixelFormat) frame->get()->format)) {
//输入分辨率发生变化了
// 输入分辨率发生变化了 [AUTO-TRANSLATED:0e4ea2e8]
// Input resolution has changed
sws_freeContext(_ctx);
_ctx = nullptr;
}

View File

@ -103,16 +103,19 @@ bool DevChannel::inputAAC(const char *data_without_adts, int len, uint64_t dts,
}
if (!adts_header) {
//没有adts头
// 没有adts头 [AUTO-TRANSLATED:b9faaa83]
// No adts header
return inputFrame(std::make_shared<FrameFromPtr>(CodecAAC, (char *) data_without_adts, len, dts, 0, 0));
}
if (adts_header + ADTS_HEADER_LEN == data_without_adts) {
//adts头和帧在一起
// adts头和帧在一起 [AUTO-TRANSLATED:76c4e678]
// adts header and frame together
return inputFrame(std::make_shared<FrameFromPtr>(CodecAAC, (char *) data_without_adts - ADTS_HEADER_LEN, len + ADTS_HEADER_LEN, dts, 0, ADTS_HEADER_LEN));
}
//adts头和帧不在一起
// adts头和帧不在一起 [AUTO-TRANSLATED:591dd07a]
// adts header and frame not together
char *data_with_adts = new char[len + ADTS_HEADER_LEN];
memcpy(data_with_adts, adts_header, ADTS_HEADER_LEN);
memcpy(data_with_adts + ADTS_HEADER_LEN, data_without_adts, len);

View File

@ -41,12 +41,16 @@ public:
/**
* MultiMediaSourceMuxer类的包装便使
* Wrapper class for MultiMediaSourceMuxer, making it easier for beginners to use.
* [AUTO-TRANSLATED:101887bd]
*/
class DevChannel : public MultiMediaSourceMuxer{
public:
using Ptr = std::shared_ptr<DevChannel>;
//fDuration<=0为直播否则为点播
// fDuration<=0为直播否则为点播 [AUTO-TRANSLATED:e3b6029a]
// fDuration<=0 for live streaming, otherwise for on-demand
DevChannel(const MediaTuple& tuple, float duration = 0, const ProtocolOption &option = ProtocolOption())
: MultiMediaSourceMuxer(tuple, duration, option) {}
@ -54,6 +58,11 @@ public:
* Track
* MultiMediaSourceMuxer::addTrack(VideoTrack::Ptr );
* @param info
* Initialize the video Track
* Equivalent to MultiMediaSourceMuxer::addTrack(VideoTrack::Ptr );
* @param info Video related information
* [AUTO-TRANSLATED:6845d52d]
*/
bool initVideo(const VideoInfo &info);
@ -61,6 +70,11 @@ public:
* Track
* MultiMediaSourceMuxer::addTrack(AudioTrack::Ptr );
* @param info
* Initialize the audio Track
* Equivalent to MultiMediaSourceMuxer::addTrack(AudioTrack::Ptr );
* @param info Audio related information
* [AUTO-TRANSLATED:5be9d272]
*/
bool initAudio(const AudioInfo &info);
@ -70,6 +84,13 @@ public:
* @param len
* @param dts 0
* @param pts 0dts
* Input 264 frame
* @param data 264 single frame data pointer
* @param len Data pointer length
* @param dts Decode timestamp, in milliseconds; If it is 0, the timestamp will be generated automatically internally
* @param pts Play timestamp, in milliseconds; If it is 0, it will be assigned to dts internally
* [AUTO-TRANSLATED:bda112e9]
*/
bool inputH264(const char *data, int len, uint64_t dts, uint64_t pts = 0);
@ -79,6 +100,13 @@ public:
* @param len
* @param dts 0
* @param pts 0dts
* Input 265 frame
* @param data 265 single frame data pointer
* @param len Data pointer length
* @param dts Decode timestamp, in milliseconds; If it is 0, the timestamp will be generated automatically internally
* @param pts Play timestamp, in milliseconds; If it is 0, it will be assigned to dts internally
* [AUTO-TRANSLATED:1fc1c892]
*/
bool inputH265(const char *data, int len, uint64_t dts, uint64_t pts = 0);
@ -88,6 +116,13 @@ public:
* @param len
* @param dts
* @param adts_header adts头
* Input aac frame
* @param data_without_adts aac frame without adts header
* @param len Frame data length
* @param dts Timestamp, in milliseconds
* @param adts_header adts header
* [AUTO-TRANSLATED:6eca0279]
*/
bool inputAAC(const char *data_without_adts, int len, uint64_t dts, const char *adts_header);
@ -96,6 +131,12 @@ public:
* @param data
* @param len
* @param dts
* Input OPUS/G711 audio frame
* @param data Audio frame
* @param len Frame data length
* @param dts Timestamp, in milliseconds
* [AUTO-TRANSLATED:5f13cdf6]
*/
bool inputAudio(const char *data, int len, uint64_t dts);
@ -104,6 +145,12 @@ public:
* @param yuv yuv420p数据指针
* @param linesize yuv420p数据linesize
* @param cts
* Input yuv420p video frame, encoding will be completed internally and inputH264 method will be called
* @param yuv yuv420p data pointer
* @param linesize yuv420p data linesize
* @param cts Capture timestamp, in milliseconds
* [AUTO-TRANSLATED:1b945575]
*/
bool inputYUV(char *yuv[3], int linesize[3], uint64_t cts);
@ -112,10 +159,17 @@ public:
* @param data pcm数据指针int16整形
* @param len pcm数据长度
* @param cts
* Input pcm data, encoding will be completed internally and inputAAC method will be called
* @param data pcm data pointer, int16 integer
* @param len pcm data length
* @param cts Capture timestamp, in milliseconds
* [AUTO-TRANSLATED:b99a9e82]
*/
bool inputPCM(char *data, int len, uint64_t cts);
//// 重载基类方法,确保线程安全 ////
// // 重载基类方法,确保线程安全 //// [AUTO-TRANSLATED:86e2df12]
// // Override base class methods to ensure thread safety ////
bool inputFrame(const Frame::Ptr &frame) override;
bool addTrack(const Track::Ptr & track) override;
void addTrackCompleted() override;

View File

@ -24,9 +24,11 @@ bool MediaSink::addTrack(const Track::Ptr &track_in) {
return false;
}
if (!_enable_audio) {
// 关闭音频时,加快单视频流注册速度
// 关闭音频时,加快单视频流注册速度 [AUTO-TRANSLATED:4d5a361d]
// Speed up single video stream registration when audio is off
if (track_in->getTrackType() == TrackAudio) {
// 音频被全局忽略
// 音频被全局忽略 [AUTO-TRANSLATED:a8134a0b]
// Audio is globally ignored
InfoL << "Audio disabled, audio track ignored";
return false;
}
@ -35,7 +37,8 @@ bool MediaSink::addTrack(const Track::Ptr &track_in) {
WarnL << "All track is ready, add track too late: " << track_in->getCodecName();
return false;
}
// 克隆Track只拷贝其数据不拷贝其数据转发关系
// 克隆Track只拷贝其数据不拷贝其数据转发关系 [AUTO-TRANSLATED:09edaa31]
// Clone Track, only copy its data, not its data forwarding relationship
auto track = track_in->clone();
CHECK(track, "Clone track failed: ", track_in->getCodecName());
auto index = track->getIndex();
@ -55,11 +58,13 @@ bool MediaSink::addTrack(const Track::Ptr &track_in) {
GET_CONFIG(uint32_t, kMaxUnreadyFrame, General::kUnreadyFrameCache);
if (frame_unread.size() > kMaxUnreadyFrame) {
// 未就绪的的track不能缓存太多的帧否则可能内存溢出
// 未就绪的的track不能缓存太多的帧否则可能内存溢出 [AUTO-TRANSLATED:23958376]
// Unready tracks cannot cache too many frames, otherwise memory may overflow
frame_unread.clear();
WarnL << "Cached frame of unready track(" << frame->getCodecName() << ") is too much, now cleared";
}
// 还有Track未就绪先缓存之
// 还有Track未就绪先缓存之 [AUTO-TRANSLATED:f96eadfa]
// There are still unready tracks, cache them first
frame_unread.emplace_back(Frame::getCacheAbleFrame(frame));
return true;
});
@ -86,7 +91,8 @@ bool MediaSink::inputFrame(const Frame::Ptr &frame) {
it->second.second = true;
auto ret = it->second.first->inputFrame(frame);
if (_mute_audio_maker && frame->getTrackType() == TrackVideo) {
// 视频驱动产生静音音频
// 视频驱动产生静音音频 [AUTO-TRANSLATED:2a8c789c]
// Video driver generates silent audio
_mute_audio_maker->inputFrame(frame);
}
checkTrackIfReady();
@ -97,7 +103,8 @@ void MediaSink::checkTrackIfReady() {
if (!_all_track_ready && !_track_ready_callback.empty()) {
for (auto &pr : _track_map) {
if (pr.second.second && pr.second.first->ready()) {
// Track由未就绪状态转换成就绪状态我们就触发onTrackReady回调
// Track由未就绪状态转换成就绪状态我们就触发onTrackReady回调 [AUTO-TRANSLATED:f8975e53]
// When a Track transitions from an unready state to a ready state, we trigger the onTrackReady callback
auto it = _track_ready_callback.find(pr.first);
if (it != _track_ready_callback.end()) {
it->second();
@ -107,34 +114,57 @@ void MediaSink::checkTrackIfReady() {
}
}
// 等待音频超时时间
GET_CONFIG(uint32_t, kWaitAudioTrackDataMS, General::kWaitAudioTrackDataMS);
if (_max_track_size > 1) {
for (auto it = _track_map.begin(); it != _track_map.end();) {
if (it->second.first->getTrackType() == TrackAudio && _ticker.elapsedTime() > kWaitAudioTrackDataMS && !it->second.second) {
// 音频超时且完全没收到音频数据,忽略音频
auto index = it->second.first->getIndex();
WarnL << "Audio track index " << index << " codec " << it->second.first->getCodecName() << " receive no data for long "
<< _ticker.elapsedTime() << "ms. Ignore it!";
it = _track_map.erase(it);
_max_track_size -= 1;
_track_ready_callback.erase(index);
} else {
++it;
}
}
}
if (!_all_track_ready) {
GET_CONFIG(uint32_t, kMaxWaitReadyMS, General::kWaitTrackReadyMS);
if (_ticker.elapsedTime() > kMaxWaitReadyMS) {
// 如果超过规定时间那么不再等待并忽略未准备好的Track
// 如果超过规定时间那么不再等待并忽略未准备好的Track [AUTO-TRANSLATED:fd089806]
// If it exceeds the specified time, then stop waiting and ignore unprepared Tracks
emitAllTrackReady();
return;
}
if (!_track_ready_callback.empty()) {
// 在超时时间内如果存在未准备好的Track那么继续等待
// 在超时时间内如果存在未准备好的Track那么继续等待 [AUTO-TRANSLATED:cfaf3b49]
// Within the timeout period, if there are unprepared Tracks, then continue waiting
return;
}
if (_only_audio && _audio_add) {
// 只开启音频
// 只开启音频 [AUTO-TRANSLATED:bac07e47]
// Only enable audio
emitAllTrackReady();
return;
}
if (_track_map.size() == _max_track_size) {
// 如果已经添加了音视频Track并且不存在未准备好的Track那么说明所有Track都准备好了
// 如果已经添加了音视频Track并且不存在未准备好的Track那么说明所有Track都准备好了 [AUTO-TRANSLATED:6fce8779]
// If audio and video Tracks have been added, and there are no unprepared Tracks, then all Tracks are ready
emitAllTrackReady();
return;
}
GET_CONFIG(uint32_t, kMaxAddTrackMS, General::kWaitAddTrackMS);
if (_track_map.size() == 1 && (_ticker.elapsedTime() > kMaxAddTrackMS || !_enable_audio)) {
// 如果只有一个Track那么在该Track添加后我们最多还等待若干时间(可能后面还会添加Track)
// 如果只有一个Track那么在该Track添加后我们最多还等待若干时间(可能后面还会添加Track) [AUTO-TRANSLATED:5b4bd438]
// If there is only one Track, then after the Track is added, we wait for a certain amount of time at most (more Tracks may be added later)
emitAllTrackReady();
return;
}
@ -161,9 +191,11 @@ void MediaSink::emitAllTrackReady() {
DebugL << "All track ready use " << _ticker.elapsedTime() << "ms";
if (!_track_ready_callback.empty()) {
// 这是超时强制忽略未准备好的Track
// 这是超时强制忽略未准备好的Track [AUTO-TRANSLATED:d4f57e00]
// This is a timeout forced ignore of unprepared Tracks
_track_ready_callback.clear();
// 移除未准备好的Track
// 移除未准备好的Track [AUTO-TRANSLATED:69965c62]
// Remove unprepared Tracks
for (auto it = _track_map.begin(); it != _track_map.end();) {
if (!it->second.second || !it->second.first->ready()) {
WarnL << "Track not ready for a long time, ignored: " << it->second.first->getCodecName();
@ -175,13 +207,16 @@ void MediaSink::emitAllTrackReady() {
}
if (!_track_map.empty()) {
// 最少有一个有效的Track
// 最少有一个有效的Track [AUTO-TRANSLATED:099adc94]
// There is at least one valid Track
onAllTrackReady_l();
// 全部Track就绪我们一次性把之前的帧输出
// 全部Track就绪我们一次性把之前的帧输出 [AUTO-TRANSLATED:2431422b]
// All Tracks are ready, we output all the previous frames at once
for (auto &pr : _frame_unread) {
if (_track_map.find(pr.first) == _track_map.end()) {
// 该Track已经被移除
// 该Track已经被移除 [AUTO-TRANSLATED:d44bf74e]
// The Track has been removed
continue;
}
pr.second.for_each([&](const Frame::Ptr &frame) { MediaSink::inputFrame(frame); });
@ -193,7 +228,8 @@ void MediaSink::emitAllTrackReady() {
}
void MediaSink::onAllTrackReady_l() {
// 是否添加静音音频
// 是否添加静音音频 [AUTO-TRANSLATED:bbfbfe73]
// Whether to add silent audio
if (_add_mute_audio) {
addMuteAudioTrack();
}
@ -293,11 +329,13 @@ Frame::Ptr MuteAudioMaker::makeSlienceFrame(int64_t dts) {
bool MuteAudioMaker::inputFrame(const Frame::Ptr &frame) {
if (_track_index == -1) {
// 锁定track
// 锁定track [AUTO-TRANSLATED:41aff35e]
// Lock track
_track_index = frame->getIndex();
}
if (frame->getIndex() != _track_index) {
// 不是锁定的track
// 不是锁定的track [AUTO-TRANSLATED:496bd08b]
// Not a locked track
return false;
}
if (frame->getTrackType() == TrackVideo && _frame_ms > 0) {

View File

@ -27,16 +27,27 @@ public:
* trackTrack的clone方法
* sps pps这些信息 Delegate相关关系
* @param track
* Add track, internally calls the clone method of Track
* Only clones sps pps information, not the Delegate relationship
* @param track
* [AUTO-TRANSLATED:ba6faf58]
*/
virtual bool addTrack(const Track::Ptr & track) = 0;
/**
* track完毕
* Track added
* [AUTO-TRANSLATED:dc70ddea]
*/
virtual void addTrackCompleted() {};
/**
* track
* Reset track
* [AUTO-TRANSLATED:95dc0b4f]
*/
virtual void resetTracks() {};
};
@ -48,6 +59,9 @@ public:
/**
* aac静音音频添加器
* AAC mute audio adder
* [AUTO-TRANSLATED:aa154f71]
*/
class MuteAudioMaker : public FrameDispatcher, public CodecInfo {
public:
@ -67,6 +81,10 @@ private:
/**
* Track ready()true也就是就绪后再通知派生类进行下一步的操作
* Frame前由Track截取处理下便sps pps aa_cfg
* The role of this class is to wait for Track ready() to return true, that is, ready, and then notify the derived class to perform the next operation.
* The purpose is to intercept and process the input Frame by Track before inputting the Frame, so as to obtain valid information (such as sps pps aa_cfg)
* [AUTO-TRANSLATED:9e4f096b]
*/
class MediaSink : public MediaSinkInterface, public TrackSource{
public:
@ -74,6 +92,10 @@ public:
/**
* frame
* @param frame
* Input frame
* @param frame
* [AUTO-TRANSLATED:7aaa5bba]
*/
bool inputFrame(const Frame::Ptr &frame) override;
@ -81,6 +103,11 @@ public:
* trackTrack的clone方法
* sps pps这些信息 Delegate相关关系
* @param track
* Add track, internally calls the clone method of Track
* Only clones sps pps information, not the Delegate relationship
* @param track
* [AUTO-TRANSLATED:ba6faf58]
*/
bool addTrack(const Track::Ptr & track) override;
@ -88,43 +115,71 @@ public:
* Track完毕Track3onAllTrackReady
* Track
*
* Track added, if it is a single Track, it will wait for a maximum of 3 seconds before triggering onAllTrackReady
* This will increase the delay in generating the stream. If you add both audio and video tracks, you can skip this method.
* Otherwise, to reduce the stream registration delay, please call this method manually.
* [AUTO-TRANSLATED:580b6163]
*/
void addTrackCompleted() override;
/**
* track数>=1addTrackCompleted类型
* track时
* Set the maximum number of tracks, the value range is >=1; this method is of the addTrackCompleted type;
* When setting a single track, it can speed up media registration
* [AUTO-TRANSLATED:cd521c6f]
*/
void setMaxTrackCount(size_t i);
/**
* track
* Reset track
* [AUTO-TRANSLATED:95dc0b4f]
*/
void resetTracks() override;
/**
* Track
* @param trackReady Track
* Get all Tracks
* @param trackReady Whether to get the ready Track
* [AUTO-TRANSLATED:32032e47]
*/
std::vector<Track::Ptr> getTracks(bool trackReady = true) const override;
/**
* onAllTrackReady事件
* Determine whether the onAllTrackReady event has been triggered
* [AUTO-TRANSLATED:fb8b4c71]
*/
bool isAllTrackReady() const;
/**
*
* Set whether to enable audio
* [AUTO-TRANSLATED:0e9a3ef0]
*/
void enableAudio(bool flag);
/**
*
* Set single audio
* [AUTO-TRANSLATED:48fc734a]
*/
void setOnlyAudio();
/**
*
* Set whether to enable adding mute audio
* [AUTO-TRANSLATED:49efef10]
*/
void enableMuteAudio(bool flag);
bool hasMuteAudio() const { return _mute_audio_maker != nullptr; }
@ -133,6 +188,9 @@ public:
}
/**
* track
* Whether there is a video track
* [AUTO-TRANSLATED:4c4d651d]
*/
bool haveVideo() const;
@ -141,33 +199,54 @@ protected:
* track已经准备好ready()true
* sps pps等相关信息了
* @param track
* A certain track is ready, its ready() status returns true,
* This means that you can get its related information such as sps pps
* @param track
* [AUTO-TRANSLATED:720dedc1]
*/
virtual bool onTrackReady(const Track::Ptr & track) { return false; };
/**
* Track已经准备好
* All Tracks are ready,
* [AUTO-TRANSLATED:c54d02e2]
*/
virtual void onAllTrackReady() {};
/**
* Track输出frameonAllTrackReady触发后才会调用此方法
* @param frame
* A certain Track outputs a frame, this method will be called only after onAllTrackReady is triggered
* @param frame
* [AUTO-TRANSLATED:debbd247]
*/
virtual bool onTrackFrame(const Frame::Ptr &frame) { return false; };
private:
/**
* onAllTrackReady事件
* Trigger the onAllTrackReady event
* [AUTO-TRANSLATED:068fdb61]
*/
void emitAllTrackReady();
/**
* track是否准备完毕
* Check if the track is ready
* [AUTO-TRANSLATED:12e7c3e6]
*/
void checkTrackIfReady();
void onAllTrackReady_l();
/**
* aac静音轨道
* Add AAC mute track
* [AUTO-TRANSLATED:9ba052b5]
*/
bool addMuteAudioTrack();
@ -193,6 +272,9 @@ class MediaSinkDelegate : public MediaSink {
public:
/**
* track监听器
* Set track listener
* [AUTO-TRANSLATED:cedc97d7]
*/
void setTrackListener(TrackListener *listener);

View File

@ -95,11 +95,13 @@ MediaSource::~MediaSource() {
std::shared_ptr<void> MediaSource::getOwnership() {
if (_owned.test_and_set()) {
//已经被所有
// 已经被所有 [AUTO-TRANSLATED:bab937dc]
// Already owned by all
return nullptr;
}
weak_ptr<MediaSource> weak_self = shared_from_this();
//确保返回的Ownership智能指针不为空0x01无实际意义
// 确保返回的Ownership智能指针不为空0x01无实际意义 [AUTO-TRANSLATED:9a4cca08]
// Ensure that the returned Ownership smart pointer is not empty, 0x01 has no practical meaning
return std::shared_ptr<void>((void *) 0x01, [weak_self](void *ptr) {
auto strong_self = weak_self.lock();
if (strong_self) {
@ -116,7 +118,8 @@ int MediaSource::getBytesSpeed(TrackType type){
}
uint64_t MediaSource::getAliveSecond() const {
//使用Ticker对象获取存活时间的目的是防止修改系统时间导致回退
// 使用Ticker对象获取存活时间的目的是防止修改系统时间导致回退 [AUTO-TRANSLATED:68474061]
// The purpose of using the Ticker object to obtain the survival time is to prevent the modification of the system time from causing a rollback
return _ticker.createdTime() / 1000;
}
@ -202,7 +205,8 @@ bool MediaSource::close(bool force) {
return false;
}
if (!force && totalReaderCount()) {
//有人观看,不强制关闭
// 有人观看,不强制关闭 [AUTO-TRANSLATED:44b7e24d]
// Someone is watching, do not force close
return false;
}
return listener->close(*this);
@ -249,11 +253,14 @@ void MediaSource::onReaderChanged(int size) {
}
});
} catch (MediaSourceEvent::NotImplemented &ex) {
// 未实现接口,应该打印异常
// 未实现接口,应该打印异常 [AUTO-TRANSLATED:84f28c9d]
// The interface is not implemented, an exception should be printed
WarnL << ex.what();
} catch (...) {
// getOwnerPoller()接口抛异常机制应该只对外不对内
// 所以listener已经销毁导致获取归属线程失败的异常直接忽略
// getOwnerPoller()接口抛异常机制应该只对外不对内 [AUTO-TRANSLATED:ee2e2923]
// The getOwnerPoller() interface should only throw exceptions externally, not internally
// 所以listener已经销毁导致获取归属线程失败的异常直接忽略 [AUTO-TRANSLATED:26cb5521]
// Therefore, the exception that the listener has been destroyed and the ownership thread cannot be obtained is directly ignored
}
}
@ -350,7 +357,8 @@ static MediaSource::Ptr find_l(const string &schema, const string &vhost_in, con
}
if (app.empty() || id.empty()) {
//如果未指定app与stream id那么就是遍历而非查找所以应该返回查找失败
// 如果未指定app与stream id那么就是遍历而非查找所以应该返回查找失败 [AUTO-TRANSLATED:84976471]
// If no app and stream id are specified, then it is traversal instead of searching, so it should return search failure
return nullptr;
}
@ -358,8 +366,10 @@ static MediaSource::Ptr find_l(const string &schema, const string &vhost_in, con
MediaSource::for_each_media([&](const MediaSource::Ptr &src) { ret = std::move(const_cast<MediaSource::Ptr &>(src)); }, schema, vhost, app, id);
if(!ret && from_mp4 && schema != HLS_SCHEMA){
//未找到媒体源则读取mp4创建一个
//播放hls不触发mp4点播(因为HLS也可以用于录像不是纯粹的直播)
// 未找到媒体源则读取mp4创建一个 [AUTO-TRANSLATED:e2e03a82]
// If the media source is not found, read mp4 to create one
// 播放hls不触发mp4点播(因为HLS也可以用于录像不是纯粹的直播) [AUTO-TRANSLATED:30b18b6d]
// Playing hls does not trigger mp4 on-demand (because HLS can also be used for recording, not purely live)
ret = MediaSource::createFromMP4(schema, vhost, app, id);
}
return ret;
@ -379,23 +389,27 @@ static void findAsync_l(const MediaInfo &info, const std::shared_ptr<Session> &s
std::shared_ptr<atomic_flag> invoked(new atomic_flag{false});
auto cb_once = [cb, invoked](const MediaSource::Ptr &src) {
if (invoked->test_and_set()) {
//回调已经执行过了
// 回调已经执行过了 [AUTO-TRANSLATED:f034e2eb]
// The callback has already been executed
return;
}
cb(src);
};
auto on_timeout = poller->doDelayTask(maxWaitMS, [cb_once, listener_tag]() {
// 最多等待一定时间,如在这个时间内,流还未注册上,则返回空
// 最多等待一定时间,如在这个时间内,流还未注册上,则返回空 [AUTO-TRANSLATED:e8851208]
// Wait for a certain amount of time at most, if the stream is not registered within this time, return empty
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
cb_once(nullptr);
return 0;
});
auto cancel_all = [on_timeout, listener_tag]() {
//取消延时任务,防止多次回调
// 取消延时任务,防止多次回调 [AUTO-TRANSLATED:42988b9c]
// Cancel the delayed task to prevent multiple callbacks
on_timeout->cancel();
//取消媒体注册事件监听
// 取消媒体注册事件监听 [AUTO-TRANSLATED:efb9aacb]
// Cancel the media registration event listener
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
};
@ -404,32 +418,38 @@ static void findAsync_l(const MediaInfo &info, const std::shared_ptr<Session> &s
if (!bRegist ||
sender.getSchema() != info.schema ||
!equalMediaTuple(sender.getMediaTuple(), info)) {
//不是自己感兴趣的事件,忽略之
// 不是自己感兴趣的事件,忽略之 [AUTO-TRANSLATED:b4e102d4]
// Not an event of interest, ignore it
return;
}
poller->async([weak_session, cancel_all, info, cb_once]() {
cancel_all();
if (auto strong_session = weak_session.lock()) {
//播发器请求的流终于注册上了,切换到自己的线程再回复
// 播发器请求的流终于注册上了,切换到自己的线程再回复 [AUTO-TRANSLATED:7b79ad9b]
// The stream requested by the player is finally registered, switch to its own thread and reply
DebugL << "收到媒体注册事件,回复播放器:" << info.getUrl();
//再找一遍媒体源,一般能找到
// 再找一遍媒体源,一般能找到 [AUTO-TRANSLATED:069de7f6]
// Find the media source again, usually it can be found
findAsync_l(info, strong_session, false, cb_once);
}
}, false);
};
//监听媒体注册事件
// 监听媒体注册事件 [AUTO-TRANSLATED:9cf13779]
// Listen for media registration events
NoticeCenter::Instance().addListener(listener_tag, Broadcast::kBroadcastMediaChanged, on_register);
function<void()> close_player = [cb_once, cancel_all, poller]() {
poller->async([cancel_all, cb_once]() {
cancel_all();
//告诉播放器,流不存在,这样会立即断开播放器
// 告诉播放器,流不存在,这样会立即断开播放器 [AUTO-TRANSLATED:b5b4eead]
// Tell the player that the stream does not exist, so it will immediately disconnect the player
cb_once(nullptr);
});
};
//广播未找到流,此时可以立即去拉流,这样还来得及
// 广播未找到流,此时可以立即去拉流,这样还来得及 [AUTO-TRANSLATED:794014f1]
// Broadcast that the stream is not found, at this time you can immediately pull the stream, so it is still in time
NOTICE_EMIT(BroadcastNotFoundStreamArgs, Broadcast::kBroadcastNotFoundStream, info, *session, close_player);
}
@ -468,17 +488,20 @@ MediaSource::Ptr MediaSource::find(const string &vhost, const string &app, const
void MediaSource::emitEvent(bool regist){
auto listener = _listener.lock();
if (listener) {
//触发回调
// 触发回调 [AUTO-TRANSLATED:08ea452d]
// Trigger callback
listener->onRegist(*this, regist);
}
//触发广播
// 触发广播 [AUTO-TRANSLATED:a5b415a4]
// Trigger broadcast
NOTICE_EMIT(BroadcastMediaChangedArgs, Broadcast::kBroadcastMediaChanged, regist, *this);
InfoL << (regist ? "媒体注册:" : "媒体注销:") << getUrl();
}
void MediaSource::regist() {
{
//减小互斥锁临界区
// 减小互斥锁临界区 [AUTO-TRANSLATED:1309d309]
// Reduce mutex lock critical area
lock_guard<recursive_mutex> lock(s_media_source_mtx);
auto &ref = s_media_source_map[_schema][_tuple.vhost][_tuple.app][_tuple.stream];
auto src = ref.lock();
@ -486,7 +509,8 @@ void MediaSource::regist() {
if (src.get() == this) {
return;
}
//增加判断, 防止当前流已注册时再次注册
// 增加判断, 防止当前流已注册时再次注册 [AUTO-TRANSLATED:ccc5dcb1]
// Add judgment to prevent re-registration when the current stream is already registered
throw std::invalid_argument("media source already existed:" + getUrl());
}
ref = shared_from_this();
@ -509,7 +533,8 @@ static bool erase_media_source(bool &hit, const MediaSource *thiz, MAP &map, con
if (it != map.end()) {
auto src = it->second.lock();
if (!src || src.get() == thiz) {
//对象已经销毁或者对象就是自己,那么移除之
// 对象已经销毁或者对象就是自己,那么移除之 [AUTO-TRANSLATED:1b9a11d1]
// If the object has been destroyed or the object is itself, then remove it
map.erase(it);
hit = true;
}
@ -517,11 +542,13 @@ static bool erase_media_source(bool &hit, const MediaSource *thiz, MAP &map, con
return map.empty();
}
//反注册该源
// 反注册该源 [AUTO-TRANSLATED:682c27ab]
// Unregister the source
bool MediaSource::unregist() {
bool ret = false;
{
//减小互斥锁临界区
// 减小互斥锁临界区 [AUTO-TRANSLATED:1309d309]
// Reduce mutex lock critical area
lock_guard<recursive_mutex> lock(s_media_source_mtx);
erase_media_source(ret, this, s_media_source_map, _schema, _tuple.vhost, _tuple.app, _tuple.stream);
}
@ -557,7 +584,8 @@ void MediaInfo::parse(const std::string &url_in){
splitUrl(split_vec[0], host, port);
vhost = host;
if (vhost == "localhost" || isIP(vhost.data())) {
//如果访问的是localhost或ip那么则为默认虚拟主机
// 如果访问的是localhost或ip那么则为默认虚拟主机 [AUTO-TRANSLATED:67291b7a]
// If the access is to localhost or ip, then it is the default virtual host
vhost = DEFAULT_VHOST;
}
}
@ -583,7 +611,8 @@ void MediaInfo::parse(const std::string &url_in){
GET_CONFIG(bool, enableVhost, General::kEnableVhost);
if (!enableVhost || vhost.empty()) {
//如果关闭虚拟主机或者虚拟主机为空,则设置虚拟主机为默认
// 如果关闭虚拟主机或者虚拟主机为空,则设置虚拟主机为默认 [AUTO-TRANSLATED:9f76a112]
// If the virtual host is closed or the virtual host is empty, set the virtual host to the default
vhost = DEFAULT_VHOST;
}
}
@ -617,41 +646,49 @@ void MediaSourceEvent::onReaderChanged(MediaSource &sender, int size){
NOTICE_EMIT(BroadcastPlayerCountChangedArgs, Broadcast::kBroadcastPlayerCountChanged, sender.getMediaTuple(), sender.totalReaderCount());
}
if (size || sender.totalReaderCount()) {
//还有人观看该视频,不触发关闭事件
// 还有人观看该视频,不触发关闭事件 [AUTO-TRANSLATED:7f2f6ed3]
// Someone is still watching this video, do not trigger the close event
_async_close_timer = nullptr;
return;
}
//没有任何人观看该视频源,表明该源可以关闭了
// 没有任何人观看该视频源,表明该源可以关闭了 [AUTO-TRANSLATED:ea64bb8f]
// No one is watching this video source, indicating that the source can be closed.
GET_CONFIG(string, record_app, Record::kAppName);
GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
//如果mp4点播, 无人观看时我们强制关闭点播
// 如果mp4点播, 无人观看时我们强制关闭点播 [AUTO-TRANSLATED:9576e4b0]
// If it's an mp4 on-demand, we force close the on-demand when no one is watching.
bool is_mp4_vod = sender.getMediaTuple().app == record_app;
weak_ptr<MediaSource> weak_sender = sender.shared_from_this();
_async_close_timer = std::make_shared<Timer>(stream_none_reader_delay / 1000.0f, [weak_sender, is_mp4_vod]() {
auto strong_sender = weak_sender.lock();
if (!strong_sender) {
//对象已经销毁
// 对象已经销毁 [AUTO-TRANSLATED:130328af]
// The object has been destroyed.
return false;
}
if (strong_sender->totalReaderCount()) {
//还有人观看该视频,不触发关闭事件
// 还有人观看该视频,不触发关闭事件 [AUTO-TRANSLATED:7f2f6ed3]
// Someone is still watching this video, so the close event is not triggered.
return false;
}
if (!is_mp4_vod) {
auto muxer = strong_sender->getMuxer();
if (muxer && muxer->getOption().auto_close) {
// 此流被标记为无人观看自动关闭流
// 此流被标记为无人观看自动关闭流 [AUTO-TRANSLATED:64a0dac3]
// This stream is marked as an automatically closed stream with no viewers.
WarnL << "Auto cloe stream when none reader: " << strong_sender->getUrl();
strong_sender->close(false);
} else {
// 直播时触发无人观看事件,让开发者自行选择是否关闭
// 直播时触发无人观看事件,让开发者自行选择是否关闭 [AUTO-TRANSLATED:c6c75eaa]
// When live streaming, trigger the no-viewer event, allowing developers to choose whether to close it.
NOTICE_EMIT(BroadcastStreamNoneReaderArgs, Broadcast::kBroadcastStreamNoneReader, *strong_sender);
}
} else {
//这个是mp4点播我们自动关闭
// 这个是mp4点播我们自动关闭 [AUTO-TRANSLATED:8a7b9a90]
// This is an mp4 on-demand, we automatically close it.
WarnL << "MP4点播无人观看,自动关闭:" << strong_sender->getUrl();
strong_sender->close(false);
}
@ -834,39 +871,47 @@ std::shared_ptr<MediaSourceEvent> MediaSourceEventInterceptor::getDelegate() con
static bool isFlushAble_default(bool is_video, uint64_t last_stamp, uint64_t new_stamp, size_t cache_size) {
if (new_stamp + 500 < last_stamp) {
//时间戳回退比较大(可能seek中)由于rtp中时间戳是pts是可能存在一定程度的回退的
// 时间戳回退比较大(可能seek中)由于rtp中时间戳是pts是可能存在一定程度的回退的 [AUTO-TRANSLATED:67158987]
// The timestamp rollback is relatively large (possibly during seek), because the timestamp in RTP is PTS, which may have a certain degree of rollback.
return true;
}
//时间戳发送变化或者缓存超过1024个,sendmsg接口一般最多只能发送1024个数据包
// 时间戳发送变化或者缓存超过1024个,sendmsg接口一般最多只能发送1024个数据包 [AUTO-TRANSLATED:f87d1da0]
// The timestamp sends changes or the cache exceeds 1024, the sendmsg interface generally can only send a maximum of 1024 data packets.
return last_stamp != new_stamp || cache_size >= 1024;
}
static bool isFlushAble_merge(bool is_video, uint64_t last_stamp, uint64_t new_stamp, size_t cache_size, int merge_ms) {
if (new_stamp + 500 < last_stamp) {
//时间戳回退比较大(可能seek中)由于rtp中时间戳是pts是可能存在一定程度的回退的
// 时间戳回退比较大(可能seek中)由于rtp中时间戳是pts是可能存在一定程度的回退的 [AUTO-TRANSLATED:67158987]
// The timestamp rollback is relatively large (possibly during seek), because the timestamp in RTP is PTS, which may have a certain degree of rollback.
return true;
}
if (new_stamp > last_stamp + merge_ms) {
//时间戳增量超过合并写阈值
// 时间戳增量超过合并写阈值 [AUTO-TRANSLATED:cbcf3ab0]
// The timestamp increment exceeds the merge write threshold.
return true;
}
//缓存数超过1024个,这个逻辑用于避免时间戳异常的流导致的内存暴增问题
//而且sendmsg接口一般最多只能发送1024个数据包
// 缓存数超过1024个,这个逻辑用于避免时间戳异常的流导致的内存暴增问题 [AUTO-TRANSLATED:f27e11f8]
// The number of caches exceeds 1024, this logic is used to avoid memory explosion caused by streams with abnormal timestamps.
// 而且sendmsg接口一般最多只能发送1024个数据包 [AUTO-TRANSLATED:872436e2]
// Moreover, the sendmsg interface generally can only send a maximum of 1024 data packets.
return cache_size >= 1024;
}
bool FlushPolicy::isFlushAble(bool is_video, bool is_key, uint64_t new_stamp, size_t cache_size) {
bool flush_flag = false;
if (is_key && is_video) {
//遇到关键帧flush掉前面的数据确保关键帧为该组数据的第一帧确保GOP缓存有效
// 遇到关键帧flush掉前面的数据确保关键帧为该组数据的第一帧确保GOP缓存有效 [AUTO-TRANSLATED:e2ebbf9b]
// Encounter a key frame, flush the previous data, ensure that the key frame is the first frame of this group of data, and ensure the GOP cache is valid.
flush_flag = true;
} else {
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
if (mergeWriteMS <= 0) {
//关闭了合并写或者合并写阈值小于等于0
// 关闭了合并写或者合并写阈值小于等于0 [AUTO-TRANSLATED:2397b647]
// Merge writing is closed or the merge writing threshold is less than or equal to 0.
flush_flag = isFlushAble_default(is_video, _last_stamp[is_video], new_stamp, cache_size);
} else {
flush_flag = isFlushAble_merge(is_video, _last_stamp[is_video], new_stamp, cache_size, mergeWriteMS);

View File

@ -56,42 +56,60 @@ public:
virtual ~MediaSourceEvent() = default;
// 获取媒体源类型
// 获取媒体源类型 [AUTO-TRANSLATED:34290a69]
// Get media source type
virtual MediaOriginType getOriginType(MediaSource &sender) const { return MediaOriginType::unknown; }
// 获取媒体源url或者文件路径
// 获取媒体源url或者文件路径 [AUTO-TRANSLATED:fa34d795]
// Get media source url or file path
virtual std::string getOriginUrl(MediaSource &sender) const;
// 获取媒体源客户端相关信息
// 获取媒体源客户端相关信息 [AUTO-TRANSLATED:037ef910]
// Get media source client related information
virtual std::shared_ptr<toolkit::SockInfo> getOriginSock(MediaSource &sender) const { return nullptr; }
// 通知拖动进度条
// 通知拖动进度条 [AUTO-TRANSLATED:561b17f7]
// Notify drag progress bar
virtual bool seekTo(MediaSource &sender, uint32_t stamp) { return false; }
// 通知暂停或恢复
// 通知暂停或恢复 [AUTO-TRANSLATED:ee3c219f]
// Notify pause or resume
virtual bool pause(MediaSource &sender, bool pause) { return false; }
// 通知倍数
// 通知倍数 [AUTO-TRANSLATED:8f1dab15]
// Notify multiple times
virtual bool speed(MediaSource &sender, float speed) { return false; }
// 通知其停止产生流
// 通知其停止产生流 [AUTO-TRANSLATED:62c9022c]
// Notify it to stop generating streams
virtual bool close(MediaSource &sender) { return false; }
// 获取观看总人数,此函数一般强制重载
// 获取观看总人数,此函数一般强制重载 [AUTO-TRANSLATED:1da20a10]
// Get the total number of viewers, this function is generally forced to overload
virtual int totalReaderCount(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::totalReaderCount not implemented"); }
// 通知观看人数变化
// 通知观看人数变化 [AUTO-TRANSLATED:bad89528]
// Notify the change in the number of viewers
virtual void onReaderChanged(MediaSource &sender, int size);
//流注册或注销事件
// 流注册或注销事件 [AUTO-TRANSLATED:2cac8178]
// Stream registration or deregistration event
virtual void onRegist(MediaSource &sender, bool regist) {}
// 获取丢包率
// 获取丢包率 [AUTO-TRANSLATED:ec61b378]
// Get packet loss rate
virtual float getLossRate(MediaSource &sender, TrackType type) { return -1; }
// 获取所在线程, 此函数一般强制重载
// 获取所在线程, 此函数一般强制重载 [AUTO-TRANSLATED:71c99afb]
// Get the current thread, this function is generally forced to overload
virtual toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller not implemented"); }
////////////////////////仅供MultiMediaSourceMuxer对象继承////////////////////////
// 开启或关闭录制
// //////////////////////仅供MultiMediaSourceMuxer对象继承//////////////////////// [AUTO-TRANSLATED:6e810d1f]
// //////////////////////Only for MultiMediaSourceMuxer object inheritance////////////////////////
// 开启或关闭录制 [AUTO-TRANSLATED:3817e390]
// Start or stop recording
virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) { return false; };
// 获取录制状态
// 获取录制状态 [AUTO-TRANSLATED:a0499880]
// Get recording status
virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; }
// 获取所有track相关信息
// 获取所有track相关信息 [AUTO-TRANSLATED:2141be42]
// Get all track related information
virtual std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const { return std::vector<Track::Ptr>(); };
// 获取MultiMediaSourceMuxer对象
// 获取MultiMediaSourceMuxer对象 [AUTO-TRANSLATED:2de96d44]
// Get MultiMediaSourceMuxer object
virtual std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) const { return nullptr; }
// 获取RtpProcess对象
// 获取RtpProcess对象 [AUTO-TRANSLATED:c6b7da43]
// Get RtpProcess object
virtual std::shared_ptr<RtpProcess> getRtpProcess(MediaSource &sender) const { return nullptr; }
class SendRtpArgs {
@ -109,42 +127,60 @@ public:
kUdpPassive = 3 // udp被动方式等待对方发送nat打洞包然后回复rtp至打洞包源地址
};
// rtp类型
// rtp类型 [AUTO-TRANSLATED:acca40ab]
// Rtp type
DataType data_type = kRtpPS;
// 连接类型
// 连接类型 [AUTO-TRANSLATED:8ad5c881]
// Connection type
ConType con_type = kUdpActive;
// 发送es流时指定是否只发送纯音频流
// 发送es流时指定是否只发送纯音频流 [AUTO-TRANSLATED:470c761e]
// Specify whether to send only pure audio stream when sending es stream
bool only_audio = false;
// rtp payload type
uint8_t pt = 96;
// 是否支持同ssrc多服务器发送
// 是否支持同ssrc多服务器发送 [AUTO-TRANSLATED:9d817af2]
// Whether to support multiple servers sending with the same ssrc
bool ssrc_multi_send = false;
// 指定rtp ssrc
// 指定rtp ssrc [AUTO-TRANSLATED:7366c6f9]
// Specify rtp ssrc
std::string ssrc;
// 指定本地发送端口
// 指定本地发送端口 [AUTO-TRANSLATED:f5d92f40]
// Specify local sending port
uint16_t src_port = 0;
// 发送目标端口
// 发送目标端口 [AUTO-TRANSLATED:096b5574]
// Send target port
uint16_t dst_port;
// 发送目标主机地址可以是ip或域名
// 发送目标主机地址可以是ip或域名 [AUTO-TRANSLATED:2c872f2e]
// Send target host address, can be ip or domain name
std::string dst_url;
// udp发送时是否开启rr rtcp接收超时判断
// udp发送时是否开启rr rtcp接收超时判断 [AUTO-TRANSLATED:784982bd]
// When sending udp, whether to enable rr rtcp receive timeout judgment
bool udp_rtcp_timeout = false;
// passive被动、tcp主动模式超时时间
// passive被动、tcp主动模式超时时间 [AUTO-TRANSLATED:8886d475]
// Passive passive, tcp active mode timeout time
uint32_t close_delay_ms = 0;
// udp 发送时rr rtcp包接收超时时间单位毫秒
// udp 发送时rr rtcp包接收超时时间单位毫秒 [AUTO-TRANSLATED:9f0d91d9]
// When sending udp, rr rtcp packet receive timeout time, in milliseconds
uint32_t rtcp_timeout_ms = 30 * 1000;
// udp 发送时发送sr rtcp包间隔单位毫秒
// udp 发送时发送sr rtcp包间隔单位毫秒 [AUTO-TRANSLATED:c87bfed4]
// When sending udp, send sr rtcp packet interval, in milliseconds
uint32_t rtcp_send_interval_ms = 5 * 1000;
// 发送rtp同时接收一般用于双向语言对讲, 如果不为空,说明开启接收
// 发送rtp同时接收一般用于双向语言对讲, 如果不为空,说明开启接收 [AUTO-TRANSLATED:f4c18084]
// Send rtp while receiving, generally used for two-way language intercom, if not empty, it means receiving is enabled
std::string recv_stream_id;
std::string recv_stream_app;
std::string recv_stream_vhost;
};
// 开始发送ps-rtp
// 开始发送ps-rtp [AUTO-TRANSLATED:a51796fa]
// Start sending ps-rtp
virtual void startSendRtp(MediaSource &sender, const SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb) { cb(0, toolkit::SockException(toolkit::Err_other, "not implemented"));};
// 停止发送ps-rtp
// 停止发送ps-rtp [AUTO-TRANSLATED:952d2b35]
// Stop sending ps-rtp
virtual bool stopSendRtp(MediaSource &sender, const std::string &ssrc) {return false; }
private:
@ -177,69 +213,96 @@ public:
kModifyStampSystem = 1, // 采用zlmediakit接收数据时的系统时间戳(有平滑处理)
kModifyStampRelative = 2 // 采用源视频流时间戳相对时间戳(增长量),有做时间戳跳跃和回退矫正
};
// 时间戳类型
// 时间戳类型 [AUTO-TRANSLATED:7d2779e1]
// Timestamp type
int modify_stamp;
//转协议是否开启音频
// 转协议是否开启音频 [AUTO-TRANSLATED:220dddfa]
// Whether to enable audio for protocol conversion
bool enable_audio;
//添加静音音频,在关闭音频时,此开关无效
// 添加静音音频,在关闭音频时,此开关无效 [AUTO-TRANSLATED:47c0ec8e]
// Add mute audio, this switch is invalid when audio is closed
bool add_mute_audio;
// 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close)
// 此配置置1时此流如果无人观看将不触发on_none_reader hook回调
// 而是将直接关闭流
// 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close) [AUTO-TRANSLATED:dba7ab70]
// Whether to close directly when no one is watching (instead of returning close through the on_none_reader hook)
// 此配置置1时此流如果无人观看将不触发on_none_reader hook回调 [AUTO-TRANSLATED:a5ead314]
// When this configuration is set to 1, if no one is watching this stream, it will not trigger the on_none_reader hook callback,
// 而是将直接关闭流 [AUTO-TRANSLATED:06887d49]
// but will directly close the stream
bool auto_close;
//断连续推延时,单位毫秒,默认采用配置文件
// 断连续推延时,单位毫秒,默认采用配置文件 [AUTO-TRANSLATED:7a15b12f]
// Delay in milliseconds for continuous pushing, default is using the configuration file
uint32_t continue_push_ms;
// 平滑发送定时器间隔单位毫秒置0则关闭开启后影响cpu性能同时增加内存
// 该配置开启后可以解决一些流发送不平滑导致zlmediakit转发也不平滑的问题
// 平滑发送定时器间隔单位毫秒置0则关闭开启后影响cpu性能同时增加内存 [AUTO-TRANSLATED:ad4e306a]
// Smooth sending timer interval, in milliseconds, set to 0 to close; enabling it will affect cpu performance and increase memory at the same time
// 该配置开启后可以解决一些流发送不平滑导致zlmediakit转发也不平滑的问题 [AUTO-TRANSLATED:0f2b1657]
// This configuration can solve some problems where the stream is not sent smoothly, resulting in zlmediakit forwarding not being smooth
uint32_t paced_sender_ms;
//是否开启转换为hls(mpegts)
// 是否开启转换为hls(mpegts) [AUTO-TRANSLATED:bfc1167a]
// Whether to enable conversion to hls(mpegts)
bool enable_hls;
//是否开启转换为hls(fmp4)
// 是否开启转换为hls(fmp4) [AUTO-TRANSLATED:20548673]
// Whether to enable conversion to hls(fmp4)
bool enable_hls_fmp4;
//是否开启MP4录制
// 是否开启MP4录制 [AUTO-TRANSLATED:0157b014]
// Whether to enable MP4 recording
bool enable_mp4;
//是否开启转换为rtsp
// 是否开启转换为rtsp [AUTO-TRANSLATED:0711cb18]
// Whether to enable conversion to rtsp
bool enable_rtsp;
//是否开启转换为rtmp/flv
// 是否开启转换为rtmp/flv [AUTO-TRANSLATED:d4774119]
// Whether to enable conversion to rtmp/flv
bool enable_rtmp;
//是否开启转换为http-ts/ws-ts
// 是否开启转换为http-ts/ws-ts [AUTO-TRANSLATED:51acc798]
// Whether to enable conversion to http-ts/ws-ts
bool enable_ts;
//是否开启转换为http-fmp4/ws-fmp4
// 是否开启转换为http-fmp4/ws-fmp4 [AUTO-TRANSLATED:8c96e1e4]
// Whether to enable conversion to http-fmp4/ws-fmp4
bool enable_fmp4;
//是否开启转换为webrtc
bool enable_rtc;
// 是否启用音频转码
bool audio_transcode;
bool rtc_demand;
// hls协议是否按需生成如果hls.segNum配置为0(意味着hls录制)那么hls将一直生成(不管此开关)
// hls协议是否按需生成如果hls.segNum配置为0(意味着hls录制)那么hls将一直生成(不管此开关) [AUTO-TRANSLATED:4653b411]
// Whether to generate hls protocol on demand, if hls.segNum is configured to 0 (meaning hls recording), then hls will always be generated (regardless of this switch)
bool hls_demand;
// rtsp[s]协议是否按需生成
// rtsp[s]协议是否按需生成 [AUTO-TRANSLATED:1c3237b0]
// Whether to generate rtsp[s] protocol on demand
bool rtsp_demand;
// rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成
// rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成 [AUTO-TRANSLATED:09ed2c30]
// Whether to generate rtmp[s]、http[s]-flv、ws[s]-flv protocol on demand
bool rtmp_demand;
// http[s]-ts协议是否按需生成
// http[s]-ts协议是否按需生成 [AUTO-TRANSLATED:a0129db3]
// Whether to generate http[s]-ts protocol on demand
bool ts_demand;
// http[s]-fmp4、ws[s]-fmp4协议是否按需生成
// http[s]-fmp4、ws[s]-fmp4协议是否按需生成 [AUTO-TRANSLATED:828d25c7]
// Whether to generate http[s]-fmp4、ws[s]-fmp4 protocol on demand
bool fmp4_demand;
//是否将mp4录制当做观看者
// 是否将mp4录制当做观看者 [AUTO-TRANSLATED:ba351230]
// Whether to treat mp4 recording as a viewer
bool mp4_as_player;
//mp4切片大小单位秒
// mp4切片大小单位秒 [AUTO-TRANSLATED:c3fb8ec1]
// MP4 slice size, in seconds
size_t mp4_max_second;
//mp4录制保存路径
// mp4录制保存路径 [AUTO-TRANSLATED:6d860f27]
// MP4 recording save path
std::string mp4_save_path;
//hls录制保存路径
// hls录制保存路径 [AUTO-TRANSLATED:cfa90719]
// HLS recording save path
std::string hls_save_path;
// 支持通过on_publish返回值替换stream_id
// 支持通过on_publish返回值替换stream_id [AUTO-TRANSLATED:2c4e4997]
// Support replacing stream_id through the return value of on_publish
std::string stream_replace;
// 最大track数
// 最大track数 [AUTO-TRANSLATED:2565fd37]
// Maximum number of tracks
size_t max_track = 2;
template <typename MAP>
@ -284,7 +347,8 @@ public:
}
};
//该对象用于拦截感兴趣的MediaSourceEvent事件
// 该对象用于拦截感兴趣的MediaSourceEvent事件 [AUTO-TRANSLATED:fd6d0559]
// This object is used to intercept interesting MediaSourceEvent events
class MediaSourceEventInterceptor : public MediaSourceEvent {
public:
void setDelegate(const std::weak_ptr<MediaSourceEvent> &listener);
@ -317,6 +381,9 @@ private:
/**
* url获取媒体相关信息
* Parse the url to get media information
* [AUTO-TRANSLATED:3b3cfde7]
*/
class MediaInfo: public MediaTuple {
public:
@ -336,6 +403,9 @@ bool equalMediaTuple(const MediaTuple& a, const MediaTuple& b);
/**
* rtsp/rtmp的直播流都源自该对象
* Media source, any rtsp/rtmp live stream originates from this object
* [AUTO-TRANSLATED:658077ad]
*/
class MediaSource: public TrackSource, public std::enable_shared_from_this<MediaSource> {
public:
@ -345,9 +415,11 @@ public:
MediaSource(const std::string &schema, const MediaTuple& tuple);
virtual ~MediaSource();
////////////////获取MediaSource相关信息////////////////
// //////////////获取MediaSource相关信息//////////////// [AUTO-TRANSLATED:4a520f1f]
// //////////////Get MediaSource information////////////////
// 获取协议类型
// 获取协议类型 [AUTO-TRANSLATED:d6b50c14]
// Get protocol type
const std::string& getSchema() const {
return _schema;
}
@ -358,36 +430,49 @@ public:
std::string getUrl() const { return _schema + "://" + _tuple.shortUrl(); }
//获取对象所有权
// 获取对象所有权 [AUTO-TRANSLATED:84fb43cd]
// Get object ownership
std::shared_ptr<void> getOwnership();
// 获取所有Track
// 获取所有Track [AUTO-TRANSLATED:59f1c570]
// Get all Tracks
std::vector<Track::Ptr> getTracks(bool ready = true) const override;
// 获取流当前时间戳
// 获取流当前时间戳 [AUTO-TRANSLATED:f65f560a]
// Get the current timestamp of the stream
virtual uint32_t getTimeStamp(TrackType type) { return 0; };
// 设置时间戳
// 设置时间戳 [AUTO-TRANSLATED:2bfce32f]
// Set timestamp
virtual void setTimeStamp(uint32_t stamp) {};
// 获取数据速率单位bytes/s
// 获取数据速率单位bytes/s [AUTO-TRANSLATED:c70465c1]
// Get data rate, unit bytes/s
int getBytesSpeed(TrackType type = TrackInvalid);
// 获取流创建GMT unix时间戳单位秒
// 获取流创建GMT unix时间戳单位秒 [AUTO-TRANSLATED:0bbe145e]
// Get the stream creation GMT unix timestamp, unit seconds
uint64_t getCreateStamp() const { return _create_stamp; }
// 获取流上线时间,单位秒
// 获取流上线时间,单位秒 [AUTO-TRANSLATED:a087d56a]
// Get the stream online time, unit seconds
uint64_t getAliveSecond() const;
////////////////MediaSourceEvent相关接口实现////////////////
// //////////////MediaSourceEvent相关接口实现//////////////// [AUTO-TRANSLATED:aa63d949]
// //////////////MediaSourceEvent related interface implementation////////////////
// 设置监听者
// 设置监听者 [AUTO-TRANSLATED:b9b90b57]
// Set listener
virtual void setListener(const std::weak_ptr<MediaSourceEvent> &listener);
// 获取监听者
// 获取监听者 [AUTO-TRANSLATED:5c9cbb82]
// Get listener
std::weak_ptr<MediaSourceEvent> getListener() const;
// 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数
// 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数 [AUTO-TRANSLATED:0874fa7c]
// This protocol gets the number of viewers, it may return the number of viewers of this protocol, or it may return the total number of viewers
virtual int readerCount() = 0;
// 观看者个数,包括(hls/rtsp/rtmp)
// 观看者个数,包括(hls/rtsp/rtmp) [AUTO-TRANSLATED:6604020f]
// Number of viewers, including (hls/rtsp/rtmp)
virtual int totalReaderCount();
// 获取播放器列表
// 获取播放器列表 [AUTO-TRANSLATED:e7691d2b]
// Get the player list
virtual void getPlayerList(const std::function<void(const std::list<toolkit::Any> &info_list)> &cb,
const std::function<toolkit::Any(toolkit::Any &&info)> &on_change) {
assert(cb);
@ -396,66 +481,91 @@ public:
virtual bool broadcastMessage(const toolkit::Any &data) { return false; }
// 获取媒体源类型
// 获取媒体源类型 [AUTO-TRANSLATED:34290a69]
// Get the media source type
MediaOriginType getOriginType() const;
// 获取媒体源url或者文件路径
// 获取媒体源url或者文件路径 [AUTO-TRANSLATED:fa34d795]
// Get the media source url or file path
std::string getOriginUrl() const;
// 获取媒体源客户端相关信息
// 获取媒体源客户端相关信息 [AUTO-TRANSLATED:037ef910]
// Get the media source client information
std::shared_ptr<toolkit::SockInfo> getOriginSock() const;
// 拖动进度条
// 拖动进度条 [AUTO-TRANSLATED:65ee8a83]
// Drag the progress bar
bool seekTo(uint32_t stamp);
// 暂停
// 暂停 [AUTO-TRANSLATED:ffd21ae7]
// Pause
bool pause(bool pause);
// 倍数播放
// 倍数播放 [AUTO-TRANSLATED:a5e3c1c9]
// Playback speed
bool speed(float speed);
// 关闭该流
// 关闭该流 [AUTO-TRANSLATED:b3867b98]
// Close the stream
bool close(bool force);
// 该流观看人数变化
// 该流观看人数变化 [AUTO-TRANSLATED:8e583993]
// The number of viewers of this stream changes
void onReaderChanged(int size);
// 开启或关闭录制
// 开启或关闭录制 [AUTO-TRANSLATED:3817e390]
// Turn recording on or off
bool setupRecord(Recorder::type type, bool start, const std::string &custom_path, size_t max_second);
// 获取录制状态
// 获取录制状态 [AUTO-TRANSLATED:a0499880]
// Get recording status
bool isRecording(Recorder::type type);
// 开始发送ps-rtp
// 开始发送ps-rtp [AUTO-TRANSLATED:a51796fa]
// Start sending ps-rtp
void startSendRtp(const MediaSourceEvent::SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb);
// 停止发送ps-rtp
// 停止发送ps-rtp [AUTO-TRANSLATED:952d2b35]
// Stop sending ps-rtp
bool stopSendRtp(const std::string &ssrc);
// 获取丢包率
// 获取丢包率 [AUTO-TRANSLATED:ec61b378]
// Get packet loss rate
float getLossRate(mediakit::TrackType type);
// 获取所在线程
// 获取所在线程 [AUTO-TRANSLATED:75662eb8]
// Get the thread where it is running
toolkit::EventPoller::Ptr getOwnerPoller();
// 获取MultiMediaSourceMuxer对象
// 获取MultiMediaSourceMuxer对象 [AUTO-TRANSLATED:2de96d44]
// Get the MultiMediaSourceMuxer object
std::shared_ptr<MultiMediaSourceMuxer> getMuxer() const;
// 获取RtpProcess对象
// 获取RtpProcess对象 [AUTO-TRANSLATED:c6b7da43]
// Get the RtpProcess object
std::shared_ptr<RtpProcess> getRtpProcess() const;
////////////////static方法查找或生成MediaSource////////////////
// //////////////static方法查找或生成MediaSource//////////////// [AUTO-TRANSLATED:c3950036]
// //////////////static methods, find or generate MediaSource////////////////
// 同步查找流
// 同步查找流 [AUTO-TRANSLATED:97048f1e]
// Synchronously find the stream
static Ptr find(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &id, bool from_mp4 = false);
static Ptr find(const MediaInfo &info, bool from_mp4 = false) {
return find(info.schema, info.vhost, info.app, info.stream, from_mp4);
}
// 忽略schema同步查找流可能返回rtmp/rtsp/hls类型
// 忽略schema同步查找流可能返回rtmp/rtsp/hls类型 [AUTO-TRANSLATED:8c061cac]
// Ignore schema, synchronously find the stream, may return rtmp/rtsp/hls type
static Ptr find(const std::string &vhost, const std::string &app, const std::string &stream_id, bool from_mp4 = false);
// 异步查找流
// 异步查找流 [AUTO-TRANSLATED:4decf738]
// Asynchronously find the stream
static void findAsync(const MediaInfo &info, const std::shared_ptr<toolkit::Session> &session, const std::function<void(const Ptr &src)> &cb);
// 遍历所有流
// 遍历所有流 [AUTO-TRANSLATED:a39b2399]
// Traverse all streams
static void for_each_media(const std::function<void(const Ptr &src)> &cb, const std::string &schema = "", const std::string &vhost = "", const std::string &app = "", const std::string &stream = "");
// 从mp4文件生成MediaSource
// 从mp4文件生成MediaSource [AUTO-TRANSLATED:7df9762e]
// Generate MediaSource from mp4 file
static MediaSource::Ptr createFromMP4(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &stream, const std::string &file_path = "", bool check_app = true);
protected:
//媒体注册
// 媒体注册 [AUTO-TRANSLATED:dbf5c730]
// Media registration
void regist();
private:
// 媒体注销
// 媒体注销 [AUTO-TRANSLATED:06a0630a]
// Media unregistration
bool unregist();
// 触发媒体事件
// 触发媒体事件 [AUTO-TRANSLATED:0c2f9ae6]
// Trigger media events
void emitEvent(bool regist);
protected:
@ -468,7 +578,8 @@ private:
toolkit::Ticker _ticker;
std::string _schema;
std::weak_ptr<MediaSourceEvent> _listener;
// 对象个数统计
// 对象个数统计 [AUTO-TRANSLATED:f4a012d0]
// Object count statistics
toolkit::ObjectStatistic<MediaSource> _statistic;
};

View File

@ -44,7 +44,8 @@ public:
class FramePacedSender : public FrameWriterInterface, public std::enable_shared_from_this<FramePacedSender> {
public:
using OnFrame = std::function<void(const Frame::Ptr &frame)>;
// 最小缓存100ms数据
// 最小缓存100ms数据 [AUTO-TRANSLATED:7b2fcb0d]
// Minimum cache 100ms data
static constexpr auto kMinCacheMS = 100;
FramePacedSender(uint32_t paced_sender_ms, OnFrame cb) {
@ -82,21 +83,25 @@ private:
while (!_cache.empty()) {
auto &front = _cache.front();
if (getCurrentStamp() < front.first) {
// 还没到消费时间
// 还没到消费时间 [AUTO-TRANSLATED:09fb4c3d]
// Not yet time to consume
break;
}
// 时间到了该消费frame了
// 时间到了该消费frame了 [AUTO-TRANSLATED:2f007931]
// Time is up, it's time to consume the frame
_cb(front.second);
_cache.pop_front();
}
if (_cache.empty() && dst) {
// 消费太快,需要增加缓存大小
// 消费太快,需要增加缓存大小 [AUTO-TRANSLATED:c05bfbcd]
// Consumption is too fast, need to increase cache size
setCurrentStamp(dst);
_cache_ms += kMinCacheMS;
}
// 消费太慢需要强制flush数据
// 消费太慢需要强制flush数据 [AUTO-TRANSLATED:5613625e]
// Consumption is too slow, need to force flush data
if (_cache.size() > 25 * 5) {
WarnL << "Flush frame paced sender cache: " << _cache.size();
while (!_cache.empty()) {
@ -190,7 +195,8 @@ void MultiMediaSourceMuxer::forEachRtpSender(const std::function<void(const std:
MultiMediaSourceMuxer::MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_sec, const ProtocolOption &option): _tuple(tuple) {
if (!option.stream_replace.empty()) {
// 支持在on_publish hook中替换stream_id
// 支持在on_publish hook中替换stream_id [AUTO-TRANSLATED:375eb2ff]
// Support replacing stream_id in on_publish hook
_tuple.stream = option.stream_replace;
}
_poller = EventPollerPool::Instance().getPoller();
@ -227,7 +233,8 @@ MultiMediaSourceMuxer::MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_
_fmp4 = dynamic_pointer_cast<FMP4MediaSourceMuxer>(Recorder::createRecorder(Recorder::type_fmp4, _tuple, option));
}
//音频相关设置
// 音频相关设置 [AUTO-TRANSLATED:6ee58d57]
// Audio related settings
enableAudio(option.enable_audio);
enableMuteAudio(option.add_mute_audio);
}
@ -236,7 +243,8 @@ void MultiMediaSourceMuxer::setMediaListener(const std::weak_ptr<MediaSourceEven
setDelegate(listener);
auto self = shared_from_this();
//拦截事件
// 拦截事件 [AUTO-TRANSLATED:100ca068]
// Intercept events
if (_rtmp) {
_rtmp->setListener(self);
}
@ -294,61 +302,72 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) {
try {
return listener->totalReaderCount(sender);
} catch (MediaSourceEvent::NotImplemented &) {
//listener未重载totalReaderCount
// listener未重载totalReaderCount [AUTO-TRANSLATED:f098007e]
// Listener did not reload totalReaderCount
return totalReaderCount();
}
}
//此函数可能跨线程调用
// 此函数可能跨线程调用 [AUTO-TRANSLATED:e8c5f74d]
// This function may be called across threads
bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) {
CHECK(getOwnerPoller(MediaSource::NullMediaSource())->isCurrentThread(), "Can only call setupRecord in it's owner poller");
onceToken token(nullptr, [&]() {
if (_option.mp4_as_player && type == Recorder::type_mp4) {
//开启关闭mp4录制触发观看人数变化相关事件
// 开启关闭mp4录制触发观看人数变化相关事件 [AUTO-TRANSLATED:b63a8deb]
// Turn on/off mp4 recording, trigger events related to changes in the number of viewers
onReaderChanged(sender, totalReaderCount());
}
});
switch (type) {
case Recorder::type_hls : {
if (start && !_hls) {
//开始录制
// 开始录制 [AUTO-TRANSLATED:36d99250]
// Start recording
_option.hls_save_path = custom_path;
auto hls = dynamic_pointer_cast<HlsRecorder>(makeRecorder(sender, getTracks(), type, _option));
if (hls) {
//设置HlsMediaSource的事件监听器
// 设置HlsMediaSource的事件监听器 [AUTO-TRANSLATED:69990c92]
// Set the event listener for HlsMediaSource
hls->setListener(shared_from_this());
}
_hls = hls;
} else if (!start && _hls) {
//停止录制
// 停止录制 [AUTO-TRANSLATED:3dee9292]
// Stop recording
_hls = nullptr;
}
return true;
}
case Recorder::type_mp4 : {
if (start && !_mp4) {
//开始录制
// 开始录制 [AUTO-TRANSLATED:36d99250]
// Start recording
_option.mp4_save_path = custom_path;
_option.mp4_max_second = max_second;
_mp4 = makeRecorder(sender, getTracks(), type, _option);
} else if (!start && _mp4) {
//停止录制
// 停止录制 [AUTO-TRANSLATED:3dee9292]
// Stop recording
_mp4 = nullptr;
}
return true;
}
case Recorder::type_hls_fmp4: {
if (start && !_hls_fmp4) {
//开始录制
// 开始录制 [AUTO-TRANSLATED:36d99250]
// Start recording
_option.hls_save_path = custom_path;
auto hls = dynamic_pointer_cast<HlsFMP4Recorder>(makeRecorder(sender, getTracks(), type, _option));
if (hls) {
//设置HlsMediaSource的事件监听器
// 设置HlsMediaSource的事件监听器 [AUTO-TRANSLATED:69990c92]
// Set the event listener for HlsMediaSource
hls->setListener(shared_from_this());
}
_hls_fmp4 = hls;
} else if (!start && _hls_fmp4) {
//停止录制
// 停止录制 [AUTO-TRANSLATED:3dee9292]
// Stop recording
_hls_fmp4 = nullptr;
}
return true;
@ -381,7 +400,8 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
}
}
//此函数可能跨线程调用
// 此函数可能跨线程调用 [AUTO-TRANSLATED:e8c5f74d]
// This function may be called across threads
bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type) {
switch (type) {
case Recorder::type_hls: return !!_hls;
@ -408,7 +428,8 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE
rtp_sender->setOnClose([weak_self, ssrc](const toolkit::SockException &ex) {
if (auto strong_self = weak_self.lock()) {
// 可能归属线程发生变更
// 可能归属线程发生变更 [AUTO-TRANSLATED:2b379e30]
// The owning thread may change
strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() {
WarnL << "stream:" << strong_self->shortUrl() << " stop send rtp:" << ssrc << ", reason:" << ex;
strong_self->_rtp_sender.erase(ssrc);
@ -434,7 +455,8 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE
rtp_sender->inputFrame(frame);
});
// 可能归属线程发生变更
// 可能归属线程发生变更 [AUTO-TRANSLATED:2b379e30]
// The owning thread may change
strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() {
if(!ssrc_multi_send) {
strong_self->_rtp_sender.erase(ssrc);
@ -450,12 +472,14 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE
bool MultiMediaSourceMuxer::stopSendRtp(MediaSource &sender, const string &ssrc) {
#if defined(ENABLE_RTPPROXY)
if (ssrc.empty()) {
//关闭全部
// 关闭全部 [AUTO-TRANSLATED:ffaadfda]
// Close all
auto size = _rtp_sender.size();
_rtp_sender.clear();
return size;
}
//关闭特定的
// 关闭特定的 [AUTO-TRANSLATED:2286322a]
// Close specific
return _rtp_sender.erase(ssrc);
#else
return false;
@ -482,7 +506,8 @@ EventPoller::Ptr MultiMediaSourceMuxer::getOwnerPoller(MediaSource &sender) {
}
return ret;
} catch (MediaSourceEvent::NotImplemented &) {
// listener未重载getOwnerPoller
// listener未重载getOwnerPoller [AUTO-TRANSLATED:0ebf2e53]
// Listener did not reload getOwnerPoller
return _poller;
}
}
@ -494,7 +519,8 @@ std::shared_ptr<MultiMediaSourceMuxer> MultiMediaSourceMuxer::getMuxer(MediaSour
bool MultiMediaSourceMuxer::onTrackReady(const Track::Ptr &track) {
auto &stamp = _stamps[track->getIndex()];
if (_dur_sec > 0.01) {
// 点播
// 点播 [AUTO-TRANSLATED:f0b0f74a]
// On-demand
stamp.setPlayBack();
}
@ -592,7 +618,8 @@ void MultiMediaSourceMuxer::createGopCacheIfNeed() {
auto src = std::make_shared<MediaSourceForMuxer>(weak_self.lock());
_ring = std::make_shared<RingType>(1024, [weak_self, src](int size) {
if (auto strong_self = weak_self.lock()) {
// 切换到归属线程
// 切换到归属线程 [AUTO-TRANSLATED:abcf859b]
// Switch to the owning thread
strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() {
strong_self->onReaderChanged(*src, strong_self->totalReaderCount());
});
@ -632,7 +659,8 @@ void MultiMediaSourceMuxer::resetTracks() {
bool MultiMediaSourceMuxer::onTrackFrame(const Frame::Ptr &frame_in) {
auto frame = frame_in;
if (_option.modify_stamp != ProtocolOption::kModifyStampOff) {
// 时间戳不采用原始的绝对时间戳
// 时间戳不采用原始的绝对时间戳 [AUTO-TRANSLATED:8beb3bf7]
// Timestamp does not use the original absolute timestamp
frame = std::make_shared<FrameStamp>(frame, _stamps[frame->getIndex()], _option.modify_stamp);
}
return _paced_sender ? _paced_sender->inputFrame(frame) : onTrackFrame_l(frame);
@ -671,17 +699,20 @@ bool MultiMediaSourceMuxer::onTrackFrame_l(const Frame::Ptr &frame_in) {
ret = _fmp4->inputFrame(frame) ? true : ret;
}
if (_ring) {
// 此场景由于直接转发可能存在切换线程引起的数据被缓存在管道所以需要CacheAbleFrame
// 此场景由于直接转发可能存在切换线程引起的数据被缓存在管道所以需要CacheAbleFrame [AUTO-TRANSLATED:528afbb7]
// In this scenario, due to direct forwarding, there may be data cached in the pipeline due to thread switching, so CacheAbleFrame is needed
frame = Frame::getCacheAbleFrame(frame);
if (frame->getTrackType() == TrackVideo) {
// 视频时遇到第一帧配置帧或关键帧则标记为gop开始处
// 视频时遇到第一帧配置帧或关键帧则标记为gop开始处 [AUTO-TRANSLATED:66247aa8]
// When it is a video, if the first frame configuration frame or key frame is encountered, it is marked as the beginning of the GOP
auto video_key_pos = frame->keyFrame() || frame->configFrame();
_ring->write(frame, video_key_pos && !_video_key_pos);
if (!frame->dropAble()) {
_video_key_pos = video_key_pos;
}
} else {
// 没有视频时设置is_key为true目的是关闭gop缓存
// 没有视频时设置is_key为true目的是关闭gop缓存 [AUTO-TRANSLATED:f3223755]
// When there is no video, set is_key to true to disable gop caching
_ring->write(frame, !haveVideo());
}
}
@ -691,8 +722,10 @@ bool MultiMediaSourceMuxer::onTrackFrame_l(const Frame::Ptr &frame_in) {
bool MultiMediaSourceMuxer::isEnabled(){
GET_CONFIG(uint32_t, stream_none_reader_delay_ms, General::kStreamNoneReaderDelayMS);
if (!_is_enable || _last_check.elapsedTime() > stream_none_reader_delay_ms) {
//无人观看时,每次检查是否真的无人观看
//有人观看时,则延迟一定时间检查一遍是否无人观看了(节省性能)
// 无人观看时,每次检查是否真的无人观看 [AUTO-TRANSLATED:48bc59c6]
// When no one is watching, check each time if there is really no one watching
// 有人观看时,则延迟一定时间检查一遍是否无人观看了(节省性能) [AUTO-TRANSLATED:a7dfddc4]
// When someone is watching, check again after a certain delay to see if no one is watching (save performance)
_is_enable = (_rtmp ? _rtmp->isEnabled() : false) ||
(_rtsp ? _rtsp->isEnabled() : false) ||
(_rtc ? _rtc->isEnabled() : false) ||
@ -704,7 +737,8 @@ bool MultiMediaSourceMuxer::isEnabled(){
_mp4;
if (_is_enable) {
//无人观看时,不刷新计时器,因为无人观看时每次都会检查一遍所以刷新计数器无意义且浪费cpu
// 无人观看时,不刷新计时器,因为无人观看时每次都会检查一遍所以刷新计数器无意义且浪费cpu [AUTO-TRANSLATED:03ab47cf]
// When no one is watching, do not refresh the timer, because each time no one is watching, it will be checked, so refreshing the counter is meaningless and wastes cpu
_last_check.resetTime();
}
}

View File

@ -41,33 +41,54 @@ public:
/**
*
* @param listener
* Set event listener
* @param listener Listener
* [AUTO-TRANSLATED:d829419b]
*/
void setMediaListener(const std::weak_ptr<MediaSourceEvent> &listener);
/**
* Track就绪事件监听器
* @param listener
* Set Track ready event listener
* @param listener Event listener
* [AUTO-TRANSLATED:64262ac5]
*/
void setTrackListener(const std::weak_ptr<Listener> &listener);
/**
*
* Return the total number of consumers
* [AUTO-TRANSLATED:5eaac131]
*/
int totalReaderCount() const;
/**
* ()
* Determine whether it is effective (whether it is being converted to another protocol)
* [AUTO-TRANSLATED:ca92165c]
*/
bool isEnabled();
/**
* MediaSource时间戳
* @param stamp
* Set MediaSource timestamp
* @param stamp Timestamp
* [AUTO-TRANSLATED:a75cc2fa]
*/
void setTimeStamp(uint32_t stamp);
/**
* track
* Reset track
* [AUTO-TRANSLATED:95dc0b4f]
*/
void resetTracks() override;
@ -77,6 +98,11 @@ public:
*
* @param sender
* @return
* Total number of viewers
* @param sender Event sender
* @return Total number of viewers
* [AUTO-TRANSLATED:f4d7146c]
*/
int totalReaderCount(MediaSource &sender) override;
@ -86,6 +112,13 @@ public:
* @param start
* @param custom_path
* @return
* Set recording status
* @param type Recording type
* @param start Start or stop
* @param custom_path Specify a custom path when recording is enabled
* @return Whether the setting is successful
* [AUTO-TRANSLATED:cb1fd8a9]
*/
bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) override;
@ -93,6 +126,11 @@ public:
*
* @param type
* @return
* Get recording status
* @param type Recording type
* @return Recording status
* [AUTO-TRANSLATED:798afa71]
*/
bool isRecording(MediaSource &sender, Recorder::type type) override;
@ -103,12 +141,24 @@ public:
* @param ssrc rtp的ssrc
* @param is_udp udp
* @param cb
* Start sending ps-rtp stream
* @param dst_url Target ip or domain name
* @param dst_port Target port
* @param ssrc rtp's ssrc
* @param is_udp Whether it is udp
* @param cb Start success or failure callback
* [AUTO-TRANSLATED:620416c2]
*/
void startSendRtp(MediaSource &sender, const MediaSourceEvent::SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb) override;
/**
* ps-rtp发送
* @return
* Stop ps-rtp sending
* @return Whether it is successful
* [AUTO-TRANSLATED:b91e2055]
*/
bool stopSendRtp(MediaSource &sender, const std::string &ssrc) override;
@ -116,16 +166,27 @@ public:
* Track
* @param trackReady track
* @return Track
* Get all Tracks
* @param trackReady Whether to filter out unready tracks
* @return All Tracks
* [AUTO-TRANSLATED:53755f5d]
*/
std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const override;
/**
* 线
* Get the thread it belongs to
* [AUTO-TRANSLATED:a4dc847e]
*/
toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override;
/**
*
* Get this object
* [AUTO-TRANSLATED:5e119bb3]
*/
std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) const override;
@ -142,17 +203,29 @@ protected:
* track已经准备好ready()true
* sps pps等相关信息了
* @param track
* A certain track is ready, its ready() status returns true,
* This means that you can get information such as sps pps, etc.
* @param track
* [AUTO-TRANSLATED:05659d48]
*/
bool onTrackReady(const Track::Ptr & track) override;
/**
* Track已经准备好
* All Tracks are ready,
* [AUTO-TRANSLATED:c54d02e2]
*/
void onAllTrackReady() override;
/**
* Track输出frameonAllTrackReady触发后才会调用此方法
* @param frame
* A certain Track outputs a frame, this method will be called after onAllTrackReady is triggered
* @param frame
* [AUTO-TRANSLATED:debbd247]
*/
bool onTrackFrame(const Frame::Ptr &frame) override;
bool onTrackFrame_l(const Frame::Ptr &frame);
@ -183,7 +256,8 @@ private:
toolkit::EventPoller::Ptr _poller;
RingType::Ptr _ring;
//对象个数统计
// 对象个数统计 [AUTO-TRANSLATED:3b43e8c2]
// Object count statistics
toolkit::ObjectStatistic<MultiMediaSourceMuxer> _statistic;
};

View File

@ -15,20 +15,26 @@
#include "Util/List.h"
namespace mediakit {
/// 缓存刷新策略类
// / 缓存刷新策略类 [AUTO-TRANSLATED:bd941d15]
// / Cache refresh strategy class
class FlushPolicy {
public:
bool isFlushAble(bool is_video, bool is_key, uint64_t new_stamp, size_t cache_size);
private:
// 音视频的最后时间戳
// 音视频的最后时间戳 [AUTO-TRANSLATED:957d18ed]
// Last timestamp of audio and video
uint64_t _last_stamp[2] = { 0, 0 };
};
/// 合并写缓存模板
/// \tparam packet 包类型
/// \tparam policy 刷新缓存策略
/// \tparam packet_list 包缓存类型
// / 合并写缓存模板 [AUTO-TRANSLATED:25cde944]
// / Merge write cache template
// / \tparam packet 包类型 [AUTO-TRANSLATED:43085d9b]
// / \tparam packet Packet type
// / \tparam policy 刷新缓存策略 [AUTO-TRANSLATED:c5ac29a7]
// / \tparam policy Refresh cache strategy
// / \tparam packet_list 包缓存类型 [AUTO-TRANSLATED:a434e7fe]
// / \tparam packet_list Packet cache type
template<typename packet, typename policy = FlushPolicy, typename packet_list = toolkit::List<std::shared_ptr<packet> > >
class PacketCache {
public:
@ -42,7 +48,8 @@ public:
flush();
}
//追加数据到最后
// 追加数据到最后 [AUTO-TRANSLATED:e24ccfb6]
// Append data to the end
_cache->emplace_back(std::move(pkt));
if (key_pos) {
_key_pos = key_pos;
@ -70,10 +77,14 @@ public:
private:
bool flushImmediatelyWhenCloseMerge() {
// 一般的协议关闭合并写时立即刷新缓存这样可以减少一帧的延时但是rtp例外
// 因为rtp的包很小一个RtpPacket包中也不是完整的一帧图像所以在关闭合并写时
// 还是有必要缓冲一帧的rtp(也就是时间戳相同的rtp)再输出,这样虽然会增加一帧的延时
// 但是却对性能提升很大,这样做还是比较划算的
// 一般的协议关闭合并写时立即刷新缓存这样可以减少一帧的延时但是rtp例外 [AUTO-TRANSLATED:54eba701]
// Generally, when the protocol closes the merge write, the cache is refreshed immediately, which can reduce the delay of one frame, but RTP is an exception.
// 因为rtp的包很小一个RtpPacket包中也不是完整的一帧图像所以在关闭合并写时 [AUTO-TRANSLATED:b219082d]
// Because the RTP packet is very small, and a RtpPacket does not contain a complete frame of image, so when closing the merge write,
// 还是有必要缓冲一帧的rtp(也就是时间戳相同的rtp)再输出,这样虽然会增加一帧的延时 [AUTO-TRANSLATED:27c7ee8b]
// It is still necessary to buffer one frame of RTP (that is, RTP with the same timestamp) before outputting. Although this will increase the delay of one frame,
// 但是却对性能提升很大,这样做还是比较划算的 [AUTO-TRANSLATED:80eab719]
// But it greatly improves performance, so it is still worthwhile to do so.
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
GET_CONFIG(int, rtspLowLatency, Rtsp::kLowLatency);

View File

@ -17,11 +17,14 @@
namespace mediakit {
// 从字符串中提取子字符串
// 从字符串中提取子字符串 [AUTO-TRANSLATED:8493b6a5]
// Extract substring from string
std::string findSubString(const char *buf, const char *start, const char *end, size_t buf_size = 0);
// 把url解析为主机地址和端口号,兼容ipv4/ipv6/dns
// 把url解析为主机地址和端口号,兼容ipv4/ipv6/dns [AUTO-TRANSLATED:0cfa4a6c]
// Parse url to host address and port number, compatible with ipv4/ipv6/dns
void splitUrl(const std::string &url, std::string &host, uint16_t &port);
// 解析proxy url,仅支持http
// 解析proxy url,仅支持http [AUTO-TRANSLATED:194b49d7]
// Parse proxy url, only supports http
void parseProxyUrl(const std::string &proxy_url, std::string &proxy_host, uint16_t &proxy_port, std::string &proxy_auth);
struct StrCaseCompare {
@ -55,53 +58,70 @@ public:
}
};
// rtsp/http/sip解析类
// rtsp/http/sip解析类 [AUTO-TRANSLATED:188ca500]
// rtsp/http/sip parsing class
class Parser {
public:
// 解析http/rtsp/sip请求需要确保buf以\0结尾
// 解析http/rtsp/sip请求需要确保buf以\0结尾 [AUTO-TRANSLATED:552953af]
// Parse http/rtsp/sip request, ensure buf ends with \0
void parse(const char *buf, size_t size);
// 获取命令字如GET/POST
// 获取命令字如GET/POST [AUTO-TRANSLATED:34750f3d]
// Get command word, such as GET/POST
const std::string &method() const;
// 请求时获取中间url不包含?后面的参数
// 请求时获取中间url不包含?后面的参数 [AUTO-TRANSLATED:c259f1ed]
// When requesting, get the middle url, excluding the parameters after ?
const std::string &url() const;
// 回复时获取状态码如200/404
// 回复时获取状态码如200/404 [AUTO-TRANSLATED:ac3f8ed4]
// When replying, get the status code, such as 200/404
const std::string &status() const;
// 获取中间url包含?后面的参数
// 获取中间url包含?后面的参数 [AUTO-TRANSLATED:ca1fec1a]
// Get the middle url, including the parameters after ?
std::string fullUrl() const;
// 请求时获取协议名如HTTP/1.1
// 请求时获取协议名如HTTP/1.1 [AUTO-TRANSLATED:7410fed6]
// When requesting, get the protocol name, such as HTTP/1.1
const std::string &protocol() const;
// 回复时,获取状态字符串,如 OK/Not Found
// 回复时,获取状态字符串,如 OK/Not Found [AUTO-TRANSLATED:d245247a]
// When replying, get the status string, such as OK/Not Found
const std::string &statusStr() const;
// 根据header key名获取请求header value值
// 根据header key名获取请求header value值 [AUTO-TRANSLATED:5cbc9ac7]
// Get the request header value according to the header key name
const std::string &operator[](const char *name) const;
// 获取http body或sdp
// 获取http body或sdp [AUTO-TRANSLATED:d6fd1803]
// Get http body or sdp
const std::string &content() const;
// 清空,为了重用
// 清空,为了重用 [AUTO-TRANSLATED:cb7a16dd]
// Clear, for reuse
void clear();
// 获取?后面的参数
// 获取?后面的参数 [AUTO-TRANSLATED:4ada90e1]
// Get the parameters after ?
const std::string &params() const;
// 重新设置url
// 重新设置url [AUTO-TRANSLATED:4829ba8e]
// Reset url
void setUrl(std::string url);
// 重新设置content
// 重新设置content [AUTO-TRANSLATED:ac8fc8c0]
// Reset content
void setContent(std::string content);
// 获取header列表
// 获取header列表 [AUTO-TRANSLATED:90d90b03]
// Get header list
StrCaseMap &getHeader() const;
// 获取url参数列表
// 获取url参数列表 [AUTO-TRANSLATED:da1df48a]
// Get url parameter list
StrCaseMap &getUrlArgs() const;
// 解析?后面的参数
// 解析?后面的参数 [AUTO-TRANSLATED:38692051]
// Parse the parameters after ?
static StrCaseMap parseArgs(const std::string &str, const char *pair_delim = "&", const char *key_delim = "=");
static std::string mergeUrl(const std::string &base_url, const std::string &path);
@ -116,7 +136,8 @@ private:
mutable StrCaseMap _url_args;
};
// 解析rtsp url的工具类
// 解析rtsp url的工具类 [AUTO-TRANSLATED:0d31ae01]
// Utility class for parsing rtsp url
class RtspUrl {
public:
bool _is_ssl;

View File

@ -10,7 +10,8 @@
#include "Stamp.h"
// 时间戳最大允许跳变3秒主要是防止网络抖动导致的跳变
// 时间戳最大允许跳变3秒主要是防止网络抖动导致的跳变 [AUTO-TRANSLATED:144154de]
// Timestamp maximum allowable jump is 3 seconds, mainly to prevent network jitter caused by the jump
#define MAX_DELTA_STAMP (3 * 1000)
#define STAMP_LOOP_DELTA (60 * 1000)
#define MAX_CTS 500
@ -21,7 +22,8 @@ using namespace toolkit;
namespace mediakit {
DeltaStamp::DeltaStamp() {
// 时间戳最大允许跳跃300ms
// 时间戳最大允许跳跃300ms [AUTO-TRANSLATED:2458e61f]
// Timestamp maximum allowable jump is 300ms
_max_delta = 300;
}
@ -36,7 +38,8 @@ int64_t DeltaStamp::relativeStamp() {
int64_t DeltaStamp::deltaStamp(int64_t stamp, bool enable_rollback) {
if (!_last_stamp) {
// 第一次计算时间戳增量,时间戳增量为0
// 第一次计算时间戳增量,时间戳增量为0 [AUTO-TRANSLATED:32944bd3]
// Calculate the timestamp increment for the first time, the timestamp increment is 0
if (stamp) {
_last_stamp = stamp;
}
@ -45,9 +48,11 @@ int64_t DeltaStamp::deltaStamp(int64_t stamp, bool enable_rollback) {
int64_t ret = stamp - _last_stamp;
if (ret >= 0) {
// 时间戳增量为正,返回之
// 时间戳增量为正,返回之 [AUTO-TRANSLATED:308dfb22]
// The timestamp increment is positive, return it
_last_stamp = stamp;
// 在直播情况下时间戳增量不得大于MAX_DELTA_STAMP否则强制相对时间戳加1
// 在直播情况下时间戳增量不得大于MAX_DELTA_STAMP否则强制相对时间戳加1 [AUTO-TRANSLATED:c78c40d3]
// In the live broadcast case, the timestamp increment must not be greater than MAX_DELTA_STAMP, otherwise the relative timestamp is forced to add 1
if (ret > _max_delta) {
needSync();
return 1;
@ -55,10 +60,12 @@ int64_t DeltaStamp::deltaStamp(int64_t stamp, bool enable_rollback) {
return ret;
}
// 时间戳增量为负,说明时间戳回环了或回退了
// 时间戳增量为负,说明时间戳回环了或回退了 [AUTO-TRANSLATED:fd825d50]
// The timestamp increment is negative, indicating that the timestamp has looped or retreated
_last_stamp = stamp;
if (!enable_rollback || -ret > _max_delta) {
// 不允许回退或者回退太多了, 强制时间戳加1
// 不允许回退或者回退太多了, 强制时间戳加1 [AUTO-TRANSLATED:152f5ffa]
// Not allowed to retreat or retreat too much, force the timestamp to add 1
needSync();
return 1;
}
@ -86,16 +93,19 @@ void Stamp::enableRollback(bool flag) {
_enable_rollback = flag;
}
// 限制dts回退
// 限制dts回退 [AUTO-TRANSLATED:6bc53b31]
// Limit dts retreat
void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, bool modifyStamp) {
revise_l(dts, pts, dts_out, pts_out, modifyStamp);
if (_playback) {
// 回放允许时间戳回退
// 回放允许时间戳回退 [AUTO-TRANSLATED:5d822118]
// Playback allows timestamp rollback
return;
}
if (dts_out < _last_dts_out) {
// WarnL << "dts回退:" << dts_out << " < " << _last_dts_out;
// WarnL << "dts回退:" << dts_out << " < " << _last_dts_out; [AUTO-TRANSLATED:c36316f5]
// WarnL << "dts rollback:" << dts_out << " < " << _last_dts_out;
dts_out = _last_dts_out;
pts_out = _last_pts_out;
return;
@ -104,27 +114,34 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,
_last_pts_out = pts_out;
}
// 音视频时间戳同步
// 音视频时间戳同步 [AUTO-TRANSLATED:58f1e95c]
// Audio and video timestamp synchronization
void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, bool modifyStamp) {
revise_l2(dts, pts, dts_out, pts_out, modifyStamp);
if (!_sync_master || modifyStamp || _playback) {
// 自动生成时间戳或回放或同步完毕
// 自动生成时间戳或回放或同步完毕 [AUTO-TRANSLATED:a0b8f8bd]
// Automatically generate timestamps or playback or synchronization is complete
return;
}
// 需要同步时间戳
// 需要同步时间戳 [AUTO-TRANSLATED:af93e8f8]
// Need to synchronize timestamps
if (_sync_master && _sync_master->_last_dts_in && (_need_sync || _sync_master->_need_sync)) {
// 音视频dts当前时间差
// 音视频dts当前时间差 [AUTO-TRANSLATED:716468a6]
// Audio and video dts current time difference
int64_t dts_diff = _last_dts_in - _sync_master->_last_dts_in;
if (ABS(dts_diff) < 5000) {
// 如果绝对时间戳小于5秒那么说明他们的起始时间戳是一致的那么强制同步
// 如果绝对时间戳小于5秒那么说明他们的起始时间戳是一致的那么强制同步 [AUTO-TRANSLATED:5d11ef6a]
// If the absolute timestamp is less than 5 seconds, then it means that their starting timestamps are consistent, then force synchronization
auto target_stamp = _sync_master->_relative_stamp + dts_diff;
if (target_stamp > _relative_stamp || _enable_rollback) {
// 强制同步后,时间戳增加跳跃了,或允许回退
// 强制同步后,时间戳增加跳跃了,或允许回退 [AUTO-TRANSLATED:805424a9]
// After forced synchronization, the timestamp increases jump, or allows rollback
TraceL << "Relative stamp changed: " << _relative_stamp << " -> " << target_stamp;
_relative_stamp = target_stamp;
} else {
// 不允许回退, 则让另外一个Track的时间戳增长
// 不允许回退, 则让另外一个Track的时间戳增长 [AUTO-TRANSLATED:428e8ce2]
// Not allowed to rollback, then let the timestamp of the other Track increase
target_stamp = _relative_stamp - dts_diff;
TraceL << "Relative stamp changed: " << _sync_master->_relative_stamp << " -> " << target_stamp;
_sync_master->_relative_stamp = target_stamp;
@ -135,15 +152,18 @@ void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_ou
}
}
// 求取相对时间戳
// 求取相对时间戳 [AUTO-TRANSLATED:122da805]
// Obtain the relative timestamp
void Stamp::revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, bool modifyStamp) {
if (!pts) {
// 没有播放时间戳,使其赋值为解码时间戳
// 没有播放时间戳,使其赋值为解码时间戳 [AUTO-TRANSLATED:9ee71899]
// There is no playback timestamp, set it to the decoding timestamp
pts = dts;
}
if (_playback) {
// 这是点播
// 这是点播 [AUTO-TRANSLATED:f11fd173]
// This is on-demand
dts_out = dts;
pts_out = pts;
_relative_stamp = dts_out;
@ -151,13 +171,16 @@ void Stamp::revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_o
return;
}
// pts和dts的差值
// pts和dts的差值 [AUTO-TRANSLATED:3b145073]
// The difference between pts and dts
int64_t pts_dts_diff = pts - dts;
if (_last_dts_in != dts) {
// 时间戳发生变更
// 时间戳发生变更 [AUTO-TRANSLATED:7344315c]
// Timestamp changed
if (modifyStamp) {
// 内部自己生产时间戳
// 内部自己生产时间戳 [AUTO-TRANSLATED:fae889e0]
// Internal production of timestamps
_relative_stamp = _ticker.elapsedTime();
} else {
_relative_stamp += deltaStamp(dts, _enable_rollback);
@ -166,9 +189,11 @@ void Stamp::revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_o
}
dts_out = _relative_stamp;
//////////////以下是播放时间戳的计算//////////////////
// ////////////以下是播放时间戳的计算////////////////// [AUTO-TRANSLATED:6c4a56a7]
// ////////////The following is the calculation of the playback timestamp//////////////////
if (ABS(pts_dts_diff) > MAX_CTS) {
// 如果差值太大,则认为由于回环导致时间戳错乱了
// 如果差值太大,则认为由于回环导致时间戳错乱了 [AUTO-TRANSLATED:1a11b5f3]
// If the difference is too large, it is considered that the timestamp is messed up due to looping
pts_dts_diff = 0;
}
@ -186,89 +211,115 @@ int64_t Stamp::getRelativeStamp() const {
bool DtsGenerator::getDts(uint64_t pts, uint64_t &dts) {
bool ret = false;
if (pts == _last_pts) {
// pts未变说明dts也不会变返回上次dts
// pts未变说明dts也不会变返回上次dts [AUTO-TRANSLATED:dc0972e0]
// pts does not change, indicating that dts will not change, return the last dts
if (_last_dts) {
dts = _last_dts;
ret = true;
}
} else {
// pts变了尝试计算dts
// pts变了尝试计算dts [AUTO-TRANSLATED:f527d0f6]
// pts changed, try to calculate dts
ret = getDts_l(pts, dts);
if (ret) {
// 获取到了dts保存本次结果
// 获取到了dts保存本次结果 [AUTO-TRANSLATED:d6a5ce6d]
// Get the dts, save the current result
_last_dts = dts;
}
}
if (!ret) {
// pts排序列队长度还不知道也就是不知道有没有B帧
// 那么先强制dts == pts这样可能导致有B帧的情况下起始画面有几帧回退
// pts排序列队长度还不知道也就是不知道有没有B帧 [AUTO-TRANSLATED:e5ad4327]
// The pts sorting queue length is not yet known, that is, it is not known whether there is a B frame,
// 那么先强制dts == pts这样可能导致有B帧的情况下起始画面有几帧回退 [AUTO-TRANSLATED:74c97de1]
// Then force dts == pts first, which may cause the starting picture to have a few frames rollback in the case of B frames
dts = pts;
}
// 记录上次pts
// 记录上次pts [AUTO-TRANSLATED:4ecd474b]
// Record the last pts
_last_pts = pts;
return ret;
}
// 该算法核心思想是对pts进行排序排序好的pts就是dts。
// 排序有一定的滞后性,那么需要加上排序导致的时间戳偏移量
// 该算法核心思想是对pts进行排序排序好的pts就是dts。 [AUTO-TRANSLATED:efb36e04]
// The core idea of this algorithm is to sort the pts, and the sorted pts is the dts.
// 排序有一定的滞后性,那么需要加上排序导致的时间戳偏移量 [AUTO-TRANSLATED:5ada843a]
// Sorting has a certain lag, so it is necessary to add the timestamp offset caused by sorting
bool DtsGenerator::getDts_l(uint64_t pts, uint64_t &dts) {
if (_sorter_max_size == 1) {
// 没有B帧dts就等于pts
// 没有B帧dts就等于pts [AUTO-TRANSLATED:9cfae4ea]
// There is no B frame, dts is equal to pts
dts = pts;
return true;
}
if (!_sorter_max_size) {
// 尚未计算出pts排序列队长度(也就是P帧间B帧个数)
// 尚未计算出pts排序列队长度(也就是P帧间B帧个数) [AUTO-TRANSLATED:8bedb754]
// The length of the pts sorting queue (that is, the number of B frames between P frames) has not been calculated yet
if (pts > _last_max_pts) {
// pts时间戳增加了那么说明这帧画面不是B帧(说明是P帧或关键帧)
// pts时间戳增加了那么说明这帧画面不是B帧(说明是P帧或关键帧) [AUTO-TRANSLATED:4c5ef2b8]
// The pts timestamp has increased, which means that this frame is not a B frame (it means it is a P frame or a key frame)
if (_frames_since_last_max_pts && _count_sorter_max_size++ > 0) {
// 已经出现多次非B帧的情况那么我们就能知道P帧间B帧的个数
// 已经出现多次非B帧的情况那么我们就能知道P帧间B帧的个数 [AUTO-TRANSLATED:fd747b3c]
// There have been multiple non-B frames, so we can know the number of B frames between P frames
_sorter_max_size = _frames_since_last_max_pts;
// 我们记录P帧间时间间隔(也就是多个B帧时间戳增量累计)
// 我们记录P帧间时间间隔(也就是多个B帧时间戳增量累计) [AUTO-TRANSLATED:66c0cc14]
// We record the time interval between P frames (that is, the cumulative increment of multiple B frame timestamps)
_dts_pts_offset = (pts - _last_max_pts);
// 除以2防止dts大于pts
// 除以2防止dts大于pts [AUTO-TRANSLATED:52b5b3ab]
// Divide by 2 to prevent dts from being greater than pts
_dts_pts_offset /= 2;
}
// 遇到P帧或关键帧连续B帧计数清零
// 遇到P帧或关键帧连续B帧计数清零 [AUTO-TRANSLATED:537bb54d]
// When encountering a P frame or a key frame, the continuous B frame count is cleared
_frames_since_last_max_pts = 0;
// 记录上次非B帧的pts时间戳(同时也是dts)用于统计连续B帧时间戳增量
// 记录上次非B帧的pts时间戳(同时也是dts)用于统计连续B帧时间戳增量 [AUTO-TRANSLATED:194f8cdb]
// Record the pts timestamp of the last non-B frame (which is also dts), used to count the continuous B frame timestamp increment
_last_max_pts = pts;
}
// 如果pts时间戳小于上一个P帧那么断定这个是B帧,我们记录B帧连续个数
// 如果pts时间戳小于上一个P帧那么断定这个是B帧,我们记录B帧连续个数 [AUTO-TRANSLATED:1a7e33e2]
// If the pts timestamp is less than the previous P frame, then it is determined that this is a B frame, and we record the number of consecutive B frames
++_frames_since_last_max_pts;
}
// pts放入排序缓存列队缓存列队最大等于连续B帧个数
// pts放入排序缓存列队缓存列队最大等于连续B帧个数 [AUTO-TRANSLATED:ff598a97]
// Put pts into the sorting cache queue, the maximum cache queue is equal to the number of consecutive B frames
_pts_sorter.emplace(pts);
if (_sorter_max_size && _pts_sorter.size() > _sorter_max_size) {
// 如果启用了pts排序(意味着存在B帧)并且pts排序缓存列队长度大于连续B帧个数
// 意味着后续的pts都会比最早的pts大那么说明可以取出最早的pts了这个pts将当做该帧的dts基准
// 如果启用了pts排序(意味着存在B帧)并且pts排序缓存列队长度大于连续B帧个数 [AUTO-TRANSLATED:002c0d03]
// If pts sorting is enabled (meaning there are B frames), and the length of the pts sorting cache queue is greater than the number of consecutive B frames,
// 意味着后续的pts都会比最早的pts大那么说明可以取出最早的pts了这个pts将当做该帧的dts基准 [AUTO-TRANSLATED:86b8f679]
// It means that the subsequent pts will be larger than the earliest pts, which means that the earliest pts can be taken out, and this pts will be used as the dts baseline for this frame
auto it = _pts_sorter.begin();
// 由于该pts是前面偏移了个_sorter_max_size帧的pts(也就是那帧画面的dts),
// 那么我们加上时间戳偏移量基本等于该帧的dts
// 由于该pts是前面偏移了个_sorter_max_size帧的pts(也就是那帧画面的dts), [AUTO-TRANSLATED:eb3657aa]
// Since this pts is the pts of the previous _sorter_max_size frames (that is, the dts of that frame),
// 那么我们加上时间戳偏移量基本等于该帧的dts [AUTO-TRANSLATED:245aac6e]
// Then we add the timestamp offset, which is basically equal to the dts of this frame
dts = *it + _dts_pts_offset;
if (dts > pts) {
// dts不能大于pts(基本不可能到达这个逻辑)
// dts不能大于pts(基本不可能到达这个逻辑) [AUTO-TRANSLATED:847c4531]
// dts cannot be greater than pts (it is basically impossible to reach this logic)
dts = pts;
}
// pts排序缓存出列
// pts排序缓存出列 [AUTO-TRANSLATED:8b580191]
// pts sorting cache dequeue
_pts_sorter.erase(it);
return true;
}
// 排序缓存尚未满
// 排序缓存尚未满 [AUTO-TRANSLATED:3f502460]
// The sorting cache is not full yet
return false;
}
void NtpStamp::setNtpStamp(uint32_t rtp_stamp, uint64_t ntp_stamp_ms) {
if (!ntp_stamp_ms || !rtp_stamp) {
// 实测发现有些rtsp服务器发送的rtp时间戳和ntp时间戳一直为0
// 实测发现有些rtsp服务器发送的rtp时间戳和ntp时间戳一直为0 [AUTO-TRANSLATED:d3c200fc]
// It has been found that some rtsp servers send rtp timestamps and ntp timestamps that are always 0
WarnL << "Invalid sender report rtcp, ntp_stamp_ms = " << ntp_stamp_ms << ", rtp_stamp = " << rtp_stamp;
return;
}
@ -289,48 +340,59 @@ uint64_t NtpStamp::getNtpStamp(uint32_t rtp_stamp, uint32_t sample_rate) {
uint64_t NtpStamp::getNtpStampUS(uint32_t rtp_stamp, uint32_t sample_rate) {
if (!_last_ntp_stamp_us) {
// 尚未收到sender report rtcp包那么赋值为本地系统时间戳吧
// 尚未收到sender report rtcp包那么赋值为本地系统时间戳吧 [AUTO-TRANSLATED:c9056069]
// The sender report rtcp packet has not been received yet, so assign it to the local system timestamp
update(rtp_stamp, getCurrentMicrosecond(true));
}
// rtp时间戳正增长
// rtp时间戳正增长 [AUTO-TRANSLATED:4d3c87d1]
// The rtp timestamp is increasing
if (rtp_stamp >= _last_rtp_stamp) {
auto diff_us = static_cast<int64_t>((rtp_stamp - _last_rtp_stamp) / (sample_rate / 1000000.0f));
if (diff_us < MAX_DELTA_STAMP * 1000) {
// 时间戳正常增长
// 时间戳正常增长 [AUTO-TRANSLATED:db60e84a]
// The timestamp is increasing normally
update(rtp_stamp, _last_ntp_stamp_us + diff_us);
return _last_ntp_stamp_us;
}
// 时间戳大幅跳跃
// 时间戳大幅跳跃 [AUTO-TRANSLATED:c8585a51]
// The timestamp jumps significantly
uint64_t loop_delta_hz = STAMP_LOOP_DELTA * sample_rate / 1000;
if (_last_rtp_stamp < loop_delta_hz && rtp_stamp > UINT32_MAX - loop_delta_hz) {
// 应该是rtp时间戳溢出+乱序
// 应该是rtp时间戳溢出+乱序 [AUTO-TRANSLATED:13529fd6]
// It should be rtp timestamp overflow + out of order
uint64_t max_rtp_us = uint64_t(UINT32_MAX) * 1000000 / sample_rate;
return _last_ntp_stamp_us + diff_us - max_rtp_us;
}
// 不明原因的时间戳大幅跳跃,直接返回上次值
// 不明原因的时间戳大幅跳跃,直接返回上次值 [AUTO-TRANSLATED:952b769c]
// The timestamp jumps significantly for unknown reasons, directly return the last value
WarnL << "rtp stamp abnormal increased:" << _last_rtp_stamp << " -> " << rtp_stamp;
update(rtp_stamp, _last_ntp_stamp_us);
return _last_ntp_stamp_us;
}
// rtp时间戳负增长
// rtp时间戳负增长 [AUTO-TRANSLATED:54a7f797]
// The rtp timestamp is decreasing
auto diff_us = static_cast<int64_t>((_last_rtp_stamp - rtp_stamp) / (sample_rate / 1000000.0f));
if (diff_us < MAX_DELTA_STAMP * 1000) {
// 正常范围的时间戳回退说明收到rtp乱序了
// 正常范围的时间戳回退说明收到rtp乱序了 [AUTO-TRANSLATED:f691d5bf]
// The timestamp retreats within the normal range, indicating that the rtp is out of order
return _last_ntp_stamp_us - diff_us;
}
// 时间戳大幅度回退
// 时间戳大幅度回退 [AUTO-TRANSLATED:0ad69100]
// The timestamp retreats significantly
uint64_t loop_delta_hz = STAMP_LOOP_DELTA * sample_rate / 1000;
if (rtp_stamp < loop_delta_hz && _last_rtp_stamp > UINT32_MAX - loop_delta_hz) {
// 确定是时间戳溢出
// 确定是时间戳溢出 [AUTO-TRANSLATED:322274c3]
// Determine if it is a timestamp overflow
uint64_t max_rtp_us = uint64_t(UINT32_MAX) * 1000000 / sample_rate;
update(rtp_stamp, _last_ntp_stamp_us + (max_rtp_us - diff_us));
return _last_ntp_stamp_us;
}
// 不明原因的时间戳回退,直接返回上次值
// 不明原因的时间戳回退,直接返回上次值 [AUTO-TRANSLATED:c5105c14]
// Timestamp rollback for unknown reasons, return the last value directly
WarnL << "rtp stamp abnormal reduced:" << _last_rtp_stamp << " -> " << rtp_stamp;
update(rtp_stamp, _last_ntp_stamp_us);
return _last_ntp_stamp_us;

View File

@ -27,12 +27,19 @@ public:
* @param stamp
* @param enable_rollback 退
* @return
* Calculate the timestamp increment
* @param stamp Absolute timestamp
* @param enable_rollback Whether to allow the timestamp to roll back
* @return Timestamp increment
* [AUTO-TRANSLATED:e8d21dcd]
*/
int64_t deltaStamp(int64_t stamp, bool enable_rollback = true);
int64_t relativeStamp(int64_t stamp, bool enable_rollback = true);
int64_t relativeStamp();
// 设置最大允许回退或跳跃幅度
// 设置最大允许回退或跳跃幅度 [AUTO-TRANSLATED:e5b44ede]
// Set the maximum allowed rollback or jump amplitude
void setMaxDelta(size_t max_delta);
protected:
@ -44,8 +51,10 @@ protected:
int64_t _relative_stamp = 0;
};
//该类解决时间戳回环、回退问题
//计算相对时间戳或者产生平滑时间戳
// 该类解决时间戳回环、回退问题 [AUTO-TRANSLATED:b442692c]
// This class solves the problem of timestamp loopback and rollback
// 计算相对时间戳或者产生平滑时间戳 [AUTO-TRANSLATED:0deabd6e]
// Calculate the relative timestamp or generate a smooth timestamp
class Stamp : public DeltaStamp{
public:
/**
@ -55,43 +64,72 @@ public:
* @param dts_out dts
* @param pts_out pts
* @param modifyStamp
* Get the relative timestamp, which also implements audio and video synchronization, limits dts rollback, etc.
* @param dts Input dts, if it is 0, it will be generated according to the system timestamp
* @param pts Input pts, if it is 0, it is equal to dts
* @param dts_out Output dts
* @param pts_out Output pts
* @param modifyStamp Whether to overwrite with the system timestamp
* [AUTO-TRANSLATED:0b939dc5]
*/
void revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false);
/**
* seek用
* @param relativeStamp
* Set the relative timestamp again, used for seek
* @param relativeStamp Relative timestamp
* [AUTO-TRANSLATED:fc087399]
*/
void setRelativeStamp(int64_t relativeStamp);
/**
*
* @return
* Get the current relative timestamp
* @return
* [AUTO-TRANSLATED:7ca29fde]
*/
int64_t getRelativeStamp() const ;
/**
* 退
* @param playback
* Set whether it is playback mode, playback mode allows timestamp rollback
* @param playback Whether it is playback mode
* [AUTO-TRANSLATED:ffe5e40b]
*/
void setPlayBack(bool playback = true);
/**
* ()
*
* Used for audio and video synchronization, audio should be synchronized with video (only modify audio timestamp)
* Because modifying the audio timestamp does not affect the playback speed
* [AUTO-TRANSLATED:7ac41a76]
*/
void syncTo(Stamp &other);
/**
* 退
* Whether to allow timestamp rollback
* [AUTO-TRANSLATED:1d32f7e3]
*/
void enableRollback(bool flag);
private:
//主要实现音视频时间戳同步功能
// 主要实现音视频时间戳同步功能 [AUTO-TRANSLATED:45863fce]
// Mainly implements audio and video timestamp synchronization function
void revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false);
//主要实现获取相对时间戳功能
// 主要实现获取相对时间戳功能 [AUTO-TRANSLATED:4e042942]
// Mainly implements the function of obtaining the relative timestamp
void revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false);
void needSync() override;
@ -99,7 +137,8 @@ private:
private:
bool _playback = false;
bool _need_sync = false;
// 默认不允许时间戳回滚
// 默认不允许时间戳回滚 [AUTO-TRANSLATED:0163ff03]
// Default does not allow timestamp rollback
bool _enable_rollback = false;
int64_t _relative_stamp = 0;
int64_t _last_dts_in = 0;
@ -109,8 +148,10 @@ private:
Stamp *_sync_master = nullptr;
};
//dts生成器
//pts排序后就是dts
// dts生成器 [AUTO-TRANSLATED:d8a794a2]
// dts generator,
// pts排序后就是dts [AUTO-TRANSLATED:439ac368]
// pts after sorting is dts
class DtsGenerator{
public:
bool getDts(uint64_t pts, uint64_t &dts);

View File

@ -39,7 +39,8 @@ bool loadIniConfig(const char *ini_path) {
return false;
}
}
////////////广播名称///////////
// //////////广播名称/////////// [AUTO-TRANSLATED:439b2d74]
// //////////Broadcast Name///////////
namespace Broadcast {
const string kBroadcastMediaChanged = "kBroadcastMediaChanged";
const string kBroadcastRecordMP4 = "kBroadcastRecordMP4";
@ -68,7 +69,8 @@ const string kBroadcastPlayerCountChanged = "kBroadcastPlayerCountChanged";
} // namespace Broadcast
// 通用配置项目
// 通用配置项目 [AUTO-TRANSLATED:ca344202]
// General Configuration Items
namespace General {
#define GENERAL_FIELD "general."
const string kMediaServerId = GENERAL_FIELD "mediaServerId";
@ -81,6 +83,7 @@ const string kMergeWriteMS = GENERAL_FIELD "mergeWriteMS";
const string kCheckNvidiaDev = GENERAL_FIELD "check_nvidia_dev";
const string kEnableFFmpegLog = GENERAL_FIELD "enable_ffmpeg_log";
const string kWaitTrackReadyMS = GENERAL_FIELD "wait_track_ready_ms";
const string kWaitAudioTrackDataMS = GENERAL_FIELD "wait_audio_track_data_ms";
const string kWaitAddTrackMS = GENERAL_FIELD "wait_add_track_ms";
const string kUnreadyFrameCache = GENERAL_FIELD "unready_frame_cache";
const string kBroadcastPlayerCountChanged = GENERAL_FIELD "broadcast_player_count_changed";
@ -101,6 +104,7 @@ static onceToken token([]() {
mINI::Instance()[kCheckNvidiaDev] = 1;
mINI::Instance()[kEnableFFmpegLog] = 0;
mINI::Instance()[kWaitTrackReadyMS] = 10000;
mINI::Instance()[kWaitAudioTrackDataMS] = 1000;
mINI::Instance()[kWaitAddTrackMS] = 3000;
mINI::Instance()[kUnreadyFrameCache] = 100;
mINI::Instance()[kBroadcastPlayerCountChanged] = 0;
@ -172,7 +176,8 @@ static onceToken token([]() {
});
} // !Protocol
////////////HTTP配置///////////
// //////////HTTP配置/////////// [AUTO-TRANSLATED:a281d694]
// //////////HTTP Configuration///////////
namespace Http {
#define HTTP_FIELD "http."
const string kSendBufSize = HTTP_FIELD "sendBufSize";
@ -215,7 +220,8 @@ static onceToken token([]() {
} // namespace Http
////////////SHELL配置///////////
// //////////SHELL配置/////////// [AUTO-TRANSLATED:f023ec45]
// //////////SHELL Configuration///////////
namespace Shell {
#define SHELL_FIELD "shell."
const string kMaxReqSize = SHELL_FIELD "maxReqSize";
@ -223,7 +229,8 @@ const string kMaxReqSize = SHELL_FIELD "maxReqSize";
static onceToken token([]() { mINI::Instance()[kMaxReqSize] = 1024; });
} // namespace Shell
////////////RTSP服务器配置///////////
// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981]
// //////////RTSP Server Configuration///////////
namespace Rtsp {
#define RTSP_FIELD "rtsp."
const string kAuthBasic = RTSP_FIELD "authBasic";
@ -234,7 +241,8 @@ const string kLowLatency = RTSP_FIELD"lowLatency";
const string kRtpTransportType = RTSP_FIELD"rtpTransportType";
static onceToken token([]() {
// 默认Md5方式认证
// 默认Md5方式认证 [AUTO-TRANSLATED:6155d989]
// Default Md5 authentication
mINI::Instance()[kAuthBasic] = 0;
mINI::Instance()[kHandshakeSecond] = 15;
mINI::Instance()[kKeepAliveSecond] = 15;
@ -244,7 +252,8 @@ static onceToken token([]() {
});
} // namespace Rtsp
////////////RTMP服务器配置///////////
// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f]
// //////////RTMP Server Configuration///////////
namespace Rtmp {
#define RTMP_FIELD "rtmp."
const string kHandshakeSecond = RTMP_FIELD "handshakeSecond";
@ -260,13 +269,16 @@ static onceToken token([]() {
});
} // namespace Rtmp
////////////RTP配置///////////
// //////////RTP配置/////////// [AUTO-TRANSLATED:23cbcb86]
// //////////RTP Configuration///////////
namespace Rtp {
#define RTP_FIELD "rtp."
// RTP打包最大MTU,公网情况下更小
// RTP打包最大MTU,公网情况下更小 [AUTO-TRANSLATED:869f5c4b]
// Maximum RTP packet MTU, smaller for public networks
const string kVideoMtuSize = RTP_FIELD "videoMtuSize";
const string kAudioMtuSize = RTP_FIELD "audioMtuSize";
// rtp包最大长度限制单位是KB
// rtp包最大长度限制单位是KB [AUTO-TRANSLATED:aee4bffc]
// Maximum RTP packet length limit, in KB
const string kRtpMaxSize = RTP_FIELD "rtpMaxSize";
const string kLowLatency = RTP_FIELD "lowLatency";
const string kH264StapA = RTP_FIELD "h264_stap_a";
@ -280,14 +292,18 @@ static onceToken token([]() {
});
} // namespace Rtp
////////////组播配置///////////
// //////////组播配置/////////// [AUTO-TRANSLATED:dc39b9d6]
// //////////Multicast Configuration///////////
namespace MultiCast {
#define MULTI_FIELD "multicast."
// 组播分配起始地址
// 组播分配起始地址 [AUTO-TRANSLATED:069db91d]
// Multicast allocation starting address
const string kAddrMin = MULTI_FIELD "addrMin";
// 组播分配截止地址
// 组播分配截止地址 [AUTO-TRANSLATED:6d3fc54c]
// Multicast allocation ending address
const string kAddrMax = MULTI_FIELD "addrMax";
// 组播TTL
// 组播TTL [AUTO-TRANSLATED:c7c5339c]
// Multicast TTL
const string kUdpTTL = MULTI_FIELD "udpTTL";
static onceToken token([]() {
@ -297,7 +313,8 @@ static onceToken token([]() {
});
} // namespace MultiCast
////////////录像配置///////////
// //////////录像配置/////////// [AUTO-TRANSLATED:19de3e96]
// //////////Recording Configuration///////////
namespace Record {
#define RECORD_FIELD "record."
const string kAppName = RECORD_FIELD "appName";
@ -317,7 +334,8 @@ static onceToken token([]() {
});
} // namespace Record
////////////HLS相关配置///////////
// //////////HLS相关配置/////////// [AUTO-TRANSLATED:873cc84c]
// //////////HLS Related Configuration///////////
namespace Hls {
#define HLS_FIELD "hls."
const string kSegmentDuration = HLS_FIELD "segDur";
@ -343,7 +361,8 @@ static onceToken token([]() {
});
} // namespace Hls
////////////Rtp代理相关配置///////////
// //////////Rtp代理相关配置/////////// [AUTO-TRANSLATED:7b285587]
// //////////Rtp Proxy Related Configuration///////////
namespace RtpProxy {
#define RTP_PROXY_FIELD "rtp_proxy."
const string kDumpDir = RTP_PROXY_FIELD "dumpDir";
@ -448,7 +467,8 @@ public:
MemThreadInfo(bool is_thread_local) {
_is_thread_local = is_thread_local;
if (_is_thread_local) {
// 确保所有线程退出后才能释放全局内存统计器
// 确保所有线程退出后才能释放全局内存统计器 [AUTO-TRANSLATED:edb51704]
// Ensure that all threads exit before releasing the global memory statistics
total_mem = Instance(false);
}
// printf("%s %d\r\n", __FUNCTION__, (int) _is_thread_local);
@ -498,7 +518,8 @@ private:
MemThreadInfo *ptr;
};
// 该变量主要确保线程退出后才能释放MemThreadInfo变量
// 该变量主要确保线程退出后才能释放MemThreadInfo变量 [AUTO-TRANSLATED:a72494b0]
// This variable mainly ensures that the MemThreadInfo variable can be released only after the thread exits
static thread_local MemThreadInfoLocal s_thread_mem_info;
uint64_t getTotalMemUsage() {

View File

@ -21,110 +21,143 @@ namespace mediakit {
class ProtocolOption;
// 加载配置文件,如果配置文件不存在,那么会导出默认配置并生成配置文件
// 加载配置文件成功后会触发kBroadcastUpdateConfig广播
// 如果指定的文件名(ini_path)为空,那么会加载默认配置文件
// 默认配置文件名为 /path/to/your/exe.ini
// 加载配置文件成功后返回true否则返回false
// 加载配置文件,如果配置文件不存在,那么会导出默认配置并生成配置文件 [AUTO-TRANSLATED:16d0b898]
// Load the configuration file. If the configuration file does not exist, the default configuration will be exported and the configuration file will be generated.
// 加载配置文件成功后会触发kBroadcastUpdateConfig广播 [AUTO-TRANSLATED:327e5be2]
// After the configuration file is loaded successfully, the kBroadcastUpdateConfig broadcast will be triggered.
// 如果指定的文件名(ini_path)为空,那么会加载默认配置文件 [AUTO-TRANSLATED:e241a2b7]
// If the specified file name (ini_path) is empty, the default configuration file will be loaded.
// 默认配置文件名为 /path/to/your/exe.ini [AUTO-TRANSLATED:2d1acfcb]
// The default configuration file name is /path/to/your/exe.ini
// 加载配置文件成功后返回true否则返回false [AUTO-TRANSLATED:cba43e43]
// Returns true if the configuration file is loaded successfully, otherwise returns false.
bool loadIniConfig(const char *ini_path = nullptr);
////////////广播名称///////////
// //////////广播名称/////////// [AUTO-TRANSLATED:439b2d74]
// //////////Broadcast Name///////////
namespace Broadcast {
// 注册或反注册MediaSource事件广播
// 注册或反注册MediaSource事件广播 [AUTO-TRANSLATED:ec55c1cf]
// Register or unregister MediaSource event broadcast
extern const std::string kBroadcastMediaChanged;
#define BroadcastMediaChangedArgs const bool &bRegist, MediaSource &sender
// 录制mp4文件成功后广播
// 录制mp4文件成功后广播 [AUTO-TRANSLATED:479ec954]
// Broadcast after recording mp4 file successfully
extern const std::string kBroadcastRecordMP4;
#define BroadcastRecordMP4Args const RecordInfo &info
// 录制 ts 文件后广播
// 录制 ts 文件后广播 [AUTO-TRANSLATED:63a8868c]
// Broadcast after recording ts file
extern const std::string kBroadcastRecordTs;
#define BroadcastRecordTsArgs const RecordInfo &info
// 收到http api请求广播
// 收到http api请求广播 [AUTO-TRANSLATED:c72e7c3f]
// Broadcast for receiving http api request
extern const std::string kBroadcastHttpRequest;
#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, SockInfo &sender
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 [AUTO-TRANSLATED:2de426b4]
// In the http file server, broadcast for receiving http access to files or directories. Control access permissions to the http directory through this event.
extern const std::string kBroadcastHttpAccess;
#define BroadcastHttpAccessArgs const Parser &parser, const std::string &path, const bool &is_dir, const HttpSession::HttpAccessPathInvoker &invoker, SockInfo &sender
// 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射
// 在该事件中通过自行覆盖path参数可以做到譬如根据虚拟主机或者app选择不同http根目录的目的
// 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 [AUTO-TRANSLATED:0294d0c5]
// In the http file server, broadcast before receiving http access to files or directories. Control the mapping from http url to file path through this event.
// 在该事件中通过自行覆盖path参数可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 [AUTO-TRANSLATED:1bea3efb]
// By overriding the path parameter in this event, you can achieve the purpose of selecting different http root directories based on virtual hosts or apps.
extern const std::string kBroadcastHttpBeforeAccess;
#define BroadcastHttpBeforeAccessArgs const Parser &parser, std::string &path, SockInfo &sender
// 该流是否需要认证是的话调用invoker并传入realm,否则传入空的realm.如果该事件不监听则不认证
// 该流是否需要认证是的话调用invoker并传入realm,否则传入空的realm.如果该事件不监听则不认证 [AUTO-TRANSLATED:5f436d8f]
// Does this stream need authentication? If yes, call invoker and pass in realm, otherwise pass in an empty realm. If this event is not listened to, no authentication will be performed.
extern const std::string kBroadcastOnGetRtspRealm;
#define BroadcastOnGetRtspRealmArgs const MediaInfo &args, const RtspSession::onGetRealm &invoker, SockInfo &sender
// 请求认证用户密码事件user_name为用户名must_no_encrypt如果为true则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败
// 获取到密码后请调用invoker并输入对应类型的密码和密码类型invoker执行时会匹配密码
// 请求认证用户密码事件user_name为用户名must_no_encrypt如果为true则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 [AUTO-TRANSLATED:22b6dfcc]
// Request authentication user password event, user_name is the username, must_no_encrypt if true, then the plaintext password must be provided (because it is base64 authentication method at this time), otherwise it will lead to authentication failure.
// 获取到密码后请调用invoker并输入对应类型的密码和密码类型invoker执行时会匹配密码 [AUTO-TRANSLATED:8c57fd43]
// After getting the password, please call invoker and input the corresponding type of password and password type. The invoker will match the password when executing.
extern const std::string kBroadcastOnRtspAuth;
#define BroadcastOnRtspAuthArgs const MediaInfo &args, const std::string &realm, const std::string &user_name, const bool &must_no_encrypt, const RtspSession::onAuth &invoker, SockInfo &sender
// 推流鉴权结果回调对象
// 如果err为空则代表鉴权成功
// 推流鉴权结果回调对象 [AUTO-TRANSLATED:7e508ed1]
// Push stream authentication result callback object
// 如果err为空则代表鉴权成功 [AUTO-TRANSLATED:d49b0544]
// If err is empty, it means authentication is successful.
using PublishAuthInvoker = std::function<void(const std::string &err, const ProtocolOption &option)>;
// 收到rtsp/rtmp推流事件广播通过该事件控制推流鉴权
// 收到rtsp/rtmp推流事件广播通过该事件控制推流鉴权 [AUTO-TRANSLATED:72417373]
// Broadcast for receiving rtsp/rtmp push stream event. Control push stream authentication through this event.
extern const std::string kBroadcastMediaPublish;
#define BroadcastMediaPublishArgs const MediaOriginType &type, const MediaInfo &args, const Broadcast::PublishAuthInvoker &invoker, SockInfo &sender
// 播放鉴权结果回调对象
// 如果err为空则代表鉴权成功
// 播放鉴权结果回调对象 [AUTO-TRANSLATED:c980162b]
// Playback authentication result callback object
// 如果err为空则代表鉴权成功 [AUTO-TRANSLATED:d49b0544]
// If err is empty, it means authentication is successful.
using AuthInvoker = std::function<void(const std::string &err)>;
// 播放rtsp/rtmp/http-flv事件广播通过该事件控制播放鉴权
// 播放rtsp/rtmp/http-flv事件广播通过该事件控制播放鉴权 [AUTO-TRANSLATED:eddd7014]
// Broadcast for playing rtsp/rtmp/http-flv events. Control playback authentication through this event.
extern const std::string kBroadcastMediaPlayed;
#define BroadcastMediaPlayedArgs const MediaInfo &args, const Broadcast::AuthInvoker &invoker, SockInfo &sender
// shell登录鉴权
// shell登录鉴权 [AUTO-TRANSLATED:26b135d4]
// Shell login authentication
extern const std::string kBroadcastShellLogin;
#define BroadcastShellLoginArgs const std::string &user_name, const std::string &passwd, const Broadcast::AuthInvoker &invoker, SockInfo &sender
// 停止rtsp/rtmp/http-flv会话后流量汇报事件广播
// 停止rtsp/rtmp/http-flv会话后流量汇报事件广播 [AUTO-TRANSLATED:69df61d8]
// Broadcast for traffic reporting event after stopping rtsp/rtmp/http-flv session
extern const std::string kBroadcastFlowReport;
#define BroadcastFlowReportArgs const MediaInfo &args, const uint64_t &totalBytes, const uint64_t &totalDuration, const bool &isPlayer, SockInfo &sender
// 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了
// 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 [AUTO-TRANSLATED:0c00171d]
// This event will be broadcast after the stream is not found. Please pull the stream or other methods to generate the stream after listening to this event, so that you can pull the stream on demand.
extern const std::string kBroadcastNotFoundStream;
#define BroadcastNotFoundStreamArgs const MediaInfo &args, SockInfo &sender, const std::function<void()> &closePlayer
// 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑
// 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑 [AUTO-TRANSLATED:3c45f002]
// Triggered when a stream is not consumed by anyone. The purpose is to achieve business logic such as actively disconnecting the pull stream when no one is watching.
extern const std::string kBroadcastStreamNoneReader;
#define BroadcastStreamNoneReaderArgs MediaSource &sender
// rtp推流被动停止时触发
// rtp推流被动停止时触发 [AUTO-TRANSLATED:43881965]
// Triggered when rtp push stream is passively stopped.
extern const std::string kBroadcastSendRtpStopped;
#define BroadcastSendRtpStoppedArgs MultiMediaSourceMuxer &sender, const std::string &ssrc, const SockException &ex
// 更新配置文件事件广播,执行loadIniConfig函数加载配置文件成功后会触发该广播
// 更新配置文件事件广播,执行loadIniConfig函数加载配置文件成功后会触发该广播 [AUTO-TRANSLATED:ad4e167d]
// Update configuration file event broadcast. This broadcast will be triggered after the loadIniConfig function loads the configuration file successfully.
extern const std::string kBroadcastReloadConfig;
#define BroadcastReloadConfigArgs void
// rtp server 超时
// rtp server 超时 [AUTO-TRANSLATED:a65573fd]
// Rtp server timeout
extern const std::string kBroadcastRtpServerTimeout;
#define BroadcastRtpServerTimeoutArgs uint16_t &local_port, const MediaTuple &tuple, int &tcp_mode, bool &re_use_port, uint32_t &ssrc
// rtc transport sctp 连接状态
// rtc transport sctp 连接状态 [AUTO-TRANSLATED:f00284da]
// Rtc transport sctp connection status
extern const std::string kBroadcastRtcSctpConnecting;
extern const std::string kBroadcastRtcSctpConnected;
extern const std::string kBroadcastRtcSctpFailed;
extern const std::string kBroadcastRtcSctpClosed;
#define BroadcastRtcSctpConnectArgs WebRtcTransport& sender
// rtc transport sctp 发送数据
// rtc transport sctp 发送数据 [AUTO-TRANSLATED:258f1ba8]
// rtc transport sctp send data
extern const std::string kBroadcastRtcSctpSend;
#define BroadcastRtcSctpSendArgs WebRtcTransport& sender, const uint8_t *&data, size_t& len
// rtc transport sctp 接收数据
// rtc transport sctp 接收数据 [AUTO-TRANSLATED:ce4cb57e]
// rtc transport sctp receive data
extern const std::string kBroadcastRtcSctpReceived;
#define BroadcastRtcSctpReceivedArgs WebRtcTransport& sender, uint16_t &streamId, uint32_t &ppid, const uint8_t *&msg, size_t &len
// 观看人数变化广播
// 观看人数变化广播 [AUTO-TRANSLATED:5b246b54]
// broadcast viewer count changes
extern const std::string kBroadcastPlayerCountChanged;
#define BroadcastPlayerCountChangedArgs const MediaTuple& args, const int& count
@ -139,7 +172,8 @@ extern const std::string kBroadcastPlayerCountChanged;
InfoL << "reload config:" << key << "=" << arg; \
} while (0)
// 监听某个配置发送变更
// 监听某个配置发送变更 [AUTO-TRANSLATED:7f46b5b1]
// listen for configuration changes
#define LISTEN_RELOAD_KEY(arg, key, ...) \
do { \
static ::toolkit::onceToken s_token_listen([]() { \
@ -168,41 +202,65 @@ extern const std::string kBroadcastPlayerCountChanged;
} // namespace Broadcast
////////////通用配置///////////
// //////////通用配置/////////// [AUTO-TRANSLATED:b09b9640]
// //////////General Configuration///////////
namespace General {
// 每个流媒体服务器的IDGUID
// 每个流媒体服务器的IDGUID [AUTO-TRANSLATED:c6ac6e56]
// ID (GUID) of each media server
extern const std::string kMediaServerId;
// 流量汇报事件流量阈值,单位KB默认1MB
// 流量汇报事件流量阈值,单位KB默认1MB [AUTO-TRANSLATED:fd036326]
// Traffic reporting event traffic threshold, unit KB, default 1MB
extern const std::string kFlowThreshold;
// 流无人观看并且超过若干时间后才触发kBroadcastStreamNoneReader事件
// 默认连续5秒无人观看然后触发kBroadcastStreamNoneReader事件
// 流无人观看并且超过若干时间后才触发kBroadcastStreamNoneReader事件 [AUTO-TRANSLATED:baeea387]
// Trigger kBroadcastStreamNoneReader event only after the stream has been unwatched for a certain period of time
// 默认连续5秒无人观看然后触发kBroadcastStreamNoneReader事件 [AUTO-TRANSLATED:477bf488]
// Default to trigger kBroadcastStreamNoneReader event after 5 seconds of no viewers
extern const std::string kStreamNoneReaderDelayMS;
// 等待流注册超时时间,收到播放器后请求后,如果未找到相关流,服务器会等待一定时间,
// 如果在这个时间内,相关流注册上了,那么服务器会立即响应播放器播放成功,
// 否则会最多等待kMaxStreamWaitTimeMS毫秒然后响应播放器播放失败
// 等待流注册超时时间,收到播放器后请求后,如果未找到相关流,服务器会等待一定时间, [AUTO-TRANSLATED:7ccd518d]
// Stream registration timeout, after receiving the player's request, if the related stream is not found, the server will wait for a certain period of time,
// 如果在这个时间内,相关流注册上了,那么服务器会立即响应播放器播放成功, [AUTO-TRANSLATED:93ef0249]
// If the related stream is registered within this time, the server will immediately respond to the player that the playback is successful,
// 否则会最多等待kMaxStreamWaitTimeMS毫秒然后响应播放器播放失败 [AUTO-TRANSLATED:f8c9f19e]
// Otherwise, it will wait for a maximum of kMaxStreamWaitTimeMS milliseconds and then respond to the player that the playback failed
extern const std::string kMaxStreamWaitTimeMS;
// 是否启动虚拟主机
// 是否启动虚拟主机 [AUTO-TRANSLATED:e1cea728]
// Whether to enable virtual host
extern const std::string kEnableVhost;
// 拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始,
// 如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写)
// 拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, [AUTO-TRANSLATED:d150ffaa]
// When pulling stream proxy, whether to delete the previous media stream data if the stream is disconnected and reconnected successfully, if deleted, it will start again,
// 如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) [AUTO-TRANSLATED:21a5be7e]
// If not deleted, it will continue to write from the previous data (when recording hls/mp4, it will continue to write after the previous file)
extern const std::string kResetWhenRePlay;
// 合并写缓存大小(单位毫秒)合并写指服务器缓存一定的数据后才会一次性写入socket这样能提高性能但是会提高延时
// 开启后会同时关闭TCP_NODELAY并开启MSG_MORE
// 合并写缓存大小(单位毫秒)合并写指服务器缓存一定的数据后才会一次性写入socket这样能提高性能但是会提高延时 [AUTO-TRANSLATED:6cc6fcf7]
// Merge write cache size (unit milliseconds), merge write refers to the server caching a certain amount of data before writing to the socket at once, which can improve performance but increase latency
// 开启后会同时关闭TCP_NODELAY并开启MSG_MORE [AUTO-TRANSLATED:953b82cf]
// When enabled, TCP_NODELAY will be closed and MSG_MORE will be enabled at the same time
extern const std::string kMergeWriteMS;
// 在docker环境下不能通过英伟达驱动是否存在来判断是否支持硬件转码
// 在docker环境下不能通过英伟达驱动是否存在来判断是否支持硬件转码 [AUTO-TRANSLATED:de678431]
// In the docker environment, the existence of the NVIDIA driver cannot be used to determine whether hardware transcoding is supported
extern const std::string kCheckNvidiaDev;
// 是否开启ffmpeg日志
// 是否开启ffmpeg日志 [AUTO-TRANSLATED:038b471e]
// Whether to enable ffmpeg log
extern const std::string kEnableFFmpegLog;
// 最多等待未初始化的Track 10秒超时之后会忽略未初始化的Track
// 最多等待未初始化的Track 10秒超时之后会忽略未初始化的Track [AUTO-TRANSLATED:826cd533]
// Maximum wait time for uninitialized Track is 10 seconds, after timeout, uninitialized Track will be ignored
extern const std::string kWaitTrackReadyMS;
// 如果直播流只有单Track最多等待3秒超时后未收到其他Track的数据则认为是单Track
// 如果协议元数据有声明特定track数那么无此等待时间
//最多等待音频Track收到数据时间单位毫秒超时且完全没收到音频数据忽略音频Track
//加快某些带封装的流metadata说明有音频但是实际上没有的流ready时间比如很多厂商的GB28181 PS
extern const std::string kWaitAudioTrackDataMS;
// 如果直播流只有单Track最多等待3秒超时后未收到其他Track的数据则认为是单Track [AUTO-TRANSLATED:0e7a364d]
// If the live stream has only one Track, wait for a maximum of 3 seconds, if no data from other Tracks is received after timeout, it is considered a single Track
// 如果协议元数据有声明特定track数那么无此等待时间 [AUTO-TRANSLATED:76606846]
// If the protocol metadata declares a specific number of tracks, there is no such waiting time
extern const std::string kWaitAddTrackMS;
// 如果track未就绪我们先缓存帧数据但是有最大个数限制(100帧时大约4秒),防止内存溢出
// 如果track未就绪我们先缓存帧数据但是有最大个数限制(100帧时大约4秒),防止内存溢出 [AUTO-TRANSLATED:c520054f]
// If the track is not ready, we will cache the frame data first, but there is a maximum number limit (100 frames is about 4 seconds) to prevent memory overflow
extern const std::string kUnreadyFrameCache;
// 是否启用观看人数变化事件广播置1则启用置0则关闭
// 是否启用观看人数变化事件广播置1则启用置0则关闭 [AUTO-TRANSLATED:3b7f0748]
// Whether to enable viewer count change event broadcast, set to 1 to enable, set to 0 to disable
extern const std::string kBroadcastPlayerCountChanged;
// 绑定的本地网卡ip
// 绑定的本地网卡ip [AUTO-TRANSLATED:daa90832]
// Bound local network card ip
extern const std::string kListenIP;
extern const std::string kOpusBitrate;
extern const std::string kAacBitrate;
@ -210,52 +268,73 @@ extern const std::string kAacBitrate;
namespace Protocol {
static constexpr char kFieldName[] = "protocol.";
//时间戳修复这一路流标志位
// 时间戳修复这一路流标志位 [AUTO-TRANSLATED:cc208f41]
// Timestamp repair flag for this stream
extern const std::string kModifyStamp;
//转协议是否开启音频
// 转协议是否开启音频 [AUTO-TRANSLATED:220dddfa]
// Whether to enable audio for protocol conversion
extern const std::string kEnableAudio;
//添加静音音频,在关闭音频时,此开关无效
// 添加静音音频,在关闭音频时,此开关无效 [AUTO-TRANSLATED:47c0ec8e]
// Add silent audio, this switch is invalid when audio is closed
extern const std::string kAddMuteAudio;
// 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close)
// 此配置置1时此流如果无人观看将不触发on_none_reader hook回调
// 而是将直接关闭流
// 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close) [AUTO-TRANSLATED:dba7ab70]
// When there are no viewers, whether to close directly (instead of returning close through the on_none_reader hook)
// 此配置置1时此流如果无人观看将不触发on_none_reader hook回调 [AUTO-TRANSLATED:a5ead314]
// When this configuration is set to 1, if this stream has no viewers, it will not trigger the on_none_reader hook callback,
// 而是将直接关闭流 [AUTO-TRANSLATED:06887d49]
// Instead, it will directly close the stream
extern const std::string kAutoClose;
//断连续推延时,单位毫秒,默认采用配置文件
// 断连续推延时,单位毫秒,默认采用配置文件 [AUTO-TRANSLATED:7a15b12f]
// When the continuous delay is interrupted, the unit is milliseconds, and the configuration file is used by default
extern const std::string kContinuePushMS;
// 平滑发送定时器间隔单位毫秒置0则关闭开启后影响cpu性能同时增加内存
// 该配置开启后可以解决一些流发送不平滑导致zlmediakit转发也不平滑的问题
// 平滑发送定时器间隔单位毫秒置0则关闭开启后影响cpu性能同时增加内存 [AUTO-TRANSLATED:ad4e306a]
// Smooth sending timer interval, unit is milliseconds, set to 0 to close; enabling it will affect CPU performance and increase memory
// 该配置开启后可以解决一些流发送不平滑导致zlmediakit转发也不平滑的问题 [AUTO-TRANSLATED:0f2b1657]
// Enabling this configuration can solve some problems where the stream is not sent smoothly, resulting in ZLMediaKit forwarding not being smooth
extern const std::string kPacedSenderMS;
//是否开启转换为hls(mpegts)
// 是否开启转换为hls(mpegts) [AUTO-TRANSLATED:bfc1167a]
// Whether to enable conversion to HLS (MPEGTS)
extern const std::string kEnableHls;
//是否开启转换为hls(fmp4)
// 是否开启转换为hls(fmp4) [AUTO-TRANSLATED:20548673]
// Whether to enable conversion to HLS (FMP4)
extern const std::string kEnableHlsFmp4;
//是否开启MP4录制
// 是否开启MP4录制 [AUTO-TRANSLATED:0157b014]
// Whether to enable MP4 recording
extern const std::string kEnableMP4;
//是否开启转换为rtsp
// 是否开启转换为rtsp [AUTO-TRANSLATED:0711cb18]
// Whether to enable conversion to RTSP
extern const std::string kEnableRtsp;
//是否开启转换为rtmp/flv
// 是否开启转换为rtmp/flv [AUTO-TRANSLATED:d4774119]
// Whether to enable conversion to RTMP/FLV
extern const std::string kEnableRtmp;
//是否开启转换为http-ts/ws-ts
// 是否开启转换为http-ts/ws-ts [AUTO-TRANSLATED:51acc798]
// Whether to enable conversion to HTTP-TS/WS-TS
extern const std::string kEnableTS;
//是否开启转换为http-fmp4/ws-fmp4
// 是否开启转换为http-fmp4/ws-fmp4 [AUTO-TRANSLATED:8c96e1e4]
// Whether to enable conversion to HTTP-FMP4/WS-FMP4
extern const std::string kEnableFMP4;
//是否开启转换为webrtc
extern const std::string kEnableRtc;
//是否开启音频转码
extern const std::string kAudioTranscode;
//是否将mp4录制当做观看者
// 是否将mp4录制当做观看者 [AUTO-TRANSLATED:ba351230]
// Whether to treat MP4 recording as a viewer
extern const std::string kMP4AsPlayer;
//mp4切片大小单位秒
// mp4切片大小单位秒 [AUTO-TRANSLATED:c3fb8ec1]
// MP4 fragment size, unit is seconds
extern const std::string kMP4MaxSecond;
//mp4录制保存路径
// mp4录制保存路径 [AUTO-TRANSLATED:6d860f27]
// MP4 recording save path
extern const std::string kMP4SavePath;
//hls录制保存路径
// hls录制保存路径 [AUTO-TRANSLATED:cfa90719]
// HLS recording save path
extern const std::string kHlsSavePath;
// 按需转协议的开关
// 按需转协议的开关 [AUTO-TRANSLATED:9f350899]
// On-demand protocol conversion switch
extern const std::string kRtcDemand;
extern const std::string kHlsDemand;
extern const std::string kRtspDemand;
@ -264,159 +343,230 @@ extern const std::string kTSDemand;
extern const std::string kFMP4Demand;
} // !Protocol
////////////HTTP配置///////////
// //////////HTTP配置/////////// [AUTO-TRANSLATED:a281d694]
// //////////HTTP configuration///////////
namespace Http {
// http 文件发送缓存大小
// http 文件发送缓存大小 [AUTO-TRANSLATED:51fb08c0]
// HTTP file sending cache size
extern const std::string kSendBufSize;
// http 最大请求字节数
// http 最大请求字节数 [AUTO-TRANSLATED:8239eb9c]
// HTTP maximum request byte size
extern const std::string kMaxReqSize;
// http keep-alive秒数
// http keep-alive秒数 [AUTO-TRANSLATED:d4930c66]
// HTTP keep-alive seconds
extern const std::string kKeepAliveSecond;
// http 字符编码
// http 字符编码 [AUTO-TRANSLATED:f7e55c83]
// HTTP character encoding
extern const std::string kCharSet;
// http 服务器根目录
// http 服务器根目录 [AUTO-TRANSLATED:f8f55daf]
// HTTP server root directory
extern const std::string kRootPath;
// http 服务器虚拟目录 虚拟目录名和文件路径使用","隔开,多个配置路径间用";"隔开,例如 path_d,d:/record;path_e,e:/record
// http 服务器虚拟目录 虚拟目录名和文件路径使用","隔开,多个配置路径间用";"隔开,例如 path_d,d:/record;path_e,e:/record [AUTO-TRANSLATED:fa4ee929]
// HTTP server virtual directory. Virtual directory name and file path are separated by ",", and multiple configuration paths are separated by ";", for example, path_d,d:/record;path_e,e:/record
extern const std::string kVirtualPath;
// http 404错误提示内容
// http 404错误提示内容 [AUTO-TRANSLATED:91adb026]
// HTTP 404 error prompt content
extern const std::string kNotFound;
// 是否显示文件夹菜单
// 是否显示文件夹菜单 [AUTO-TRANSLATED:77301b85]
// Whether to display the folder menu
extern const std::string kDirMenu;
// 禁止缓存文件的后缀
// 禁止缓存文件的后缀 [AUTO-TRANSLATED:92bcb7f7]
// Forbidden cache file suffixes
extern const std::string kForbidCacheSuffix;
// 可以把http代理前真实客户端ip放在http头中https://github.com/ZLMediaKit/ZLMediaKit/issues/1388
// 可以把http代理前真实客户端ip放在http头中https://github.com/ZLMediaKit/ZLMediaKit/issues/1388 [AUTO-TRANSLATED:afcd9556]
// You can put the real client IP address before the HTTP proxy in the HTTP header: https://github.com/ZLMediaKit/ZLMediaKit/issues/1388
extern const std::string kForwardedIpHeader;
// 是否允许所有跨域请求
// 是否允许所有跨域请求 [AUTO-TRANSLATED:2551c096]
// Whether to allow all cross-domain requests
extern const std::string kAllowCrossDomains;
// 允许访问http api和http文件索引的ip地址范围白名单置空情况下不做限制
// 允许访问http api和http文件索引的ip地址范围白名单置空情况下不做限制 [AUTO-TRANSLATED:ab939863]
// Whitelist of IP address ranges allowed to access HTTP API and HTTP file index. No restrictions are imposed when empty
extern const std::string kAllowIPRange;
} // namespace Http
////////////SHELL配置///////////
// //////////SHELL配置/////////// [AUTO-TRANSLATED:f023ec45]
// //////////SHELL configuration///////////
namespace Shell {
extern const std::string kMaxReqSize;
} // namespace Shell
////////////RTSP服务器配置///////////
// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981]
// //////////RTSP Server Configuration///////////
namespace Rtsp {
// 是否优先base64方式认证默认Md5方式认证
// 是否优先base64方式认证默认Md5方式认证 [AUTO-TRANSLATED:0ea332b5]
// Is base64 authentication prioritized? Default is Md5 authentication
extern const std::string kAuthBasic;
// 握手超时时间默认15秒
// 握手超时时间默认15秒 [AUTO-TRANSLATED:6f69a65b]
// Handshake timeout, default 15 seconds
extern const std::string kHandshakeSecond;
// 维持链接超时时间默认15秒
// 维持链接超时时间默认15秒 [AUTO-TRANSLATED:b6339c90]
// Keep-alive timeout, default 15 seconds
extern const std::string kKeepAliveSecond;
// rtsp拉流代理是否直接代理
// 直接代理后支持任意编码格式但是会导致GOP缓存无法定位到I帧可能会导致开播花屏
// 并且如果是tcp方式拉流如果rtp大于mtu会导致无法使用udp方式代理
// 假定您的拉流源地址不是264或265或AAC那么你可以使用直接代理的方式来支持rtsp代理
// 默认开启rtsp直接代理rtmp由于没有这些问题是强制开启直接代理的
// rtsp拉流代理是否直接代理 [AUTO-TRANSLATED:9cd82709]
// Whether RTSP pull stream proxy is direct proxy
// 直接代理后支持任意编码格式但是会导致GOP缓存无法定位到I帧可能会导致开播花屏 [AUTO-TRANSLATED:36525a92]
// Direct proxy supports any encoding format, but it will cause GOP cache unable to locate I-frame, which may lead to screen flickering
// 并且如果是tcp方式拉流如果rtp大于mtu会导致无法使用udp方式代理 [AUTO-TRANSLATED:a1ab467e]
// And if it is TCP pull stream, if RTP is larger than MTU, it will not be able to use UDP proxy
// 假定您的拉流源地址不是264或265或AAC那么你可以使用直接代理的方式来支持rtsp代理 [AUTO-TRANSLATED:9efaedcd]
// Assuming your pull stream source address is not 264 or 265 or AAC, then you can use direct proxy to support RTSP proxy
// 默认开启rtsp直接代理rtmp由于没有这些问题是强制开启直接代理的 [AUTO-TRANSLATED:0e55d051]
// Default to enable RTSP direct proxy, RTMP does not have these problems, it is forced to enable direct proxy
extern const std::string kDirectProxy;
// rtsp 转发是否使用低延迟模式当开启时不会缓存rtp包来提高并发可以降低一帧的延迟
// rtsp 转发是否使用低延迟模式当开启时不会缓存rtp包来提高并发可以降低一帧的延迟 [AUTO-TRANSLATED:f6fe8c6c]
// Whether RTSP forwarding uses low latency mode, when enabled, it will not cache RTP packets to improve concurrency and reduce one frame delay
extern const std::string kLowLatency;
//强制协商rtp传输方式 (0:TCP,1:UDP,2:MULTICAST,-1:不限制)
//当客户端发起RTSP SETUP的时候如果传输类型和此配置不一致则返回461 Unsupport Transport
//迫使客户端重新SETUP并切换到对应协议。目前支持FFMPEG和VLC
// 强制协商rtp传输方式 (0:TCP,1:UDP,2:MULTICAST,-1:不限制) [AUTO-TRANSLATED:38574ed5]
// Force negotiation of RTP transport method (0: TCP, 1: UDP, 2: MULTICAST, -1: no restriction)
// 当客户端发起RTSP SETUP的时候如果传输类型和此配置不一致则返回461 Unsupport Transport [AUTO-TRANSLATED:b0fd0336]
// When the client initiates RTSP SETUP, if the transport type is inconsistent with this configuration, it will return 461 Unsupport Transport
// 迫使客户端重新SETUP并切换到对应协议。目前支持FFMPEG和VLC [AUTO-TRANSLATED:45f9cddb]
// Force the client to re-SETUP and switch to the corresponding protocol. Currently supports FFMPEG and VLC
extern const std::string kRtpTransportType;
} // namespace Rtsp
////////////RTMP服务器配置///////////
// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f]
// //////////RTMP Server Configuration///////////
namespace Rtmp {
// 握手超时时间默认15秒
// 握手超时时间默认15秒 [AUTO-TRANSLATED:6f69a65b]
// Handshake timeout, default 15 seconds
extern const std::string kHandshakeSecond;
// 维持链接超时时间默认15秒
// 维持链接超时时间默认15秒 [AUTO-TRANSLATED:b6339c90]
// Keep-alive timeout, default 15 seconds
extern const std::string kKeepAliveSecond;
// 是否直接代理
// 是否直接代理 [AUTO-TRANSLATED:25268b70]
// Whether direct proxy
extern const std::string kDirectProxy;
// h265-rtmp是否采用增强型(或者国内扩展)
// h265-rtmp是否采用增强型(或者国内扩展) [AUTO-TRANSLATED:4a52d042]
// Whether h265-rtmp uses enhanced (or domestic extension)
extern const std::string kEnhanced;
} // namespace Rtmp
////////////RTP配置///////////
// //////////RTP配置/////////// [AUTO-TRANSLATED:23cbcb86]
// //////////RTP Configuration///////////
namespace Rtp {
// RTP打包最大MTU,公网情况下更小
// RTP打包最大MTU,公网情况下更小 [AUTO-TRANSLATED:869f5c4b]
// Maximum RTP packet MTU, smaller in public network
extern const std::string kVideoMtuSize;
// RTP打包最大MTU,公网情况下更小
// RTP打包最大MTU,公网情况下更小 [AUTO-TRANSLATED:869f5c4b]
// Maximum RTP packet MTU, smaller in public network
extern const std::string kAudioMtuSize;
// rtp包最大长度限制, 单位KB
// rtp包最大长度限制, 单位KB [AUTO-TRANSLATED:1da42584]
// Maximum RTP packet length limit, unit KB
extern const std::string kRtpMaxSize;
// rtp 打包时低延迟开关默认关闭为0h264存在一帧多个sliceNAL的情况在这种情况下如果开启可能会导致画面花屏
// rtp 打包时低延迟开关默认关闭为0h264存在一帧多个sliceNAL的情况在这种情况下如果开启可能会导致画面花屏 [AUTO-TRANSLATED:4cf0cb8d]
// When RTP is packaged, low latency switch, default off (0), H264 has multiple slices (NAL) in one frame, in this case, if enabled, it may cause screen flickering
extern const std::string kLowLatency;
//H264 rtp打包模式是否采用stap-a模式(为了在老版本浏览器上兼容webrtc)还是采用Single NAL unit packet per H.264 模式
// H264 rtp打包模式是否采用stap-a模式(为了在老版本浏览器上兼容webrtc)还是采用Single NAL unit packet per H.264 模式 [AUTO-TRANSLATED:30632378]
// Whether H264 RTP packaging mode uses stap-a mode (for compatibility with webrtc on older browsers) or Single NAL unit packet per H.264 mode
extern const std::string kH264StapA;
} // namespace Rtp
////////////组播配置///////////
// //////////组播配置/////////// [AUTO-TRANSLATED:dc39b9d6]
// //////////Multicast Configuration///////////
namespace MultiCast {
// 组播分配起始地址
// 组播分配起始地址 [AUTO-TRANSLATED:069db91d]
// Multicast allocation start address
extern const std::string kAddrMin;
// 组播分配截止地址
// 组播分配截止地址 [AUTO-TRANSLATED:6d3fc54c]
// Multicast allocation end address
extern const std::string kAddrMax;
// 组播TTL
// 组播TTL [AUTO-TRANSLATED:c7c5339c]
// Multicast TTL
extern const std::string kUdpTTL;
} // namespace MultiCast
////////////录像配置///////////
// //////////录像配置/////////// [AUTO-TRANSLATED:19de3e96]
// //////////Recording Configuration///////////
namespace Record {
// 查看录像的应用名称
// 查看录像的应用名称 [AUTO-TRANSLATED:a71b5daf]
// Application name for viewing recordings
extern const std::string kAppName;
// 每次流化MP4文件的时长,单位毫秒
// 每次流化MP4文件的时长,单位毫秒 [AUTO-TRANSLATED:0add878d]
// Duration of each MP4 file streaming, in milliseconds
extern const std::string kSampleMS;
// mp4文件写缓存大小
// mp4文件写缓存大小 [AUTO-TRANSLATED:9904413d]
// MP4 file write cache size
extern const std::string kFileBufSize;
// mp4录制完成后是否进行二次关键帧索引写入头部
// mp4录制完成后是否进行二次关键帧索引写入头部 [AUTO-TRANSLATED:53cfdcb5]
// Whether to perform secondary keyframe index writing to the header after MP4 recording is completed
extern const std::string kFastStart;
// mp4文件是否重头循环读取
// mp4文件是否重头循环读取 [AUTO-TRANSLATED:69ac72de]
// Whether to loop read the MP4 file from the beginning
extern const std::string kFileRepeat;
// mp4录制文件是否采用fmp4格式
// mp4录制文件是否采用fmp4格式 [AUTO-TRANSLATED:12559ae0]
// Whether to use fmp4 format for MP4 recording files
extern const std::string kEnableFmp4;
} // namespace Record
////////////HLS相关配置///////////
// //////////HLS相关配置/////////// [AUTO-TRANSLATED:873cc84c]
// //////////HLS related configuration///////////
namespace Hls {
// HLS切片时长,单位秒
// HLS切片时长,单位秒 [AUTO-TRANSLATED:ed6a4219]
// HLS slice duration, in seconds
extern const std::string kSegmentDuration;
// m3u8文件中HLS切片个数如果设置为0则不删除切片而是保存为点播
// m3u8文件中HLS切片个数如果设置为0则不删除切片而是保存为点播 [AUTO-TRANSLATED:92388a5d]
// Number of HLS slices in the m3u8 file. If set to 0, the slices will not be deleted and will be saved as on-demand
extern const std::string kSegmentNum;
// 如果设置为0则不保留切片设置为1则一直保留切片
// 如果设置为0则不保留切片设置为1则一直保留切片 [AUTO-TRANSLATED:0933fd7b]
// If set to 0, the slices will not be retained, if set to 1, the slices will be retained all the time
extern const std::string kSegmentKeep;
// HLS切片延迟个数大于0将生成hls_delay.m3u8文件0则不生成
// HLS切片延迟个数大于0将生成hls_delay.m3u8文件0则不生成 [AUTO-TRANSLATED:b1751b00]
// Number of HLS slice delays. Greater than 0 will generate hls_delay.m3u8 file, 0 will not generate
extern const std::string kSegmentDelay;
// HLS切片从m3u8文件中移除后继续保留在磁盘上的个数
// HLS切片从m3u8文件中移除后继续保留在磁盘上的个数 [AUTO-TRANSLATED:b7a23e1a]
// Number of HLS slices that continue to be retained on disk after being removed from the m3u8 file
extern const std::string kSegmentRetain;
// HLS文件写缓存大小
// HLS文件写缓存大小 [AUTO-TRANSLATED:81832c8b]
// HLS file write cache size
extern const std::string kFileBufSize;
// 是否广播 ts 切片完成通知
// 是否广播 ts 切片完成通知 [AUTO-TRANSLATED:a53644a2]
// Whether to broadcast ts slice completion notification
extern const std::string kBroadcastRecordTs;
// hls直播文件删除延时单位秒
// hls直播文件删除延时单位秒 [AUTO-TRANSLATED:5643cab7]
// HLS live file deletion delay, in seconds
extern const std::string kDeleteDelaySec;
// 如果设置为1则第一个切片长度强制设置为1个GOP
// 如果设置为1则第一个切片长度强制设置为1个GOP [AUTO-TRANSLATED:fbbb651d]
// If set to 1, the length of the first slice is forced to be 1 GOP
extern const std::string kFastRegister;
} // namespace Hls
////////////Rtp代理相关配置///////////
// //////////Rtp代理相关配置/////////// [AUTO-TRANSLATED:7b285587]
// //////////Rtp proxy related configuration///////////
namespace RtpProxy {
// rtp调试数据保存目录,置空则不生成
// rtp调试数据保存目录,置空则不生成 [AUTO-TRANSLATED:aa004af0]
// Rtp debug data save directory, empty if not generated
extern const std::string kDumpDir;
// rtp接收超时时间
// rtp接收超时时间 [AUTO-TRANSLATED:9e918489]
// Rtp receive timeout
extern const std::string kTimeoutSec;
// 随机端口范围最少确保36个端口
// 该范围同时限制rtsp服务器udp端口范围
// 随机端口范围最少确保36个端口 [AUTO-TRANSLATED:2f2b6b17]
// Random port range, at least 36 ports are guaranteed
// 该范围同时限制rtsp服务器udp端口范围 [AUTO-TRANSLATED:1ff8fd75]
// This range also limits the rtsp server udp port range
extern const std::string kPortRange;
// rtp server h264的pt
// rtp server h264的pt [AUTO-TRANSLATED:b8cf877b]
// Rtp server h264 pt
extern const std::string kH264PT;
// rtp server h265的pt
// rtp server h265的pt [AUTO-TRANSLATED:2bdb1dfb]
// Rtp server h265 pt
extern const std::string kH265PT;
// rtp server ps 的pt
// rtp server ps 的pt [AUTO-TRANSLATED:6feaf5f9]
// Rtp server ps pt
extern const std::string kPSPT;
// rtp server opus 的pt
// rtp server opus 的pt [AUTO-TRANSLATED:9f91f85a]
// Rtp server opus pt
extern const std::string kOpusPT;
// RtpSender相关功能是否提前开启gop缓存优化级联秒开体验默认开启
// RtpSender相关功能是否提前开启gop缓存优化级联秒开体验默认开启 [AUTO-TRANSLATED:40c37c77]
// Whether to enable gop cache optimization cascade second-open experience for RtpSender related functions, enabled by default
extern const std::string kGopCache;
//国标发送g711 rtp 打包时每个包的语音时长是多少默认是100 ms范围为20~180ms (gb28181-2016c.2.4规定)
//最好为20 的倍数程序自动向20的倍数取整
// 国标发送g711 rtp 打包时每个包的语音时长是多少默认是100 ms范围为20~180ms (gb28181-2016c.2.4规定) [AUTO-TRANSLATED:3b3916a3]
// When sending g711 rtp packets in national standard, what is the duration of each packet, the default is 100 ms, the range is 20~180ms (gb28181-2016, c.2.4),
// 最好为20 的倍数程序自动向20的倍数取整 [AUTO-TRANSLATED:7bc6e0ec]
// It is best to be a multiple of 20, the program automatically rounds to the nearest multiple of 20
extern const std::string kRtpG711DurMs;
// udp recv socket buffer size
extern const std::string kUdpRecvSocketBuffer;
@ -426,38 +576,60 @@ extern const std::string kUdpRecvSocketBuffer;
* rtsp/rtmp播放器
*
*
* Rtsp/rtmp player, pusher related settings name,
* These settings are not used in the configuration file
* Only used to set a specific player or pusher instance
* [AUTO-TRANSLATED:59086953]
*/
namespace Client {
// 指定网卡ip
// 指定网卡ip [AUTO-TRANSLATED:679fdccb]
// Specify network card ip
extern const std::string kNetAdapter;
// 设置rtp传输类型可选项有0(tcp默认)、1(udp)、2(组播)
// 设置方法:player[PlayerBase::kRtpType] = 0/1/2;
// 设置rtp传输类型可选项有0(tcp默认)、1(udp)、2(组播) [AUTO-TRANSLATED:bf73f779]
// Set rtp transport type, options are 0 (tcp, default), 1 (udp), 2 (multicast)
// 设置方法:player[PlayerBase::kRtpType] = 0/1/2; [AUTO-TRANSLATED:30eb2936]
// Set method: player[PlayerBase::kRtpType] = 0/1/2;
extern const std::string kRtpType;
// rtsp播放器发送信令心跳还是rtcp心跳可选项有0(同时发)、1(rtcp心跳)、2(信令心跳)
// 设置方法:player[PlayerBase::kRtspBeatType] = 0/1/2;
// rtsp播放器发送信令心跳还是rtcp心跳可选项有0(同时发)、1(rtcp心跳)、2(信令心跳) [AUTO-TRANSLATED:56d9ac7c]
// Whether the RTSP player sends signaling heartbeat or RTCP heartbeat, options are 0 (both), 1 (RTCP heartbeat), 2 (signaling heartbeat)
// 设置方法:player[PlayerBase::kRtspBeatType] = 0/1/2; [AUTO-TRANSLATED:ccc0726b]
// Set method: player[PlayerBase::kRtspBeatType] = 0/1/2;
extern const std::string kRtspBeatType;
// rtsp认证用户名
// rtsp认证用户名 [AUTO-TRANSLATED:5ab80e57]
// RTSP authentication username
extern const std::string kRtspUser;
// rtsp认证用用户密码可以是明文也可以是md5,md5密码生成方式 md5(username:realm:password)
// rtsp认证用用户密码可以是明文也可以是md5,md5密码生成方式 md5(username:realm:password) [AUTO-TRANSLATED:1228f997]
// RTSP authentication user password, can be plain text or MD5, MD5 password generation method md5(username:realm:password)
extern const std::string kRtspPwd;
// rtsp认证用用户密码是否为md5类型
// rtsp认证用用户密码是否为md5类型 [AUTO-TRANSLATED:208696d1]
// Whether the RTSP authentication user password is MD5 type
extern const std::string kRtspPwdIsMD5;
// 握手超时时间默认10,000 毫秒
// 握手超时时间默认10,000 毫秒 [AUTO-TRANSLATED:44b3f73f]
// Handshake timeout, default 10,000 milliseconds
extern const std::string kTimeoutMS;
// rtp/rtmp包接收超时时间默认5000秒
// rtp/rtmp包接收超时时间默认5000秒 [AUTO-TRANSLATED:e450d4cc]
// RTP/RTMP packet receive timeout, default 5000 seconds
extern const std::string kMediaTimeoutMS;
// rtsp/rtmp心跳时间,默认5000毫秒
// rtsp/rtmp心跳时间,默认5000毫秒 [AUTO-TRANSLATED:4d64f27f]
// RTSP/RTMP heartbeat time, default 5000 milliseconds
extern const std::string kBeatIntervalMS;
// 是否为性能测试模式性能测试模式开启后不会解析rtp或rtmp包
// 是否为性能测试模式性能测试模式开启后不会解析rtp或rtmp包 [AUTO-TRANSLATED:be9a797d]
// Whether it is performance test mode, performance test mode will not parse RTP or RTMP packets after being turned on
extern const std::string kBenchmarkMode;
// 播放器在触发播放成功事件时是否等待所有track ready时再回调
// 播放器在触发播放成功事件时是否等待所有track ready时再回调 [AUTO-TRANSLATED:73523e6d]
// Whether the player waits for all tracks to be ready before calling back when triggering the playback success event
extern const std::string kWaitTrackReady;
// rtsp播放指定track可选项有0(不指定,默认)、1(视频)、2(音频)
// 设置方法:player[Client::kPlayTrack] = 0/1/2;
// rtsp播放指定track可选项有0(不指定,默认)、1(视频)、2(音频) [AUTO-TRANSLATED:e4f481f9]
// RTSP playback specified track, options are 0 (not specified, default), 1 (video), 2 (audio)
// 设置方法:player[Client::kPlayTrack] = 0/1/2; [AUTO-TRANSLATED:0a2705c8]
// Set method: player[Client::kPlayTrack] = 0/1/2;
extern const std::string kPlayTrack;
//设置代理url目前只支持http协议
// 设置代理url目前只支持http协议 [AUTO-TRANSLATED:c84918cc]
// Set proxy url, currently only supports http protocol
extern const std::string kProxyUrl;
//设置开始rtsp倍速播放
// 设置开始rtsp倍速播放 [AUTO-TRANSLATED:5db03cad]
// Set the start RTSP playback speed
extern const std::string kRtspSpeed;
} // namespace Client
} // namespace mediakit

View File

@ -22,6 +22,12 @@ namespace mediakit {
* MIT协议MIT协议义务的同时ZLMediaKit软件版权信息的义务
* ZLMediaKit提供的各种服务中包括但不限于 "title""Server""User-Agent" "ZLMediaKit"
* ()
* This project adopts a class MIT license. Users, while fulfilling the obligations of the MIT license, should also follow the obligation to retain the copyright information of ZLMediaKit software.
* Users may not remove the "ZLMediaKit" information from the various services provided by ZLMediaKit, including but not limited to the "title", "Server", "User-Agent" fields.
* Otherwise, the main rights holder of this project (project initiator, main author) reserves the right to claim and sue.
* [AUTO-TRANSLATED:f214f734]
*/
#if !defined(ENABLE_VERSION)
const char kServerName[] = "ZLMediaKit-8.0(build in " __DATE__ " " __TIME__ ")";

View File

@ -19,7 +19,8 @@ using namespace std;
namespace mediakit {
//////////////////////////通用///////////////////////
// ////////////////////////通用/////////////////////// [AUTO-TRANSLATED:758fb788]
// ////////////////////////General///////////////////////
void UTF8ToUnicode(wchar_t *pOut, const char *pText) {
char *uchar = (char *) pOut;
uchar[1] = ((pText[0] & 0x0F) << 4) + ((pText[1] >> 2) & 0x0F);
@ -28,7 +29,8 @@ void UTF8ToUnicode(wchar_t *pOut, const char *pText) {
}
void UnicodeToUTF8(char *pOut, const wchar_t *pText) {
// 注意 WCHAR高低字的顺序,低字节在前,高字节在后
// 注意 WCHAR高低字的顺序,低字节在前,高字节在后 [AUTO-TRANSLATED:95408ed0]
// Note the order of the high and low bytes of WCHAR, the low byte is in front, and the high byte is behind
const char *pchar = (const char *) pText;
pOut[0] = (0xE0 | ((pchar[1] & 0xF0) >> 4));
pOut[1] = (0x80 | ((pchar[1] & 0x0F) << 2)) + ((pchar[0] & 0xC0) >> 6);
@ -47,7 +49,8 @@ char HexStrToBin(const char *str) {
auto high = HexCharToBin(str[0]);
auto low = HexCharToBin(str[1]);
if (high == -1 || low == -1) {
// 无法把16进制字符串转换为二进制
// 无法把16进制字符串转换为二进制 [AUTO-TRANSLATED:2c828a6f]
// Cannot convert hexadecimal string to binary
return -1;
}
return (high << 4) | low;
@ -73,13 +76,15 @@ static string UrlDecodeCommon(const string &str,const char* dont_unescape){
while (i < len) {
if (str[i] == '%') {
if (i + 3 > len) {
// %后面必须还有两个字节才会反转义
// %后面必须还有两个字节才会反转义 [AUTO-TRANSLATED:c7c4299a]
// There must be two bytes after % to escape
output.append(str, i, len - i);
break;
}
char ch = HexStrToBin(&(str[i + 1]));
if (ch == -1 || strchr(dont_unescape, (unsigned char)ch) != NULL) {
// %后面两个字节不是16进制字符串转义失败或者转义出来可能会造成url包含非path部分比如#?说明提交的是非法拼接的url直接拼接3个原始字符
// %后面两个字节不是16进制字符串转义失败或者转义出来可能会造成url包含非path部分比如#?说明提交的是非法拼接的url直接拼接3个原始字符 [AUTO-TRANSLATED:7c734054]
// The two bytes after % are not hexadecimal strings, the escape fails; or the escaped result may cause the url to contain non-path parts, such as #?, indicating that the submitted url is illegally spliced; directly splice the three original characters
output.append(str, i, 3);
} else {
output += ch;
@ -105,10 +110,13 @@ string strCoding::UrlEncodeComponent(const string &str) {
std::string strCoding::UrlEncodeUserOrPass(const std::string &str) {
// from rfc https://datatracker.ietf.org/doc/html/rfc3986
// §2.3 Unreserved characters (mark) [AUTO-TRANSLATED:d9a6a1d3]
// §2.3 Unreserved characters (mark)
//'-', '_', '.', '~'
// §2.2 Reserved characters (reserved)
// §2.2 Reserved characters (reserved) [AUTO-TRANSLATED:4da0c164]
// §2.2 Reserved characters (reserved)
// '$', '&', '+', ',', '/', ':', ';', '=', '?', '@',
// §3.2.1 [AUTO-TRANSLATED:f282bdcd]
// §3.2.1
// The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
// userinfo, so we must escape only '@', '/', and '?'.
@ -129,13 +137,15 @@ std::string strCoding::UrlDecodeComponent(const std::string &str) {
while (i < len) {
if (str[i] == '%') {
if (i + 3 > len) {
// %后面必须还有两个字节才会反转义
// %后面必须还有两个字节才会反转义 [AUTO-TRANSLATED:c7c4299a]
// There must be two bytes after % to escape
output.append(str, i, len - i);
break;
}
char ch = HexStrToBin(&(str[i + 1]));
if (ch == -1) {
// %后面两个字节不是16进制字符串转义失败直接拼接3个原始字符
// %后面两个字节不是16进制字符串转义失败直接拼接3个原始字符 [AUTO-TRANSLATED:10e614a4]
// The two bytes after % are not hexadecimal strings, the escape fails; directly splice the three original characters
output.append(str, i, 3);
} else {
output += ch;
@ -157,7 +167,8 @@ std::string strCoding::UrlDecodeUserOrPass(const std::string &str) {
const char *dont_unescape = "";
return UrlDecodeCommon(str,dont_unescape);
}
///////////////////////////////windows专用///////////////////////////////////
// /////////////////////////////windows专用/////////////////////////////////// [AUTO-TRANSLATED:e6109cf5]
// /////////////////////////////Windows Specific///////////////////////////////////
#if defined(_WIN32)
void UnicodeToGB2312(char* pOut, wchar_t uData)
{
@ -208,7 +219,8 @@ string strCoding::GB2312ToUTF8(const string &str) {
size_t i = 0, j = 0;
while (i < len)
{
//如果是英文直接复制就可以
// 如果是英文直接复制就可以 [AUTO-TRANSLATED:d6abdf68]
// If it is English, you can copy it directly
if (*(pText + i) >= 0)
{
pOut[j++] = pText[i++];

View File

@ -18,6 +18,9 @@ namespace mediakit{
/**
* rtmp解码类
* Generic rtmp decoder class
* [AUTO-TRANSLATED:b04614f4]
*/
class CommonRtmpDecoder : public RtmpCodec {
public:
@ -25,18 +28,28 @@ public:
/**
*
* Constructor
* [AUTO-TRANSLATED:41469869]
*/
CommonRtmpDecoder(const Track::Ptr &track) : RtmpCodec(track) {}
/**
* Rtmp并解码
* @param rtmp Rtmp数据包
* Input Rtmp and decode
* @param rtmp Rtmp data packet
* [AUTO-TRANSLATED:43b1eae8]
*/
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
};
/**
* rtmp编码类
* Generic rtmp encoder class
* [AUTO-TRANSLATED:4616a2a8]
*/
class CommonRtmpEncoder : public RtmpCodec {
public:
@ -46,6 +59,10 @@ public:
/**
*
* Input frame data
* [AUTO-TRANSLATED:d13bc7f2]
*/
bool inputFrame(const Frame::Ptr &frame) override;

View File

@ -26,7 +26,8 @@ void CommonRtpDecoder::obtainFrame() {
bool CommonRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool){
auto payload_size = rtp->getPayloadSize();
if (payload_size <= 0) {
//无实际负载
// 无实际负载 [AUTO-TRANSLATED:305af48f]
// No actual load
return false;
}
auto payload = rtp->getPayload();
@ -34,19 +35,23 @@ bool CommonRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool){
auto seq = rtp->getSeq();
if (_last_stamp != stamp || _frame->_buffer.size() > _max_frame_size) {
//时间戳发生变化或者缓存超过MAX_FRAME_SIZE则清空上帧数据
// 时间戳发生变化或者缓存超过MAX_FRAME_SIZE则清空上帧数据 [AUTO-TRANSLATED:96f15576]
// If the timestamp changes or the cache exceeds MAX_FRAME_SIZE, clear the previous frame data
if (!_frame->_buffer.empty()) {
//有有效帧,则输出
// 有有效帧,则输出 [AUTO-TRANSLATED:f3ff1bda]
// If there is a valid frame, output it
RtpCodec::inputFrame(_frame);
}
//新的一帧数据
// 新的一帧数据 [AUTO-TRANSLATED:5b5f3a35]
// New frame data
obtainFrame();
_frame->_dts = rtp->getStampMS();
_last_stamp = stamp;
_drop_flag = false;
} else if (_last_seq != 0 && (uint16_t)(_last_seq + 1) != seq) {
//时间戳未发生变化但是seq却不连续说明中间rtp丢包了那么整帧应该废弃
// 时间戳未发生变化但是seq却不连续说明中间rtp丢包了那么整帧应该废弃 [AUTO-TRANSLATED:577bf835]
// If the timestamp does not change, but the seq is not continuous, it means that the RTP packet has been lost in the middle, so the entire frame should be discarded
WarnL << "rtp丢包:" << _last_seq << " -> " << seq;
_drop_flag = true;
_frame->_buffer.clear();
@ -57,6 +62,10 @@ bool CommonRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool){
}
_last_seq = seq;
if (_drop_flag && rtp->getHeader()->mark) {
_drop_flag = false;
}
return false;
}

Some files were not shown because too many files have changed in this diff Show More