diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index a6e30e41..ac6ae2d7 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit a6e30e41f0c52f9d36c41eb79ac69b50020a6ac9 +Subproject commit ac6ae2d76cb7463243ade44e6aa75a552e82e5c9 diff --git a/api/include/mk_common.h b/api/include/mk_common.h index c2bf43bf..5d01e8e0 100755 --- a/api/include/mk_common.h +++ b/api/include/mk_common.h @@ -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监听端口,推荐80,传入0则随机分配 * @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监听端口,推荐554,传入0则随机分配 * @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监听端口,推荐1935,传入0则随机分配 * @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); diff --git a/api/include/mk_events.h b/api/include/mk_events.h index d765a8fa..42ebd755 100644 --- a/api/include/mk_events.h +++ b/api/include/mk_events.h @@ -23,6 +23,11 @@ typedef struct { * 注册或反注册MediaSource事件广播 * @param regist 注册为1,注销为0 * @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); diff --git a/api/include/mk_events_objects.h b/api/include/mk_events_objects.h index 42b72814..d4f2acfd 100644 --- a/api/include/mk_events_objects.h +++ b/api/include/mk_events_objects.h @@ -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 0代表失败,1代表成功 + * 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 字符串长度,为0则用strlen获取 + * 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); diff --git a/api/include/mk_frame.h b/api/include/mk_frame.h index 56309d4f..68d06ce2 100644 --- a/api/include/mk_frame.h +++ b/api/include/mk_frame.h @@ -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不知道后续是否还要添加track,所以会多等待3秒钟 * 如果产生的流是单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 1代表成功,0失败 + * 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); diff --git a/api/include/mk_h264_splitter.h b/api/include/mk_h264_splitter.h index 1249db6e..9dc6cd9c 100644 --- a/api/include/mk_h264_splitter.h +++ b/api/include/mk_h264_splitter.h @@ -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); diff --git a/api/include/mk_httpclient.h b/api/include/mk_httpclient.h index e07101de..5003af5b 100755 --- a/api/include/mk_httpclient.h +++ b/api/include/mk_httpclient.h @@ -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 请求url,支持http/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); diff --git a/api/include/mk_media.h b/api/include/mk_media.h index 85327dbf..423a08c7 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -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 1代表成功,0失败 + * 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 1代表成功,0失败 + * 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不知道后续是否还要添加track,所以会多等待3秒钟 * 如果产生的流是单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 1代表成功,0失败 + * 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 1代表成功,0失败 + * 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 1代表成功,0失败 + * 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 1代表成功,0失败 + * 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 1代表成功,0失败 + * 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 1代表成功,0失败 + * 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 1代表将处理seek请求,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 1为注册事件,0为注销事件 + * 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的ssrc,10进制的字符串打印 * @param con_type 0: tcp主动,1:udp主动,2:tcp被动,3:udp被动 + * @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的ssrc,10进制的字符串打印,如果为null或空字符串,则停止所有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); diff --git a/api/include/mk_player.h b/api/include/mk_player.h index 3aa9b836..8f499563 100755 --- a/api/include/mk_player.h +++ b/api/include/mk_player.h @@ -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.0~1.0 + * Set the on-demand progress bar + * @param ctx Object pointer + * @param progress Value range is 0.0~1.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.0~1.0 + + * [AUTO-TRANSLATED:522140b7] */ API_EXPORT float API_CALL mk_player_duration(mk_player ctx); /** * 获取点播播放进度,取值范围 0.0~1.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); diff --git a/api/include/mk_proxyplayer.h b/api/include/mk_proxyplayer.h index 4bd20333..b6d5eb79 100644 --- a/api/include/mk_proxyplayer.h +++ b/api/include/mk_proxyplayer.h @@ -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); diff --git a/api/include/mk_pusher.h b/api/include/mk_pusher.h index fd5c63d5..9d139822 100644 --- a/api/include/mk_pusher.h +++ b/api/include/mk_pusher.h @@ -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); diff --git a/api/include/mk_recorder.h b/api/include/mk_recorder.h index 96f35065..e4f101e7 100644 --- a/api/include/mk_recorder.h +++ b/api/include/mk_recorder.h @@ -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 1代表成功,0代表失败 + * 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); diff --git a/api/include/mk_rtp_server.h b/api/include/mk_rtp_server.h index 7a266fa5..1b837bec 100644 --- a/api/include/mk_rtp_server.h +++ b/api/include/mk_rtp_server.h @@ -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); diff --git a/api/include/mk_tcp.h b/api/include/mk_tcp.h index 7157b47e..2430b004 100644 --- a/api/include/mk_tcp.h +++ b/api/include/mk_tcp.h @@ -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 数据长度,等于0时,内部通过strlen获取 + * 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 数据长度,等于0时,内部通过strlen获取 + * 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); diff --git a/api/include/mk_thread.h b/api/include/mk_thread.h index b03c9502..1e22109d 100644 --- a/api/include/mk_thread.h +++ b/api/include/mk_thread.h @@ -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 线程个数,0时为cpu个数 * @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); diff --git a/api/include/mk_track.h b/api/include/mk_track.h index 63d34b0f..f836d0a0 100644 --- a/api/include/mk_track.h +++ b/api/include/mk_track.h @@ -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到track,通常你不需要调用此api + * 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); diff --git a/api/include/mk_transcode.h b/api/include/mk_transcode.h index 80866e69..59142ca3 100644 --- a/api/include/mk_transcode.h +++ b/api/include/mk_transcode.h @@ -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); diff --git a/api/include/mk_util.h b/api/include/mk_util.h index fb79dc82..fd0c6022 100644 --- a/api/include/mk_util.h +++ b/api/include/mk_util.h @@ -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__) diff --git a/api/source/mk_common.cpp b/api/source/mk_common.cpp index 335cd467..3bc274da 100644 --- a/api/source/mk_common.cpp +++ b/api/source/mk_common.cpp @@ -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", (LogLevel) log_level)); } if (log_mask & LOG_CALLBACK) { - //广播日志 + // 广播日志 [AUTO-TRANSLATED:67556df8] + // Broadcast log Logger::Instance().add(std::make_shared("EventChannel", (LogLevel) log_level)); } if (log_mask & LOG_FILE) { - //日志文件 + // 日志文件 [AUTO-TRANSLATED:afacc934] + // Log file auto channel = std::make_shared("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()); - //设置线程数 + // 设置线程数 [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->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(); 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(port); - //创建rtc tcp服务器 + // 创建rtc tcp服务器 [AUTO-TRANSLATED:1eefd92f] + // Create RTC TCP server rtcServer_tcp = std::make_shared(); rtcServer_tcp->start(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); diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp index 91ab640d..908a912b 100644 --- a/api/source/mk_events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -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 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(); + args.ssrc_multi_send = (*ini_ptr)["ssrc_multi_send"].empty() ? false : (*ini_ptr)["ssrc_multi_send"].as(); + args.pt = (*ini_ptr)["pt"].empty() ? 96 : (*ini_ptr)["pt"].as(); + args.data_type = (*ini_ptr)["data_type"].empty() ? MediaSourceEvent::SendRtpArgs::DataType::kRtpPS:(MediaSourceEvent::SendRtpArgs::DataType)(*ini_ptr)["data_type"].as(); + args.only_audio = (*ini_ptr)["only_audio"].empty() ? false : (*ini_ptr)["only_audio"].as(); + args.udp_rtcp_timeout = (*ini_ptr)["udp_rtcp_timeout"].empty() ? false : (*ini_ptr)["udp_rtcp_timeout"].as(); + 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(); + args.rtcp_timeout_ms = (*ini_ptr)["rtcp_timeout_ms"].empty() ? 30000 : (*ini_ptr)["rtcp_timeout_ms"].as(); + args.rtcp_send_interval_ms = (*ini_ptr)["rtcp_send_interval_ms"].empty() ? 5000 : (*ini_ptr)["rtcp_send_interval_ms"].as(); + std::shared_ptr 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 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()); } diff --git a/api/source/mk_h264_splitter.cpp b/api/source/mk_h264_splitter.cpp index b5cd06a3..d8001229 100644 --- a/api/source/mk_h264_splitter.cpp +++ b/api/source/mk_h264_splitter.cpp @@ -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; diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index 79f96786..65ae8e51 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -22,7 +22,8 @@ public: using Ptr = std::shared_ptr; 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(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 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(); + args.ssrc_multi_send = (*ini_ptr)["ssrc_multi_send"].empty() ? false : (*ini_ptr)["ssrc_multi_send"].as(); + args.pt = (*ini_ptr)["pt"].empty() ? 96 : (*ini_ptr)["pt"].as(); + args.data_type = (*ini_ptr)["data_type"].empty() ? MediaSourceEvent::SendRtpArgs::DataType::kRtpPS + : (MediaSourceEvent::SendRtpArgs::DataType)(*ini_ptr)["data_type"].as(); + args.only_audio = (*ini_ptr)["only_audio"].empty() ? false : (*ini_ptr)["only_audio"].as(); + args.udp_rtcp_timeout = (*ini_ptr)["udp_rtcp_timeout"].empty() ? false : (*ini_ptr)["udp_rtcp_timeout"].as(); + 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(); + args.rtcp_timeout_ms = (*ini_ptr)["rtcp_timeout_ms"].empty() ? 30000 : (*ini_ptr)["rtcp_timeout_ms"].as(); + args.rtcp_send_interval_ms = (*ini_ptr)["rtcp_send_interval_ms"].empty() ? 5000 : (*ini_ptr)["rtcp_send_interval_ms"].as(); + // sender参数无用 [AUTO-TRANSLATED:21590ae5] + // The sender parameter is useless + auto ref = *obj; + std::shared_ptr 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]() { diff --git a/api/source/mk_player.cpp b/api/source/mk_player.cpp index 42dc956b..0321b5df 100755 --- a/api/source/mk_player.cpp +++ b/api/source/mk_player.cpp @@ -57,14 +57,16 @@ public: void onEvent(bool is_shutdown, const SockException &ex){ lock_guard 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); }); } diff --git a/api/source/mk_proxyplayer.cpp b/api/source/mk_proxyplayer.cpp index 218a3a5e..7b5ace67 100644 --- a/api/source/mk_proxyplayer.cpp +++ b/api/source/mk_proxyplayer.cpp @@ -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 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 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()); diff --git a/api/source/mk_pusher.cpp b/api/source/mk_pusher.cpp index 9292081e..d54771db 100644 --- a/api/source/mk_pusher.cpp +++ b/api/source/mk_pusher.cpp @@ -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 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 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()); }); }); } diff --git a/api/source/mk_recorder.cpp b/api/source/mk_recorder.cpp index adc2a70a..3f57cc7b 100644 --- a/api/source/mk_recorder.cpp +++ b/api/source/mk_recorder.cpp @@ -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); diff --git a/api/source/mk_tcp.cpp b/api/source/mk_tcp.cpp index 166be169..f57e775c 100644 --- a/api/source/mk_tcp.cpp +++ b/api/source/mk_tcp.cpp @@ -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 >(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 >(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 >(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 >(new SessionWithSSL(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 >(new WebSocketClient(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 >(new WebSocketClient(events)); default: return nullptr; diff --git a/api/source/mk_thread.cpp b/api/source/mk_thread.cpp index 26379c8b..bf7b6098 100644 --- a/api/source/mk_thread.cpp +++ b/api/source/mk_thread.cpp @@ -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); } diff --git a/api/source/mk_util.cpp b/api/source/mk_util.cpp index e2817161..13e64887 100644 --- a/api/source/mk_util.cpp +++ b/api/source/mk_util.cpp @@ -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); }); diff --git a/api/tests/h264_media_server.c b/api/tests/h264_media_server.c index 1fc4f982..240d8145 100644 --- a/api/tests/h264_media_server.c +++ b/api/tests/h264_media_server.c @@ -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); } } diff --git a/api/tests/h264_pusher.c b/api/tests/h264_pusher.c index 81a018b0..b3c7f581 100644 --- a/api/tests/h264_pusher.c +++ b/api/tests/h264_pusher.c @@ -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); } } diff --git a/api/tests/httpclient.c b/api/tests/httpclient.c index 1e6c39ec..4d0948f2 100644 --- a/api/tests/httpclient.c +++ b/api/tests/httpclient.c @@ -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); diff --git a/api/tests/player_opencv.c b/api/tests/player_opencv.c index 1f527284..64fe5d49 100644 --- a/api/tests/player_opencv.c +++ b/api/tests/player_opencv.c @@ -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); } } diff --git a/api/tests/pusher.c b/api/tests/pusher.c index 03b2b971..bafbaaf5 100644 --- a/api/tests/pusher.c +++ b/api/tests/pusher.c @@ -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"); diff --git a/api/tests/server.c b/api/tests/server.c index a61862ed..e3ff7138 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -17,6 +17,11 @@ * 注册或反注册MediaSource事件广播 * @param regist 注册为1,注销为0 * @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 = "" @@ -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, diff --git a/api/tests/websocket.c b/api/tests/websocket.c index aadee586..6957aa96 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -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); } diff --git a/conf/config.ini b/conf/config.ini index 84b44623..6219a0b0 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -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 diff --git a/ext-codec/AAC.cpp b/ext-codec/AAC.cpp index e5fedf6f..60d9db4e 100644 --- a/ext-codec/AAC.cpp +++ b/ext-codec/AAC.cpp @@ -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; diff --git a/ext-codec/AAC.h b/ext-codec/AAC.h index 0b82ff7d..852bb44b 100644 --- a/ext-codec/AAC.h +++ b/ext-codec/AAC.h @@ -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); diff --git a/ext-codec/AACRtmp.h b/ext-codec/AACRtmp.h index 55c3eb34..5497a7db 100644 --- a/ext-codec/AACRtmp.h +++ b/ext-codec/AACRtmp.h @@ -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; diff --git a/ext-codec/AACRtp.cpp b/ext-codec/AACRtp.cpp index 006630f7..34b04833 100644 --- a/ext-codec/AACRtp.cpp +++ b/ext-codec/AACRtp.cpp @@ -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(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(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(_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); diff --git a/ext-codec/AACRtp.h b/ext-codec/AACRtp.h index f4559c27..473c5965 100644 --- a/ext-codec/AACRtp.h +++ b/ext-codec/AACRtp.h @@ -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; diff --git a/ext-codec/G711.cpp b/ext-codec/G711.cpp index e8c05df5..7a60fcda 100644 --- a/ext-codec/G711.cpp +++ b/ext-codec/G711.cpp @@ -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(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() << "/" diff --git a/ext-codec/G711.h b/ext-codec/G711.h index cacca6a1..3f1e9f64 100644 --- a/ext-codec/G711.h +++ b/ext-codec/G711.h @@ -18,6 +18,10 @@ namespace mediakit{ /** * G711音频通道 + * G711 audio channel + + + * [AUTO-TRANSLATED:57f8bc08] */ class G711Track : public AudioTrackImp{ public: diff --git a/ext-codec/G711Rtp.cpp b/ext-codec/G711Rtp.cpp index 7eb3ff9d..ac630563 100644 --- a/ext-codec/G711Rtp.cpp +++ b/ext-codec/G711Rtp.cpp @@ -16,7 +16,8 @@ void G711RtpEncoder::setOpt(int opt, const toolkit::Any ¶m) { 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; } } diff --git a/ext-codec/G711Rtp.h b/ext-codec/G711Rtp.h index dab74f42..e81ca9fc 100644 --- a/ext-codec/G711Rtp.h +++ b/ext-codec/G711Rtp.h @@ -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; diff --git a/ext-codec/H264.cpp b/ext-codec/H264.cpp index d542a8aa..fa0d97e2 100644 --- a/ext-codec/H264.cpp +++ b/ext-codec/H264.cpp @@ -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(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) = std::make_shared(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-23,24 (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-23,24 (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(); } return std::make_shared(sps, pps, 0, 0); diff --git a/ext-codec/H264.h b/ext-codec/H264.h index e76263a9..c65c8479 100644 --- a/ext-codec/H264.h +++ b/ext-codec/H264.h @@ -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; /** * 防止内存拷贝的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; /** * 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 264头长度,可以为3个或4个字节,一般为0x00 00 00 01 * @param pps_prefix_len 264头长度,可以为3个或4个字节,一般为0x00 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); diff --git a/ext-codec/H264Rtmp.cpp b/ext-codec/H264Rtmp.cpp index 6dd50807..ed431e91 100644 --- a/ext-codec/H264Rtmp.cpp +++ b/ext-codec/H264Rtmp.cpp @@ -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); diff --git a/ext-codec/H264Rtmp.h b/ext-codec/H264Rtmp.h index f610f47c..eafadf0b 100644 --- a/ext-codec/H264Rtmp.h +++ b/ext-codec/H264Rtmp.h @@ -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) {} /** * 输入264帧,可以不带sps 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; diff --git a/ext-codec/H264Rtp.cpp b/ext-codec/H264Rtp.cpp index f7ae195d..5c800ebf 100644 --- a/ext-codec/H264Rtp.cpp +++ b/ext-codec/H264Rtp.cpp @@ -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); diff --git a/ext-codec/H264Rtp.h b/ext-codec/H264Rtp.h index 3f98be07..f7a2c57e 100644 --- a/ext-codec/H264Rtp.h +++ b/ext-codec/H264Rtp.h @@ -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 diff --git a/ext-codec/H265.cpp b/ext-codec/H265.cpp index ae649b1d..8566bb1e 100644 --- a/ext-codec/H265.cpp +++ b/ext-codec/H265.cpp @@ -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(_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(); } return std::make_shared(vps, sps, pps, 0, 0, 0); diff --git a/ext-codec/H265.h b/ext-codec/H265.h index b95f72e8..0e86a0b8 100644 --- a/ext-codec/H265.h +++ b/ext-codec/H265.h @@ -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; /** * 防止内存拷贝的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; /** * 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 265头长度,可以为3个或4个字节,一般为0x00 00 00 01 * @param sps_prefix_len 265头长度,可以为3个或4个字节,一般为0x00 00 00 01 * @param pps_prefix_len 265头长度,可以为3个或4个字节,一般为0x00 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; diff --git a/ext-codec/H265Rtmp.cpp b/ext-codec/H265Rtmp.cpp index 69a27390..47eea37e 100644 --- a/ext-codec/H265Rtmp.cpp +++ b/ext-codec/H265Rtmp.cpp @@ -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); diff --git a/ext-codec/H265Rtmp.h b/ext-codec/H265Rtmp.h index 36c4d846..a87bc72a 100644 --- a/ext-codec/H265Rtmp.h +++ b/ext-codec/H265Rtmp.h @@ -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) {} /** * 输入265帧,可以不带sps 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; diff --git a/ext-codec/H265Rtp.cpp b/ext-codec/H265Rtp.cpp index 69690423..8b3a3501 100644 --- a/ext-codec/H265Rtp.cpp +++ b/ext-codec/H265Rtp.cpp @@ -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; } diff --git a/ext-codec/H265Rtp.h b/ext-codec/H265Rtp.h index 1a1c4a8b..b7b57452 100644 --- a/ext-codec/H265Rtp.h +++ b/ext-codec/H265Rtp.h @@ -22,6 +22,11 @@ namespace mediakit { * h265 rtp解码类 * 将 h265 over rtsp-rtp 解复用出 h265-Frame * 《草案(H265-over-RTP)draft-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 diff --git a/ext-codec/JPEG.h b/ext-codec/JPEG.h index 175699ce..adc5c47e 100644 --- a/ext-codec/JPEG.h +++ b/ext-codec/JPEG.h @@ -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 JPEGFrame(uint8_t pix_type, ARGS &&...args) : Parent(std::forward(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; } diff --git a/ext-codec/JPEGRtp.cpp b/ext-codec/JPEGRtp.cpp index b2e2240e..648d2d02 100644 --- a/ext-codec/JPEGRtp.cpp +++ b/ext-codec/JPEGRtp.cpp @@ -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(); diff --git a/ext-codec/JPEGRtp.h b/ext-codec/JPEGRtp.h index e8d5d81c..4931d845 100644 --- a/ext-codec/JPEGRtp.h +++ b/ext-codec/JPEGRtp.h @@ -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; diff --git a/ext-codec/L16.cpp b/ext-codec/L16.cpp index e38cc081..64ff4da1 100644 --- a/ext-codec/L16.cpp +++ b/ext-codec/L16.cpp @@ -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"; diff --git a/ext-codec/L16.h b/ext-codec/L16.h index 172fccdb..d3574fc2 100644 --- a/ext-codec/L16.h +++ b/ext-codec/L16.h @@ -18,6 +18,10 @@ namespace mediakit { /** * L16音频通道 + * L16 audio channel + + + * [AUTO-TRANSLATED:7a4b086f] */ class L16Track : public AudioTrackImp{ public: diff --git a/ext-codec/Opus.cpp b/ext-codec/Opus.cpp index 9fc116c8..beea2a13 100644 --- a/ext-codec/Opus.cpp +++ b/ext-codec/Opus.cpp @@ -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"; diff --git a/ext-codec/Opus.h b/ext-codec/Opus.h index e154b36e..a626b2f9 100644 --- a/ext-codec/Opus.h +++ b/ext-codec/Opus.h @@ -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(*this); } - //生成sdp + // 生成sdp [AUTO-TRANSLATED:663a9367] + // Generate sdp Sdp::Ptr getSdp(uint8_t payload_type) const override ; }; diff --git a/ext-codec/SPSParser.c b/ext-codec/SPSParser.c index de90a0d2..0a33ae80 100644 --- a/ext-codec/SPSParser.c +++ b/ext-codec/SPSParser.c @@ -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; +} \ No newline at end of file diff --git a/ext-codec/SPSParser.h b/ext-codec/SPSParser.h index 1943b757..1671d5c3 100644 --- a/ext-codec/SPSParser.h +++ b/ext-codec/SPSParser.h @@ -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 ); diff --git a/golang/tester/sdk_test.go b/golang/tester/sdk_test.go new file mode 100644 index 00000000..b03659b6 --- /dev/null +++ b/golang/tester/sdk_test.go @@ -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) +} diff --git a/golang/zlmediakit/mk_common.go b/golang/zlmediakit/mk_common.go new file mode 100644 index 00000000..4e5ff50c --- /dev/null +++ b/golang/zlmediakit/mk_common.go @@ -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))) +} diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 66632bc8..aa15830f 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -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 weakSelf = shared_from_this(); _timer = std::make_shared(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 functiondoDelayTask(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 functioncancel(); NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged); return; @@ -182,29 +197,38 @@ void FFmpegSource::findAsync(int maxWaitMS, const function_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 weakSelf = shared_from_this(); @@ -212,54 +236,66 @@ void FFmpegSource::startTimer(int timeout_ms) { _timer = std::make_shared(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 &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) : ""); }); diff --git a/server/FFmpegSource.h b/server/FFmpegSource.h index 067e2e83..8bb3130a 100644 --- a/server/FFmpegSource.h +++ b/server/FFmpegSource.h @@ -26,11 +26,16 @@ namespace FFmpeg { class FFmpegSnap { public: using onSnap = std::function; - /// 创建截图 - /// \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 &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: diff --git a/server/Process.cpp b/server/Process.cpp index d2b02abc..120ba0ab 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -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(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; diff --git a/server/System.cpp b/server/System.cpp index 1e2c8f57..c37e4958 100644 --- a/server/System.cpp +++ b/server/System.cpp @@ -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) diff --git a/server/VideoStack.cpp b/server/VideoStack.cpp index 8b5514f1..76b5ad49 100644 --- a/server/VideoStack.cpp +++ b/server/VideoStack.cpp @@ -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& chn) { void StackPlayer::play() { auto url = _url; - // 创建拉流 解码对象 + // 创建拉流 解码对象 [AUTO-TRANSLATED:9267c5dc] + // Create a pull stream decoding object _player = std::make_shared(); std::weak_ptr 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(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( videoTrack, 0, std::vector{"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(round(width * gaph)); int gapvPix = static_cast(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) { diff --git a/server/VideoStack.h b/server/VideoStack.h index 609060a7..1f9c114b 100644 --- a/server/VideoStack.h +++ b/server/VideoStack.h @@ -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: diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 3f7e18fd..c46f2269 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -90,7 +90,8 @@ static onceToken token([]() { }//namespace API using HttpApi = function; -//http api列表 +// http api列表 [AUTO-TRANSLATED:a05e9d9d] +// http api list static map s_map_api; static void responseApi(const Json::Value &res, const HttpSession::HttpResponseInvoker &invoker){ @@ -118,7 +119,8 @@ static HttpApi toApi(const function &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 &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 functionremainSize()) { - //有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 s_player_proxy; -//推流代理器列表 +// 推流代理器列表 [AUTO-TRANSLATED:539a1bcf] +// Push stream proxy list static ServiceController s_pusher_proxy; -//FFmpeg拉流代理器列表 +// FFmpeg拉流代理器列表 [AUTO-TRANSLATED:4bdedf10] +// FFmpeg pull stream proxy list static ServiceController s_ffmpeg_src; #if defined(ENABLE_RTPPROXY) -//rtp服务器列表 +// rtp服务器列表 [AUTO-TRANSLATED:2e362a8c] +// RTP server list static ServiceController 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 &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 &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 &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 &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 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(); @@ -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 &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(); if (allArgs["enable_tcp"].as() && !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(); if (allArgs["only_audio"].as()) { - // 兼容老版本请求,新版本去除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(); if (allArgs["enable_tcp"].as() && !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(); if (allArgs["only_audio"].as()) { - // 兼容老版本请求,新版本去除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(); if (!allArgs["use_ps"].empty()) { - // 兼容之前的use_ps参数 + // 兼容之前的use_ps参数 [AUTO-TRANSLATED:0193f489] + // Compatible with the previous use_ps parameter type = allArgs["use_ps"].as(); } 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(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(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(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"); } }); diff --git a/server/WebApi.h b/server/WebApi.h index 536c799e..cc0e95fd 100755 --- a/server/WebApi.h +++ b/server/WebApi.h @@ -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; #define API_ARGS_STRING_ASYNC API_ARGS_STRING, const mediakit::HttpSession::HttpResponseInvoker &invoker #define API_ARGS_VALUE sender, headerOut, allArgs, val -//注册http请求参数是map类型的http api +// 注册http请求参数是map类型的http api [AUTO-TRANSLATED:8a273897] +// Register http request parameters as map type http api void api_regist(const std::string &api_path, const std::function &func); -//注册http请求参数是map类型,但是可以异步回复的的http api +// 注册http请求参数是map类型,但是可以异步回复的的http api [AUTO-TRANSLATED:9da5d5f5] +// Register http request parameters as map type, but can be replied asynchronously http api void api_regist(const std::string &api_path, const std::function &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 &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 &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 &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 &func); template @@ -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(); \ diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 787b72b2..503e640e 100755 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -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 0 && should_retry) { @@ -198,12 +201,14 @@ void do_http_hook(const string &url, const ArgsType &body, const function 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 &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 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 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() { * 3、cookie标记是否有权限访问文件,如果有权限,直接返回文件 * 4、cookie中记录的url参数是否跟本次url参数一致,如果一致直接返回客户端错误码 * 5、触发kBroadcastHttpAccess事件 + * 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(); } diff --git a/server/WebHook.h b/server/WebHook.h index 1c952c8c..76c8fb46 100755 --- a/server/WebHook.h +++ b/server/WebHook.h @@ -15,7 +15,8 @@ #include #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 &func = nullptr); #endif //ZLMEDIAKIT_WEBHOOK_H diff --git a/server/main.cpp b/server/main.cpp index 92ca10ca..adcf4303 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -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 &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", logLevel)); #if !defined(ANDROID) auto fileChannel = std::make_shared("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()); 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(); - //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(); auto rtspSSLSrv = std::make_shared(); - //rtmp[s]服务器 + // rtmp[s]服务器 [AUTO-TRANSLATED:3ac98bf5] + // rtmp[s] server auto rtmpSrv = std::make_shared(); auto rtmpsSrv = std::make_shared(); - //http[s]服务器 + // http[s]服务器 [AUTO-TRANSLATED:5bbc8735] + // http[s] server auto httpSrv = std::make_shared(); auto httpsSrv = std::make_shared(); #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(); #endif//defined(ENABLE_RTPPROXY) #if defined(ENABLE_WEBRTC) auto rtcSrv_tcp = std::make_shared(); - //webrtc udp服务器 + // webrtc udp服务器 [AUTO-TRANSLATED:157a64e5] + // webrtc udp server auto rtcSrv_udp = std::make_shared(); 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(rtspPort, listen_ip); } - //rtsps服务器,端口默认322 + // rtsps服务器,端口默认322 [AUTO-TRANSLATED:e8a9fd71] + // rtsps server, default port 322 if (rtspsPort) { rtspSSLSrv->start(rtspsPort, listen_ip); } - //rtmp服务器,端口默认1935 + // rtmp服务器,端口默认1935 [AUTO-TRANSLATED:58324c74] + // rtmp server, default port 1935 if (rtmpPort) { rtmpSrv->start(rtmpPort, listen_ip); } - //rtmps服务器,端口默认19350 + // rtmps服务器,端口默认19350 [AUTO-TRANSLATED:c565ff4e] + // rtmps server, default port 19350 if (rtmpsPort) { rtmpsSrv->start(rtmpsPort, listen_ip); } - //http服务器,端口默认80 + // http服务器,端口默认80 [AUTO-TRANSLATED:8899e852] + // http server, default port 80 if (httpPort) { httpSrv->start(httpPort, listen_ip); } - //https服务器,端口默认443 + // https服务器,端口默认443 [AUTO-TRANSLATED:24999616] + // https server, default port 443 if (httpsPort) { httpsSrv->start(httpsPort, listen_ip); } - //telnet远程调试服务器 + // telnet远程调试服务器 [AUTO-TRANSLATED:577cb7cf] + // telnet remote debug server if (shellPort) { shellSrv->start(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(rtcPort, listen_ip);} if (rtcTcpPort) { rtcSrv_tcp->start(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(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 << "程序退出完毕!"; diff --git a/src/Codec/H264Encoder.cpp b/src/Codec/H264Encoder.cpp index 414eb5f1..787e72ad 100644 --- a/src/Codec/H264Encoder.cpp +++ b/src/Codec/H264Encoder.cpp @@ -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的最后一帧(上例中是第5帧)设置为B帧, 这个码流就是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; diff --git a/src/Codec/Transcode.cpp b/src/Codec/Transcode.cpp index 2663b8d4..ebe8770a 100644 --- a/src/Codec/Transcode.cpp +++ b/src/Codec/Transcode.cpp @@ -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; } diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index 2d424091..bffb5276 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -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(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(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); diff --git a/src/Common/Device.h b/src/Common/Device.h index 6af52626..c670b77a 100644 --- a/src/Common/Device.h +++ b/src/Common/Device.h @@ -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; - //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 播放时间戳,单位毫秒;等于0时内部会赋值为dts + * 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 播放时间戳,单位毫秒;等于0时内部会赋值为dts + * 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; diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index 7d63a4e4..73dec93f 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -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) { diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index 2ad57a47..809a84d4 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -27,16 +27,27 @@ public: * 添加track,内部会调用Track的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: * 添加track,内部会调用Track的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完毕,如果是单Track,会最多等待3秒才会触发onAllTrackReady * 这样会增加生成流的延时,如果添加了音视频双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数,取值范围>=1;该方法与addTrackCompleted类型; * 在设置单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 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输出frame,在onAllTrackReady触发后才会调用此方法 * @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); diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 8406f680..14c18d13 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -95,11 +95,13 @@ MediaSource::~MediaSource() { std::shared_ptr MediaSource::getOwnership() { if (_owned.test_and_set()) { - //已经被所有 + // 已经被所有 [AUTO-TRANSLATED:bab937dc] + // Already owned by all return nullptr; } weak_ptr 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 *) 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(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 &s std::shared_ptr 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 &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 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 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 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 weak_sender = sender.shared_from_this(); _async_close_timer = std::make_shared(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 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); diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index afbf8785..1acf3666 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -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 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 getMediaTracks(MediaSource &sender, bool trackReady = true) const { return std::vector(); }; - // 获取MultiMediaSourceMuxer对象 + // 获取MultiMediaSourceMuxer对象 [AUTO-TRANSLATED:2de96d44] + // Get MultiMediaSourceMuxer object virtual std::shared_ptr getMuxer(MediaSource &sender) const { return nullptr; } - // 获取RtpProcess对象 + // 获取RtpProcess对象 [AUTO-TRANSLATED:c6b7da43] + // Get RtpProcess object virtual std::shared_ptr 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 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 @@ -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 &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 { 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 getOwnership(); - // 获取所有Track + // 获取所有Track [AUTO-TRANSLATED:59f1c570] + // Get all Tracks std::vector 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 &listener); - // 获取监听者 + // 获取监听者 [AUTO-TRANSLATED:5c9cbb82] + // Get listener std::weak_ptr 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 &info_list)> &cb, const std::function &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 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 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 getMuxer() const; - // 获取RtpProcess对象 + // 获取RtpProcess对象 [AUTO-TRANSLATED:c6b7da43] + // Get the RtpProcess object std::shared_ptr 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 &session, const std::function &cb); - // 遍历所有流 + // 遍历所有流 [AUTO-TRANSLATED:a39b2399] + // Traverse all streams static void for_each_media(const std::function &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 _listener; - // 对象个数统计 + // 对象个数统计 [AUTO-TRANSLATED:f4a012d0] + // Object count statistics toolkit::ObjectStatistic _statistic; }; diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index 6036fed6..7db0202c 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -44,7 +44,8 @@ public: class FramePacedSender : public FrameWriterInterface, public std::enable_shared_from_this { public: using OnFrame = std::function; - // 最小缓存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(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_ptrsetListener(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(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(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::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(weak_self.lock()); _ring = std::make_shared(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(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(); } } diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index ac20aeea..d3035c86 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -41,33 +41,54 @@ public: /** * 设置事件监听器 * @param listener 监听器 + * Set event listener + * @param listener Listener + + * [AUTO-TRANSLATED:d829419b] */ void setMediaListener(const std::weak_ptr &listener); /** * 设置Track就绪事件监听器 * @param listener 事件监听器 + * Set Track ready event listener + * @param listener Event listener + + * [AUTO-TRANSLATED:64262ac5] */ void setTrackListener(const std::weak_ptr &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 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 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 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输出frame,在onAllTrackReady触发后才会调用此方法 * @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 _statistic; }; diff --git a/src/Common/PacketCache.h b/src/Common/PacketCache.h index 5e930a5f..593f0203 100644 --- a/src/Common/PacketCache.h +++ b/src/Common/PacketCache.h @@ -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 > > 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); diff --git a/src/Common/Parser.h b/src/Common/Parser.h index 324658ba..3bf23826 100644 --- a/src/Common/Parser.h +++ b/src/Common/Parser.h @@ -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 ¶ms() 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; diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index 892d2565..0913b7fa 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -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((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((_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; diff --git a/src/Common/Stamp.h b/src/Common/Stamp.h index 0b525748..a21aacfa 100644 --- a/src/Common/Stamp.h +++ b/src/Common/Stamp.h @@ -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); diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 025d61d7..31febe1a 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -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() { diff --git a/src/Common/config.h b/src/Common/config.h index 72f5cb34..5371654b 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -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; -// 收到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; -// 播放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 &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 { -// 每个流媒体服务器的ID(GUID) +// 每个流媒体服务器的ID(GUID) [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 打包时,低延迟开关,默认关闭(为0),h264存在一帧多个slice(NAL)的情况,在这种情况下,如果开启可能会导致画面花屏 +// rtp 打包时,低延迟开关,默认关闭(为0),h264存在一帧多个slice(NAL)的情况,在这种情况下,如果开启可能会导致画面花屏 [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-2016,c.2.4规定), -//最好为20 的倍数,程序自动向20的倍数取整 +// 国标发送g711 rtp 打包时,每个包的语音时长是多少,默认是100 ms,范围为20~180ms (gb28181-2016,c.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 diff --git a/src/Common/macros.cpp b/src/Common/macros.cpp index f6529ce4..e9574dcc 100644 --- a/src/Common/macros.cpp +++ b/src/Common/macros.cpp @@ -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__ ")"; diff --git a/src/Common/strCoding.cpp b/src/Common/strCoding.cpp index 49a345ef..5eafe40f 100644 --- a/src/Common/strCoding.cpp +++ b/src/Common/strCoding.cpp @@ -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++]; diff --git a/src/Extension/CommonRtmp.h b/src/Extension/CommonRtmp.h index 636e9fb8..faff330f 100644 --- a/src/Extension/CommonRtmp.h +++ b/src/Extension/CommonRtmp.h @@ -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; diff --git a/src/Extension/CommonRtp.cpp b/src/Extension/CommonRtp.cpp index cf395ef4..fd3b8abd 100644 --- a/src/Extension/CommonRtp.cpp +++ b/src/Extension/CommonRtp.cpp @@ -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; } diff --git a/src/Extension/CommonRtp.h b/src/Extension/CommonRtp.h index 96cb5457..46a370db 100644 --- a/src/Extension/CommonRtp.h +++ b/src/Extension/CommonRtp.h @@ -18,6 +18,9 @@ namespace mediakit{ /** * 通用 rtp解码类 + * Generic rtp decoder class + + * [AUTO-TRANSLATED:41b57089] */ class CommonRtpDecoder : public RtpCodec { public: @@ -27,6 +30,11 @@ public: * 构造函数 * @param codec 编码id * @param max_frame_size 允许的最大帧大小 + * Constructor + * @param codec codec id + * @param max_frame_size maximum allowed frame size + + * [AUTO-TRANSLATED:c6b0414f] */ CommonRtpDecoder(CodecId codec, size_t max_frame_size = 2 * 1024); @@ -34,6 +42,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; @@ -51,6 +64,9 @@ private: /** * 通用 rtp编码类 + * Generic rtp encoder class + + * [AUTO-TRANSLATED:bb3991a5] */ class CommonRtpEncoder : public RtpCodec { public: @@ -58,6 +74,10 @@ public: /** * 输入帧数据并编码成rtp + * Input frame data and encode into rtp + + + * [AUTO-TRANSLATED:02bc9009] */ bool inputFrame(const Frame::Ptr &frame) override; }; diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index 49feeb94..f4309675 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -45,7 +45,8 @@ void Factory::registerPlugin(const CodecPlugin &plugin) { Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { auto codec = getCodecId(track->_codec); if (codec == CodecInvalid) { - // 根据传统的payload type 获取编码类型以及采样率等信息 + // 根据传统的payload type 获取编码类型以及采样率等信息 [AUTO-TRANSLATED:d01ca068] + // Get the encoding type, sampling rate, and other information based on the traditional payload type codec = RtpPayload::getCodecId(track->_pt); } auto it = s_plugins.find(codec); @@ -83,7 +84,8 @@ RtpCodec::Ptr Factory::getRtpDecoderByCodecId(CodecId codec) { return it->second->getRtpDecoderByCodecId(); } -/////////////////////////////rtmp相关/////////////////////////////////////////// +// ///////////////////////////rtmp相关/////////////////////////////////////////// [AUTO-TRANSLATED:da9645df] +// ///////////////////////////rtmp related/////////////////////////////////////////// static CodecId getVideoCodecIdByAmf(const AMFValue &val){ if (val.type() == AMF_STRING) { @@ -197,7 +199,8 @@ AMFValue Factory::getAmfByCodecId(CodecId codecId) { Frame::Ptr Factory::getFrameFromPtr(CodecId codec, const char *data, size_t bytes, uint64_t dts, uint64_t pts) { auto it = s_plugins.find(codec); if (it == s_plugins.end()) { - // 创建不支持codec的frame + // 创建不支持codec的frame [AUTO-TRANSLATED:00936c6c] + // Create a frame that does not support the codec return std::make_shared(codec, (char *)data, bytes, dts, pts); } return it->second->getFrameFromPtr(data, bytes, dts, pts); diff --git a/src/Extension/Factory.h b/src/Extension/Factory.h index 8ec446bc..d7d3665c 100644 --- a/src/Extension/Factory.h +++ b/src/Extension/Factory.h @@ -44,6 +44,9 @@ class Factory { public: /** * 注册插件,非线程安全的 + * Register plugin, not thread-safe + + * [AUTO-TRANSLATED:43e22d01] */ static void registerPlugin(const CodecPlugin &plugin); @@ -53,17 +56,31 @@ public: * @param sample_rate 采样率,视频固定为90000 * @param channels 音频通道数 * @param sample_bit 音频采样位数 + * Get track by codec_id + * @param codecId codec id + * @param sample_rate sample rate, video is fixed to 90000 + * @param channels number of audio channels + * @param sample_bit audio sample bit + + * [AUTO-TRANSLATED:397b982e] */ static Track::Ptr getTrackByCodecId(CodecId codecId, int sample_rate = 0, int channels = 0, int sample_bit = 0); - ////////////////////////////////rtsp相关////////////////////////////////// + // //////////////////////////////rtsp相关////////////////////////////////// [AUTO-TRANSLATED:884055ec] + // //////////////////////////////rtsp相关////////////////////////////////// /** * 根据sdp生成Track对象 + * Generate Track object based on sdp + + * [AUTO-TRANSLATED:79a99990] */ static Track::Ptr getTrackBySdp(const SdpTrack::Ptr &track); /** * 根据c api 抽象的Track生成具体Track对象 + * Generate specific Track object based on Track abstracted from c api + + * [AUTO-TRANSLATED:991e7721] */ static Track::Ptr getTrackByAbstractTrack(const Track::Ptr& track); @@ -71,43 +88,72 @@ public: * 根据codec id生成rtp编码器 * @param codec_id 编码id * @param pt rtp payload type + * Generate rtp encoder based on codec id + * @param codec_id codec id + * @param pt rtp payload type + + * [AUTO-TRANSLATED:3895b39c] */ static RtpCodec::Ptr getRtpEncoderByCodecId(CodecId codec_id, uint8_t pt); /** * 根据Track生成Rtp解包器 + * Generate Rtp unpacker based on Track + + * [AUTO-TRANSLATED:50dbf826] */ static RtpCodec::Ptr getRtpDecoderByCodecId(CodecId codec); - ////////////////////////////////rtmp相关////////////////////////////////// + // //////////////////////////////rtmp相关////////////////////////////////// [AUTO-TRANSLATED:df02d6fb] + // //////////////////////////////rtmp相关////////////////////////////////// /** * 根据amf对象获取视频相应的Track * @param amf rtmp metadata中的videocodecid的值 + * Get the corresponding video Track based on the amf object + * @param amf the value of videocodecid in rtmp metadata + + * [AUTO-TRANSLATED:c0c632c1] */ static Track::Ptr getVideoTrackByAmf(const AMFValue &amf); /** * 根据amf对象获取音频相应的Track * @param amf rtmp metadata中的audiocodecid的值 + * Get the corresponding audio Track based on the amf object + * @param amf the value of audiocodecid in rtmp metadata + + * [AUTO-TRANSLATED:fc34f9e4] */ static Track::Ptr getAudioTrackByAmf(const AMFValue& amf, int sample_rate, int channels, int sample_bit); /** * 根据Track获取Rtmp的编码器 * @param track 媒体描述对象 + * Get the Rtmp encoder based on Track + * @param track media description object + + * [AUTO-TRANSLATED:81fc38af] */ static RtmpCodec::Ptr getRtmpEncoderByTrack(const Track::Ptr &track); /** * 根据Track获取Rtmp的解码器 * @param track 媒体描述对象 + * Get the Rtmp decoder based on Track + * @param track media description object + + * [AUTO-TRANSLATED:0744b09e] */ static RtmpCodec::Ptr getRtmpDecoderByTrack(const Track::Ptr &track); /** * 根据codecId获取rtmp的codec描述 + * Get the rtmp codec description based on codecId + + + * [AUTO-TRANSLATED:67c749b7] */ static AMFValue getAmfByCodecId(CodecId codecId); diff --git a/src/Extension/Frame.cpp b/src/Extension/Frame.cpp index 6daafda5..ceccced9 100644 --- a/src/Extension/Frame.cpp +++ b/src/Extension/Frame.cpp @@ -42,7 +42,8 @@ FrameStamp::FrameStamp(Frame::Ptr frame, Stamp &stamp, int modify_stamp) { setIndex(frame->getIndex()); _frame = std::move(frame); - // kModifyStampSystem时采用系统时间戳,kModifyStampRelative采用相对时间戳 + // kModifyStampSystem时采用系统时间戳,kModifyStampRelative采用相对时间戳 [AUTO-TRANSLATED:54dd5685] + // When using kModifyStampSystem, the system timestamp is used, and when using kModifyStampRelative, the relative timestamp is used. stamp.revise(_frame->dts(), _frame->pts(), _dts, _pts, modify_stamp == ProtocolOption::kModifyStampSystem); } @@ -94,7 +95,8 @@ int getMpegIdByCodec(CodecId codec) { CodecId getCodecByMpegId(int mpeg_id) { if (mpeg_id == PSI_STREAM_RESERVED || mpeg_id == 0xBD) { - // 海康的 PS 流中会有0xBD 的包 + // 海康的 PS 流中会有0xBD 的包 [AUTO-TRANSLATED:32a250cb] + // Hikvision's PS stream will have 0xBD packets. return CodecInvalid; } @@ -165,7 +167,8 @@ static size_t constexpr kMaxFrameCacheSize = 100; bool FrameMerger::willFlush(const Frame::Ptr &frame) const{ if (_frame_cache.empty()) { - //缓存为空 + // 缓存为空 [AUTO-TRANSLATED:b9505a19] + // Cache is empty. return false; } if (!frame) { @@ -173,29 +176,34 @@ bool FrameMerger::willFlush(const Frame::Ptr &frame) const{ } switch (_type) { case none : { - //frame不是完整的帧,我们合并为一帧 + // frame不是完整的帧,我们合并为一帧 [AUTO-TRANSLATED:00e9f200] + // The frame is not a complete frame, we merge it into one frame. bool new_frame = false; switch (frame->getCodecId()) { case CodecH264: case CodecH265: { - //如果是新的一帧,前面的缓存需要输出 + // 如果是新的一帧,前面的缓存需要输出 [AUTO-TRANSLATED:b4deff81] + // If it is a new frame, the previous cache needs to be output. new_frame = frame->prefixSize(); break; } default: break; } - //遇到新帧、或时间戳变化或缓存太多,防止内存溢出,则flush输出 + // 遇到新帧、或时间戳变化或缓存太多,防止内存溢出,则flush输出 [AUTO-TRANSLATED:0292964a] + // When encountering a new frame, or a timestamp change, or too much cache, flush the output to prevent memory overflow. return new_frame || _frame_cache.back()->dts() != frame->dts() || _frame_cache.size() > kMaxFrameCacheSize; } case mp4_nal_size: case h264_prefix: { if (!_have_decode_able_frame) { - //缓存中没有有效的能解码的帧,所以这次不flush + // 缓存中没有有效的能解码的帧,所以这次不flush [AUTO-TRANSLATED:5d860722] + // There are no valid frames that can be decoded in the cache, so no flush this time. return _frame_cache.size() > kMaxFrameCacheSize; } if (_frame_cache.back()->dts() != frame->dts() || frame->decodeAble() || frame->configFrame()) { - //时间戳变化了,或新的一帧,或遇到config帧,立即flush + // 时间戳变化了,或新的一帧,或遇到config帧,立即flush [AUTO-TRANSLATED:8c2523b1] + // When the timestamp changes, or a new frame, or a config frame is encountered, flush immediately. return true; } return _frame_cache.size() > kMaxFrameCacheSize; @@ -207,8 +215,10 @@ bool FrameMerger::willFlush(const Frame::Ptr &frame) const{ void FrameMerger::doMerge(BufferLikeString &merged, const Frame::Ptr &frame) const{ switch (_type) { case none : { - //此处是合并ps解析输出的流,解析出的流可能是半帧或多帧,不能简单的根据nal type过滤 - //此流程只用于合并ps解析输出为H264/H265,后面流程有split和忽略无效帧操作 + // 此处是合并ps解析输出的流,解析出的流可能是半帧或多帧,不能简单的根据nal type过滤 [AUTO-TRANSLATED:4a231bdc] + // Here, the PS parsing output stream is merged. The parsed stream may be half a frame or multiple frames, and cannot be simply filtered according to the nal type. + // 此流程只用于合并ps解析输出为H264/H265,后面流程有split和忽略无效帧操作 [AUTO-TRANSLATED:2d40274e] + // This process is only used to merge PS parsing output into H264/H265. The subsequent process has split and ignore invalid frame operations. merged.append(frame->data(), frame->size()); break; } @@ -251,7 +261,8 @@ bool FrameMerger::inputFrame(const Frame::Ptr &frame, onOutput cb, BufferLikeStr bool have_key_frame = back->keyFrame(); if (_frame_cache.size() != 1 || _type == mp4_nal_size || buffer) { - //在MP4模式下,一帧数据也需要在前添加nalu_size + // 在MP4模式下,一帧数据也需要在前添加nalu_size [AUTO-TRANSLATED:4a7e5c20] + // In MP4 mode, a frame of data also needs to add nalu_size in front. BufferLikeString tmp; BufferLikeString &merged = buffer ? *buffer : tmp; @@ -301,6 +312,9 @@ void FrameMerger::flush() { } /** * 写帧接口转function,辅助类 + * Write frame interface to function, auxiliary class + + * [AUTO-TRANSLATED:ce04a5e9] */ class FrameWriterInterfaceHelper : public FrameWriterInterface { public: @@ -309,11 +323,18 @@ public: /** * inputFrame后触发onWriteFrame回调 + * Trigger onWriteFrame callback after inputFrame + + * [AUTO-TRANSLATED:169e5944] */ FrameWriterInterfaceHelper(onWriteFrame cb) { _callback = std::move(cb); } /** * 写入帧数据 + * Write frame data + + + * [AUTO-TRANSLATED:d46c6fc2] */ bool inputFrame(const Frame::Ptr &frame) override { return _callback(frame); } diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index da55e8fe..49ef4766 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -55,11 +55,17 @@ typedef enum { /** * 字符串转媒体类型转 + * String to media type conversion + + * [AUTO-TRANSLATED:59850011] */ TrackType getTrackType(const std::string &str); /** * 媒体类型转字符串 + * Media type to string conversion + + * [AUTO-TRANSLATED:0456e0e2] */ const char* getTrackString(TrackType type); @@ -67,41 +73,67 @@ const char* getTrackString(TrackType type); * 根据SDP中描述获取codec_id * @param str * @return + * Get codec_id from SDP description + * @param str + * @return + + * [AUTO-TRANSLATED:024f2ed1] */ CodecId getCodecId(const std::string &str); /** * 获取编码器名称 + * Get encoder name + + * [AUTO-TRANSLATED:0253534b] */ const char *getCodecName(CodecId codecId); /** * 获取音视频类型 + * Get audio/video type + + * [AUTO-TRANSLATED:e2f06ac2] */ TrackType getTrackType(CodecId codecId); /** * 根据codecid获取mov object id + * Get mov object id by codecid + + * [AUTO-TRANSLATED:c315b87d] */ int getMovIdByCodec(CodecId codecId); /** * 根据mov object id获取CodecId + * Get CodecId by mov object id + + * [AUTO-TRANSLATED:de2237a1] */ CodecId getCodecByMovId(int object_id); /** * 根据codecid获取mpeg id + * Get mpeg id by codecid + + * [AUTO-TRANSLATED:d365eac7] */ int getMpegIdByCodec(CodecId codec); /** * 根据mpeg id获取CodecId + * Get CodecId by mpeg id + + * [AUTO-TRANSLATED:ca190565] */ CodecId getCodecByMpegId(int mpeg_id); /** * 编码信息的抽象接口 + * Abstract interface for encoding information + + * [AUTO-TRANSLATED:c3b14625] */ class CodecInfo { public: @@ -111,31 +143,49 @@ public: /** * 获取编解码器类型 + * Get codec type + + * [AUTO-TRANSLATED:2dbf103b] */ virtual CodecId getCodecId() const = 0; /** * 获取编码器名称 + * Get encoder name + + * [AUTO-TRANSLATED:a92f41f6] */ const char *getCodecName() const; /** * 获取音视频类型 + * Get audio/video type + + * [AUTO-TRANSLATED:ff8ba95b] */ TrackType getTrackType() const; /** * 获取音视频类型描述 + * Get audio/video type description + + * [AUTO-TRANSLATED:a460e432] */ std::string getTrackTypeStr() const; /** * 设置track index, 用于支持多track + * Set track index, for multi-track support + + * [AUTO-TRANSLATED:da5bdb91] */ void setIndex(int index) { _index = index; } /** * 获取track index, 用于支持多track + * Get track index, for multi-track support + + * [AUTO-TRANSLATED:1e96c587] */ int getIndex() const { return _index < 0 ? (int)getTrackType() : _index; } @@ -145,6 +195,9 @@ private: /** * 帧类型的抽象接口 + * Abstract interface for frame types + + * [AUTO-TRANSLATED:eb166e7e] */ class Frame : public toolkit::Buffer, public CodecInfo { public: @@ -152,32 +205,51 @@ public: /** * 返回解码时间戳,单位毫秒 + * Return decoding timestamp, in milliseconds + + * [AUTO-TRANSLATED:00072dad] */ virtual uint64_t dts() const = 0; /** * 返回显示时间戳,单位毫秒 + * Return display timestamp, in milliseconds + + * [AUTO-TRANSLATED:c7eecb91] */ virtual uint64_t pts() const { return dts(); } /** * 前缀长度,譬如264前缀为0x00 00 00 01,那么前缀长度就是4 * aac前缀则为7个字节 + * Prefix length, for example, the 264 prefix is 0x00 00 00 01, so the prefix length is 4 + * aac prefix is 7 bytes + + * [AUTO-TRANSLATED:6334f58e] */ virtual size_t prefixSize() const = 0; /** * 返回是否为关键帧 + * Return whether it is a key frame + + * [AUTO-TRANSLATED:2e52426a] */ virtual bool keyFrame() const = 0; /** * 是否为配置帧,譬如sps pps vps + * Whether it is a configuration frame, such as sps pps vps + + * [AUTO-TRANSLATED:595c7ecf] */ virtual bool configFrame() const = 0; /** * 是否可以缓存 + * Whether it can be cached + + * [AUTO-TRANSLATED:5c35d3e0] */ virtual bool cacheAble() const { return true; } @@ -185,29 +257,44 @@ public: * 该帧是否可以丢弃 * SEI/AUD帧可以丢弃 * 默认都不能丢帧 + * Whether this frame can be dropped + * SEI/AUD frames can be dropped + * By default, no frames can be dropped + + * [AUTO-TRANSLATED:42957087] */ virtual bool dropAble() const { return false; } /** * 是否为可解码帧 * sps pps等帧不能解码 + * Whether it is a decodable frame + * sps pps frames cannot be decoded + + * [AUTO-TRANSLATED:52f093c7] */ virtual bool decodeAble() const { if (getTrackType() != TrackVideo) { - //非视频帧都可以解码 + // 非视频帧都可以解码 [AUTO-TRANSLATED:24aa4342] + // Non-video frames can be decoded return true; } - //默认非sps pps帧都可以解码 + // 默认非sps pps帧都可以解码 [AUTO-TRANSLATED:b14d1e34] + // By default, non-sps pps frames can be decoded return !configFrame(); } /** * 返回可缓存的frame + * Return the cacheable frame + + * [AUTO-TRANSLATED:88fb9c3e] */ static Ptr getCacheAbleFrame(const Ptr &frame); private: - //对象个数统计 + // 对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + // Object count statistics toolkit::ObjectStatistic _statistic; }; @@ -250,7 +337,8 @@ public: toolkit::BufferLikeString _buffer; private: - //对象个数统计 + // 对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + // Object count statistics toolkit::ObjectStatistic _statistic; protected: @@ -258,7 +346,8 @@ protected: FrameImp() = default; }; -// 包装一个指针成不可缓存的frame +// 包装一个指针成不可缓存的frame [AUTO-TRANSLATED:c3e5d65e] +// Wrap a pointer into a non-cacheable frame class FrameFromPtr : public Frame { public: using Ptr = std::shared_ptr; @@ -311,6 +400,12 @@ protected: * ZLMediaKit会先把这种复合帧split成单个帧然后再处理 * 一个复合帧可以通过无内存拷贝的方式切割成多个子Frame * 提供该类的目的是切割复合帧时防止内存拷贝,提高性能 + * A Frame class can have multiple frames (AAC), and the timestamp will change + * ZLMediaKit will first split this composite frame into single frames and then process it + * A composite frame can be split into multiple sub-Frames without memory copy + * The purpose of providing this class is to prevent memory copy when splitting composite frames, improving performance + + * [AUTO-TRANSLATED:4010c0a5] */ template class FrameInternalBase : public Parent { @@ -333,6 +428,12 @@ private: * ZLMediaKit会先把这种复合帧split成单个帧然后再处理 * 一个复合帧可以通过无内存拷贝的方式切割成多个子Frame * 提供该类的目的是切割复合帧时防止内存拷贝,提高性能 + * A Frame class can have multiple frames, they are separated by 0x 00 00 01 + * ZLMediaKit will first split this composite frame into single frames and then process it + * A composite frame can be split into multiple sub-Frames without memory copy + * The purpose of providing this class is to prevent memory copy when splitting composite frames, improving performance + + * [AUTO-TRANSLATED:ed49148b] */ template class FrameInternal : public FrameInternalBase { @@ -342,7 +443,8 @@ public: : FrameInternalBase(parent_frame, ptr, size, parent_frame->dts(), parent_frame->pts(), prefix_size) {} }; -// 管理一个指针生命周期并生产一个frame +// 管理一个指针生命周期并生产一个frame [AUTO-TRANSLATED:449d107b] +// Manage the lifetime of a pointer and produce a frame class FrameAutoDelete : public FrameFromPtr { public: template @@ -353,7 +455,8 @@ public: bool cacheAble() const override { return true; } }; -// 把一个不可缓存的frame声明为可缓存的 +// 把一个不可缓存的frame声明为可缓存的 [AUTO-TRANSLATED:2c8d0659] +// Declare a non-cacheable frame as cacheable template class FrameToCache : public Parent { public: @@ -365,7 +468,8 @@ public: } }; -// 该对象的功能是把一个不可缓存的帧转换成可缓存的帧 +// 该对象的功能是把一个不可缓存的帧转换成可缓存的帧 [AUTO-TRANSLATED:5851119b] +// The function of this object is to convert a non-cacheable frame into a cacheable frame class FrameCacheAble : public FrameFromPtr { public: using Ptr = std::shared_ptr; @@ -397,6 +501,9 @@ public: /** * 可以被缓存 + * Can be cached + + * [AUTO-TRANSLATED:7f9cec13] */ bool cacheAble() const override { return true; } bool keyFrame() const override { return _key; } @@ -412,7 +519,8 @@ private: toolkit::Buffer::Ptr _buffer; }; -//该类实现frame级别的时间戳覆盖 +// 该类实现frame级别的时间戳覆盖 [AUTO-TRANSLATED:77c28d0f] +// This class implements frame-level timestamp overwrite class FrameStamp : public Frame { public: using Ptr = std::shared_ptr; @@ -439,6 +547,9 @@ private: /** * 该对象可以把Buffer对象转换成可缓存的Frame对象 + * This object can convert a Buffer object into a cacheable Frame object + + * [AUTO-TRANSLATED:3c5786b8] */ template class FrameFromBuffer : public Parent { @@ -450,6 +561,14 @@ public: * @param pts 显示时间戳 * @param prefix 帧前缀长度 * @param offset buffer有效数据偏移量 + * Construct frame + * @param buf Data cache + * @param dts Decode timestamp + * @param pts Display timestamp + * @param prefix Frame prefix length + * @param offset Buffer valid data offset + + * [AUTO-TRANSLATED:6afec0f1] */ FrameFromBuffer(toolkit::Buffer::Ptr buf, uint64_t dts, uint64_t pts, size_t prefix = 0, size_t offset = 0) : Parent(buf->data() + offset, buf->size() - offset, dts, pts, prefix) { @@ -464,6 +583,15 @@ public: * @param prefix 帧前缀长度 * @param offset buffer有效数据偏移量 * @param codec 帧类型 + * Construct frame + * @param buf Data cache + * @param dts Decode timestamp + * @param pts Display timestamp + * @param prefix Frame prefix length + * @param offset Buffer valid data offset + * @param codec Frame type + + * [AUTO-TRANSLATED:f1c42e38] */ FrameFromBuffer(CodecId codec, toolkit::Buffer::Ptr buf, uint64_t dts, uint64_t pts, size_t prefix = 0, size_t offset = 0) : Parent(codec, buf->data() + offset, buf->size() - offset, dts, pts, prefix) { @@ -472,6 +600,9 @@ public: /** * 该帧可缓存 + * This frame is cacheable + + * [AUTO-TRANSLATED:e089250f] */ bool cacheAble() const override { return true; } @@ -481,6 +612,9 @@ private: /** * 合并一些时间戳相同的frame + * Merge some frames with the same timestamp + + * [AUTO-TRANSLATED:392a23df] */ class FrameMerger { public: @@ -497,6 +631,10 @@ public: /** * 刷新输出缓冲,注意此时会调用FrameMerger::inputFrame传入的onOutput回调 * 请注意回调捕获参数此时是否有效 + * Refresh the output buffer, note that FrameMerger::inputFrame's onOutput callback will be called at this time + * Please note whether the callback capture parameters are valid at this time + + * [AUTO-TRANSLATED:18c25a14] */ void flush(); void clear(); @@ -515,6 +653,9 @@ private: /** * 写帧接口的抽象接口类 + * Abstract interface class for write frame interface + + * [AUTO-TRANSLATED:dbe6a33c] */ class FrameWriterInterface { public: @@ -523,17 +664,26 @@ public: /** * 写入帧数据 + * Write frame data + + * [AUTO-TRANSLATED:d46c6fc2] */ virtual bool inputFrame(const Frame::Ptr &frame) = 0; /** * 刷新输出所有frame缓存 + * Flush all frame caches in the output + + * [AUTO-TRANSLATED:adaea568] */ virtual void flush() {}; }; /** * 支持代理转发的帧环形缓存 + * Frame circular buffer that supports proxy forwarding + + * [AUTO-TRANSLATED:06bf1541] */ class FrameDispatcher : public FrameWriterInterface { public: @@ -541,6 +691,9 @@ public: /** * 添加代理 + * Add proxy + + * [AUTO-TRANSLATED:0be3c076] */ FrameWriterInterface* addDelegate(FrameWriterInterface::Ptr delegate) { std::lock_guard lck(_mtx); @@ -551,6 +704,9 @@ public: /** * 删除代理 + * Delete proxy + + * [AUTO-TRANSLATED:c2c915aa] */ void delDelegate(FrameWriterInterface *ptr) { std::lock_guard lck(_mtx); @@ -559,6 +715,9 @@ public: /** * 写入帧并派发 + * Write frame and dispatch + + * [AUTO-TRANSLATED:a3e7e6db] */ bool inputFrame(const Frame::Ptr &frame) override { std::lock_guard lck(_mtx); @@ -574,6 +733,9 @@ public: /** * 返回代理个数 + * Return the number of proxies + + * [AUTO-TRANSLATED:93ebe7ec] */ size_t size() const { std::lock_guard lck(_mtx); @@ -587,6 +749,9 @@ public: /** * 获取累计关键帧数 + * Get the cumulative number of keyframes + + * [AUTO-TRANSLATED:73cb2ab0] */ uint64_t getVideoKeyFrames() const { std::lock_guard lck(_mtx); @@ -595,6 +760,9 @@ public: /** * 获取帧数 + * Get the number of frames + + * [AUTO-TRANSLATED:118b395e] */ uint64_t getFrames() const { std::lock_guard lck(_mtx); @@ -619,12 +787,14 @@ public: private: void doStatistics(const Frame::Ptr &frame) { if (!frame->configFrame() && !frame->dropAble()) { - // 忽略配置帧与可丢弃的帧 + // 忽略配置帧与可丢弃的帧 [AUTO-TRANSLATED:da4ff7ac] + // Ignore configuration frames and discardable frames ++_frames; int64_t out; _stamp.revise(frame->dts(), frame->pts(), out, out); if (frame->keyFrame() && frame->getTrackType() == TrackVideo) { - // 遇视频关键帧时统计 + // 遇视频关键帧时统计 [AUTO-TRANSLATED:72b0e569] + // Statistics when encountering video keyframes ++_video_key_frames; _gop_size = _frames - _last_frames; _gop_interval_ms = _ticker.elapsedTime(); diff --git a/src/Extension/Track.h b/src/Extension/Track.h index 006bd87a..6c5712b2 100644 --- a/src/Extension/Track.h +++ b/src/Extension/Track.h @@ -20,6 +20,9 @@ namespace mediakit{ /** * 媒体通道描述类,也支持帧输入输出 + * Media channel description class, also supports frame input and output + + * [AUTO-TRANSLATED:a3acd089] */ class Track : public FrameDispatcher, public CodecInfo { public: @@ -27,12 +30,19 @@ public: /** * 默认构造 + * Default constructor + + * [AUTO-TRANSLATED:acda54ab] */ Track() = default; /** * 复制拷贝,只能拷贝派生类的信息, * 环形缓存和代理关系不能拷贝,否则会关系紊乱 + * Copy, only copy information of derived classes, + * Circular buffer and proxy relationships cannot be copied, otherwise the relationship will be disordered + + * [AUTO-TRANSLATED:308e6502] */ Track(const Track &that) { _bit_rate = that._bit_rate; @@ -41,6 +51,9 @@ public: /** * 是否准备好,准备好才能获取譬如sps pps等信息 + * Whether it is ready, it can be used to get information such as sps pps + + * [AUTO-TRANSLATED:6d819ef7] */ virtual bool ready() const = 0; @@ -48,39 +61,65 @@ public: * 克隆接口,用于复制本对象用 * 在调用该接口时只会复制派生类的信息 * 环形缓存和代理关系不能拷贝,否则会关系紊乱 + * Clone interface, used to copy this object + * When calling this interface, only the information of the derived class will be copied + * Circular buffer and proxy relationships cannot be copied, otherwise the relationship will be disordered + + * [AUTO-TRANSLATED:270874c6] */ virtual Track::Ptr clone() const = 0; /** * 更新track信息,比如触发sps/pps解析 + * Update track information, such as triggering sps/pps parsing + + * [AUTO-TRANSLATED:324879ef] */ virtual bool update() { return false; } /** * 生成sdp * @return sdp对象 + * Generate sdp + * @return sdp object + + * [AUTO-TRANSLATED:3ab2fd30] */ virtual Sdp::Ptr getSdp(uint8_t payload_type) const = 0; /** * 获取extra data, 一般用于rtmp/mp4生成 + * Get extra data, generally used for rtmp/mp4 generation + + * [AUTO-TRANSLATED:d8ff2cd5] */ virtual toolkit::Buffer::Ptr getExtraData() const { return nullptr; } /** * 设置extra data, + * Set extra data, + + * [AUTO-TRANSLATED:9e551857] */ virtual void setExtraData(const uint8_t *data, size_t size) {} /** * 返回比特率 * @return 比特率 + * Return bitrate + * @return Bitrate + + * [AUTO-TRANSLATED:265dda35] */ virtual int getBitRate() const { return _bit_rate; } /** * 设置比特率 * @param bit_rate 比特率 + * Set bitrate + * @param bit_rate Bitrate + + * [AUTO-TRANSLATED:77a43064] */ virtual void setBitRate(int bit_rate) { _bit_rate = bit_rate; } @@ -90,6 +129,9 @@ private: /** * 视频通道描述Track类,支持获取宽高fps信息 + * Video channel description Track class, supports getting width, height and fps information + + * [AUTO-TRANSLATED:8d1893c5] */ class VideoTrack : public Track { public: @@ -97,21 +139,33 @@ public: /** * 返回视频高度 + * Return video height + + * [AUTO-TRANSLATED:b24aabc0] */ virtual int getVideoHeight() const { return 0; } /** * 返回视频宽度 + * Return video width + + * [AUTO-TRANSLATED:2f3bb6e3] */ virtual int getVideoWidth() const { return 0; } /** * 返回视频fps + * Return video fps + + * [AUTO-TRANSLATED:ced99aef] */ virtual float getVideoFps() const { return 0; } /** * 返回相关 sps/pps 等 + * Return related sps/pps, etc. + + * [AUTO-TRANSLATED:30fc4f63] */ virtual std::vector getConfigFrames() const { return std::vector{}; } }; @@ -126,6 +180,13 @@ public: * @param width 宽 * @param height 高 * @param fps 帧率 + * Constructor + * @param codec_id Encoding type + * @param width Width + * @param height Height + * @param fps Frame rate + + * [AUTO-TRANSLATED:b3d1ef4d] */ VideoTrackImp(CodecId codec_id, int width, int height, int fps) { _codec_id = codec_id; @@ -152,6 +213,9 @@ private: /** * 音频Track派生类,支持采样率通道数,采用位数信息 + * Audio Track derived class, supports sampling rate, number of channels, and sampling bit information + + * [AUTO-TRANSLATED:5f57819d] */ class AudioTrack : public Track { public: @@ -159,16 +223,25 @@ public: /** * 返回音频采样率 + * Return audio sampling rate + + * [AUTO-TRANSLATED:9af5a0a4] */ virtual int getAudioSampleRate() const {return 0;}; /** * 返回音频采样位数,一般为16或8 + * Return audio sampling bit depth, generally 16 or 8 + + * [AUTO-TRANSLATED:5fedc65d] */ virtual int getAudioSampleBit() const {return 0;}; /** * 返回音频通道数 + * Return audio number of channels + + * [AUTO-TRANSLATED:2613b317] */ virtual int getAudioChannel() const {return 0;}; }; @@ -183,6 +256,13 @@ public: * @param sample_rate 采样率(HZ) * @param channels 通道数 * @param sample_bit 采样位数,一般为16 + * Constructor + * @param codecId Encoding type + * @param sample_rate Sampling rate (HZ) + * @param channels Number of channels + * @param sample_bit Sampling bit depth, generally 16 + + * [AUTO-TRANSLATED:0ad0211f] */ AudioTrackImp(CodecId codecId, int sample_rate, int channels, int sample_bit){ _codecid = codecId; @@ -193,6 +273,9 @@ public: /** * 返回编码类型 + * Return encoding type + + * [AUTO-TRANSLATED:c8731864] */ CodecId getCodecId() const override{ return _codecid; @@ -200,6 +283,9 @@ public: /** * 是否已经初始化 + * Whether it has been initialized + + * [AUTO-TRANSLATED:5dc6693e] */ bool ready() const override { return true; @@ -207,6 +293,9 @@ public: /** * 返回音频采样率 + * Return audio sampling rate + + * [AUTO-TRANSLATED:9af5a0a4] */ int getAudioSampleRate() const override{ return _sample_rate; @@ -214,6 +303,9 @@ public: /** * 返回音频采样位数,一般为16或8 + * Return audio sampling bit depth, generally 16 or 8 + + * [AUTO-TRANSLATED:5fedc65d] */ int getAudioSampleBit() const override{ return _sample_bit; @@ -221,6 +313,9 @@ public: /** * 返回音频通道数 + * Return audio number of channels + + * [AUTO-TRANSLATED:2613b317] */ int getAudioChannel() const override{ return _channels; @@ -243,6 +338,10 @@ public: /** * 获取全部的Track * @param trackReady 是否获取全部已经准备好的Track + * Get all Tracks + * @param trackReady Whether to get all ready Tracks + + * [AUTO-TRANSLATED:f0779985] */ virtual std::vector getTracks(bool trackReady = true) const = 0; @@ -250,6 +349,10 @@ public: * 获取特定Track * @param type track类型 * @param trackReady 是否获取全部已经准备好的Track + * Get specific Track + * @param type Track type + * @param trackReady Whether to get all ready Tracks + * [AUTO-TRANSLATED:c50781b9] */ Track::Ptr getTrack(TrackType type , bool trackReady = true) const { auto tracks = getTracks(trackReady); diff --git a/src/FMP4/FMP4MediaSource.h b/src/FMP4/FMP4MediaSource.h index 5353f1a4..7fe23214 100644 --- a/src/FMP4/FMP4MediaSource.h +++ b/src/FMP4/FMP4MediaSource.h @@ -19,7 +19,8 @@ namespace mediakit { -//FMP4直播数据包 +// FMP4直播数据包 [AUTO-TRANSLATED:64f8a1d1] +// FMP4 Live Data Packet class FMP4Packet : public toolkit::BufferString{ public: using Ptr = std::shared_ptr; @@ -31,7 +32,8 @@ public: uint64_t time_stamp = 0; }; -//FMP4直播源 +// FMP4直播源 [AUTO-TRANSLATED:15c43604] +// FMP4 Live Source class FMP4MediaSource final : public MediaSource, public toolkit::RingDelegate, private PacketCache{ public: using Ptr = std::shared_ptr; @@ -51,6 +53,9 @@ public: /** * 获取媒体源的环形缓冲 + * Get the circular buffer of the media source + + * [AUTO-TRANSLATED:91a762bc] */ const RingType::Ptr &getRing() const { return _ring; @@ -63,6 +68,9 @@ public: /** * 获取fmp4 init segment + * Get the fmp4 init segment + + * [AUTO-TRANSLATED:6c704ec9] */ const std::string &getInitSegment() const{ return _init_segment; @@ -71,6 +79,10 @@ public: /** * 设置fmp4 init segment * @param str init segment + * Set the fmp4 init segment + * @param str init segment + + * [AUTO-TRANSLATED:3f41879f] */ void setInitSegment(std::string str) { _init_segment = std::move(str); @@ -79,6 +91,9 @@ public: /** * 获取播放器个数 + * Get the number of players + + * [AUTO-TRANSLATED:a451c846] */ int readerCount() override { return _ring ? _ring->readerCount() : 0; @@ -88,6 +103,11 @@ public: * 输入FMP4包 * @param packet FMP4包 * @param key 是否为关键帧第一个包 + * Input FMP4 packet + * @param packet FMP4 packet + * @param key Whether it is the first packet of the key frame + + * [AUTO-TRANSLATED:3b310b27] */ void onWrite(FMP4Packet::Ptr packet, bool key) override { if (!_ring) { @@ -103,6 +123,9 @@ public: /** * 情况GOP缓存 + * Clear GOP cache + + * [AUTO-TRANSLATED:d863f8c9] */ void clearCache() override { PacketCache::clearCache(); @@ -128,9 +151,15 @@ private: * 合并写回调 * @param packet_list 合并写缓存列队 * @param key_pos 是否包含关键帧 + * Merge write callback + * @param packet_list Merge write cache queue + * @param key_pos Whether it contains a key frame + + * [AUTO-TRANSLATED:6e93913e] */ void onFlush(std::shared_ptr > packet_list, bool key_pos) override { - //如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存 + // 如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存 [AUTO-TRANSLATED:66208f94] + // If there is no video, then there is no meaning to the existence of GOP cache, so make sure to clear the GOP cache all the time _ring->write(std::move(packet_list), _have_video ? key_pos : true); } diff --git a/src/FMP4/FMP4MediaSourceMuxer.h b/src/FMP4/FMP4MediaSourceMuxer.h index 300533b9..4cb8906a 100644 --- a/src/FMP4/FMP4MediaSourceMuxer.h +++ b/src/FMP4/FMP4MediaSourceMuxer.h @@ -63,7 +63,8 @@ public: } bool isEnabled() { - //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + // 缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 [AUTO-TRANSLATED:7cfd4d49] + // The inputFrame function is still allowed to be triggered when the cache has not been cleared, so that the cache can be cleared in time. return _option.fmp4_demand ? (_clear_cache ? true : _enabled) : true; } diff --git a/src/Http/HlsParser.cpp b/src/Http/HlsParser.cpp index c92a81d0..91c038f4 100644 --- a/src/Http/HlsParser.cpp +++ b/src/Http/HlsParser.cpp @@ -39,10 +39,12 @@ bool HlsParser::parse(const string &http_url, const string &m3u8) { segment.duration = extinf_dur; segment.url = Parser::mergeUrl(http_url, line); if (!_is_m3u8_inner) { - //ts按照先后顺序排序 + // ts按照先后顺序排序 [AUTO-TRANSLATED:c34f8c9d] + // Sort by order of appearance ts_map.emplace(index++, segment); } else { - //子m3u8按照带宽排序 + // 子m3u8按照带宽排序 [AUTO-TRANSLATED:749cb42b] + // Sort sub m3u8 by bandwidth ts_map.emplace(segment.bandwidth, segment); } extinf_dur = 0; @@ -91,7 +93,8 @@ bool HlsParser::parse(const string &http_url, const string &m3u8) { } if (line.find("#EXT-X-ENDLIST") == 0) { - //点播 + // 点播 [AUTO-TRANSLATED:a64427bc] + // On-demand _is_live = false; continue; } diff --git a/src/Http/HlsParser.h b/src/Http/HlsParser.h index e61540e7..c9697914 100644 --- a/src/Http/HlsParser.h +++ b/src/Http/HlsParser.h @@ -18,19 +18,26 @@ namespace mediakit { typedef struct{ - //url地址 + // url地址 [AUTO-TRANSLATED:64a1b5d1] + // URL address std::string url; - //ts切片长度 + // ts切片长度 [AUTO-TRANSLATED:9d5545f8] + // TS segment length float duration; - //////内嵌m3u8////// - //节目id + // ////内嵌m3u8////// [AUTO-TRANSLATED:c3fabbfd] + // //// Embedded m3u8 ////// + // 节目id [AUTO-TRANSLATED:8c6000cc] + // Program ID int program_id; - //带宽 + // 带宽 [AUTO-TRANSLATED:5f852828] + // Bandwidth int bandwidth; - //宽度 + // 宽度 [AUTO-TRANSLATED:06ad2724] + // Width int width; - //高度 + // 高度 [AUTO-TRANSLATED:87a07641] + // Height int height; } ts_segment; @@ -40,41 +47,65 @@ public: /** * 是否存在#EXTM3U字段,是否为m3u8文件 + * Whether the #EXTM3U field exists, whether it is an m3u8 file + + * [AUTO-TRANSLATED:ac1bf089] */ bool isM3u8() const; /** * #EXT-X-ALLOW-CACHE值,是否允许cache + * #EXT-X-ALLOW-CACHE value, whether caching is allowed + + * [AUTO-TRANSLATED:90e88422] */ bool allowCache() const; /** * 是否存在#EXT-X-ENDLIST字段,是否为直播 + * Whether the #EXT-X-ENDLIST field exists, whether it is a live stream + + * [AUTO-TRANSLATED:f18e3c44] */ bool isLive() const ; /** * #EXT-X-VERSION值,版本号 + * #EXT-X-VERSION value, version number + + * [AUTO-TRANSLATED:89a99b3d] */ int getVersion() const; /** * #EXT-X-TARGETDURATION字段值 + * #EXT-X-TARGETDURATION field value + + * [AUTO-TRANSLATED:6720dc84] */ int getTargetDur() const; /** * #EXT-X-MEDIA-SEQUENCE字段值,该m3u8序号 + * #EXT-X-MEDIA-SEQUENCE field value, the sequence number of this m3u8 + + * [AUTO-TRANSLATED:1a75250a] */ int64_t getSequence() const; /** * 内部是否含有子m3u8 + * Whether it contains sub-m3u8 internally + + * [AUTO-TRANSLATED:67b4a20c] */ bool isM3u8Inner() const; /** * 得到总时间 + * Get the total time + + * [AUTO-TRANSLATED:aa5e797b] */ float getTotalDuration() const; @@ -85,6 +116,13 @@ protected: * @param sequence ts序号 * @param ts_list ts地址列表 * @return 是否解析成功,返回false时,将导致HlsParser::parse返回false + * Callback for parsing the m3u8 file + * @param is_m3u8_inner Whether this m3u8 file contains multiple HLS addresses + * @param sequence TS sequence number + * @param ts_list TS address list + * @return Whether the parsing is successful, returning false will cause HlsParser::parse to return false + + * [AUTO-TRANSLATED:be34e59f] */ virtual bool onParsed(bool is_m3u8_inner, int64_t sequence, const std::map &ts_list) = 0; @@ -96,7 +134,8 @@ private: int _target_dur = 0; float _total_dur = 0; int64_t _sequence = 0; - //每部是否有m3u8 + // 每部是否有m3u8 [AUTO-TRANSLATED:c0d01536] + // Whether each part has an m3u8 bool _is_m3u8_inner = false; }; diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index aeb74c65..2443297e 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -44,10 +44,12 @@ void HlsPlayer::teardown_l(const SockException &ex) { _play_result = true; onPlayResult(ex); } else { - // 如果不是主动关闭的,则重新拉取索引文件 + // 如果不是主动关闭的,则重新拉取索引文件 [AUTO-TRANSLATED:e187c069] + // If it is not actively closed, then re-pull the index file // if not actively closed, re-fetch the index file if (ex.getErrCode() != Err_shutdown && HlsParser::isLive()) { - // 如果重试次数已经达到最大次数时, 且切片列表已空, 而且没有正在下载的切片, 则认为失败关闭播放器 + // 如果重试次数已经达到最大次数时, 且切片列表已空, 而且没有正在下载的切片, 则认为失败关闭播放器 [AUTO-TRANSLATED:2afe6c3a] + // If the retry count has reached the maximum number of times, and the slice list is empty, and there are no slices being downloaded, then it is considered a failure to close the player // If the retry count has reached the maximum number of times, and the segments list is empty, and there is no segment being downloaded, // the player is considered to be closed due to failure if (_ts_list.empty() && !(_http_ts_player && _http_ts_player->waitResponse()) && _try_fetch_index_times >= MAX_TRY_FETCH_INDEX_TIMES) { @@ -56,11 +58,14 @@ void HlsPlayer::teardown_l(const SockException &ex) { _try_fetch_index_times += 1; shutdown(ex); WarnL << "Attempt to pull the m3u8 file again[" << _try_fetch_index_times << "]:" << _play_url; - // 当网络波动时有可能拉取m3u8文件失败, 因此快速重试拉取m3u8文件, 而不是直接关闭播放器 - // 这里增加一个延时是为了防止_http_ts_player的socket还保持alive状态,就多次拉取m3u8文件了 + // 当网络波动时有可能拉取m3u8文件失败, 因此快速重试拉取m3u8文件, 而不是直接关闭播放器 [AUTO-TRANSLATED:0cb45f5f] + // When the network fluctuates, it is possible that the m3u8 file will fail to be pulled, so quickly retry pulling the m3u8 file instead of directly closing the player + // 这里增加一个延时是为了防止_http_ts_player的socket还保持alive状态,就多次拉取m3u8文件了 [AUTO-TRANSLATED:f779e7e9] + // A delay is added here to prevent the _http_ts_player socket from remaining alive and pulling the m3u8 file multiple times // When the network fluctuates, it is possible to fail to pull the m3u8 file, so quickly retry to pull the m3u8 file instead of closing the player directly // The delay here is to prevent the socket of _http_ts_player from still keeping alive state, and pull the m3u8 file multiple times - //todo _http_ts_player->waitResponse()这个判断条件是否有必要?因为有时候存在_complete==true,但是_http_ts_player->alive()为true的情况 + // todo _http_ts_player->waitResponse()这个判断条件是否有必要?因为有时候存在_complete==true,但是_http_ts_player->alive()为true的情况 [AUTO-TRANSLATED:a92efd3e] + // todo Is the _http_ts_player->waitResponse() condition necessary? Because sometimes there is _complete==true, but _http_ts_player->alive() is true playDelay(0.3); return; } @@ -80,20 +85,23 @@ void HlsPlayer::teardown() { void HlsPlayer::fetchSegment() { if (_ts_list.empty()) { - // 如果是点播文件,播放列表为空代表文件播放结束,关闭播放器: #2628 + // 如果是点播文件,播放列表为空代表文件播放结束,关闭播放器: #2628 [AUTO-TRANSLATED:c2d0b647] + // If it is an on-demand file, an empty playlist means that the file playback is finished, and the player is closed: #2628 // If it is a video-on-demand file, the playlist is empty means the file is finished playing, close the player: #2628 if (!HlsParser::isLive()) { teardown(); return; } - // 播放列表为空,那么立即重新下载m3u8文件 + // 播放列表为空,那么立即重新下载m3u8文件 [AUTO-TRANSLATED:e01943f3] + // If the playlist is empty, then immediately re-download the m3u8 file // The playlist is empty, so download the m3u8 file immediately _timer.reset(); fetchIndexFile(); return; } if (_http_ts_player && _http_ts_player->waitResponse()) { - // 播放器目前还存活,正在下载中 + // 播放器目前还存活,正在下载中 [AUTO-TRANSLATED:c18d8446] + // The player is still alive and is currently downloading return; } weak_ptr weak_self = static_pointer_cast(shared_from_this()); @@ -115,7 +123,8 @@ void HlsPlayer::fetchSegment() { if (!strong_self) { return; } - // 收到ts包 + // 收到ts包 [AUTO-TRANSLATED:334862da] + // Received ts packet // Received ts package strong_self->onPacket(data, len); }); @@ -152,19 +161,23 @@ void HlsPlayer::fetchSegment() { } else { strong_self->_ts_download_failed_count = 0; } - // 提前0.5秒下载好,支持点播文件控制下载速度: #2628 + // 提前0.5秒下载好,支持点播文件控制下载速度: #2628 [AUTO-TRANSLATED:82247326] + // Download 0.5 seconds in advance to support on-demand file download speed control: #2628 // Download 0.5 seconds in advance to support video-on-demand files to control download speed: #2628 auto delay = duration - 0.5 - ticker.elapsedTime() / 1000.0f; if (delay > 2.0) { - // 提前1秒下载 + // 提前1秒下载 [AUTO-TRANSLATED:852349aa] + // Download 1 second in advance // Download 1 second in advance delay -= 1.0; } else if (delay <= 0) { - // 延时最小10ms + // 延时最小10ms [AUTO-TRANSLATED:fbb3665e] + // Delay a minimum of 10ms // Delay at least 10ms delay = 0.01; } - // 延时下载下一个切片 + // 延时下载下一个切片 [AUTO-TRANSLATED:26eb528d] + // Delay downloading the next slice strong_self->_timer_ts.reset(new Timer(delay, [weak_self]() { auto strong_self = weak_self.lock(); if (strong_self) { @@ -175,7 +188,8 @@ void HlsPlayer::fetchSegment() { }); _http_ts_player->setMethod("GET"); - // ts切片必须在其时长的2-5倍内下载完毕 + // ts切片必须在其时长的2-5倍内下载完毕 [AUTO-TRANSLATED:d458e7b5] + // The ts slice must be downloaded within 2-5 times its duration // The ts segment must be downloaded within 2-5 times its duration _http_ts_player->setCompleteTimeout(_timeout_multiple * duration * 1000); _http_ts_player->sendRequest(url); @@ -183,12 +197,16 @@ void HlsPlayer::fetchSegment() { bool HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map &ts_map) { if (!is_m3u8_inner) { - // 这是ts播放列表 + // 这是ts播放列表 [AUTO-TRANSLATED:7ce3d81b] + // This is the ts playlist // This is the ts playlist if (_last_sequence == sequence) { - // 如果是重复的ts列表,那么忽略 - // 但是需要注意, 如果当前ts列表为空了, 那么表明直播结束了或者m3u8文件有问题,需要重新拉流 - // 这里的5倍是为了防止m3u8文件有问题导致的无限重试 + // 如果是重复的ts列表,那么忽略 [AUTO-TRANSLATED:d15a47f3] + // If it is a duplicate ts list, then ignore it + // 但是需要注意, 如果当前ts列表为空了, 那么表明直播结束了或者m3u8文件有问题,需要重新拉流 [AUTO-TRANSLATED:438a8df0] + // However, it should be noted that if the current ts list is empty, then it means that the live broadcast has ended or the m3u8 file has a problem, and the stream needs to be re-pulled + // 这里的5倍是为了防止m3u8文件有问题导致的无限重试 [AUTO-TRANSLATED:3c8d073d] + // The 5 times here is to prevent infinite retries caused by problems with the m3u8 file // If it is a duplicate ts list, ignore it // But it should be noted that if the current ts list is empty, it means that the live broadcast is over or the m3u8 file is problematic, and you need to re-pull the stream // The 5 times here is to prevent infinite retries caused by problems with the m3u8 file @@ -206,23 +224,27 @@ bool HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map 2 * ts_map.size()) { - // 去除防重列表中过多的数据 + // 去除防重列表中过多的数据 [AUTO-TRANSLATED:94173d03] + // Remove excessive data from the anti-repetition list // Remove too much data from the anti-repetition list _ts_url_cache.erase(_ts_url_sort.front()); _ts_url_sort.pop_front(); } fetchSegment(); } else { - // 这是m3u8列表,我们播放最高清的子hls + // 这是m3u8列表,我们播放最高清的子hls [AUTO-TRANSLATED:6e6981ef] + // This is the m3u8 list, we play the highest definition sub-hls // This is the m3u8 list, we play the highest quality sub-hls if (ts_map.empty()) { throw invalid_argument("empty sub hls list:" + getUrl()); @@ -242,7 +264,8 @@ bool HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map HlsParser::getTotalDuration()) { targetOffset = HlsParser::getTotalDuration(); } - // 根据规范为一半的时间 + // 根据规范为一半的时间 [AUTO-TRANSLATED:07652637] + // According to the specification, it is half the time // According to the specification, it is half the time if (targetOffset / 2 > 1.0f) { return targetOffset / 2; @@ -331,7 +359,8 @@ void HlsDemuxer::start(const EventPoller::Ptr &poller, TrackListener *listener) _frame_cache.clear(); _delegate.setTrackListener(listener); - // 每50毫秒执行一次 + // 每50毫秒执行一次 [AUTO-TRANSLATED:e32f2140] + // Execute once every 50 milliseconds // Execute every 50 milliseconds weak_ptr weak_self = shared_from_this(); _timer = std::make_shared(0.05f, [weak_self]() { @@ -353,7 +382,8 @@ void HlsDemuxer::pushTask(std::function task) { } bool HlsDemuxer::inputFrame(const Frame::Ptr &frame) { - // 为了避免track准备时间过长, 因此在没准备好之前, 直接消费掉所有的帧 + // 为了避免track准备时间过长, 因此在没准备好之前, 直接消费掉所有的帧 [AUTO-TRANSLATED:72b35430] + // To avoid the track preparation time being too long, all frames are directly consumed before it is ready // In order to avoid the track preparation time is too long, so before it is ready, all frames are consumed directly if (!_delegate.isAllTrackReady()) { _delegate.inputFrame(frame); @@ -361,11 +391,13 @@ bool HlsDemuxer::inputFrame(const Frame::Ptr &frame) { } if (_frame_cache.empty()) { - // 设置当前播放位置时间戳 + // 设置当前播放位置时间戳 [AUTO-TRANSLATED:14799e6c] + // Set the current playback position timestamp // Set the current playback position timestamp setPlayPosition(frame->dts()); } - // 根据时间戳缓存frame + // 根据时间戳缓存frame [AUTO-TRANSLATED:f84d3698] + // Cache frames based on the timestamp // Cache frame according to timestamp auto cached_frame = Frame::getCacheAbleFrame(frame); _frame_cache.emplace_back(std::make_pair(frame->dts(), [cached_frame, this]() { @@ -373,13 +405,15 @@ bool HlsDemuxer::inputFrame(const Frame::Ptr &frame) { })); if (getBufferMS() > 30 * 1000) { - // 缓存超过30秒,强制消费至15秒(减少延时或内存占用) + // 缓存超过30秒,强制消费至15秒(减少延时或内存占用) [AUTO-TRANSLATED:d6d58dde] + // If the cache exceeds 30 seconds, force consumption to 15 seconds (reduce latency or memory usage) // The cache exceeds 30 seconds, and the consumption is forced to 15 seconds (reduce delay or memory usage) while (getBufferMS() > 15 * 1000) { _frame_cache.begin()->second(); _frame_cache.erase(_frame_cache.begin()); } - // 接着播放缓存中最早的帧 + // 接着播放缓存中最早的帧 [AUTO-TRANSLATED:a1c76e0e] + // Then play the earliest frame in the cache // Then play the earliest frame in the cache setPlayPosition(_frame_cache.begin()->first); } @@ -406,20 +440,24 @@ void HlsDemuxer::onTick() { auto it = _frame_cache.begin(); while (it != _frame_cache.end()) { if (it->first > getPlayPosition()) { - // 这些帧还未到时间播放 + // 这些帧还未到时间播放 [AUTO-TRANSLATED:e1ef7fe2] + // These frames have not yet reached their playback time // These frames are not yet time to play break; } if (getBufferMS() < 3 * 1000) { - // 缓存小于3秒,那么降低定时器消费速度(让剩余的数据在3秒后消费完毕) - // 目的是为了防止定时器长时间干等后,数据瞬间消费完毕 + // 缓存小于3秒,那么降低定时器消费速度(让剩余的数据在3秒后消费完毕) [AUTO-TRANSLATED:bc14fe02] + // If the cache is less than 3 seconds, then reduce the timer consumption speed (so that the remaining data is consumed after 3 seconds) + // 目的是为了防止定时器长时间干等后,数据瞬间消费完毕 [AUTO-TRANSLATED:55ac9c3d] + // The goal is to prevent the timer from waiting for a long time before the data is consumed instantly // If the cache is less than 3 seconds, then reduce the speed of the timer to consume (let the remaining data be consumed after 3 seconds) // The purpose is to prevent the timer from waiting for a long time, and the data is consumed instantly setPlayPosition(_frame_cache.begin()->first); } - // 消费掉已经到期的帧 + // 消费掉已经到期的帧 [AUTO-TRANSLATED:f2d1230a] + // Consume expired frames // Consume expired frames it->second(); it = _frame_cache.erase(it); @@ -458,13 +496,15 @@ void HlsPlayerImp::onPlayResult(const SockException &ex) { void HlsPlayerImp::onShutdown(const SockException &ex) { while (_demuxer) { try { - // shared_from_this()可能抛异常 + // shared_from_this()可能抛异常 [AUTO-TRANSLATED:c57c464a] + // shared_from_this() may throw an exception // shared_from_this() may throw an exception std::weak_ptr weak_self = static_pointer_cast(shared_from_this()); if (_decoder) { _decoder->flush(); } - // 等待所有frame flush输出后,再触发onShutdown事件 + // 等待所有frame flush输出后,再触发onShutdown事件 [AUTO-TRANSLATED:6db59f15] + // Wait for all frames to be flushed before triggering the onShutdown event // Wait for all frame flush output, then trigger the onShutdown event static_pointer_cast(_demuxer)->pushTask([weak_self, ex]() { if (auto strong_self = weak_self.lock()) { diff --git a/src/Http/HlsPlayer.h b/src/Http/HlsPlayer.h index 8f4a84b0..10373894 100644 --- a/src/Http/HlsPlayer.h +++ b/src/Http/HlsPlayer.h @@ -56,12 +56,20 @@ public: /** * 开始播放 * start play + * Start playing + * start play + + * [AUTO-TRANSLATED:03d41cf7] */ void play(const std::string &url) override; /** * 停止播放 * stop play + * Stop playing + * stop play + + * [AUTO-TRANSLATED:88068dac] */ void teardown() override; @@ -71,6 +79,12 @@ protected: * Received ts package * @param data ts数据负载 ts data payload * @param len ts包长度 ts package length + * Received ts package + * Received ts package + * @param data ts data payload + * @param len ts package length + + * [AUTO-TRANSLATED:159a6559] */ virtual void onPacket(const char *data, size_t len) = 0; @@ -90,7 +104,8 @@ private: private: struct UrlComp { - // url忽略?后面的参数 + // url忽略?后面的参数 [AUTO-TRANSLATED:788784c3] + // url ignore? parameters after // Ignore the parameters after the url? bool operator()(const std::string& __x, const std::string& __y) const { return toolkit::split(__x,"?")[0] < toolkit::split(__y,"?")[0]; diff --git a/src/Http/HttpBody.cpp b/src/Http/HttpBody.cpp index a6aeb695..b73538c7 100644 --- a/src/Http/HttpBody.cpp +++ b/src/Http/HttpBody.cpp @@ -44,7 +44,8 @@ int64_t HttpStringBody::remainSize() { Buffer::Ptr HttpStringBody::readData(size_t size) { size = MIN((size_t)remainSize(), size); if (!size) { - //没有剩余字节了 + // 没有剩余字节了 [AUTO-TRANSLATED:7bbaa343] + // No remaining bytes return nullptr; } auto ret = std::make_shared(_str, _offset, size); @@ -72,7 +73,8 @@ static void mmap_close(HANDLE _hfile, HANDLE _hmapping, void *_addr) { } #endif -//删除mmap记录 +// 删除mmap记录 [AUTO-TRANSLATED:c956201d] +// Delete mmap record static void delSharedMmap(const string &file_path, char *ptr) { lock_guard lck(s_mtx); auto it = s_shared_mmap.find(file_path); @@ -88,21 +90,24 @@ static std::shared_ptr getSharedMmap(const string &file_path, int64_t &fil if (it != s_shared_mmap.end()) { auto ret = std::get<2>(it->second).lock(); if (ret) { - //命中mmap缓存 + // 命中mmap缓存 [AUTO-TRANSLATED:95131a66] + // Hit mmap cache file_size = std::get<1>(it->second); return ret; } } } - //打开文件 + // 打开文件 [AUTO-TRANSLATED:55bfe68a] + // Open file std::shared_ptr fp(fopen(file_path.data(), "rb"), [](FILE *fp) { if (fp) { fclose(fp); } }); if (!fp) { - //文件不存在 + // 文件不存在 [AUTO-TRANSLATED:ed160bcf] + // File does not exist file_size = -1; return nullptr; } @@ -111,7 +116,8 @@ static std::shared_ptr getSharedMmap(const string &file_path, int64_t &fil #if defined(_WIN32) auto fd = _fileno(fp.get()); #else - //获取文件大小 + // 获取文件大小 [AUTO-TRANSLATED:82974eea] + // Get file size file_size = File::fileSize(fp.get()); auto fd = fileno(fp.get()); #endif @@ -171,7 +177,8 @@ static std::shared_ptr getSharedMmap(const string &file_path, int64_t &fil #if 0 if (file_size < 10 * 1024 * 1024 && file_path.rfind(".ts") != string::npos) { - //如果是小ts文件,那么尝试先加载到内存 + // 如果是小ts文件,那么尝试先加载到内存 [AUTO-TRANSLATED:0d96c5cd] + // If it is a small ts file, try to load it into memory first auto buf = BufferRaw::create(); buf->assign(ret.get(), file_size); ret.reset(buf->data(), [buf, file_path](char *ptr) { @@ -192,20 +199,24 @@ HttpFileBody::HttpFileBody(const string &file_path, bool use_mmap) { } if (!_map_addr && _read_to != -1) { - //mmap失败(且不是由于文件不存在导致的)或未执行mmap时,才进入fread逻辑分支 + // mmap失败(且不是由于文件不存在导致的)或未执行mmap时,才进入fread逻辑分支 [AUTO-TRANSLATED:8c7efed5] + // Only enter the fread logic branch when mmap fails (and is not due to file not existing) or when mmap is not executed _fp.reset(fopen(file_path.data(), "rb"), [](FILE *fp) { if (fp) { fclose(fp); } }); if (!_fp) { - //文件不存在 + // 文件不存在 [AUTO-TRANSLATED:ed160bcf] + // File does not exist _read_to = -1; return; } if (!_read_to) { - //_read_to等于0时,说明还未尝试获取文件大小 - //加上该判断逻辑,在mmap失败时,可以省去一次该操作 + // _read_to等于0时,说明还未尝试获取文件大小 [AUTO-TRANSLATED:4e3ef6ca] + // When _read_to equals 0, it means that the file size has not been attempted to be obtained yet + // 加上该判断逻辑,在mmap失败时,可以省去一次该操作 [AUTO-TRANSLATED:b9b585de] + // Adding this judgment logic can save one operation when mmap fails _read_to = File::fileSize(_fp.get()); } } @@ -241,7 +252,8 @@ public: _data = map_addr.get() + offset; _size = size; } - //返回数据长度 + // 返回数据长度 [AUTO-TRANSLATED:955f731c] + // Return data length char *data() const override { return _data; } size_t size() const override { return _size; } @@ -258,11 +270,13 @@ int64_t HttpFileBody::remainSize() { Buffer::Ptr HttpFileBody::readData(size_t size) { size = (size_t)(MIN(remainSize(), (int64_t)size)); if (!size) { - //没有剩余字节了 + // 没有剩余字节了 [AUTO-TRANSLATED:7bbaa343] + // No remaining bytes return nullptr; } if (!_map_addr) { - // fread模式 + // fread模式 [AUTO-TRANSLATED:c4dee2a3] + // fread mode ssize_t iRead; auto ret = _pool.obtain2(); ret->setCapacity(size + 1); @@ -271,18 +285,21 @@ Buffer::Ptr HttpFileBody::readData(size_t size) { } while (-1 == iRead && UV_EINTR == get_uv_error(false)); if (iRead > 0) { - //读到数据了 + // 读到数据了 [AUTO-TRANSLATED:7e5ada62] + // Data is read ret->setSize(iRead); _file_offset += iRead; return std::move(ret); } - //读取文件异常,文件真实长度小于声明长度 + // 读取文件异常,文件真实长度小于声明长度 [AUTO-TRANSLATED:89d09f9b] + // File reading exception, the actual length of the file is less than the declared length _file_offset = _read_to; WarnL << "read file err:" << get_uv_errmsg(); return nullptr; } - // mmap模式 + // mmap模式 [AUTO-TRANSLATED:b8d616f1] + // mmap mode auto ret = std::make_shared(_map_addr, _file_offset, size); _file_offset += size; return ret; @@ -321,7 +338,8 @@ Buffer::Ptr HttpMultiFormBody::readData(size_t size) { if (_fileBody->remainSize()) { auto ret = _fileBody->readData(size); if (!ret) { - //读取文件出现异常,提前中断 + // 读取文件出现异常,提前中断 [AUTO-TRANSLATED:5b8052d9] + // An exception occurred while reading the file, and the process was interrupted prematurely _offset = _totalSize; } else { _offset += ret->size(); diff --git a/src/Http/HttpBody.h b/src/Http/HttpBody.h index 3ba60565..fad045f9 100644 --- a/src/Http/HttpBody.h +++ b/src/Http/HttpBody.h @@ -26,6 +26,9 @@ namespace mediakit { /** * http content部分基类定义 + * Base class definition for http content part + + * [AUTO-TRANSLATED:1eee419a] */ class HttpBody : public std::enable_shared_from_this{ public: @@ -34,6 +37,9 @@ public: /** * 剩余数据大小,如果返回-1, 那么就不设置content-length + * Remaining data size, if -1 is returned, then content-length is not set + + * [AUTO-TRANSLATED:75375ce7] */ virtual int64_t remainSize() { return 0;}; @@ -41,6 +47,11 @@ public: * 读取一定字节数,返回大小可能小于size * @param size 请求大小 * @return 字节对象,如果读完了,那么请返回nullptr + * Read a certain number of bytes, the returned size may be less than size + * @param size Request size + * @return Byte object, if it is read, please return nullptr + + * [AUTO-TRANSLATED:6fd85f91] */ virtual toolkit::Buffer::Ptr readData(size_t size) { return nullptr;}; @@ -48,11 +59,19 @@ public: * 异步请求读取一定字节数,返回大小可能小于size * @param size 请求大小 * @param cb 回调函数 + * Asynchronously request to read a certain number of bytes, the returned size may be less than size + * @param size Request size + * @param cb Callback function + + * [AUTO-TRANSLATED:a5304046] */ virtual void readDataAsync(size_t size,const std::function &cb){ - //由于unix和linux是通过mmap的方式读取文件,所以把读文件操作放在后台线程并不能提高性能 - //反而会由于频繁的线程切换导致性能降低以及延时增加,所以我们默认同步获取文件内容 - //(其实并没有读,拷贝文件数据时在内核态完成文件读) + // 由于unix和linux是通过mmap的方式读取文件,所以把读文件操作放在后台线程并不能提高性能 [AUTO-TRANSLATED:59ef443d] + // Since unix and linux read files through mmap, putting file reading operations in the background thread does not improve performance + // 反而会由于频繁的线程切换导致性能降低以及延时增加,所以我们默认同步获取文件内容 [AUTO-TRANSLATED:93d2a0b5] + // On the contrary, frequent thread switching will lead to performance degradation and increased latency, so we get the file content synchronously by default + // (其实并没有读,拷贝文件数据时在内核态完成文件读) [AUTO-TRANSLATED:6eb98a5d] + // (Actually, there is no reading, the file data is copied in the kernel state when copying) cb(readData(size)); } @@ -60,6 +79,11 @@ public: * 使用sendfile优化文件发送 * @param fd socket fd * @return 0成功,其他为错误代码 + * Use sendfile to optimize file sending + * @param fd socket fd + * @return 0 success, other error codes + + * [AUTO-TRANSLATED:eacc5f98] */ virtual int sendFile(int fd) { return -1; @@ -68,6 +92,9 @@ public: /** * std::string类型的content + * std::string type content + + * [AUTO-TRANSLATED:59fc3e5b] */ class HttpStringBody : public HttpBody{ public: @@ -84,6 +111,9 @@ private: /** * Buffer类型的content + * Buffer type content + + * [AUTO-TRANSLATED:350b9513] */ class HttpBufferBody : public HttpBody{ public: @@ -99,6 +129,9 @@ private: /** * 文件类型的content + * File type content + + * [AUTO-TRANSLATED:baf9c0f3] */ class HttpFileBody : public HttpBody { public: @@ -108,6 +141,11 @@ public: * 构造函数 * @param file_path 文件路径 * @param use_mmap 是否使用mmap方式访问文件 + * Constructor + * @param file_path File path + * @param use_mmap Whether to use mmap to access the file + + * [AUTO-TRANSLATED:40c85c53] */ HttpFileBody(const std::string &file_path, bool use_mmap = true); @@ -115,6 +153,11 @@ public: * 设置读取范围 * @param offset 相对文件头的偏移量 * @param max_size 最大读取字节数 + * Set the reading range + * @param offset Offset relative to the file header + * @param max_size Maximum number of bytes to read + + * [AUTO-TRANSLATED:30532a4e] */ void setRange(uint64_t offset, uint64_t max_size); @@ -134,6 +177,9 @@ class HttpArgs; /** * http MultiForm 方式提交的http content + * http MultiForm way to submit http content + + * [AUTO-TRANSLATED:211a2d8e] */ class HttpMultiFormBody : public HttpBody { public: @@ -144,6 +190,13 @@ public: * @param args http提交参数列表 * @param filePath 文件路径 * @param boundary boundary字符串 + * Constructor + * @param args http submission parameter list + * @param filePath File path + * @param boundary Boundary string + + + * [AUTO-TRANSLATED:d093cfa7] */ HttpMultiFormBody(const HttpArgs &args,const std::string &filePath,const std::string &boundary = "0xKhTmLbOuNdArY"); int64_t remainSize() override ; diff --git a/src/Http/HttpChunkedSplitter.cpp b/src/Http/HttpChunkedSplitter.cpp index 248ac0ab..224f9d0b 100644 --- a/src/Http/HttpChunkedSplitter.cpp +++ b/src/Http/HttpChunkedSplitter.cpp @@ -33,7 +33,8 @@ void HttpChunkedSplitter::onRecvContent(const char *data, size_t len) { ssize_t HttpChunkedSplitter::onRecvHeader(const char *data, size_t len) { int size; CHECK(sscanf(data, "%X", &size) == 1 && size >= 0); - //包括后面\r\n两个字节 + // 包括后面\r\n两个字节 [AUTO-TRANSLATED:f5567007] + // Including the following two bytes \r\n return size + 2; } diff --git a/src/Http/HttpChunkedSplitter.h b/src/Http/HttpChunkedSplitter.h index 52a58053..1c29f79d 100644 --- a/src/Http/HttpChunkedSplitter.h +++ b/src/Http/HttpChunkedSplitter.h @@ -20,6 +20,10 @@ class HttpChunkedSplitter : public HttpRequestSplitter { public: /** * len == 0时代表结束 + * When len == 0, it represents the end. + + + * [AUTO-TRANSLATED:1607d203] */ using onChunkData = std::function; diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index 84450d2b..23b97b42 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -43,11 +43,13 @@ void HttpClient::sendRequest(const string &url) { if (_path.empty()) { _path = "/"; } - //重新设置header,防止上次请求的header干扰 + // 重新设置header,防止上次请求的header干扰 [AUTO-TRANSLATED:d8d06841] + // Reset the header to prevent interference from the previous request's header _header = _user_set_header; auto pos = host.find('@'); if (pos != string::npos) { - //去除?后面的字符串 + // 去除?后面的字符串 [AUTO-TRANSLATED:0ccb41c2] + // Remove the string after the "?" auto authStr = host.substr(0, pos); host = host.substr(pos + 1, host.size()); _header.emplace("Authorization", "Basic " + encodeBase64(authStr)); @@ -168,7 +170,8 @@ void HttpClient::onConnect_l(const SockException &ex) { return; } _StrPrinter printer; - //不使用代理或者代理服务器已经连接成功 + // 不使用代理或者代理服务器已经连接成功 [AUTO-TRANSLATED:e051567c] + // No proxy is used or the proxy server has connected successfully if (_proxy_connected || !isUsedProxy()) { printer << _method + " " << _path + " HTTP/1.1\r\n"; for (auto &pr : _header) { @@ -195,8 +198,10 @@ void HttpClient::onRecv(const Buffer::Ptr &pBuf) { void HttpClient::onError(const SockException &ex) { if (ex.getErrCode() == Err_reset && _allow_resend_request && _http_persistent && _recved_body_size == 0 && !_header_recved) { - // 连接被重置,可能是服务器主动断开了连接, 或者服务器内核参数或防火墙的持久连接空闲时间超时或不一致. - // 如果是持久化连接,那么我们可以通过重连来解决这个问题 + // 连接被重置,可能是服务器主动断开了连接, 或者服务器内核参数或防火墙的持久连接空闲时间超时或不一致. [AUTO-TRANSLATED:8a78f452] + // The connection was reset, possibly because the server actively closed the connection, or the server kernel parameters or firewall's persistent connection idle timeout or inconsistency. + // 如果是持久化连接,那么我们可以通过重连来解决这个问题 [AUTO-TRANSLATED:6c113e17] + // If it is a persistent connection, we can solve this problem by reconnecting // The connection was reset, possibly because the server actively disconnected the connection, // or the persistent connection idle time of the server kernel parameters or firewall timed out or inconsistent. // If it is a persistent connection, then we can solve this problem by reconnecting @@ -226,7 +231,8 @@ ssize_t HttpClient::onRecvHeader(const char *data, size_t len) { _header_recved = true; if (_parser["Transfer-Encoding"] == "chunked") { - //如果Transfer-Encoding字段等于chunked,则认为后续的content是不限制长度的 + // 如果Transfer-Encoding字段等于chunked,则认为后续的content是不限制长度的 [AUTO-TRANSLATED:ebbcb35c] + // If the Transfer-Encoding field is equal to chunked, it is considered that the subsequent content is unlimited in length _total_body_size = -1; _chunked_splitter = std::make_shared([this](const char *data, size_t len) { if (len > 0) { @@ -241,27 +247,34 @@ ssize_t HttpClient::onRecvHeader(const char *data, size_t len) { } } }); - //后续为源源不断的body + // 后续为源源不断的body [AUTO-TRANSLATED:bf551bbd] + // The following is a continuous body return -1; } if (!_parser["Content-Length"].empty()) { - //有Content-Length字段时忽略onResponseHeader的返回值 + // 有Content-Length字段时忽略onResponseHeader的返回值 [AUTO-TRANSLATED:50380ba8] + // Ignore the return value of onResponseHeader when there is a Content-Length field _total_body_size = atoll(_parser["Content-Length"].data()); } else { _total_body_size = -1; } if (_total_body_size == 0) { - //后续没content,本次http请求结束 + // 后续没content,本次http请求结束 [AUTO-TRANSLATED:8532172f] + // There is no content afterwards, this http request ends onResponseCompleted_l(SockException(Err_success, "The request is successful but has no body")); return 0; } - //当_total_body_size != 0时到达这里,代表后续有content - //虽然我们在_total_body_size >0 时知道content的确切大小, - //但是由于我们没必要等content接收完毕才回调onRecvContent(因为这样浪费内存并且要多次拷贝数据) - //所以返回-1代表我们接下来分段接收content + // 当_total_body_size != 0时到达这里,代表后续有content [AUTO-TRANSLATED:3a55b268] + // When _total_body_size != 0, it means there is content afterwards + // 虽然我们在_total_body_size >0 时知道content的确切大小, [AUTO-TRANSLATED:af91f74f] + // Although we know the exact size of the content when _total_body_size > 0, + // 但是由于我们没必要等content接收完毕才回调onRecvContent(因为这样浪费内存并且要多次拷贝数据) [AUTO-TRANSLATED:fd71692c] + // But because we don't need to wait for the content to be received before calling onRecvContent (because this wastes memory and requires multiple data copies) + // 所以返回-1代表我们接下来分段接收content [AUTO-TRANSLATED:388756f6] + // So returning -1 means we will receive the content in segments next _recved_body_size = 0; return -1; } @@ -273,26 +286,31 @@ void HttpClient::onRecvContent(const char *data, size_t len) { } _recved_body_size += len; if (_total_body_size < 0) { - //不限长度的content + // 不限长度的content [AUTO-TRANSLATED:325a9dbc] + // Unlimited length content onResponseBody(data, len); return; } - //固定长度的content + // 固定长度的content [AUTO-TRANSLATED:4d169746] + // Fixed length content if (_recved_body_size < (size_t) _total_body_size) { - //content还未接收完毕 + // content还未接收完毕 [AUTO-TRANSLATED:b30ca92c] + // Content has not been received yet onResponseBody(data, len); return; } if (_recved_body_size == (size_t)_total_body_size) { - //content接收完毕 + // content接收完毕 [AUTO-TRANSLATED:e730ea8c] + // Content received onResponseBody(data, len); onResponseCompleted_l(SockException(Err_success, "completed")); return; } - //声明的content数据比真实的小,断开链接 + // 声明的content数据比真实的小,断开链接 [AUTO-TRANSLATED:38204302] + // The declared content data is smaller than the real one, disconnect onResponseBody(data, len); throw invalid_argument("http response content size bigger than expected"); } @@ -302,40 +320,50 @@ void HttpClient::onFlush() { while (_body && _body->remainSize() && !isSocketBusy()) { auto buffer = _body->readData(send_buf_size); if (!buffer) { - //数据发送结束或读取数据异常 + // 数据发送结束或读取数据异常 [AUTO-TRANSLATED:75179972] + // Data transmission ends or data reading exception break; } if (send(buffer) <= 0) { - //发送数据失败,不需要回滚数据,因为发送前已经通过isSocketBusy()判断socket可写 - //所以发送缓存区肯定未满,该buffer肯定已经写入socket + // 发送数据失败,不需要回滚数据,因为发送前已经通过isSocketBusy()判断socket可写 [AUTO-TRANSLATED:30762202] + // Data transmission failed, no need to roll back data, because the socket is writable before sending + // 所以发送缓存区肯定未满,该buffer肯定已经写入socket [AUTO-TRANSLATED:769fff52] + // So the send buffer is definitely not full, this buffer must have been written to the socket break; } } } void HttpClient::onManager() { - //onManager回调在连接中或已连接状态才会调用 + // onManager回调在连接中或已连接状态才会调用 [AUTO-TRANSLATED:acf86dce] + // The onManager callback is only called when the connection is in progress or connected if (_wait_complete_ms > 0) { - //设置了总超时时间 + // 设置了总超时时间 [AUTO-TRANSLATED:ac47c234] + // Total timeout is set if (!_complete && _wait_complete.elapsedTime() > _wait_complete_ms) { - //等待http回复完毕超时 + // 等待http回复完毕超时 [AUTO-TRANSLATED:711ebc7b] + // Timeout waiting for http reply to finish shutdown(SockException(Err_timeout, "wait http response complete timeout")); return; } return; } - //未设置总超时时间 + // 未设置总超时时间 [AUTO-TRANSLATED:a936338f] + // Total timeout is not set if (!_header_recved) { - //等待header中 + // 等待header中 [AUTO-TRANSLATED:f8635de6] + // Waiting for header if (_wait_header.elapsedTime() > _wait_header_ms) { - //等待header中超时 + // 等待header中超时 [AUTO-TRANSLATED:860d3a16] + // Timeout waiting for header shutdown(SockException(Err_timeout, "wait http response header timeout")); return; } } else if (_wait_body_ms > 0 && _wait_body.elapsedTime() > _wait_body_ms) { - //等待body中,等待超时 + // 等待body中,等待超时 [AUTO-TRANSLATED:f9bb1d66] + // Waiting for body, timeout shutdown(SockException(Err_timeout, "wait http response body timeout")); return; } @@ -349,25 +377,30 @@ void HttpClient::onResponseCompleted_l(const SockException &ex) { _wait_complete.resetTime(); if (!ex) { - //确认无疑的成功 + // 确认无疑的成功 [AUTO-TRANSLATED:e1db8ce2] + // Confirmed success onResponseCompleted(ex); return; } - //可疑的失败 + // 可疑的失败 [AUTO-TRANSLATED:1258a436] + // Suspicious failure if (_total_body_size > 0 && _recved_body_size >= (size_t)_total_body_size) { - //回复header中有content-length信息,那么收到的body大于等于声明值则认为成功 + // 回复header中有content-length信息,那么收到的body大于等于声明值则认为成功 [AUTO-TRANSLATED:2f813650] + // If the response header contains content-length information, then the received body is considered successful if it is greater than or equal to the declared value onResponseCompleted(SockException(Err_success, "read body completed")); return; } if (_total_body_size == -1 && _recved_body_size > 0) { - //回复header中无content-length信息,那么收到一点body也认为成功 + // 回复header中无content-length信息,那么收到一点body也认为成功 [AUTO-TRANSLATED:6c0e87fc] + // If the response header does not contain content-length information, then receiving any body is considered successful onResponseCompleted(SockException(Err_success, ex.what())); return; } - //确认无疑的失败 + // 确认无疑的失败 [AUTO-TRANSLATED:33b216d9] + // Confirmed failure onResponseCompleted(ex); } @@ -409,7 +442,8 @@ void HttpClient::checkCookie(HttpClient::HttpHeader &headers) { } if (!(*cookie)) { - //无效的cookie + // 无效的cookie [AUTO-TRANSLATED:5f06aec8] + // Invalid cookie continue; } HttpCookieStorage::Instance().set(cookie); diff --git a/src/Http/HttpClient.h b/src/Http/HttpClient.h index 06a0cdb8..c7ca2433 100644 --- a/src/Http/HttpClient.h +++ b/src/Http/HttpClient.h @@ -52,23 +52,38 @@ public: /** * 发送http[s]请求 * @param url 请求url + * Send http[s] request + * @param url Request url + + * [AUTO-TRANSLATED:01b6c9ac] */ virtual void sendRequest(const std::string &url); /** * 重置对象 + * Reset object + + * [AUTO-TRANSLATED:d23b5bbb] */ virtual void clear(); /** * 设置http方法 * @param method GET/POST等 + * Set http method + * @param method GET/POST etc. + + * [AUTO-TRANSLATED:5199546a] */ void setMethod(std::string method); /** * 覆盖http头 * @param header + * Override http header + * @param header + + * [AUTO-TRANSLATED:ea31a471] */ void setHeader(HttpHeader header); @@ -77,48 +92,78 @@ public: /** * 设置http content * @param body http content + * Set http content + * @param body http content + + * [AUTO-TRANSLATED:9993580c] */ void setBody(std::string body); /** * 设置http content * @param body http content + * Set http content + * @param body http content + + * [AUTO-TRANSLATED:9993580c] */ void setBody(HttpBody::Ptr body); /** * 获取回复,在收到完整回复后有效 + * Get response, valid after receiving the complete response + + * [AUTO-TRANSLATED:b107995e] */ const Parser &response() const; /** * 获取回复header声明的body大小 + * Get the body size declared in the response header + + * [AUTO-TRANSLATED:65f8e782] */ ssize_t responseBodyTotalSize() const; /** * 获取已经下载body的大小 + * Get the size of the body that has been downloaded + + * [AUTO-TRANSLATED:a3cde7b4] */ size_t responseBodySize() const; /** * 获取请求url + * Get the request url + + * [AUTO-TRANSLATED:cc7fe537] */ const std::string &getUrl() const; /** * 判断是否正在等待响应 + * Determine if the response is pending + + * [AUTO-TRANSLATED:058719d7] */ bool waitResponse() const; /** * 判断是否为https + * Determine if it is https + + * [AUTO-TRANSLATED:9b3a0254] */ bool isHttps() const; /** * 设置从发起连接到接收header完毕的延时,默认10秒 * 此参数必须大于0 + * Set the delay from initiating the connection to receiving the header, default 10 seconds + * This parameter must be greater than 0 + + * [AUTO-TRANSLATED:4cce3e85] */ void setHeaderTimeout(size_t timeout_ms); @@ -126,17 +171,29 @@ public: * 设置接收body数据超时时间, 默认5秒 * 此参数可以用于处理超大body回复的超时问题 * 此参数可以等于0 + * Set the timeout for receiving body data, default 5 seconds + * This parameter can be used to handle timeout issues for large body responses + * This parameter can be equal to 0 + + * [AUTO-TRANSLATED:48585852] */ void setBodyTimeout(size_t timeout_ms); /** * 设置整个链接超时超时时间, 默认0 * 该值设置不为0后,HeaderTimeout和BodyTimeout无效 + * Set the timeout for the entire link, default 0 + * After this value is set to non-zero, HeaderTimeout and BodyTimeout are invalid + + * [AUTO-TRANSLATED:df094868] */ void setCompleteTimeout(size_t timeout_ms); /** * 设置http代理url + * Set http proxy url + + * [AUTO-TRANSLATED:95df17e7] */ void setProxyUrl(std::string proxy_url); @@ -144,6 +201,10 @@ public: * 当重用连接失败时, 是否允许重新发起请求 * If the reuse connection fails, whether to allow the request to be resent * @param allow true:允许重新发起请求 / true: allow the request to be resent + * When the reuse connection fails, whether to allow the request to be resent + * @param allow true: allow the request to be resent + + * [AUTO-TRANSLATED:71bd8e67] */ void setAllowResendRequest(bool allow); @@ -152,6 +213,11 @@ protected: * 收到http回复头 * @param status 状态码,譬如:200 OK * @param headers http头 + * Receive http response header + * @param status Status code, such as: 200 OK + * @param headers http header + + * [AUTO-TRANSLATED:a685f8ef] */ virtual void onResponseHeader(const std::string &status, const HttpHeader &headers) = 0; @@ -159,11 +225,19 @@ protected: * 收到http conten数据 * @param buf 数据指针 * @param size 数据大小 + * Receive http content data + * @param buf Data pointer + * @param size Data size + + * [AUTO-TRANSLATED:bee3bf62] */ virtual void onResponseBody(const char *buf, size_t size) = 0; /** * 接收http回复完毕, + * Receive http response complete, + + * [AUTO-TRANSLATED:b96ed715] */ virtual void onResponseCompleted(const toolkit::SockException &ex) = 0; @@ -172,6 +246,11 @@ protected: * @param url 重定向url * @param temporary 是否为临时重定向 * @return 是否继续 + * Redirect event + * @param url Redirect url + * @param temporary Whether it is a temporary redirect + * @return Whether to continue + * [AUTO-TRANSLATED:b64d5f8b] */ virtual bool onRedirectUrl(const std::string &url, bool temporary) { return true; }; diff --git a/src/Http/HttpClientImp.cpp b/src/Http/HttpClientImp.cpp index 1858c284..3c01e2c5 100644 --- a/src/Http/HttpClientImp.cpp +++ b/src/Http/HttpClientImp.cpp @@ -16,12 +16,14 @@ namespace mediakit { void HttpClientImp::onConnect(const SockException &ex) { if (isUsedProxy() && !isProxyConnected()) { - // 连接代理服务器 + // 连接代理服务器 [AUTO-TRANSLATED:e7a8979a] + // Connect to the proxy server setDoNotUseSSL(); HttpClient::onConnect(ex); } else { if (!isHttps()) { - // https 302跳转 http时,需要关闭ssl + // https 302跳转 http时,需要关闭ssl [AUTO-TRANSLATED:2ba55daf] + // When https 302 redirects to http, ssl needs to be closed setDoNotUseSSL(); HttpClient::onConnect(ex); } else { diff --git a/src/Http/HttpConst.h b/src/Http/HttpConst.h index a737306e..7973243c 100644 --- a/src/Http/HttpConst.h +++ b/src/Http/HttpConst.h @@ -24,6 +24,11 @@ public: * 根据http错误代码获取字符说明 * @param status 譬如404 * @return 错误代码字符说明,譬如Not Found + * Get character description based on http error code + * @param status For example 404 + * @return Error code character description, for example Not Found + + * [AUTO-TRANSLATED:7b844410] */ static const char *getHttpStatusMessage(int status); @@ -31,6 +36,12 @@ public: * 根据文件后缀返回http mime * @param name 文件后缀,譬如html * @return mime值,譬如text/html + * Return http mime based on file suffix + * @param name File suffix, for example html + * @return mime value, for example text/html + + + * [AUTO-TRANSLATED:03d63e1f] */ static const std::string &getHttpContentType(const char *name); }; diff --git a/src/Http/HttpCookie.cpp b/src/Http/HttpCookie.cpp index 5ac6bb7b..43816bd6 100644 --- a/src/Http/HttpCookie.cpp +++ b/src/Http/HttpCookie.cpp @@ -37,6 +37,7 @@ static time_t time_to_epoch(const struct tm *ltm, int utcdiff) { tyears = ltm->tm_year - 70; // tm->tm_year is from 1900. leaps = (tyears + 2) / 4; // no of next two lines until year 2100. + // i = (ltm->tm_year – 100) / 100; [AUTO-TRANSLATED:12beea30] // i = (ltm->tm_year – 100) / 100; // leaps -= ( (i/4)*3 + i%4 ); tdays = 0; @@ -53,7 +54,8 @@ static time_t time_to_epoch(const struct tm *ltm, int utcdiff) { static time_t timeStrToInt(const string &date) { struct tm tt; strptime(date.data(), "%a, %b %d %Y %H:%M:%S %Z", &tt); - // mktime内部有使用互斥锁,非常影响性能 + // mktime内部有使用互斥锁,非常影响性能 [AUTO-TRANSLATED:b3270635] + // mktime uses mutex internally, which significantly affects performance return time_to_epoch(&tt, getGMTOff() / 3600); // mktime(&tt); } @@ -99,23 +101,29 @@ vector HttpCookieStorage::get(const string &host, const string lock_guard lck(_mtx_cookie); auto it = _all_cookie.find(host); if (it == _all_cookie.end()) { - //未找到该host相关记录 + // 未找到该host相关记录 [AUTO-TRANSLATED:0655542a] + // No record found for this host return ret; } - //遍历该host下所有path + // 遍历该host下所有path [AUTO-TRANSLATED:94ca2180] + // Traverse all paths under this host for (auto &pr : it->second) { if (path.find(pr.first) != 0) { - //这个path不匹配 + // 这个path不匹配 [AUTO-TRANSLATED:3ec99732] + // This path does not match continue; } - //遍历该path下的各个cookie + // 遍历该path下的各个cookie [AUTO-TRANSLATED:ceab9c83] + // Traverse all cookies under this path for (auto it_cookie = pr.second.begin(); it_cookie != pr.second.end();) { if (!*(it_cookie->second)) { - //该cookie已经过期,移除之 + // 该cookie已经过期,移除之 [AUTO-TRANSLATED:52762286] + // This cookie has expired, remove it it_cookie = pr.second.erase(it_cookie); continue; } - //保存有效cookie + // 保存有效cookie [AUTO-TRANSLATED:bd875507] + // Save valid cookies ret.emplace_back(it_cookie->second); ++it_cookie; } diff --git a/src/Http/HttpCookie.h b/src/Http/HttpCookie.h index 171a7edc..86d17a9a 100644 --- a/src/Http/HttpCookie.h +++ b/src/Http/HttpCookie.h @@ -22,6 +22,9 @@ namespace mediakit { /** * http客户端cookie对象 + * http client cookie object + + * [AUTO-TRANSLATED:5c1840bb] */ class HttpCookie { public: @@ -47,6 +50,10 @@ private: /** * http客户端cookie全局保存器 + * http client cookie global saver + + + * [AUTO-TRANSLATED:cac4a704] */ class HttpCookieStorage{ public: diff --git a/src/Http/HttpCookieManager.cpp b/src/Http/HttpCookieManager.cpp index b0b7ed52..1b3d4c35 100644 --- a/src/Http/HttpCookieManager.cpp +++ b/src/Http/HttpCookieManager.cpp @@ -75,7 +75,8 @@ string HttpServerCookie::cookieExpireTime() const { INSTANCE_IMP(HttpCookieManager); HttpCookieManager::HttpCookieManager() { - //定时删除过期的cookie,防止内存膨胀 + // 定时删除过期的cookie,防止内存膨胀 [AUTO-TRANSLATED:dd9dc9c0] + // Delete expired cookies periodically to prevent memory bloat _timer = std::make_shared( 10.0f, [this]() { @@ -91,12 +92,15 @@ HttpCookieManager::~HttpCookieManager() { void HttpCookieManager::onManager() { lock_guard lck(_mtx_cookie); - //先遍历所有类型 + // 先遍历所有类型 [AUTO-TRANSLATED:4917ee89] + // First iterate through all types for (auto it_name = _map_cookie.begin(); it_name != _map_cookie.end();) { - //再遍历该类型下的所有cookie + // 再遍历该类型下的所有cookie [AUTO-TRANSLATED:0aab9e18] + // Then iterate through all cookies under that type for (auto it_cookie = it_name->second.begin(); it_cookie != it_name->second.end();) { if (it_cookie->second->isExpired()) { - // cookie过期,移除记录 + // cookie过期,移除记录 [AUTO-TRANSLATED:8b48b8a2] + // Cookie expired, remove record DebugL << it_cookie->second->getUid() << " cookie过期:" << it_cookie->second->getCookie(); it_cookie = it_name->second.erase(it_cookie); continue; @@ -105,7 +109,8 @@ void HttpCookieManager::onManager() { } if (it_name->second.empty()) { - //该类型下没有任何cookie记录,移除之 + // 该类型下没有任何cookie记录,移除之 [AUTO-TRANSLATED:92e3b783] + // There are no cookie records under this type, remove it DebugL << "该path下没有任何cookie记录:" << it_name->first; it_name = _map_cookie.erase(it_name); continue; @@ -120,13 +125,16 @@ HttpServerCookie::Ptr HttpCookieManager::addCookie(const string &cookie_name, co auto uid = uid_in.empty() ? cookie : uid_in; auto oldCookie = getOldestCookie(cookie_name, uid, max_client); if (!oldCookie.empty()) { - //假如该账号已经登录了,那么删除老的cookie。 - //目的是实现单账号多地登录时挤占登录 + // 假如该账号已经登录了,那么删除老的cookie。 [AUTO-TRANSLATED:f18d826d] + // If the account has already logged in, delete the old cookie. + // 目的是实现单账号多地登录时挤占登录 [AUTO-TRANSLATED:8a64aec7] + // The purpose is to achieve login squeeze when multiple devices log in with the same account delCookie(cookie_name, oldCookie); } HttpServerCookie::Ptr data(new HttpServerCookie(shared_from_this(), cookie_name, uid, cookie, max_elapsed)); data->setAttach(std::move(attach)); - //保存该账号下的新cookie + // 保存该账号下的新cookie [AUTO-TRANSLATED:e476c9c8] + // Save the new cookie under this account _map_cookie[cookie_name][cookie] = data; return data; } @@ -135,16 +143,19 @@ HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name, co lock_guard lck(_mtx_cookie); auto it_name = _map_cookie.find(cookie_name); if (it_name == _map_cookie.end()) { - //不存在该类型的cookie + // 不存在该类型的cookie [AUTO-TRANSLATED:d32b0997] + // There is no cookie of this type return nullptr; } auto it_cookie = it_name->second.find(cookie); if (it_cookie == it_name->second.end()) { - //该类型下没有对应的cookie + // 该类型下没有对应的cookie [AUTO-TRANSLATED:62caa764] + // There is no corresponding cookie under this type return nullptr; } if (it_cookie->second->isExpired()) { - // cookie过期 + // cookie过期 [AUTO-TRANSLATED:a980453f] + // Cookie expired DebugL << "cookie过期:" << it_cookie->second->getCookie(); it_name->second.erase(it_cookie); return nullptr; @@ -195,48 +206,58 @@ bool HttpCookieManager::delCookie(const string &cookie_name, const string &cooki } void HttpCookieManager::onAddCookie(const string &cookie_name, const string &uid, const string &cookie) { - //添加新的cookie,我们记录下这个uid下有哪些cookie,目的是实现单账号多地登录时挤占登录 + // 添加新的cookie,我们记录下这个uid下有哪些cookie,目的是实现单账号多地登录时挤占登录 [AUTO-TRANSLATED:60b752e9] + // Add a new cookie, we record which cookies are under this uid, the purpose is to achieve login squeeze when multiple devices log in with the same account lock_guard lck(_mtx_cookie); - //相同用户下可以存在多个cookie(意味多地登录),这些cookie根据登录时间的早晚依次排序 + // 相同用户下可以存在多个cookie(意味多地登录),这些cookie根据登录时间的早晚依次排序 [AUTO-TRANSLATED:1e0b93b9] + // Multiple cookies can exist under the same user (meaning multiple devices log in), these cookies are sorted in order of login time _map_uid_to_cookie[cookie_name][uid][getCurrentMillisecond()] = cookie; } void HttpCookieManager::onDelCookie(const string &cookie_name, const string &uid, const string &cookie) { lock_guard lck(_mtx_cookie); - //回收随机字符串 + // 回收随机字符串 [AUTO-TRANSLATED:18a699ff] + // Recycle random string _generator.release(cookie); auto it_name = _map_uid_to_cookie.find(cookie_name); if (it_name == _map_uid_to_cookie.end()) { - //该类型下未有任意用户登录 + // 该类型下未有任意用户登录 [AUTO-TRANSLATED:8ba458b9] + // No user has logged in under this type return; } auto it_uid = it_name->second.find(uid); if (it_uid == it_name->second.end()) { - //该用户尚未登录 + // 该用户尚未登录 [AUTO-TRANSLATED:ec07ce1b] + // This user has not logged in yet return; } - //遍历同一名用户下的所有客户端,移除命中的客户端 + // 遍历同一名用户下的所有客户端,移除命中的客户端 [AUTO-TRANSLATED:cae6e264] + // Iterate through all clients under the same user and remove the matching client for (auto it_cookie = it_uid->second.begin(); it_cookie != it_uid->second.end(); ++it_cookie) { if (it_cookie->second != cookie) { - //不是该cookie + // 不是该cookie [AUTO-TRANSLATED:cf5eca3b] + // Not this cookie continue; } - //移除该用户名下的某个cookie,这个设备cookie将失效 + // 移除该用户名下的某个cookie,这个设备cookie将失效 [AUTO-TRANSLATED:bf2de2a0] + // Remove a cookie under this username, this device cookie will become invalid it_uid->second.erase(it_cookie); if (!it_uid->second.empty()) { break; } - //该用户名下没有任何设备在线,移除之 + // 该用户名下没有任何设备在线,移除之 [AUTO-TRANSLATED:6a8a2305] + // There are no devices online under this username, remove it it_name->second.erase(it_uid); if (!it_name->second.empty()) { break; } - //该类型下未有任何用户在线,移除之 + // 该类型下未有任何用户在线,移除之 [AUTO-TRANSLATED:e705cfe6] + // There are no users online under this type, remove it _map_uid_to_cookie.erase(it_name); break; } @@ -246,29 +267,35 @@ string HttpCookieManager::getOldestCookie(const string &cookie_name, const strin lock_guard lck(_mtx_cookie); auto it_name = _map_uid_to_cookie.find(cookie_name); if (it_name == _map_uid_to_cookie.end()) { - //不存在该类型的cookie + // 不存在该类型的cookie [AUTO-TRANSLATED:d32b0997] + // There is no cookie of this type return ""; } auto it_uid = it_name->second.find(uid); if (it_uid == it_name->second.end()) { - //该用户从未登录过 + // 该用户从未登录过 [AUTO-TRANSLATED:fc6dbcf6] + // This user has never logged in return ""; } if ((int)it_uid->second.size() < MAX(1, max_client)) { - //同一名用户下,客户端个数还没达到限制个数 + // 同一名用户下,客户端个数还没达到限制个数 [AUTO-TRANSLATED:a31f6ada] + // Under the same user, the number of clients has not reached the limit return ""; } - //客户端个数超过限制,移除最先登录的客户端 + // 客户端个数超过限制,移除最先登录的客户端 [AUTO-TRANSLATED:a284ce91] + // The number of clients exceeds the limit, remove the first client to log in return it_uid->second.begin()->second; } /////////////////////////////////RandStrGenerator//////////////////////////////////// string RandStrGenerator::obtain() { - //获取唯一的防膨胀的随机字符串 + // 获取唯一的防膨胀的随机字符串 [AUTO-TRANSLATED:1306465c] + // Get a unique anti-bloating random string while (true) { auto str = obtain_l(); if (_obtained.find(str) == _obtained.end()) { - //没有重复 + // 没有重复 [AUTO-TRANSLATED:16af311b] + // No duplicates _obtained.emplace(str); return str; } @@ -276,12 +303,14 @@ string RandStrGenerator::obtain() { } void RandStrGenerator::release(const string &str) { - //从防膨胀库中移除 + // 从防膨胀库中移除 [AUTO-TRANSLATED:1165d5fe] + // Remove from the anti-bloating library _obtained.erase(str); } string RandStrGenerator::obtain_l() { - // 12个伪随机字节 + 4个递增的整形字节,然后md5即为随机字符串 + // 12个伪随机字节 + 4个递增的整形字节,然后md5即为随机字符串 [AUTO-TRANSLATED:8571a327] + // 12 pseudo-random bytes + 4 incrementing integer bytes, then md5 is the random string auto str = makeRandStr(12, false); str.append((char *)&_index, sizeof(_index)); ++_index; diff --git a/src/Http/HttpCookieManager.h b/src/Http/HttpCookieManager.h index 0a375e3c..a0e29a4e 100644 --- a/src/Http/HttpCookieManager.h +++ b/src/Http/HttpCookieManager.h @@ -27,6 +27,9 @@ class HttpCookieManager; /** * cookie对象,用于保存cookie的一些相关属性 + * cookie object, used to store some related attributes of the cookie + + * [AUTO-TRANSLATED:267fbbc3] */ class HttpServerCookie : public toolkit::noncopyable { public: @@ -38,6 +41,14 @@ public: * @param uid 用户唯一id * @param cookie cookie随机字符串 * @param max_elapsed 最大过期时间,单位秒 + * Construct cookie + * @param manager cookie manager object + * @param cookie_name cookie name, such as MY_SESSION + * @param uid user unique id + * @param cookie cookie random string + * @param max_elapsed maximum expiration time, in seconds + + * [AUTO-TRANSLATED:a24f209d] */ HttpServerCookie( @@ -48,6 +59,10 @@ public: /** * 获取uid * @return uid + * Get uid + * @return uid + + * [AUTO-TRANSLATED:71a3afab] */ const std::string &getUid() const; @@ -56,39 +71,67 @@ public: * @param cookie_name 该cookie的名称,譬如 MY_SESSION * @param path http访问路径 * @return 例如 MY_SESSION=XXXXXX;expires=Wed, Jun 12 2019 06:30:48 GMT;path=/index/files/ + * Get the value of the Set-Cookie field in http + * @param cookie_name the name of this cookie, such as MY_SESSION + * @param path http access path + * @return For example, MY_SESSION=XXXXXX;expires=Wed, Jun 12 2019 06:30:48 GMT;path=/index/files/ + + * [AUTO-TRANSLATED:8699036b] */ std::string getCookie(const std::string &path) const; /** * 获取cookie随机字符串 * @return cookie随机字符串 + * Get cookie random string + * @return cookie random string + + * [AUTO-TRANSLATED:1853611a] */ const std::string &getCookie() const; /** * 获取该cookie名 * @return + * Get the name of this cookie + * @return + + * [AUTO-TRANSLATED:6251f9f5] */ const std::string &getCookieName() const; /** * 更新该cookie的过期时间,可以让此cookie不失效 + * Update the expiration time of this cookie, so that this cookie will not expire + + * [AUTO-TRANSLATED:d3a3300b] */ void updateTime(); /** * 判断该cookie是否过期 * @return + * Determine whether this cookie has expired + * @return + + * [AUTO-TRANSLATED:3b0d3d59] */ bool isExpired(); /** * 设置附加数据 + * Set additional data + + * [AUTO-TRANSLATED:afde9874] */ void setAttach(toolkit::Any attach); /* * 获取附加数据 + /* + * Get additional data + + * [AUTO-TRANSLATED:e277d75d] */ template T& getAttach() { @@ -110,6 +153,9 @@ private: /** * cookie随机字符串生成器 + * cookie random string generator + + * [AUTO-TRANSLATED:501ea34c] */ class RandStrGenerator { public: @@ -117,12 +163,20 @@ public: /** * 获取不碰撞的随机字符串 * @return 随机字符串 + * Get a random string that does not collide + * @return random string + + * [AUTO-TRANSLATED:6daa3fd8] */ std::string obtain(); /** * 释放随机字符串 * @param str 随机字符串 + * Release random string + * @param str random string + + * [AUTO-TRANSLATED:90ea164a] */ void release(const std::string &str); @@ -130,15 +184,21 @@ private: std::string obtain_l(); private: - //碰撞库 + // 碰撞库 [AUTO-TRANSLATED:25a2ca2b] + // Collision library std::unordered_set _obtained; - //增长index,防止碰撞用 + // 增长index,防止碰撞用 [AUTO-TRANSLATED:85778468] + // Increase index, used to prevent collisions int _index = 0; }; /** * cookie管理器,用于管理cookie的生成以及过期管理,同时实现了同账号异地挤占登录功能 * 该对象实现了同账号最多登录若干个设备 + * Cookie manager, used to manage cookie generation and expiration management, and also implements the function of occupying login from different locations with the same account + * This object implements the function that the same account can log in to at most several devices + + * [AUTO-TRANSLATED:ad6008e8] */ class HttpCookieManager : public std::enable_shared_from_this { public: @@ -148,6 +208,9 @@ public: /** * 获取单例 + * Get singleton + + * [AUTO-TRANSLATED:d082a6ee] */ static HttpCookieManager &Instance(); @@ -158,6 +221,14 @@ public: * @param max_client 该账号最多登录多少个设备 * @param max_elapsed 该cookie过期时间,单位秒 * @return cookie对象 + * Add cookie + * @param cookie_name cookie name, such as MY_SESSION + * @param uid user id, if empty, it is anonymous login + * @param max_client the maximum number of devices that this account can log in to + * @param max_elapsed the expiration time of this cookie, in seconds + * @return cookie object + + * [AUTO-TRANSLATED:c23f2321] */ HttpServerCookie::Ptr addCookie( const std::string &cookie_name, const std::string &uid, uint64_t max_elapsed = COOKIE_DEFAULT_LIFE, @@ -169,6 +240,12 @@ public: * @param cookie_name cookie名,例如MY_SESSION * @param cookie cookie随机字符串 * @return cookie对象,可以为nullptr + * Find cookie object by cookie random string + * @param cookie_name cookie name, such as MY_SESSION + * @param cookie cookie random string + * @return cookie object, can be nullptr + + * [AUTO-TRANSLATED:a0c7ed63] */ HttpServerCookie::Ptr getCookie(const std::string &cookie_name, const std::string &cookie); @@ -177,6 +254,12 @@ public: * @param cookie_name cookie名,例如MY_SESSION * @param http_header http头 * @return cookie对象 + * Get cookie object from http header + * @param cookie_name cookie name, such as MY_SESSION + * @param http_header http header + * @return cookie object + + * [AUTO-TRANSLATED:93661474] */ HttpServerCookie::Ptr getCookie(const std::string &cookie_name, const StrCaseMap &http_header); @@ -185,6 +268,12 @@ public: * @param cookie_name cookie名,例如MY_SESSION * @param uid 用户id * @return cookie对象 + * Get cookie by uid + * @param cookie_name cookie name, such as MY_SESSION + * @param uid user id + * @return cookie object + + * [AUTO-TRANSLATED:623277e4] */ HttpServerCookie::Ptr getCookieByUid(const std::string &cookie_name, const std::string &uid); @@ -192,6 +281,11 @@ public: * 删除cookie,用户登出时使用 * @param cookie cookie对象,可以为nullptr * @return + * Delete cookie, used when user logs out + * @param cookie cookie object, can be nullptr + * @return + + * [AUTO-TRANSLATED:f80c6974] */ bool delCookie(const HttpServerCookie::Ptr &cookie); @@ -204,6 +298,12 @@ private: * @param cookie_name cookie名,例如MY_SESSION * @param uid 用户id * @param cookie cookie随机字符串 + * Triggered when constructing a cookie object, the purpose is to record multiple cookies under a certain account + * @param cookie_name cookie name, such as MY_SESSION + * @param uid user id + * @param cookie cookie random string + + * [AUTO-TRANSLATED:bb2bb670] */ void onAddCookie(const std::string &cookie_name, const std::string &uid, const std::string &cookie); @@ -212,6 +312,12 @@ private: * @param cookie_name cookie名,例如MY_SESSION * @param uid 用户id * @param cookie cookie随机字符串 + * Triggered when destructing a cookie object + * @param cookie_name cookie name, such as MY_SESSION + * @param uid user id + * @param cookie cookie random string + + * [AUTO-TRANSLATED:bdf9cce5] */ void onDelCookie(const std::string &cookie_name, const std::string &uid, const std::string &cookie); @@ -221,6 +327,13 @@ private: * @param uid 用户id * @param max_client 最多登录的设备个数 * @return 最早的cookie随机字符串 + * Get the cookie that logged in first under a certain username, the purpose is to implement the function that at most several devices can log in under a certain user + * @param cookie_name cookie name, such as MY_SESSION + * @param uid user id + * @param max_client the maximum number of devices that can log in + * @return the earliest cookie random string + + * [AUTO-TRANSLATED:431b0732] */ std::string getOldestCookie(const std::string &cookie_name, const std::string &uid, int max_client = 1); @@ -229,6 +342,12 @@ private: * @param cookie_name cookie名,例如MY_SESSION * @param cookie cookie随机字符串 * @return 成功true + * Delete cookie + * @param cookie_name cookie name, such as MY_SESSION + * @param cookie cookie random string + * @return success true + + * [AUTO-TRANSLATED:09fa1e44] */ bool delCookie(const std::string &cookie_name, const std::string &cookie); diff --git a/src/Http/HttpDownloader.cpp b/src/Http/HttpDownloader.cpp index 633575cd..b2ab5fb0 100644 --- a/src/Http/HttpDownloader.cpp +++ b/src/Http/HttpDownloader.cpp @@ -33,7 +33,8 @@ void HttpDownloader::startDownload(const string &url, const string &file_path, b if (append) { auto currentLen = ftell(_save_file); if (currentLen) { - //最少续传一个字节,怕遇到http 416的错误 + // 最少续传一个字节,怕遇到http 416的错误 [AUTO-TRANSLATED:8a3c5303] + // Resume downloading at least one byte to avoid encountering a http 416 error currentLen -= 1; fseek(_save_file, -1, SEEK_CUR); } @@ -45,7 +46,8 @@ void HttpDownloader::startDownload(const string &url, const string &file_path, b void HttpDownloader::onResponseHeader(const string &status, const HttpHeader &headers) { if (status != "200" && status != "206") { - //失败 + // 失败 [AUTO-TRANSLATED:27ec5fb1] + // Failure throw std::invalid_argument("bad http status: " + status); } } diff --git a/src/Http/HttpDownloader.h b/src/Http/HttpDownloader.h index 6c618aa6..08f8f3fa 100644 --- a/src/Http/HttpDownloader.h +++ b/src/Http/HttpDownloader.h @@ -27,6 +27,13 @@ public: * @param url 下载http url * @param file_path 文件保存地址,置空则选择默认文件路径 * @param append 如果文件已经存在,是否断点续传方式下载 + * Start downloading the file, default to resume download + * @param url Download http url + * @param file_path File save address, leave blank to choose the default file path + * @param append If the file already exists, whether to download in resume mode + + + * [AUTO-TRANSLATED:6f651882] */ void startDownload(const std::string &url, const std::string &file_path = "", bool append = false); diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index cf6003da..6f53b8d3 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -23,9 +23,12 @@ using namespace toolkit; namespace mediakit { -// hls的播放cookie缓存时间默认60秒, -// 每次访问一次该cookie,那么将重新刷新cookie有效期 -// 假如播放器在60秒内都未访问该cookie,那么将重新触发hls播放鉴权 +// hls的播放cookie缓存时间默认60秒, [AUTO-TRANSLATED:88198dfa] +// The default cache time for the hls playback cookie is 60 seconds. +// 每次访问一次该cookie,那么将重新刷新cookie有效期 [AUTO-TRANSLATED:a1b76209] +// Each time this cookie is accessed, the cookie's validity period will be refreshed. +// 假如播放器在60秒内都未访问该cookie,那么将重新触发hls播放鉴权 [AUTO-TRANSLATED:55000c94] +// If the player does not access the cookie within 60 seconds, the hls playback authentication will be triggered again. static size_t kHlsCookieSecond = 60; static size_t kFindSrcIntervalSecond = 3; static const string kCookieName = "ZL_COOKIE"; @@ -33,15 +36,20 @@ static const string kHlsSuffix = "/hls.m3u8"; static const string kHlsFMP4Suffix = "/hls.fmp4.m3u8"; struct HttpCookieAttachment { - // 是否已经查找到过MediaSource + // 是否已经查找到过MediaSource [AUTO-TRANSLATED:b5b9922a] + // Whether the MediaSource has been found bool _find_src = false; - // 查找MediaSource计时 + // 查找MediaSource计时 [AUTO-TRANSLATED:39904ba9] + // MediaSource search timing Ticker _find_src_ticker; - //cookie生效作用域,本cookie只对该目录下的文件生效 + // cookie生效作用域,本cookie只对该目录下的文件生效 [AUTO-TRANSLATED:7a59ad9a] + // Cookie effective scope, this cookie only takes effect for files under this directory string _path; - //上次鉴权失败信息,为空则上次鉴权成功 + // 上次鉴权失败信息,为空则上次鉴权成功 [AUTO-TRANSLATED:de48b753] + // Last authentication failure information, empty means last authentication succeeded string _err_msg; - //hls直播时的其他一些信息,主要用于播放器个数计数以及流量计数 + // hls直播时的其他一些信息,主要用于播放器个数计数以及流量计数 [AUTO-TRANSLATED:790de53a] + // Other information during hls live broadcast, mainly used for player count and traffic count HlsCookieData::Ptr _hls_data; }; @@ -182,11 +190,13 @@ static string searchIndexFile(const string &dir) { static bool makeFolderMenu(const string &httpPath, const string &strFullPath, string &strRet) { GET_CONFIG(bool, dirMenu, Http::kDirMenu); if (!dirMenu) { - //不允许浏览文件夹 + // 不允许浏览文件夹 [AUTO-TRANSLATED:a0c30a94] + // Not allowed to browse folders return false; } string strPathPrefix(strFullPath); - //url后缀有没有'/'访问文件夹,处理逻辑不一致 + // url后缀有没有'/'访问文件夹,处理逻辑不一致 [AUTO-TRANSLATED:39c6a933] + // Whether the url suffix has '/' to access the folder, the processing logic is inconsistent string last_dir_name; if (strPathPrefix.back() == '/') { strPathPrefix.pop_back(); @@ -231,7 +241,8 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st file_map.emplace(strCoding::UrlEncodePath(name), std::make_pair(name, path)); return true; }); - //如果是root目录,添加虚拟目录 + // 如果是root目录,添加虚拟目录 [AUTO-TRANSLATED:3149d7f9] + // If it is the root directory, add a virtual directory if (httpPath == "/") { GET_CONFIG_FUNC(StrCaseMap, virtualPathMap, Http::kVirtualPath, [](const string &str) { return Parser::parseArgs(str, ";", ","); @@ -246,7 +257,8 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st bool isDir = File::is_dir(strAbsolutePath); ss << "
  • " << i++ << "\t"; ss << ""; - //路径名称 + // 路径名称 [AUTO-TRANSLATED:4dae8790] + // Path name ss << pr.second.first; if (isDir) { ss << "/
  • \r\n"; continue; } - //是文件 + // 是文件 [AUTO-TRANSLATED:70473f2f] + // It's a file auto fileSize = File::fileSize(strAbsolutePath); if (fileSize < 1024) { ss << " (" << fileSize << "B)" << endl; @@ -282,16 +296,20 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st return true; } -//拦截hls的播放请求 +// 拦截hls的播放请求 [AUTO-TRANSLATED:dd1bbeec] +// Intercept the hls playback request static bool emitHlsPlayed(const Parser &parser, const MediaInfo &media_info, const HttpSession::HttpAccessPathInvoker &invoker,Session &sender){ - //访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 + // 访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 [AUTO-TRANSLATED:b7a67c84] + // The hls.m3u8 ending of the access, we convert it to the kBroadcastMediaPlayed event Broadcast::AuthInvoker auth_invoker = [invoker](const string &err) { - //cookie有效期为kHlsCookieSecond + // cookie有效期为kHlsCookieSecond [AUTO-TRANSLATED:a0026dcd] + // The cookie validity period is kHlsCookieSecond invoker(err, "", kHlsCookieSecond); }; bool flag = NOTICE_EMIT(BroadcastMediaPlayedArgs, Broadcast::kBroadcastMediaPlayed, media_info, auth_invoker, sender); if (!flag) { - //未开启鉴权,那么允许播放 + // 未开启鉴权,那么允许播放 [AUTO-TRANSLATED:077feed1] + // Authentication is not enabled, so playback is allowed auth_invoker(""); } return flag; @@ -335,19 +353,31 @@ public: * 3、cookie标记是否有权限访问文件,如果有权限,直接返回文件 * 4、cookie中记录的url参数是否跟本次url参数一致,如果一致直接返回客户端错误码 * 5、触发kBroadcastHttpAccess事件 + * The logical steps to determine whether the http client has permission to access the file + * 1. Find the cookie according to the http request header, find it and enter step 3 + * 2. Find the cookie according to the http url parameter, if the cookie is still not found, enter step 5 + * 3. Whether the cookie mark 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:dfc0f15f] */ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo &media_info, bool is_dir, const function &callback) { - //获取用户唯一id + // 获取用户唯一id [AUTO-TRANSLATED:5b1cf4bf] + // Get the user's unique id auto uid = parser.params(); auto path = parser.url(); - //先根据http头中的cookie字段获取cookie + // 先根据http头中的cookie字段获取cookie [AUTO-TRANSLATED:155cf682] + // First get the cookie according to the cookie field in the http header HttpServerCookie::Ptr cookie = HttpCookieManager::Instance().getCookie(kCookieName, parser.getHeader()); - //是否需要更新cookie + // 是否需要更新cookie [AUTO-TRANSLATED:b95121d5] + // Whether to update the cookie bool update_cookie = false; if (!cookie && !uid.empty()) { - //客户端请求中无cookie,再根据该用户的用户id获取cookie + // 客户端请求中无cookie,再根据该用户的用户id获取cookie [AUTO-TRANSLATED:42cb8ade] + // There is no cookie in the client request, then get the cookie according to the user id of the user cookie = HttpCookieManager::Instance().getCookieByUid(kCookieName, uid); update_cookie = true; } @@ -355,25 +385,31 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo if (cookie) { auto& attach = cookie->getAttach(); if (path.find(attach._path) == 0) { - //上次cookie是限定本目录 + // 上次cookie是限定本目录 [AUTO-TRANSLATED:a5c40abf] + // The last cookie is limited to this directory if (attach._err_msg.empty()) { - //上次鉴权成功 + // 上次鉴权成功 [AUTO-TRANSLATED:1a23f781] + // Last authentication succeeded if (attach._hls_data) { - //如果播放的是hls,那么刷新hls的cookie(获取ts文件也会刷新) + // 如果播放的是hls,那么刷新hls的cookie(获取ts文件也会刷新) [AUTO-TRANSLATED:02acac59] + // If the playback is hls, then refresh the hls cookie (getting the ts file will also refresh) cookie->updateTime(); update_cookie = true; } callback("", update_cookie ? cookie : nullptr); return; } - //上次鉴权失败,但是如果url参数发生变更,那么也重新鉴权下 + // 上次鉴权失败,但是如果url参数发生变更,那么也重新鉴权下 [AUTO-TRANSLATED:df9bd345] + // Last authentication failed, but if the url parameter changes, then re-authenticate if (parser.params().empty() || parser.params() == cookie->getUid()) { - //url参数未变,或者本来就没有url参数,那么判断本次请求为重复请求,无访问权限 + // url参数未变,或者本来就没有url参数,那么判断本次请求为重复请求,无访问权限 [AUTO-TRANSLATED:f46b4fca] + // The url parameter has not changed, or there is no url parameter at all, then determine that the current request is a duplicate request and has no access permission callback(attach._err_msg, update_cookie ? cookie : nullptr); return; } } - //如果url参数变了或者不是限定本目录,那么旧cookie失效,重新鉴权 + // 如果url参数变了或者不是限定本目录,那么旧cookie失效,重新鉴权 [AUTO-TRANSLATED:acf6d49e] + // If the url parameter changes or is not limited to this directory, then the old cookie expires and re-authentication is required HttpCookieManager::Instance().delCookie(cookie); } @@ -386,25 +422,31 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo info->_local_ip = sender.get_local_ip(); info->_local_port = sender.get_local_port(); - //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 + // 该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 [AUTO-TRANSLATED:8f4b3dd2] + // This user has never obtained a cookie, at this time we broadcast whether to allow this user to access this http directory HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, media_info, info] (const string &err_msg, const string &cookie_path_in, int life_second) { HttpServerCookie::Ptr cookie; if (life_second) { - //本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 + // 本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 [AUTO-TRANSLATED:5a12f48e] + // This authentication has an expiration date, we cache the authentication result in the cookie string cookie_path = cookie_path_in; if (cookie_path.empty()) { - //如果未设置鉴权目录,那么我们采用当前目录 + // 如果未设置鉴权目录,那么我们采用当前目录 [AUTO-TRANSLATED:701ada2d] + // If no authentication directory is set, we use the current directory cookie_path = is_dir ? path : path.substr(0, path.rfind("/") + 1); } auto attach = std::make_shared(); - //记录用户能访问的路径 + // 记录用户能访问的路径 [AUTO-TRANSLATED:80a2ba33] + // Record the paths that the user can access attach->_path = cookie_path; - //记录能否访问 + // 记录能否访问 [AUTO-TRANSLATED:972f6fc5] + // Record whether access is allowed attach->_err_msg = err_msg; if (is_hls) { - // hls相关信息 + // hls相关信息 [AUTO-TRANSLATED:37893a71] + // hls related information attach->_hls_data = std::make_shared(media_info, info); } toolkit::Any any; @@ -416,21 +458,27 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo }; if (is_hls) { - //是hls的播放鉴权,拦截之 + // 是hls的播放鉴权,拦截之 [AUTO-TRANSLATED:c5ba86bb] + // This is hls playback authentication, intercept it emitHlsPlayed(parser, media_info, accessPathInvoker, sender); return; } - // 事件未被拦截,则认为是http下载请求 + // 事件未被拦截,则认为是http下载请求 [AUTO-TRANSLATED:7d449ccc] + // The event was not intercepted, it is considered an http download request bool flag = NOTICE_EMIT(BroadcastHttpAccessArgs, Broadcast::kBroadcastHttpAccess, parser, path, is_dir, accessPathInvoker, sender); if (!flag) { - // 此事件无人监听,我们默认都有权限访问 + // 此事件无人监听,我们默认都有权限访问 [AUTO-TRANSLATED:e1524c0f] + // No one is listening to this event, we assume that everyone has permission to access it by default callback("", nullptr); } } /** * 发送404 Not Found + * Send 404 Not Found + + * [AUTO-TRANSLATED:1297f2e7] */ static void sendNotFound(const HttpFileManager::invoker &cb) { GET_CONFIG(string, notFound, Http::kNotFound); @@ -439,6 +487,9 @@ static void sendNotFound(const HttpFileManager::invoker &cb) { /** * 拼接文件路径 + * Concatenate the file path + + * [AUTO-TRANSLATED:cf6f5c53] */ static string pathCat(const string &a, const string &b){ if (a.back() == '/') { @@ -454,16 +505,26 @@ static string pathCat(const string &a, const string &b){ * @param media_info http url信息 * @param file_path 文件绝对路径 * @param cb 回调对象 + * Access the file + * @param sender Event trigger + * @param parser http request + * @param media_info http url information + * @param file_path Absolute file path + * @param cb Callback object + + * [AUTO-TRANSLATED:2d840fe6] */ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &media_info, const string &file_path, const HttpFileManager::invoker &cb) { bool is_hls = end_with(file_path, kHlsSuffix) || end_with(file_path, kHlsFMP4Suffix); if (!is_hls && !File::fileExist(file_path)) { - //文件不存在且不是hls,那么直接返回404 + // 文件不存在且不是hls,那么直接返回404 [AUTO-TRANSLATED:7aae578b] + // The file does not exist and is not hls, so directly return 404 sendNotFound(cb); return; } if (is_hls) { - // hls,那么移除掉后缀获取真实的stream_id并且修改协议为HLS + // hls,那么移除掉后缀获取真实的stream_id并且修改协议为HLS [AUTO-TRANSLATED:94b5818a] + // hls, then remove the suffix to get the real stream_id and change the protocol to HLS if (end_with(file_path, kHlsSuffix)) { const_cast(media_info.schema) = HLS_SCHEMA; replace(const_cast(media_info.stream), kHlsSuffix, ""); @@ -474,15 +535,18 @@ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &m } weak_ptr weakSession = static_pointer_cast(sender.shared_from_this()); - //判断是否有权限访问该文件 + // 判断是否有权限访问该文件 [AUTO-TRANSLATED:b7f595f5] + // Determine whether you have permission to access this file canAccessPath(sender, parser, media_info, false, [cb, file_path, parser, is_hls, media_info, weakSession](const string &err_msg, const HttpServerCookie::Ptr &cookie) { auto strongSession = weakSession.lock(); if (!strongSession) { - // http客户端已经断开,不需要回复 + // http客户端已经断开,不需要回复 [AUTO-TRANSLATED:9a252e21] + // The http client has disconnected and does not need to reply return; } if (!err_msg.empty()) { - //文件鉴权失败 + // 文件鉴权失败 [AUTO-TRANSLATED:0feb8885] + // File authentication failed StrCaseMap headerOut; if (cookie) { headerOut["Set-Cookie"] = cookie->getCookie(cookie->getAttach()._path); @@ -519,7 +583,8 @@ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &m }; if (!is_hls || !cookie) { - //不是hls或访问m3u8文件不带cookie, 直接回复文件或404 + // 不是hls或访问m3u8文件不带cookie, 直接回复文件或404 [AUTO-TRANSLATED:64e5d19b] + // Not hls or accessing m3u8 files without cookies, directly reply to the file or 404 response_file(cookie, cb, file_path, parser); if (is_hls) { WarnL << "access m3u8 file without cookie:" << file_path; @@ -530,36 +595,44 @@ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &m auto &attach = cookie->getAttach(); auto src = attach._hls_data->getMediaSource(); if (src) { - // 直接从内存获取m3u8索引文件(而不是从文件系统) + // 直接从内存获取m3u8索引文件(而不是从文件系统) [AUTO-TRANSLATED:c772e342] + // Get the m3u8 index file directly from memory (instead of from the file system) response_file(cookie, cb, file_path, parser, src->getIndexFile()); return; } if (attach._find_src && attach._find_src_ticker.elapsedTime() < kFindSrcIntervalSecond * 1000) { - // 最近已经查找过MediaSource了,为了防止频繁查找导致占用全局互斥锁的问题,我们尝试直接从磁盘返回hls索引文件 + // 最近已经查找过MediaSource了,为了防止频繁查找导致占用全局互斥锁的问题,我们尝试直接从磁盘返回hls索引文件 [AUTO-TRANSLATED:a33d5e4d] + // MediaSource has been searched recently, in order to prevent frequent searches from occupying the global mutex, we try to return the hls index file directly from the disk response_file(cookie, cb, file_path, parser); return; } - //hls流可能未注册,MediaSource::findAsync可以触发not_found事件,然后再按需推拉流 + // hls流可能未注册,MediaSource::findAsync可以触发not_found事件,然后再按需推拉流 [AUTO-TRANSLATED:f4acd717] + // The hls stream may not be registered, MediaSource::findAsync can trigger the not_found event, and then push and pull the stream on demand MediaSource::findAsync(media_info, strongSession, [response_file, cookie, cb, file_path, parser](const MediaSource::Ptr &src) { auto hls = dynamic_pointer_cast(src); if (!hls) { - //流不在线 + // 流不在线 [AUTO-TRANSLATED:5a6a5695] + // The stream is not online response_file(cookie, cb, file_path, parser); return; } auto &attach = cookie->getAttach(); attach._hls_data->setMediaSource(hls); - // 添加HlsMediaSource的观看人数(HLS是按需生成的,这样可以触发HLS文件的生成) + // 添加HlsMediaSource的观看人数(HLS是按需生成的,这样可以触发HLS文件的生成) [AUTO-TRANSLATED:bd98e100] + // Add the number of viewers of HlsMediaSource (HLS is generated on demand, so this can trigger the generation of HLS files) attach._hls_data->addByteUsage(0); - // 标记找到MediaSource + // 标记找到MediaSource [AUTO-TRANSLATED:1e298005] + // Mark that MediaSource has been found attach._find_src = true; - // 重置查找MediaSource计时 + // 重置查找MediaSource计时 [AUTO-TRANSLATED:d1e47e07] + // Reset the MediaSource search timer attach._find_src_ticker.resetTime(); - // m3u8文件可能不存在, 等待m3u8索引文件按需生成 + // m3u8文件可能不存在, 等待m3u8索引文件按需生成 [AUTO-TRANSLATED:0dbd4df2] + // The m3u8 file may not exist, wait for the m3u8 index file to be generated on demand hls->getIndexFile([response_file, file_path, cookie, cb, parser](const string &file) { response_file(cookie, cb, file_path, parser, file); }); @@ -577,28 +650,33 @@ static string getFilePath(const Parser &parser,const MediaInfo &media_info, Sess string url, path, virtual_app; auto it = virtualPathMap.find(media_info.app); if (it != virtualPathMap.end()) { - //访问的是virtualPath + // 访问的是virtualPath [AUTO-TRANSLATED:a36c7b20] + // Accessing virtualPath path = it->second; url = parser.url().substr(1 + media_info.app.size()); virtual_app = media_info.app + "/"; } else { - //访问的是rootPath + // 访问的是rootPath [AUTO-TRANSLATED:600765f0] + // Accessing rootPath path = rootPath; url = parser.url(); } for (auto &ch : url) { if (ch == '\\') { - //如果url中存在"\",这种目录是Windows样式的;需要批量转换为标准的"/"; 防止访问目录权限外的文件 + // 如果url中存在"\",这种目录是Windows样式的;需要批量转换为标准的"/"; 防止访问目录权限外的文件 [AUTO-TRANSLATED:fd6b5900] + // If the url contains "\", this directory is in Windows style; it needs to be converted to standard "/" in batches; prevent access to files outside the directory permissions ch = '/'; } } auto ret = File::absolutePath(enableVhost ? media_info.vhost + url : url, path); auto http_root = File::absolutePath(enableVhost ? media_info.vhost + "/" : "/", path); if (!start_with(ret, http_root)) { - // 访问的http文件不得在http根目录之外 + // 访问的http文件不得在http根目录之外 [AUTO-TRANSLATED:7d85a8f9] + // The accessed http file must not be outside the http root directory throw std::runtime_error("Attempting to access files outside of the http root directory"); } - // 替换url,防止返回的目录索引网页被注入非法内容 + // 替换url,防止返回的目录索引网页被注入非法内容 [AUTO-TRANSLATED:463ad1b1] + // Replace the url to prevent the returned directory index page from being injected with illegal content const_cast(parser).setUrl("/" + virtual_app + ret.substr(http_root.size())); NOTICE_EMIT(BroadcastHttpBeforeAccessArgs, Broadcast::kBroadcastHttpBeforeAccess, parser, ret, sender); return ret; @@ -609,6 +687,12 @@ static string getFilePath(const Parser &parser,const MediaInfo &media_info, Sess * @param sender 事件触发者 * @param parser http请求 * @param cb 回调对象 + * Access file or folder + * @param sender Event trigger + * @param parser http request + * @param cb Callback object + + * [AUTO-TRANSLATED:a79c824d] */ void HttpFileManager::onAccessPath(Session &sender, Parser &parser, const HttpFileManager::invoker &cb) { auto fullUrl = "http://" + parser["Host"] + parser.fullUrl(); @@ -618,27 +702,33 @@ void HttpFileManager::onAccessPath(Session &sender, Parser &parser, const HttpFi sendNotFound(cb); return; } - //访问的是文件夹 + // 访问的是文件夹 [AUTO-TRANSLATED:279974bb] + // Accessing a folder if (File::is_dir(file_path)) { auto indexFile = searchIndexFile(file_path); if (!indexFile.empty()) { - // 发现该文件夹下有index文件 + // 发现该文件夹下有index文件 [AUTO-TRANSLATED:4a697758] + // Found index file in this folder file_path = pathCat(file_path, indexFile); if (!File::is_dir(file_path)) { - // 不是文件夹 + // 不是文件夹 [AUTO-TRANSLATED:af893469] + // Not a folder parser.setUrl(pathCat(parser.url(), indexFile)); accessFile(sender, parser, media_info, file_path, cb); return; } } string strMenu; - //生成文件夹菜单索引 + // 生成文件夹菜单索引 [AUTO-TRANSLATED:04150cc8] + // Generate folder menu index if (!makeFolderMenu(parser.url(), file_path, strMenu)) { - //文件夹不存在 + // 文件夹不存在 [AUTO-TRANSLATED:a2dc6c89] + // Folder does not exist sendNotFound(cb); return; } - //判断是否有权限访问该目录 + // 判断是否有权限访问该目录 [AUTO-TRANSLATED:963d02a6] + // Determine if there is permission to access this directory canAccessPath(sender, parser, media_info, true, [strMenu, cb](const string &err_msg, const HttpServerCookie::Ptr &cookie) mutable{ if (!err_msg.empty()) { strMenu = err_msg; @@ -652,7 +742,8 @@ void HttpFileManager::onAccessPath(Session &sender, Parser &parser, const HttpFi return; } - //访问的是文件 + // 访问的是文件 [AUTO-TRANSLATED:7a400b3c] + // Accessing a file accessFile(sender, parser, media_info, file_path, cb); }; @@ -697,17 +788,20 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, bool use_mmap, bool is_path) const { if (!is_path) { - //file是文件内容 + // file是文件内容 [AUTO-TRANSLATED:61d0be82] + // file is the file content (*this)(200, responseHeader, std::make_shared(file)); return; } - //file是文件路径 + // file是文件路径 [AUTO-TRANSLATED:28dcac38] + // file is the file path GET_CONFIG(string, charSet, Http::kCharSet); StrCaseMap &httpHeader = const_cast(responseHeader); auto fileBody = std::make_shared(file, use_mmap); if (fileBody->remainSize() < 0) { - //打开文件失败 + // 打开文件失败 [AUTO-TRANSLATED:1f0405cb] + // Failed to open file GET_CONFIG(string, notFound, Http::kNotFound); auto strContentType = StrPrinter << "text/html; charset=" << charSet << endl; @@ -716,13 +810,15 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, return; } - // 尝试添加Content-Type + // 尝试添加Content-Type [AUTO-TRANSLATED:2c08b371] + // Try to add Content-Type httpHeader.emplace("Content-Type", HttpConst::getHttpContentType(file.data()) + "; charset=" + charSet); auto &strRange = const_cast(requestHeader)["Range"]; int code = 200; if (!strRange.empty()) { - //分节下载 + // 分节下载 [AUTO-TRANSLATED:01920230] + // Segmented download code = 206; auto iRangeStart = atoll(findSubString(strRange.data(), "bytes=", "-").data()); auto iRangeEnd = atoll(findSubString(strRange.data(), "-", nullptr).data()); @@ -730,13 +826,16 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, if (iRangeEnd == 0) { iRangeEnd = fileSize - 1; } - //设置文件范围 + // 设置文件范围 [AUTO-TRANSLATED:aa51fd28] + // Set file range fileBody->setRange(iRangeStart, iRangeEnd - iRangeStart + 1); - //分节下载返回Content-Range头 + // 分节下载返回Content-Range头 [AUTO-TRANSLATED:4b78e7b6] + // Segmented download returns Content-Range header httpHeader.emplace("Content-Range", StrPrinter << "bytes " << iRangeStart << "-" << iRangeEnd << "/" << fileSize << endl); } - //回复文件 + // 回复文件 [AUTO-TRANSLATED:5d91a916] + // Reply file (*this)(code, httpHeader, fileBody); } diff --git a/src/Http/HttpFileManager.h b/src/Http/HttpFileManager.h index 737da943..4c636dda 100644 --- a/src/Http/HttpFileManager.h +++ b/src/Http/HttpFileManager.h @@ -41,6 +41,9 @@ private: /** * 该对象用于控制http静态文件夹服务器的访问权限 + * This object is used to control access permissions for the http static folder server. + + * [AUTO-TRANSLATED:2eb7e5f2] */ class HttpFileManager { public: @@ -51,6 +54,12 @@ public: * @param sender 事件触发者 * @param parser http请求 * @param cb 回调对象 + * Access files or folders + * @param sender Event trigger + * @param parser http request + * @param cb Callback object + + * [AUTO-TRANSLATED:669301a8] */ static void onAccessPath(toolkit::Session &sender, Parser &parser, const invoker &cb); @@ -58,12 +67,22 @@ public: * 获取mime值 * @param name 文件后缀 * @return mime值 + * Get mime value + * @param name File suffix + * @return mime value + + * [AUTO-TRANSLATED:f1d25b59] */ static const std::string &getContentType(const char *name); /** * 该ip是否再白名单中 * @param ip 支持ipv4和ipv6 + * Whether this ip is in the whitelist + * @param ip Supports ipv4 and ipv6 + + + * [AUTO-TRANSLATED:4c7756c3] */ static bool isIPAllowed(const std::string &ip); diff --git a/src/Http/HttpRequestSplitter.cpp b/src/Http/HttpRequestSplitter.cpp index 9cc4cfcb..65a6c650 100644 --- a/src/Http/HttpRequestSplitter.cpp +++ b/src/Http/HttpRequestSplitter.cpp @@ -14,7 +14,8 @@ using namespace toolkit; using namespace std; -//协议解析最大缓存4兆数据 +// 协议解析最大缓存4兆数据 [AUTO-TRANSLATED:75159526] +// Protocol parsing maximum cache 4MB data static constexpr size_t kMaxCacheSize = 4 * 1024 * 1024; namespace mediakit { @@ -23,7 +24,8 @@ void HttpRequestSplitter::input(const char *data,size_t len) { { auto size = remainDataSize(); if (size > _max_cache_size) { - //缓存太多数据无法处理则上抛异常 + // 缓存太多数据无法处理则上抛异常 [AUTO-TRANSLATED:30e48e9e] + // If too much data is cached and cannot be processed, throw an exception reset(); throw std::out_of_range("remain data size is too huge, now cleared:" + to_string(size)); } @@ -41,13 +43,20 @@ void HttpRequestSplitter::input(const char *data,size_t len) { *由于ZLToolKit确保内存最后一个字节是保留未使用字节并置0, *所以此处可以不用再次置0 *但是上层数据可能来自其他渠道,保险起见还是置0 + *Ensure the last byte of ptr is 0 to prevent strstr from going out of bounds + * Since ZLToolKit ensures that the last byte of memory is a reserved unused byte and set to 0, + * so there is no need to set it to 0 again here + * But the upper layer data may come from other channels, so it is better to set it to 0 for safety + + * [AUTO-TRANSLATED:28ff47a5] */ char &tail_ref = ((char *) ptr)[len]; char tail_tmp = tail_ref; tail_ref = 0; - //数据按照请求头处理 + // 数据按照请求头处理 [AUTO-TRANSLATED:e7a0dbb4] + // Data is processed according to the request header const char *index = nullptr; _remain_data_size = len; while (_content_len == 0 && _remain_data_size > 0 && (index = onSearchPacketTail(ptr,_remain_data_size)) != nullptr) { @@ -57,7 +66,8 @@ void HttpRequestSplitter::input(const char *data,size_t len) { if (index < ptr || index > ptr + _remain_data_size) { throw std::out_of_range("上层分包逻辑异常"); } - //_content_len == 0,这是请求头 + // _content_len == 0,这是请求头 [AUTO-TRANSLATED:32af637b] + // _content_len == 0, this is the request header const char *header_ptr = ptr; ssize_t header_size = index - ptr; ptr = index; @@ -68,39 +78,52 @@ void HttpRequestSplitter::input(const char *data,size_t len) { /* * 恢复末尾字节 * 移动到这来,目的是防止HttpRequestSplitter::reset()导致内存失效 + /* + * Restore the last byte + * Move it here to prevent HttpRequestSplitter::reset() from causing memory failure + + * [AUTO-TRANSLATED:9c3e0597] */ tail_ref = tail_tmp; if(_remain_data_size <= 0){ - //没有剩余数据,清空缓存 + // 没有剩余数据,清空缓存 [AUTO-TRANSLATED:16613daa] + // No remaining data, clear the cache _remain_data.clear(); return; } if(_content_len == 0){ - //尚未找到http头,缓存定位到剩余数据部分 + // 尚未找到http头,缓存定位到剩余数据部分 [AUTO-TRANSLATED:7a9d6205] + // HTTP header not found yet, cache is located at the remaining data part _remain_data.assign(ptr,_remain_data_size); return; } - //已经找到http头了 + // 已经找到http头了 [AUTO-TRANSLATED:df166db7] + // HTTP header has been found if(_content_len > 0){ - //数据按照固定长度content处理 + // 数据按照固定长度content处理 [AUTO-TRANSLATED:7272b7e7] + // Data is processed according to fixed length content if(_remain_data_size < (size_t)_content_len){ - //数据不够,缓存定位到剩余数据部分 + // 数据不够,缓存定位到剩余数据部分 [AUTO-TRANSLATED:61c32f5c] + // Insufficient data, cache is located at the remaining data part _remain_data.assign(ptr, _remain_data_size); return; } - //收到content数据,并且接收content完毕 + // 收到content数据,并且接收content完毕 [AUTO-TRANSLATED:0342dc0e] + // Content data received and content reception completed onRecvContent(ptr,_content_len); _remain_data_size -= _content_len; ptr += _content_len; - //content处理完毕,后面数据当做请求头处理 + // content处理完毕,后面数据当做请求头处理 [AUTO-TRANSLATED:d268dfe4] + // Content processing completed, subsequent data is treated as request header _content_len = 0; if(_remain_data_size > 0){ - //还有数据没有处理完毕 + // 还有数据没有处理完毕 [AUTO-TRANSLATED:1cac6727] + // There is still data that has not been processed _remain_data.assign(ptr,_remain_data_size); data = ptr = (char *)_remain_data.data(); len = _remain_data.size(); @@ -111,7 +134,8 @@ void HttpRequestSplitter::input(const char *data,size_t len) { } - //_content_len < 0;数据按照不固定长度content处理 + // _content_len < 0;数据按照不固定长度content处理 [AUTO-TRANSLATED:68d6a4d0] + // _content_len < 0; Data is processed according to variable length content onRecvContent(ptr,_remain_data_size);//消费掉所有剩余数据 _remain_data.clear(); } diff --git a/src/Http/HttpRequestSplitter.h b/src/Http/HttpRequestSplitter.h index c4345c9f..0f9735bd 100644 --- a/src/Http/HttpRequestSplitter.h +++ b/src/Http/HttpRequestSplitter.h @@ -26,26 +26,44 @@ public: * @param data 需要添加的数据 * @param len 数据长度 * @warning 实际内存需保证不小于 len + 1, 内部使用 strstr 进行查找, 为防止查找越界, 会在 @p len + 1 的位置设置 '\0' 结束符. + * Add data + * @param data Data to be added + * @param len Data length + * @warning Actual memory must be no less than len + 1. strstr is used internally for searching. To prevent out-of-bounds search, a '\0' terminator is set at the @p len + 1 position. + + * [AUTO-TRANSLATED:3bbfc2ab] */ virtual void input(const char *data, size_t len); /** * 恢复初始设置 + * Restore initial settings + + * [AUTO-TRANSLATED:f797ec5a] */ void reset(); /** * 剩余数据大小 + * Remaining data size + + * [AUTO-TRANSLATED:808a9399] */ size_t remainDataSize(); /** * 获取剩余数据指针 + * Get remaining data pointer + + * [AUTO-TRANSLATED:e419f28a] */ const char *remainData() const; /** * 设置最大缓存大小 + * Set maximum cache size + + * [AUTO-TRANSLATED:19333c32] */ void setMaxCacheSize(size_t max_cache_size); @@ -59,6 +77,16 @@ protected: * <0 : 代表后面所有数据都是content,此时后面的content将分段通过onRecvContent函数回调出去 * 0 : 代表为后面数据还是请求头, * >0 : 代表后面数据为固定长度content,此时将缓存content并等到所有content接收完毕一次性通过onRecvContent函数回调出去 + * Receive request header + * @param data Request header data + * @param len Request header length + * + * @return Content length after request header, + * <0 : Represents that all subsequent data is content, in which case the subsequent content will be called back in segments through the onRecvContent function + * 0 : Represents that the subsequent data is still the request header, + * >0 : Represents that the subsequent data is fixed-length content, in which case the content will be cached and called back through the onRecvContent function once all content is received + + * [AUTO-TRANSLATED:f185e6c5] */ virtual ssize_t onRecvHeader(const char *data,size_t len) = 0; @@ -67,6 +95,12 @@ protected: * onRecvHeader函数返回>0,则为全部数据 * @param data content分片或全部数据 * @param len 数据长度 + * Receive content fragments or all data + * onRecvHeader function returns >0, then it is all data + * @param data Content fragments or all data + * @param len Data length + + * [AUTO-TRANSLATED:2ef280fb] */ virtual void onRecvContent(const char *data,size_t len) {}; @@ -75,11 +109,21 @@ protected: * @param data 数据指针 * @param len 数据长度 * @return nullptr代表未找到包位,否则返回包尾指针 + * Determine if there is a packet tail in the data + * @param data Data pointer + * @param len Data length + * @return nullptr represents that the packet position is not found, otherwise returns the packet tail pointer + + * [AUTO-TRANSLATED:f7190dec] */ virtual const char *onSearchPacketTail(const char *data, size_t len); /** * 设置content len + * Set content len + + + * [AUTO-TRANSLATED:6dce48f8] */ void setContentLen(ssize_t content_len); diff --git a/src/Http/HttpRequester.cpp b/src/Http/HttpRequester.cpp index 82070b2e..7a564ac7 100644 --- a/src/Http/HttpRequester.cpp +++ b/src/Http/HttpRequester.cpp @@ -251,7 +251,8 @@ static std::string httpBody() { args["rand_str"] = makeRandStr(32); for (auto &pr : mINI::Instance()) { - // 只获取转协议相关配置 + // 只获取转协议相关配置 [AUTO-TRANSLATED:4e1e5840] + // Only get the configuration related to protocol conversion if (pr.first.find("protocol.") == 0) { args[pr.first] = pr.second; } @@ -261,21 +262,25 @@ static std::string httpBody() { static void sendReport() { static HttpRequester::Ptr requester = std::make_shared(); - // 获取一次静态信息,定时上报主要方便统计在线实例个数 + // 获取一次静态信息,定时上报主要方便统计在线实例个数 [AUTO-TRANSLATED:1612d609] + // Get static information once, and report it regularly to facilitate statistics of the number of online instances static auto body = httpBody(); requester->setMethod("POST"); requester->setBody(body); - // http超时时间设置为30秒 + // http超时时间设置为30秒 [AUTO-TRANSLATED:466f9b71] + // Set the http timeout to 30 seconds requester->startRequester(s_report_url, nullptr, 30); } static toolkit::onceToken s_token([]() { NoticeCenter::Instance().addListener(nullptr, "kBroadcastEventPollerPoolStarted", [](EventPollerPoolOnStartedArgs) { - // 第一次汇报在程序启动后5分钟 + // 第一次汇报在程序启动后5分钟 [AUTO-TRANSLATED:02e6c508] + // The first report is 5 minutes after the program starts pool.getPoller()->doDelayTask(5 * 60 * 1000, []() { sendReport(); - // 后续每一个小时汇报一次 + // 后续每一个小时汇报一次 [AUTO-TRANSLATED:0322b8aa] + // Report every hour afterwards return 60 * 60 * 1000; }); }); diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index ba25f3f4..0b89d182 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -24,15 +24,19 @@ using namespace toolkit; namespace mediakit { HttpSession::HttpSession(const Socket::Ptr &pSock) : Session(pSock) { - //设置默认参数 + // 设置默认参数 [AUTO-TRANSLATED:ae5b72e6] + // Set default parameters setMaxReqSize(0); setTimeoutSec(0); } void HttpSession::onHttpRequest_HEAD() { - // 暂时全部返回200 OK,因为HTTP GET存在按需生成流的操作,所以不能按照HTTP GET的流程返回 - // 如果直接返回404,那么又会导致按需生成流的逻辑失效,所以HTTP HEAD在静态文件或者已存在资源时才有效 - // 对于按需生成流的直播场景并不适用 + // 暂时全部返回200 OK,因为HTTP GET存在按需生成流的操作,所以不能按照HTTP GET的流程返回 [AUTO-TRANSLATED:0ce05db5] + // Temporarily return 200 OK for all, because HTTP GET has on-demand generation stream operations, so it cannot return according to the HTTP GET process + // 如果直接返回404,那么又会导致按需生成流的逻辑失效,所以HTTP HEAD在静态文件或者已存在资源时才有效 [AUTO-TRANSLATED:ea2b6faa] + // If you return 404 directly, it will also cause the on-demand generation stream logic to fail, so HTTP HEAD is only valid for static files or existing resources + // 对于按需生成流的直播场景并不适用 [AUTO-TRANSLATED:5a47bf00] + // Not applicable to live streaming scenarios that generate streams on demand sendResponse(200, false); } @@ -57,7 +61,8 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) { static onceToken token([]() { s_func_map.emplace("GET", &HttpSession::onHttpRequest_GET); s_func_map.emplace("POST", &HttpSession::onHttpRequest_POST); - // DELETE命令用于whip/whep用,只用于触发http api + // DELETE命令用于whip/whep用,只用于触发http api [AUTO-TRANSLATED:f3b7aaea] + // DELETE command is used for whip/whep, only used to trigger http api s_func_map.emplace("DELETE", &HttpSession::onHttpRequest_POST); s_func_map.emplace("HEAD", &HttpSession::onHttpRequest_HEAD); s_func_map.emplace("OPTIONS", &HttpSession::onHttpRequest_OPTIONS); @@ -80,27 +85,32 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) { auto &content_len_str = _parser["Content-Length"]; if (content_len_str.empty()) { if (it->first == "POST") { - // Http post未指定长度,我们认为是不定长的body + // Http post未指定长度,我们认为是不定长的body [AUTO-TRANSLATED:3578206b] + // Http post does not specify length, we consider it to be an indefinite length body WarnL << "Received http post request without content-length, consider it to be unlimited length"; content_len = SIZE_MAX; } else { content_len = 0; } } else { - // 已经指定长度 + // 已经指定长度 [AUTO-TRANSLATED:a360c374] + // Length has been specified content_len = atoll(content_len_str.data()); } if (content_len == 0) { - //// 没有body的情况,直接触发回调 //// + // // 没有body的情况,直接触发回调 //// [AUTO-TRANSLATED:f2988336] + // // No body case, trigger callback directly //// (this->*(it->second))(); _parser.clear(); - // 如果设置了_on_recv_body, 那么说明后续要处理body + // 如果设置了_on_recv_body, 那么说明后续要处理body [AUTO-TRANSLATED:2dac5fc2] + // If _on_recv_body is set, it means that the body will be processed later return _on_recv_body ? -1 : 0; } if (content_len > _max_req_size) { - //// 不定长body或超大body //// + // // 不定长body或超大body //// [AUTO-TRANSLATED:8d66ee77] + // // Indefinite length body or oversized body //// if (content_len != SIZE_MAX) { WarnL << "Http body size is too huge: " << content_len << " > " << _max_req_size << ", please set " << Http::kMaxReqSize << " in config.ini file."; @@ -112,30 +122,37 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) { received += len; onRecvUnlimitedContent(parser, data, len, content_len, received); if (received < content_len) { - // 还没收满 + // 还没收满 [AUTO-TRANSLATED:cecc867e] + // Not yet received return true; } - // 收满了 + // 收满了 [AUTO-TRANSLATED:0c9cebd7] + // Received full setContentLen(0); return false; }; - // 声明后续都是body;Http body在本对象缓冲,不通过HttpRequestSplitter保存 + // 声明后续都是body;Http body在本对象缓冲,不通过HttpRequestSplitter保存 [AUTO-TRANSLATED:0012b6c1] + // Declare that the following is all body; Http body is buffered in this object, not saved through HttpRequestSplitter return -1; } - //// body size明确指定且小于最大值的情况 //// + // // body size明确指定且小于最大值的情况 //// [AUTO-TRANSLATED:f1f1ee5d] + // // Body size is explicitly specified and less than the maximum value //// _on_recv_body = [this, it](const char *data, size_t len) mutable { - // 收集body完毕 + // 收集body完毕 [AUTO-TRANSLATED:981ad2c8] + // Body collection complete _parser.setContent(std::string(data, len)); (this->*(it->second))(); _parser.clear(); - // _on_recv_body置空 + // _on_recv_body置空 [AUTO-TRANSLATED:437a201a] + // _on_recv_body is cleared return false; }; - // 声明body长度,通过HttpRequestSplitter缓存然后一次性回调到_on_recv_body + // 声明body长度,通过HttpRequestSplitter缓存然后一次性回调到_on_recv_body [AUTO-TRANSLATED:3b11cfb7] + // Declare the body length, cache it through HttpRequestSplitter and then callback to _on_recv_body at once return content_len; } @@ -152,7 +169,8 @@ void HttpSession::onRecv(const Buffer::Ptr &pBuf) { void HttpSession::onError(const SockException &err) { if (_is_live_stream) { - // flv/ts播放器 + // flv/ts播放器 [AUTO-TRANSLATED:5b444fd9] + // flv/ts player uint64_t duration = _ticker.createdTime() / 1000; WarnP(this) << "FLV/TS/FMP4播放器(" << _media_info.shortUrl() << ")断开:" << err << ",耗时(s):" << duration; @@ -184,7 +202,8 @@ void HttpSession::setMaxReqSize(size_t max_req_size) { void HttpSession::onManager() { if (_ticker.elapsedTime() > _keep_alive_sec * 1000) { - //http超时 + // http超时 [AUTO-TRANSLATED:6f2fdd1f] + // http timeout shutdown(SockException(Err_timeout, "session timeout")); } } @@ -215,25 +234,32 @@ bool HttpSession::checkWebSocket() { sendResponse(101, false, nullptr, headerOut, nullptr, true); }; - // 判断是否为websocket-flv + // 判断是否为websocket-flv [AUTO-TRANSLATED:31682d7a] + // Determine whether it is websocket-flv if (checkLiveStreamFlv(res_cb_flv)) { - // 这里是websocket-flv直播请求 + // 这里是websocket-flv直播请求 [AUTO-TRANSLATED:4bea5956] + // This is a websocket-flv live request return true; } - // 判断是否为websocket-ts + // 判断是否为websocket-ts [AUTO-TRANSLATED:9e8eb374] + // Determine whether it is websocket-ts if (checkLiveStreamTS(res_cb)) { - // 这里是websocket-ts直播请求 + // 这里是websocket-ts直播请求 [AUTO-TRANSLATED:8ab08dd6] + // This is a websocket-ts live request return true; } - // 判断是否为websocket-fmp4 + // 判断是否为websocket-fmp4 [AUTO-TRANSLATED:318f793f] + // Determine whether it is websocket-fmp4 if (checkLiveStreamFMP4(res_cb)) { - // 这里是websocket-fmp4直播请求 + // 这里是websocket-fmp4直播请求 [AUTO-TRANSLATED:ccf0c1e2] + // This is a websocket-fmp4 live request return true; } - // 这是普通的websocket连接 + // 这是普通的websocket连接 [AUTO-TRANSLATED:754721f8] + // This is a normal websocket connection if (!onWebSocketConnect(_parser)) { sendResponse(501, true, nullptr, headerOut); return true; @@ -253,57 +279,69 @@ bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix } else { auto prefix_size = url_suffix.size(); if (url.size() < prefix_size || strcasecmp(url.data() + (url.size() - prefix_size), url_suffix.data())) { - // 未找到后缀 + // 未找到后缀 [AUTO-TRANSLATED:6635499a] + // Suffix not found return false; } - // url去除特殊后缀 + // url去除特殊后缀 [AUTO-TRANSLATED:31c0c080] + // Remove special suffix from url url.resize(url.size() - prefix_size); } - // 带参数的url + // 带参数的url [AUTO-TRANSLATED:074764b0] + // Url with parameters if (!_parser.params().empty()) { url += "?"; url += _parser.params(); } - // 解析带上协议+参数完整的url + // 解析带上协议+参数完整的url [AUTO-TRANSLATED:5cdc7e68] + // Parse the complete url with protocol + parameters _media_info.parse(schema + "://" + _parser["Host"] + url); if (_media_info.app.empty() || _media_info.stream.empty()) { - // url不合法 + // url不合法 [AUTO-TRANSLATED:9aad134e] + // URL is invalid return false; } bool close_flag = !strcasecmp(_parser["Connection"].data(), "close"); weak_ptr weak_self = static_pointer_cast(shared_from_this()); - // 鉴权结果回调 + // 鉴权结果回调 [AUTO-TRANSLATED:021df191] + // Authentication result callback auto onRes = [cb, weak_self, close_flag](const string &err) { auto strong_self = weak_self.lock(); if (!strong_self) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } if (!err.empty()) { - // 播放鉴权失败 + // 播放鉴权失败 [AUTO-TRANSLATED:64f99eeb] + // Playback authentication failed strong_self->sendResponse(401, close_flag, nullptr, KeyValue(), std::make_shared(err)); return; } - // 异步查找直播流 + // 异步查找直播流 [AUTO-TRANSLATED:7cde5dac] + // Asynchronously find live stream MediaSource::findAsync(strong_self->_media_info, strong_self, [weak_self, close_flag, cb](const MediaSource::Ptr &src) { auto strong_self = weak_self.lock(); if (!strong_self) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } if (!src) { - // 未找到该流 + // 未找到该流 [AUTO-TRANSLATED:2699ef82] + // Stream not found strong_self->sendNotFound(close_flag); } else { strong_self->_is_live_stream = true; - // 触发回调 + // 触发回调 [AUTO-TRANSLATED:ae2ff258] + // Trigger callback cb(src); } }); @@ -317,26 +355,31 @@ bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix auto flag = NOTICE_EMIT(BroadcastMediaPlayedArgs, Broadcast::kBroadcastMediaPlayed, _media_info, invoker, *this); if (!flag) { - // 该事件无人监听,默认不鉴权 + // 该事件无人监听,默认不鉴权 [AUTO-TRANSLATED:e1fbc6ae] + // No one is listening to this event, no authentication by default onRes(""); } return true; } -// http-fmp4 链接格式:http://vhost-url:port/app/streamid.live.mp4?key1=value1&key2=value2 +// http-fmp4 链接格式:http://vhost-url:port/app/streamid.live.mp4?key1=value1&key2=value2 [AUTO-TRANSLATED:c0174f8f] +// http-fmp4 link format: http://vhost-url:port/app/streamid.live.mp4?key1=value1&key2=value2 bool HttpSession::checkLiveStreamFMP4(const function &cb) { return checkLiveStream(FMP4_SCHEMA, ".live.mp4", [this, cb](const MediaSource::Ptr &src) { auto fmp4_src = dynamic_pointer_cast(src); assert(fmp4_src); if (!cb) { - // 找到源,发送http头,负载后续发送 + // 找到源,发送http头,负载后续发送 [AUTO-TRANSLATED:ac272410] + // Found the source, send the http header, and send the load later sendResponse(200, false, HttpFileManager::getContentType(".mp4").data(), KeyValue(), nullptr, true); } else { - // 自定义发送http头 + // 自定义发送http头 [AUTO-TRANSLATED:b8a8f683] + // Custom send http header cb(); } - // 直播牺牲延时提升发送性能 + // 直播牺牲延时提升发送性能 [AUTO-TRANSLATED:7c6616c9] + // Live streaming sacrifices delay to improve sending performance setSocketFlags(); onWrite(std::make_shared(fmp4_src->getInitSegment()), true); weak_ptr weak_self = static_pointer_cast(shared_from_this()); @@ -350,7 +393,8 @@ bool HttpSession::checkLiveStreamFMP4(const function &cb) { _fmp4_reader->setDetachCB([weak_self]() { auto strong_self = weak_self.lock(); if (!strong_self) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } strong_self->shutdown(SockException(Err_shutdown, "fmp4 ring buffer detached")); @@ -358,7 +402,8 @@ bool HttpSession::checkLiveStreamFMP4(const function &cb) { _fmp4_reader->setReadCB([weak_self](const FMP4MediaSource::RingDataType &fmp4_list) { auto strong_self = weak_self.lock(); if (!strong_self) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } size_t i = 0; @@ -368,20 +413,24 @@ bool HttpSession::checkLiveStreamFMP4(const function &cb) { }); } -// http-ts 链接格式:http://vhost-url:port/app/streamid.live.ts?key1=value1&key2=value2 +// http-ts 链接格式:http://vhost-url:port/app/streamid.live.ts?key1=value1&key2=value2 [AUTO-TRANSLATED:aa1a9151] +// http-ts link format: http://vhost-url:port/app/streamid.live.ts?key1=value1&key2=value2 bool HttpSession::checkLiveStreamTS(const function &cb) { return checkLiveStream(TS_SCHEMA, ".live.ts", [this, cb](const MediaSource::Ptr &src) { auto ts_src = dynamic_pointer_cast(src); assert(ts_src); if (!cb) { - // 找到源,发送http头,负载后续发送 + // 找到源,发送http头,负载后续发送 [AUTO-TRANSLATED:ac272410] + // Found the source, send the http header, and send the load later sendResponse(200, false, HttpFileManager::getContentType(".ts").data(), KeyValue(), nullptr, true); } else { - // 自定义发送http头 + // 自定义发送http头 [AUTO-TRANSLATED:b8a8f683] + // Custom send http header cb(); } - // 直播牺牲延时提升发送性能 + // 直播牺牲延时提升发送性能 [AUTO-TRANSLATED:7c6616c9] + // Live streaming sacrifices delay to improve sending performance setSocketFlags(); weak_ptr weak_self = static_pointer_cast(shared_from_this()); ts_src->pause(false); @@ -394,7 +443,8 @@ bool HttpSession::checkLiveStreamTS(const function &cb) { _ts_reader->setDetachCB([weak_self]() { auto strong_self = weak_self.lock(); if (!strong_self) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } strong_self->shutdown(SockException(Err_shutdown, "ts ring buffer detached")); @@ -402,7 +452,8 @@ bool HttpSession::checkLiveStreamTS(const function &cb) { _ts_reader->setReadCB([weak_self](const TSMediaSource::RingDataType &ts_list) { auto strong_self = weak_self.lock(); if (!strong_self) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } size_t i = 0; @@ -412,25 +463,30 @@ bool HttpSession::checkLiveStreamTS(const function &cb) { }); } -// http-flv 链接格式:http://vhost-url:port/app/streamid.live.flv?key1=value1&key2=value2 +// http-flv 链接格式:http://vhost-url:port/app/streamid.live.flv?key1=value1&key2=value2 [AUTO-TRANSLATED:7e78aa20] +// http-flv link format: http://vhost-url:port/app/streamid.live.flv?key1=value1&key2=value2 bool HttpSession::checkLiveStreamFlv(const function &cb) { auto start_pts = atoll(_parser.getUrlArgs()["starPts"].data()); return checkLiveStream(RTMP_SCHEMA, ".live.flv", [this, cb, start_pts](const MediaSource::Ptr &src) { auto rtmp_src = dynamic_pointer_cast(src); assert(rtmp_src); if (!cb) { - // 找到源,发送http头,负载后续发送 + // 找到源,发送http头,负载后续发送 [AUTO-TRANSLATED:ac272410] + // Found the source, send the http header, and send the load later KeyValue headerOut; headerOut["Cache-Control"] = "no-store"; sendResponse(200, false, HttpFileManager::getContentType(".flv").data(), headerOut, nullptr, true); } else { - // 自定义发送http头 + // 自定义发送http头 [AUTO-TRANSLATED:b8a8f683] + // Custom send http header cb(); } - // 直播牺牲延时提升发送性能 + // 直播牺牲延时提升发送性能 [AUTO-TRANSLATED:7c6616c9] + // Live streaming sacrifices delay to improve sending performance setSocketFlags(); - // 非H264/AAC时打印警告日志,防止用户提无效问题 + // 非H264/AAC时打印警告日志,防止用户提无效问题 [AUTO-TRANSLATED:59ee60df] + // Print warning log when it is not H264/AAC, to prevent users from raising invalid issues auto tracks = src->getTracks(false); for (auto &track : tracks) { switch (track->getCodecId()) { @@ -448,34 +504,41 @@ bool HttpSession::checkLiveStreamFlv(const function &cb) { } void HttpSession::onHttpRequest_GET() { - // 先看看是否为WebSocket请求 + // 先看看是否为WebSocket请求 [AUTO-TRANSLATED:98cd3a86] + // First check if it is a WebSocket request if (checkWebSocket()) { - // 后续都是websocket body数据 + // 后续都是websocket body数据 [AUTO-TRANSLATED:c4fcbdcf] + // The following are all websocket body data _on_recv_body = [this](const char *data, size_t len) { WebSocketSplitter::decode((uint8_t *)data, len); - // _contentCallBack是可持续的,后面还要处理后续数据 + // _contentCallBack是可持续的,后面还要处理后续数据 [AUTO-TRANSLATED:920e8c23] + // _contentCallBack is sustainable, and subsequent data needs to be processed later return true; }; return; } if (emitHttpEvent(false)) { - // 拦截http api事件 + // 拦截http api事件 [AUTO-TRANSLATED:2f5e319d] + // Intercept http api events return; } if (checkLiveStreamFlv()) { - // 拦截http-flv播放器 + // 拦截http-flv播放器 [AUTO-TRANSLATED:299f6449] + // Intercept http-flv player return; } if (checkLiveStreamTS()) { - // 拦截http-ts播放器 + // 拦截http-ts播放器 [AUTO-TRANSLATED:d9e303e4] + // Intercept http-ts player return; } if (checkLiveStreamFMP4()) { - // 拦截http-fmp4播放器 + // 拦截http-fmp4播放器 [AUTO-TRANSLATED:78cdf3a1] + // Intercept http-fmp4 player return; } @@ -527,7 +590,8 @@ public: static bool onSocketFlushed(const AsyncSenderData::Ptr &data) { if (data->_read_complete) { if (data->_close_when_complete) { - // 发送完毕需要关闭socket + // 发送完毕需要关闭socket [AUTO-TRANSLATED:fe660e55] + // Close socket after sending is complete shutdown(data->_session.lock()); } return false; @@ -537,13 +601,15 @@ public: data->_body->readDataAsync(sendBufSize, [data](const Buffer::Ptr &sendBuf) { auto session = data->_session.lock(); if (!session) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } session->async([data, sendBuf]() { auto session = data->_session.lock(); if (!session) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } onRequestData(data, session, sendBuf); @@ -556,14 +622,17 @@ private: static void onRequestData(const AsyncSenderData::Ptr &data, const std::shared_ptr &session, const Buffer::Ptr &sendBuf) { session->_ticker.resetTime(); if (sendBuf && session->send(sendBuf) != -1) { - // 文件还未读完,还需要继续发送 + // 文件还未读完,还需要继续发送 [AUTO-TRANSLATED:c454ca1a] + // The file has not been read completely, and needs to be sent continuously if (!session->isSocketBusy()) { - // socket还可写,继续请求数据 + // socket还可写,继续请求数据 [AUTO-TRANSLATED:041df414] + // Socket can still write, continue to request data onSocketFlushed(data); } return; } - // 文件写完了 + // 文件写完了 [AUTO-TRANSLATED:a9f8c117] + // The file is written data->_read_complete = true; if (!session->isSocketBusy() && data->_close_when_complete) { shutdown(session); @@ -586,18 +655,22 @@ void HttpSession::sendResponse(int code, GET_CONFIG(string, charSet, Http::kCharSet); GET_CONFIG(uint32_t, keepAliveSec, Http::kKeepAliveSecond); - // body默认为空 + // body默认为空 [AUTO-TRANSLATED:527ccb6f] + // Body defaults to empty int64_t size = 0; if (body && body->remainSize()) { - // 有body,获取body大小 + // 有body,获取body大小 [AUTO-TRANSLATED:0d5f4b9a] + // There is a body, get the body size size = body->remainSize(); } if (no_content_length) { - // http-flv直播是Keep-Alive类型 + // http-flv直播是Keep-Alive类型 [AUTO-TRANSLATED:0ef3adfe] + // Http-flv live broadcast is Keep-Alive type bClose = false; } else if ((size_t)size >= SIZE_MAX || size < 0) { - // 不固定长度的body,那么发送完body后应该关闭socket,以便浏览器做下载完毕的判断 + // 不固定长度的body,那么发送完body后应该关闭socket,以便浏览器做下载完毕的判断 [AUTO-TRANSLATED:fc714997] + // If the body is not fixed length, then the socket should be closed after sending the body, so that the browser can judge the download completion bClose = true; } @@ -620,24 +693,28 @@ void HttpSession::sendResponse(int code, } if (!no_content_length && size >= 0 && (size_t)size < SIZE_MAX) { - // 文件长度为固定值,且不是http-flv强制设置Content-Length + // 文件长度为固定值,且不是http-flv强制设置Content-Length [AUTO-TRANSLATED:185c02a8] + // The file length is a fixed value, and it is not http-flv that forcibly sets Content-Length headerOut["Content-Length"] = to_string(size); } if (size && !pcContentType) { - // 有body时,设置缺省类型 + // 有body时,设置缺省类型 [AUTO-TRANSLATED:21c9b233] + // When there is a body, set the default type pcContentType = "text/plain"; } if ((size || no_content_length) && pcContentType) { - // 有body时,设置文件类型 + // 有body时,设置文件类型 [AUTO-TRANSLATED:0dcbeecc] + // When there is a body, set the file type string strContentType = pcContentType; strContentType += "; charset="; strContentType += charSet; headerOut.emplace("Content-Type", std::move(strContentType)); } - // 发送http头 + // 发送http头 [AUTO-TRANSLATED:cca51598] + // Send http header string str; str.reserve(256); str += "HTTP/1.1 "; @@ -656,7 +733,8 @@ void HttpSession::sendResponse(int code, _ticker.resetTime(); if (!size) { - // 没有body + // 没有body [AUTO-TRANSLATED:bf891e3a] + // No body if (bClose) { shutdown(SockException(Err_shutdown, StrPrinter << "close connection after send http header completed with status code:" << code)); } @@ -664,20 +742,24 @@ void HttpSession::sendResponse(int code, } #if 0 - //sendfile跟共享mmap相比并没有性能上的优势,相反,sendfile还有功能上的缺陷,先屏蔽 + // sendfile跟共享mmap相比并没有性能上的优势,相反,sendfile还有功能上的缺陷,先屏蔽 [AUTO-TRANSLATED:4de77827] + // Sendfile has no performance advantage over shared mmap, on the contrary, sendfile also has functional defects, so it is blocked first if (typeid(*this) == typeid(HttpSession) && !body->sendFile(getSock()->rawFD())) { - // http支持sendfile优化 + // http支持sendfile优化 [AUTO-TRANSLATED:04f691f1] + // Http supports sendfile optimization return; } #endif GET_CONFIG(uint32_t, sendBufSize, Http::kSendBufSize); if (body->remainSize() > sendBufSize) { - // 文件下载提升发送性能 + // 文件下载提升发送性能 [AUTO-TRANSLATED:500922cc] + // File download improves sending performance setSocketFlags(); } - // 发送http body + // 发送http body [AUTO-TRANSLATED:e9fc35d6] + // Send http body AsyncSenderData::Ptr data = std::make_shared(static_pointer_cast(shared_from_this()), body, bClose); getSock()->setOnFlush([data]() { return AsyncSender::onSocketFlushed(data); }); AsyncSender::onSocketFlushed(data); @@ -692,7 +774,8 @@ void HttpSession::urlDecode(Parser &parser) { bool HttpSession::emitHttpEvent(bool doInvoke) { bool bClose = !strcasecmp(_parser["Connection"].data(), "close"); - /////////////////////异步回复Invoker/////////////////////////////// + // ///////////////////异步回复Invoker/////////////////////////////// [AUTO-TRANSLATED:6d0c5fda] + // ///////////////////Asynchronous reply Invoker/////////////////////////////// weak_ptr weak_self = static_pointer_cast(shared_from_this()); HttpResponseInvoker invoker = [weak_self, bClose](int code, const KeyValue &headerOut, const HttpBody::Ptr &body) { auto strong_self = weak_self.lock(); @@ -702,17 +785,20 @@ bool HttpSession::emitHttpEvent(bool doInvoke) { strong_self->async([weak_self, bClose, code, headerOut, body]() { auto strong_self = weak_self.lock(); if (!strong_self) { - // 本对象已经销毁 + // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] + // This object has been destroyed return; } strong_self->sendResponse(code, bClose, nullptr, headerOut, body); }); }; - ///////////////////广播HTTP事件/////////////////////////// + // /////////////////广播HTTP事件/////////////////////////// [AUTO-TRANSLATED:fff9769c] + // /////////////////Broadcast HTTP event/////////////////////////// bool consumed = false; // 该事件是否被消费 NOTICE_EMIT(BroadcastHttpRequestArgs, Broadcast::kBroadcastHttpRequest, _parser, invoker, consumed, *this); if (!consumed && doInvoke) { - // 该事件无人消费,所以返回404 + // 该事件无人消费,所以返回404 [AUTO-TRANSLATED:8a890dec] + // This event is not consumed, so return 404 invoker(404, KeyValue(), HttpBody::Ptr()); } return consumed; @@ -738,16 +824,19 @@ void HttpSession::sendNotFound(bool bClose) { void HttpSession::setSocketFlags() { GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); if (mergeWriteMS > 0) { - // 推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 + // 推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 [AUTO-TRANSLATED:c8ec8fb8] + // In push mode, closing TCP_NODELAY will increase the delay of the push end, but the server performance will be improved SockUtil::setNoDelay(getSock()->rawFD(), false); - // 播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 + // 播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 [AUTO-TRANSLATED:7b558ab9] + // In playback mode, enabling MSG_MORE will increase the delay, but it can improve sending performance setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); } } void HttpSession::onWrite(const Buffer::Ptr &buffer, bool flush) { if (flush) { - // 需要flush那么一次刷新缓存 + // 需要flush那么一次刷新缓存 [AUTO-TRANSLATED:8d1ec961] + // Need to flush, then flush the cache once HttpSession::setSendFlushFlag(true); } @@ -765,7 +854,8 @@ void HttpSession::onWrite(const Buffer::Ptr &buffer, bool flush) { } if (flush) { - // 本次刷新缓存后,下次不用刷新缓存 + // 本次刷新缓存后,下次不用刷新缓存 [AUTO-TRANSLATED:f56139f7] + // After this cache flush, the next time you don't need to flush the cache HttpSession::setSendFlushFlag(false); } } diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index 9f328063..27544318 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -36,6 +36,11 @@ public: * @param errMsg 如果为空,则代表鉴权通过,否则为错误提示 * @param accessPath 运行或禁止访问的根目录 * @param cookieLifeSecond 鉴权cookie有效期 + * @param errMsg If empty, it means authentication passed, otherwise it is an error message + * @param accessPath The root directory to run or prohibit access + * @param cookieLifeSecond Authentication cookie validity period + * + * [AUTO-TRANSLATED:2e733a35] **/ using HttpAccessPathInvoker = std::function; @@ -65,6 +70,15 @@ protected: * @param len content分片数据大小 * @param totalSize content总大小,如果为0则是不限长度content * @param recvedSize 已收数据大小 + * Overload for handling indefinite length content + * This function can be used to handle large file uploads, http-flv streaming + * @param header http request header + * @param data content fragment data + * @param len content fragment data size + * @param totalSize total content size, if 0, it is unlimited length content + * @param recvedSize received data size + + * [AUTO-TRANSLATED:ee75080d] */ virtual void onRecvUnlimitedContent(const Parser &header, const char *data, @@ -78,6 +92,11 @@ protected: * websocket客户端连接上事件 * @param header http头 * @return true代表允许websocket连接,否则拒绝 + * websocket client connection event + * @param header http header + * @return true means allow websocket connection, otherwise refuse + + * [AUTO-TRANSLATED:d857fb0f] */ virtual bool onWebSocketConnect(const Parser &header){ WarnP(this) << "http server do not support websocket default"; @@ -88,16 +107,25 @@ protected: /** * 发送数据进行websocket协议打包后回调 * @param buffer websocket协议数据 + * Callback after sending data for websocket protocol packaging + * @param buffer websocket protocol data + + * [AUTO-TRANSLATED:48b3b028] */ void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override; /** * 接收到完整的一个webSocket数据包后回调 * @param header 数据包包头 + * Callback after receiving a complete webSocket data packet + * @param header data packet header + + * [AUTO-TRANSLATED:f506a7c5] */ void onWebSocketDecodeComplete(const WebSocketHeader &header_in) override; - //重载获取客户端ip + // 重载获取客户端ip [AUTO-TRANSLATED:6e497ea4] + // Overload to get client ip std::string get_peer_ip() override; private: @@ -120,7 +148,8 @@ private: const HttpSession::KeyValue &header = HttpSession::KeyValue(), const HttpBody::Ptr &body = nullptr, bool no_content_length = false); - //设置socket标志 + // 设置socket标志 [AUTO-TRANSLATED:4086e686] + // Set socket flag void setSocketFlags(); protected: @@ -129,19 +158,24 @@ protected: private: bool _is_live_stream = false; bool _live_over_websocket = false; - //超时时间 + // 超时时间 [AUTO-TRANSLATED:f15e2672] + // Timeout size_t _keep_alive_sec = 0; - //最大http请求字节大小 + // 最大http请求字节大小 [AUTO-TRANSLATED:c1fbc8e5] + // Maximum http request byte size size_t _max_req_size = 0; - //消耗的总流量 + // 消耗的总流量 [AUTO-TRANSLATED:45ad2785] + // Total traffic consumed uint64_t _total_bytes_usage = 0; - // http请求中的 Origin字段 + // http请求中的 Origin字段 [AUTO-TRANSLATED:7b8dd2c0] + // Origin field in http request std::string _origin; Parser _parser; toolkit::Ticker _ticker; TSMediaSource::RingType::RingReader::Ptr _ts_reader; FMP4MediaSource::RingType::RingReader::Ptr _fmp4_reader; - //处理content数据的callback + // 处理content数据的callback [AUTO-TRANSLATED:38890e8d] + // Callback to handle content data std::function _on_recv_body; }; diff --git a/src/Http/HttpTSPlayer.cpp b/src/Http/HttpTSPlayer.cpp index ff2f6afc..f22aafe1 100644 --- a/src/Http/HttpTSPlayer.cpp +++ b/src/Http/HttpTSPlayer.cpp @@ -21,7 +21,8 @@ HttpTSPlayer::HttpTSPlayer(const EventPoller::Ptr &poller) { void HttpTSPlayer::onResponseHeader(const string &status, const HttpClient::HttpHeader &header) { if (status != "200" && status != "206") { - // http状态码不符合预期 + // http状态码不符合预期 [AUTO-TRANSLATED:2b6996f7] + // HTTP status code is not as expected throw invalid_argument("bad http status code:" + status); } diff --git a/src/Http/HttpTSPlayer.h b/src/Http/HttpTSPlayer.h index 219cdcc8..523d5584 100644 --- a/src/Http/HttpTSPlayer.h +++ b/src/Http/HttpTSPlayer.h @@ -17,7 +17,8 @@ namespace mediakit { -//http-ts播发器,未实现ts解复用 +// http-ts播发器,未实现ts解复用 [AUTO-TRANSLATED:cecbd6e7] +// http-ts broadcaster, ts demultiplexing not implemented class HttpTSPlayer : public HttpClientImp { public: using Ptr = std::shared_ptr; @@ -27,11 +28,18 @@ public: /** * 设置下载完毕或异常断开回调 + * Set the callback for download completion or abnormal disconnection + + * [AUTO-TRANSLATED:4f25d583] */ void setOnComplete(onComplete cb); /** * 设置接收ts包回调 + * Set the callback for receiving ts packets + + + * [AUTO-TRANSLATED:af3044a1] */ void setOnPacket(TSSegment::onSegment cb); diff --git a/src/Http/TsPlayer.h b/src/Http/TsPlayer.h index 5ecd73da..4674f688 100644 --- a/src/Http/TsPlayer.h +++ b/src/Http/TsPlayer.h @@ -22,11 +22,18 @@ public: /** * 开始播放 + * Start playing + + * [AUTO-TRANSLATED:53a212c5] */ void play(const std::string &url) override; /** * 停止播放 + * Stop playing + + + * [AUTO-TRANSLATED:db52bf15] */ void teardown() override; diff --git a/src/Http/TsplayerImp.cpp b/src/Http/TsplayerImp.cpp index 131adbd7..42df6a5a 100644 --- a/src/Http/TsplayerImp.cpp +++ b/src/Http/TsplayerImp.cpp @@ -48,12 +48,14 @@ void TsPlayerImp::onPlayResult(const SockException &ex) { void TsPlayerImp::onShutdown(const SockException &ex) { while (_demuxer) { try { - //shared_from_this()可能抛异常 + // shared_from_this()可能抛异常 [AUTO-TRANSLATED:6af9bd3c] + // shared_from_this() may throw an exception std::weak_ptr weak_self = static_pointer_cast(shared_from_this()); if (_decoder) { _decoder->flush(); } - //等待所有frame flush输出后,再触发onShutdown事件 + // 等待所有frame flush输出后,再触发onShutdown事件 [AUTO-TRANSLATED:93982eb3] + // Wait for all frame flush output before triggering the onShutdown event static_pointer_cast(_demuxer)->pushTask([weak_self, ex]() { if (auto strong_self = weak_self.lock()) { strong_self->_demuxer = nullptr; diff --git a/src/Http/WebSocketClient.h b/src/Http/WebSocketClient.h index 9578a9d4..14fe79b5 100644 --- a/src/Http/WebSocketClient.h +++ b/src/Http/WebSocketClient.h @@ -27,6 +27,11 @@ class HttpWsClient; * 辅助类,用于拦截TcpClient数据发送前的拦截 * @tparam ClientType TcpClient派生类 * @tparam DataType 这里无用,为了声明友元用 + * Helper class for intercepting data sent by TcpClient before sending + * @tparam ClientType TcpClient derived class + * @tparam DataType This is useless, used for declaring friends + + * [AUTO-TRANSLATED:02cc7424] */ template class ClientTypeImp : public ClientType { @@ -40,6 +45,9 @@ public: /** * 发送前拦截并打包为websocket协议 + * Intercept before sending and package it into websocket protocol + + * [AUTO-TRANSLATED:b43b6169] */ ssize_t send(toolkit::Buffer::Ptr buf) override { if (_beforeSendCB) { @@ -52,6 +60,10 @@ protected: /** * 设置发送数据截取回调函数 * @param cb 截取回调函数 + * Set the data interception callback function + * @param cb Interception callback function + + * [AUTO-TRANSLATED:3e74fcdd] */ void setOnBeforeSendCB(const onBeforeSendCB &cb) { _beforeSendCB = cb; } @@ -63,6 +75,11 @@ private: * 此对象完成了weksocket 客户端握手协议,以及到TcpClient派生类事件的桥接 * @tparam ClientType TcpClient派生类 * @tparam DataType websocket负载类型,是TEXT还是BINARY类型 + * This object completes the weksocket client handshake protocol and bridges to the TcpClient derived class events + * @tparam ClientType TcpClient derived class + * @tparam DataType websocket payload type, TEXT or BINARY type + + * [AUTO-TRANSLATED:912c15f6] */ template class HttpWsClient : public HttpClientImp, public WebSocketSplitter { @@ -78,6 +95,11 @@ public: * 发起ws握手 * @param ws_url ws连接url * @param fTimeOutSec 超时时间 + * Initiate ws handshake + * @param ws_url ws connection url + * @param fTimeOutSec Timeout time + + * [AUTO-TRANSLATED:453c027c] */ void startWsClient(const std::string &ws_url, float fTimeOutSec) { std::string http_url = ws_url; @@ -95,14 +117,16 @@ public: void closeWsClient() { if (!_onRecv) { - // 未连接 + // 未连接 [AUTO-TRANSLATED:94510177] + // Not connected return; } WebSocketHeader header; header._fin = true; header._reserved = 0; header._opcode = CLOSE; - // 客户端需要加密 + // 客户端需要加密 [AUTO-TRANSLATED:d6958acf] + // Client needs encryption header._mask_flag = true; WebSocketSplitter::encode(header, nullptr); } @@ -114,6 +138,11 @@ protected: * 收到http回复头 * @param status 状态码,譬如:200 OK * @param headers http头 + * Receive http response header + * @param status Status code, such as: 200 OK + * @param headers http header + + * [AUTO-TRANSLATED:a685f8ef] */ void onResponseHeader(const std::string &status, const HttpHeader &headers) override { if (status == "101") { @@ -121,7 +150,8 @@ protected: if (Sec_WebSocket_Accept == const_cast(headers)["Sec-WebSocket-Accept"]) { // success onWebSocketException(toolkit::SockException()); - // 防止ws服务器返回Content-Length + // 防止ws服务器返回Content-Length [AUTO-TRANSLATED:f4454ae6] + // Prevent ws server from returning Content-Length const_cast(headers).erase("Content-Length"); return; } @@ -134,15 +164,22 @@ protected: /** * 接收http回复完毕, + * Receive http response complete, + + * [AUTO-TRANSLATED:b96ed715] */ void onResponseCompleted(const toolkit::SockException &ex) override {} /** * 接收websocket负载数据 + * Receive websocket payload data + + * [AUTO-TRANSLATED:55d403d9] */ void onResponseBody(const char *buf, size_t size) override { if (_onRecv) { - // 完成websocket握手后,拦截websocket数据并解析 + // 完成websocket握手后,拦截websocket数据并解析 [AUTO-TRANSLATED:734280fe] + // After completing the websocket handshake, intercept the websocket data and parse it _onRecv(buf, size); } }; @@ -155,27 +192,34 @@ protected: /** * 定时触发 + * Triggered periodically + + * [AUTO-TRANSLATED:2a75dbf6] */ void onManager() override { if (_onRecv) { - // websocket连接成功了 + // websocket连接成功了 [AUTO-TRANSLATED:45a9e005] + // websocket connection succeeded if (auto strong_ref = _weak_delegate.lock()) { strong_ref->onManager(); } } else { - // websocket连接中... + // websocket连接中... [AUTO-TRANSLATED:861cb158] + // websocket connecting... HttpClientImp::onManager(); } if (!_onRecv) { - // websocket尚未链接 + // websocket尚未链接 [AUTO-TRANSLATED:164129da] + // websocket not yet connected return; } if (_recv_ticker.elapsedTime() > 30 * 1000) { shutdown(toolkit::SockException(toolkit::Err_timeout, "websocket timeout")); } else if (_recv_ticker.elapsedTime() > 10 * 1000) { - // 没收到回复,每10秒发送次ping 包 + // 没收到回复,每10秒发送次ping 包 [AUTO-TRANSLATED:31b4dc13] + // No response received, send a ping packet every 10 seconds WebSocketHeader header; header._fin = true; header._reserved = 0; @@ -187,37 +231,51 @@ protected: /** * 数据全部发送完毕后回调 + * Callback after all data has been sent + + * [AUTO-TRANSLATED:8b2ba800] */ void onFlush() override { if (_onRecv) { - // websocket连接成功了 + // websocket连接成功了 [AUTO-TRANSLATED:45a9e005] + // websocket connection succeeded if (auto strong_ref = _weak_delegate.lock()) { strong_ref->onFlush(); } } else { - // websocket连接中... + // websocket连接中... [AUTO-TRANSLATED:861cb158] + // websocket connecting... HttpClientImp::onFlush(); } } /** * tcp连接结果 + * tcp connection result + + * [AUTO-TRANSLATED:eaca9fcc] */ void onConnect(const toolkit::SockException &ex) override { if (ex) { - // tcp连接失败,直接返回失败 + // tcp连接失败,直接返回失败 [AUTO-TRANSLATED:dcd81b67] + // tcp connection failed, return failure directly onWebSocketException(ex); return; } - // 开始websocket握手 + // 开始websocket握手 [AUTO-TRANSLATED:544a5ba3] + // Start websocket handshake HttpClientImp::onConnect(ex); } /** * tcp连接断开 + * tcp connection disconnected + + * [AUTO-TRANSLATED:732b0740] */ void onError(const toolkit::SockException &ex) override { - // tcp断开或者shutdown导致的断开 + // tcp断开或者shutdown导致的断开 [AUTO-TRANSLATED:5b6b7ad4] + // Disconnection caused by tcp disconnection or shutdown onWebSocketException(ex); } @@ -226,6 +284,10 @@ protected: /** * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePayload回调 * @param header 数据包头 + * Receive a webSocket data packet header, and then continue to trigger the onWebSocketDecodePayload callback + * @param header Data packet header + + * [AUTO-TRANSLATED:7bc6b7c6] */ void onWebSocketDecodeHeader(const WebSocketHeader &header) override { _payload_section.clear(); } @@ -235,6 +297,13 @@ protected: * @param ptr 负载数据指针 * @param len 负载数据长度 * @param recved 已接收数据长度(包含本次数据长度),等于header._payload_len时则接受完毕 + * Receive webSocket data packet payload + * @param header Data packet header + * @param ptr Payload data pointer + * @param len Payload data length + * @param recved Received data length (including the current data length), equal to header._payload_len when the reception is complete + + * [AUTO-TRANSLATED:ca056d2e] */ void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, size_t len, size_t recved) override { _payload_section.append((char *)ptr, len); @@ -243,23 +312,30 @@ protected: /** * 接收到完整的一个webSocket数据包后回调 * @param header 数据包包头 + * Callback after receiving a complete webSocket data packet + * @param header Data packet header + + * [AUTO-TRANSLATED:f506a7c5] */ void onWebSocketDecodeComplete(const WebSocketHeader &header_in) override { WebSocketHeader &header = const_cast(header_in); auto flag = header._mask_flag; - // websocket客户端发送数据需要加密 + // websocket客户端发送数据需要加密 [AUTO-TRANSLATED:2bbbb390] + // websocket client needs to encrypt data sent header._mask_flag = true; _recv_ticker.resetTime(); switch (header._opcode) { case WebSocketHeader::CLOSE: { - // 服务器主动关闭 + // 服务器主动关闭 [AUTO-TRANSLATED:5a59e1bf] + // Server actively closes WebSocketSplitter::encode(header, nullptr); shutdown(toolkit::SockException(toolkit::Err_eof, "websocket server close the connection")); break; } case WebSocketHeader::PING: { - // 心跳包 + // 心跳包 [AUTO-TRANSLATED:1b4b9ae4] + // Heartbeat packet header._opcode = WebSocketHeader::PONG; WebSocketSplitter::encode(header, std::make_shared(std::move(_payload_section))); break; @@ -269,25 +345,31 @@ protected: case WebSocketHeader::TEXT: case WebSocketHeader::BINARY: { if (!header._fin) { - // 还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出 + // 还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出 [AUTO-TRANSLATED:0a237b29] + // There are subsequent fragment data, we cache the data first, and output it all at once after all fragments are collected _payload_cache.append(std::move(_payload_section)); if (_payload_cache.size() < MAX_WS_PACKET) { - // 还有内存容量缓存分片数据 + // 还有内存容量缓存分片数据 [AUTO-TRANSLATED:8da8074a] + // There is also memory capacity to cache fragment data break; } - // 分片缓存太大,需要清空 + // 分片缓存太大,需要清空 [AUTO-TRANSLATED:a0d9c101] + // Fragment cache is too large, need to clear } - // 最后一个包 + // 最后一个包 [AUTO-TRANSLATED:82e1bf79] + // Last packet if (_payload_cache.empty()) { - // 这个包是唯一个分片 + // 这个包是唯一个分片 [AUTO-TRANSLATED:079a9865] + // This packet is the only fragment if (auto strong_ref = _weak_delegate.lock()) { strong_ref->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_section))); } break; } - // 这个包由多个分片组成 + // 这个包由多个分片组成 [AUTO-TRANSLATED:27fd75df] + // This packet consists of multiple fragments _payload_cache.append(std::move(_payload_section)); if (auto strong_ref = _weak_delegate.lock()) { strong_ref->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_cache))); @@ -306,14 +388,21 @@ protected: * websocket数据编码回调 * @param ptr 数据指针 * @param len 数据指针长度 + * websocket data encoding callback + * @param ptr data pointer + * @param len data pointer length + + * [AUTO-TRANSLATED:7c940c67] */ void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override { HttpClientImp::send(std::move(buffer)); } private: void onWebSocketException(const toolkit::SockException &ex) { if (!ex) { - // websocket握手成功 - // 此处截取TcpClient派生类发送的数据并进行websocket协议打包 + // websocket握手成功 [AUTO-TRANSLATED:bceba441] + // websocket handshake successful + // 此处截取TcpClient派生类发送的数据并进行websocket协议打包 [AUTO-TRANSLATED:8cae42cd] + // Here, the data sent by the TcpClient derived class is intercepted and packaged into the websocket protocol std::weak_ptr weakSelf = std::static_pointer_cast(shared_from_this()); if (auto strong_ref = _weak_delegate.lock()) { strong_ref->setOnBeforeSendCB([weakSelf](const toolkit::Buffer::Ptr &buf) { @@ -323,29 +412,36 @@ private: header._fin = true; header._reserved = 0; header._opcode = DataType; - // 客户端需要加密 + // 客户端需要加密 [AUTO-TRANSLATED:d6958acf] + // Client needs encryption header._mask_flag = true; strong_self->WebSocketSplitter::encode(header, buf); } return buf->size(); }); - // 设置sock,否则shutdown等接口都无效 + // 设置sock,否则shutdown等接口都无效 [AUTO-TRANSLATED:4586b98b] + // Set sock, otherwise shutdown and other interfaces are invalid strong_ref->setSock(HttpClientImp::getSock()); - // 触发连接成功事件 + // 触发连接成功事件 [AUTO-TRANSLATED:0459f68f] + // Trigger connection success event strong_ref->onConnect(ex); } - // 拦截websocket数据接收 + // 拦截websocket数据接收 [AUTO-TRANSLATED:fb93bbe9] + // Intercept websocket data reception _onRecv = [this](const char *data, size_t len) { - // 解析websocket数据包 + // 解析websocket数据包 [AUTO-TRANSLATED:656b8c89] + // Parse websocket data packet this->WebSocketSplitter::decode((uint8_t *)data, len); }; return; } - // websocket握手失败或者tcp连接失败或者中途断开 + // websocket握手失败或者tcp连接失败或者中途断开 [AUTO-TRANSLATED:acf8d1ff] + // websocket handshake failed or tcp connection failed or disconnected in the middle if (_onRecv) { - // 握手成功之后的中途断开 + // 握手成功之后的中途断开 [AUTO-TRANSLATED:dd5d412c] + // Disconnected in the middle after handshake success _onRecv = nullptr; if (auto strong_ref = _weak_delegate.lock()) { strong_ref->onError(ex); @@ -353,7 +449,8 @@ private: return; } - // websocket握手失败或者tcp连接失败 + // websocket握手失败或者tcp连接失败 [AUTO-TRANSLATED:3f03cf1f] + // websocket handshake failed or tcp connection failed if (auto strong_ref = _weak_delegate.lock()) { strong_ref->onConnect(ex); } @@ -374,6 +471,13 @@ private: * @tparam ClientType TcpClient派生类 * @tparam DataType websocket负载类型,是TEXT还是BINARY类型 * @tparam useWSS 是否使用ws还是wss连接 + * Tcp client to WebSocket client template, + * Through this template, developers can quickly implement WebSocket protocol packaging without modifying any code of the TcpClient derived class + * @tparam ClientType TcpClient derived class + * @tparam DataType websocket payload type, is it TEXT or BINARY type + * @tparam useWSS Whether to use ws or wss connection + + * [AUTO-TRANSLATED:ac1516b8] */ template class WebSocketClient : public ClientTypeImp { @@ -391,14 +495,24 @@ public: * @param iPort websocket服务器端口 * @param timeout_sec 超时时间 * @param local_port 本地监听端口,此处不起作用 + * Overload the startConnect method, + * The purpose is to replace the TcpClient's connection server behavior, so that it completes the WebSocket handshake first + * @param host websocket server ip or domain name + * @param iPort websocket server port + * @param timeout_sec timeout time + * @param local_port local listening port, which does not work here + + * [AUTO-TRANSLATED:1aed295d] */ void startConnect(const std::string &host, uint16_t port, float timeout_sec = 3, uint16_t local_port = 0) override { std::string ws_url; if (useWSS) { - // 加密的ws + // 加密的ws [AUTO-TRANSLATED:d1385825] + // Encrypted ws ws_url = StrPrinter << "wss://" + host << ":" << port << "/"; } else { - // 明文ws + // 明文ws [AUTO-TRANSLATED:71aa82d1] + // Plaintext ws ws_url = StrPrinter << "ws://" + host << ":" << port << "/"; } startWebSocket(ws_url, timeout_sec); diff --git a/src/Http/WebSocketSession.h b/src/Http/WebSocketSession.h index 93bb820e..5c121c1e 100644 --- a/src/Http/WebSocketSession.h +++ b/src/Http/WebSocketSession.h @@ -16,6 +16,9 @@ /** * 数据发送拦截器 + * Data Send Interceptor + + * [AUTO-TRANSLATED:5eaf7060] */ class SendInterceptor{ public: @@ -28,6 +31,10 @@ public: /** * 该类实现了Session派生类发送数据的截取 * 目的是发送业务数据前进行websocket协议的打包 + * This class implements the interception of data sent by the Session derived class. + * The purpose is to package the websocket protocol before sending business data. + + * [AUTO-TRANSLATED:15c96e5f] */ template class SessionTypeImp : public SessionType, public SendInterceptor{ @@ -40,6 +47,10 @@ public: /** * 设置发送数据截取回调函数 * @param cb 截取回调函数 + * Set the send data interception callback function + * @param cb Interception callback function + + * [AUTO-TRANSLATED:3e74fcdd] */ void setOnBeforeSendCB(const onBeforeSendCB &cb) override { _beforeSendCB = cb; @@ -50,6 +61,11 @@ protected: * 重载send函数截取数据 * @param buf 需要截取的数据 * @return 数据字节数 + * Overload the send function to intercept data + * @param buf Data to be intercepted + * @return Number of data bytes + + * [AUTO-TRANSLATED:d3304949] */ ssize_t send(toolkit::Buffer::Ptr buf) override { if (_beforeSendCB) { @@ -65,7 +81,8 @@ private: template class SessionCreator { public: - //返回的Session必须派生于SendInterceptor,可以返回null + // 返回的Session必须派生于SendInterceptor,可以返回null [AUTO-TRANSLATED:6cc95812] + // The returned Session must be derived from SendInterceptor, and can return null toolkit::Session::Ptr operator()(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock, mediakit::WebSocketHeader::Type &data_type){ return std::make_shared >(header,parent,pSock); } @@ -74,20 +91,26 @@ public: /** * 通过该模板类可以透明化WebSocket协议, * 用户只要实现WebSock协议下的具体业务协议,譬如基于WebSocket协议的Rtmp协议等 + * Through this template class, the WebSocket protocol can be transparently implemented. + * Users only need to implement specific business protocols under the WebSock protocol, such as the Rtmp protocol based on the WebSocket protocol. + + * [AUTO-TRANSLATED:07e2e8a5] */ template class WebSocketSessionBase : public HttpSessionType { public: WebSocketSessionBase(const toolkit::Socket::Ptr &pSock) : HttpSessionType(pSock){} - //收到eof或其他导致脱离TcpServer事件的回调 + // 收到eof或其他导致脱离TcpServer事件的回调 [AUTO-TRANSLATED:6d48b35c] + // Callback when receiving eof or other events that cause disconnection from TcpServer void onError(const toolkit::SockException &err) override{ HttpSessionType::onError(err); if(_session){ _session->onError(err); } } - //每隔一段时间触发,用来做超时管理 + // 每隔一段时间触发,用来做超时管理 [AUTO-TRANSLATED:823ffe1f] + // Triggered every period of time, used for timeout management void onManager() override{ if (_session) { _session->onManager(); @@ -95,13 +118,15 @@ public: HttpSessionType::onManager(); } if (!_session) { - // websocket尚未链接 + // websocket尚未链接 [AUTO-TRANSLATED:164129da] + // websocket is not yet connected return; } if (_recv_ticker.elapsedTime() > 30 * 1000) { HttpSessionType::shutdown(toolkit::SockException(toolkit::Err_timeout, "websocket timeout")); } else if (_recv_ticker.elapsedTime() > 10 * 1000) { - // 没收到回复,每10秒发送次ping 包 + // 没收到回复,每10秒发送次ping 包 [AUTO-TRANSLATED:31b4dc13] + // No reply received, send a ping packet every 10 seconds mediakit::WebSocketHeader header; header._fin = true; header._reserved = 0; @@ -121,13 +146,20 @@ protected: * websocket客户端连接上事件 * @param header http头 * @return true代表允许websocket连接,否则拒绝 + * websocket client connection event + * @param header http header + * @return true means allowing websocket connection, otherwise refuse + + * [AUTO-TRANSLATED:d857fb0f] */ bool onWebSocketConnect(const mediakit::Parser &header) override{ - //创建websocket session类 + // 创建websocket session类 [AUTO-TRANSLATED:099f6963] + // Create websocket session class auto data_type = DataType; _session = _creator(header, *this, HttpSessionType::getSock(), data_type); if (!_session) { - // 此url不允许创建websocket连接 + // 此url不允许创建websocket连接 [AUTO-TRANSLATED:47480366] + // This url is not allowed to create websocket connection return false; } auto strongServer = _weak_server.lock(); @@ -135,7 +167,8 @@ protected: _session->attachServer(*strongServer); } - //此处截取数据并进行websocket协议打包 + // 此处截取数据并进行websocket协议打包 [AUTO-TRANSLATED:89053032] + // Intercept data here and package it with websocket protocol std::weak_ptr weakSelf = std::static_pointer_cast(HttpSessionType::shared_from_this()); std::dynamic_pointer_cast(_session)->setOnBeforeSendCB([weakSelf, data_type](const toolkit::Buffer::Ptr &buf) { auto strongSelf = weakSelf.lock(); @@ -150,20 +183,28 @@ protected: return buf->size(); }); - //允许websocket客户端 + // 允许websocket客户端 [AUTO-TRANSLATED:3a06f181] + // Allow websocket client return true; } /** * 开始收到一个webSocket数据包 + * Start receiving a webSocket data packet + + * [AUTO-TRANSLATED:0f16a5b5] */ void onWebSocketDecodeHeader(const mediakit::WebSocketHeader &packet) override{ - //新包,原来的包残余数据清空掉 + // 新包,原来的包残余数据清空掉 [AUTO-TRANSLATED:0fd23412] + // New package, the residual data of the original package is cleared _payload_section.clear(); } /** * 收到websocket数据包负载 + * Receive websocket data packet payload + + * [AUTO-TRANSLATED:b317988d] */ void onWebSocketDecodePayload(const mediakit::WebSocketHeader &packet,const uint8_t *ptr,size_t len,size_t recved) override { _payload_section.append((char *)ptr,len); @@ -172,6 +213,10 @@ protected: /** * 接收到完整的一个webSocket数据包后回调 * @param header 数据包包头 + * Callback after receiving a complete webSocket data packet + * @param header Data packet header + + * [AUTO-TRANSLATED:f506a7c5] */ void onWebSocketDecodeComplete(const mediakit::WebSocketHeader &header_in) override { auto header = const_cast(header_in); @@ -195,23 +240,29 @@ protected: case mediakit::WebSocketHeader::TEXT: case mediakit::WebSocketHeader::BINARY:{ if (!header._fin) { - //还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出 + // 还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出 [AUTO-TRANSLATED:75d21e17] + // There is subsequent fragment data, we cache the data first, and output it all at once after all fragments are collected _payload_cache.append(std::move(_payload_section)); if (_payload_cache.size() < MAX_WS_PACKET) { - //还有内存容量缓存分片数据 + // 还有内存容量缓存分片数据 [AUTO-TRANSLATED:621da1f9] + // There is memory capacity to cache fragment data break; } - //分片缓存太大,需要清空 + // 分片缓存太大,需要清空 [AUTO-TRANSLATED:98882d1f] + // Fragment cache is too large, need to be cleared } - //最后一个包 + // 最后一个包 [AUTO-TRANSLATED:dcf860cf] + // Last package if (_payload_cache.empty()) { - //这个包是唯一个分片 + // 这个包是唯一个分片 [AUTO-TRANSLATED:94802e24] + // This package is the only fragment _session->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_section))); break; } - //这个包由多个分片组成 + // 这个包由多个分片组成 [AUTO-TRANSLATED:044123f1] + // This package consists of multiple fragments _payload_cache.append(std::move(_payload_section)); _session->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_cache))); _payload_cache.clear(); @@ -226,6 +277,9 @@ protected: /** * 发送数据进行websocket协议打包后回调 + * Callback after sending data and packaging it with websocket protocol + + * [AUTO-TRANSLATED:3327ce78] */ void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override{ HttpSessionType::send(std::move(buffer)); diff --git a/src/Http/WebSocketSplitter.cpp b/src/Http/WebSocketSplitter.cpp index 7e17a390..676df467 100644 --- a/src/Http/WebSocketSplitter.cpp +++ b/src/Http/WebSocketSplitter.cpp @@ -58,7 +58,8 @@ do{ \ void WebSocketSplitter::decode(uint8_t *data, size_t len) { uint8_t *ptr = data; if(!_got_header) { - //还没有获取数据头 + // 还没有获取数据头 [AUTO-TRANSLATED:2b50f282] + // No data header has been obtained yet if(!_remain_data.empty()){ _remain_data.append((char *)data,len); data = ptr = (uint8_t *)_remain_data.data(); @@ -107,7 +108,8 @@ begin_decode: } } - //进入后面逻辑代表已经获取到了webSocket协议头, + // 进入后面逻辑代表已经获取到了webSocket协议头, [AUTO-TRANSLATED:e6bd2556] + // Entering the following logic means that the webSocket protocol header has been obtained, auto remain = len - (ptr - data); if(remain > 0){ @@ -121,13 +123,15 @@ begin_decode: if(_payload_offset == _payload_len){ onWebSocketDecodeComplete(*this); - //这是下一个包 + // 这是下一个包 [AUTO-TRANSLATED:bf657413] + // This is the next package remain -= payload_slice_len; ptr += payload_slice_len; _got_header = false; if(remain > 0){ - //剩余数据是下一个包,把它的数据放置在缓存中 + // 剩余数据是下一个包,把它的数据放置在缓存中 [AUTO-TRANSLATED:7b2ebfad] + // The remaining data is the next package, place its data in the cache string str((char *)ptr,remain); _remain_data = str; diff --git a/src/Http/WebSocketSplitter.h b/src/Http/WebSocketSplitter.h index 96ea2c0c..a49b6932 100644 --- a/src/Http/WebSocketSplitter.h +++ b/src/Http/WebSocketSplitter.h @@ -17,7 +17,8 @@ #include #include "Network/Buffer.h" -//websocket组合包最大不得超过4MB(防止内存爆炸) +// websocket组合包最大不得超过4MB(防止内存爆炸) [AUTO-TRANSLATED:99c11a1d] +// websocket combined package size must not exceed 4MB (to prevent memory explosion) #define MAX_WS_PACKET (4 * 1024 * 1024) namespace mediakit { @@ -46,9 +47,11 @@ public: public: WebSocketHeader() : _mask(4){ - //获取_mask内部buffer的内存地址,该内存是malloc开辟的,地址为随机 + // 获取_mask内部buffer的内存地址,该内存是malloc开辟的,地址为随机 [AUTO-TRANSLATED:9406f0b6] + // Get the memory address of the internal buffer of _mask, the memory is allocated by malloc, and the address is random uint64_t ptr = (uint64_t)(&_mask[0]); - //根据内存地址设置掩码随机数 + // 根据内存地址设置掩码随机数 [AUTO-TRANSLATED:47881295] + // Set the mask random number according to the memory address _mask.assign((uint8_t*)(&ptr), (uint8_t*)(&ptr) + 4); } @@ -63,7 +66,8 @@ public: std::vector _mask; }; -//websocket协议收到的字符串类型缓存,用户协议层获取该数据传输的方式 +// websocket协议收到的字符串类型缓存,用户协议层获取该数据传输的方式 [AUTO-TRANSLATED:a66e0177] +// String type cache received by the websocket protocol, the way the user protocol layer obtains this data transmission class WebSocketBuffer : public toolkit::BufferString { public: using Ptr = std::shared_ptr; @@ -88,6 +92,12 @@ public: * 可能触发onWebSocketDecodeHeader和onWebSocketDecodePayload回调 * @param data 需要解包的数据,可能是不完整的包或多个包 * @param len 数据长度 + * Input data to unpack webSocket data and handle sticky packet problems + * May trigger onWebSocketDecodeHeader and onWebSocketDecodePayload callbacks + * @param data Data to be unpacked, may be incomplete packets or multiple packets + * @param len Data length + + * [AUTO-TRANSLATED:e5f2c2c6] */ void decode(uint8_t *data, size_t len); @@ -96,6 +106,12 @@ public: * 将触发2次onWebSocketEncodeData回调 * @param header 数据头 * @param buffer 负载数据 + * Encode a data packet + * Will trigger 2 onWebSocketEncodeData callbacks + * @param header Data header + * @param buffer Payload data + + * [AUTO-TRANSLATED:f308e552] */ void encode(const WebSocketHeader &header,const toolkit::Buffer::Ptr &buffer); @@ -103,6 +119,10 @@ protected: /** * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePayload回调 * @param header 数据包头 + * Receive a webSocket data packet header, and will continue to trigger onWebSocketDecodePayload callback + * @param header Data packet header + + * [AUTO-TRANSLATED:7bc6b7c6] */ virtual void onWebSocketDecodeHeader(const WebSocketHeader &header) {}; @@ -112,12 +132,23 @@ protected: * @param ptr 负载数据指针 * @param len 负载数据长度 * @param recved 已接收数据长度(包含本次数据长度),等于header._payload_len时则接受完毕 + * Receive webSocket data packet payload + * @param header Data packet header + * @param ptr Payload data pointer + * @param len Payload data length + * @param recved Received data length (including the length of this data), equals header._payload_len when the reception is complete + + * [AUTO-TRANSLATED:ca056d2e] */ virtual void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, size_t len, size_t recved) {}; /** * 接收到完整的一个webSocket数据包后回调 * @param header 数据包包头 + * Callback after receiving a complete webSocket data packet + * @param header Data packet header + + * [AUTO-TRANSLATED:f506a7c5] */ virtual void onWebSocketDecodeComplete(const WebSocketHeader &header) {}; @@ -125,6 +156,12 @@ protected: * websocket数据编码回调 * @param ptr 数据指针 * @param len 数据指针长度 + * websocket data encoding callback + * @param ptr Data pointer + * @param len Data pointer length + + + * [AUTO-TRANSLATED:7c940c67] */ virtual void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer){}; diff --git a/src/Player/MediaPlayer.cpp b/src/Player/MediaPlayer.cpp index c5cf5108..8ed4cc2f 100644 --- a/src/Player/MediaPlayer.cpp +++ b/src/Player/MediaPlayer.cpp @@ -26,7 +26,8 @@ static void setOnCreateSocket_l(const std::shared_ptr &delegate, con if (cb) { helper->setOnCreateSocket(cb); } else { - //客户端,确保开启互斥锁 + // 客户端,确保开启互斥锁 [AUTO-TRANSLATED:a75e6e36] + // Client, ensure mutual exclusion lock is enabled helper->setOnCreateSocket([](const EventPoller::Ptr &poller) { return Socket::createSocket(poller, true); }); diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index 9cc18f98..b94aaee9 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -38,7 +38,8 @@ PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &in_poller, cons string prefix = findSubString(url.data(), NULL, "://"); auto pos = url.find('?'); if (pos != string::npos) { - //去除?后面的字符串 + // 去除?后面的字符串 [AUTO-TRANSLATED:0ccb41c2] + // Remove the string after the question mark url = url.substr(0, pos); } diff --git a/src/Player/PlayerBase.h b/src/Player/PlayerBase.h index b2f2f27d..dc033948 100644 --- a/src/Player/PlayerBase.h +++ b/src/Player/PlayerBase.h @@ -36,81 +36,133 @@ public: /** * 开始播放 * @param url 视频url,支持rtsp/rtmp + * Start playback + * @param url Video url, supports rtsp/rtmp + + * [AUTO-TRANSLATED:3871cbee] */ virtual void play(const std::string &url) {}; /** * 暂停或恢复 * @param flag true:暂停,false:恢复 + * Pause or resume + * @param flag true: pause, false: resume + + * [AUTO-TRANSLATED:2a17eab2] */ virtual void pause(bool flag) {}; /** * 获取节目总时长,单位秒 + * Get the total duration of the program, in seconds + + * [AUTO-TRANSLATED:f3de1631] */ virtual float getDuration() const { return 0; }; /** * 倍数播放 * @param speed 1.0 2.0 0.5 + * Playback at a multiple + * @param speed 1.0 2.0 0.5 + + * [AUTO-TRANSLATED:46bf057e] */ virtual void speed(float speed) {}; /** * 中断播放 + * Interrupt playback + + * [AUTO-TRANSLATED:d962e9bc] */ virtual void teardown() {}; /** * 获取播放进度,取值 0.0 ~ 1.0 + * Get playback progress, value 0.0 ~ 1.0 + + * [AUTO-TRANSLATED:ba24f450] */ virtual float getProgress() const { return 0; }; /** * 获取播放进度pos,取值 相对开始时间增量 单位秒 + * Get playback progress pos, value relative to the start time increment, unit seconds + + * [AUTO-TRANSLATED:1eb148ad] */ virtual uint32_t getProgressPos() const { return 0; }; /** * 拖动进度条 * @param progress 进度,取值 0.0 ~ 1.0 + * Drag the progress bar + * @param progress Progress, value 0.0 ~ 1.0 + + * [AUTO-TRANSLATED:c4907336] */ virtual void seekTo(float progress) {}; /** * 拖动进度条 * @param pos 进度,取值 相对于开始时间的增量 单位秒 + * Drag the progress bar + * @param pos Progress, value relative to the start time increment, unit seconds + + * [AUTO-TRANSLATED:77dab991] */ virtual void seekTo(uint32_t pos) {}; /** * 获取丢包率,只支持rtsp * @param type 音频或视频,TrackInvalid时为总丢包率 + * Get packet loss rate, only supports rtsp + * @param type Audio or video, TrackInvalid for total packet loss rate + + * [AUTO-TRANSLATED:aac7f19c] */ virtual float getPacketLossRate(TrackType type) const { return -1; }; /** * 获取所有track + * Get all tracks + + * [AUTO-TRANSLATED:5860aed6] */ std::vector getTracks(bool ready = true) const override { return std::vector(); }; /** * 设置一个MediaSource,直接生产rtsp/rtmp代理 + * Set a MediaSource, directly produce rtsp/rtmp proxy + + * [AUTO-TRANSLATED:dda602c4] */ virtual void setMediaSource(const MediaSource::Ptr &src) = 0; /** * 设置异常中断回调 + * Set exception interrupt callback + + * [AUTO-TRANSLATED:d931e70d] */ virtual void setOnShutdown(const Event &cb) = 0; /** * 设置播放结果回调 + * Set playback result callback + + * [AUTO-TRANSLATED:f6d73f89] */ virtual void setOnPlayResult(const Event &cb) = 0; /** * 设置播放恢复回调 + * Set playback resume callback + + + * [AUTO-TRANSLATED:8fb31d43] */ virtual void setOnResume(const std::function &cb) = 0; diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 2c931212..9ccd701f 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -94,7 +94,8 @@ void PlayerProxy::setTranslationInfo() static int getMaxTrackSize(const std::string &url) { if (url.find(".m3u8") != std::string::npos || url.find(".ts") != std::string::npos) { - // hls和ts协议才开放多track支持 + // hls和ts协议才开放多track支持 [AUTO-TRANSLATED:6c5f8f04] + // Only hls and ts protocols support multiple tracks return 16; } return 2; @@ -116,11 +117,13 @@ void PlayerProxy::play(const string &strUrlTmp) { } if (!err) { - // 取消定时器,避免hls拉流索引文件因为网络波动失败重连成功后出现循环重试的情况 + // 取消定时器,避免hls拉流索引文件因为网络波动失败重连成功后出现循环重试的情况 [AUTO-TRANSLATED:91e5f0c8] + // Cancel the timer to avoid the situation where the hls stream index file fails to reconnect due to network fluctuations and then retries in a loop after successful reconnection strongSelf->_timer.reset(); strongSelf->_live_ticker.resetTime(); strongSelf->_live_status = 0; - // 播放成功 + // 播放成功 [AUTO-TRANSLATED:e43f9fb8] + // Play successfully *piFailedCnt = 0; // 连续播放失败次数清0 strongSelf->onPlaySuccess(); strongSelf->setTranslationInfo(); @@ -128,11 +131,13 @@ void PlayerProxy::play(const string &strUrlTmp) { InfoL << "play " << strUrlTmp << " success"; } else if (*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) { - // 播放失败,延时重试播放 + // 播放失败,延时重试播放 [AUTO-TRANSLATED:d7537c9c] + // Play failed, retry playing with delay strongSelf->_on_disconnect(); strongSelf->rePlay(strUrlTmp, (*piFailedCnt)++); } else { - // 达到了最大重试次数,回调关闭 + // 达到了最大重试次数,回调关闭 [AUTO-TRANSLATED:610f31f3] + // Reached the maximum number of retries, callback to close strongSelf->_on_close(err); } }); @@ -142,7 +147,8 @@ void PlayerProxy::play(const string &strUrlTmp) { return; } - // 注销直接拉流代理产生的流:#532 + // 注销直接拉流代理产生的流:#532 [AUTO-TRANSLATED:c6343a3b] + // Unregister the stream generated by the direct stream proxy: #532 strongSelf->setMediaSource(nullptr); if (strongSelf->_muxer) { @@ -160,18 +166,21 @@ void PlayerProxy::play(const string &strUrlTmp) { } if (*piFailedCnt == 0) { - // 第一次重拉更新时长 + // 第一次重拉更新时长 [AUTO-TRANSLATED:3c414b08] + // Update the duration for the first time strongSelf->_live_secs += strongSelf->_live_ticker.elapsedTime() / 1000; strongSelf->_live_ticker.resetTime(); TraceL << " live secs " << strongSelf->_live_secs; } - // 播放异常中断,延时重试播放 + // 播放异常中断,延时重试播放 [AUTO-TRANSLATED:fee316b2] + // Play interrupted abnormally, retry playing with delay if (*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) { strongSelf->_repull_count++; strongSelf->rePlay(strUrlTmp, (*piFailedCnt)++); } else { - // 达到了最大重试次数,回调关闭 + // 达到了最大重试次数,回调关闭 [AUTO-TRANSLATED:610f31f3] + // Reached the maximum number of retries, callback to close strongSelf->_on_close(err); } }); @@ -189,13 +198,15 @@ void PlayerProxy::play(const string &strUrlTmp) { void PlayerProxy::setDirectProxy() { MediaSource::Ptr mediaSource; if (dynamic_pointer_cast(_delegate)) { - // rtsp拉流 + // rtsp拉流 [AUTO-TRANSLATED:189cf691] + // Rtsp stream GET_CONFIG(bool, directProxy, Rtsp::kDirectProxy); if (directProxy && _option.enable_rtsp) { mediaSource = std::make_shared(_tuple); } } else if (dynamic_pointer_cast(_delegate)) { - // rtmp拉流 + // rtmp拉流 [AUTO-TRANSLATED:f70a142c] + // Rtmp stream GET_CONFIG(bool, directProxy, Rtmp::kDirectProxy); if (directProxy && _option.enable_rtmp) { mediaSource = std::make_shared(_tuple); @@ -208,7 +219,8 @@ void PlayerProxy::setDirectProxy() { PlayerProxy::~PlayerProxy() { _timer.reset(); - // 避免析构时, 忘记回调api请求 + // 避免析构时, 忘记回调api请求 [AUTO-TRANSLATED:1ad9ad52] + // Avoid forgetting to callback api request when destructing if (_on_play) { try { _on_play(SockException(Err_shutdown, "player proxy close")); @@ -225,7 +237,8 @@ void PlayerProxy::rePlay(const string &strUrl, int iFailedCnt) { _timer = std::make_shared( iDelay / 1000.0f, [weakSelf, strUrl, iFailedCnt]() { - // 播放失败次数越多,则延时越长 + // 播放失败次数越多,则延时越长 [AUTO-TRANSLATED:5af39264] + // The more times the playback fails, the longer the delay auto strongPlayer = weakSelf.lock(); if (!strongPlayer) { return false; @@ -239,7 +252,8 @@ void PlayerProxy::rePlay(const string &strUrl, int iFailedCnt) { } bool PlayerProxy::close(MediaSource &sender) { - // 通知其停止推流 + // 通知其停止推流 [AUTO-TRANSLATED:d69d10d8] + // Notify it to stop pushing the stream weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); getPoller()->async_first([weakSelf]() { auto strongSelf = weakSelf.lock(); @@ -286,19 +300,26 @@ TranslationInfo PlayerProxy::getTranslationInfo() { void PlayerProxy::onPlaySuccess() { GET_CONFIG(bool, reset_when_replay, General::kResetWhenRePlay); if (dynamic_pointer_cast(_media_src)) { - // rtsp拉流代理 + // rtsp拉流代理 [AUTO-TRANSLATED:3935cf68] + // Rtsp stream proxy if (reset_when_replay || !_muxer) { + auto old = _option.enable_rtsp; _option.enable_rtsp = false; _muxer = std::make_shared(_tuple, getDuration(), _option); + _option.enable_rtsp = old; } } else if (dynamic_pointer_cast(_media_src)) { - // rtmp拉流代理 + // rtmp拉流代理 [AUTO-TRANSLATED:21173335] + // Rtmp stream proxy if (reset_when_replay || !_muxer) { + auto old = _option.enable_rtmp; _option.enable_rtmp = false; _muxer = std::make_shared(_tuple, getDuration(), _option); + _option.enable_rtmp = old; } } else { - // 其他拉流代理 + // 其他拉流代理 [AUTO-TRANSLATED:e5f2e45d] + // Other stream proxies if (reset_when_replay || !_muxer) { _muxer = std::make_shared(_tuple, getDuration(), _option); } @@ -307,25 +328,31 @@ void PlayerProxy::onPlaySuccess() { auto videoTrack = getTrack(TrackVideo, false); if (videoTrack) { - // 添加视频 + // 添加视频 [AUTO-TRANSLATED:afc7e0f7] + // Add video _muxer->addTrack(videoTrack); - // 视频数据写入_mediaMuxer + // 视频数据写入_mediaMuxer [AUTO-TRANSLATED:fc07e1c9] + // Write video data to _mediaMuxer videoTrack->addDelegate(_muxer); } auto audioTrack = getTrack(TrackAudio, false); if (audioTrack) { - // 添加音频 + // 添加音频 [AUTO-TRANSLATED:e08e79ce] + // Add audio _muxer->addTrack(audioTrack); - // 音频数据写入_mediaMuxer + // 音频数据写入_mediaMuxer [AUTO-TRANSLATED:69911524] + // Write audio data to _mediaMuxer audioTrack->addDelegate(_muxer); } - // 添加完毕所有track,防止单track情况下最大等待3秒 + // 添加完毕所有track,防止单track情况下最大等待3秒 [AUTO-TRANSLATED:8908bc01] + // After adding all tracks, prevent the maximum waiting time of 3 seconds in the case of a single track _muxer->addTrackCompleted(); if (_media_src) { - // 让_muxer对象拦截一部分事件(比如说录像相关事件) + // 让_muxer对象拦截一部分事件(比如说录像相关事件) [AUTO-TRANSLATED:7d27c400] + // Let the _muxer object intercept some events (such as recording related events) _media_src->setListener(_muxer); } } diff --git a/src/Player/PlayerProxy.h b/src/Player/PlayerProxy.h index c484ca6a..a90d2b45 100644 --- a/src/Player/PlayerProxy.h +++ b/src/Player/PlayerProxy.h @@ -64,8 +64,10 @@ class PlayerProxy public: using Ptr = std::shared_ptr; - // 如果retry_count<0,则一直重试播放;否则重试retry_count次数 - // 默认一直重试 + // 如果retry_count<0,则一直重试播放;否则重试retry_count次数 [AUTO-TRANSLATED:5582d53c] + // If retry_count < 0, then retry playing indefinitely; otherwise, retry retry_count times + // 默认一直重试 [AUTO-TRANSLATED:779d3c46] + // Default to retrying indefinitely PlayerProxy(const MediaTuple &tuple, const ProtocolOption &option, int retry_count = -1, const toolkit::EventPoller::Ptr &poller = nullptr, int reconnect_delay_min = 2, int reconnect_delay_max = 60, int reconnect_delay_step = 3); @@ -75,35 +77,58 @@ public: /** * 设置play结果回调,只触发一次;在play执行之前有效 * @param cb 回调对象 + * Set a callback for the play result, triggered only once; effective before play execution + * @param cb Callback object + + * [AUTO-TRANSLATED:f34625f6] */ void setPlayCallbackOnce(std::function cb); /** * 设置主动关闭回调 * @param cb 回调对象 + * Set a callback for active closure + * @param cb Callback object + + * [AUTO-TRANSLATED:83b7700a] */ void setOnClose(std::function cb); /** * Set a callback for failed server connection * @param cb 回调对象 + * Set a callback for failed server connection + * @param cb Callback object + + * [AUTO-TRANSLATED:e7f5e7cc] */ void setOnDisconnect(std::function cb); /** * Set a callback for a successful connection to the server * @param cb 回调对象 + * Set a callback for a successful connection to the server + * @param cb Callback object + + * [AUTO-TRANSLATED:b88e0d4c] */ void setOnConnect(std::function cb); /** * 开始拉流播放 * @param strUrl + * Start streaming playback + * @param strUrl + + * [AUTO-TRANSLATED:a2f0e859] */ void play(const std::string &strUrl) override; /** * 获取观看总人数 + * Get the total number of viewers + + * [AUTO-TRANSLATED:6c1b8bf1] */ int totalReaderCount(); @@ -145,7 +170,8 @@ private: MultiMediaSourceMuxer::Ptr _muxer; toolkit::Ticker _live_ticker; - // 0 表示正常 1 表示正在尝试拉流 + // 0 表示正常 1 表示正在尝试拉流 [AUTO-TRANSLATED:2080bedf] + // 0 indicates normal, 1 indicates attempting to stream std::atomic _live_status; std::atomic _live_secs; diff --git a/src/Pusher/PusherBase.h b/src/Pusher/PusherBase.h index fa5b879b..859722ed 100644 --- a/src/Pusher/PusherBase.h +++ b/src/Pusher/PusherBase.h @@ -36,21 +36,34 @@ public: /** * 开始推流 * @param strUrl 视频url,支持rtsp/rtmp + * Start streaming + * @param strUrl Video url, supports rtsp/rtmp + + * [AUTO-TRANSLATED:d1decdf6] */ virtual void publish(const std::string &strUrl) {}; /** * 中断推流 + * Stop streaming + + * [AUTO-TRANSLATED:db8d228b] */ virtual void teardown() {}; /** * 摄像推流结果回调 + * Camera streaming result callback + + * [AUTO-TRANSLATED:33825a4d] */ virtual void setOnPublished(const Event &cb) = 0; /** * 设置断开回调 + * Set disconnect callback + + * [AUTO-TRANSLATED:b948082c] */ virtual void setOnShutdown(const Event &cb) = 0; @@ -70,6 +83,10 @@ public: /** * 开始推流 * @param url 推流url,支持rtsp/rtmp + * Start streaming + * @param url Streaming url, supports rtsp/rtmp + + * [AUTO-TRANSLATED:ffa95c22] */ void publish(const std::string &url) override { return _delegate ? _delegate->publish(url) : Parent::publish(url); @@ -77,6 +94,9 @@ public: /** * 中断推流 + * Stop streaming + + * [AUTO-TRANSLATED:db8d228b] */ void teardown() override { return _delegate ? _delegate->teardown() : Parent::teardown(); @@ -88,6 +108,9 @@ public: /** * 摄像推流结果回调 + * Camera streaming result callback + + * [AUTO-TRANSLATED:33825a4d] */ void setOnPublished(const PusherBase::Event &cb) override { if (_delegate) { @@ -98,6 +121,10 @@ public: /** * 设置断开回调 + * Set disconnect callback + + + * [AUTO-TRANSLATED:b948082c] */ void setOnShutdown(const PusherBase::Event &cb) override { if (_delegate) { diff --git a/src/Pusher/PusherProxy.cpp b/src/Pusher/PusherProxy.cpp index de22f302..397aadaa 100644 --- a/src/Pusher/PusherProxy.cpp +++ b/src/Pusher/PusherProxy.cpp @@ -54,18 +54,21 @@ void PusherProxy::publish(const string &dst_url) { auto src = strong_self->_weak_src.lock(); if (!err) { - // 推流成功 + // 推流成功 [AUTO-TRANSLATED:28ce6e56] + // Stream successfully pushed strong_self->_live_ticker.resetTime(); strong_self->_live_status = 0; *failed_cnt = 0; InfoL << "Publish " << dst_url << " success"; } else if (src && (*failed_cnt < strong_self->_retry_count || strong_self->_retry_count < 0)) { - // 推流失败,延时重试推送 + // 推流失败,延时重试推送 [AUTO-TRANSLATED:92b094ae] + // Stream failed, retry pushing with delay strong_self->_republish_count++; strong_self->_live_status = 1; strong_self->rePublish(dst_url, (*failed_cnt)++); } else { - // 如果媒体源已经注销, 或达到了最大重试次数,回调关闭 + // 如果媒体源已经注销, 或达到了最大重试次数,回调关闭 [AUTO-TRANSLATED:444adf27] + // If the media source has been deregistered, or the maximum retry count has been reached, callback to close strong_self->_on_close(err); } }); @@ -77,19 +80,22 @@ void PusherProxy::publish(const string &dst_url) { } if (*failed_cnt == 0) { - // 第一次重推更新时长 + // 第一次重推更新时长 [AUTO-TRANSLATED:5f778703] + // Update duration for the first re-push strong_self->_live_secs += strong_self->_live_ticker.elapsedTime() / 1000; strong_self->_live_ticker.resetTime(); TraceL << " live secs " << strong_self->_live_secs; } auto src = strong_self->_weak_src.lock(); - // 推流异常中断,延时重试播放 + // 推流异常中断,延时重试播放 [AUTO-TRANSLATED:e69e5a05] + // Stream abnormally interrupted, retry playing with delay if (src && (*failed_cnt < strong_self->_retry_count || strong_self->_retry_count < 0)) { strong_self->_republish_count++; strong_self->rePublish(dst_url, (*failed_cnt)++); } else { - // 如果媒体源已经注销, 或达到了最大重试次数,回调关闭 + // 如果媒体源已经注销, 或达到了最大重试次数,回调关闭 [AUTO-TRANSLATED:444adf27] + // If the media source has been deregistered, or the maximum retry count has been reached, callback to close strong_self->_on_close(err); } }); @@ -103,7 +109,8 @@ void PusherProxy::rePublish(const string &dst_url, int failed_cnt) { _timer = std::make_shared( delay / 1000.0f, [weak_self, dst_url, failed_cnt]() { - // 推流失败次数越多,则延时越长 + // 推流失败次数越多,则延时越长 [AUTO-TRANSLATED:bda77afe] + // The more times the stream fails, the longer the delay auto strong_self = weak_self.lock(); if (!strong_self) { return false; diff --git a/src/Pusher/PusherProxy.h b/src/Pusher/PusherProxy.h index 8a61488c..965096bb 100644 --- a/src/Pusher/PusherProxy.h +++ b/src/Pusher/PusherProxy.h @@ -22,26 +22,40 @@ class PusherProxy public: using Ptr = std::shared_ptr; - // 如果retry_count<0,则一直重试播放;否则重试retry_count次数 - // 默认一直重试,创建此对象时候,需要外部保证MediaSource存在 + // 如果retry_count<0,则一直重试播放;否则重试retry_count次数 [AUTO-TRANSLATED:5582d53c] + // If retry_count < 0, then retry playback indefinitely; otherwise, retry retry_count times + // 默认一直重试,创建此对象时候,需要外部保证MediaSource存在 [AUTO-TRANSLATED:c6159497] + // Default is to retry indefinitely. When creating this object, the external environment needs to ensure that MediaSource exists. PusherProxy(const MediaSource::Ptr &src, int retry_count = -1, const toolkit::EventPoller::Ptr &poller = nullptr); ~PusherProxy() override; /** * 设置push结果回调,只触发一次;在publish执行之前有效 * @param cb 回调对象 + * Set the push result callback, which is triggered only once; it is effective before publish is executed. + * @param cb Callback object + + * [AUTO-TRANSLATED:7cd775fb] */ void setPushCallbackOnce(const std::function &cb); /** * 设置主动关闭回调 * @param cb 回调对象 + * Set the active close callback + * @param cb Callback object + + * [AUTO-TRANSLATED:83b7700a] */ void setOnClose(const std::function &cb); /** * 开始拉流播放 * @param dstUrl 目标推流地址 + * Start pulling and playing the stream + * @param dstUrl Target push stream address + + * [AUTO-TRANSLATED:a9a5da08] */ void publish(const std::string &dstUrl) override; @@ -50,14 +64,16 @@ public: uint64_t getRePublishCount(); private: - // 重推逻辑函数 + // 重推逻辑函数 [AUTO-TRANSLATED:e0fa273c] + // Repush logic function void rePublish(const std::string &dstUrl, int iFailedCnt); private: int _retry_count; toolkit::Timer::Ptr _timer; toolkit::Ticker _live_ticker; - // 0 表示正常 1 表示正在尝试推流 + // 0 表示正常 1 表示正在尝试推流 [AUTO-TRANSLATED:acb9835e] + // 0 indicates normal, 1 indicates that the push stream is being attempted std::atomic _live_status; std::atomic _live_secs; std::atomic _republish_count; diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp index bc11028f..b26d3bcb 100644 --- a/src/Record/HlsMaker.cpp +++ b/src/Record/HlsMaker.cpp @@ -18,7 +18,8 @@ namespace mediakit { HlsMaker::HlsMaker(bool is_fmp4, float seg_duration, uint32_t seg_number, bool seg_keep) { _is_fmp4 = is_fmp4; - //最小允许设置为0,0个切片代表点播 + // 最小允许设置为0,0个切片代表点播 [AUTO-TRANSLATED:19235e8e] + // Minimum allowed setting is 0, 0 slices represent on-demand _seg_number = seg_number; _seg_duration = seg_duration; _seg_keep = seg_keep; @@ -96,21 +97,25 @@ void HlsMaker::inputInitSegment(const char *data, size_t len) { void HlsMaker::inputData(const char *data, size_t len, uint64_t timestamp, bool is_idr_fast_packet) { if (data && len) { if (timestamp < _last_timestamp) { - // 时间戳回退了,切片时长重新计时 + // 时间戳回退了,切片时长重新计时 [AUTO-TRANSLATED:fe91bd7f] + // Timestamp has been rolled back, slice duration is recalculated WarnL << "Timestamp reduce: " << _last_timestamp << " -> " << timestamp; _last_seg_timestamp = _last_timestamp = timestamp; } if (is_idr_fast_packet) { - // 尝试切片ts + // 尝试切片ts [AUTO-TRANSLATED:62264109] + // Attempt to slice ts addNewSegment(timestamp); } if (!_last_file_name.empty()) { - // 存在切片才写入ts数据 + // 存在切片才写入ts数据 [AUTO-TRANSLATED:ddd46115] + // Write ts data only if there are slices onWriteSegment(data, len); _last_timestamp = timestamp; } } else { - // resetTracks时触发此逻辑 + // resetTracks时触发此逻辑 [AUTO-TRANSLATED:0ba915ed] + // This logic is triggered when resetTracks is called flushLastSegment(false); } } @@ -118,19 +123,23 @@ void HlsMaker::inputData(const char *data, size_t len, uint64_t timestamp, bool void HlsMaker::delOldSegment() { GET_CONFIG(uint32_t, segDelay, Hls::kSegmentDelay); if (_seg_number == 0) { - //如果设置为保留0个切片,则认为是保存为点播 + // 如果设置为保留0个切片,则认为是保存为点播 [AUTO-TRANSLATED:5bf20108] + // If set to keep 0 slices, it is considered to be saved as on-demand return; } - //在hls m3u8索引文件中,我们保存的切片个数跟_seg_number相关设置一致 + // 在hls m3u8索引文件中,我们保存的切片个数跟_seg_number相关设置一致 [AUTO-TRANSLATED:b14b5b98] + // In the hls m3u8 index file, the number of slices we save is consistent with the _seg_number setting if (_file_index > _seg_number + segDelay) { _seg_dur_list.pop_front(); } - //如果设置为一直保存,就不删除 + // 如果设置为一直保存,就不删除 [AUTO-TRANSLATED:7c622e24] + // If set to always save, it will not be deleted if (_seg_keep) { return; } GET_CONFIG(uint32_t, segRetain, Hls::kSegmentRetain); - //但是实际保存的切片个数比m3u8所述多若干个,这样做的目的是防止播放器在切片删除前能下载完毕 + // 但是实际保存的切片个数比m3u8所述多若干个,这样做的目的是防止播放器在切片删除前能下载完毕 [AUTO-TRANSLATED:1688f857] + // However, the actual number of slices saved is a few more than what is stated in the m3u8, this is done to prevent the player from downloading the slices before they are deleted if (_file_index > _seg_number + segDelay + segRetain) { onDelSegment(_file_index - _seg_number - segDelay - segRetain - 1); } @@ -139,35 +148,44 @@ void HlsMaker::delOldSegment() { void HlsMaker::addNewSegment(uint64_t stamp) { GET_CONFIG(bool, fastRegister, Hls::kFastRegister); if (_file_index > fastRegister && stamp - _last_seg_timestamp < _seg_duration * 1000) { - //确保序号为0的切片立即open,如果开启快速注册功能,序号为1的切片也应该遇到关键帧立即生成;否则需要等切片时长够长 + // 确保序号为0的切片立即open,如果开启快速注册功能,序号为1的切片也应该遇到关键帧立即生成;否则需要等切片时长够长 [AUTO-TRANSLATED:d81d1a1c] + // Ensure that the slice with sequence number 0 is opened immediately, if the fast registration function is enabled, the slice with sequence number 1 should also be generated immediately when it encounters a keyframe; otherwise, it needs to wait until the slice duration is long enough return; } - //关闭并保存上一个切片,如果_seg_number==0,那么是点播。 + // 关闭并保存上一个切片,如果_seg_number==0,那么是点播。 [AUTO-TRANSLATED:14076b61] + // Close and save the previous slice, if _seg_number==0, then it is on-demand. flushLastSegment(false); - //新增切片 + // 新增切片 [AUTO-TRANSLATED:b8623419] + // Add a new slice _last_file_name = onOpenSegment(_file_index++); - //记录本次切片的起始时间戳 + // 记录本次切片的起始时间戳 [AUTO-TRANSLATED:8eb776e9] + // Record the starting timestamp of this slice _last_seg_timestamp = _last_timestamp ? _last_timestamp : stamp; } void HlsMaker::flushLastSegment(bool eof){ GET_CONFIG(uint32_t, segDelay, Hls::kSegmentDelay); if (_last_file_name.empty()) { - //不存在上个切片 + // 不存在上个切片 [AUTO-TRANSLATED:d81fe08e] + // There is no previous slice return; } - //文件创建到最后一次数据写入的时间即为切片长度 + // 文件创建到最后一次数据写入的时间即为切片长度 [AUTO-TRANSLATED:1f85739c] + // The time from file creation to the last data write is the slice length auto seg_dur = _last_timestamp - _last_seg_timestamp; if (seg_dur <= 0) { seg_dur = 100; } _seg_dur_list.emplace_back(seg_dur, std::move(_last_file_name)); delOldSegment(); - //先flush ts切片,否则可能存在ts文件未写入完毕就被访问的情况 + // 先flush ts切片,否则可能存在ts文件未写入完毕就被访问的情况 [AUTO-TRANSLATED:f8d6dc87] + // Flush the ts slice first, otherwise there may be a situation where the ts file is not written completely before it is accessed onFlushLastSegment(seg_dur); - //然后写m3u8文件 + // 然后写m3u8文件 [AUTO-TRANSLATED:67200ce1] + // Then write the m3u8 file makeIndexFile(false, eof); - //写入切片延迟的m3u8文件 + // 写入切片延迟的m3u8文件 [AUTO-TRANSLATED:b1f12e43] + // Write the m3u8 file with slice delay if (segDelay) { makeIndexFile(true, eof); } diff --git a/src/Record/HlsMaker.h b/src/Record/HlsMaker.h index a95e2296..05621b0f 100644 --- a/src/Record/HlsMaker.h +++ b/src/Record/HlsMaker.h @@ -25,6 +25,12 @@ public: * @param seg_duration 切片文件长度 * @param seg_number 切片个数 * @param seg_keep 是否保留切片文件 + * @param is_fmp4 Use fmp4 or mpegts + * @param seg_duration Segment file length + * @param seg_number Number of segments + * @param seg_keep Whether to keep the segment file + + * [AUTO-TRANSLATED:260bbca3] */ HlsMaker(bool is_fmp4 = false, float seg_duration = 5, uint32_t seg_number = 3, bool seg_keep = false); virtual ~HlsMaker() = default; @@ -35,6 +41,13 @@ public: * @param len 数据长度 * @param timestamp 毫秒时间戳 * @param is_idr_fast_packet 是否为关键帧第一个包 + * Write ts data + * @param data Data + * @param len Data length + * @param timestamp Millisecond timestamp + * @param is_idr_fast_packet Whether it is the first packet of the key frame + + * [AUTO-TRANSLATED:b886bbbf] */ void inputData(const char *data, size_t len, uint64_t timestamp, bool is_idr_fast_packet); @@ -42,26 +55,43 @@ public: * 输入fmp4 init segment * @param data 数据 * @param len 数据长度 + * Input fmp4 init segment + * @param data Data + * @param len Data length + + * [AUTO-TRANSLATED:8d613a42] */ void inputInitSegment(const char *data, size_t len); /** * 是否为直播 + * Whether it is live + + * [AUTO-TRANSLATED:1dae0496] */ bool isLive() const; /** * 是否保留切片文件 + * Whether to keep the segment file + + * [AUTO-TRANSLATED:c2d1bce5] */ bool isKeep() const; /** * 是否采用fmp4切片还是mpegts + * Whether to use fmp4 segmentation or mpegts + + * [AUTO-TRANSLATED:36763fc8] */ bool isFmp4() const; /** * 清空记录 + * Clear records + + * [AUTO-TRANSLATED:34a4b6cd] */ void clear(); @@ -70,12 +100,21 @@ protected: * 创建ts切片文件回调 * @param index * @return + * Create ts segment file callback + * @param index + * @return + + * [AUTO-TRANSLATED:2a3806fc] */ virtual std::string onOpenSegment(uint64_t index) = 0; /** * 删除ts切片文件回调 * @param index + * Delete ts segment file callback + * @param index + + * [AUTO-TRANSLATED:1c0d4397] */ virtual void onDelSegment(uint64_t index) = 0; @@ -83,6 +122,11 @@ protected: * 写init.mp4切片文件回调 * @param data * @param len + * Write init.mp4 segment file callback + * @param data + * @param len + + * [AUTO-TRANSLATED:e0021ec5] */ virtual void onWriteInitSegment(const char *data, size_t len) = 0; @@ -90,23 +134,39 @@ protected: * 写ts切片文件回调 * @param data * @param len + * Write ts segment file callback + * @param data + * @param len + + * [AUTO-TRANSLATED:bb81e206] */ virtual void onWriteSegment(const char *data, size_t len) = 0; /** * 写m3u8文件回调 + * Write m3u8 file callback + + * [AUTO-TRANSLATED:5754525f] */ virtual void onWriteHls(const std::string &data, bool include_delay) = 0; /** * 上一个 ts 切片写入完成, 可在这里进行通知处理 * @param duration_ms 上一个 ts 切片的时长, 单位为毫秒 + * The previous ts segment is written, you can notify here + * @param duration_ms The duration of the previous ts segment, in milliseconds + + * [AUTO-TRANSLATED:36b42bc0] */ virtual void onFlushLastSegment(uint64_t duration_ms) {}; /** * 关闭上个ts切片并且写入m3u8索引 * @param eof HLS直播是否已结束 + * Close the previous ts segment and write the m3u8 index + * @param eof Whether the HLS live broadcast has ended + + * [AUTO-TRANSLATED:614b7e14] */ void flushLastSegment(bool eof); @@ -114,17 +174,28 @@ private: /** * 生成m3u8文件 * @param eof true代表点播 + * Generate m3u8 file + * @param eof true represents on-demand + + * [AUTO-TRANSLATED:d6c74fb6] */ void makeIndexFile(bool include_delay, bool eof = false); /** * 删除旧的ts切片 + * Delete old ts segments + + * [AUTO-TRANSLATED:5da8bd70] */ void delOldSegment(); /** * 添加新的ts切片 * @param timestamp + * Add new ts segments + * @param timestamp + + * [AUTO-TRANSLATED:e321e9f0] */ void addNewSegment(uint64_t timestamp); diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index cbd19fb7..002902f4 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -43,7 +43,8 @@ HlsMakerImp::HlsMakerImp(bool is_fmp4, const string &m3u8_file, const string &pa HlsMakerImp::~HlsMakerImp() { try { - // 可能hls注册时导致抛异常 + // 可能hls注册时导致抛异常 [AUTO-TRANSLATED:82add30d] + // Possible exception thrown during hls registration clearCache(false, true); } catch (std::exception &ex) { WarnL << ex.what(); @@ -62,7 +63,8 @@ static void clearHls(const std::list &files) { } void HlsMakerImp::clearCache(bool immediately, bool eof) { - // 录制完了 + // 录制完了 [AUTO-TRANSLATED:5d3bfbeb] + // Recording finished flushLastSegment(eof); if (!isLive() || isKeep()) { return; @@ -79,7 +81,8 @@ void HlsMakerImp::clearCache(bool immediately, bool eof) { lst.emplace_back(std::move(pr.second)); } - // hls直播才删除文件 + // hls直播才删除文件 [AUTO-TRANSLATED:81d2aaa5] + // Delete file only after hls live streaming GET_CONFIG(uint32_t, delay, Hls::kDeleteDelaySec); if (!delay || immediately) { clearHls(lst); @@ -110,7 +113,8 @@ string HlsMakerImp::onOpenSegment(uint64_t index) { } _file = makeFile(segment_path, true); - // 保存本切片的元数据 + // 保存本切片的元数据 [AUTO-TRANSLATED:64e6f692] + // Save metadata for this slice _info.start_time = ::time(NULL); _info.file_name = segment_name; _info.file_path = segment_path; @@ -171,7 +175,8 @@ void HlsMakerImp::onWriteHls(const std::string &data, bool include_delay) { } void HlsMakerImp::onFlushLastSegment(uint64_t duration_ms) { - // 关闭并flush文件到磁盘 + // 关闭并flush文件到磁盘 [AUTO-TRANSLATED:9798ec4d] + // Close and flush file to disk _file = nullptr; GET_CONFIG(bool, broadcastRecordTs, Hls::kBroadcastRecordTs); diff --git a/src/Record/HlsMakerImp.h b/src/Record/HlsMakerImp.h index 1485e8d3..e82be46b 100644 --- a/src/Record/HlsMakerImp.h +++ b/src/Record/HlsMakerImp.h @@ -27,17 +27,28 @@ public: /** * 设置媒体信息 + * Set media information + + * [AUTO-TRANSLATED:d205db9f] */ void setMediaSource(const MediaTuple& tuple); /** * 获取MediaSource * @return + * Get MediaSource + * @return + + * [AUTO-TRANSLATED:af916433] */ HlsMediaSource::Ptr getMediaSource() const; /** * 清空缓存 + * Clear cache + + + * [AUTO-TRANSLATED:f872d7e2] */ void clearCache(); diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index 382e2779..e1d9b127 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -30,7 +30,8 @@ void HlsCookieData::addReaderCount() { _ring_reader = src->getRing()->attach(EventPollerPool::Instance().getPoller()); auto added = _added; _ring_reader->setDetachCB([added]() { - // HlsMediaSource已经销毁 + // HlsMediaSource已经销毁 [AUTO-TRANSLATED:bedb0385] + // HlsMediaSource has been destroyed *added = false; }); auto info = _sock_info; @@ -90,7 +91,8 @@ void HlsMediaSource::setIndexFile(std::string index_file) regist(); } - //赋值m3u8索引文件内容 + // 赋值m3u8索引文件内容 [AUTO-TRANSLATED:c11882b5] + // Assign m3u8 index file content std::lock_guard lck(_mtx_index); _index_file = std::move(index_file); @@ -107,7 +109,8 @@ void HlsMediaSource::getIndexFile(std::function cb cb(_index_file); return; } - //等待生成m3u8文件 + // 等待生成m3u8文件 [AUTO-TRANSLATED:c3ae3286] + // Waiting for m3u8 file generation _list_cb.emplace_back(std::move(cb)); } diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index f9c4c56d..53f74520 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -29,26 +29,42 @@ public: /** * 获取媒体源的环形缓冲 + * Get the circular buffer of the media source + + * [AUTO-TRANSLATED:75ac76b6] */ const RingType::Ptr &getRing() const { return _ring; } /** * 获取播放器个数 + * Get the number of players + + * [AUTO-TRANSLATED:a451c846] */ int readerCount() override { return _ring ? _ring->readerCount() : 0; } /** * 设置或清空m3u8索引文件内容 + * Set or clear the m3u8 index file content + + * [AUTO-TRANSLATED:71db921d] */ void setIndexFile(std::string index_file); /** * 异步获取m3u8文件 + * Asynchronously get the m3u8 file + + * [AUTO-TRANSLATED:e962b3ad] */ void getIndexFile(std::function cb); /** * 同步获取m3u8文件 + * Synchronously get the m3u8 file + + + * [AUTO-TRANSLATED:52b228df] */ std::string getIndexFile() const { std::lock_guard lck(_mtx_index); diff --git a/src/Record/HlsRecorder.h b/src/Record/HlsRecorder.h index bbd869b1..1810aec5 100644 --- a/src/Record/HlsRecorder.h +++ b/src/Record/HlsRecorder.h @@ -29,7 +29,8 @@ public: _option = option; _hls = std::make_shared(is_fmp4, m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep); - // 清空上次的残余文件 + // 清空上次的残余文件 [AUTO-TRANSLATED:e16122be] + // Clear the residual files from the last time _hls->clearCache(); } @@ -45,10 +46,12 @@ public: int readerCount() { return _hls->getMediaSource()->readerCount(); } void onReaderChanged(MediaSource &sender, int size) override { - // hls保留切片个数为0时代表为hls录制(不删除切片),那么不管有无观看者都一直生成hls + // hls保留切片个数为0时代表为hls录制(不删除切片),那么不管有无观看者都一直生成hls [AUTO-TRANSLATED:55709255] + // When the number of hls slices is 0, it means hls recording (not deleting slices), so hls is generated all the time regardless of whether there are viewers _enabled = _option.hls_demand ? (_hls->isLive() ? size : true) : true; if (!size && _hls->isLive() && _option.hls_demand) { - // hls直播时,如果无人观看就删除视频缓存,目的是为了防止视频跳跃 + // hls直播时,如果无人观看就删除视频缓存,目的是为了防止视频跳跃 [AUTO-TRANSLATED:1d875c6a] + // When hls is live, if no one is watching, delete the video cache to prevent video jumping _clear_cache = true; } MediaSourceEventInterceptor::onReaderChanged(sender, size); @@ -57,7 +60,8 @@ public: bool inputFrame(const Frame::Ptr &frame) override { if (_clear_cache && _option.hls_demand) { _clear_cache = false; - //清空旧的m3u8索引文件于ts切片 + // 清空旧的m3u8索引文件于ts切片 [AUTO-TRANSLATED:a4ce0664] + // Clear the old m3u8 index file and ts slices _hls->clearCache(); _hls->getMediaSource()->setIndexFile(""); } @@ -68,7 +72,8 @@ public: } bool isEnabled() { - //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + // 缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 [AUTO-TRANSLATED:7cfd4d49] + // When the cache has not been cleared, it is still allowed to trigger the inputFrame function to clear the cache in time return _option.hls_demand ? (_clear_cache ? true : _enabled) : true; } diff --git a/src/Record/MP4.cpp b/src/Record/MP4.cpp index 793c7794..f9222899 100644 --- a/src/Record/MP4.cpp +++ b/src/Record/MP4.cpp @@ -42,7 +42,8 @@ static struct mov_buffer_t s_io = { MP4FileIO::Writer MP4FileIO::createWriter(int flags, bool is_fmp4){ Writer writer; Ptr self = shared_from_this(); - //保存自己的强引用,防止提前释放 + // 保存自己的强引用,防止提前释放 [AUTO-TRANSLATED:e8e14f60] + // Save a strong reference to itself to prevent premature release writer.reset(mp4_writer_create(is_fmp4, &s_io,this, flags),[self](mp4_writer_t *ptr){ if(ptr){ mp4_writer_destroy(ptr); @@ -57,7 +58,8 @@ MP4FileIO::Writer MP4FileIO::createWriter(int flags, bool is_fmp4){ MP4FileIO::Reader MP4FileIO::createReader(){ Reader reader; Ptr self = shared_from_this(); - //保存自己的强引用,防止提前释放 + // 保存自己的强引用,防止提前释放 [AUTO-TRANSLATED:e8e14f60] + // Save a strong reference to itself to prevent premature release reader.reset(mov_reader_create(&s_io,this),[self](mov_reader_t *ptr){ if(ptr){ mov_reader_destroy(ptr); @@ -80,7 +82,8 @@ MP4FileIO::Reader MP4FileIO::createReader(){ #endif void MP4FileDisk::openFile(const char *file, const char *mode) { - //创建文件 + // 创建文件 [AUTO-TRANSLATED:bd145ed5] + // Create a file auto fp = File::create_file(file, mode); if(!fp){ throw std::runtime_error(string("打开文件失败:") + file); @@ -88,7 +91,8 @@ void MP4FileDisk::openFile(const char *file, const char *mode) { GET_CONFIG(uint32_t,mp4BufSize,Record::kFileBufSize); - //新建文件io缓存 + // 新建文件io缓存 [AUTO-TRANSLATED:fda9ff47] + // Create a new file io cache std::shared_ptr file_buf(new char[mp4BufSize],[](char *ptr){ if(ptr){ delete [] ptr; @@ -96,11 +100,13 @@ void MP4FileDisk::openFile(const char *file, const char *mode) { }); if(file_buf){ - //设置文件io缓存 + // 设置文件io缓存 [AUTO-TRANSLATED:0ed9c8ad] + // Set the file io cache setvbuf(fp, file_buf.get(), _IOFBF, mp4BufSize); } - //创建智能指针 + // 创建智能指针 [AUTO-TRANSLATED:e7920ab2] + // Create a smart pointer _file.reset(fp,[file_buf](FILE *fp) { fflush(fp); fclose(fp); @@ -168,7 +174,8 @@ int MP4FileMemory::onRead(void *data, size_t bytes){ int MP4FileMemory::onWrite(const void *data, size_t bytes){ if (_offset + bytes > _memory.size()) { - //需要扩容 + // 需要扩容 [AUTO-TRANSLATED:211c91e3] + // Need to expand _memory.resize(_offset + bytes); } memcpy((uint8_t *) _memory.data() + _offset, data, bytes); diff --git a/src/Record/MP4.h b/src/Record/MP4.h index 4b150170..cc815e89 100644 --- a/src/Record/MP4.h +++ b/src/Record/MP4.h @@ -26,7 +26,8 @@ namespace mediakit { -//mp4文件IO的抽象接口类 +// mp4文件IO的抽象接口类 [AUTO-TRANSLATED:dab24105] +// Abstract interface class for mp4 file IO class MP4FileIO : public std::enable_shared_from_this { public: using Ptr = std::shared_ptr; @@ -40,17 +41,30 @@ public: * @param flags 支持0、MOV_FLAG_FASTSTART、MOV_FLAG_SEGMENT * @param is_fmp4 是否为fmp4还是普通mp4 * @return mp4复用器 + * Create an mp4 muxer + * @param flags Supports 0, MOV_FLAG_FASTSTART, MOV_FLAG_SEGMENT + * @param is_fmp4 Whether it is fmp4 or ordinary mp4 + * @return mp4 muxer + + * [AUTO-TRANSLATED:97fefe95] */ virtual Writer createWriter(int flags, bool is_fmp4 = false); /** * 创建mp4解复用器 * @return mp4解复用器 + * Create an mp4 demuxer + * @return mp4 demuxer + + * [AUTO-TRANSLATED:4a303019] */ virtual Reader createReader(); /** * 获取文件读写位置 + * Get the file read/write position + + * [AUTO-TRANSLATED:f8a5b290] */ virtual uint64_t onTell() = 0; @@ -58,6 +72,11 @@ public: * seek至文件某处 * @param offset 文件偏移量 * @return 是否成功(0成功) + * Seek to a certain location in the file + * @param offset File offset + * @return Whether it is successful (0 successful) + + * [AUTO-TRANSLATED:936089eb] */ virtual int onSeek(uint64_t offset) = 0; @@ -66,6 +85,12 @@ public: * @param data 数据存放指针 * @param bytes 指针长度 * @return 是否成功(0成功) + * Read a certain amount of data from the file + * @param data Data storage pointer + * @param bytes Pointer length + * @return Whether it is successful (0 successful) + + * [AUTO-TRANSLATED:926bf3f0] */ virtual int onRead(void *data, size_t bytes) = 0; @@ -74,11 +99,18 @@ public: * @param data 数据指针 * @param bytes 数据长度 * @return 是否成功(0成功) + * Write a certain amount of data to the file + * @param data Data pointer + * @param bytes Data length + * @return Whether it is successful (0 successful) + + * [AUTO-TRANSLATED:dc0abb95] */ virtual int onWrite(const void *data, size_t bytes) = 0; }; -//磁盘MP4文件类 +// 磁盘MP4文件类 [AUTO-TRANSLATED:e3f5ac07] +// Disk MP4 file class class MP4FileDisk : public MP4FileIO { public: using Ptr = std::shared_ptr; @@ -87,11 +119,19 @@ public: * 打开磁盘文件 * @param file 文件路径 * @param mode fopen的方式 + * Open the disk file + * @param file File path + * @param mode fopen mode + + * [AUTO-TRANSLATED:c3144f10] */ void openFile(const char *file, const char *mode); /** * 关闭磁盘文件 + * Close the disk file + + * [AUTO-TRANSLATED:fc6b4f50] */ void closeFile(); @@ -111,11 +151,18 @@ public: /** * 获取文件大小 + * Get the file size + + * [AUTO-TRANSLATED:3a2b682a] */ size_t fileSize() const; /** * 获取并清空文件缓存 + * Get and clear the file cache + + + * [AUTO-TRANSLATED:620d5cf6] */ std::string getAndClearMemory(); diff --git a/src/Record/MP4Demuxer.h b/src/Record/MP4Demuxer.h index 889f91fc..87cce6a2 100644 --- a/src/Record/MP4Demuxer.h +++ b/src/Record/MP4Demuxer.h @@ -25,11 +25,18 @@ public: /** * 打开文件 * @param file mp4文件路径 + * Open file + * @param file mp4 file path + + * [AUTO-TRANSLATED:a64c5a6b] */ void openMP4(const std::string &file); /** * @brief 关闭 mp4 文件 + * @brief Close mp4 file + + * [AUTO-TRANSLATED:527865d9] */ void closeMP4(); @@ -37,6 +44,11 @@ public: * 移动时间轴至某处 * @param stamp_ms 预期的时间轴位置,单位毫秒 * @return 时间轴位置 + * Move timeline to a specific location + * @param stamp_ms Expected timeline position, in milliseconds + * @return Timeline position + + * [AUTO-TRANSLATED:51ce0f6d] */ int64_t seekTo(int64_t stamp_ms); @@ -45,6 +57,12 @@ public: * @param keyFrame 是否为关键帧 * @param eof 是否文件读取完毕 * @return 帧数据,可能为空 + * Read a frame of data + * @param keyFrame Whether it is a key frame + * @param eof Whether the file has been read completely + * @return Frame data, may be empty + + * [AUTO-TRANSLATED:adf550de] */ Frame::Ptr readFrame(bool &keyFrame, bool &eof); @@ -52,12 +70,22 @@ public: * 获取所有Track信息 * @param trackReady 是否要求track为就绪状态 * @return 所有Track + * Get all Track information + * @param trackReady Whether to require the track to be ready + * @return All Tracks + + * [AUTO-TRANSLATED:c07ad51a] */ std::vector getTracks(bool trackReady) const override; /** * 获取文件长度 * @return 文件长度,单位毫秒 + * Get file length + * @return File length, in milliseconds + + + * [AUTO-TRANSLATED:dcd865d6] */ uint64_t getDurationMS() const; diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 6d686b50..ffa3295f 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -85,21 +85,26 @@ void MP4MuxerInterface::flush() { bool MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) { auto it = _tracks.find(frame->getIndex()); if (it == _tracks.end()) { - // 该Track不存在或初始化失败 + // 该Track不存在或初始化失败 [AUTO-TRANSLATED:316597dc] + // This Track does not exist or initialization failed return false; } if (!_started) { - // 该逻辑确保含有视频时,第一帧为关键帧 + // 该逻辑确保含有视频时,第一帧为关键帧 [AUTO-TRANSLATED:04f177fb] + // This logic ensures that the first frame is a keyframe when there is video if (_have_video && !frame->keyFrame()) { - // 含有视频,但是不是关键帧,那么前面的帧丢弃 + // 含有视频,但是不是关键帧,那么前面的帧丢弃 [AUTO-TRANSLATED:5f0ba99e] + // Contains video, but not a keyframe, then the previous frames are discarded return false; } - // 开始写文件 + // 开始写文件 [AUTO-TRANSLATED:bc3f11e2] + // Start writing the file _started = true; } - // fmp4封装超过一定I帧间隔,强制刷新segment,防止内存上涨 + // fmp4封装超过一定I帧间隔,强制刷新segment,防止内存上涨 [AUTO-TRANSLATED:0be6ef15] + // fmp4 encapsulation exceeds a certain I-frame interval, force refresh segment to prevent memory increase if (frame->getTrackType() == TrackVideo && _mov_writter->fmp4) { if (frame->keyFrame()) { _non_iframe_video_count = 0; @@ -113,12 +118,14 @@ bool MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) { } } - // mp4文件时间戳需要从0开始 + // mp4文件时间戳需要从0开始 [AUTO-TRANSLATED:c963b841] + // The mp4 file timestamp needs to start from 0 auto &track = it->second; switch (frame->getCodecId()) { case CodecH264: case CodecH265: { - // 这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理, + // 这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理, [AUTO-TRANSLATED:edf57c32] + // The code logic here is to package frames with the same timestamp, such as SPS, PPS, and IDR, as one frame, track.merger.inputFrame(frame, [this, &track](uint64_t dts, uint64_t pts, const Buffer::Ptr &buffer, bool have_idr) { int64_t dts_out, pts_out; track.stamp.revise(dts, pts, dts_out, pts_out); @@ -190,7 +197,8 @@ bool MP4MuxerInterface::addTrack(const Track::Ptr &track) { _tracks[track->getIndex()].track_id = track_id; } - // 尝试音视频同步 + // 尝试音视频同步 [AUTO-TRANSLATED:5f8b8040] + // Try audio and video synchronization stampSync(); return true; } @@ -222,16 +230,19 @@ void MP4MuxerMemory::resetTracks() { bool MP4MuxerMemory::inputFrame(const Frame::Ptr &frame) { if (_init_segment.empty()) { - // 尚未生成init segment + // 尚未生成init segment [AUTO-TRANSLATED:b4baa65f] + // Init segment has not been generated yet return false; } - // flush切片 + // flush切片 [AUTO-TRANSLATED:c4358dce] + // Flush segment saveSegment(); auto data = _memory_file->getAndClearMemory(); if (!data.empty()) { - // 输出切片数据 + // 输出切片数据 [AUTO-TRANSLATED:4bc994c9] + // Output segment data onSegmentData(std::move(data), _last_dst, _key_frame); _key_frame = false; } diff --git a/src/Record/MP4Muxer.h b/src/Record/MP4Muxer.h index f0d7fce1..1a143d6f 100644 --- a/src/Record/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -24,41 +24,65 @@ public: /** * 添加已经ready状态的track + * Add tracks that are in ready state + + * [AUTO-TRANSLATED:ea4983df] */ bool addTrack(const Track::Ptr &track) override; /** * 输入帧 + * Input frame + + * [AUTO-TRANSLATED:c91b5ec6] */ bool inputFrame(const Frame::Ptr &frame) override; /** * 重置所有track + * Reset all tracks + + * [AUTO-TRANSLATED:f203fa3e] */ void resetTracks() override; /** * 刷新输出所有frame缓存 + * Refresh all frame cache output + + * [AUTO-TRANSLATED:adaea568] */ void flush() override; /** * 是否包含视频 + * Whether it contains video + + * [AUTO-TRANSLATED:6d9e1039] */ bool haveVideo() const; /** * 保存fmp4分片 + * Save fmp4 fragment + + * [AUTO-TRANSLATED:7b808759] */ void saveSegment(); /** * 创建新切片 + * Create new fragment + + * [AUTO-TRANSLATED:b27545cf] */ void initSegment(); /** * 获取mp4时长,单位毫秒 + * Get mp4 duration, in milliseconds + + * [AUTO-TRANSLATED:d87afcfb] */ uint64_t getDuration() const; @@ -93,17 +117,27 @@ public: ~MP4Muxer() override; /** * 重置所有track + * Reset all tracks + + * [AUTO-TRANSLATED:f203fa3e] */ void resetTracks() override; /** * 打开mp4 * @param file 文件完整路径 + * Open mp4 + * @param file Full file path + + * [AUTO-TRANSLATED:416892f4] */ void openMP4(const std::string &file); /** * 手动关闭文件(对象析构时会自动关闭) + * Manually close the file (it will be closed automatically when the object is destructed) + + * [AUTO-TRANSLATED:9ca68ff9] */ void closeMP4(); @@ -121,16 +155,25 @@ public: /** * 重置所有track + * Reset all tracks + + * [AUTO-TRANSLATED:f203fa3e] */ void resetTracks() override; /** * 输入帧 + * Input frame + + * [AUTO-TRANSLATED:c91b5ec6] */ bool inputFrame(const Frame::Ptr &frame) override; /** * 获取fmp4 init segment + * Get fmp4 init segment + + * [AUTO-TRANSLATED:6c704ec9] */ const std::string &getInitSegment(); @@ -140,6 +183,12 @@ protected: * @param std::string 切片内容 * @param stamp 切片末尾时间戳 * @param key_frame 是否有关键帧 + * Output fmp4 fragment callback function + * @param std::string Fragment content + * @param stamp Fragment end timestamp + * @param key_frame Whether there is a key frame + + * [AUTO-TRANSLATED:dd742da5] */ virtual void onSegmentData(std::string string, uint64_t stamp, bool key_frame) = 0; @@ -173,6 +222,13 @@ protected: * @param std::string 切片内容 * @param stamp 切片末尾时间戳 * @param key_frame 是否有关键帧 + * Output fmp4 fragment callback function + * @param std::string Fragment content + * @param stamp Fragment end timestamp + * @param key_frame Whether there is a key frame + + + * [AUTO-TRANSLATED:dd742da5] */ virtual void onSegmentData(std::string string, uint64_t stamp, bool key_frame) = 0; }; diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 146b70b1..55941be4 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -23,11 +23,13 @@ namespace mediakit { MP4Reader::MP4Reader(const MediaTuple &tuple, const string &file_path, toolkit::EventPoller::Ptr poller) { ProtocolOption option; - // 读取mp4文件并流化时,不重复生成mp4/hls文件 + // 读取mp4文件并流化时,不重复生成mp4/hls文件 [AUTO-TRANSLATED:5d414546] + // Read mp4 file and stream it, do not regenerate mp4/hls file repeatedly option.enable_mp4 = false; option.enable_hls = false; option.enable_hls_fmp4 = false; - // mp4支持多track + // mp4支持多track [AUTO-TRANSLATED:b9688762] + // mp4 supports multiple tracks option.max_track = 16; setup(tuple, file_path, option, std::move(poller)); } @@ -37,7 +39,8 @@ MP4Reader::MP4Reader(const MediaTuple &tuple, const string &file_path, const Pro } void MP4Reader::setup(const MediaTuple &tuple, const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller) { - //读写文件建议放在后台线程 + // 读写文件建议放在后台线程 [AUTO-TRANSLATED:6f09ef53] + // It is recommended to read and write files in the background thread _poller = poller ? std::move(poller) : WorkThreadPool::Instance().getPoller(); _file_path = file_path; if (_file_path.empty()) { @@ -69,13 +72,15 @@ void MP4Reader::setup(const MediaTuple &tuple, const std::string &file_path, con _have_video = true; } } - //添加完毕所有track,防止单track情况下最大等待3秒 + // 添加完毕所有track,防止单track情况下最大等待3秒 [AUTO-TRANSLATED:445e3403] + // After all tracks are added, prevent the maximum waiting time of 3 seconds in the case of a single track _muxer->addTrackCompleted(); } bool MP4Reader::readSample() { if (_paused) { - //确保暂停时,时间轴不走动 + // 确保暂停时,时间轴不走动 [AUTO-TRANSLATED:3d38dd31] + // Ensure that the timeline does not move when paused _seek_ticker.resetTime(); return true; } @@ -95,7 +100,8 @@ bool MP4Reader::readSample() { GET_CONFIG(bool, file_repeat, Record::kFileRepeat); if (eof && (file_repeat || _file_repeat)) { - //需要从头开始看 + // 需要从头开始看 [AUTO-TRANSLATED:5b563a35] + // Need to start from the beginning seekTo(0); return true; } @@ -126,15 +132,18 @@ void MP4Reader::startReadMP4(uint64_t sample_ms, bool ref_self, bool file_repeat setCurrentStamp(0); auto strong_self = shared_from_this(); if (_muxer) { - //一直读到所有track就绪为止 + // 一直读到所有track就绪为止 [AUTO-TRANSLATED:410f9ecc] + // Keep reading until all tracks are ready while (!_muxer->isAllTrackReady() && readNextSample()); - //注册后再切换OwnerPoller + // 注册后再切换OwnerPoller [AUTO-TRANSLATED:4a483e23] + // Register and then switch OwnerPoller _muxer->setMediaListener(strong_self); } auto timer_sec = (sample_ms ? sample_ms : sampleMS) / 1000.0f; - //启动定时器 + // 启动定时器 [AUTO-TRANSLATED:0b93ed77] + // Start the timer if (ref_self) { _timer = std::make_shared(timer_sec, [strong_self]() { lock_guard lck(strong_self->_mtx); @@ -169,13 +178,15 @@ void MP4Reader::setCurrentStamp(uint32_t new_stamp) { _last_dts = new_stamp; _seek_ticker.resetTime(); if (old_stamp != new_stamp && _muxer) { - //时间轴未拖动时不操作 + // 时间轴未拖动时不操作 [AUTO-TRANSLATED:c5b53103] + // Do not operate when the timeline is not dragged _muxer->setTimeStamp(new_stamp); } } bool MP4Reader::seekTo(MediaSource &sender, uint32_t stamp) { - //拖动进度条后应该恢复播放 + // 拖动进度条后应该恢复播放 [AUTO-TRANSLATED:8a6d11f7] + // Playback should resume after dragging the progress bar pause(sender, false); TraceL << getOriginUrl(sender) << ",stamp:" << stamp; return seekTo(stamp); @@ -185,7 +196,8 @@ bool MP4Reader::pause(MediaSource &sender, bool pause) { if (_paused == pause) { return true; } - //_seek_ticker重新计时,不管是暂停还是seek都不影响总的播放进度 + // _seek_ticker重新计时,不管是暂停还是seek都不影响总的播放进度 [AUTO-TRANSLATED:96051076] + // _seek_ticker restarts the timer, whether it is paused or seek does not affect the total playback progress setCurrentStamp(getCurrentStamp()); _paused = pause; TraceL << getOriginUrl(sender) << ",pause:" << pause; @@ -197,9 +209,11 @@ bool MP4Reader::speed(MediaSource &sender, float speed) { WarnL << "播放速度取值范围非法:" << speed; return false; } - //_seek_ticker重置,赋值_seek_to + // _seek_ticker重置,赋值_seek_to [AUTO-TRANSLATED:b30a3f06] + // _seek_ticker reset, assign _seek_to setCurrentStamp(getCurrentStamp()); - // 设置播放速度后应该恢复播放 + // 设置播放速度后应该恢复播放 [AUTO-TRANSLATED:851fcde9] + // Playback should resume after setting the playback speed _paused = false; if (_speed == speed) { return true; @@ -212,35 +226,42 @@ bool MP4Reader::speed(MediaSource &sender, float speed) { bool MP4Reader::seekTo(uint32_t stamp_seek) { lock_guard lck(_mtx); if (stamp_seek > _demuxer->getDurationMS()) { - //超过文件长度 + // 超过文件长度 [AUTO-TRANSLATED:b4361054] + // Exceeds the file length return false; } auto stamp = _demuxer->seekTo(stamp_seek); if (stamp == -1) { - //seek失败 + // seek失败 [AUTO-TRANSLATED:88cc8444] + // Seek failed return false; } if (!_have_video) { - //没有视频,不需要搜索关键帧;设置当前时间戳 + // 没有视频,不需要搜索关键帧;设置当前时间戳 [AUTO-TRANSLATED:82f87f21] + // There is no video, no need to search for keyframes; set the current timestamp setCurrentStamp((uint32_t) stamp); return true; } - //搜索到下一帧关键帧 + // 搜索到下一帧关键帧 [AUTO-TRANSLATED:aa2ec689] + // Search for the next keyframe bool keyFrame = false; bool eof = false; while (!eof) { auto frame = _demuxer->readFrame(keyFrame, eof); if (!frame) { - //文件读完了都未找到下一帧关键帧 + // 文件读完了都未找到下一帧关键帧 [AUTO-TRANSLATED:49a8d3a7] + // The file has been read but the next keyframe has not been found continue; } if (keyFrame || frame->keyFrame() || frame->configFrame()) { - //定位到key帧 + // 定位到key帧 [AUTO-TRANSLATED:0300901d] + // Locate to the keyframe if (_muxer) { _muxer->inputFrame(frame); } - //设置当前时间戳 + // 设置当前时间戳 [AUTO-TRANSLATED:88949974] + // Set the current timestamp setCurrentStamp(frame->dts()); return true; } diff --git a/src/Record/MP4Reader.h b/src/Record/MP4Reader.h index f6607d85..2bd95975 100644 --- a/src/Record/MP4Reader.h +++ b/src/Record/MP4Reader.h @@ -27,6 +27,13 @@ public: * @param app 应用名 * @param stream_id 流id,置空时,只解复用mp4,但是不生成MediaSource * @param file_path 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件 + * Play an mp4 file and convert it to a MediaSource stream + * @param vhost Virtual host + * @param app Application name + * @param stream_id Stream id, if empty, only demultiplex mp4, but not generate MediaSource + * @param file_path File path, if empty, it will be automatically generated according to the configuration file and the above parameters, otherwise use the specified file + + * [AUTO-TRANSLATED:2faeb5db] */ MP4Reader(const MediaTuple &tuple, const std::string &file_path = "", toolkit::EventPoller::Ptr poller = nullptr); @@ -37,16 +44,29 @@ public: * @param sample_ms 每次读取文件数据量,单位毫秒,置0时采用配置文件配置 * @param ref_self 是否让定时器引用此对象本身,如果无其他对象引用本身,在不循环读文件时,读取文件结束后本对象将自动销毁 * @param file_repeat 是否循环读取文件,如果配置文件设置为循环读文件,此参数无效 + * Start demultiplexing the MP4 file + * @param sample_ms The amount of file data read each time, in milliseconds, set to 0 to use the configuration file configuration + * @param ref_self Whether to let the timer reference this object itself, if there is no other object referencing itself, when not looping to read the file, after reading the file, this object will be automatically destroyed + * @param file_repeat Whether to loop to read the file, if the configuration file is set to loop to read the file, this parameter is invalid + + * [AUTO-TRANSLATED:2164a99d] */ void startReadMP4(uint64_t sample_ms = 0, bool ref_self = true, bool file_repeat = false); /** * 停止解复用MP4定时器 + * Stop demultiplexing the MP4 timer + + * [AUTO-TRANSLATED:45fb1ef7] */ void stopReadMP4(); /** * 获取mp4解复用器 + * Get the mp4 demultiplexer + + + * [AUTO-TRANSLATED:4f0dfc29] */ const MP4Demuxer::Ptr& getDemuxer() const; diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index cd55d30a..cd9e0e53 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -24,7 +24,8 @@ namespace mediakit { MP4Recorder::MP4Recorder(const MediaTuple &tuple, const string &path, size_t max_second) { _folder_path = path; - /////record 业务逻辑////// + // ///record 业务逻辑////// [AUTO-TRANSLATED:2e78931a] + // ///record Business Logic////// static_cast(_info) = tuple; _info.folder = path; GET_CONFIG(uint32_t, s_max_second, Protocol::kMP4MaxSecond); @@ -47,7 +48,8 @@ void MP4Recorder::createFile() { auto full_path = _folder_path + date + "/" + file_name; auto full_path_tmp = _folder_path + date + "/." + file_name; - /////record 业务逻辑////// + // ///record 业务逻辑////// [AUTO-TRANSLATED:2e78931a] + // ///record Business Logic////// _info.start_time = ::time(NULL); _info.file_name = file_name; _info.file_path = full_path; @@ -59,7 +61,8 @@ void MP4Recorder::createFile() { TraceL << "Open tmp mp4 file: " << full_path_tmp; _muxer->openMP4(full_path_tmp); for (auto &track :_tracks) { - //添加track + // 添加track [AUTO-TRANSLATED:80ae762a] + // Add track _muxer->addTrack(track); } _full_path_tmp = full_path_tmp; @@ -77,23 +80,28 @@ void MP4Recorder::asyncClose() { TraceL << "Start close tmp mp4 file: " << full_path_tmp; WorkThreadPool::Instance().getExecutor()->async([muxer, full_path_tmp, full_path, info]() mutable { info.time_len = muxer->getDuration() / 1000.0f; - // 关闭mp4可能非常耗时,所以要放在后台线程执行 + // 关闭mp4可能非常耗时,所以要放在后台线程执行 [AUTO-TRANSLATED:a7378a11] + // Closing mp4 can be very time-consuming, so it should be executed in the background thread TraceL << "Closing tmp mp4 file: " << full_path_tmp; muxer->closeMP4(); TraceL << "Closed tmp mp4 file: " << full_path_tmp; if (!full_path_tmp.empty()) { - // 获取文件大小 + // 获取文件大小 [AUTO-TRANSLATED:7b90eb41] + // Get file size info.file_size = File::fileSize(full_path_tmp); if (info.file_size < 1024) { - // 录像文件太小,删除之 + // 录像文件太小,删除之 [AUTO-TRANSLATED:923d27c3] + // The recording file is too small, delete it File::delete_file(full_path_tmp); return; } - // 临时文件名改成正式文件名,防止mp4未完成时被访问 + // 临时文件名改成正式文件名,防止mp4未完成时被访问 [AUTO-TRANSLATED:541a6f00] + // Change the temporary file name to the official file name to prevent access to the mp4 before it is completed rename(full_path_tmp.data(), full_path.data()); } TraceL << "Emit mp4 record event: " << full_path; - //触发mp4录制切片生成事件 + // 触发mp4录制切片生成事件 [AUTO-TRANSLATED:9959dcd4] + // Trigger mp4 recording slice generation event NOTICE_EMIT(BroadcastRecordMP4Args, Broadcast::kBroadcastRecordMP4, info); }); } @@ -113,9 +121,11 @@ void MP4Recorder::flush() { bool MP4Recorder::inputFrame(const Frame::Ptr &frame) { if (!(_have_video && frame->getTrackType() == TrackAudio)) { - //如果有视频且输入的是音频,那么应该忽略切片逻辑 + // 如果有视频且输入的是音频,那么应该忽略切片逻辑 [AUTO-TRANSLATED:fbb15d93] + // If there is video and the input is audio, then the slice logic should be ignored if (_last_dts == 0 || _last_dts > frame->dts()) { - //b帧情况下dts时间戳可能回退 + // b帧情况下dts时间戳可能回退 [AUTO-TRANSLATED:1de38f77] + // In the case of b-frames, the dts timestamp may regress _last_dts = MAX(frame->dts(), _last_dts); } auto duration = 5u; // 默认至少一帧5ms @@ -123,24 +133,30 @@ bool MP4Recorder::inputFrame(const Frame::Ptr &frame) { duration = MAX(duration, frame->dts() - _last_dts); } if (!_muxer || ((duration > _max_second * 1000) && (!_have_video || (_have_video && frame->keyFrame())))) { - //成立条件 - // 1、_muxer为空 - // 2、到了切片时间,并且只有音频 - // 3、到了切片时间,有视频并且遇到视频的关键帧 + // 成立条件 [AUTO-TRANSLATED:8c9c6083] + // Conditions for establishment + // 1、_muxer为空 [AUTO-TRANSLATED:fa236097] + // 1. _muxer is empty + // 2、到了切片时间,并且只有音频 [AUTO-TRANSLATED:212e9d23] + // 2. It's time to slice, and there is only audio + // 3、到了切片时间,有视频并且遇到视频的关键帧 [AUTO-TRANSLATED:fa4a71ad] + // 3. It's time to slice, there is video and a video keyframe is encountered _last_dts = 0; createFile(); } } if (_muxer) { - //生成mp4文件 + // 生成mp4文件 [AUTO-TRANSLATED:76a8d77c] + // Generate mp4 file return _muxer->inputFrame(frame); } return false; } bool MP4Recorder::addTrack(const Track::Ptr &track) { - //保存所有的track,为创建MP4MuxerFile做准备 + // 保存所有的track,为创建MP4MuxerFile做准备 [AUTO-TRANSLATED:815c2486] + // Save all tracks in preparation for creating MP4MuxerFile _tracks.emplace_back(track); if (track->getTrackType() == TrackVideo) { _have_video = true; diff --git a/src/Record/MP4Recorder.h b/src/Record/MP4Recorder.h index e1258029..6c042b8e 100644 --- a/src/Record/MP4Recorder.h +++ b/src/Record/MP4Recorder.h @@ -31,21 +31,34 @@ public: /** * 重置所有Track + * Reset all Tracks + + * [AUTO-TRANSLATED:8dd80826] */ void resetTracks() override; /** * 输入frame + * Input frame + + * [AUTO-TRANSLATED:3722ea0e] */ bool inputFrame(const Frame::Ptr &frame) override; /** * 刷新输出所有frame缓存 + * Refresh output all frame cache + + * [AUTO-TRANSLATED:adaea568] */ void flush() override; /** * 添加ready状态的track + * Add ready state track + + + * [AUTO-TRANSLATED:2d8138b3] */ bool addTrack(const Track::Ptr & track) override; diff --git a/src/Record/MPEG.cpp b/src/Record/MPEG.cpp index bca086dc..483cf12c 100644 --- a/src/Record/MPEG.cpp +++ b/src/Record/MPEG.cpp @@ -54,10 +54,12 @@ bool MpegMuxer::inputFrame(const Frame::Ptr &frame) { switch (frame->getCodecId()) { case CodecH264: case CodecH265: { - // 这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理, + // 这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理, [AUTO-TRANSLATED:edf57c32] + // The code logic here is to package frames with the same timestamp, such as SPS, PPS, and IDR, together as one frame. return track.merger.inputFrame(frame, [this, &track](uint64_t dts, uint64_t pts, const Buffer::Ptr &buffer, bool have_idr) { _key_pos = have_idr; - // 取视频时间戳为TS的时间戳 + // 取视频时间戳为TS的时间戳 [AUTO-TRANSLATED:5ff7796d] + // Take the video timestamp as the TS timestamp. _timestamp = dts; _max_cache_size = 512 + 1.2 * buffer->size(); mpeg_muxer_input((::mpeg_muxer_t *)_context, track.track_id, have_idr ? 0x0001 : 0, pts * 90LL, dts * 90LL, buffer->data(), buffer->size()); @@ -71,7 +73,8 @@ bool MpegMuxer::inputFrame(const Frame::Ptr &frame) { default: { if (!_have_video) { - // 没有视频时,才以音频时间戳为TS的时间戳 + // 没有视频时,才以音频时间戳为TS的时间戳 [AUTO-TRANSLATED:17cef4f7] + // When there is no video, use the audio timestamp as the TS timestamp. _timestamp = frame->dts(); } @@ -89,7 +92,8 @@ bool MpegMuxer::inputFrame(const Frame::Ptr &frame) { void MpegMuxer::resetTracks() { _have_video = false; - //通知片段中断 + // 通知片段中断 [AUTO-TRANSLATED:ed3d87ba] + // Notify fragment interruption. onWrite(nullptr, _timestamp, false); releaseContext(); createContext(); @@ -113,7 +117,8 @@ void MpegMuxer::createContext() { }, /*free*/ [](void *param, void *packet) { - //什么也不做 + // 什么也不做 [AUTO-TRANSLATED:e2f8de75] + // Do nothing. }, /*wtite*/ [](void *param, int stream, void *packet, size_t bytes) { diff --git a/src/Record/MPEG.h b/src/Record/MPEG.h index dc07af4a..e74f763c 100644 --- a/src/Record/MPEG.h +++ b/src/Record/MPEG.h @@ -22,7 +22,8 @@ #include "Util/ResourcePool.h" namespace mediakit { -//该类用于产生MPEG-TS/MPEG-PS +// 该类用于产生MPEG-TS/MPEG-PS [AUTO-TRANSLATED:267efc85] +// This class is used to generate MPEG-TS/MPEG-PS class MpegMuxer : public MediaSinkInterface { public: MpegMuxer(bool is_ps = false); @@ -30,21 +31,33 @@ public: /** * 添加音视频轨道 + * Add audio and video tracks + + * [AUTO-TRANSLATED:7b0c1d64] */ bool addTrack(const Track::Ptr &track) override; /** * 重置音视频轨道 + * Reset audio and video tracks + + * [AUTO-TRANSLATED:6eb1b742] */ void resetTracks() override; /** * 输入帧数据 + * Input frame data + + * [AUTO-TRANSLATED:d13bc7f2] */ bool inputFrame(const Frame::Ptr &frame) override; /** * 刷新输出所有frame缓存 + * Flush all frame buffers in the output + + * [AUTO-TRANSLATED:adaea568] */ void flush() override; @@ -54,6 +67,13 @@ protected: * @param buffer ts/ps数据包 * @param timestamp 时间戳,单位毫秒 * @param key_pos 是否为关键帧的第一个ts/ps包,用于确保ts切片第一帧为关键帧 + * Callback for outputting ts/ps data + * @param buffer ts/ps data packet + * @param timestamp Timestamp, in milliseconds + * @param key_pos Whether it is the first ts/ps packet of a key frame, used to ensure that the first frame of the ts slice is a key frame + + + * [AUTO-TRANSLATED:dda8ed40] */ virtual void onWrite(std::shared_ptr buffer, uint64_t timestamp, bool key_pos) = 0; diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 1913ae05..1e73453d 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -42,15 +42,20 @@ public: class Recorder{ public: typedef enum { - // 录制hls + // 录制hls [AUTO-TRANSLATED:24a50dff] + // Record hls type_hls = 0, - // 录制MP4 + // 录制MP4 [AUTO-TRANSLATED:03d73bb7] + // Record MP4 type_mp4 = 1, - // 录制hls.fmp4 + // 录制hls.fmp4 [AUTO-TRANSLATED:031cf6f1] + // Record hls.fmp4 type_hls_fmp4 = 2, - // fmp4直播 + // fmp4直播 [AUTO-TRANSLATED:ac37a248] + // fmp4 live type_fmp4 = 3, - // ts直播 + // ts直播 [AUTO-TRANSLATED:b062b43a] + // ts live type_ts = 4, } type; @@ -62,6 +67,15 @@ public: * @param stream_id 流id * @param customized_path 录像文件保存自定义根目录,为空则采用配置文件设置 * @return 录制文件绝对路径 + * Get the absolute path of the recording file + * @param type hls or MP4 recording + * @param vhost virtual host + * @param app application name + * @param stream_id stream id + * @param customized_path custom root directory for saving recording files, empty means using configuration file settings + * @return absolute path of the recording file + + * [AUTO-TRANSLATED:2fd57fcd] */ static std::string getRecordPath(type type, const MediaTuple& tuple, const std::string &customized_path = ""); @@ -74,6 +88,17 @@ public: * @param customized_path 录像文件保存自定义根目录,为空则采用配置文件设置 * @param max_second mp4录制最大切片时间,单位秒,置0则采用配置文件配置 * @return 对象指针,可能为nullptr + * Create a recorder object + * @param type hls or MP4 recording + * @param vhost virtual host + * @param app application name + * @param stream_id stream id + * @param customized_path custom root directory for saving recording files, empty means using configuration file settings + * @param max_second maximum slice time for mp4 recording, in seconds, 0 means using configuration file settings + * @return object pointer, may be nullptr + + + * [AUTO-TRANSLATED:e0b6e43b] */ static std::shared_ptr createRecorder(type type, const MediaTuple& tuple, const ProtocolOption &option); diff --git a/src/Rtcp/Rtcp.cpp b/src/Rtcp/Rtcp.cpp index dd60492f..afaa9be2 100644 --- a/src/Rtcp/Rtcp.cpp +++ b/src/Rtcp/Rtcp.cpp @@ -69,7 +69,8 @@ static void setupHeader(RtcpHeader *rtcp, RtcpType type, size_t report_count, si if (report_count > 0x1F) { throw std::invalid_argument(StrPrinter << "rtcp report_count最大赋值为31,当前为:" << report_count); } - // items总个数 + // items总个数 [AUTO-TRANSLATED:2d40d010] + // Total number of items rtcp->report_count = report_count; rtcp->pt = (uint8_t)type; rtcp->setSize(total_bytes); @@ -149,7 +150,8 @@ string RtcpHeader::dumpString() const { } size_t RtcpHeader::getSize() const { - // 加上rtcp头长度 + // 加上rtcp头长度 [AUTO-TRANSLATED:21a40b4b] + // Add rtcp header length return (1 + ntohs(length)) << 2; } @@ -161,7 +163,8 @@ size_t RtcpHeader::getPaddingSize() const { } void RtcpHeader::setSize(size_t size) { - // 不包含rtcp头的长度 + // 不包含rtcp头的长度 [AUTO-TRANSLATED:b26ad8ef] + // Length excluding rtcp header length = htons((uint16_t)((size >> 2) - 1)); } @@ -230,7 +233,8 @@ vector RtcpHeader::loadFromBytes(char *data, size_t len) { rtcp->net2Host(rtcp_len); ret.emplace_back(rtcp); } catch (std::exception &ex) { - // 不能处理的rtcp包,或者无法解析的rtcp包,忽略掉 + // 不能处理的rtcp包,或者无法解析的rtcp包,忽略掉 [AUTO-TRANSLATED:752ec400] + // Ignore unprocessable rtcp packets or rtcp packets that cannot be parsed WarnL << ex.what() << ",长度为:" << rtcp_len; } ptr += rtcp_len; @@ -273,8 +277,10 @@ string RtcpSR::getNtpStamp() const { uint64_t RtcpSR::getNtpUnixStampMS() const { if (ntpmsw < 0x83AA7E80) { - // ntp时间戳起始时间为1900年,但是utc时间戳起始时间为1970年,两者相差0x83AA7E80秒 - // ntp时间戳不得早于1970年,否则无法转换为utc时间戳 + // ntp时间戳起始时间为1900年,但是utc时间戳起始时间为1970年,两者相差0x83AA7E80秒 [AUTO-TRANSLATED:6b3ac2fa] + // The ntp timestamp starts from 1900, but the utc timestamp starts from 1970, with a difference of 0x83AA7E80 seconds + // ntp时间戳不得早于1970年,否则无法转换为utc时间戳 [AUTO-TRANSLATED:d70fc88c] + // The ntp timestamp must not be earlier than 1970, otherwise it cannot be converted to utc timestamp return 0; } struct timeval tv; @@ -321,7 +327,10 @@ string RtcpSR::dumpString() const { } #define CHECK_REPORT_COUNT(item_count) \ - /*修正个数,防止getItemList时内存越界*/ \ + /*修正个数,防止getItemList时内存越界 + /*Correct the number to prevent memory overflow when getItemList + * [AUTO-TRANSLATED:852bd70e] + */ \ if (report_count != item_count) { \ WarnL << rtcpTypeToStr((RtcpType)pt) << " report_count 字段不正确,已修正为:" << (int)report_count << " -> " \ << item_count; \ @@ -461,7 +470,8 @@ string SdesChunk::dumpString() const { std::shared_ptr RtcpSdes::create(const std::vector &item_text) { size_t item_total_size = 0; for (auto &text : item_text) { - // 统计所有SdesChunk对象占用的空间 + // 统计所有SdesChunk对象占用的空间 [AUTO-TRANSLATED:87871205] + // Count the space occupied by all SdesChunk objects item_total_size += alignSize(SdesChunk::minSize() + (0xFF & text.size())); } auto real_size = sizeof(RtcpSdes) - sizeof(SdesChunk) + item_total_size; @@ -471,7 +481,8 @@ std::shared_ptr RtcpSdes::create(const std::vector &item_text) auto item_ptr = &ptr->chunks; for (auto &text : item_text) { item_ptr->txt_len = (0xFF & text.size()); - // 确保赋值\0为RTCP_SDES_END + // 确保赋值\0为RTCP_SDES_END [AUTO-TRANSLATED:316be0a3] + // Ensure that the assignment \0 is RTCP_SDES_END memcpy(item_ptr->text, text.data(), item_ptr->txt_len + 1); item_ptr = (SdesChunk *)((char *)item_ptr + item_ptr->totalBytes()); } @@ -680,14 +691,16 @@ void RtcpBye::net2Host(size_t size) { ssrc[i] = ntohl(ssrc[i]); offset += sizeof(ssrc); } - // 修正ssrc个数 + // 修正ssrc个数 [AUTO-TRANSLATED:57c74f58] + // Correct the number of ssrcs CHECK_REPORT_COUNT(i); if (offset < size) { uint8_t *reason_len_ptr = &reason_len + sizeof(ssrc) * (report_count - 1); if (reason_len_ptr + 1 + *reason_len_ptr > (uint8_t *)this + size) { WarnL << "invalid rtcp bye reason length"; - // 修正reason_len长度 + // 修正reason_len长度 [AUTO-TRANSLATED:1c0c9645] + // Correct the length of reason_len *reason_len_ptr = ((uint8_t *)this + size - reason_len_ptr - 1) & 0xFF; } } diff --git a/src/Rtcp/RtcpContext.cpp b/src/Rtcp/RtcpContext.cpp index 64de2519..64db7acf 100644 --- a/src/Rtcp/RtcpContext.cpp +++ b/src/Rtcp/RtcpContext.cpp @@ -64,13 +64,16 @@ void RtcpContextForSend::onRtcp(RtcpHeader *rtcp) { if (it == _sender_report_ntp.end()) { continue; } - // 发送sr到收到rr之间的时间戳增量 + // 发送sr到收到rr之间的时间戳增量 [AUTO-TRANSLATED:da014bf1] + // Timestamp increment between sending sr and receiving rr auto ms_inc = getCurrentMillisecond() - it->second; - // rtp接收端收到sr包后,回复rr包的延时,已转换为毫秒 + // rtp接收端收到sr包后,回复rr包的延时,已转换为毫秒 [AUTO-TRANSLATED:bfab8622] + // Delay of the rtp receiver replying to the rr packet after receiving the sr packet, converted to milliseconds auto delay_ms = (uint64_t)item->delay_since_last_sr * 1000 / 65536; auto rtt = (int)(ms_inc - delay_ms); if (rtt >= 0) { - // rtt不可能小于0 + // rtt不可能小于0 [AUTO-TRANSLATED:34914014] + // RTT cannot be less than 0 _rtt[item->ssrc] = rtt; // InfoL << "ssrc:" << item->ssrc << ",rtt:" << rtt; } @@ -111,11 +114,13 @@ Buffer::Ptr RtcpContextForSend::createRtcpSR(uint32_t rtcp_ssrc) { rtcp->packet_count = htonl((uint32_t)_packets); rtcp->octet_count = htonl((uint32_t)_bytes); - // 记录上次发送的sender report信息,用于后续统计rtt + // 记录上次发送的sender report信息,用于后续统计rtt [AUTO-TRANSLATED:1d22d2c8] + // Record the last sent sender report information for subsequent RTT statistics auto last_sr_lsr = ((ntohl(rtcp->ntpmsw) & 0xFFFF) << 16) | ((ntohl(rtcp->ntplsw) >> 16) & 0xFFFF); _sender_report_ntp[last_sr_lsr] = getCurrentMillisecond(); if (_sender_report_ntp.size() >= 5) { - // 删除最早的sr rtcp + // 删除最早的sr rtcp [AUTO-TRANSLATED:2457e08d] + // Delete the earliest sr rtcp _sender_report_ntp.erase(_sender_report_ntp.begin()); } @@ -141,7 +146,8 @@ toolkit::Buffer::Ptr RtcpContextForSend::createRtcpXRDLRR(uint32_t rtcp_ssrc, ui rtcp->items.dlrr = 0; WarnL; } else { - // now - Last SR time,单位毫秒 + // now - Last SR time,单位毫秒 [AUTO-TRANSLATED:cc449199] + // now - Last SR time, in milliseconds auto delay = getCurrentMillisecond() - _xr_rrtr_recv_sys_stamp[rtp_ssrc]; // in units of 1/65536 seconds auto dlsr = (uint32_t)(delay / 1000.0f * 65536); @@ -155,38 +161,46 @@ toolkit::Buffer::Ptr RtcpContextForSend::createRtcpXRDLRR(uint32_t rtcp_ssrc, ui void RtcpContextForRecv::onRtp( uint16_t seq, uint32_t stamp, uint64_t ntp_stamp_ms, uint32_t sample_rate, size_t bytes) { { - // 接收者才做复杂的统计运算 + // 接收者才做复杂的统计运算 [AUTO-TRANSLATED:853c68e0] + // The receiver performs complex statistical calculations auto sys_stamp = getCurrentMillisecond(); if (_last_rtp_sys_stamp) { - // 计算时间戳抖动值 + // 计算时间戳抖动值 [AUTO-TRANSLATED:cd3571b4] + // Calculate the timestamp jitter value double diff = double( (int64_t(sys_stamp) - int64_t(_last_rtp_sys_stamp)) * (sample_rate / double(1000.0)) - (int64_t(stamp) - int64_t(_last_rtp_stamp))); if (diff < 0) { diff = -diff; } - // 抖动单位为采样次数 + // 抖动单位为采样次数 [AUTO-TRANSLATED:b713633a] + // Jitter unit is the number of samples _jitter += (diff - _jitter) / 16.0; } else { _jitter = 0; } if (_last_rtp_seq > 0xFF00 && seq < 0xFF && (!_seq_cycles || _packets - _last_cycle_packets > 0x1FFF)) { - // 上次seq大于0xFF00且本次seq小于0xFF, - // 且未发生回环或者距离上次回环间隔超过0x1FFF个包,则认为回环 + // 上次seq大于0xFF00且本次seq小于0xFF, [AUTO-TRANSLATED:82dd69fa] + // Last seq is greater than 0xFF00 and this seq is less than 0xFF, + // 且未发生回环或者距离上次回环间隔超过0x1FFF个包,则认为回环 [AUTO-TRANSLATED:2907b595] + // and no loopback occurs or the interval between the last loopback is greater than 0x1FFF packets, then it is considered a loopback ++_seq_cycles; _last_cycle_packets = _packets; _seq_max = seq; } else if (seq > _seq_max) { - // 本次回环前最大seq + // 本次回环前最大seq [AUTO-TRANSLATED:c02f6a87] + // Maximum seq before this loopback _seq_max = seq; } if (!_seq_base) { - // 记录第一个rtp的seq + // 记录第一个rtp的seq [AUTO-TRANSLATED:ce2bb7d7] + // Record the seq of the first rtp _seq_base = seq; } else if (!_seq_cycles && seq < _seq_base) { - // 未发生回环,那么取最新的seq为基准seq + // 未发生回环,那么取最新的seq为基准seq [AUTO-TRANSLATED:721b37fc] + // If no loopback occurs, then take the latest seq as the base seq _seq_base = seq; } @@ -258,7 +272,8 @@ Buffer::Ptr RtcpContextForRecv::createRtcpRR(uint32_t rtcp_ssrc, uint32_t rtp_ss item->jitter = htonl(uint32_t(_jitter)); item->last_sr_stamp = htonl(_last_sr_lsr); - // now - Last SR time,单位毫秒 + // now - Last SR time,单位毫秒 [AUTO-TRANSLATED:cc449199] + // now - Last SR time, in milliseconds auto delay = getCurrentMillisecond() - _last_sr_ntp_sys; // in units of 1/65536 seconds auto dlsr = (uint32_t)(delay / 1000.0f * 65536); diff --git a/src/Rtcp/RtcpContext.h b/src/Rtcp/RtcpContext.h index f80b2efe..20d7ffe0 100644 --- a/src/Rtcp/RtcpContext.h +++ b/src/Rtcp/RtcpContext.h @@ -29,22 +29,40 @@ public: * @param ntp_stamp_ms ntp时间戳 * @param rtp rtp时间戳采样率,视频一般为90000,音频一般为采样率 * @param bytes rtp数据长度 + * Called when outputting or inputting rtp + * @param seq rtp's seq + * @param stamp rtp's timestamp, unit is sample number (not millisecond) + * @param ntp_stamp_ms ntp timestamp + * @param rtp rtp timestamp sampling rate, video is generally 90000, audio is generally sampling rate + * @param bytes rtp data length + + * [AUTO-TRANSLATED:745772b5] */ virtual void onRtp(uint16_t seq, uint32_t stamp, uint64_t ntp_stamp_ms, uint32_t sample_rate, size_t bytes); /** * 输入sr rtcp包 * @param rtcp 输入一个rtcp + * Input sr rtcp packet + * @param rtcp input an rtcp + + * [AUTO-TRANSLATED:46f309ec] */ virtual void onRtcp(RtcpHeader *rtcp) = 0; /** * 计算总丢包数 + * Calculate the total number of lost packets + + * [AUTO-TRANSLATED:084f3832] */ virtual size_t getLost(); /** * 返回理应收到的rtp数 + * Return the number of rtp that should be received + + * [AUTO-TRANSLATED:ede367a0] */ virtual size_t getExpectedPackets() const; @@ -52,6 +70,11 @@ public: * 创建SR rtcp包 * @param rtcp_ssrc rtcp的ssrc * @return rtcp包 + * Create SR rtcp packet + * @param rtcp_ssrc rtcp's ssrc + * @return rtcp packet + + * [AUTO-TRANSLATED:a9ec36d0] */ virtual toolkit::Buffer::Ptr createRtcpSR(uint32_t rtcp_ssrc); @@ -59,6 +82,11 @@ public: * @brief 创建xr的dlrr包,用于接收者估算rtt * * @return toolkit::Buffer::Ptr + * @brief Create xr's dlrr packet, used by receiver to estimate rtt + * + * @return toolkit::Buffer::Ptr + + * [AUTO-TRANSLATED:a5094e1d] */ virtual toolkit::Buffer::Ptr createRtcpXRDLRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc); @@ -67,25 +95,40 @@ public: * @param rtcp_ssrc rtcp的ssrc * @param rtp_ssrc rtp的ssrc * @return rtcp包 + * Create RR rtcp packet + * @param rtcp_ssrc rtcp's ssrc + * @param rtp_ssrc rtp's ssrc + * @return rtcp packet + + * [AUTO-TRANSLATED:81ebbf81] */ virtual toolkit::Buffer::Ptr createRtcpRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc); /** * 上次结果与本次结果间应收包数 + * Number of packets that should be received between the last result and the current result + + * [AUTO-TRANSLATED:3b2846ab] */ virtual size_t getExpectedPacketsInterval(); /** * 上次结果与本次结果间丢包个数 + * Number of lost packets between the last result and the current result + + * [AUTO-TRANSLATED:fe5ac890] */ virtual size_t getLostInterval(); protected: - // 收到或发送的rtp的字节数 + // 收到或发送的rtp的字节数 [AUTO-TRANSLATED:a38d88a9] + // Number of bytes of rtp received or sent size_t _bytes = 0; - // 收到或发送的rtp的个数 + // 收到或发送的rtp的个数 [AUTO-TRANSLATED:b28c3c90] + // Number of rtp received or sent size_t _packets = 0; - // 上次的rtp时间戳,毫秒 + // 上次的rtp时间戳,毫秒 [AUTO-TRANSLATED:99eecec6] + // Last rtp timestamp, milliseconds uint32_t _last_rtp_stamp = 0; uint64_t _last_ntp_stamp_ms = 0; }; @@ -102,6 +145,11 @@ public: * 获取rtt * @param ssrc rtp ssrc * @return rtt,单位毫秒 + * Get rtt + * @param ssrc rtp ssrc + * @return rtt, unit is millisecond + + * [AUTO-TRANSLATED:f0885551] */ uint32_t getRtt(uint32_t ssrc) const; @@ -124,27 +172,38 @@ public: void onRtcp(RtcpHeader *rtcp) override; private: - // 时间戳抖动值 + // 时间戳抖动值 [AUTO-TRANSLATED:8100680c] + // Timestamp jitter value double _jitter = 0; - // 第一个seq的值 + // 第一个seq的值 [AUTO-TRANSLATED:d893719d] + // The value of the first seq uint16_t _seq_base = 0; - // rtp最大seq + // rtp最大seq [AUTO-TRANSLATED:5cc9f775] + // Maximum rtp seq uint16_t _seq_max = 0; - // rtp回环次数 + // rtp回环次数 [AUTO-TRANSLATED:9fe9c340] + // Rtp loopback times uint16_t _seq_cycles = 0; - // 上次回环发生时,记录的rtp包数 + // 上次回环发生时,记录的rtp包数 [AUTO-TRANSLATED:c32cb555] + // Number of rtp packets recorded when the last loopback occurred size_t _last_cycle_packets = 0; - // 上次的seq + // 上次的seq [AUTO-TRANSLATED:07364b7d] + // Last seq uint16_t _last_rtp_seq = 0; - // 上次的rtp的系统时间戳(毫秒)用于统计抖动 + // 上次的rtp的系统时间戳(毫秒)用于统计抖动 [AUTO-TRANSLATED:b1e8c89b] + // Last rtp system timestamp (milliseconds) used for jitter statistics uint64_t _last_rtp_sys_stamp = 0; - // 上次统计的丢包总数 + // 上次统计的丢包总数 [AUTO-TRANSLATED:242e75ed] + // Last total number of lost packets counted size_t _last_lost = 0; - // 上次统计应收rtp包总数 + // 上次统计应收rtp包总数 [AUTO-TRANSLATED:eb2d5f4d] + // Last total number of rtp packets that should be received counted size_t _last_expected = 0; - // 上次收到sr包时计算出的Last SR timestamp + // 上次收到sr包时计算出的Last SR timestamp [AUTO-TRANSLATED:fdec069e] + // Last SR timestamp calculated when the last SR packet was received uint32_t _last_sr_lsr = 0; - // 上次收到sr时的系统时间戳,单位毫秒 + // 上次收到sr时的系统时间戳,单位毫秒 [AUTO-TRANSLATED:044fa0d5] + // System timestamp when the last SR was received, unit is millisecond uint64_t _last_sr_ntp_sys = 0; }; diff --git a/src/Rtcp/RtcpFCI.cpp b/src/Rtcp/RtcpFCI.cpp index 843798df..64e7b390 100644 --- a/src/Rtcp/RtcpFCI.cpp +++ b/src/Rtcp/RtcpFCI.cpp @@ -119,7 +119,8 @@ string FCI_REMB::create(const vector &ssrcs, uint32_t bitrate) { // BR Mantissa (18 bits) thiz->bitrate[3] = (uint8_t)(mantissa); - // 设置ssrc列表 + // 设置ssrc列表 [AUTO-TRANSLATED:51a0df3a] + // Set ssrc list int i = 0; for (auto ssrc : ssrcs) { thiz->ssrc_feedback[i++] = htonl(ssrc); @@ -186,7 +187,8 @@ uint16_t FCI_NACK::getBlp() const { vector FCI_NACK::getBitArray() const { vector ret; ret.resize(kBitSize + 1); - // nack第一个包丢包 + // nack第一个包丢包 [AUTO-TRANSLATED:357feeea] + // Nack the first packet loss ret[0] = true; auto blp_h = getBlp(); @@ -224,21 +226,28 @@ public: uint16_t symbol : 2; uint16_t run_length_high : 5; #else - // Run Length 高5位 + // Run Length 高5位 [AUTO-TRANSLATED:a1426130] + // Run Length high 5 bits uint16_t run_length_high : 5; - // 参考SymbolStatus定义 + // 参考SymbolStatus定义 [AUTO-TRANSLATED:0268c65e] + // Refer to SymbolStatus definition uint16_t symbol : 2; - // 固定为0 + // 固定为0 [AUTO-TRANSLATED:7b516577] + // Fixed to 0 uint16_t type : 1; #endif - // Run Length 低8位 + // Run Length 低8位 [AUTO-TRANSLATED:8984c00d] + // Run Length low 8 bits uint16_t run_length_low : 8; - // 获取Run Length + // 获取Run Length [AUTO-TRANSLATED:9fb792e6] + // Get Run Length uint16_t getRunLength() const; - // 构造函数 + // 构造函数 [AUTO-TRANSLATED:b9f7407d] + // Constructor RunLengthChunk(SymbolStatus status, uint16_t run_length); - // 打印本对象 + // 打印本对象 [AUTO-TRANSLATED:e8bd8207] + // Print this object string dumpString() const; }; #pragma pack(pop) @@ -276,21 +285,28 @@ public: uint16_t symbol : 1; uint16_t symbol_list_high : 6; #else - // symbol_list 高6位 + // symbol_list 高6位 [AUTO-TRANSLATED:2ef1be51] + // symbol_list high 6 bits uint16_t symbol_list_high : 6; - // symbol_list中元素是1个还是2个bit + // symbol_list中元素是1个还是2个bit [AUTO-TRANSLATED:0f56756f] + // Whether the element in symbol_list is 1 or 2 bits uint16_t symbol : 1; - // 固定为1 + // 固定为1 [AUTO-TRANSLATED:1fb4c9c8] + // Fixed to 1 uint16_t type : 1; #endif - // symbol_list 低8位 + // symbol_list 低8位 [AUTO-TRANSLATED:257bf13b] + // symbol_list low 8 bits uint16_t symbol_list_low : 8; - // 获取symbollist + // 获取symbollist [AUTO-TRANSLATED:a24b8d73] + // Get symbollist vector getSymbolList() const; - // 构造函数 + // 构造函数 [AUTO-TRANSLATED:b9f7407d] + // Constructor StatusVecChunk(bool symbol_bit, const vector &status); - // 打印本对象 + // 打印本对象 [AUTO-TRANSLATED:e8bd8207] + // Print this object string dumpString() const; }; #pragma pack(pop) @@ -321,13 +337,15 @@ vector StatusVecChunk::getSymbolList() const { vector ret; auto thiz = ntohs(*((uint16_t *)this)); if (symbol == 0) { - // s = 0 时,表示symbollist的每一个bit能表示一个数据包的到达状态 + // s = 0 时,表示symbollist的每一个bit能表示一个数据包的到达状态 [AUTO-TRANSLATED:89d8f104] + // When s = 0, each bit in symbollist represents the arrival status of a data packet for (int i = 13; i >= 0; --i) { SymbolStatus status = (SymbolStatus)((bool)(thiz & (1 << i))); ret.emplace_back(status); } } else { - // s = 1 时,表示symbollist每两个bit表示一个数据包的状态 + // s = 1 时,表示symbollist每两个bit表示一个数据包的状态 [AUTO-TRANSLATED:fd7eb5fe] + // When s = 1, every two bits in symbollist represent the status of a data packet for (int i = 12; i >= 0; i -= 2) { SymbolStatus status = (SymbolStatus)((thiz & (3 << i)) >> i); ret.emplace_back(status); @@ -398,29 +416,34 @@ static int16_t getRecvDelta(SymbolStatus status, uint8_t *&ptr, const uint8_t *e int16_t delta = 0; switch (status) { case SymbolStatus::not_received: { - // 丢包, recv delta为0个字节 + // 丢包, recv delta为0个字节 [AUTO-TRANSLATED:4312b1ce] + // Packet loss, recv delta is 0 bytes break; } case SymbolStatus::small_delta: { CHECK(ptr + 1 <= end); - // 时间戳增量小于256, recv delta为1个字节 + // 时间戳增量小于256, recv delta为1个字节 [AUTO-TRANSLATED:4b93efeb] + // Timestamp increment is less than 256, recv delta is 1 byte delta = *ptr; ptr += 1; break; } case SymbolStatus::large_delta: { CHECK(ptr + 2 <= end); - // 时间戳增量256~65535间,recv delta为2个字节 + // 时间戳增量256~65535间,recv delta为2个字节 [AUTO-TRANSLATED:989c8340] + // Timestamp increment is between 256 and 65535, recv delta is 2 bytes delta = *ptr << 8 | *(ptr + 1); ptr += 2; break; } case SymbolStatus::reserved: { - // 没有时间戳 + // 没有时间戳 [AUTO-TRANSLATED:0767909f] + // No timestamp break; } default: - // 这个逻辑分支不可达到 + // 这个逻辑分支不可达到 [AUTO-TRANSLATED:451ba1eb] + // This logic branch cannot be reached CHECK(0); break; } @@ -479,17 +502,20 @@ string FCI_TWCC::dumpString(size_t total_size) const { static void appendDeltaString(string &delta_str, FCI_TWCC::TwccPacketStatus &status, int count) { for (auto it = status.begin(); it != status.end() && count--;) { switch (it->second.first) { - // large delta模式先写高字节,再写低字节 + // large delta模式先写高字节,再写低字节 [AUTO-TRANSLATED:cb25ff45] + // Large delta mode writes the high byte first, then the low byte case SymbolStatus::large_delta: delta_str.push_back((it->second.second >> 8) & 0xFF); - // small delta模式只写低字节 + // small delta模式只写低字节 [AUTO-TRANSLATED:5caea5d6] + // Small delta mode only writes the low byte case SymbolStatus::small_delta: delta_str.push_back(it->second.second & 0xFF); break; default: break; } - // 移除已经处理过的数据 + // 移除已经处理过的数据 [AUTO-TRANSLATED:106fb6db] + // Remove the data that has been processed it = status.erase(it); } } @@ -508,21 +534,25 @@ string FCI_TWCC::create(uint32_t ref_time, uint8_t fb_pkt_count, TwccPacketStatu string delta_str; while (!status.empty()) { { - // 第一个rtp的状态 + // 第一个rtp的状态 [AUTO-TRANSLATED:b0efb9ad] + // The status of the first rtp auto symbol = status.begin()->second.first; int16_t count = 0; for (auto &pr : status) { if (pr.second.first != symbol) { - // 状态发送变更了,本chunk结束 + // 状态发送变更了,本chunk结束 [AUTO-TRANSLATED:f1c2c6d1] + // The status sending changed, this chunk ends break; } if (++count >= (0xFFFF >> 3)) { - // RunLengthChunk 13个bit表明rtp个数,最多可以表述0xFFFF >> 3个rtp状态 + // RunLengthChunk 13个bit表明rtp个数,最多可以表述0xFFFF >> 3个rtp状态 [AUTO-TRANSLATED:c6c4de01] + // RunLengthChunk 13 bits indicate the number of rtp, at most 0xFFFF >> 3 rtp status can be expressed break; } } if (count >= 7) { - // 连续状态相同个数大于6个时,使用RunLengthChunk模式比较节省带宽 + // 连续状态相同个数大于6个时,使用RunLengthChunk模式比较节省带宽 [AUTO-TRANSLATED:c243c73f] + // When the number of consecutive states is greater than 6, using RunLengthChunk mode is more bandwidth-saving RunLengthChunk chunk(symbol, count); fci.append((char *)&chunk, RunLengthChunk::kSize); appendDeltaString(delta_str, status, count); @@ -531,20 +561,25 @@ string FCI_TWCC::create(uint32_t ref_time, uint8_t fb_pkt_count, TwccPacketStatu } { - // StatusVecChunk模式 - // symbol_list中元素是1个bit + // StatusVecChunk模式 [AUTO-TRANSLATED:612e548c] + // StatusVecChunk mode + // symbol_list中元素是1个bit [AUTO-TRANSLATED:e5f8cbf8] + // Elements in symbol_list are 1 bit auto symbol = 0; vector vec; for (auto &pr : status) { vec.push_back(pr.second.first); if (pr.second.first >= SymbolStatus::large_delta) { - // symbol_list中元素是2个bit + // symbol_list中元素是2个bit [AUTO-TRANSLATED:43429094] + // Elements in symbol_list are 2 bits symbol = 1; } if (vec.size() << symbol >= 14) { - // symbol为0时,最多存放14个rtp的状态 - // symbol为1时,最多存放7个rtp的状态 + // symbol为0时,最多存放14个rtp的状态 [AUTO-TRANSLATED:1da4fad6] + // When symbol is 0, at most 14 RTP statuses can be stored + // symbol为1时,最多存放7个rtp的状态 [AUTO-TRANSLATED:34f39e9a] + // When symbol is 1, at most 7 RTP statuses can be stored break; } } @@ -555,7 +590,8 @@ string FCI_TWCC::create(uint32_t ref_time, uint8_t fb_pkt_count, TwccPacketStatu } } - // recv delta部分 + // recv delta部分 [AUTO-TRANSLATED:f7e0d5bc] + // recv delta part fci.append(delta_str); return fci; } diff --git a/src/Rtcp/RtcpFCI.h b/src/Rtcp/RtcpFCI.h index 43c45487..4e21d797 100644 --- a/src/Rtcp/RtcpFCI.h +++ b/src/Rtcp/RtcpFCI.h @@ -255,7 +255,8 @@ public: void check(size_t size); uint16_t getPid() const; uint16_t getBlp() const; - // 返回丢包列表,总长度17,第一个包必丢 + // 返回丢包列表,总长度17,第一个包必丢 [AUTO-TRANSLATED:5d5cd4b8] + // Return the list of lost packets, total length 17, the first packet must be lost // TODO: replace std::bitset std::vector getBitArray() const; std::string dumpString() const; @@ -321,9 +322,11 @@ public: enum class SymbolStatus : uint8_t { // Packet not received not_received = 0, - // Packet received, small delta (所谓small detal是指能用一个字节表示的数值) + // Packet received, small delta (所谓small detal是指能用一个字节表示的数值) [AUTO-TRANSLATED:50af3beb] + // Packet received, small delta (so-called small delta refers to a value that can be represented by one byte) small_delta = 1, - // Packet received, large ornegative delta (large即是能用两个字节表示的数值) + // Packet received, large ornegative delta (large即是能用两个字节表示的数值) [AUTO-TRANSLATED:7a16594d] + // Packet received, large or negative delta (large is a value that can be represented by two bytes) large_delta = 2, // Reserved reserved = 3 @@ -359,7 +362,8 @@ public: void check(size_t size); std::string dumpString(size_t total_size) const; uint16_t getBaseSeq() const; - // 单位64ms + // 单位64ms [AUTO-TRANSLATED:992ffed7] + // Unit 64ms uint32_t getReferenceTime() const; uint16_t getPacketCount() const; TwccPacketStatus getPacketChunkList(size_t total_size) const; @@ -367,13 +371,17 @@ public: static std::string create(uint32_t ref_time, uint8_t fb_pkt_count, TwccPacketStatus &status); private: - // base sequence number,基础序号,本次反馈的第一个包的序号;也就是RTP扩展头的序列号 + // base sequence number,基础序号,本次反馈的第一个包的序号;也就是RTP扩展头的序列号 [AUTO-TRANSLATED:4e43ffcc] + // base sequence number, basic sequence number, the sequence number of the first packet in this feedback; that is, the sequence number of the RTP extension header uint16_t base_seq; - // packet status count, 包个数,本次反馈包含多少个包的状态;从基础序号开始算 + // packet status count, 包个数,本次反馈包含多少个包的状态;从基础序号开始算 [AUTO-TRANSLATED:533efb94] + // packet status count, number of packets, how many packet statuses are included in this feedback; counted from the base sequence number uint16_t pkt_status_count; - // reference time,基准时间,绝对时间;计算该包中每个媒体包的到达时间都要基于这个基准时间计算 + // reference time,基准时间,绝对时间;计算该包中每个媒体包的到达时间都要基于这个基准时间计算 [AUTO-TRANSLATED:5265d98e] + // reference time, reference time, absolute time; the arrival time of each media packet in this packet is calculated based on this reference time uint8_t ref_time[3]; - // feedback packet count,反馈包号,本包是第几个transport-cc包,每次加1 | + // feedback packet count,反馈包号,本包是第几个transport-cc包,每次加1 | [AUTO-TRANSLATED:1ff6d73e] + // feedback packet count, feedback packet number, this packet is the nth transport-cc packet, incremented by 1 each time | uint8_t fb_pkt_count; }; #pragma pack(pop) diff --git a/src/Rtmp/FlvMuxer.cpp b/src/Rtmp/FlvMuxer.cpp index 5917f3a0..936a9b9a 100644 --- a/src/Rtmp/FlvMuxer.cpp +++ b/src/Rtmp/FlvMuxer.cpp @@ -30,7 +30,8 @@ void FlvMuxer::start(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr } if (!poller->isCurrentThread()) { weak_ptr weak_self = getSharedPtr(); - //延时两秒启动录制,目的是为了等待config帧收集完毕 + // 延时两秒启动录制,目的是为了等待config帧收集完毕 [AUTO-TRANSLATED:d359f59d] + // Start recording after a delay of two seconds, the purpose is to wait for the config frame to be collected. poller->doDelayTask(2000, [weak_self, poller, media, start_pts]() { auto strong_self = weak_self.lock(); if (strong_self) { @@ -91,7 +92,8 @@ BufferRaw::Ptr FlvMuxer::obtainBuffer(const void *data, size_t len) { } void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &src) { - //发送flv文件头 + // 发送flv文件头 [AUTO-TRANSLATED:ee2c5556] + // Send the flv file header. auto buffer = obtainBuffer(); buffer->setCapacity(sizeof(FLVHeader)); buffer->setSize(sizeof(FLVHeader)); @@ -105,7 +107,8 @@ void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &src) { header->length = htonl(FLVHeader::kFlvHeaderLength); header->have_video = src->haveVideo(); header->have_audio = src->haveAudio(); - //memset时已经赋值为0 + // memset时已经赋值为0 [AUTO-TRANSLATED:0f71eef1] + // It has already been assigned to 0 during memset. //header->previous_tag_size0 = 0; //flv header @@ -167,13 +170,15 @@ void FlvRecorder::startRecord(const EventPoller::Ptr &poller, const RtmpMediaSou const string &file_path) { stop(); lock_guard lck(_file_mtx); - //开辟文件写缓存 + // 开辟文件写缓存 [AUTO-TRANSLATED:22d1c17f] + // Allocate file write cache. std::shared_ptr fileBuf(new char[FILE_BUF_SIZE], [](char *ptr) { if (ptr) { delete[] ptr; } }); - //新建文件 + // 新建文件 [AUTO-TRANSLATED:f3d512a6] + // Create a new file. _file.reset(File::create_file(file_path, "wb"), [fileBuf](FILE *fp) { if (fp) { fflush(fp); @@ -184,7 +189,8 @@ void FlvRecorder::startRecord(const EventPoller::Ptr &poller, const RtmpMediaSou throw std::runtime_error(StrPrinter << "打开文件失败:" << file_path); } - //设置文件写缓存 + // 设置文件写缓存 [AUTO-TRANSLATED:a767e55c] + // Set the file write cache. setvbuf(_file.get(), fileBuf.get(), _IOFBF, FILE_BUF_SIZE); start(poller, media); } diff --git a/src/Rtmp/FlvPlayer.cpp b/src/Rtmp/FlvPlayer.cpp index 1a54f69e..8349ab8f 100644 --- a/src/Rtmp/FlvPlayer.cpp +++ b/src/Rtmp/FlvPlayer.cpp @@ -31,7 +31,8 @@ void FlvPlayer::play(const string &url) { void FlvPlayer::onResponseHeader(const string &status, const HttpClient::HttpHeader &header) { if (status != "200" && status != "206") { - // http状态码不符合预期 + // http状态码不符合预期 [AUTO-TRANSLATED:2b6996f7] + // HTTP status code does not meet expectations throw invalid_argument("bad http status code:" + status); } @@ -56,7 +57,8 @@ void FlvPlayer::onResponseCompleted(const SockException &ex) { void FlvPlayer::onResponseBody(const char *buf, size_t size) { if (!_benchmark_mode) { - // 性能测试模式不做数据解析,节省cpu + // 性能测试模式不做数据解析,节省cpu [AUTO-TRANSLATED:53e4af73] + // Performance test mode does not parse data to save CPU FlvSplitter::input(buf, size); } } diff --git a/src/Rtmp/FlvSplitter.cpp b/src/Rtmp/FlvSplitter.cpp index 732c9a31..7898e233 100644 --- a/src/Rtmp/FlvSplitter.cpp +++ b/src/Rtmp/FlvSplitter.cpp @@ -17,17 +17,21 @@ namespace mediakit { const char *FlvSplitter::onSearchPacketTail(const char *data, size_t len) { if (!_flv_started) { - //还没获取到flv头 + // 还没获取到flv头 [AUTO-TRANSLATED:d1c8deaa] + // Not yet got the flv header if (len < sizeof(FLVHeader)) { - //数据不够 + // 数据不够 [AUTO-TRANSLATED:72802244] + // Insufficient data return nullptr; } return data + sizeof(FLVHeader); } - //获取到flv头,处理tag数据 + // 获取到flv头,处理tag数据 [AUTO-TRANSLATED:a15a91da] + // Got the flv header, processing tag data if (len < sizeof(RtmpTagHeader)) { - //数据不够 + // 数据不够 [AUTO-TRANSLATED:72802244] + // Insufficient data return nullptr; } return data + sizeof(RtmpTagHeader); @@ -35,7 +39,8 @@ const char *FlvSplitter::onSearchPacketTail(const char *data, size_t len) { ssize_t FlvSplitter::onRecvHeader(const char *data, size_t len) { if (!_flv_started) { - //获取到flv头了 + // 获取到flv头了 [AUTO-TRANSLATED:1da417c0] + // Got the flv header auto header = reinterpret_cast(data); if (memcmp(header->flv, "FLV", 3)) { throw std::invalid_argument("不是flv容器格式!"); @@ -57,7 +62,8 @@ ssize_t FlvSplitter::onRecvHeader(const char *data, size_t len) { return 0; } - //获取到flv头,处理tag数据 + // 获取到flv头,处理tag数据 [AUTO-TRANSLATED:a15a91da] + // Got the flv header, processing tag data auto tag = reinterpret_cast(data); auto data_size = load_be24(tag->data_size); _type = tag->type; diff --git a/src/Rtmp/Rtmp.cpp b/src/Rtmp/Rtmp.cpp index 83e2a4fd..d1d44c11 100644 --- a/src/Rtmp/Rtmp.cpp +++ b/src/Rtmp/Rtmp.cpp @@ -74,7 +74,8 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track) { case CodecG711U: flvAudioType = (uint8_t)RtmpAudioCodec::g711u; break; case CodecOpus: { flvAudioType = (uint8_t)RtmpAudioCodec::opus; - // opus不通过flags获取音频相关信息 + // opus不通过flags获取音频相关信息 [AUTO-TRANSLATED:0ddf328b] + // opus does not get audio information through flags iSampleRate = 44100; iSampleBit = 16; iChannel = 2; @@ -82,7 +83,8 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track) { } case CodecAAC: { flvAudioType = (uint8_t)RtmpAudioCodec::aac; - // aac不通过flags获取音频相关信息 + // aac不通过flags获取音频相关信息 [AUTO-TRANSLATED:63ac5081] + // aac does not get audio information through flags iSampleRate = 44100; iSampleBit = 16; iChannel = 2; diff --git a/src/Rtmp/Rtmp.h b/src/Rtmp/Rtmp.h index ec87892e..fd02da7c 100644 --- a/src/Rtmp/Rtmp.h +++ b/src/Rtmp/Rtmp.h @@ -81,7 +81,8 @@ public: uint8_t chunk_id : 6; #else uint8_t chunk_id : 6; - //0、1、2、3分别对应 12、8、4、1长度 + // 0、1、2、3分别对应 12、8、4、1长度 [AUTO-TRANSLATED:31d67e40] + // 0, 1, 2, 3 correspond to lengths of 12, 8, 4, 1 respectively uint8_t fmt : 2; #endif uint8_t time_stamp[3]; @@ -99,27 +100,37 @@ public: //File version (for example, 0x01 for FLV version 1) uint8_t version; #if __BYTE_ORDER == __BIG_ENDIAN - //保留,置0 + // 保留,置0 [AUTO-TRANSLATED:46985374] + // Preserve, set to 0 uint8_t : 5; - //是否有音频 + // 是否有音频 [AUTO-TRANSLATED:9467870a] + // Whether there is audio uint8_t have_audio: 1; - //保留,置0 + // 保留,置0 [AUTO-TRANSLATED:46985374] + // Preserve, set to 0 uint8_t : 1; - //是否有视频 + // 是否有视频 [AUTO-TRANSLATED:42d0ed81] + // Whether there is video uint8_t have_video: 1; #else - //是否有视频 + // 是否有视频 [AUTO-TRANSLATED:42d0ed81] + // Whether there is video uint8_t have_video: 1; - //保留,置0 + // 保留,置0 [AUTO-TRANSLATED:46985374] + // Preserve, set to 0 uint8_t : 1; - //是否有音频 + // 是否有音频 [AUTO-TRANSLATED:9467870a] + // Whether there is audio uint8_t have_audio: 1; - //保留,置0 + // 保留,置0 [AUTO-TRANSLATED:46985374] + // Preserve, set to 0 uint8_t : 5; #endif - //The length of this header in bytes,固定为9 + // The length of this header in bytes,固定为9 [AUTO-TRANSLATED:126988fc] + // The length of this header in bytes, fixed to 9 uint32_t length; - //固定为0 + // 固定为0 [AUTO-TRANSLATED:d266c0a7] + // Fixed to 0 uint32_t previous_tag_size0; }; @@ -185,12 +196,16 @@ public: void clear(); - // video config frame和key frame都返回true - // 用于gop缓存定位 + // video config frame和key frame都返回true [AUTO-TRANSLATED:de025c52] + // video config frame and key frame both return true + // 用于gop缓存定位 [AUTO-TRANSLATED:828204e5] + // Used for gop cache positioning bool isVideoKeyFrame() const; - // aac config或h264/h265 config返回true,支持增强型rtmp - // 用于缓存解码配置信息 + // aac config或h264/h265 config返回true,支持增强型rtmp [AUTO-TRANSLATED:221955ec] + // aac config or h264/h265 config returns true, supports enhanced rtmp + // 用于缓存解码配置信息 [AUTO-TRANSLATED:19304f64] + // Used to cache decoding configuration information bool isConfigFrame() const; int getRtmpCodecId() const; @@ -207,12 +222,16 @@ private: RtmpPacket &operator=(const RtmpPacket &that); private: - //对象个数统计 + // 对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + // Object count statistics toolkit::ObjectStatistic _statistic; }; /** * rtmp metadata基类,用于描述rtmp格式信息 + * rtmp metadata base class, used to describe rtmp format information + + * [AUTO-TRANSLATED:8ced489c] */ class Metadata { public: @@ -231,6 +250,9 @@ protected: /** * metadata中除音视频外的其他描述部分 + * Other descriptive parts in metadata besides audio and video + + * [AUTO-TRANSLATED:e11f031f] */ class TitleMeta : public Metadata { public: @@ -256,7 +278,8 @@ public: AudioMeta(const AudioTrack::Ptr &audio); }; -//根据音频track获取flags +// 根据音频track获取flags [AUTO-TRANSLATED:a25fdd07] +// Get flags based on audio track uint8_t getAudioRtmpFlags(const Track::Ptr &track); ////////////////// rtmp video ////////////////////////// @@ -284,7 +307,8 @@ enum class RtmpVideoCodec : uint32_t { h264 = 7, // avc h265 = 12, // 国内扩展 - // 增强型rtmp FourCC + // 增强型rtmp FourCC [AUTO-TRANSLATED:442b77fb] + // Enhanced rtmp FourCC fourcc_vp9 = MKBETAG('v', 'p', '0', '9'), fourcc_av1 = MKBETAG('a', 'v', '0', '1'), fourcc_hevc = MKBETAG('h', 'v', 'c', '1') diff --git a/src/Rtmp/RtmpCodec.h b/src/Rtmp/RtmpCodec.h index a01f1f63..17caae14 100644 --- a/src/Rtmp/RtmpCodec.h +++ b/src/Rtmp/RtmpCodec.h @@ -26,6 +26,9 @@ public: /** * 设置rtmp环形缓存 + * Set rtmp ring buffer + + * [AUTO-TRANSLATED:0a25f795] */ void setRtmpRing(const RingType::Ptr &ring) { _ring = ring; @@ -34,6 +37,11 @@ public: /** * 输入rtmp包 * @param rtmp rtmp包 + * Input rtmp packet + * @param rtmp rtmp packet + + + * [AUTO-TRANSLATED:3a0f0599] */ virtual void inputRtmp(const RtmpPacket::Ptr &rtmp) { if (_ring) { diff --git a/src/Rtmp/RtmpDemuxer.cpp b/src/Rtmp/RtmpDemuxer.cpp index 151f6958..faa31f60 100644 --- a/src/Rtmp/RtmpDemuxer.cpp +++ b/src/Rtmp/RtmpDemuxer.cpp @@ -19,12 +19,14 @@ size_t RtmpDemuxer::trackCount(const AMFValue &metadata) { size_t ret = 0; metadata.object_for_each([&](const string &key, const AMFValue &val) { if (key == "videocodecid") { - // 找到视频 + // 找到视频 [AUTO-TRANSLATED:e66249fc] + // Find video ++ret; return; } if (key == "audiocodecid") { - // 找到音频 + // 找到音频 [AUTO-TRANSLATED:126ce656] + // Find audio ++ret; return; } @@ -60,12 +62,14 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val) { return; } if (key == "videocodecid") { - // 找到视频 + // 找到视频 [AUTO-TRANSLATED:e66249fc] + // Find video videocodecid = &val; return; } if (key == "audiocodecid") { - // 找到音频 + // 找到音频 [AUTO-TRANSLATED:126ce656] + // Find audio audiocodecid = &val; return; } @@ -79,12 +83,14 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val) { } }); if (videocodecid) { - // 有视频 + // 有视频 [AUTO-TRANSLATED:8d6ad811] + // Has video ret = true; makeVideoTrack(*videocodecid, videodatarate * 1024); } if (audiocodecid) { - // 有音频 + // 有音频 [AUTO-TRANSLATED:8f9ac7f1] + // Has audio ret = true; makeAudioTrack(*audiocodecid, audiosamplerate, audiochannels, audiosamplesize, audiodatarate * 1024); } @@ -93,7 +99,8 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val) { } if (ret) { - // metadata中存在track相关的描述,那么我们根据metadata判断有多少个track + // metadata中存在track相关的描述,那么我们根据metadata判断有多少个track [AUTO-TRANSLATED:47e02e95] + // If there is a track-related description in the metadata, we determine the number of tracks based on the metadata addTrackCompleted(); } return ret; @@ -140,15 +147,18 @@ void RtmpDemuxer::makeVideoTrack(const Track::Ptr &track, int bit_rate) { if (_video_rtmp_decoder) { return; } - // 生成Track对象 + // 生成Track对象 [AUTO-TRANSLATED:8c7aee28] + // Generate Track object _video_track = dynamic_pointer_cast(track); if (!_video_track) { return; } - // 生成rtmpCodec对象以便解码rtmp + // 生成rtmpCodec对象以便解码rtmp [AUTO-TRANSLATED:a3c81353] + // Generate rtmpCodec object to decode rtmp _video_rtmp_decoder = Factory::getRtmpDecoderByTrack(_video_track); if (!_video_rtmp_decoder) { - // 找不到相应的rtmp解码器,该track无效 + // 找不到相应的rtmp解码器,该track无效 [AUTO-TRANSLATED:bbea0d74] + // Cannot find the corresponding rtmp decoder, the track is invalid _video_track.reset(); return; } @@ -161,15 +171,18 @@ void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec, int sample_rate, in if (_audio_rtmp_decoder) { return; } - // 生成Track对象 + // 生成Track对象 [AUTO-TRANSLATED:8c7aee28] + // Generate Track object _audio_track = dynamic_pointer_cast(Factory::getAudioTrackByAmf(audioCodec, sample_rate, channels, sample_bit)); if (!_audio_track) { return; } - // 生成rtmpCodec对象以便解码rtmp + // 生成rtmpCodec对象以便解码rtmp [AUTO-TRANSLATED:a3c81353] + // Generate rtmpCodec object to decode rtmp _audio_rtmp_decoder = Factory::getRtmpDecoderByTrack(_audio_track); if (!_audio_rtmp_decoder) { - // 找不到相应的rtmp解码器,该track无效 + // 找不到相应的rtmp解码器,该track无效 [AUTO-TRANSLATED:bbea0d74] + // Cannot find the corresponding rtmp decoder, the track is invalid _audio_track.reset(); return; } diff --git a/src/Rtmp/RtmpDemuxer.h b/src/Rtmp/RtmpDemuxer.h index 85f358ed..e61bf874 100644 --- a/src/Rtmp/RtmpDemuxer.h +++ b/src/Rtmp/RtmpDemuxer.h @@ -31,12 +31,21 @@ public: /** * 开始解复用 * @param pkt rtmp包 + * Start demultiplexing + * @param pkt rtmp packet + + * [AUTO-TRANSLATED:3a6f81de] */ void inputRtmp(const RtmpPacket::Ptr &pkt); /** * 获取节目总时长 * @return 节目总时长,单位秒 + * Get the total duration of the program + * @return Total duration of the program, in seconds + + + * [AUTO-TRANSLATED:6b2ec56c] */ float getDuration() const; diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 55a7afd6..0baab485 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -32,6 +32,13 @@ namespace mediakit { * 其中metadata是非必须的,有些编码格式也没有config帧(比如MP3) * 只要生成了这三要素,那么要实现rtmp推流、rtmp服务器就很简单了 * rtmp推拉流协议中,先传递metadata,然后传递config帧,然后一直传递普通帧 + * Data abstraction of rtmp media source + * rtmp has three key elements: metadata, config frame, and ordinary frame + * Metadata is optional, and some encoding formats do not have config frames (such as MP3) + * As long as these three elements are generated, it is very simple to implement rtmp push stream and rtmp server + * In the rtmp push and pull stream protocol, metadata is transmitted first, then config frame, and then ordinary frames are continuously transmitted + + * [AUTO-TRANSLATED:72d515c8] */ class RtmpMediaSource : public MediaSource, public toolkit::RingDelegate, private PacketCache{ public: @@ -45,6 +52,13 @@ public: * @param app 应用名 * @param stream_id 流id * @param ring_size 可以设置固定的环形缓冲大小,0则自适应 + * Constructor + * @param vhost Virtual host name + * @param app Application name + * @param stream_id Stream id + * @param ring_size You can set a fixed ring buffer size, 0 is adaptive + + * [AUTO-TRANSLATED:5dd23423] */ RtmpMediaSource(const MediaTuple& tuple, int ring_size = RTMP_GOP_SIZE): MediaSource(RTMP_SCHEMA, tuple), _ring_size(ring_size) {} @@ -58,6 +72,9 @@ public: /** * 获取媒体源的环形缓冲 + * Get the ring buffer of the media source + + * [AUTO-TRANSLATED:75ac76b6] */ const RingType::Ptr &getRing() const { return _ring; @@ -71,6 +88,10 @@ public: /** * 获取播放器个数 * @return + * Get the number of players + * @return + + * [AUTO-TRANSLATED:0ba31e32] */ int readerCount() override { return _ring ? _ring->readerCount() : 0; @@ -78,6 +99,9 @@ public: /** * 获取metadata + * Get metadata + + * [AUTO-TRANSLATED:270cc022] */ template void getMetaData(const FUNC &func) const { @@ -89,6 +113,9 @@ public: /** * 获取所有的config帧 + * Get all config frames + + * [AUTO-TRANSLATED:cc3f5b85] */ template void getConfigFrame(const FUNC &func) { @@ -100,17 +127,27 @@ public: /** * 设置metadata + * Set metadata + + * [AUTO-TRANSLATED:e32234cf] */ virtual void setMetaData(const AMFValue &metadata); /** * 输入rtmp包 * @param pkt rtmp包 + * Input rtmp packet + * @param pkt rtmp packet + + * [AUTO-TRANSLATED:ac020e50] */ void onWrite(RtmpPacket::Ptr pkt, bool = true) override; /** * 获取当前时间戳 + * Get the current timestamp + + * [AUTO-TRANSLATED:42e38069] */ uint32_t getTimeStamp(TrackType trackType) override; @@ -132,9 +169,15 @@ private: * 批量flush rtmp包时触发该函数 * @param rtmp_list rtmp包列表 * @param key_pos 是否包含关键帧 + * Trigger this function when batch flushing rtmp packets + * @param rtmp_list rtmp packet list + * @param key_pos Whether it contains key frames + + * [AUTO-TRANSLATED:581fe3a4] */ void onFlush(std::shared_ptr > rtmp_list, bool key_pos) override { - //如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存 + // 如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存 [AUTO-TRANSLATED:5818a8d8] + // If there is no video, then there is no point in having a GOP cache, so is_key is always true to ensure that the GOP cache is always cleared _ring->write(std::move(rtmp_list), _have_video ? key_pos : true); } diff --git a/src/Rtmp/RtmpMediaSourceImp.cpp b/src/Rtmp/RtmpMediaSourceImp.cpp index ff5320e6..74c993f6 100644 --- a/src/Rtmp/RtmpMediaSourceImp.cpp +++ b/src/Rtmp/RtmpMediaSourceImp.cpp @@ -6,11 +6,13 @@ namespace mediakit { uint32_t RtmpMediaSource::getTimeStamp(TrackType trackType) { assert(trackType >= TrackInvalid && trackType < TrackMax); if (trackType != TrackInvalid) { - // 获取某track的时间戳 + // 获取某track的时间戳 [AUTO-TRANSLATED:2735ed10] + // Get the timestamp of a track return _track_stamps[trackType]; } - // 获取所有track的最小时间戳 + // 获取所有track的最小时间戳 [AUTO-TRANSLATED:179a75cd] + // Get the minimum timestamp of all tracks uint32_t ret = UINT32_MAX; for (auto &stamp : _track_stamps) { if (stamp > 0 && stamp < ret) { @@ -47,7 +49,8 @@ void RtmpMediaSource::setMetaData(const AMFValue &metadata) { void RtmpMediaSource::onWrite(RtmpPacket::Ptr pkt, bool /*= true*/) { bool is_video = pkt->type_id == MSG_VIDEO; _speed[is_video ? TrackVideo : TrackAudio] += pkt->size(); - // 保存当前时间戳 + // 保存当前时间戳 [AUTO-TRANSLATED:2b09ff42] + // Save the current timestamp switch (pkt->type_id) { case MSG_VIDEO: _track_stamps[TrackVideo] = pkt->time_stamp, _have_video = true; break; case MSG_AUDIO: _track_stamps[TrackAudio] = pkt->time_stamp, _have_audio = true; break; @@ -58,7 +61,8 @@ void RtmpMediaSource::onWrite(RtmpPacket::Ptr pkt, bool /*= true*/) { std::lock_guard lock(_mtx); _config_frame_map[pkt->type_id] = pkt; if (!_ring) { - // 注册后收到config帧更新到各播放器 + // 注册后收到config帧更新到各播放器 [AUTO-TRANSLATED:7200693d] + // After registration, receive the config frame and update it to each player return; } } @@ -73,8 +77,10 @@ void RtmpMediaSource::onWrite(RtmpPacket::Ptr pkt, bool /*= true*/) { strong_self->onReaderChanged(size); }; - // GOP默认缓冲512组RTMP包,每组RTMP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTMP包), - // 每次遇到关键帧第一个RTMP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开) + // GOP默认缓冲512组RTMP包,每组RTMP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTMP包), [AUTO-TRANSLATED:4a372774] + // GOP defaults to buffering 512 groups of RTMP packets, each group of RTMP packets has the same timestamp (if merge writing is enabled, then each group is the RTMP packet within the merge writing time), + // 每次遇到关键帧第一个RTMP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开) [AUTO-TRANSLATED:dee67297] + // Every time a key frame's first RTMP packet is encountered, the GOP cache will be cleared (because there is a new key frame, which can also achieve instant opening) _ring = std::make_shared(_ring_size, std::move(lam)); if (_metadata) { regist(); @@ -93,7 +99,8 @@ RtmpMediaSourceImp::RtmpMediaSourceImp(const MediaTuple &tuple, int ringSize) void RtmpMediaSourceImp::setMetaData(const AMFValue &metadata) { if (!_demuxer->loadMetaData(metadata)) { - // 该metadata无效,需要重新生成 + // 该metadata无效,需要重新生成 [AUTO-TRANSLATED:44a65c0c] + // This metadata is invalid and needs to be regenerated _metadata = metadata; _recreate_metadata = true; } @@ -102,12 +109,14 @@ void RtmpMediaSourceImp::setMetaData(const AMFValue &metadata) { void RtmpMediaSourceImp::onWrite(RtmpPacket::Ptr pkt, bool /*= true*/) { if (!_all_track_ready || _muxer->isEnabled()) { - // 未获取到所有Track后,或者开启转协议,那么需要解复用rtmp + // 未获取到所有Track后,或者开启转协议,那么需要解复用rtmp [AUTO-TRANSLATED:76f6f56e] + // If all Tracks are not obtained, or protocol conversion is enabled, then demultiplexing rtmp is required _demuxer->inputRtmp(pkt); } GET_CONFIG(bool, directProxy, Rtmp::kDirectProxy); if (directProxy) { - //直接代理模式才直接使用原始rtmp + // 直接代理模式才直接使用原始rtmp [AUTO-TRANSLATED:ece580ea] + // Only direct proxy mode uses the original rtmp directly RtmpMediaSource::onWrite(std::move(pkt)); } } @@ -123,7 +132,8 @@ void RtmpMediaSourceImp::setProtocolOption(const ProtocolOption &option) { _muxer = std::make_shared(_tuple, _demuxer->getDuration(), _option); _muxer->setMediaListener(getListener()); _muxer->setTrackListener(std::static_pointer_cast(shared_from_this())); - // 让_muxer对象拦截一部分事件(比如说录像相关事件) + // 让_muxer对象拦截一部分事件(比如说录像相关事件) [AUTO-TRANSLATED:7d27c400] + // Let the _muxer object intercept some events (such as recording related events) MediaSource::setListener(_muxer); for (auto &track : _demuxer->getTracks(false)) { @@ -158,7 +168,8 @@ void RtmpMediaSourceImp::onAllTrackReady() { _all_track_ready = true; if (_recreate_metadata) { - // 更新metadata + // 更新metadata [AUTO-TRANSLATED:396f34c7] + // Update metadata for (auto &track : _muxer->getTracks()) { Metadata::addTrack(_metadata, track); } @@ -168,10 +179,12 @@ void RtmpMediaSourceImp::onAllTrackReady() { void RtmpMediaSourceImp::setListener(const std::weak_ptr &listener) { if (_muxer) { - //_muxer对象不能处理的事件再给listener处理 + // _muxer对象不能处理的事件再给listener处理 [AUTO-TRANSLATED:47858305] + // Events that the _muxer object cannot handle are then handled by the listener _muxer->setMediaListener(listener); } else { - // 未创建_muxer对象,事件全部给listener处理 + // 未创建_muxer对象,事件全部给listener处理 [AUTO-TRANSLATED:54efbd82] + // If the _muxer object is not created, all events are handled by the listener MediaSource::setListener(listener); } } diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index 31857c65..925b864e 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -34,26 +34,45 @@ public: * @param app 应用名 * @param id 流id * @param ringSize 环形缓存大小 + * Constructor + * @param vhost Virtual host + * @param app Application name + * @param id Stream id + * @param ringSize Ring buffer size + + * [AUTO-TRANSLATED:7679d212] */ RtmpMediaSourceImp(const MediaTuple& tuple, int ringSize = RTMP_GOP_SIZE); /** * 设置metadata + * Set metadata + + * [AUTO-TRANSLATED:e32234cf] */ void setMetaData(const AMFValue &metadata) override; /** * 输入rtmp并解析 + * Input rtmp and parse + + * [AUTO-TRANSLATED:de255b79] */ void onWrite(RtmpPacket::Ptr pkt, bool = true) override; /** * 获取观看总人数,包括(hls/rtsp/rtmp) + * Get total number of viewers, including (hls/rtsp/rtmp) + + * [AUTO-TRANSLATED:19a26d5a] */ int totalReaderCount() override; /** * 设置协议转换 + * Set protocol conversion + + * [AUTO-TRANSLATED:f7206bf3] */ void setProtocolOption(const ProtocolOption &option); @@ -63,11 +82,17 @@ public: /** * _demuxer触发的添加Track事件 + * _demuxer triggered add Track event + + * [AUTO-TRANSLATED:80dbcf16] */ bool addTrack(const Track::Ptr &track) override; /** * _demuxer触发的Track添加完毕事件 + * _demuxer triggered Track add complete event + + * [AUTO-TRANSLATED:939cb312] */ void addTrackCompleted() override; @@ -75,12 +100,20 @@ public: /** * _muxer触发的所有Track就绪的事件 + * _muxer triggered all Track ready event + + * [AUTO-TRANSLATED:1d34b7e0] */ void onAllTrackReady() override; /** * 设置事件监听器 * @param listener 监听器 + * Set event listener + * @param listener Listener + + + * [AUTO-TRANSLATED:d829419b] */ void setListener(const std::weak_ptr &listener) override; diff --git a/src/Rtmp/RtmpMediaSourceMuxer.h b/src/Rtmp/RtmpMediaSourceMuxer.h index 62141ae0..6a587ea5 100644 --- a/src/Rtmp/RtmpMediaSourceMuxer.h +++ b/src/Rtmp/RtmpMediaSourceMuxer.h @@ -80,7 +80,8 @@ public: } bool isEnabled() { - //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + // 缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 [AUTO-TRANSLATED:7cfd4d49] + // The inputFrame function is still allowed to be triggered when the cache has not been cleared, so that the cache can be cleared in time. return _option.rtmp_demand ? (_clear_cache ? true : _enabled) : true; } diff --git a/src/Rtmp/RtmpMuxer.cpp b/src/Rtmp/RtmpMuxer.cpp index 5a5e8b2d..6ea1c575 100644 --- a/src/Rtmp/RtmpMuxer.cpp +++ b/src/Rtmp/RtmpMuxer.cpp @@ -24,7 +24,8 @@ RtmpMuxer::RtmpMuxer(const TitleMeta::Ptr &title) { bool RtmpMuxer::addTrack(const Track::Ptr &track) { if (_track_existed[track->getTrackType()]) { - // rtmp不支持多个同类型track + // rtmp不支持多个同类型track [AUTO-TRANSLATED:c69a7864] + // rtmp does not support multiple tracks of the same type WarnL << "Already add a track kind of: " << track->getTrackTypeStr() << ", ignore track: " << track->getCodecName(); return false; } @@ -36,13 +37,16 @@ bool RtmpMuxer::addTrack(const Track::Ptr &track) { return false; } - // 标记已经存在该类型track + // 标记已经存在该类型track [AUTO-TRANSLATED:ed79ebb5] + // Mark that a track of this type already exists _track_existed[track->getTrackType()] = true; - // 设置rtmp输出环形缓存 + // 设置rtmp输出环形缓存 [AUTO-TRANSLATED:d65af70c] + // Set the rtmp output circular buffer encoder->setRtmpRing(_rtmp_ring); - // 添加metadata + // 添加metadata [AUTO-TRANSLATED:eaf2f5ae] + // Add metadata Metadata::addTrack(_metadata, track); return true; } diff --git a/src/Rtmp/RtmpMuxer.h b/src/Rtmp/RtmpMuxer.h index d029a632..6cc50abc 100644 --- a/src/Rtmp/RtmpMuxer.h +++ b/src/Rtmp/RtmpMuxer.h @@ -24,44 +24,72 @@ public: /** * 构造函数 + * Constructor + + * [AUTO-TRANSLATED:41469869] */ RtmpMuxer(const TitleMeta::Ptr &title); /** * 获取完整的SDP字符串 * @return SDP字符串 + * Get the complete SDP string + * @return SDP string + + * [AUTO-TRANSLATED:f5d1b0a6] */ const AMFValue &getMetadata() const ; /** * 获取rtmp环形缓存 * @return + * Get the rtmp ring buffer + * @return + + * [AUTO-TRANSLATED:81679f3c] */ RtmpRing::RingType::Ptr getRtmpRing() const; /** * 添加ready状态的track + * Add a ready state track + + * [AUTO-TRANSLATED:2d8138b3] */ bool addTrack(const Track::Ptr & track) override; /** * 写入帧数据 * @param frame 帧 + * Write frame data + * @param frame frame + + * [AUTO-TRANSLATED:b7c92013] */ bool inputFrame(const Frame::Ptr &frame) override; /** * 刷新输出所有frame缓存 + * Flush all frame buffers + + * [AUTO-TRANSLATED:adaea568] */ void flush() override; /** * 重置所有track + * Reset all tracks + + * [AUTO-TRANSLATED:f203fa3e] */ void resetTracks() override ; /** * 生成config包 + * Generate config package + + + * [AUTO-TRANSLATED:8f851364] */ void makeConfigPacket(); diff --git a/src/Rtmp/RtmpPlayer.cpp b/src/Rtmp/RtmpPlayer.cpp index abc45387..c758d98d 100644 --- a/src/Rtmp/RtmpPlayer.cpp +++ b/src/Rtmp/RtmpPlayer.cpp @@ -58,7 +58,8 @@ void RtmpPlayer::play(const string &url) { _stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL); auto app_second = findSubString(_stream_id.data(), nullptr, "/"); if (!app_second.empty() && app_second.find('?') == std::string::npos) { - // _stream_id存在多级;不包含'?', 说明分割符'/'不是url参数的一部分 + // _stream_id存在多级;不包含'?', 说明分割符'/'不是url参数的一部分 [AUTO-TRANSLATED:ef820905] + // _stream_id exists at multiple levels; it does not contain '?', indicating that the delimiter '/' is not part of the url parameter _app += "/" + app_second; _stream_id.erase(0, app_second.size() + 1); } @@ -92,33 +93,40 @@ void RtmpPlayer::play(const string &url) { } void RtmpPlayer::onError(const SockException &ex){ - //定时器_pPlayTimer为空后表明握手结束了 + // 定时器_pPlayTimer为空后表明握手结束了 [AUTO-TRANSLATED:6e2661f4] + // The timer _pPlayTimer is empty after the handshake is finished onPlayResult_l(ex, !_play_timer); } void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) { if (ex.getErrCode() == Err_shutdown) { - //主动shutdown的,不触发回调 + // 主动shutdown的,不触发回调 [AUTO-TRANSLATED:bd97b1c1] + // Active shutdown does not trigger a callback return; } WarnL << ex.getErrCode() << " " << ex; if (!handshake_done) { - //开始播放阶段 + // 开始播放阶段 [AUTO-TRANSLATED:a246c5ee] + // Start playback stage _play_timer.reset(); - //是否为性能测试模式 + // 是否为性能测试模式 [AUTO-TRANSLATED:1fde8234] + // Whether it is a performance test mode _benchmark_mode = (*this)[Client::kBenchmarkMode].as(); onPlayResult(ex); } else if (ex) { - //播放成功后异常断开回调 + // 播放成功后异常断开回调 [AUTO-TRANSLATED:b5c5fa80] + // Callback for abnormal disconnection after successful playback onShutdown(ex); } else { - //恢复播放 + // 恢复播放 [AUTO-TRANSLATED:19a73f21] + // Resume playback onResume(); } if (!ex) { - //播放成功,恢复rtmp接收超时定时器 + // 播放成功,恢复rtmp接收超时定时器 [AUTO-TRANSLATED:29b58110] + // After successful playback, restore the rtmp receive timeout timer _rtmp_recv_ticker.resetTime(); auto timeout_ms = (*this)[Client::kMediaTimeoutMS].as(); weak_ptr weak_self = static_pointer_cast(shared_from_this()); @@ -128,14 +136,16 @@ void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) { return false; } if (strong_self->_rtmp_recv_ticker.elapsedTime() > timeout_ms) { - //接收rtmp媒体数据超时 + // 接收rtmp媒体数据超时 [AUTO-TRANSLATED:e14bc1fe] + // Receive rtmp media data timeout SockException ex(Err_timeout, "receive rtmp timeout"); strong_self->onPlayResult_l(ex, true); return false; } return true; }; - //创建rtmp数据接收超时检测定时器 + // 创建rtmp数据接收超时检测定时器 [AUTO-TRANSLATED:d255312b] + // Create an rtmp data receive timeout detection timer _rtmp_recv_timer = std::make_shared(timeout_ms / 2000.0f, lam, getPoller()); } else { shutdown(SockException(Err_shutdown,"teardown")); @@ -158,14 +168,16 @@ void RtmpPlayer::onConnect(const SockException &err) { void RtmpPlayer::onRecv(const Buffer::Ptr &buf){ try { if (_benchmark_mode && !_play_timer) { - //在性能测试模式下,如果rtmp握手完毕后,不再解析rtmp包 + // 在性能测试模式下,如果rtmp握手完毕后,不再解析rtmp包 [AUTO-TRANSLATED:a39356cc] + // In performance test mode, if the rtmp handshake is complete, the rtmp packet will no longer be parsed _rtmp_recv_ticker.resetTime(); return; } onParseRtmp(buf->data(), buf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); - //定时器_pPlayTimer为空后表明握手结束了 + // 定时器_pPlayTimer为空后表明握手结束了 [AUTO-TRANSLATED:6e2661f4] + // The timer _pPlayTimer is empty after the handshake is finished onPlayResult_l(ex, !_play_timer); } } @@ -182,15 +194,20 @@ void RtmpPlayer::send_connect() { AMFValue obj(AMF_OBJECT); obj.set("app", _app); obj.set("tcUrl", _tc_url); - //未使用代理 + // 未使用代理 [AUTO-TRANSLATED:fa1ef5d7] + // No proxy used obj.set("fpad", false); - //参考librtmp,什么作用? + // 参考librtmp,什么作用? [AUTO-TRANSLATED:c6e3349f] + // Refer to librtmp, what is the role? obj.set("capabilities", 15); - //SUPPORT_VID_CLIENT_SEEK 支持seek + // SUPPORT_VID_CLIENT_SEEK 支持seek [AUTO-TRANSLATED:81d2bb06] + // SUPPORT_VID_CLIENT_SEEK supports seek obj.set("videoFunction", 1); - //只支持aac + // 只支持aac [AUTO-TRANSLATED:ab086b5b] + // Only supports aac obj.set("audioCodecs", (double) (0x0400)); - //只支持H264 + // 只支持H264 [AUTO-TRANSLATED:d8fb8696] + // Only supports H264 obj.set("videoCodecs", (double) (0x0080)); AMFValue fourCcList(AMF_STRICT_ARRAY); @@ -257,7 +274,8 @@ void RtmpPlayer::send_pause(bool pause) { if (!pause) { onPlayResult_l(SockException(Err_success, "resum rtmp success"), true); } else { - //暂停播放 + // 暂停播放 [AUTO-TRANSLATED:09cc521a] + // Pause playback _rtmp_recv_timer.reset(); } } @@ -309,7 +327,8 @@ void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) { auto level = val["level"]; auto code = val["code"].as_string(); if (level.type() == AMF_STRING) { - // warning 不应该断开 + // warning 不应该断开 [AUTO-TRANSLATED:6db13b98] + // warning should not be disconnected if (level.as_string() != "status" && level.as_string() != "warning") { throw std::runtime_error(StrPrinter << "onStatus 失败:" << level.as_string() << " " << code << endl); } @@ -335,18 +354,22 @@ void RtmpPlayer::onStreamDry(uint32_t stream_index) { void RtmpPlayer::onMediaData_l(RtmpPacket::Ptr chunk_data) { _rtmp_recv_ticker.resetTime(); if (!_play_timer) { - //已经触发了onPlayResult事件,直接触发onMediaData事件 + // 已经触发了onPlayResult事件,直接触发onMediaData事件 [AUTO-TRANSLATED:5c12bd46] + // The onPlayResult event has been triggered, directly trigger the onMediaData event onRtmpPacket(chunk_data); return; } if (chunk_data->isConfigFrame()) { - //输入配置帧以便初始化完成各个track + // 输入配置帧以便初始化完成各个track [AUTO-TRANSLATED:2f571d31] + // Input configuration frame to initialize each track onRtmpPacket(chunk_data); } else { - //先触发onPlayResult事件,这个时候解码器才能初始化完毕 + // 先触发onPlayResult事件,这个时候解码器才能初始化完毕 [AUTO-TRANSLATED:403c9195] + // Trigger the onPlayResult event first, at this time the decoder can be initialized onPlayResult_l(SockException(Err_success, "play rtmp success"), false); - //触发onPlayResult事件后,再把帧数据输入到解码器 + // 触发onPlayResult事件后,再把帧数据输入到解码器 [AUTO-TRANSLATED:bf058334] + // After triggering the onPlayResult event, input the frame data to the decoder onRtmpPacket(chunk_data); } } @@ -383,7 +406,8 @@ void RtmpPlayer::onRtmpChunk(RtmpPacket::Ptr packet) { case MSG_VIDEO: { auto idx = chunk_data.type_id % 2; if (_now_stamp_ticker[idx].elapsedTime() > 500) { - //计算播放进度时间轴用 + // 计算播放进度时间轴用 [AUTO-TRANSLATED:383fd62c] + // Used to calculate the playback progress timeline _now_stamp[idx] = chunk_data.time_stamp; } if (!_metadata_got) { diff --git a/src/Rtmp/RtmpPlayer.h b/src/Rtmp/RtmpPlayer.h index ccac9360..d11cda2a 100644 --- a/src/Rtmp/RtmpPlayer.h +++ b/src/Rtmp/RtmpPlayer.h @@ -24,7 +24,8 @@ namespace mediakit { -//实现了rtmp播放器协议部分的功能,及数据接收功能 +// 实现了rtmp播放器协议部分的功能,及数据接收功能 [AUTO-TRANSLATED:1548a233] +// Implemented the rtmp player protocol part functionality, and data receiving functionality class RtmpPlayer : public PlayerBase, public toolkit::TcpClient, public RtmpProtocol { public: using Ptr = std::shared_ptr; @@ -44,7 +45,8 @@ protected: protected: void onMediaData_l(RtmpPacket::Ptr chunk_data); - //在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了 + // 在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了 [AUTO-TRANSLATED:c2d8c7a2] + // Trigger onPlayResult_l after getting the config frame (instead of receiving the play command reply), so all tracks are initialized at this time void onPlayResult_l(const toolkit::SockException &ex, bool handshake_done); //form Tcpclient @@ -83,10 +85,12 @@ private: bool _paused = false; bool _metadata_got = false; - //是否为性能测试模式 + // 是否为性能测试模式 [AUTO-TRANSLATED:1fde8234] + // Whether it is performance test mode bool _benchmark_mode = false; - //播放进度控制 + // 播放进度控制 [AUTO-TRANSLATED:62b3ee44] + // Playback progress control uint32_t _seek_ms = 0; uint32_t _fist_stamp[2] = {0, 0}; uint32_t _now_stamp[2] = {0, 0}; @@ -94,13 +98,17 @@ private: std::deque > _deque_on_status; std::unordered_map > _map_on_result; - //rtmp接收超时计时器 + // rtmp接收超时计时器 [AUTO-TRANSLATED:292af22c] + // Rtmp receive timeout timer toolkit::Ticker _rtmp_recv_ticker; - //心跳发送定时器 + // 心跳发送定时器 [AUTO-TRANSLATED:04ac8e65] + // Heartbeat send timer std::shared_ptr _beat_timer; - //播放超时定时器 + // 播放超时定时器 [AUTO-TRANSLATED:74d33688] + // Playback timeout timer std::shared_ptr _play_timer; - //rtmp接收超时定时器 + // rtmp接收超时定时器 [AUTO-TRANSLATED:8c121e8c] + // Rtmp receive timeout timer std::shared_ptr _rtmp_recv_timer; }; diff --git a/src/Rtmp/RtmpPlayerImp.h b/src/Rtmp/RtmpPlayerImp.h index ddcf469f..33784a37 100644 --- a/src/Rtmp/RtmpPlayerImp.h +++ b/src/Rtmp/RtmpPlayerImp.h @@ -43,9 +43,11 @@ public: } private: - //派生类回调函数 + // 派生类回调函数 [AUTO-TRANSLATED:61e20903] + // Derived class callback function bool onMetadata(const AMFValue &val) override { - //无metadata或metadata中无track信息时,需要从数据包中获取track + // 无metadata或metadata中无track信息时,需要从数据包中获取track [AUTO-TRANSLATED:92a71803] + // When there is no metadata or no track information in the metadata, it is necessary to obtain the track from the data packet _wait_track_ready = this->Super::operator[](Client::kWaitTrackReady).template as() || RtmpDemuxer::trackCount(val) == 0; onCheckMeta_l(val); return true; @@ -53,7 +55,8 @@ private: void onRtmpPacket(RtmpPacket::Ptr chunkData) override { if (!_demuxer) { - //有些rtmp流没metadata + // 有些rtmp流没metadata [AUTO-TRANSLATED:2509786f] + // Some rtmp streams do not have metadata onCheckMeta_l(TitleMeta().getMetadata()); } _demuxer->inputRtmp(chunkData); diff --git a/src/Rtmp/RtmpProtocol.cpp b/src/Rtmp/RtmpProtocol.cpp index 010e30d4..191fcbed 100644 --- a/src/Rtmp/RtmpProtocol.cpp +++ b/src/Rtmp/RtmpProtocol.cpp @@ -36,7 +36,8 @@ static string openssl_HMACsha256(const void *key, size_t key_len, const void *da unsigned int out_len; #if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER > 0x10100000L) - //openssl 1.1.0新增api,老版本api作废 + // openssl 1.1.0新增api,老版本api作废 [AUTO-TRANSLATED:4c7b59a8] + // OpenSSL 1.1.0 added new APIs, old APIs are deprecated HMAC_CTX *ctx = HMAC_CTX_new(); HMAC_CTX_reset(ctx); HMAC_Init_ex(ctx, key, (int)key_len, EVP_sha256(), NULL); @@ -171,10 +172,12 @@ void RtmpProtocol::sendInvoke(const string &cmd, const AMFValue &val) { void RtmpProtocol::sendRequest(int cmd, const string& str) { if (cmd <= MSG_SET_PEER_BW) { - // 若 cmd 属于 Protocol Control Messages ,则应使用 chunk id 2 发送 + // 若 cmd 属于 Protocol Control Messages ,则应使用 chunk id 2 发送 [AUTO-TRANSLATED:3f17e21f] + // If cmd belongs to Protocol Control Messages, it should be sent using chunk id 2 sendRtmp(cmd, _stream_index, str, 0, CHUNK_NETWORK); } else { - // 否则使用 chunk id 发送(任意值3-128,参见 obs 及 ffmpeg 选取 3) + // 否则使用 chunk id 发送(任意值3-128,参见 obs 及 ffmpeg 选取 3) [AUTO-TRANSLATED:65f8d861] + // Otherwise, use chunk id to send (any value 3-128, see obs and ffmpeg select 3) sendRtmp(cmd, _stream_index, str, 0, CHUNK_SYSTEM); } } @@ -210,14 +213,17 @@ void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::P auto strErr = StrPrinter << "不支持发送该类型的块流 ID:" << chunk_id << endl; throw std::runtime_error(strErr); } - //是否有扩展时间戳 + // 是否有扩展时间戳 [AUTO-TRANSLATED:85bae69f] + // Does it have an extended timestamp bool ext_stamp = stamp >= 0xFFFFFF; - //rtmp头 + // rtmp头 [AUTO-TRANSLATED:915b278c] + // RTMP header BufferRaw::Ptr buffer_header = obtainBuffer(); buffer_header->setCapacity(sizeof(RtmpHeader)); buffer_header->setSize(sizeof(RtmpHeader)); - //对rtmp头赋值,如果使用整形赋值,在arm android上可能由于数据对齐导致总线错误的问题 + // 对rtmp头赋值,如果使用整形赋值,在arm android上可能由于数据对齐导致总线错误的问题 [AUTO-TRANSLATED:90c79d70] + // Assign values to the RTMP header. If using integer assignment, it may cause bus errors on ARM Android due to data alignment issues RtmpHeader *header = (RtmpHeader *) buffer_header->data(); header->fmt = 0; header->chunk_id = chunk_id; @@ -225,20 +231,24 @@ void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::P set_be24(header->time_stamp, ext_stamp ? 0xFFFFFF : stamp); set_be24(header->body_size, (uint32_t)buf->size()); set_le32(header->stream_index, stream_index); - //发送rtmp头 + // 发送rtmp头 [AUTO-TRANSLATED:3c038cd5] + // Send RTMP header onSendRawData(std::move(buffer_header)); - //扩展时间戳字段 + // 扩展时间戳字段 [AUTO-TRANSLATED:6f79a475] + // Extended timestamp field BufferRaw::Ptr buffer_ext_stamp; if (ext_stamp) { - //生成扩展时间戳 + // 生成扩展时间戳 [AUTO-TRANSLATED:cd22977a] + // Generate extended timestamp buffer_ext_stamp = obtainBuffer(); buffer_ext_stamp->setCapacity(4); buffer_ext_stamp->setSize(4); set_be32(buffer_ext_stamp->data(), stamp); } - //生成一个字节的flag,标明是什么chunkId + // 生成一个字节的flag,标明是什么chunkId [AUTO-TRANSLATED:fbdbf476] + // Generate a one-byte flag to indicate what chunkId it is BufferRaw::Ptr buffer_flags = obtainBuffer(); buffer_flags->setCapacity(1); buffer_flags->setSize(1); @@ -254,7 +264,8 @@ void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::P totalSize += 1; } if (ext_stamp) { - //扩展时间戳 + // 扩展时间戳 [AUTO-TRANSLATED:f263b9bf] + // Extended timestamp onSendRawData(buffer_ext_stamp); totalSize += 4; } @@ -275,12 +286,15 @@ void RtmpProtocol::onParseRtmp(const char *data, size_t size) { } const char *RtmpProtocol::onSearchPacketTail(const char *data,size_t len){ - //移动拷贝提高性能 + // 移动拷贝提高性能 [AUTO-TRANSLATED:1e6fce8d] + // Move copy to improve performance auto next_step_func(std::move(_next_step_func)); - //执行下一步 + // 执行下一步 [AUTO-TRANSLATED:4199a3e3] + // Execute the next step auto ret = next_step_func(data, len); if (!_next_step_func) { - //为设置下一步,恢复之 + // 为设置下一步,恢复之 [AUTO-TRANSLATED:b5494ef3] + // Set the next step, restore it next_step_func.swap(_next_step_func); } return ret; @@ -288,7 +302,8 @@ const char *RtmpProtocol::onSearchPacketTail(const char *data,size_t len){ ////for client//// void RtmpProtocol::startClientSession(const function &func, bool complex) { - //发送 C0C1 + // 发送 C0C1 [AUTO-TRANSLATED:5da06136] + // Send C0C1 char handshake_head = HANDSHAKE_PLAINTEXT; onSendRawData(obtainBuffer(&handshake_head, 1)); RtmpHandshake c1(0); @@ -297,25 +312,30 @@ void RtmpProtocol::startClientSession(const function &func, bool complex } onSendRawData(obtainBuffer((char *) (&c1), sizeof(c1))); _next_step_func = [this, func](const char *data, size_t len) { - //等待 S0+S1+S2 + // 等待 S0+S1+S2 [AUTO-TRANSLATED:3f95dd6d] + // Wait for S0+S1+S2 return handle_S0S1S2(data, len, func); }; } const char* RtmpProtocol::handle_S0S1S2(const char *data, size_t len, const function &func) { if (len < 1 + 2 * C1_HANDSHARK_SIZE) { - //数据不够 + // 数据不够 [AUTO-TRANSLATED:72802244] + // Not enough data return nullptr; } if (data[0] != HANDSHAKE_PLAINTEXT) { throw std::runtime_error("only plaintext[0x03] handshake supported"); } - //发送 C2 + // 发送 C2 [AUTO-TRANSLATED:e51c339e] + // Send C2 const char *pcC2 = data + 1; onSendRawData(obtainBuffer(pcC2, C1_HANDSHARK_SIZE)); - //握手结束 + // 握手结束 [AUTO-TRANSLATED:9df763ff] + // Handshake finished _next_step_func = [this](const char *data, size_t len) { - //握手结束并且开始进入解析命令模式 + // 握手结束并且开始进入解析命令模式 [AUTO-TRANSLATED:3b4e0886] + // Handshake finished and start entering command parsing mode return handle_rtmp(data, len); }; func(); @@ -347,24 +367,30 @@ const char * RtmpProtocol::handle_C0C1(const char *data, size_t len) { } void RtmpProtocol::handle_C1_simple(const char *data){ - //发送S0 + // 发送S0 [AUTO-TRANSLATED:79eca118] + // Send S0 char handshake_head = HANDSHAKE_PLAINTEXT; onSendRawData(obtainBuffer(&handshake_head, 1)); - //发送S1 + // 发送S1 [AUTO-TRANSLATED:a86847cf] + // Send S1 RtmpHandshake s1(0); onSendRawData(obtainBuffer((char *) &s1, C1_HANDSHARK_SIZE)); - //发送S2 + // 发送S2 [AUTO-TRANSLATED:dbcf960a] + // Send S2 onSendRawData(obtainBuffer(data + 1, C1_HANDSHARK_SIZE)); - //等待C2 + // 等待C2 [AUTO-TRANSLATED:b9900831] + // Wait for C2 _next_step_func = [this](const char *data, size_t len) { - //握手结束并且开始进入解析命令模式 + // 握手结束并且开始进入解析命令模式 [AUTO-TRANSLATED:3b4e0886] + // Handshake finished and start entering command parsing mode return handle_C2(data, len); }; } #ifdef ENABLE_OPENSSL void RtmpProtocol::handle_C1_complex(const char *data){ - //参考自:http://blog.csdn.net/win_lin/article/details/13006803 + // 参考自:http://blog.csdn.net/win_lin/article/details/13006803 [AUTO-TRANSLATED:97a05bd3] + // Refer to: http://blog.csdn.net/win_lin/article/details/13006803 //skip c0,time,version const char *c1_start = data + 1; const char *schema_start = c1_start + 8; @@ -384,7 +410,8 @@ void RtmpProtocol::handle_C1_complex(const char *data){ send_complex_S0S1S2(0, digest); // InfoL << "schema0"; } catch (std::exception &) { - //貌似flash从来都不用schema1 + // 貌似flash从来都不用schema1 [AUTO-TRANSLATED:2c6d140f] + // It seems that flash never uses schema1 // WarnL << "try rtmp complex schema0 failed:" << ex.what(); try { /* c1s1 schema1 @@ -449,6 +476,13 @@ string RtmpProtocol::get_C1_digest(const uint8_t *ptr,char **digestPos){ random-data: (offset)bytes digest-data: 32bytes random-data: (764-4-offset-32)bytes + *764bytes digest structure + offset: 4bytes + random-data: (offset)bytes + digest-data: 32bytes + random-data: (764-4-offset-32)bytes + + * [AUTO-TRANSLATED:99455c22] */ int offset = 0; for (int i = 0; i < C1_OFFSET_SIZE; ++i) { @@ -467,6 +501,13 @@ string RtmpProtocol::get_C1_key(const uint8_t *ptr){ key-data: 128bytes random-data: (764-offset-128-4)bytes offset: 4bytes + *764bytes key structure + random-data: (offset)bytes + key-data: 128bytes + random-data: (764-offset-128-4)bytes + offset: 4bytes + + * [AUTO-TRANSLATED:25b8d7c7] */ int offset = 0; for (int i = C1_SCHEMA_SIZE - C1_OFFSET_SIZE; i < C1_SCHEMA_SIZE; ++i) { @@ -479,8 +520,10 @@ string RtmpProtocol::get_C1_key(const uint8_t *ptr){ } void RtmpProtocol::send_complex_S0S1S2(int schemeType,const string &digest){ - //S1S2计算参考自:https://github.com/hitYangfei/golang/blob/master/rtmpserver.go - //发送S0 + // S1S2计算参考自:https://github.com/hitYangfei/golang/blob/master/rtmpserver.go [AUTO-TRANSLATED:8a372a12] + // S1S2 calculation refers to: https://github.com/hitYangfei/golang/blob/master/rtmpserver.go + // 发送S0 [AUTO-TRANSLATED:79eca118] + // Send S0 char handshake_head = HANDSHAKE_PLAINTEXT; onSendRawData(obtainBuffer(&handshake_head, 1)); //S1 @@ -518,29 +561,36 @@ void RtmpProtocol::send_complex_S0S1S2(int schemeType,const string &digest){ string s2_digest = openssl_HMACsha256(s2_key.data(), s2_key.size(), &s2, sizeof(s2) - C1_DIGEST_SIZE); memcpy((char *) &s2 + C1_HANDSHARK_SIZE - C1_DIGEST_SIZE, s2_digest.data(), C1_DIGEST_SIZE); onSendRawData(obtainBuffer((char *) &s2, sizeof(s2))); - //等待C2 + // 等待C2 [AUTO-TRANSLATED:b9900831] + // Wait for C2 _next_step_func = [this](const char *data, size_t len) { return handle_C2(data, len); }; } #endif //ENABLE_OPENSSL -//发送复杂握手c0c1 +// 发送复杂握手c0c1 [AUTO-TRANSLATED:f6f646dc] +// Send complex handshake c0c1 void RtmpHandshake::create_complex_c0c1() { #ifdef ENABLE_OPENSSL memcpy(zero, "\x80\x00\x07\x02", 4); - //digest随机偏移长度 + // digest随机偏移长度 [AUTO-TRANSLATED:758606f0] + // Digest random offset length auto offset_value = rand() % (C1_SCHEMA_SIZE - C1_OFFSET_SIZE - C1_DIGEST_SIZE); - //设置digest偏移长度 + // 设置digest偏移长度 [AUTO-TRANSLATED:1a4caf92] + // Set digest offset length auto offset_ptr = random + C1_SCHEMA_SIZE; offset_ptr[0] = offset_ptr[1] = offset_ptr[2] = offset_value / 4; offset_ptr[3] = offset_value - 3 * (offset_value / 4); - //去除digest后的剩余随机数据 + // 去除digest后的剩余随机数据 [AUTO-TRANSLATED:133cc74e] + // Remove the remaining random data after digest string str((char *) this, sizeof(*this)); str.erase(8 + C1_SCHEMA_SIZE + C1_OFFSET_SIZE + offset_value, C1_DIGEST_SIZE); - //获取摘要 + // 获取摘要 [AUTO-TRANSLATED:2a7aab55] + // Get digest auto digest_value = openssl_HMACsha256(FPKey, C1_FPKEY_SIZE, str.data(), str.size()); - //插入摘要 + // 插入摘要 [AUTO-TRANSLATED:3f12641b] + // Insert digest memcpy(random + C1_SCHEMA_SIZE + C1_OFFSET_SIZE + offset_value, digest_value.data(), digest_value.size()); #endif } @@ -554,7 +604,8 @@ const char* RtmpProtocol::handle_C2(const char *data, size_t len) { return handle_rtmp(data, len); }; - //握手结束,进入命令模式 + // 握手结束,进入命令模式 [AUTO-TRANSLATED:2b22d604] + // Handshake ends, enter command mode return handle_rtmp(data + C1_HANDSHARK_SIZE, len - C1_HANDSHARK_SIZE); } @@ -569,8 +620,10 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) { _now_chunk_id = header->chunk_id; switch (_now_chunk_id) { case 0: { - //0 值表示二字节形式,并且 ID 范围 64 - 319 - //(第二个字节 + 64)。 + // 0 值表示二字节形式,并且 ID 范围 64 - 319 [AUTO-TRANSLATED:46207a57] + // 0 value represents two-byte form, and ID range is 64 - 319 + // (第二个字节 + 64)。 [AUTO-TRANSLATED:ab640c90] + // (second byte + 64). if (len < 2) { //need more data return ptr; @@ -581,8 +634,10 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) { } case 1: { - //1 值表示三字节形式,并且 ID 范围为 64 - 65599 - //((第三个字节) * 256 + 第二个字节 + 64)。 + // 1 值表示三字节形式,并且 ID 范围为 64 - 65599 [AUTO-TRANSLATED:e7f24d54] + // 1 value represents three-byte form, and ID range is 64 - 65599 + // ((第三个字节) * 256 + 第二个字节 + 64)。 [AUTO-TRANSLATED:6e631032] + // ((third byte) * 256 + second byte + 64). if (len < 3) { //need more data return ptr; @@ -592,7 +647,8 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) { break; } - //带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。 + // 带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。 [AUTO-TRANSLATED:b0987fdb] + // Block stream IDs with a value of 2 are reserved for lower-level protocol control messages and commands. default : break; } @@ -607,10 +663,12 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) { if (!now_packet) { now_packet = RtmpPacket::create(); if (last_packet) { - //恢复chunk上下文 + // 恢复chunk上下文 [AUTO-TRANSLATED:84bf0621] + // Restore chunk context *now_packet = *last_packet; } - //绝对时间戳标记复位 + // 绝对时间戳标记复位 [AUTO-TRANSLATED:a0d50bad] + // Absolute timestamp marker reset now_packet->is_abs_stamp = false; } auto &chunk_data = *now_packet; @@ -654,7 +712,8 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) { //frame is ready _now_stream_index = chunk_data.stream_index; chunk_data.time_stamp = time_stamp + (chunk_data.is_abs_stamp ? 0 : chunk_data.time_stamp); - //保存chunk上下文 + // 保存chunk上下文 [AUTO-TRANSLATED:4ed4fbb0] + // Save chunk context last_packet = now_packet; if (chunk_data.body_size) { handle_chunk(std::move(now_packet)); @@ -715,7 +774,8 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) { } case CONTROL_STREAM_BEGIN: { - //开始播放 + // 开始播放 [AUTO-TRANSLATED:1d82b293] + // Start playback if (chunk_data.buffer.size() < 4) { WarnL << "CONTROL_STREAM_BEGIN: Not enough data:" << chunk_data.buffer.size(); break; @@ -727,7 +787,8 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) { } case CONTROL_STREAM_EOF: { - //暂停 + // 暂停 [AUTO-TRANSLATED:73a3f145] + // Pause if (chunk_data.buffer.size() < 4) { throw std::runtime_error("CONTROL_STREAM_EOF: Not enough data."); } @@ -738,7 +799,8 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) { } case CONTROL_STREAM_DRY: { - //停止播放 + // 停止播放 [AUTO-TRANSLATED:1292c1b5] + // Stop playback if (chunk_data.buffer.size() < 4) { throw std::runtime_error("CONTROL_STREAM_DRY: Not enough data."); } @@ -754,8 +816,10 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) { } case MSG_WIN_SIZE: { - //如果窗口太小,会导致发送sendAcknowledgement时无限递归:https://github.com/ZLMediaKit/ZLMediaKit/issues/1839 - //窗口太大,也可能导致fms服务器认为播放器心跳超时 + // 如果窗口太小,会导致发送sendAcknowledgement时无限递归:https://github.com/ZLMediaKit/ZLMediaKit/issues/1839 [AUTO-TRANSLATED:05267962] + // If the window is too small, it will cause infinite recursion when sending sendAcknowledgement: https://github.com/ZLMediaKit/ZLMediaKit/issues/1839 + // 窗口太大,也可能导致fms服务器认为播放器心跳超时 [AUTO-TRANSLATED:30147e88] + // If the window is too large, it may also cause the fms server to consider the player heartbeat timeout _windows_size = min(max(load_be32(&chunk_data.buffer[0]), 32 * 1024U), 1280 * 1024U); TraceL << "MSG_WIN_SIZE:" << _windows_size; break; @@ -784,7 +848,8 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) { ts |= (*ptr << 24); ptr += 1; ptr += 3; - //参考FFmpeg多拷贝了4个字节 + // 参考FFmpeg多拷贝了4个字节 [AUTO-TRANSLATED:d8aae4ae] + // Reference FFmpeg copied 4 more bytes size += 4; if (ptr + size > ptr_tail) { break; diff --git a/src/Rtmp/RtmpProtocol.h b/src/Rtmp/RtmpProtocol.h index 7ace93eb..7a12495c 100644 --- a/src/Rtmp/RtmpProtocol.h +++ b/src/Rtmp/RtmpProtocol.h @@ -28,7 +28,8 @@ public: virtual ~RtmpProtocol(); void onParseRtmp(const char *data, size_t size); - //作为客户端发送c0c1,等待s0s1s2并且回调 + // 作为客户端发送c0c1,等待s0s1s2并且回调 [AUTO-TRANSLATED:fed23902] + // Send c0c1 as a client, wait for s0s1s2 and callback void startClientSession(const std::function &cb, bool complex = true); protected: @@ -103,7 +104,8 @@ private: std::function _next_step_func; ////////////Chunk//////////// std::unordered_map > _map_chunk_data; - //循环池 + // 循环池 [AUTO-TRANSLATED:cf2e86c5] + // Thread pool toolkit::ResourcePool _packet_pool; }; diff --git a/src/Rtmp/RtmpPusher.cpp b/src/Rtmp/RtmpPusher.cpp index 92363241..badcd01c 100644 --- a/src/Rtmp/RtmpPusher.cpp +++ b/src/Rtmp/RtmpPusher.cpp @@ -46,15 +46,18 @@ void RtmpPusher::teardown() { void RtmpPusher::onPublishResult_l(const SockException &ex, bool handshake_done) { DebugL << ex.what(); if (ex.getErrCode() == Err_shutdown) { - //主动shutdown的,不触发回调 + // 主动shutdown的,不触发回调 [AUTO-TRANSLATED:bd97b1c1] + // Actively shutdown, no callback triggered return; } if (!handshake_done) { - //播放结果回调 + // 播放结果回调 [AUTO-TRANSLATED:a5714269] + // Playback result callback _publish_timer.reset(); onPublishResult(ex); } else { - //播放成功后异常断开回调 + // 播放成功后异常断开回调 [AUTO-TRANSLATED:b5c5fa80] + // Callback for abnormal disconnection after successful playback onShutdown(ex); } @@ -71,7 +74,8 @@ void RtmpPusher::publish(const string &url) { _stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL); auto app_second = findSubString(_stream_id.data(), nullptr, "/"); if (!app_second.empty() && app_second.find('?') == std::string::npos) { - // _stream_id存在多级;不包含'?', 说明分割符'/'不是url参数的一部分 + // _stream_id存在多级;不包含'?', 说明分割符'/'不是url参数的一部分 [AUTO-TRANSLATED:ef820905] + // _stream_id has multiple levels; does not contain '?', indicating that the delimiter '/' is not part of the URL parameters _app += "/" + app_second; _stream_id.erase(0, app_second.size() + 1); } @@ -104,7 +108,8 @@ void RtmpPusher::publish(const string &url) { } void RtmpPusher::onError(const SockException &ex){ - //定时器_pPublishTimer为空后表明握手结束了 + // 定时器_pPublishTimer为空后表明握手结束了 [AUTO-TRANSLATED:630ec31e] + // The timer _pPublishTimer is empty, indicating that the handshake is over onPublishResult_l(ex, !_publish_timer); } @@ -130,7 +135,8 @@ void RtmpPusher::onRecv(const Buffer::Ptr &buf){ onParseRtmp(buf->data(), buf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); - //定时器_pPublishTimer为空后表明握手结束了 + // 定时器_pPublishTimer为空后表明握手结束了 [AUTO-TRANSLATED:630ec31e] + // The timer _pPublishTimer is empty, indicating that the handshake is over onPublishResult_l(ex, !_publish_timer); } } @@ -243,14 +249,16 @@ void RtmpPusher::send_metaData(){ } }); onPublishResult_l(SockException(Err_success, "success"), false); - //提升发送性能 + // 提升发送性能 [AUTO-TRANSLATED:90630751] + // Improve sending performance setSocketFlags(); } void RtmpPusher::setSocketFlags(){ GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); if (mergeWriteMS > 0) { - //提高发送性能 + // 提高发送性能 [AUTO-TRANSLATED:de96ec30] + // Improve sending performance setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); SockUtil::setNoDelay(getSock()->rawFD(), false); } diff --git a/src/Rtmp/RtmpPusher.h b/src/Rtmp/RtmpPusher.h index e74012ee..41caac49 100644 --- a/src/Rtmp/RtmpPusher.h +++ b/src/Rtmp/RtmpPusher.h @@ -68,7 +68,8 @@ private: std::deque > _deque_on_status; std::unordered_map > _map_on_result; - //推流超时定时器 + // 推流超时定时器 [AUTO-TRANSLATED:7d2dcb86] + // Stream timeout timer std::shared_ptr _publish_timer; std::weak_ptr _publish_src; RtmpMediaSource::RingType::RingReader::Ptr _rtmp_reader; diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index 754b2609..251475b5 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -66,17 +66,23 @@ private: } ///////MediaSourceEvent override/////// - // 关闭 + // 关闭 [AUTO-TRANSLATED:92392f02] + // Close bool close(MediaSource &sender) override; - // 播放总人数 + // 播放总人数 [AUTO-TRANSLATED:c42a3161] + // Total number of plays int totalReaderCount(MediaSource &sender) override; - // 获取媒体源类型 + // 获取媒体源类型 [AUTO-TRANSLATED:34290a69] + // Get media source type MediaOriginType getOriginType(MediaSource &sender) const override; - // 获取媒体源url或者文件路径 + // 获取媒体源url或者文件路径 [AUTO-TRANSLATED:fa34d795] + // Get media source url or file path std::string getOriginUrl(MediaSource &sender) const override; - // 获取媒体源客户端相关信息 + // 获取媒体源客户端相关信息 [AUTO-TRANSLATED:037ef910] + // Get media source client related information std::shared_ptr getOriginSock(MediaSource &sender) const override; - // 由于支持断连续推,存在OwnerPoller变更的可能 + // 由于支持断连续推,存在OwnerPoller变更的可能 [AUTO-TRANSLATED:1c863b40] + // Due to support for discontinuous pushing, there may be changes in OwnerPoller toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; void setSocketFlags(); @@ -87,11 +93,14 @@ private: private: bool _set_meta_data = false; double _recv_req_id = 0; - //断连续推延时 + // 断连续推延时 [AUTO-TRANSLATED:13ad578a] + // Discontinuous pushing delay uint32_t _continue_push_ms = 0; - //消耗的总流量 + // 消耗的总流量 [AUTO-TRANSLATED:45ad2785] + // Total traffic consumed uint64_t _total_bytes = 0; - //数据接收超时计时器 + // 数据接收超时计时器 [AUTO-TRANSLATED:3fba518a] + // Data reception timeout timer toolkit::Ticker _ticker; MediaInfo _media_info; std::weak_ptr _play_src; @@ -104,6 +113,10 @@ private: /** * 支持ssl加密的rtmp服务器 + * Supports ssl encrypted rtmp server + + + * [AUTO-TRANSLATED:21d167ba] */ using RtmpSessionWithSSL = toolkit::SessionWithSSL; diff --git a/src/Rtp/Decoder.cpp b/src/Rtp/Decoder.cpp index 5274c11d..5a148703 100644 --- a/src/Rtp/Decoder.cpp +++ b/src/Rtp/Decoder.cpp @@ -88,12 +88,14 @@ void DecoderImp::onStream(int stream, int codecid, const void *extra, size_t byt if (_finished) { return; } - // G711传统只支持 8000/1/16的规格,FFmpeg貌似做了扩展,但是这里不管它了 + // G711传统只支持 8000/1/16的规格,FFmpeg貌似做了扩展,但是这里不管它了 [AUTO-TRANSLATED:851813f7] + // G711 traditionally only supports the 8000/1/16 specification. FFmpeg seems to have extended it, but we'll ignore that here. auto track = Factory::getTrackByCodecId(getCodecByMpegId(codecid), 8000, 1, 16); if (track) { onTrack(stream, std::move(track)); } - // 防止未获取视频track提前complete导致忽略后续视频的问题,用于兼容一些不太规范的ps流 + // 防止未获取视频track提前complete导致忽略后续视频的问题,用于兼容一些不太规范的ps流 [AUTO-TRANSLATED:d6b349b5] + // Prevent the problem of ignoring subsequent video due to premature completion of the video track before it is obtained. This is used to be compatible with some non-standard PS streams. if (finish && _have_video) { _finished = true; _sink->addTrackCompleted(); diff --git a/src/Rtp/GB28181Process.cpp b/src/Rtp/GB28181Process.cpp index 29c9704f..26210d05 100644 --- a/src/Rtp/GB28181Process.cpp +++ b/src/Rtp/GB28181Process.cpp @@ -23,7 +23,8 @@ using namespace toolkit; namespace mediakit { -// 判断是否为ts负载 +// 判断是否为ts负载 [AUTO-TRANSLATED:77d1aa3c] +// Determine if it is a ts payload static inline bool checkTS(const uint8_t *packet, size_t bytes) { return bytes % TS_PACKET_SIZE == 0 && packet[0] == TS_SYNC_BYTE; } @@ -36,7 +37,8 @@ public: _sample_rate = sample_rate; setOnSorted(std::move(cb)); setBeforeSorted(std::move(cb_before)); - // GB28181推流不支持ntp时间戳 + // GB28181推流不支持ntp时间戳 [AUTO-TRANSLATED:f661f052] + // GB28181 streaming does not support ntp timestamps setNtpStamp(0, 0); } @@ -77,7 +79,8 @@ bool GB28181Process::inputRtp(bool, const char *data, size_t data_len) { auto &ref = _rtp_receiver[pt]; if (!ref) { if (_rtp_receiver.size() > 2) { - // 防止pt类型太多导致内存溢出 + // 防止pt类型太多导致内存溢出 [AUTO-TRANSLATED:7695e49b] + // Prevent too many pt types from causing memory overflow WarnL << "Rtp payload type more than 2 types: " << _rtp_receiver.size(); } switch (pt) { @@ -104,7 +107,8 @@ bool GB28181Process::inputRtp(bool, const char *data, size_t data_len) { } default: { if (pt == opus_pt) { - // opus负载 + // opus负载 [AUTO-TRANSLATED:defa6a8d] + // opus payload ref = std::make_shared(48000, [this](RtpPacket::Ptr rtp) { onRtpSorted(std::move(rtp)); }); auto track = Factory::getTrackByCodecId(CodecOpus); CHECK(track); @@ -112,7 +116,8 @@ bool GB28181Process::inputRtp(bool, const char *data, size_t data_len) { _interface->addTrack(track); _rtp_decoder[pt] = Factory::getRtpDecoderByCodecId(track->getCodecId()); } else if (pt == h265_pt) { - // H265负载 + // H265负载 [AUTO-TRANSLATED:61fbcf7f] + // H265 payload ref = std::make_shared(90000, [this](RtpPacket::Ptr rtp) { onRtpSorted(std::move(rtp)); }); auto track = Factory::getTrackByCodecId(CodecH265); CHECK(track); @@ -120,7 +125,8 @@ bool GB28181Process::inputRtp(bool, const char *data, size_t data_len) { _interface->addTrack(track); _rtp_decoder[pt] = Factory::getRtpDecoderByCodecId(track->getCodecId()); } else if (pt == h264_pt) { - // H264负载 + // H264负载 [AUTO-TRANSLATED:6f3fbb0d] + // H264 payload ref = std::make_shared(90000, [this](RtpPacket::Ptr rtp) { onRtpSorted(std::move(rtp)); }); auto track = Factory::getTrackByCodecId(CodecH264); CHECK(track); @@ -132,9 +138,11 @@ bool GB28181Process::inputRtp(bool, const char *data, size_t data_len) { WarnL << "Unknown rtp payload type(" << (int)pt << "), decode it as mpeg-ps or mpeg-ts"; } ref = std::make_shared(90000, [this](RtpPacket::Ptr rtp) { onRtpSorted(std::move(rtp)); }); - // ts或ps负载 + // ts或ps负载 [AUTO-TRANSLATED:3ca31480] + // ts or ps payload _rtp_decoder[pt] = std::make_shared(CodecInvalid, 32 * 1024); - // 设置dump目录 + // 设置dump目录 [AUTO-TRANSLATED:23c88ace] + // Set dump directory GET_CONFIG(string, dump_dir, RtpProxy::kDumpDir); if (!dump_dir.empty()) { auto save_path = File::absolutePath(_media_info.stream + ".mpeg", dump_dir); @@ -148,7 +156,8 @@ bool GB28181Process::inputRtp(bool, const char *data, size_t data_len) { break; } } - // 设置frame回调 + // 设置frame回调 [AUTO-TRANSLATED:dec7590f] + // Set frame callback _rtp_decoder[pt]->addDelegate([this, pt](const Frame::Ptr &frame) { frame->setIndex(pt); onRtpDecode(frame); @@ -161,24 +170,29 @@ bool GB28181Process::inputRtp(bool, const char *data, size_t data_len) { void GB28181Process::onRtpDecode(const Frame::Ptr &frame) { if (frame->getCodecId() != CodecInvalid) { - // 这里不是ps或ts + // 这里不是ps或ts [AUTO-TRANSLATED:6f79ac69] + // This is not ps or ts _interface->inputFrame(frame); return; } - // 这是TS或PS + // 这是TS或PS [AUTO-TRANSLATED:55782860] + // This is TS or PS if (_save_file_ps) { fwrite(frame->data(), frame->size(), 1, _save_file_ps.get()); } if (!_decoder) { - // 创建解码器 + // 创建解码器 [AUTO-TRANSLATED:0cc03d90] + // Create decoder if (checkTS((uint8_t *)frame->data(), frame->size())) { - // 猜测是ts负载 + // 猜测是ts负载 [AUTO-TRANSLATED:c2be3a47] + // Guess it is a ts payload InfoL << _media_info.stream << " judged to be TS"; _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ts, _interface); } else { - // 猜测是ps负载 + // 猜测是ps负载 [AUTO-TRANSLATED:b7c0ff45] + // Guess it is a ps payload InfoL << _media_info.stream << " judged to be PS"; _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ps, _interface); } diff --git a/src/Rtp/GB28181Process.h b/src/Rtp/GB28181Process.h index 4cef1e95..d2db13a8 100644 --- a/src/Rtp/GB28181Process.h +++ b/src/Rtp/GB28181Process.h @@ -33,11 +33,21 @@ public: * @param data rtp数据指针 * @param data_len rtp数据长度 * @return 是否解析成功 + * Input rtp + * @param data rtp data pointer + * @param data_len rtp data length + * @return Whether the parsing is successful + + * [AUTO-TRANSLATED:d7b14ffe] */ bool inputRtp(bool, const char *data, size_t data_len) override; /** * 刷新输出所有缓存 + * Refresh and output all caches + + + * [AUTO-TRANSLATED:4509b01f] */ void flush() override; diff --git a/src/Rtp/PSDecoder.cpp b/src/Rtp/PSDecoder.cpp index 4949e236..21014b4b 100644 --- a/src/Rtp/PSDecoder.cpp +++ b/src/Rtp/PSDecoder.cpp @@ -57,17 +57,20 @@ const char *PSDecoder::onSearchPacketTail(const char *data, size_t len) { try { auto ret = ps_demuxer_input(static_cast(_ps_demuxer), reinterpret_cast(data), len); if (ret >= 0) { - //解析成功全部或部分 + // 解析成功全部或部分 [AUTO-TRANSLATED:a8085d34] + // Parse successful, all or part return data + ret; } - //解析失败,丢弃所有数据 + // 解析失败,丢弃所有数据 [AUTO-TRANSLATED:e6f644d9] + // Parse failed, discard all data return data + len; } catch (toolkit::AssertFailedException &ex) { InfoL << "解析 ps 异常: bytes=" << len << ", exception=" << ex.what() << ", hex=" << hexdump(data, MIN(len, 32)); - //触发断言,解析失败,丢弃所有数据 + // 触发断言,解析失败,丢弃所有数据 [AUTO-TRANSLATED:b60c6db0] + // Trigger assertion, parse failed, discard all data return data + len; } } diff --git a/src/Rtp/PSDecoder.h b/src/Rtp/PSDecoder.h index db05e282..268836d7 100644 --- a/src/Rtp/PSDecoder.h +++ b/src/Rtp/PSDecoder.h @@ -18,7 +18,8 @@ namespace mediakit{ -//ps解析器 +// ps解析器 [AUTO-TRANSLATED:f156a1f1] +// ps parser class PSDecoder : public Decoder, private HttpRequestSplitter { public: PSDecoder(); diff --git a/src/Rtp/PSEncoder.cpp b/src/Rtp/PSEncoder.cpp index 9de7f43b..24cb8ced 100644 --- a/src/Rtp/PSEncoder.cpp +++ b/src/Rtp/PSEncoder.cpp @@ -24,7 +24,8 @@ PSEncoderImp::PSEncoderImp(uint32_t ssrc, uint8_t payload_type, bool ps_or_ts) : _rtp_encoder = std::make_shared(); auto video_mtu = s_video_mtu; if (!ps_or_ts) { - // 确保ts rtp负载部分长度是188的倍数 + // 确保ts rtp负载部分长度是188的倍数 [AUTO-TRANSLATED:ad7aa6c0] + // Ensure the ts rtp payload length is a multiple of 188 video_mtu = RtpPacket::kRtpHeaderSize + (s_video_mtu - (s_video_mtu % 188)); if (video_mtu > s_video_mtu) { video_mtu -= 188; diff --git a/src/Rtp/PSEncoder.h b/src/Rtp/PSEncoder.h index e195913a..08897d8d 100644 --- a/src/Rtp/PSEncoder.h +++ b/src/Rtp/PSEncoder.h @@ -27,12 +27,19 @@ public: * @param ssrc rtp的ssrc * @param payload_type rtp的pt * @param ps_or_ts true: ps, false: ts + * Create a psh or ts rtp encoder + * @param ssrc rtp's ssrc + * @param payload_type rtp's pt + * @param ps_or_ts true: ps, false: ts + + * [AUTO-TRANSLATED:b79d8b65] */ PSEncoderImp(uint32_t ssrc, uint8_t payload_type = 96, bool ps_or_ts = true); ~PSEncoderImp() override; protected: - //rtp打包后回调 + // rtp打包后回调 [AUTO-TRANSLATED:8f88aef9] + // Callback after rtp packaging virtual void onRTP(toolkit::Buffer::Ptr rtp, bool is_key = false) = 0; protected: diff --git a/src/Rtp/ProcessInterface.h b/src/Rtp/ProcessInterface.h index b4fd97f4..deda6cdf 100644 --- a/src/Rtp/ProcessInterface.h +++ b/src/Rtp/ProcessInterface.h @@ -28,11 +28,22 @@ public: * @param data rtp数据指针 * @param data_len rtp数据长度 * @return 是否解析成功 + * Input rtp + * @param is_udp Whether it is udp mode + * @param data rtp data pointer + * @param data_len rtp data length + * @return Whether the parsing is successful + + * [AUTO-TRANSLATED:7d5b06f0] */ virtual bool inputRtp(bool is_udp, const char *data, size_t data_len) = 0; /** * 刷新输出所有缓存 + * Refresh and output all caches + + + * [AUTO-TRANSLATED:4509b01f] */ virtual void flush() {} }; diff --git a/src/Rtp/RawEncoder.h b/src/Rtp/RawEncoder.h index 71ae12f5..c4cdb9e7 100644 --- a/src/Rtp/RawEncoder.h +++ b/src/Rtp/RawEncoder.h @@ -25,21 +25,31 @@ public: /** * 添加音视频轨道 + * Add audio and video tracks + + * [AUTO-TRANSLATED:7b0c1d64] */ bool addTrack(const Track::Ptr &track) override; /** * 重置音视频轨道 + * Reset audio and video tracks + + * [AUTO-TRANSLATED:6eb1b742] */ void resetTracks() override; /** * 输入帧数据 + * Input frame data + + * [AUTO-TRANSLATED:d13bc7f2] */ bool inputFrame(const Frame::Ptr &frame) override; protected: - // rtp打包后回调 + // rtp打包后回调 [AUTO-TRANSLATED:61f5159b] + // Callback after RTP packaging virtual void onRTP(toolkit::Buffer::Ptr rtp, bool is_key = false) = 0; private: diff --git a/src/Rtp/RtpCache.h b/src/Rtp/RtpCache.h index c63e18f4..8432eef5 100644 --- a/src/Rtp/RtpCache.h +++ b/src/Rtp/RtpCache.h @@ -28,6 +28,11 @@ protected: /** * 输入rtp(目的是为了合并写) * @param buffer rtp数据 + * Input rtp (for merging) + * @param buffer rtp data + + + * [AUTO-TRANSLATED:de9469b5] */ void input(uint64_t stamp, toolkit::Buffer::Ptr buffer,bool is_key = false); diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 6ae19456..62a82e90 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -17,8 +17,10 @@ using namespace std; using namespace toolkit; -//在创建_muxer对象前(也就是推流鉴权成功前),需要先缓存frame,这样可以防止丢包,提高体验 -//但是同时需要控制缓冲长度,防止内存溢出。最多缓存10秒数据,应该足矣等待鉴权hook返回 +// 在创建_muxer对象前(也就是推流鉴权成功前),需要先缓存frame,这样可以防止丢包,提高体验 [AUTO-TRANSLATED:fb12a6c2] +// Before creating the _muxer object (before the streaming authentication is successful), you need to cache the frame first, which can prevent packet loss and improve the experience. +// 但是同时需要控制缓冲长度,防止内存溢出。最多缓存10秒数据,应该足矣等待鉴权hook返回 [AUTO-TRANSLATED:23ff0a4a] +// But at the same time, you need to control the buffer length to prevent memory overflow. Caching 10 seconds of data should be enough to wait for the authentication hook to return. static constexpr size_t kMaxCachedFrameMS = 10 * 1000; namespace mediakit { @@ -65,7 +67,8 @@ RtpProcess::~RtpProcess() { << _media_info.shortUrl() << ")断开,耗时(s):" << duration; - //流量统计事件广播 + // 流量统计事件广播 [AUTO-TRANSLATED:6b0b1234] + // Traffic statistics event broadcast GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); if (_total_bytes >= iFlowThreshold * 1024) { try { @@ -83,7 +86,8 @@ void RtpProcess::onManager() { } void RtpProcess::createTimer() { - //创建超时管理定时器 + // 创建超时管理定时器 [AUTO-TRANSLATED:865cf865] + // Create a timeout management timer weak_ptr weakSelf = shared_from_this(); _timer = std::make_shared(3.0f, [weakSelf] { auto strongSelf = weakSelf.lock(); @@ -104,7 +108,8 @@ bool RtpProcess::inputRtp(bool is_udp, const Socket::Ptr &sock, const char *data throw toolkit::SockException(toolkit::Err_other, _auth_err); } if (_sock != sock) { - // 第一次运行本函数 + // 第一次运行本函数 [AUTO-TRANSLATED:a1d7ac17] + // First time running this function bool first = !_sock; _sock = sock; _addr.reset(new sockaddr_storage(*((sockaddr_storage *)addr))); @@ -130,7 +135,8 @@ bool RtpProcess::inputRtp(bool is_udp, const Socket::Ptr &sock, const char *data GET_CONFIG(string, dump_dir, RtpProxy::kDumpDir); if (_muxer && !_muxer->isEnabled() && !dts_out && dump_dir.empty()) { - //无人访问、且不取时间戳、不导出调试文件时,我们可以直接丢弃数据 + // 无人访问、且不取时间戳、不导出调试文件时,我们可以直接丢弃数据 [AUTO-TRANSLATED:2fc75705] + // When there is no access, and no timestamp is taken, and no debug file is exported, we can directly discard the data. _last_frame_time.resetTime(); return false; } @@ -198,7 +204,8 @@ void RtpProcess::doCachedFunc() { bool RtpProcess::alive() { if (_stop_rtp_check.load()) { if(_last_check_alive.elapsedTime() > 5 * 60 * 1000){ - //最多暂停5分钟的rtp超时检测,因为NAT映射有效期一般不会太长 + // 最多暂停5分钟的rtp超时检测,因为NAT映射有效期一般不会太长 [AUTO-TRANSLATED:2df59aad] + // Pause the RTP timeout detection for a maximum of 5 minutes, because the NAT mapping validity period is generally not very long. _stop_rtp_check = false; } else { return true; @@ -293,10 +300,12 @@ void RtpProcess::emitOnPublish() { }); }; - //触发推流鉴权事件 + // 触发推流鉴权事件 [AUTO-TRANSLATED:cd889b29] + // Trigger the streaming authentication event auto flag = NOTICE_EMIT(BroadcastMediaPublishArgs, Broadcast::kBroadcastMediaPublish, MediaOriginType::rtp_push, _media_info, invoker, *this); if (!flag) { - // 该事件无人监听,默认不鉴权 + // 该事件无人监听,默认不鉴权 [AUTO-TRANSLATED:e1fbc6ae] + // No one is listening to this event, and authentication is not performed by default. invoker("", ProtocolOption()); } } diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index 855eddd4..ef82f82d 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -38,33 +38,60 @@ public: * @param addr 数据源地址 * @param dts_out 解析出最新的dts * @return 是否解析成功 + * Input rtp + * @param is_udp Whether it is udp mode + * @param sock Local listening socket + * @param data Rtp data pointer + * @param len Rtp data length + * @param addr Data source address + * @param dts_out Parse out the latest dts + * @return Whether the parsing is successful + + * [AUTO-TRANSLATED:a10c5edf] */ bool inputRtp(bool is_udp, const toolkit::Socket::Ptr &sock, const char *data, size_t len, const struct sockaddr *addr , uint64_t *dts_out = nullptr); /** * 超时时被RtpSelector移除时触发 + * Triggered when removed by RtpSelector when timeout + + * [AUTO-TRANSLATED:dc4c6609] */ void onDetach(const toolkit::SockException &ex); /** * 设置onDetach事件回调 + * Set onDetach event callback + + * [AUTO-TRANSLATED:b30f67c3] */ void setOnDetach(onDetachCB cb); /** * 设置onDetach事件回调,false检查RTP超时,true停止 + * Set onDetach event callback, false checks RTP timeout, true stops + + * [AUTO-TRANSLATED:2780397f] */ void setStopCheckRtp(bool is_check=false); /** * 设置为单track,单音频/单视频时可以加快媒体注册速度 * 请在inputRtp前调用此方法,否则可能会是空操作 + * Set to single track, single audio/single video can speed up media registration + * Please call this method before inputRtp, otherwise it may be a null operation + + * [AUTO-TRANSLATED:55095289] */ void setOnlyTrack(OnlyTrack only_track); /** * flush输出缓存 + * Flush output cache + + + * [AUTO-TRANSLATED:40618a29] */ void flush() override; diff --git a/src/Rtp/RtpSender.cpp b/src/Rtp/RtpSender.cpp index 3012dda7..21b82ab6 100644 --- a/src/Rtp/RtpSender.cpp +++ b/src/Rtp/RtpSender.cpp @@ -38,7 +38,8 @@ RtpSender::~RtpSender() { void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const function &cb){ _args = args; if (!_interface) { - //重连时不重新创建对象 + // 重连时不重新创建对象 [AUTO-TRANSLATED:b788cd5d] + // Do not recreate the object when reconnecting auto lam = [this](std::shared_ptr> list) { onFlushRtpList(std::move(list)); }; switch (args.data_type) { case MediaSourceEvent::SendRtpArgs::kRtpPS: _interface = std::make_shared(lam, atoi(args.ssrc.data()), args.pt, true); break; @@ -53,18 +54,22 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct if (args.con_type == MediaSourceEvent::SendRtpArgs::kTcpPassive) { auto tcp_listener = Socket::createSocket(_poller, false); if (args.src_port) { - // 指定端口 + // 指定端口 [AUTO-TRANSLATED:ed4ca3dd] + // Specify the port if (!tcp_listener->listen(args.src_port)) { throw std::invalid_argument(StrPrinter << "open tcp passive server failed on port: " << args.src_port << ", err: " << get_uv_errmsg(true)); } } else { auto pr = std::make_pair(tcp_listener, Socket::createSocket(_poller, false)); - // 从端口池获取随机端口 + // 从端口池获取随机端口 [AUTO-TRANSLATED:139ceb4f] + // Get a random port from the port pool makeSockPair(pr, "::", true, false); } - // 定时器持有tcp_listener,保证超时时间内保持监听 + // 定时器持有tcp_listener,保证超时时间内保持监听 [AUTO-TRANSLATED:39df3f48] + // The timer holds the tcp_listener to ensure listening within the timeout period auto delay_task = _poller->doDelayTask(delay_ms, [weak_self, tcp_listener]() mutable { - // 防止循环引用 + // 防止循环引用 [AUTO-TRANSLATED:e2e9f9e7] + // Prevent circular references tcp_listener = nullptr; if (auto strong_self = weak_self.lock()) { strong_self->onClose(SockException(Err_timeout, "wait tcp connection timeout")); @@ -86,18 +91,21 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct } else if (args.con_type == MediaSourceEvent::SendRtpArgs::kUdpPassive) { if (args.src_port) { - // 指定端口 + // 指定端口 [AUTO-TRANSLATED:ed4ca3dd] + // Specify the port if (!_socket_rtp->bindUdpSock(args.src_port, "::", true)) { throw std::invalid_argument(StrPrinter << "open udp passive server failed on port: " << args.src_port << ", err: " << get_uv_errmsg(true)); } } else { auto pr = std::make_pair(_socket_rtp, Socket::createSocket(_poller, false)); - // 从端口池获取随机端口 + // 从端口池获取随机端口 [AUTO-TRANSLATED:139ceb4f] + // Get a random port from the port pool makeSockPair(pr, "::", true, true); } auto delay_task = _poller->doDelayTask(delay_ms, [weak_self]() mutable { if (auto strong_self = weak_self.lock()) { - // 关闭端口 + // 关闭端口 [AUTO-TRANSLATED:3b3dff64] + // Close the port strong_self->_socket_rtp->closeSock(); strong_self->onClose(SockException(Err_timeout, "wait udp connection timeout")); } @@ -110,7 +118,8 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct } delay_task->cancel(); strong_self->_socket_rtp->bindPeerAddr(addr, addr_len, true); - // 异步执行onConnect,防止在OnRead回调中调用setOnRead + // 异步执行onConnect,防止在OnRead回调中调用setOnRead [AUTO-TRANSLATED:83881d7f] + // Execute onConnect asynchronously to prevent calling setOnRead in the OnRead callback strong_self->_poller->async([strong_self]() { strong_self->onConnect(); }, false); InfoL << "accept udp connection from: " << strong_self->_socket_rtp->get_peer_ip() << ":" << strong_self->_socket_rtp->get_peer_port(); }); @@ -121,18 +130,22 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct auto poller = _poller; WorkThreadPool::Instance().getPoller()->async([cb, args, weak_self, poller]() { struct sockaddr_storage addr; - // 切换线程目的是为了dns解析放在后台线程执行 + // 切换线程目的是为了dns解析放在后台线程执行 [AUTO-TRANSLATED:1a09ba8a] + // The purpose of switching threads is to perform DNS resolution in the background thread if (!SockUtil::getDomainIP(args.dst_url.data(), args.dst_port, addr, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) { poller->async([args, cb]() { - // 切回自己的线程 + // 切回自己的线程 [AUTO-TRANSLATED:b95746d6] + // Switch back to your own thread cb(0, SockException(Err_dns, StrPrinter << "dns resolution failed: " << args.dst_url)); }); return; } - // dns解析成功 + // dns解析成功 [AUTO-TRANSLATED:e1b35821] + // DNS resolution successful poller->async([args, addr, weak_self, cb]() { - // 切回自己的线程 + // 切回自己的线程 [AUTO-TRANSLATED:b95746d6] + // Switch back to your own thread auto strong_self = weak_self.lock(); if (!strong_self) { return; @@ -140,13 +153,15 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct string ifr_ip = addr.ss_family == AF_INET ? "0.0.0.0" : "::"; try { if (args.src_port) { - // 指定端口 + // 指定端口 [AUTO-TRANSLATED:ed4ca3dd] + // Specify the port if (!strong_self->_socket_rtp->bindUdpSock(args.src_port, ifr_ip, true)) { throw std::invalid_argument(StrPrinter << "open udp active client failed on port: " << args.src_port << ", err: " << get_uv_errmsg(true)); } } else { auto pr = std::make_pair(strong_self->_socket_rtp, Socket::createSocket(strong_self->_poller, false)); - // 从端口池获取随机端口 + // 从端口池获取随机端口 [AUTO-TRANSLATED:139ceb4f] + // Get a random port from the port pool makeSockPair(pr, ifr_ip, true, true); } } catch (std::exception &ex) { @@ -165,7 +180,8 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct auto strong_self = weak_self.lock(); if (strong_self) { if (!err) { - // tcp连接成功 + // tcp连接成功 [AUTO-TRANSLATED:1a33669a] + // TCP connection successful strong_self->onConnect(); } cb(strong_self->_socket_rtp->get_local_port(), err); @@ -184,14 +200,16 @@ void RtpSender::createRtcpSocket() { return; } _socket_rtcp = Socket::createSocket(_socket_rtp->getPoller(), false); - //rtcp端口使用户rtp端口+1 + // rtcp端口使用户rtp端口+1 [AUTO-TRANSLATED:8a0a6b2c] + // The RTCP port is the RTP port + 1 if(!_socket_rtcp->bindUdpSock(_socket_rtp->get_local_port() + 1, _socket_rtp->get_local_ip(), true)){ WarnL << "bind rtcp udp socket failed: " << get_uv_errmsg(true); _socket_rtcp = nullptr; return; } - // 绑定目标rtcp端口(目标rtp端口 + 1) + // 绑定目标rtcp端口(目标rtp端口 + 1) [AUTO-TRANSLATED:c402de5b] + // Bind the target RTCP port (target RTP port + 1) auto addr = SockUtil::make_sockaddr(_socket_rtp->get_peer_ip().data(), _socket_rtp->get_peer_port() + 1); _socket_rtcp->bindPeerAddr((struct sockaddr *)&addr, 0, true); @@ -199,13 +217,15 @@ void RtpSender::createRtcpSocket() { weak_ptr weak_self = shared_from_this(); bool bind_addr = false; _socket_rtcp->setOnRead([weak_self, bind_addr](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) mutable { - //接收receive report rtcp + // 接收receive report rtcp [AUTO-TRANSLATED:38d3c1ba] + // Receive receive report RTCP auto strong_self = weak_self.lock(); if (!strong_self) { return; } if (!bind_addr) { - // 收到对方rtcp打洞包后,再回复rtcp + // 收到对方rtcp打洞包后,再回复rtcp [AUTO-TRANSLATED:393868ca] + // Reply RTCP after receiving the other party's RTCP hole punching packet bind_addr = true; strong_self->_socket_rtcp->bindPeerAddr(addr, addr_len, true); } @@ -222,23 +242,31 @@ void RtpSender::onRecvRtcp(RtcpHeader *rtcp) { _rtcp_recv_ticker.resetTime(); } -// 连接建立成功事件 +// 连接建立成功事件 [AUTO-TRANSLATED:ac279c86] +// Connection established successfully event void RtpSender::onConnect() { _is_connect = true; - // 加大发送缓存,防止udp丢包之类的问题 + // 加大发送缓存,防止udp丢包之类的问题 [AUTO-TRANSLATED:6e1cb40a] + // Increase the send buffer to prevent problems such as UDP packet loss SockUtil::setSendBuf(_socket_rtp->rawFD(), 4 * 1024 * 1024); if (_args.con_type == MediaSourceEvent::SendRtpArgs::kTcpActive || _args.con_type == MediaSourceEvent::SendRtpArgs::kTcpPassive) { - // 关闭tcp no_delay并开启MSG_MORE, 提高发送性能 + // 关闭tcp no_delay并开启MSG_MORE, 提高发送性能 [AUTO-TRANSLATED:c0f4e378] + // Close TCP no_delay and enable MSG_MORE to improve sending performance SockUtil::setNoDelay(_socket_rtp->rawFD(), false); _socket_rtp->setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); } else if (_args.udp_rtcp_timeout) { createRtcpSocket(); } - // 连接建立成功事件 + // 连接建立成功事件 [AUTO-TRANSLATED:ac279c86] + // Connection established successfully event weak_ptr weak_self = shared_from_this(); if (!_args.recv_stream_id.empty()) { mINI ini; ini[RtpSession::kStreamID] = _args.recv_stream_id; + // 强制同步接收流和发送流的app和vhost [AUTO-TRANSLATED:134c9663] + // Force synchronization of the app and vhost of the receive stream and send stream + ini[RtpSession::kApp] = _args.recv_stream_app; + ini[RtpSession::kVhost] = _args.recv_stream_vhost; _rtp_session = std::make_shared(_socket_rtp); _rtp_session->setParams(ini); @@ -268,7 +296,8 @@ void RtpSender::onConnect() { bool RtpSender::addTrack(const Track::Ptr &track) { if (_args.only_audio && track->getTrackType() == TrackVideo) { - // 如果只发送音频则忽略视频 + // 如果只发送音频则忽略视频 [AUTO-TRANSLATED:6843e322] + // Ignore video if only audio is sent return false; } return _interface->addTrack(track); @@ -290,10 +319,12 @@ void RtpSender::flush() { bool RtpSender::inputFrame(const Frame::Ptr &frame) { if (_args.only_audio && frame->getTrackType() == TrackVideo) { - // 如果只发送音频则忽略视频 + // 如果只发送音频则忽略视频 [AUTO-TRANSLATED:6843e322] + // Ignore video if only audio is sent return false; } - // 连接成功后才做实质操作(节省cpu资源) + // 连接成功后才做实质操作(节省cpu资源) [AUTO-TRANSLATED:666253b3] + // Perform the actual operation after the connection is successful (save CPU resources) return _is_connect ? _interface->inputFrame(frame) : false; } @@ -305,20 +336,24 @@ void RtpSender::onSendRtpUdp(const toolkit::Buffer::Ptr &buf, bool check) { _rtcp_context->onRtp(rtp->getSeq(), rtp->getStamp(), rtp->ntp_stamp, 90000 /*not used*/, rtp->size()); if (!check) { - // 减少判断次数 + // 减少判断次数 [AUTO-TRANSLATED:0cfaddd8] + // Reduce the number of judgments return; } - // 每5秒发送一次rtcp + // 每5秒发送一次rtcp [AUTO-TRANSLATED:3c9bcb7b] + // Send RTCP every 5 seconds if (_rtcp_send_ticker.elapsedTime() > _args.rtcp_send_interval_ms) { _rtcp_send_ticker.resetTime(); - // rtcp ssrc为rtp ssrc + 1 + // rtcp ssrc为rtp ssrc + 1 [AUTO-TRANSLATED:318fada3] + // rtcp ssrc is rtp ssrc + 1 auto sr = _rtcp_context->createRtcpSR(atoi(_args.ssrc.data()) + 1); // send sender report rtcp _socket_rtcp->send(sr); } if (_rtcp_recv_ticker.elapsedTime() > _args.rtcp_timeout_ms) { - // 接收rr rtcp超时 + // 接收rr rtcp超时 [AUTO-TRANSLATED:a6ccd262] + // Receive rr rtcp timeout WarnL << "recv rr rtcp timeout"; _rtcp_recv_ticker.resetTime(); onClose(SockException(Err_timeout, "recv rr rtcp timeout")); @@ -328,15 +363,18 @@ void RtpSender::onSendRtpUdp(const toolkit::Buffer::Ptr &buf, bool check) { void RtpSender::onClose(const SockException &ex) { auto cb = _on_close; if (cb) { - // 在下次循环时触发onClose,原因是防止遍历map时删除元素 + // 在下次循环时触发onClose,原因是防止遍历map时删除元素 [AUTO-TRANSLATED:2841df7f] + // Trigger onClose in the next loop to prevent deleting elements while iterating through the map _poller->async([cb, ex]() { cb(ex); }, false); } } -// 此函数在其他线程执行 +// 此函数在其他线程执行 [AUTO-TRANSLATED:3569a681] +// This function is executed in another thread void RtpSender::onFlushRtpList(shared_ptr> rtp_list) { if (!_is_connect) { - // 连接成功后才能发送数据 + // 连接成功后才能发送数据 [AUTO-TRANSLATED:14d00ad5] + // Data can only be sent after the connection is successful return; } @@ -347,13 +385,15 @@ void RtpSender::onFlushRtpList(shared_ptr> rtp_list) { case MediaSourceEvent::SendRtpArgs::kUdpActive: case MediaSourceEvent::SendRtpArgs::kUdpPassive: { onSendRtpUdp(packet, i == 0); - // udp模式,rtp over tcp前4个字节可以忽略 + // udp模式,rtp over tcp前4个字节可以忽略 [AUTO-TRANSLATED:5d648f4b] + // UDP mode, the first 4 bytes of rtp over tcp can be ignored _socket_rtp->send(std::make_shared(std::move(packet), RtpPacket::kRtpTcpHeaderSize), nullptr, 0, ++i == size); break; } case MediaSourceEvent::SendRtpArgs::kTcpActive: case MediaSourceEvent::SendRtpArgs::kTcpPassive: { - // tcp模式, rtp over tcp前2个字节可以忽略,只保留后续rtp长度的2个字节 + // tcp模式, rtp over tcp前2个字节可以忽略,只保留后续rtp长度的2个字节 [AUTO-TRANSLATED:a3bc338a] + // TCP mode, the first 2 bytes of rtp over tcp can be ignored, only the subsequent 2 bytes of rtp length are retained _socket_rtp->send(std::make_shared(std::move(packet), 2), nullptr, 0, ++i == size); break; } diff --git a/src/Rtp/RtpSender.h b/src/Rtp/RtpSender.h index 3ad09e8d..dbdf4a1c 100644 --- a/src/Rtp/RtpSender.h +++ b/src/Rtp/RtpSender.h @@ -21,7 +21,8 @@ namespace mediakit{ class RtpSession; -//rtp发送客户端,支持发送GB28181协议 +// rtp发送客户端,支持发送GB28181协议 [AUTO-TRANSLATED:668038b6] +// RTP sending client, supporting sending GB28181 protocol class RtpSender final : public MediaSinkInterface, public std::enable_shared_from_this{ public: using Ptr = std::shared_ptr; @@ -33,16 +34,27 @@ public: * 开始发送ps-rtp包 * @param args 发送参数 * @param cb 连接目标端口是否成功的回调 + * Start sending ps-rtp packets + * @param args Sending parameters + * @param cb Callback for whether the connection to the target port is successful + + * [AUTO-TRANSLATED:c31bd9b3] */ void startSend(const MediaSourceEvent::SendRtpArgs &args, const std::function &cb); /** * 输入帧数据 + * Input frame data + + * [AUTO-TRANSLATED:d13bc7f2] */ bool inputFrame(const Frame::Ptr &frame) override; /** * 刷新输出frame缓存 + * Refresh the output frame cache + + * [AUTO-TRANSLATED:547f851c] */ void flush() override; @@ -50,30 +62,47 @@ public: * 添加track,内部会调用Track的clone方法 * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 * @param track + * Add track, internally calls the clone method of Track + * Only clones sps pps information, not Delegate relationships + * @param track + + * [AUTO-TRANSLATED:ba6faf58] */ virtual bool addTrack(const Track::Ptr & track) override; /** * 添加所有Track完毕 + * All Tracks added + + * [AUTO-TRANSLATED:751c45ca] */ virtual void addTrackCompleted() override; /** * 重置track + * Reset track + + * [AUTO-TRANSLATED:95dc0b4f] */ virtual void resetTracks() override; /** * 设置发送rtp停止回调 + * Set RTP sending stop callback + + * [AUTO-TRANSLATED:7e0a6714] */ void setOnClose(std::function on_close); private: - //合并写输出 + // 合并写输出 [AUTO-TRANSLATED:23544836] + // Merge write output void onFlushRtpList(std::shared_ptr > rtp_list); - //udp/tcp连接成功回调 + // udp/tcp连接成功回调 [AUTO-TRANSLATED:ca35017d] + // UDP/TCP connection success callback void onConnect(); - //异常断开socket事件 + // 异常断开socket事件 [AUTO-TRANSLATED:a59cd9de] + // Abnormal socket disconnect event void onErr(const toolkit::SockException &ex); void createRtcpSocket(); void onRecvRtcp(RtcpHeader *rtcp); diff --git a/src/Rtp/RtpServer.cpp b/src/Rtp/RtpServer.cpp index 5c657d19..ebd3db9b 100644 --- a/src/Rtp/RtpServer.cpp +++ b/src/Rtp/RtpServer.cpp @@ -63,7 +63,8 @@ public: void onRecvRtp(const Socket::Ptr &sock, const Buffer::Ptr &buf, struct sockaddr *addr) { _process->inputRtp(true, sock, buf->data(), buf->size(), addr); - // 统计rtp接受情况,用于发送rr包 + // 统计rtp接受情况,用于发送rr包 [AUTO-TRANSLATED:bd2fbe7e] + // Count RTP reception status, used to send RR packets auto header = (RtpHeader *)buf->data(); sendRtcp(ntohl(header->ssrc), addr); } @@ -71,13 +72,15 @@ public: void startRtcp() { weak_ptr weak_self = shared_from_this(); _rtcp_sock->setOnRead([weak_self](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { - // 用于接受rtcp打洞包 + // 用于接受rtcp打洞包 [AUTO-TRANSLATED:d75d9d87] + // Used to receive RTCP hole punching packets auto strong_self = weak_self.lock(); if (!strong_self || !strong_self->_process) { return; } if (!strong_self->_rtcp_addr) { - // 只设置一次rtcp对端端口 + // 只设置一次rtcp对端端口 [AUTO-TRANSLATED:fdc4eb4e] + // Set the RTCP peer port only once strong_self->_rtcp_addr = std::make_shared(); memcpy(strong_self->_rtcp_addr.get(), addr, addr_len); } @@ -85,14 +88,16 @@ public: for (auto &rtcp : rtcps) { strong_self->_process->onRtcp(rtcp); } - // 收到sr rtcp后驱动返回rr rtcp + // 收到sr rtcp后驱动返回rr rtcp [AUTO-TRANSLATED:d7373077] + // After receiving SR RTCP, the driver returns RR RTCP strong_self->sendRtcp(strong_self->_ssrc, (struct sockaddr *)(strong_self->_rtcp_addr.get())); }); } private: void sendRtcp(uint32_t rtp_ssrc, struct sockaddr *addr) { - // 每5秒发送一次rtcp + // 每5秒发送一次rtcp [AUTO-TRANSLATED:3c9bcb7b] + // Send RTCP every 5 seconds if (_ticker.elapsedTime() < 5000) { return; } @@ -100,12 +105,14 @@ private: auto rtcp_addr = (struct sockaddr *)_rtcp_addr.get(); if (!rtcp_addr) { - // 默认的,rtcp端口为rtp端口+1 + // 默认的,rtcp端口为rtp端口+1 [AUTO-TRANSLATED:273d164d] + // By default, the RTCP port is the RTP port + 1 switch (addr->sa_family) { case AF_INET: ((sockaddr_in *)addr)->sin_port = htons(ntohs(((sockaddr_in *)addr)->sin_port) + 1); break; case AF_INET6: ((sockaddr_in6 *)addr)->sin6_port = htons(ntohs(((sockaddr_in6 *)addr)->sin6_port) + 1); break; } - // 未收到rtcp打洞包时,采用默认的rtcp端口 + // 未收到rtcp打洞包时,采用默认的rtcp端口 [AUTO-TRANSLATED:d99b12b8] + // When no RTCP hole punching packet is received, the default RTCP port is used rtcp_addr = addr; } _rtcp_sock->send(_process->createRtcpRR(rtp_ssrc + 1, rtp_ssrc), rtcp_addr); @@ -123,33 +130,41 @@ private: }; void RtpServer::start(uint16_t local_port, const char *local_ip, const MediaTuple &tuple, TcpMode tcp_mode, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex) { - //创建udp服务器 + // 创建udp服务器 [AUTO-TRANSLATED:99619428] + // Create UDP server auto poller = EventPollerPool::Instance().getPoller(); Socket::Ptr rtp_socket = Socket::createSocket(poller, true); Socket::Ptr rtcp_socket = Socket::createSocket(poller, true); if (local_port == 0) { - //随机端口,rtp端口采用偶数 + // 随机端口,rtp端口采用偶数 [AUTO-TRANSLATED:3664eaf5] + // Random port, RTP port uses even numbers auto pair = std::make_pair(rtp_socket, rtcp_socket); makeSockPair(pair, local_ip, re_use_port); local_port = rtp_socket->get_local_port(); } else if (!rtp_socket->bindUdpSock(local_port, local_ip, re_use_port)) { - //用户指定端口 + // 用户指定端口 [AUTO-TRANSLATED:1328393b] + // User-specified port throw std::runtime_error(StrPrinter << "创建rtp端口 " << local_ip << ":" << local_port << " 失败:" << get_uv_errmsg(true)); } else if (!rtcp_socket->bindUdpSock(local_port + 1, local_ip, re_use_port)) { - // rtcp端口 + // rtcp端口 [AUTO-TRANSLATED:00cf932e] + // RTCP port throw std::runtime_error(StrPrinter << "创建rtcp端口 " << local_ip << ":" << local_port + 1 << " 失败:" << get_uv_errmsg(true)); } - //设置udp socket读缓存 + // 设置udp socket读缓存 [AUTO-TRANSLATED:3bf101d7] + // Set UDP socket read cache GET_CONFIG(int, udpRecvSocketBuffer, RtpProxy::kUdpRecvSocketBuffer); SockUtil::setRecvBuf(rtp_socket->rawFD(), udpRecvSocketBuffer); - //创建udp服务器 + // 创建udp服务器 [AUTO-TRANSLATED:99619428] + // Create UDP server UdpServer::Ptr udp_server; RtcpHelper::Ptr helper; - //增加了多路复用判断,如果多路复用为true,就走else逻辑,同时保留了原来stream_id为空走else逻辑 + // 增加了多路复用判断,如果多路复用为true,就走else逻辑,同时保留了原来stream_id为空走else逻辑 [AUTO-TRANSLATED:114690b1] + // Added multiplexing judgment. If multiplexing is true, then go to the else logic, while retaining the original stream_id is empty to go to the else logic if (!tuple.stream.empty() && !multiplex) { - //指定了流id,那么一个端口一个流(不管是否包含多个ssrc的多个流,绑定rtp源后,会筛选掉ip端口不匹配的流) + // 指定了流id,那么一个端口一个流(不管是否包含多个ssrc的多个流,绑定rtp源后,会筛选掉ip端口不匹配的流) [AUTO-TRANSLATED:3aeb611e] + // If a stream ID is specified, then one port is one stream (regardless of whether it contains multiple streams with multiple SSRCs, after binding the RTP source, streams that do not match the IP port will be filtered out) helper = std::make_shared(std::move(rtcp_socket), tuple); helper->startRtcp(); helper->setRtpServerInfo(local_port, tcp_mode, re_use_port, ssrc, only_track); @@ -164,7 +179,8 @@ void RtpServer::start(uint16_t local_port, const char *local_ip, const MediaTupl WarnL << "ssrc mismatched, rtp dropped: " << rtp_ssrc << " != " << ssrc; } else { if (!bind_peer_addr) { - //绑定对方ip+端口,防止多个设备或一个设备多次推流从而日志报ssrc不匹配问题 + // 绑定对方ip+端口,防止多个设备或一个设备多次推流从而日志报ssrc不匹配问题 [AUTO-TRANSLATED:f27dd373] + // Bind the peer IP + port to prevent multiple devices or one device from pushing multiple streams, resulting in log reports of mismatched SSRCs bind_peer_addr = true; rtp_socket->bindPeerAddr(addr, addr_len); } @@ -172,7 +188,8 @@ void RtpServer::start(uint16_t local_port, const char *local_ip, const MediaTupl } }); } else { - //单端口多线程接收多个流,根据ssrc区分流 + // 单端口多线程接收多个流,根据ssrc区分流 [AUTO-TRANSLATED:e11c3ca8] + // Single-port multi-threaded reception of multiple streams, distinguishing streams based on SSRC udp_server = std::make_shared(); (*udp_server)[RtpSession::kOnlyTrack] = only_track; (*udp_server)[RtpSession::kUdpRecvBuffer] = udpRecvSocketBuffer; @@ -185,7 +202,8 @@ void RtpServer::start(uint16_t local_port, const char *local_ip, const MediaTupl TcpServer::Ptr tcp_server; if (tcp_mode == PASSIVE || tcp_mode == ACTIVE) { auto processor = helper ? helper->getProcess() : nullptr; - // 如果共享同一个processor对象,那么tcp server深圳为单线程模式确保线程安全 + // 如果共享同一个processor对象,那么tcp server深圳为单线程模式确保线程安全 [AUTO-TRANSLATED:68bdd877] + // If the same processor object is shared, then the TCP server Shenzhen is in single-threaded mode to ensure thread safety tcp_server = std::make_shared(processor ? poller : nullptr); (*tcp_server)[RtpSession::kVhost] = tuple.vhost; (*tcp_server)[RtpSession::kApp] = tuple.app; @@ -198,14 +216,16 @@ void RtpServer::start(uint16_t local_port, const char *local_ip, const MediaTupl session->setRtpProcess(processor); }); } else if (tuple.stream.empty()) { - // tcp主动模式时只能一个端口一个流,必须指定流id; 创建TcpServer对象也仅用于传参 + // tcp主动模式时只能一个端口一个流,必须指定流id; 创建TcpServer对象也仅用于传参 [AUTO-TRANSLATED:61d2a642] + // In TCP active mode, only one port can have one stream, and the stream ID must be specified; the TcpServer object is created only for parameter passing throw std::runtime_error(StrPrinter << "tcp主动模式时必需指定流id"); } } _on_cleanup = [rtp_socket]() { if (rtp_socket) { - //去除循环引用 + // 去除循环引用 [AUTO-TRANSLATED:9afaed31] + // Remove circular references rtp_socket->setOnRead(nullptr); } }; diff --git a/src/Rtp/RtpServer.h b/src/Rtp/RtpServer.h index f98db95b..daf5b6e0 100644 --- a/src/Rtp/RtpServer.h +++ b/src/Rtp/RtpServer.h @@ -24,6 +24,9 @@ class RtcpHelper; /** * RTP服务器,支持UDP/TCP + * RTP server, supports UDP/TCP + + * [AUTO-TRANSLATED:03e7daba] */ class RtpServer : public std::enable_shared_from_this { public: @@ -42,6 +45,16 @@ public: * @param re_use_port 是否设置socket为re_use属性 * @param ssrc 指定的ssrc * @param multiplex 多路复用 + * Start the server, may throw an exception + * @param local_port Local port, 0 for random port + * @param local_ip Local network interface ip to bind + * @param stream_id Stream id, use ssrc if empty + * @param tcp_mode TCP service mode + * @param re_use_port Whether to set the socket to re_use property + * @param ssrc Specified ssrc + * @param multiplex Multiplexing + + * [AUTO-TRANSLATED:cb9e0717] */ void start(uint16_t local_port, const char *local_ip = "::", const MediaTuple &tuple = MediaTuple{DEFAULT_VHOST, kRtpAppName, "", ""}, TcpMode tcp_mode = PASSIVE, bool re_use_port = true, uint32_t ssrc = 0, int only_track = 0, bool multiplex = false); @@ -51,26 +64,42 @@ public: * @param url 服务器地址 * @param port 服务器端口 * @param cb 连接服务器是否成功的回调 + * Connect to the tcp service (tcp active mode) + * @param url Server address + * @param port Server port + * @param cb Callback whether the connection to the server is successful + + * [AUTO-TRANSLATED:3de57bc0] */ void connectToServer(const std::string &url, uint16_t port, const std::function &cb); /** * 获取绑定的本地端口 + * Get the bound local port + + * [AUTO-TRANSLATED:beed15d6] */ uint16_t getPort(); /** * 设置RtpProcess onDetach事件回调 + * Set RtpProcess onDetach event callback + + * [AUTO-TRANSLATED:a316d07e] */ void setOnDetach(RtpProcess::onDetachCB cb); /** * 更新ssrc + * Update ssrc + + * [AUTO-TRANSLATED:dae5e276] */ void updateSSRC(uint32_t ssrc); private: - // tcp主动模式连接服务器成功回调 + // tcp主动模式连接服务器成功回调 [AUTO-TRANSLATED:0775844e] + // tcp active mode connection server success callback void onConnect(); protected: @@ -82,7 +111,8 @@ protected: std::function _on_cleanup; int _only_track = 0; - //用于tcp主动模式 + // 用于tcp主动模式 [AUTO-TRANSLATED:faedf05c] + // Used for tcp active mode TcpMode _tcp_mode = NONE; }; diff --git a/src/Rtp/RtpSession.cpp b/src/Rtp/RtpSession.cpp index 6b68388d..a68146d7 100644 --- a/src/Rtp/RtpSession.cpp +++ b/src/Rtp/RtpSession.cpp @@ -40,7 +40,8 @@ void RtpSession::setParams(mINI &ini) { _only_track = ini[kOnlyTrack]; int udp_socket_buffer = ini[kUdpRecvBuffer]; if (_is_udp) { - // 设置udp socket读缓存 + // 设置udp socket读缓存 [AUTO-TRANSLATED:80cfb6e3] + // Set udp socket read buffer SockUtil::setRecvBuf(getSock()->rawFD(), (udp_socket_buffer > 0) ? udp_socket_buffer : (4 * 1024 * 1024)); } @@ -83,15 +84,18 @@ void RtpSession::setRtpProcess(RtpProcess::Ptr process) { void RtpSession::onRtpPacket(const char *data, size_t len) { if (!isRtp(data, len)) { - // 忽略非rtp数据 + // 忽略非rtp数据 [AUTO-TRANSLATED:771b77d8] + // Ignore non-rtp data WarnP(this) << "Not rtp packet"; return; } if (!_is_udp) { if (_search_rtp) { - //搜索上下文期间,数据丢弃 + // 搜索上下文期间,数据丢弃 [AUTO-TRANSLATED:e0a3b407] + // Data discarded during context search if (_search_rtp_finished) { - //下个包开始就是正确的rtp包了 + // 下个包开始就是正确的rtp包了 [AUTO-TRANSLATED:a73a3a61] + // The next packet is the correct rtp packet _search_rtp_finished = false; _search_rtp = false; } @@ -105,12 +109,14 @@ void RtpSession::onRtpPacket(const char *data, size_t len) { } } - // 未设置ssrc时,尝试获取ssrc + // 未设置ssrc时,尝试获取ssrc [AUTO-TRANSLATED:30f31a81] + // Try to get ssrc when ssrc is not set if (!_ssrc && !getSSRC(data, len, _ssrc)) { return; } - // 未指定流id就使用ssrc为流id + // 未指定流id就使用ssrc为流id [AUTO-TRANSLATED:9eb98394] + // Use ssrc as stream id if stream id is not specified if (_tuple.stream.empty()) { _tuple.stream = printSSRC(_ssrc); } @@ -146,7 +152,8 @@ void RtpSession::onRtpPacket(const char *data, size_t len) { } static const char *findSSRC(const char *data, ssize_t len, uint32_t ssrc) { - // rtp前面必须预留两个字节的长度字段 + // rtp前面必须预留两个字节的长度字段 [AUTO-TRANSLATED:2af4e647] + // Two bytes of length field must be reserved before rtp for (ssize_t i = 2; i <= len - 4; ++i) { auto ptr = (const uint8_t *)data + i; if (ptr[0] == (ssrc >> 24) && ptr[1] == ((ssrc >> 16) & 0xFF) && ptr[2] == ((ssrc >> 8) & 0xFF) @@ -160,7 +167,8 @@ static const char *findSSRC(const char *data, ssize_t len, uint32_t ssrc) { static const char *findPsHeaderFlag(const char *data, ssize_t len) { for (ssize_t i = 2; i <= len - 4; ++i) { auto ptr = (const uint8_t *)data + i; - // PsHeader 0x000001ba、PsSystemHeader0x000001bb(关键帧标识) + // PsHeader 0x000001ba、PsSystemHeader0x000001bb(关键帧标识) [AUTO-TRANSLATED:f8146534] + // PsHeader 0x000001ba, PsSystemHeader 0x000001bb (keyframe identifier) if (ptr[0] == (0x00) && ptr[1] == (0x00) && ptr[2] == (0x01) && ptr[3] == (0xbb)) { return (const char *)ptr; } @@ -169,15 +177,19 @@ static const char *findPsHeaderFlag(const char *data, ssize_t len) { return nullptr; } -// rtp长度到ssrc间的长度固定为10 +// rtp长度到ssrc间的长度固定为10 [AUTO-TRANSLATED:7428bd59] +// The length between rtp length and ssrc is fixed to 10 static size_t constexpr kSSRCOffset = 2 + 4 + 4; -// rtp长度到ps header间的长度固定为14 (暂时不采用找ps header,采用找system header代替) -// rtp长度到ps system header间的长度固定为20 (关键帧标识) +// rtp长度到ps header间的长度固定为14 (暂时不采用找ps header,采用找system header代替) [AUTO-TRANSLATED:cf6b289c] +// The length between rtp length and ps header is fixed to 14 (temporarily not using ps header, using system header instead) +// rtp长度到ps system header间的长度固定为20 (关键帧标识) [AUTO-TRANSLATED:abe8bb8e] +// The length between rtp length and ps system header is fixed to 20 (keyframe identifier) static size_t constexpr kPSHeaderOffset = 2 + 4 + 4 + 4 + 20; const char *RtpSession::onSearchPacketTail(const char *data, size_t len) { if (!_search_rtp) { - // tcp上下文正常,不用搜索ssrc + // tcp上下文正常,不用搜索ssrc [AUTO-TRANSLATED:cab86669] + // Tcp context is normal, no need to search ssrc return RtpSplitter::onSearchPacketTail(data, len); } if (!_process) { @@ -189,51 +201,62 @@ const char *RtpSession::onSearchPacketTail(const char *data, size_t len) { if (rtp_ptr0) { return rtp_ptr0; } - // ssrc搜索失败继续尝试搜索ps header flag + // ssrc搜索失败继续尝试搜索ps header flag [AUTO-TRANSLATED:e8f65bd2] + // Continue to search for ps header flag if ssrc search fails auto rtp_ptr2 = searchByPsHeaderFlag(data, len); return rtp_ptr2; } const char *RtpSession::searchBySSRC(const char *data, size_t len) { InfoL << "尝试rtp搜索ssrc..._ssrc=" << _ssrc; - // 搜索第一个rtp的ssrc + // 搜索第一个rtp的ssrc [AUTO-TRANSLATED:6b010df0] + // Search for the first rtp's ssrc auto ssrc_ptr0 = findSSRC(data, len, _ssrc); if (!ssrc_ptr0) { - // 未搜索到任意rtp,返回数据不够 + // 未搜索到任意rtp,返回数据不够 [AUTO-TRANSLATED:50db17ed] + // Return insufficient data if no rtp is found InfoL << "rtp搜索ssrc失败(第一个数据不够),丢弃rtp数据为:" << len; return nullptr; } - // 这两个字节是第一个rtp的长度字段 + // 这两个字节是第一个rtp的长度字段 [AUTO-TRANSLATED:75816ba4] + // These two bytes are the length field of the first rtp auto rtp_len_ptr = (ssrc_ptr0 - kSSRCOffset); auto rtp_len = ((uint8_t *)rtp_len_ptr)[0] << 8 | ((uint8_t *)rtp_len_ptr)[1]; - // 搜索第二个rtp的ssrc + // 搜索第二个rtp的ssrc [AUTO-TRANSLATED:238eaa43] + // Search for the second rtp's ssrc auto ssrc_ptr1 = findSSRC(ssrc_ptr0 + rtp_len, data + (ssize_t)len - ssrc_ptr0 - rtp_len, _ssrc); if (!ssrc_ptr1) { - // 未搜索到第二个rtp,返回数据不够 + // 未搜索到第二个rtp,返回数据不够 [AUTO-TRANSLATED:3a78a586] + // Return insufficient data if the second rtp is not found InfoL << "rtp搜索ssrc失败(第二个数据不够),丢弃rtp数据为:" << len; return nullptr; } - // 两个ssrc的间隔正好等于rtp的长度(外加rtp长度字段),那么说明找到rtp + // 两个ssrc的间隔正好等于rtp的长度(外加rtp长度字段),那么说明找到rtp [AUTO-TRANSLATED:b1517bfd] + // The interval between the two ssrcs is exactly equal to the length of the rtp (plus the rtp length field), which means that the rtp is found auto ssrc_offset = ssrc_ptr1 - ssrc_ptr0; if (ssrc_offset == rtp_len + 2 || ssrc_offset == rtp_len + 4) { InfoL << "rtp搜索ssrc成功,tcp上下文恢复成功,丢弃的rtp残余数据为:" << rtp_len_ptr - data; _search_rtp_finished = true; if (rtp_len_ptr == data) { - // 停止搜索rtp,否则会进入死循环 + // 停止搜索rtp,否则会进入死循环 [AUTO-TRANSLATED:319eefa7] + // Stop searching for rtp, otherwise it will enter an infinite loop _search_rtp = false; } - // 前面的数据都需要丢弃,这个是rtp的起始 + // 前面的数据都需要丢弃,这个是rtp的起始 [AUTO-TRANSLATED:129082d2] + // All previous data needs to be discarded, this is the start of rtp return rtp_len_ptr; } - // 第一个rtp长度不匹配,说明第一个找到的ssrc不是rtp,丢弃之,我们从第二个ssrc所在rtp开始搜索 + // 第一个rtp长度不匹配,说明第一个找到的ssrc不是rtp,丢弃之,我们从第二个ssrc所在rtp开始搜索 [AUTO-TRANSLATED:ec35b2ba] + // The length of the first rtp does not match, which means that the first ssrc found is not rtp, discard it, we start searching from the second ssrc rtp return ssrc_ptr1 - kSSRCOffset; } const char *RtpSession::searchByPsHeaderFlag(const char *data, size_t len) { InfoL << "尝试rtp搜索PsSystemHeaderFlag..._ssrc=" << _ssrc; - // 搜索rtp中的第一个PsHeaderFlag + // 搜索rtp中的第一个PsHeaderFlag [AUTO-TRANSLATED:77a18970] + // Search for the first PsHeaderFlag in rtp auto ps_header_flag_ptr = findPsHeaderFlag(data, len); if (!ps_header_flag_ptr) { InfoL << "rtp搜索flag失败,丢弃rtp数据为:" << len; @@ -243,12 +266,14 @@ const char *RtpSession::searchByPsHeaderFlag(const char *data, size_t len) { auto rtp_ptr = ps_header_flag_ptr - kPSHeaderOffset; _search_rtp_finished = true; if (rtp_ptr == data) { - // 停止搜索rtp,否则会进入死循环 + // 停止搜索rtp,否则会进入死循环 [AUTO-TRANSLATED:319eefa7] + // Stop searching for rtp, otherwise it will enter an infinite loop _search_rtp = false; } InfoL << "rtp搜索flag成功,tcp上下文恢复成功,丢弃的rtp残余数据为:" << rtp_ptr - data; - // TODO or Not ? 更新设置ssrc + // TODO or Not ? 更新设置ssrc [AUTO-TRANSLATED:9c21db0a] + // TODO or Not ? Update setting ssrc uint32_t rtp_ssrc = 0; getSSRC(rtp_ptr + 2, len, rtp_ssrc); _ssrc = rtp_ssrc; diff --git a/src/Rtp/RtpSession.h b/src/Rtp/RtpSession.h index b6dd300e..4a060858 100644 --- a/src/Rtp/RtpSession.h +++ b/src/Rtp/RtpSession.h @@ -40,13 +40,16 @@ public: void setRtpProcess(RtpProcess::Ptr process); protected: - // 收到rtp回调 + // 收到rtp回调 [AUTO-TRANSLATED:446b2cda] + // Received RTP callback void onRtpPacket(const char *data, size_t len) override; // RtpSplitter override const char *onSearchPacketTail(const char *data, size_t len) override; - // 搜寻SSRC + // 搜寻SSRC [AUTO-TRANSLATED:2cfec2e1] + // Search for SSRC const char *searchBySSRC(const char *data, size_t len); - // 搜寻PS包里的关键帧标头 + // 搜寻PS包里的关键帧标头 [AUTO-TRANSLATED:d8e88339] + // Search for keyframe header in PS packet const char *searchByPsHeaderFlag(const char *data, size_t len); private: diff --git a/src/Rtp/RtpSplitter.cpp b/src/Rtp/RtpSplitter.cpp index bdf4e960..d82cf6cd 100644 --- a/src/Rtp/RtpSplitter.cpp +++ b/src/Rtp/RtpSplitter.cpp @@ -16,12 +16,14 @@ namespace mediakit{ static const int kEHOME_OFFSET = 256; ssize_t RtpSplitter::onRecvHeader(const char *data,size_t len){ - //忽略偏移量 + // 忽略偏移量 [AUTO-TRANSLATED:7fbc3d5d] + // Ignore offset data += _offset; len -= _offset; if (_is_ehome && len > 12 && data[12] == '\r') { - //这是ehome,移除第12个字节 + // 这是ehome,移除第12个字节 [AUTO-TRANSLATED:643b3f39] + // This is ehome, remove the 12th byte memmove((char *) data + 1, data, 12); data += 1; len -= 1; @@ -42,21 +44,26 @@ static bool isEhome(const char *data, size_t len){ const char *RtpSplitter::onSearchPacketTail(const char *data, size_t len) { if (len < 4) { - //数据不够 + // 数据不够 [AUTO-TRANSLATED:72802244] + // Not enough data return nullptr; } if (_check_ehome_count) { if (isEhome(data, len)) { - //是ehome协议 + // 是ehome协议 [AUTO-TRANSLATED:daa874ca] + // It is the ehome protocol if (len < kEHOME_OFFSET + 4) { - //数据不够 + // 数据不够 [AUTO-TRANSLATED:72802244] + // Not enough data return nullptr; } - //忽略ehome私有头后是rtsp样式的rtp,多4个字节, + // 忽略ehome私有头后是rtsp样式的rtp,多4个字节, [AUTO-TRANSLATED:d9bc652c] + // Ignore the ehome private header, then it is an rtsp-style rtp, with 4 extra bytes, _offset = kEHOME_OFFSET + 4; _is_ehome = true; - //忽略ehome私有头 + // 忽略ehome私有头 [AUTO-TRANSLATED:4fc98661] + // Ignore the ehome private header return onSearchPacketTail_l(data + kEHOME_OFFSET + 2, len - kEHOME_OFFSET - 2); } _check_ehome_count--; @@ -64,26 +71,31 @@ const char *RtpSplitter::onSearchPacketTail(const char *data, size_t len) { if ( _is_rtsp_interleaved ) { if (data[0] == '$') { - //可能是4个字节的rtp头 + // 可能是4个字节的rtp头 [AUTO-TRANSLATED:c287c3cb] + // It may be a 4-byte rtp header _offset = 4; return onSearchPacketTail_l(data + 2, len - 2); } _is_rtsp_interleaved = false; } - //两个字节的rtp头 + // 两个字节的rtp头 [AUTO-TRANSLATED:85fed462] + // A 2-byte rtp header _offset = 2; return onSearchPacketTail_l(data, len); } const char *RtpSplitter::onSearchPacketTail_l(const char *data, size_t len) { - //这是rtp包 + // 这是rtp包 [AUTO-TRANSLATED:627d4881] + // This is an rtp packet uint16_t length = (((uint8_t *) data)[0] << 8) | ((uint8_t *) data)[1]; if (len < (size_t)(length + 2)) { - //数据不够 + // 数据不够 [AUTO-TRANSLATED:72802244] + // Not enough data return nullptr; } - //返回rtp包末尾 + // 返回rtp包末尾 [AUTO-TRANSLATED:2546d5ce] + // Return the end of the rtp packet return data + 2 + length; } diff --git a/src/Rtp/RtpSplitter.h b/src/Rtp/RtpSplitter.h index c6b85257..c583c932 100644 --- a/src/Rtp/RtpSplitter.h +++ b/src/Rtp/RtpSplitter.h @@ -22,6 +22,12 @@ protected: * 收到rtp包回调 * @param data RTP包数据指针 * @param len RTP包数据长度 + * RTP packet received callback + * @param data RTP packet data pointer + * @param len RTP packet data length + + + * [AUTO-TRANSLATED:18a85278] */ virtual void onRtpPacket(const char *data, size_t len) = 0; diff --git a/src/Rtp/TSDecoder.cpp b/src/Rtp/TSDecoder.cpp index f3f4086f..8efefc31 100644 --- a/src/Rtp/TSDecoder.cpp +++ b/src/Rtp/TSDecoder.cpp @@ -35,7 +35,8 @@ const char *TSSegment::onSearchPacketTail(const char *data, size_t len) { } return nullptr; } - //下一个包头 + // 下一个包头 [AUTO-TRANSLATED:c653c49d] + // Next packet header if (((uint8_t *) data)[_size] == TS_SYNC_BYTE) { return data + _size; } @@ -44,10 +45,12 @@ const char *TSSegment::onSearchPacketTail(const char *data, size_t len) { return (char *) pos; } if (remainDataSize() > 4 * _size) { - //数据这么多都没ts包,全部清空 + // 数据这么多都没ts包,全部清空 [AUTO-TRANSLATED:95bece98] + // So much data but no ts packets, clear all return data + len; } - //等待更多数据 + // 等待更多数据 [AUTO-TRANSLATED:b47fbc81] + // Wait for more data return nullptr; } @@ -93,7 +96,8 @@ ssize_t TSDecoder::input(const uint8_t *data, size_t bytes) { try { _ts_segment.input((char *) data, bytes); } catch (...) { - //ts解析失败,清空缓存数据 + // ts解析失败,清空缓存数据 [AUTO-TRANSLATED:18b3de5b] + // ts parsing failed, clear cache data _ts_segment.reset(); throw; } diff --git a/src/Rtp/TSDecoder.h b/src/Rtp/TSDecoder.h index 849b358b..7812d517 100644 --- a/src/Rtp/TSDecoder.h +++ b/src/Rtp/TSDecoder.h @@ -20,7 +20,8 @@ namespace mediakit { -//TS包分割器,用于split一个一个的ts包 +// TS包分割器,用于split一个一个的ts包 [AUTO-TRANSLATED:a10b66b3] +// TS package splitter, used to split one ts package at a time class TSSegment : public HttpRequestSplitter { public: typedef std::function onSegment; @@ -38,7 +39,8 @@ private: }; #if defined(ENABLE_HLS) -//ts解析器 +// ts解析器 [AUTO-TRANSLATED:f2b9f0cc] +// ts parser class TSDecoder : public Decoder { public: TSDecoder(); diff --git a/src/Rtsp/RtpCodec.cpp b/src/Rtsp/RtpCodec.cpp index 65ee311f..0ed1cd2c 100644 --- a/src/Rtsp/RtpCodec.cpp +++ b/src/Rtsp/RtpCodec.cpp @@ -21,14 +21,16 @@ RtpPacket::Ptr RtpInfo::makeRtp(TrackType type, const void* data, size_t len, bo rtp->type = type; rtp->track_index = _track_index; - //rtsp over tcp 头 + // rtsp over tcp 头 [AUTO-TRANSLATED:4225b9ec] + // rtsp over tcp header auto ptr = (uint8_t *) rtp->data(); ptr[0] = '$'; ptr[1] = _interleaved; ptr[2] = payload_len >> 8; ptr[3] = payload_len & 0xFF; - //rtp头 + // rtp头 [AUTO-TRANSLATED:64aef747] + // rtp header auto header = rtp->getHeader(); header->version = RtpPacket::kRtpVersion; header->padding = 0; @@ -41,7 +43,8 @@ RtpPacket::Ptr RtpInfo::makeRtp(TrackType type, const void* data, size_t len, bo header->stamp = htonl(uint64_t(stamp) * _sample_rate / 1000); header->ssrc = htonl(_ssrc); rtp->ntp_stamp = stamp; - //有效负载 + // 有效负载 [AUTO-TRANSLATED:8530a274] + // payload if (data) { memcpy(&ptr[RtpPacket::kRtpHeaderSize + RtpPacket::kRtpTcpHeaderSize], data, len); } diff --git a/src/Rtsp/RtpCodec.h b/src/Rtsp/RtpCodec.h index 588e7fc6..b247dd46 100644 --- a/src/Rtsp/RtpCodec.h +++ b/src/Rtsp/RtpCodec.h @@ -28,6 +28,10 @@ public: /** * 设置rtp环形缓存 * @param ring + * Set the RTP ring buffer + * @param ring + + * [AUTO-TRANSLATED:283d737a] */ void setRtpRing(RingType::Ptr ring) { _ring = std::move(ring); @@ -38,6 +42,12 @@ public: * @param rtp rtp包 * @param key_pos 是否为关键帧第一个rtp包 * @return 是否为关键帧第一个rtp包 + * Input RTP packet + * @param rtp RTP packet + * @param key_pos Whether it is the first RTP packet of the key frame + * @return Whether it is the first RTP packet of the key frame + + * [AUTO-TRANSLATED:05ccdd17] */ virtual bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) { if (_ring) { @@ -66,7 +76,8 @@ public: _track_index = track_index; } - //返回rtp负载最大长度 + // 返回rtp负载最大长度 [AUTO-TRANSLATED:0a8ee7d9] + // Return the maximum length of the RTP payload size_t getMaxSize() const { return _mtu_size - RtpPacket::kRtpHeaderSize; } @@ -107,6 +118,13 @@ public: * * @param opt 设置的选项 * @param param 设置的参数 + * @brief Set the parameters of the RTP packer and unpacker, mainly used for g711 RTP packer, the usage is similar to setsockopt + * + * @param opt Set options + * @param param Set parameters + + + * [AUTO-TRANSLATED:95859425] */ virtual void setOpt(int opt, const toolkit::Any ¶m) {}; diff --git a/src/Rtsp/RtpMultiCaster.cpp b/src/Rtsp/RtpMultiCaster.cpp index 37ada760..1df69dc9 100644 --- a/src/Rtsp/RtpMultiCaster.cpp +++ b/src/Rtsp/RtpMultiCaster.cpp @@ -58,11 +58,13 @@ std::shared_ptr MultiCastAddressMaker::obtain(uint32_t max_try) { } auto iGotAddr = _addr++; if (_used_addr.find(iGotAddr) != _used_addr.end()) { - //已经分配过了 + // 已经分配过了 [AUTO-TRANSLATED:b231af33] + // Already allocated if (max_try) { return obtain(--max_try); } - //分配完了,应该不可能到这里 + // 分配完了,应该不可能到这里 [AUTO-TRANSLATED:c7f06cb9] + // Allocation is complete, it should not be possible to reach here ErrorL; return nullptr; } @@ -111,7 +113,8 @@ RtpMultiCaster::RtpMultiCaster(SocketHelper &helper, const string &local_ip, con } for (auto i = 0; i < 2; ++i) { - //创建udp socket, 数组下标为TrackType + // 创建udp socket, 数组下标为TrackType [AUTO-TRANSLATED:17d153d5] + // Create UDP socket, array index is TrackType _udp_sock[i] = helper.createSocket(); if (!_udp_sock[i]->bindUdpSock((i == TrackVideo) ? video_port : audio_port, local_ip.data())) { auto err = StrPrinter << "绑定UDP端口失败:" << local_ip << endl; @@ -125,9 +128,11 @@ RtpMultiCaster::RtpMultiCaster(SocketHelper &helper, const string &local_ip, con struct sockaddr_in peer; peer.sin_family = AF_INET; - //组播目标端口为本地发送端口 + // 组播目标端口为本地发送端口 [AUTO-TRANSLATED:9eae5d47] + // Multicast target port is the local sending port peer.sin_port = htons(_udp_sock[i]->get_local_port()); - //组播目标地址 + // 组播目标地址 [AUTO-TRANSLATED:3291a33b] + // Multicast target address peer.sin_addr.s_addr = htonl(*_multicast_ip); bzero(&(peer.sin_zero), sizeof peer.sin_zero); _udp_sock[i]->bindPeerAddr((struct sockaddr *) &peer); diff --git a/src/Rtsp/RtpReceiver.cpp b/src/Rtsp/RtpReceiver.cpp index 8dd88e70..f17db01f 100644 --- a/src/Rtsp/RtpReceiver.cpp +++ b/src/Rtsp/RtpReceiver.cpp @@ -39,7 +39,8 @@ RtpPacket::Ptr RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, return nullptr; } if (!sample_rate) { - //无法把时间戳转换成毫秒 + // 无法把时间戳转换成毫秒 [AUTO-TRANSLATED:ec2d97b6] + // Unable to convert timestamp to milliseconds return nullptr; } RtpHeader *header = (RtpHeader *) ptr; @@ -47,11 +48,13 @@ RtpPacket::Ptr RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, throw BadRtpException("invalid rtp version"); } if (header->getPayloadSize(len) < 0) { - //rtp有效负载小于0,非法 + // rtp有效负载小于0,非法 [AUTO-TRANSLATED:07eb3ec3] + // RTP payload is less than 0, illegal throw BadRtpException("invalid rtp payload size"); } - //比对缓存ssrc + // 比对缓存ssrc [AUTO-TRANSLATED:206cb66f] + // Compare cache ssrc auto ssrc = ntohl(header->ssrc); if (_pt == 0xFF) { @@ -62,16 +65,20 @@ RtpPacket::Ptr RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, } if (!_ssrc) { - //记录并锁定ssrc + // 记录并锁定ssrc [AUTO-TRANSLATED:29452029] + // Record and lock ssrc _ssrc = ssrc; _ssrc_alive.resetTime(); } else if (_ssrc == ssrc) { - //ssrc匹配正确,刷新计时器 + // ssrc匹配正确,刷新计时器 [AUTO-TRANSLATED:266518e6] + // SSRC matches correctly, refresh timer _ssrc_alive.resetTime(); } else { - //ssrc错误 + // ssrc错误 [AUTO-TRANSLATED:b967d497] + // SSRC error if (_ssrc_alive.elapsedTime() < 3 * 1000) { - //接收正确ssrc的rtp在10秒内,那么我们认为存在多路rtp,忽略掉ssrc不匹配的rtp + // 接收正确ssrc的rtp在10秒内,那么我们认为存在多路rtp,忽略掉ssrc不匹配的rtp [AUTO-TRANSLATED:2f98c2b5] + // If the RTP with the correct SSRC is received within 10 seconds, we consider it to be multi-path RTP, and ignore the RTP with mismatched SSRC WarnL << "ssrc mismatch, rtp dropped:" << ssrc << " != " << _ssrc; return nullptr; } @@ -81,25 +88,30 @@ RtpPacket::Ptr RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, } auto rtp = RtpPacket::create(); - //需要添加4个字节的rtp over tcp头 + // 需要添加4个字节的rtp over tcp头 [AUTO-TRANSLATED:a37d639b] + // Need to add 4 bytes of RTP over TCP header rtp->setCapacity(RtpPacket::kRtpTcpHeaderSize + len); rtp->setSize(RtpPacket::kRtpTcpHeaderSize + len); rtp->sample_rate = sample_rate; rtp->type = type; - //赋值4个字节的rtp over tcp头 + // 赋值4个字节的rtp over tcp头 [AUTO-TRANSLATED:eeb990a9] + // Assign 4 bytes of RTP over TCP header uint8_t *data = (uint8_t *) rtp->data(); data[0] = '$'; data[1] = 2 * type; data[2] = (len >> 8) & 0xFF; data[3] = len & 0xFF; - //拷贝rtp + // 拷贝rtp [AUTO-TRANSLATED:3a2466c2] + // Copy RTP memcpy(&data[4], ptr, len); if (_disable_ntp) { - //不支持ntp时间戳,例如国标推流,那么直接使用rtp时间戳 + // 不支持ntp时间戳,例如国标推流,那么直接使用rtp时间戳 [AUTO-TRANSLATED:20085979] + // Does not support NTP timestamp, such as national standard streaming, so directly use RTP timestamp rtp->ntp_stamp = rtp->getStamp() * uint64_t(1000) / sample_rate; } else { - //设置ntp时间戳 + // 设置ntp时间戳 [AUTO-TRANSLATED:5e60d5cf] + // Set NTP timestamp rtp->ntp_stamp = _ntp_stamp.getNtpStamp(rtp->getStamp(), sample_rate); } onBeforeRtpSorted(rtp); diff --git a/src/Rtsp/RtpReceiver.h b/src/Rtsp/RtpReceiver.h index c2049855..e650f7ae 100644 --- a/src/Rtsp/RtpReceiver.h +++ b/src/Rtsp/RtpReceiver.h @@ -34,6 +34,9 @@ public: /** * 清空状态 + * Clear the state + + * [AUTO-TRANSLATED:6aadbd77] */ void clear() { _started = false; @@ -43,6 +46,9 @@ public: /** * 获取排序缓存长度 + * Get the length of the sorting cache + + * [AUTO-TRANSLATED:8e05a703] */ size_t getJitterSize() const { return _pkt_sort_cache_map.size(); } @@ -50,25 +56,45 @@ public: * 输入并排序 * @param seq 序列号 * @param packet 包负载 + * Input and sort + * @param seq Sequence number + * @param packet Packet payload + + * [AUTO-TRANSLATED:0fbf096e] */ void sortPacket(SEQ seq, T packet) { _latest_seq = seq; if (!_started) { - // 记录第一个seq + // 记录第一个seq [AUTO-TRANSLATED:410c831f] + // Record the first seq _started = true; _last_seq_out = seq - 1; } auto next_seq = static_cast(_last_seq_out + 1); if (seq == next_seq) { - // 收到下一个seq + // 收到下一个seq [AUTO-TRANSLATED:44960fea] + // Receive the next seq output(seq, std::move(packet)); - // 清空连续包列表 + // 清空连续包列表 [AUTO-TRANSLATED:fdaafd3b] + // Clear the continuous packet list flushPacket(); + _pkt_drop_cache_map.clear(); return; } if (seq < next_seq && !mayLooped(next_seq, seq)) { - // 无回环风险, 过滤seq回退包 + // 无回环风险, 缓存seq回退包 [AUTO-TRANSLATED:4200dd1b] + // No loop risk, cache seq rollback packets + _pkt_drop_cache_map.emplace(seq, std::move(packet)); + if (_pkt_drop_cache_map.size() > _max_distance || _ticker.elapsedTime() > _max_buffer_ms) { + // seq回退包太多,可能源端重置seq计数器,这部分数据需要输出 [AUTO-TRANSLATED:d31aead7] + // Too many seq rollback packets, the source may reset the seq counter, this part of data needs to be output + forceFlush(next_seq); + // 旧的seq计数器的数据清空后把新seq计数器的数据赋值给排序列队 [AUTO-TRANSLATED:f69f864c] + // After clearing the data of the old seq counter, assign the data of the new seq counter to the sorting queue + _pkt_sort_cache_map = std::move(_pkt_drop_cache_map); + popIterator(_pkt_sort_cache_map.begin()); + } return; } _pkt_sort_cache_map.emplace(seq, std::move(packet)); @@ -107,23 +133,29 @@ private: } bool needForceFlush(SEQ seq) { - return !_pkt_sort_cache_map.empty() && (_pkt_sort_cache_map.size() > _max_buffer_size || - distance(seq) > _max_distance || _ticker.elapsedTime() > _max_buffer_ms); + return _pkt_sort_cache_map.size() > _max_buffer_size || distance(seq) > _max_distance || _ticker.elapsedTime() > _max_buffer_ms; } - //外部调用代码确保_pkt_sort_cache_map不为空 void forceFlush(SEQ next_seq) { - // 寻找距离比next_seq大的最近的seq + if (_pkt_sort_cache_map.empty()) { + return; + } + // 寻找距离比next_seq大的最近的seq [AUTO-TRANSLATED:d2de6f5b] + // Find the nearest seq that is greater than next_seq auto it = _pkt_sort_cache_map.lower_bound(next_seq); if (it == _pkt_sort_cache_map.end()) { - // 没有比next_seq更大的seq,应该是回环时丢包导致 + // 没有比next_seq更大的seq,应该是回环时丢包导致 [AUTO-TRANSLATED:d0d6970b] + // There is no seq greater than next_seq, it should be caused by packet loss during loopback it = _pkt_sort_cache_map.begin(); } - // 丢包无法恢复,把这个包当做next_seq + // 丢包无法恢复,把这个包当做next_seq [AUTO-TRANSLATED:2d8c0b9e] + // Packet loss cannot be recovered, treat this packet as next_seq popIterator(it); - // 清空连续包列表 + // 清空连续包列表 [AUTO-TRANSLATED:fdaafd3b] + // Clear the continuous packet list flushPacket(); - // 删除距离next_seq太大的包 + // 删除距离next_seq太大的包 [AUTO-TRANSLATED:9e774c5e] + // Delete packets that are too far away from next_seq for (auto it = _pkt_sort_cache_map.begin(); it != _pkt_sort_cache_map.end();) { if (distance(it->first) > _max_distance) { it = _pkt_sort_cache_map.erase(it); @@ -142,12 +174,14 @@ private: auto next_seq = static_cast(_last_seq_out + 1); auto it = _pkt_sort_cache_map.lower_bound(next_seq); if (!mayLooped(next_seq, next_seq)) { - // 无回环风险, 清空 < next_seq的值 + // 无回环风险, 清空 < next_seq的值 [AUTO-TRANSLATED:10c77bf9] + // No loop risk, clear values less than next_seq it = _pkt_sort_cache_map.erase(_pkt_sort_cache_map.begin(), it); } while (it != _pkt_sort_cache_map.end()) { - // 找到下一个包 + // 找到下一个包 [AUTO-TRANSLATED:8e20ab9f] + // Find the next packet if (it->first == static_cast(_last_seq_out + 1)) { it = popIterator(it); continue; @@ -176,21 +210,32 @@ private: private: bool _started = false; - // 排序缓存最大保存数据长度,单位毫秒 + // 排序缓存最大保存数据长度,单位毫秒 [AUTO-TRANSLATED:ed217b1c] + // Maximum data length of sorting cache, unit: milliseconds size_t _max_buffer_ms = 1000; - // 排序缓存最大保存数据个数 + // 排序缓存最大保存数据个数 [AUTO-TRANSLATED:9cfa91b4] + // Maximum number of data in sorting cache size_t _max_buffer_size = 1024; - // seq最大跳跃距离 + // seq最大跳跃距离 [AUTO-TRANSLATED:bb663e41] + // Maximum seq jump distance size_t _max_distance = 256; - // 记录上次output至今的时间 + // 记录上次output至今的时间 [AUTO-TRANSLATED:83e53e42] + // Record the time since the last output toolkit::Ticker _ticker; - // 最近输入的seq + // 最近输入的seq [AUTO-TRANSLATED:24ca96ee] + // The most recently input seq SEQ _latest_seq = 0; - // 下次应该输出的SEQ + // 下次应该输出的SEQ [AUTO-TRANSLATED:e757a4fa] + // The next SEQ to be output SEQ _last_seq_out = 0; - // pkt排序缓存,根据seq排序 + // pkt排序缓存,根据seq排序 [AUTO-TRANSLATED:3787f9a6] + // pkt sorting cache, sorted by seq std::map _pkt_sort_cache_map; - // 回调 + // 预丢弃包列表 [AUTO-TRANSLATED:67e57ebc] + // Pre-discard packet list + std::map _pkt_drop_cache_map; + // 回调 [AUTO-TRANSLATED:03bad27d] + // Callback std::function _cb; }; @@ -265,6 +310,15 @@ public: * @param ptr rtp数据指针 * @param len rtp数据指针长度 * @return 解析成功返回true + * Generate and sort rtp packets from input data pointer + * @param index Track index + * @param type Track type + * @param samplerate RTP timestamp base clock, 90000 for video, sample rate for audio + * @param ptr RTP data pointer + * @param len RTP data pointer length + * @return Return true if parsing is successful + + * [AUTO-TRANSLATED:4ec12e4a] */ bool handleOneRtp(int index, TrackType type, int sample_rate, uint8_t *ptr, size_t len) { assert(index < kCount && index >= 0); @@ -277,6 +331,13 @@ public: * @param index track下标索引 * @param rtp_stamp rtp时间戳 * @param ntp_stamp_ms ntp时间戳 + * Set ntp timestamp, set when receiving rtcp sender report + * If rtp_stamp/sample_rate/ntp_stamp_ms are all 0, then use rtp timestamp as ntp timestamp + * @param index Track index + * @param rtp_stamp RTP timestamp + * @param ntp_stamp_ms NTP timestamp + + * [AUTO-TRANSLATED:1e50904e] */ void setNtpStamp(int index, uint32_t rtp_stamp, uint64_t ntp_stamp_ms) { assert(index < kCount && index >= 0); @@ -309,6 +370,11 @@ protected: * rtp数据包排序后输出 * @param rtp rtp数据包 * @param track_index track索引 + * Output rtp data packets after sorting + * @param rtp RTP data packet + * @param track_index Track index + + * [AUTO-TRANSLATED:55022da9] */ virtual void onRtpSorted(RtpPacket::Ptr rtp, int index) {} @@ -316,6 +382,11 @@ protected: * 解析出rtp但还未排序 * @param rtp rtp数据包 * @param track_index track索引 + * RTP data packet parsed but not yet sorted + * @param rtp RTP data packet + * @param track_index Track index + + * [AUTO-TRANSLATED:c1636911] */ virtual void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int index) {} diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index d1cc0356..19d8da5d 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -102,7 +102,8 @@ string SdpTrack::getName() const { string SdpTrack::getControlUrl(const string &base_url) const { if (_control.find("://") != string::npos) { - // 以rtsp://开头 + // 以rtsp://开头 [AUTO-TRANSLATED:293b3f8c] + // Starts with rtsp:// return _control; } return base_url + "/" + _control; @@ -224,7 +225,8 @@ void SdpParser::load(const string &sdp) { sscanf(rtpmap.data(), "%d", &pt); if (track._pt != pt && track._pt != 0xff) { - // pt不匹配 + // pt不匹配 [AUTO-TRANSLATED:ce7abb0a] + // pt mismatch it = track._attr.erase(it); continue; } @@ -245,7 +247,8 @@ void SdpParser::load(const string &sdp) { int pt; sscanf(fmtp.data(), "%d", &pt); if (track._pt != pt && track._pt != 0xff) { - // pt不匹配 + // pt不匹配 [AUTO-TRANSLATED:ce7abb0a] + // pt mismatch it = track._attr.erase(it); continue; } @@ -259,7 +262,8 @@ void SdpParser::load(const string &sdp) { } if (!track._samplerate && track._type == TrackVideo) { - // 未设置视频采样率时,赋值为90000 + // 未设置视频采样率时,赋值为90000 [AUTO-TRANSLATED:416c4f0f] + // If video sampling rate is not set, assign it to 90000 track._samplerate = 90000; } else if (!track._samplerate && track._type == TrackAudio) { // some rtsp sdp no sample rate but has fmt config to parser get sample rate @@ -333,7 +337,8 @@ string SdpParser::toString() const { std::string SdpParser::getControlUrl(const std::string &url) const { auto title_track = getTrack(TrackTitle); if (title_track && title_track->_control.find("://") != string::npos) { - // 以rtsp://开头 + // 以rtsp://开头 [AUTO-TRANSLATED:293b3f8c] + // Starts with rtsp:// return title_track->_control; } return url; @@ -366,7 +371,8 @@ public: } makeSockPair_l(sock_pair, pair, local_ip, re_use_port, is_udp); - // 确保udp和tcp模式都能打开 + // 确保udp和tcp模式都能打开 [AUTO-TRANSLATED:dcb46232] + // Ensure both udp and tcp modes are open auto new_pair = std::make_pair(Socket::createSocket(), Socket::createSocket()); makeSockPair_l(sock_pair, new_pair, local_ip, re_use_port, !is_udp); } @@ -376,32 +382,38 @@ public: auto &sock1 = pair.second; if (is_udp) { if (!sock0->bindUdpSock(2 * *sock_pair, local_ip.data(), re_use_port)) { - // 分配端口失败 + // 分配端口失败 [AUTO-TRANSLATED:59ecd25d] + // Port allocation failed throw runtime_error("open udp socket[0] failed"); } if (!sock1->bindUdpSock(2 * *sock_pair + 1, local_ip.data(), re_use_port)) { - // 分配端口失败 + // 分配端口失败 [AUTO-TRANSLATED:59ecd25d] + // Port allocation failed throw runtime_error("open udp socket[1] failed"); } auto on_cycle = [sock_pair](Socket::Ptr &, std::shared_ptr &) {}; - // udp socket没onAccept事件,设置该回调,目的是为了在销毁socket时,回收对象 + // udp socket没onAccept事件,设置该回调,目的是为了在销毁socket时,回收对象 [AUTO-TRANSLATED:ee91256f] + // UDP socket has no onAccept event, set this callback to recycle the object when destroying the socket sock0->setOnAccept(on_cycle); sock1->setOnAccept(on_cycle); } else { if (!sock0->listen(2 * *sock_pair, local_ip.data())) { - // 分配端口失败 + // 分配端口失败 [AUTO-TRANSLATED:59ecd25d] + // Port allocation failed throw runtime_error("listen tcp socket[0] failed"); } if (!sock1->listen(2 * *sock_pair + 1, local_ip.data())) { - // 分配端口失败 + // 分配端口失败 [AUTO-TRANSLATED:59ecd25d] + // Port allocation failed throw runtime_error("listen tcp socket[1] failed"); } auto on_cycle = [sock_pair](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) {}; - // udp socket没onAccept事件,设置该回调,目的是为了在销毁socket时,回收对象 + // udp socket没onAccept事件,设置该回调,目的是为了在销毁socket时,回收对象 [AUTO-TRANSLATED:ee91256f] + // UDP socket has no onAccept event, set this callback to recycle the object when destroying the socket sock0->setOnRead(on_cycle); sock1->setOnRead(on_cycle); } @@ -413,7 +425,8 @@ private: lock_guard lck(_pool_mtx); auto it = _port_pair_pool.begin(); while (start_pos < end_pos) { - // 随机端口排序,防止重启后导致分配的端口重复 + // 随机端口排序,防止重启后导致分配的端口重复 [AUTO-TRANSLATED:b622db1c] + // Randomly sort ports to prevent duplicate port allocation after restart _port_pair_pool.insert(it, start_pos++); it = _port_pair_pool.begin() + (rng() % (1 + _port_pair_pool.size())); } @@ -436,7 +449,8 @@ private: return; } InfoL << "return port to pool:" << 2 * pos << "-" << 2 * pos + 1; - // 回收端口号 + // 回收端口号 [AUTO-TRANSLATED:646a5284] + // Recycle port number lock_guard lck(strong_self->_pool_mtx); strong_self->_port_pair_pool.emplace_back(pos); }); @@ -452,7 +466,8 @@ void makeSockPair(std::pair &pair, const string &local int try_count = 0; while (true) { try { - // udp和tcp端口池使用相同算法和范围分配,但是互不相干 + // udp和tcp端口池使用相同算法和范围分配,但是互不相干 [AUTO-TRANSLATED:86200c72] + // UDP and TCP port pools use the same algorithm and range for allocation, but are independent of each other if (is_udp) { PortManager<0>::Instance().makeSockPair(pair, local_ip, re_use_port, is_udp); } else { @@ -518,7 +533,8 @@ Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved) { #define AV_RB16(x) ((((const uint8_t *)(x))[0] << 8) | ((const uint8_t *)(x))[1]) size_t RtpHeader::getCsrcSize() const { - // 每个csrc占用4字节 + // 每个csrc占用4字节 [AUTO-TRANSLATED:6237ca37] + // Each csrc occupies 4 bytes return csrc << 2; } @@ -530,18 +546,21 @@ uint8_t *RtpHeader::getCsrcData() { } size_t RtpHeader::getExtSize() const { - // rtp有ext + // rtp有ext [AUTO-TRANSLATED:d5d9af4f] + // RTP has ext if (!ext) { return 0; } auto ext_ptr = &payload + getCsrcSize(); // uint16_t reserved = AV_RB16(ext_ptr); - // 每个ext占用4字节 + // 每个ext占用4字节 [AUTO-TRANSLATED:93e9b453] + // Each ext occupies 4 bytes return AV_RB16(ext_ptr + 2) << 2; } uint16_t RtpHeader::getExtReserved() const { - // rtp有ext + // rtp有ext [AUTO-TRANSLATED:d5d9af4f] + // RTP has ext if (!ext) { return 0; } @@ -554,12 +573,14 @@ uint8_t *RtpHeader::getExtData() { return nullptr; } auto ext_ptr = &payload + getCsrcSize(); - // 多出的4个字节分别为reserved、ext_len + // 多出的4个字节分别为reserved、ext_len [AUTO-TRANSLATED:070138f4] + // The extra 4 bytes are reserved, ext_len return ext_ptr + 4; } size_t RtpHeader::getPayloadOffset() const { - // 有ext时,还需要忽略reserved、ext_len 4个字节 + // 有ext时,还需要忽略reserved、ext_len 4个字节 [AUTO-TRANSLATED:3e222997] + // When there is ext, you also need to ignore the reserved, ext_len 4 bytes return getCsrcSize() + (ext ? (4 + getExtSize()) : 0); } @@ -600,7 +621,8 @@ string RtpHeader::dumpString(size_t rtp_size) const { /////////////////////////////////////////////////////////////////////// RtpHeader *RtpPacket::getHeader() { - // 需除去rtcp over tcp 4个字节长度 + // 需除去rtcp over tcp 4个字节长度 [AUTO-TRANSLATED:936f6f5b] + // Need to remove the rtcp over tcp 4 byte length return (RtpHeader *)(data() + RtpPacket::kRtpTcpHeaderSize); } @@ -633,7 +655,8 @@ uint8_t *RtpPacket::getPayload() { } size_t RtpPacket::getPayloadSize() const { - // 需除去rtcp over tcp 4个字节长度 + // 需除去rtcp over tcp 4个字节长度 [AUTO-TRANSLATED:936f6f5b] + // Need to remove the rtcp over tcp 4 byte length return getHeader()->getPayloadSize(size() - kRtpTcpHeaderSize); } @@ -656,6 +679,12 @@ RtpPacket::Ptr RtpPacket::create() { * @param dur_sec rtsp点播时长,0代表直播,单位秒 * @param header 自定义sdp描述 * @param version sdp版本 + * Construct title type sdp + * @param dur_sec rtsp on-demand duration, 0 represents live broadcast, unit is seconds + * @param header Custom sdp description + * @param version sdp version + + * [AUTO-TRANSLATED:a548fc69] */ TitleSdp::TitleSdp(float dur_sec, const std::map &header, int version) @@ -674,10 +703,12 @@ TitleSdp::TitleSdp(float dur_sec, const std::map &head } if (dur_sec <= 0) { - // 直播 + // 直播 [AUTO-TRANSLATED:079c0cbc] + // Live broadcast _printer << "a=range:npt=now-\r\n"; } else { - // 点播 + // 点播 [AUTO-TRANSLATED:f0b0f74a] + // On-demand _dur_sec = dur_sec; _printer << "a=range:npt=0-" << dur_sec << "\r\n"; } diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index 87520d27..a46e4927 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -70,100 +70,130 @@ typedef enum { class RtpHeader { public: #if __BYTE_ORDER == __BIG_ENDIAN - // 版本号,固定为2 + // 版本号,固定为2 [AUTO-TRANSLATED:08ed82fa] + // Version number, fixed to 2 uint32_t version : 2; // padding uint32_t padding : 1; - // 扩展 + // 扩展 [AUTO-TRANSLATED:6189584a] + // Extension uint32_t ext : 1; // csrc uint32_t csrc : 4; // mark uint32_t mark : 1; - // 负载类型 + // 负载类型 [AUTO-TRANSLATED:09b49a77] + // Payload type uint32_t pt : 7; #else // csrc uint32_t csrc : 4; - // 扩展 + // 扩展 [AUTO-TRANSLATED:6189584a] + // Extension uint32_t ext : 1; // padding uint32_t padding : 1; - // 版本号,固定为2 + // 版本号,固定为2 [AUTO-TRANSLATED:08ed82fa] + // Version number, fixed to 2 uint32_t version : 2; - // 负载类型 + // 负载类型 [AUTO-TRANSLATED:09b49a77] + // Payload type uint32_t pt : 7; // mark uint32_t mark : 1; #endif - // 序列号 + // 序列号 [AUTO-TRANSLATED:fe421425] + // Sequence number uint32_t seq : 16; - // 时间戳 + // 时间戳 [AUTO-TRANSLATED:516f43a9] + // Timestamp uint32_t stamp; // ssrc uint32_t ssrc; - // 负载,如果有csrc和ext,前面为 4 * csrc + (4 + 4 * ext_len) + // 负载,如果有csrc和ext,前面为 4 * csrc + (4 + 4 * ext_len) [AUTO-TRANSLATED:fcd87b19] + // Payload, if csrc and ext exist, the front is 4 * csrc + (4 + 4 * ext_len) uint8_t payload; public: - // 返回csrc字段字节长度 + // 返回csrc字段字节长度 [AUTO-TRANSLATED:2203e1db] + // Return the byte length of the csrc field size_t getCsrcSize() const; - // 返回csrc字段首地址,不存在时返回nullptr + // 返回csrc字段首地址,不存在时返回nullptr [AUTO-TRANSLATED:2c89718a] + // Return the starting address of the csrc field, return nullptr if it does not exist uint8_t *getCsrcData(); - // 返回ext字段字节长度 + // 返回ext字段字节长度 [AUTO-TRANSLATED:487dcc4e] + // Return the byte length of the ext field size_t getExtSize() const; - // 返回ext reserved值 + // 返回ext reserved值 [AUTO-TRANSLATED:131355c0] + // Return the ext reserved value uint16_t getExtReserved() const; - // 返回ext段首地址,不存在时返回nullptr + // 返回ext段首地址,不存在时返回nullptr [AUTO-TRANSLATED:6662b24f] + // Return the starting address of the ext segment, return nullptr if it does not exist uint8_t *getExtData(); - // 返回有效负载指针,跳过csrc、ext + // 返回有效负载指针,跳过csrc、ext [AUTO-TRANSLATED:139a5683] + // Return the valid payload pointer, skip csrc, ext uint8_t *getPayloadData(); - // 返回有效负载总长度,不包括csrc、ext、padding + // 返回有效负载总长度,不包括csrc、ext、padding [AUTO-TRANSLATED:b4ec90d4] + // Return the total length of the valid payload, excluding csrc, ext, padding ssize_t getPayloadSize(size_t rtp_size) const; - // 打印调试信息 + // 打印调试信息 [AUTO-TRANSLATED:f36e06da] + // Print debug information std::string dumpString(size_t rtp_size) const; private: - // 返回有效负载偏移量 + // 返回有效负载偏移量 [AUTO-TRANSLATED:47496d37] + // Return the valid payload offset size_t getPayloadOffset() const; - // 返回padding长度 + // 返回padding长度 [AUTO-TRANSLATED:6dabd44d] + // Return the padding length size_t getPaddingSize(size_t rtp_size) const; }; #pragma pack(pop) -// 此rtp为rtp over tcp形式,需要忽略前4个字节 +// 此rtp为rtp over tcp形式,需要忽略前4个字节 [AUTO-TRANSLATED:ceb00f83] +// This rtp is in the form of rtp over tcp, the first 4 bytes need to be ignored class RtpPacket : public toolkit::BufferRaw { public: using Ptr = std::shared_ptr; enum { kRtpVersion = 2, kRtpHeaderSize = 12, kRtpTcpHeaderSize = 4 }; - // 获取rtp头 + // 获取rtp头 [AUTO-TRANSLATED:41d58919] + // Get the rtp header RtpHeader *getHeader(); const RtpHeader *getHeader() const; - // 打印调试信息 + // 打印调试信息 [AUTO-TRANSLATED:f36e06da] + // Print debug information std::string dumpString() const; - // 主机字节序的seq + // 主机字节序的seq [AUTO-TRANSLATED:67c278dd] + // Host byte order seq uint16_t getSeq() const; uint32_t getStamp() const; - // 主机字节序的时间戳,已经转换为毫秒 + // 主机字节序的时间戳,已经转换为毫秒 [AUTO-TRANSLATED:14cdf080] + // Host byte order timestamp, converted to milliseconds uint64_t getStampMS(bool ntp = true) const; - // 主机字节序的ssrc + // 主机字节序的ssrc [AUTO-TRANSLATED:37d06f6c] + // Host byte order ssrc uint32_t getSSRC() const; - // 有效负载,跳过csrc、ext + // 有效负载,跳过csrc、ext [AUTO-TRANSLATED:e4e97453] + // Valid payload, skip csrc, ext uint8_t *getPayload(); - // 有效负载长度,不包括csrc、ext、padding + // 有效负载长度,不包括csrc、ext、padding [AUTO-TRANSLATED:a93e4b08] + // Valid payload length, excluding csrc, ext, padding size_t getPayloadSize() const; - // 音视频类型 + // 音视频类型 [AUTO-TRANSLATED:dc0fa851] + // Audio and video type TrackType type; - // 音频为采样率,视频一般为90000 + // 音频为采样率,视频一般为90000 [AUTO-TRANSLATED:8bd1854b] + // Audio is the sampling rate, video is generally 90000 uint32_t sample_rate; - // ntp时间戳 + // ntp时间戳 [AUTO-TRANSLATED:912cacf2] + // ntp timestamp uint64_t ntp_stamp; int track_index; @@ -175,7 +205,8 @@ private: RtpPacket() = default; private: - // 对象个数统计 + // 对象个数统计 [AUTO-TRANSLATED:f4a012d0] + // Object Count Statistics toolkit::ObjectStatistic _statistic; }; @@ -224,7 +255,8 @@ public: uint8_t _interleaved = 0; uint16_t _seq = 0; uint32_t _ssrc = 0; - // 时间戳,单位毫秒 + // 时间戳,单位毫秒 [AUTO-TRANSLATED:9513087f] + // Timestamp, unit: milliseconds uint32_t _time_stamp = 0; }; @@ -248,6 +280,9 @@ private: /** * rtsp sdp基类 + * rtsp sdp base class + + * [AUTO-TRANSLATED:05395f48] */ class Sdp { public: @@ -257,6 +292,11 @@ public: * 构造sdp * @param sample_rate 采样率 * @param payload_type pt类型 + * Construct sdp + * @param sample_rate Sampling rate + * @param payload_type pt type + + * [AUTO-TRANSLATED:49f61fff] */ Sdp(uint32_t sample_rate, uint8_t payload_type) { _sample_rate = sample_rate; @@ -268,18 +308,30 @@ public: /** * 获取sdp字符串 * @return + * Get sdp string + * @return + + * [AUTO-TRANSLATED:ca0e2ad7] */ virtual std::string getSdp() const = 0; /** * 获取pt * @return + * Get pt + * @return + + * [AUTO-TRANSLATED:872f1b6a] */ uint8_t getPayloadType() const { return _payload_type; } /** * 获取采样率 * @return + * Get sampling rate + * @return + + * [AUTO-TRANSLATED:463a0075] */ uint32_t getSampleRate() const { return _sample_rate; } @@ -290,6 +342,9 @@ private: /** * sdp中除音视频外的其他描述部分 + * Other description part in sdp except audio and video + + * [AUTO-TRANSLATED:3a1dad65] */ class TitleSdp : public Sdp { public: @@ -299,6 +354,12 @@ public: * @param dur_sec rtsp点播时长,0代表直播,单位秒 * @param header 自定义sdp描述 * @param version sdp版本 + * Construct title type sdp + * @param dur_sec rtsp on-demand duration, 0 represents live broadcast, unit: seconds + * @param header Custom sdp description + * @param version sdp version + + * [AUTO-TRANSLATED:bcd1dd5a] */ TitleSdp(float dur_sec = 0, const std::map &header = std::map(), int version = 0); @@ -311,11 +372,14 @@ private: toolkit::_StrPrinter _printer; }; -// 创建rtp over tcp4个字节的头 +// 创建rtp over tcp4个字节的头 [AUTO-TRANSLATED:25031f0d] +// Create 4-byte header for rtp over tcp toolkit::Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved); -// 创建rtp-rtcp端口对 +// 创建rtp-rtcp端口对 [AUTO-TRANSLATED:f8abe61b] +// Create rtp-rtcp port pair void makeSockPair(std::pair &pair, const std::string &local_ip, bool re_use_port = false, bool is_udp = true); -// 十六进制方式打印ssrc +// 十六进制方式打印ssrc [AUTO-TRANSLATED:c58cd95f] +// Print ssrc in hexadecimal format std::string printSSRC(uint32_t ui32Ssrc); bool getSSRC(const char *data, size_t data_len, uint32_t &ssrc); diff --git a/src/Rtsp/RtspDemuxer.cpp b/src/Rtsp/RtspDemuxer.cpp index 1dca605b..b43a446f 100644 --- a/src/Rtsp/RtspDemuxer.cpp +++ b/src/Rtsp/RtspDemuxer.cpp @@ -39,7 +39,8 @@ void RtspDemuxer::loadSdp(const SdpParser &attr) { break; } } - //rtsp能通过sdp立即知道有多少个track + // rtsp能通过sdp立即知道有多少个track [AUTO-TRANSLATED:66a4c8d3] + // rtsp can immediately know how many tracks there are through sdp addTrackCompleted(); auto titleTrack = attr.getTrack(TrackTitle); @@ -85,20 +86,24 @@ void RtspDemuxer::makeAudioTrack(const SdpTrack::Ptr &audio) { if (_audio_rtp_decoder) { return; } - //生成Track对象 + // 生成Track对象 [AUTO-TRANSLATED:c2f2ac3b] + // Generate Track object _audio_track = dynamic_pointer_cast(Factory::getTrackBySdp(audio)); if (!_audio_track) { return; } setBitRate(audio, _audio_track); - //生成RtpCodec对象以便解码rtp + // 生成RtpCodec对象以便解码rtp [AUTO-TRANSLATED:889376fd] + // Generate RtpCodec object to decode rtp _audio_rtp_decoder = Factory::getRtpDecoderByCodecId(_audio_track->getCodecId()); if (!_audio_rtp_decoder) { - //找不到相应的rtp解码器,该track无效 + // 找不到相应的rtp解码器,该track无效 [AUTO-TRANSLATED:1c8c5eab] + // Cannot find the corresponding rtp decoder, the track is invalid _audio_track.reset(); return; } - //设置rtp解码器代理,生成的frame写入该Track + // 设置rtp解码器代理,生成的frame写入该Track [AUTO-TRANSLATED:b2a2362e] + // Set the rtp decoder proxy, the generated frame is written to this Track _audio_rtp_decoder->addDelegate(_audio_track); addTrack(_audio_track); } @@ -107,20 +112,24 @@ void RtspDemuxer::makeVideoTrack(const SdpTrack::Ptr &video) { if (_video_rtp_decoder) { return; } - //生成Track对象 + // 生成Track对象 [AUTO-TRANSLATED:c2f2ac3b] + // Generate Track object _video_track = dynamic_pointer_cast(Factory::getTrackBySdp(video)); if (!_video_track) { return; } setBitRate(video, _video_track); - //生成RtpCodec对象以便解码rtp + // 生成RtpCodec对象以便解码rtp [AUTO-TRANSLATED:889376fd] + // Generate RtpCodec object to decode rtp _video_rtp_decoder = Factory::getRtpDecoderByCodecId(_video_track->getCodecId()); if (!_video_rtp_decoder) { - //找不到相应的rtp解码器,该track无效 + // 找不到相应的rtp解码器,该track无效 [AUTO-TRANSLATED:1c8c5eab] + // Cannot find the corresponding rtp decoder, the track is invalid _video_track.reset(); return; } - //设置rtp解码器代理,生成的frame写入该Track + // 设置rtp解码器代理,生成的frame写入该Track [AUTO-TRANSLATED:b2a2362e] + // Set the rtp decoder proxy, the generated frame is written to this Track _video_rtp_decoder->addDelegate(_video_track); addTrack(_video_track); } diff --git a/src/Rtsp/RtspDemuxer.h b/src/Rtsp/RtspDemuxer.h index 636b6986..13795967 100644 --- a/src/Rtsp/RtspDemuxer.h +++ b/src/Rtsp/RtspDemuxer.h @@ -23,6 +23,9 @@ public: /** * 加载sdp + * Load sdp + + * [AUTO-TRANSLATED:235be34f] */ void loadSdp(const std::string &sdp); @@ -30,12 +33,22 @@ public: * 开始解复用 * @param rtp rtp包 * @return true 代表是i帧第一个rtp包 + * Start demultiplexing + * @param rtp rtp packet + * @return true represents the first rtp packet of the i-frame + + * [AUTO-TRANSLATED:116d3186] */ bool inputRtp(const RtpPacket::Ptr &rtp); /** * 获取节目总时长 * @return 节目总时长,单位秒 + * Get the total duration of the program + * @return Total duration of the program, in seconds + + + * [AUTO-TRANSLATED:6b2ec56c] */ float getDuration() const; diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index fd65b322..ae4c6022 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -28,6 +28,12 @@ namespace mediakit { * rtsp有关键的两要素,分别是sdp、rtp包 * 只要生成了这两要素,那么要实现rtsp推流、rtsp服务器就很简单了 * rtsp推拉流协议中,先传递sdp,然后再协商传输方式(tcp/udp/组播),最后一直传递rtp + * Data abstraction of rtsp media source + * Rtsp has two key elements, sdp and rtp packets + * As long as these two elements are generated, it is very simple to implement rtsp push stream and rtsp server + * In the rtsp push and pull stream protocol, sdp is transmitted first, then the transmission method (tcp/udp/multicast) is negotiated, and finally rtp is continuously transmitted + + * [AUTO-TRANSLATED:e04eee56] */ class RtspMediaSource : public MediaSource, public toolkit::RingDelegate, private PacketCache { public: @@ -41,6 +47,13 @@ public: * @param app 应用名 * @param stream_id 流id * @param ring_size 可以设置固定的环形缓冲大小,0则自适应 + * Constructor + * @param vhost Virtual host name + * @param app Application name + * @param stream_id Stream id + * @param ring_size You can set a fixed ring buffer size, 0 is adaptive + + * [AUTO-TRANSLATED:5dd23423] */ RtspMediaSource(const MediaTuple& tuple, const std::string &schema = RTSP_SCHEMA, int ring_size = RTP_GOP_SIZE): MediaSource(schema, tuple), _ring_size(ring_size) {} @@ -54,6 +67,9 @@ public: /** * 获取媒体源的环形缓冲 + * Get the ring buffer of the media source + + * [AUTO-TRANSLATED:91a762bc] */ const RingType::Ptr &getRing() const { return _ring; @@ -73,6 +89,9 @@ public: /** * 获取播放器个数 + * Get the number of players + + * [AUTO-TRANSLATED:a451c846] */ int readerCount() override { return _ring ? _ring->readerCount() : 0; @@ -80,6 +99,9 @@ public: /** * 获取该源的sdp + * Get the sdp of this source + + * [AUTO-TRANSLATED:ebc43430] */ const std::string &getSdp() const { return _sdp; @@ -91,6 +113,9 @@ public: /** * 获取相应轨道的ssrc + * Get the ssrc of the corresponding track + + * [AUTO-TRANSLATED:d26d7f76] */ virtual uint32_t getSsrc(TrackType trackType) { assert(trackType >= 0 && trackType < TrackMax); @@ -103,6 +128,9 @@ public: /** * 获取相应轨道的seqence + * Get the sequence of the corresponding track + + * [AUTO-TRANSLATED:24b0ee74] */ virtual uint16_t getSeqence(TrackType trackType) { assert(trackType >= 0 && trackType < TrackMax); @@ -115,16 +143,25 @@ public: /** * 获取相应轨道的时间戳,单位毫秒 + * Get the timestamp of the corresponding track, in milliseconds + + * [AUTO-TRANSLATED:564a0794] */ uint32_t getTimeStamp(TrackType trackType) override; /** * 更新时间戳 + * Update timestamp + + * [AUTO-TRANSLATED:8defe253] */ void setTimeStamp(uint32_t stamp) override; /** * 设置sdp + * Set sdp + + * [AUTO-TRANSLATED:76a533c4] */ virtual void setSdp(const std::string &sdp); @@ -132,6 +169,11 @@ public: * 输入rtp * @param rtp rtp包 * @param keyPos 该包是否为关键帧的第一个包 + * Input rtp + * @param rtp rtp packet + * @param keyPos Whether this packet is the first packet of a key frame + + * [AUTO-TRANSLATED:fe55afe8] */ void onWrite(RtpPacket::Ptr rtp, bool keyPos) override; @@ -145,9 +187,15 @@ private: * 批量flush rtp包时触发该函数 * @param rtp_list rtp包列表 * @param key_pos 是否包含关键帧 + * Trigger this function when flushing rtp packets in batches + * @param rtp_list rtp packet list + * @param key_pos Whether it contains a key frame + + * [AUTO-TRANSLATED:612c574b] */ void onFlush(std::shared_ptr > rtp_list, bool key_pos) override { - //如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存 + // 如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存 [AUTO-TRANSLATED:5818a8d8] + // If there is no video, then there is no point in having a GOP cache, so is_key is always true to ensure that the GOP cache is always cleared _ring->write(std::move(rtp_list), _have_video ? key_pos : true); } diff --git a/src/Rtsp/RtspMediaSourceImp.cpp b/src/Rtsp/RtspMediaSourceImp.cpp index bc8d6ca9..b8db32eb 100644 --- a/src/Rtsp/RtspMediaSourceImp.cpp +++ b/src/Rtsp/RtspMediaSourceImp.cpp @@ -16,14 +16,16 @@ void RtspMediaSource::setSdp(const std::string &sdp) { uint32_t RtspMediaSource::getTimeStamp(TrackType trackType) { assert(trackType >= TrackInvalid && trackType < TrackMax); if (trackType != TrackInvalid) { - //获取某track的时间戳 + // 获取某track的时间戳 [AUTO-TRANSLATED:e3fecb81] + // Get the timestamp of a track auto &track = _tracks[trackType]; if (track) { return track->_time_stamp; } } - //获取所有track的最小时间戳 + // 获取所有track的最小时间戳 [AUTO-TRANSLATED:8cf9218b] + // Get the minimum timestamp of all tracks uint32_t ret = UINT32_MAX; for (auto &track : _tracks) { if (track && track->_time_stamp < ret) { @@ -35,6 +37,9 @@ uint32_t RtspMediaSource::getTimeStamp(TrackType trackType) { /** * 更新时间戳 + * Update timestamp + + * [AUTO-TRANSLATED:fa02a89e] */ void RtspMediaSource::setTimeStamp(uint32_t stamp) { for (auto &track : _tracks) { @@ -63,8 +68,10 @@ void RtspMediaSource::onWrite(RtpPacket::Ptr rtp, bool keyPos) { } strongSelf->onReaderChanged(size); }; - //GOP默认缓冲512组RTP包,每组RTP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTP包), - //每次遇到关键帧第一个RTP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开) + // GOP默认缓冲512组RTP包,每组RTP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTP包), [AUTO-TRANSLATED:dc09b92e] + // GOP defaults to buffering 512 groups of RTP packets, each group of RTP packets has the same timestamp (if merge writing is enabled, then each group is the RTP packet within the merge writing time), + // 每次遇到关键帧第一个RTP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开) [AUTO-TRANSLATED:db44dc72] + // Every time a key frame's first RTP packet is encountered, the GOP cache will be cleared (because there is a new key frame, which can also achieve instant playback) _ring = std::make_shared(_ring_size, std::move(lam)); if (!_sdp.empty()) { regist(); @@ -92,16 +99,20 @@ void RtspMediaSourceImp::setSdp(const std::string &strSdp) void RtspMediaSourceImp::onWrite(RtpPacket::Ptr rtp, bool key_pos) { if (_all_track_ready && !_muxer->isEnabled()) { - //获取到所有Track后,并且未开启转协议,那么不需要解复用rtp - //在关闭rtp解复用后,无法知道是否为关键帧,这样会导致无法秒开,或者开播花屏 + // 获取到所有Track后,并且未开启转协议,那么不需要解复用rtp [AUTO-TRANSLATED:31cbc558] + // After getting all Tracks and not enabling protocol conversion, there is no need to demultiplex rtp + // 在关闭rtp解复用后,无法知道是否为关键帧,这样会导致无法秒开,或者开播花屏 [AUTO-TRANSLATED:279f1332] + // After closing rtp demultiplexing, it is impossible to know whether it is a key frame, which will lead to the inability to achieve instant playback or the screen will be garbled when playing key_pos = rtp->type == TrackVideo; } else { - //需要解复用rtp + // 需要解复用rtp [AUTO-TRANSLATED:0deaf9f1] + // Need to demultiplex rtp key_pos = _demuxer->inputRtp(rtp); } GET_CONFIG(bool, directProxy, Rtsp::kDirectProxy); if (directProxy) { - //直接代理模式才直接使用原始rtp + // 直接代理模式才直接使用原始rtp [AUTO-TRANSLATED:afd4ae3b] + // Only the direct proxy mode directly uses the original rtp RtspMediaSource::onWrite(std::move(rtp), key_pos); } } @@ -109,8 +120,10 @@ void RtspMediaSourceImp::onWrite(RtpPacket::Ptr rtp, bool key_pos) void RtspMediaSourceImp::setProtocolOption(const ProtocolOption &option) { GET_CONFIG(bool, direct_proxy, Rtsp::kDirectProxy); - //开启直接代理模式时,rtsp直接代理,不重复产生;但是有些rtsp推流端,由于sdp中已有sps pps,rtp中就不再包括sps pps, - //导致rtc无法播放,所以在rtsp推流rtc播放时,建议关闭直接代理模式 + // 开启直接代理模式时,rtsp直接代理,不重复产生;但是有些rtsp推流端,由于sdp中已有sps pps,rtp中就不再包括sps pps, [AUTO-TRANSLATED:1bbc0e31] + // When direct proxy mode is enabled, rtsp is directly proxied and not duplicated; however, some rtsp push stream ends, because there are already sps pps in the sdp, rtp no longer includes sps pps, + // 导致rtc无法播放,所以在rtsp推流rtc播放时,建议关闭直接代理模式 [AUTO-TRANSLATED:2c705dec] + // This leads to the inability of rtc to play, so it is recommended to turn off direct proxy mode when rtsp pushes the stream and rtc plays _option = option; if (direct_proxy) { if (getSchema() == RTC_SCHEMA) @@ -121,7 +134,8 @@ void RtspMediaSourceImp::setProtocolOption(const ProtocolOption &option) _muxer = std::make_shared(_tuple, _demuxer->getDuration(), _option); _muxer->setMediaListener(getListener()); _muxer->setTrackListener(std::static_pointer_cast(shared_from_this())); - //让_muxer对象拦截一部分事件(比如说录像相关事件) + // 让_muxer对象拦截一部分事件(比如说录像相关事件) [AUTO-TRANSLATED:32320477] + // Let the _muxer object intercept some events (such as recording related events) MediaSource::setListener(_muxer); for (auto &track : _demuxer->getTracks(false)) { diff --git a/src/Rtsp/RtspMediaSourceImp.h b/src/Rtsp/RtspMediaSourceImp.h index fe2bad29..f4610975 100644 --- a/src/Rtsp/RtspMediaSourceImp.h +++ b/src/Rtsp/RtspMediaSourceImp.h @@ -26,21 +26,37 @@ public: * @param app 应用名 * @param id 流id * @param ringSize 环形缓存大小 + * Constructor + * @param vhost Virtual host + * @param app Application name + * @param id Stream id + * @param ringSize Ring buffer size + + * [AUTO-TRANSLATED:7679d212] */ RtspMediaSourceImp(const MediaTuple& tuple, const std::string &schema = RTSP_SCHEMA, int ringSize = RTP_GOP_SIZE); /** * 设置sdp + * Set sdp + + * [AUTO-TRANSLATED:76a533c4] */ void setSdp(const std::string &strSdp) override; /** * 输入rtp并解析 + * Input rtp and parse + + * [AUTO-TRANSLATED:778f743f] */ void onWrite(RtpPacket::Ptr rtp, bool key_pos) override; /** * 获取观看总人数,包括(hls/rtsp/rtmp) + * Get total number of viewers, including (hls/rtsp/rtmp) + + * [AUTO-TRANSLATED:19a26d5a] */ int totalReaderCount() override { return readerCount() + (_muxer ? _muxer->totalReaderCount() : 0); @@ -48,6 +64,9 @@ public: /** * 设置协议转换选项 + * Set protocol conversion options + + * [AUTO-TRANSLATED:a6a9b24a] */ void setProtocolOption(const ProtocolOption &option); @@ -57,11 +76,17 @@ public: /** * _demuxer触发的添加Track事件 + * _demuxer triggered add Track event + + * [AUTO-TRANSLATED:80dbcf16] */ bool addTrack(const Track::Ptr &track) override; /** * _demuxer触发的Track添加完毕事件 + * _demuxer triggered Track add complete event + + * [AUTO-TRANSLATED:939cb312] */ void addTrackCompleted() override; @@ -69,6 +94,9 @@ public: /** * _muxer触发的所有Track就绪的事件 + * _muxer triggered all Track ready event + + * [AUTO-TRANSLATED:1d34b7e0] */ void onAllTrackReady() override{ _all_track_ready = true; @@ -77,6 +105,10 @@ public: /** * 设置事件监听器 * @param listener 监听器 + * Set event listener + * @param listener Listener + + * [AUTO-TRANSLATED:d829419b] */ void setListener(const std::weak_ptr &listener) override; diff --git a/src/Rtsp/RtspMediaSourceMuxer.h b/src/Rtsp/RtspMediaSourceMuxer.h index 1f1f2312..070dae68 100644 --- a/src/Rtsp/RtspMediaSourceMuxer.h +++ b/src/Rtsp/RtspMediaSourceMuxer.h @@ -78,7 +78,8 @@ public: } bool isEnabled() { - //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + // 缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 [AUTO-TRANSLATED:7cfd4d49] + // The inputFrame function is still allowed to be triggered when the cache has not been cleared, so that the cache can be cleared in time. return _on_demand ? (_clear_cache ? true : _enabled) : true; } diff --git a/src/Rtsp/RtspMuxer.cpp b/src/Rtsp/RtspMuxer.cpp index ea45c4fc..28563a4e 100644 --- a/src/Rtsp/RtspMuxer.cpp +++ b/src/Rtsp/RtspMuxer.cpp @@ -21,20 +21,24 @@ void RtspMuxer::onRtp(RtpPacket::Ptr in, bool is_key) { if (_live) { auto &ref = _tracks[in->track_index]; if (ref.rtp_stamp != in->getHeader()->stamp) { - // rtp时间戳变化才计算ntp,节省cpu资源 + // rtp时间戳变化才计算ntp,节省cpu资源 [AUTO-TRANSLATED:729d54f2] + // Only calculate NTP when the RTP timestamp changes, saving CPU resources int64_t stamp_ms = in->getStamp() * uint64_t(1000) / in->sample_rate; int64_t stamp_ms_inc; - // 求rtp时间戳增量 + // 求rtp时间戳增量 [AUTO-TRANSLATED:f6ba022f] + // Get the RTP timestamp increment ref.stamp.revise(stamp_ms, stamp_ms, stamp_ms_inc, stamp_ms_inc); ref.rtp_stamp = in->getHeader()->stamp; ref.ntp_stamp = stamp_ms_inc + _ntp_stamp_start; } - // rtp拦截入口,此处统一赋值ntp + // rtp拦截入口,此处统一赋值ntp [AUTO-TRANSLATED:1412435a] + // RTP interception entry, set NTP here uniformly in->ntp_stamp = ref.ntp_stamp; ref.rtp_seq = in->getSeq() + 1; } else { - // 点播情况下设置ntp时间戳为rtp时间戳加基准ntp时间戳 + // 点播情况下设置ntp时间戳为rtp时间戳加基准ntp时间戳 [AUTO-TRANSLATED:b9f77de4] + // In on-demand scenarios, set the NTP timestamp to the RTP timestamp plus the base NTP timestamp in->ntp_stamp = _ntp_stamp_start + (in->getStamp() * uint64_t(1000) / in->sample_rate); } _rtpRing->write(std::move(in), is_key); @@ -58,7 +62,8 @@ RtspMuxer::RtspMuxer(const TitleSdp::Ptr &title) { bool RtspMuxer::addTrack(const Track::Ptr &track) { if (_track_existed[track->getTrackType()]) { - // rtsp不支持多个同类型track + // rtsp不支持多个同类型track [AUTO-TRANSLATED:87262d86] + // RTSP does not support multiple tracks of the same type WarnL << "Already add a track kind of: " << track->getTrackTypeStr() << ", ignore track: " << track->getCodecName(); return false; } @@ -67,7 +72,8 @@ bool RtspMuxer::addTrack(const Track::Ptr &track) { auto &encoder = ref.encoder; CHECK(!encoder); - // payload type 96以后则为动态pt + // payload type 96以后则为动态pt [AUTO-TRANSLATED:812ac0a2] + // Payload type 96 and above is dynamic PT Sdp::Ptr sdp = track->getSdp(96 + _index); if (!sdp) { WarnL << "Unsupported codec: " << track->getCodecName(); @@ -79,21 +85,25 @@ bool RtspMuxer::addTrack(const Track::Ptr &track) { return false; } auto rtp = std::dynamic_pointer_cast(encoder); - // 标记已经存在该类型track + // 标记已经存在该类型track [AUTO-TRANSLATED:ed79ebb5] + // Mark that a track of this type already exists _track_existed[track->getTrackType()] = true; { static atomic s_ssrc(0); uint32_t ssrc = s_ssrc++; if (!ssrc) { - // ssrc不能为0 + // ssrc不能为0 [AUTO-TRANSLATED:312a1b47] + // SSRC cannot be 0 ssrc = s_ssrc++; } if (track->getTrackType() == TrackVideo) { - // 视频的ssrc是偶数,方便调试 + // 视频的ssrc是偶数,方便调试 [AUTO-TRANSLATED:c22cd03f] + // The video SSRC is even for debugging convenience ssrc = 2 * ssrc; } else { - // 音频ssrc是奇数 + // 音频ssrc是奇数 [AUTO-TRANSLATED:50688636] + // The audio SSRC is odd ssrc = 2 * ssrc + 1; } GET_CONFIG(uint32_t, audio_mtu, Rtp::kAudioMtuSize); @@ -103,7 +113,8 @@ bool RtspMuxer::addTrack(const Track::Ptr &track) { encoder->getRtpInfo().setSeq(ref.rtp_seq); } - // 设置rtp输出环形缓存 + // 设置rtp输出环形缓存 [AUTO-TRANSLATED:5ac7e24a] + // Set the RTP output circular buffer encoder->setRtpRing(_rtpInterceptor); auto str = sdp->getSdp(); @@ -111,11 +122,13 @@ bool RtspMuxer::addTrack(const Track::Ptr &track) { str += std::to_string(_index); str += "\r\n"; - // 添加其sdp + // 添加其sdp [AUTO-TRANSLATED:80958925] + // Add its SDP _sdp.append(str); trySyncTrack(); - // rtp的时间戳是pts,允许回退 + // rtp的时间戳是pts,允许回退 [AUTO-TRANSLATED:f4a977fc] + // The RTP timestamp is PTS, allowing rollback if (track->getTrackType() == TrackVideo) { ref.stamp.enableRollback(true); } diff --git a/src/Rtsp/RtspMuxer.h b/src/Rtsp/RtspMuxer.h index 3f640283..b0b8f891 100644 --- a/src/Rtsp/RtspMuxer.h +++ b/src/Rtsp/RtspMuxer.h @@ -36,6 +36,9 @@ private: /** * rtsp生成器 + * RTSP generator + + * [AUTO-TRANSLATED:2a72d801] */ class RtspMuxer : public MediaSinkInterface { public: @@ -43,39 +46,64 @@ public: /** * 构造函数 + * Constructor + + * [AUTO-TRANSLATED:41469869] */ RtspMuxer(const TitleSdp::Ptr &title = nullptr); /** * 获取完整的SDP字符串 * @return SDP字符串 + * Get the complete SDP string + * @return SDP string + + * [AUTO-TRANSLATED:f5d1b0a6] */ std::string getSdp(); /** * 获取rtp环形缓存 * @return + * Get the RTP ring buffer + * @return + + * [AUTO-TRANSLATED:644e8634] */ RtpRing::RingType::Ptr getRtpRing() const; /** * 添加ready状态的track + * Add a ready state track + + * [AUTO-TRANSLATED:2d8138b3] */ bool addTrack(const Track::Ptr & track) override; /** * 写入帧数据 * @param frame 帧 + * Write frame data + * @param frame Frame + + * [AUTO-TRANSLATED:b7c92013] */ bool inputFrame(const Frame::Ptr &frame) override; /** * 刷新输出所有frame缓存 + * Flush all frame buffers + + * [AUTO-TRANSLATED:adaea568] */ void flush() override; /** * 重置所有track + * Reset all tracks + + + * [AUTO-TRANSLATED:f203fa3e] */ void resetTracks() override; diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index f442cbc5..6a2cea5c 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -121,7 +121,8 @@ void RtspPlayer::onConnect(const SockException &err) { void RtspPlayer::onRecv(const Buffer::Ptr &buf) { if (_benchmark_mode && !_play_check_timer) { - // 在性能测试模式下,如果rtsp握手完毕后,不再解析rtp包 + // 在性能测试模式下,如果rtsp握手完毕后,不再解析rtp包 [AUTO-TRANSLATED:747b5399] + // In performance test mode, if the RTSP handshake is complete, no RTP packets will be parsed _rtp_recv_ticker.resetTime(); return; } @@ -134,14 +135,16 @@ void RtspPlayer::onRecv(const Buffer::Ptr &buf) { } void RtspPlayer::onError(const SockException &ex) { - // 定时器_pPlayTimer为空后表明握手结束了 + // 定时器_pPlayTimer为空后表明握手结束了 [AUTO-TRANSLATED:06a369c2] + // After the timer _pPlayTimer is empty, it indicates that the handshake is complete onPlayResult_l(ex, !_play_check_timer); } // from live555 bool RtspPlayer::handleAuthenticationFailure(const string ¶msStr) { if (!_realm.empty()) { - // 已经认证过了 + // 已经认证过了 [AUTO-TRANSLATED:f2db5f9c] + // It has been authenticated return false; } @@ -173,7 +176,8 @@ bool RtspPlayer::handleAuthenticationFailure(const string ¶msStr) { bool RtspPlayer::handleResponse(const string &cmd, const Parser &parser) { string authInfo = parser["WWW-Authenticate"]; - // 发送DESCRIBE命令后的回复 + // 发送DESCRIBE命令后的回复 [AUTO-TRANSLATED:39629cf0] + // The response after sending the DESCRIBE command if ((parser.status() == "401") && handleAuthenticationFailure(authInfo)) { sendOptions(); return false; @@ -204,7 +208,8 @@ void RtspPlayer::handleResDESCRIBE(const Parser &parser) { _content_base.pop_back(); } - // 解析sdp + // 解析sdp [AUTO-TRANSLATED:ed3f07fe] + // Parse SDP SdpParser sdpParser(parser.content()); _control_url = sdpParser.getControlUrl(_content_base); @@ -237,7 +242,8 @@ void RtspPlayer::handleResDESCRIBE(const Parser &parser) { sendSetup(0); } -// 有必要的情况下创建udp端口 +// 有必要的情况下创建udp端口 [AUTO-TRANSLATED:651202fc] +// Create UDP port if necessary void RtspPlayer::createUdpSockIfNecessary(int track_idx) { auto &rtpSockRef = _rtp_sock[track_idx]; auto &rtcpSockRef = _rtcp_sock[track_idx]; @@ -249,7 +255,8 @@ void RtspPlayer::createUdpSockIfNecessary(int track_idx) { } } -// 发送SETUP命令 +// 发送SETUP命令 [AUTO-TRANSLATED:68a7ca33] +// Send SETUP command void RtspPlayer::sendSetup(unsigned int track_idx) { _on_response = std::bind(&RtspPlayer::handleResSETUP, this, placeholders::_1, track_idx); auto &track = _sdp_track[track_idx]; @@ -314,10 +321,12 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) { auto &pRtcpSockRef = _rtcp_sock[track_idx]; if (_rtp_type == Rtsp::RTP_MULTICAST) { - // udp组播 + // udp组播 [AUTO-TRANSLATED:ccc90d1f] + // UDP multicast auto multiAddr = transport_map["destination"]; pRtpSockRef = createSocket(); - // 目前组播仅支持ipv4 + // 目前组播仅支持ipv4 [AUTO-TRANSLATED:8215bfd2] + // Currently, multicast only supports IPv4 if (!pRtpSockRef->bindUdpSock(rtp_port, "0.0.0.0")) { pRtpSockRef.reset(); throw std::runtime_error("open udp sock err"); @@ -327,33 +336,41 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) { SockUtil::joinMultiAddr(fd, multiAddr.data(), get_local_ip().data()); } - // 设置rtcp发送端口 + // 设置rtcp发送端口 [AUTO-TRANSLATED:f39b07bd] + // Set RTCP send port pRtcpSockRef = createSocket(); - // 目前组播仅支持ipv4 + // 目前组播仅支持ipv4 [AUTO-TRANSLATED:8215bfd2] + // Currently, multicast only supports IPv4 if (!pRtcpSockRef->bindUdpSock(0, "0.0.0.0")) { - // 分配端口失败 + // 分配端口失败 [AUTO-TRANSLATED:59ecd25d] + // Port allocation failed throw runtime_error("open udp socket failed"); } - // 设置发送地址和发送端口 + // 设置发送地址和发送端口 [AUTO-TRANSLATED:67e1cb6e] + // Set send address and send port auto dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtcp_port); pRtcpSockRef->bindPeerAddr((struct sockaddr *)&(dst)); } else { createUdpSockIfNecessary(track_idx); - // udp单播 + // udp单播 [AUTO-TRANSLATED:7d16a875] + // UDP unicast auto dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtp_port); pRtpSockRef->bindPeerAddr((struct sockaddr *)&(dst)); - // 发送rtp打洞包 + // 发送rtp打洞包 [AUTO-TRANSLATED:9a79d94f] + // Send RTP hole punching packet pRtpSockRef->send("\xce\xfa\xed\xfe", 4); dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtcp_port); - // 设置rtcp发送目标,为后续发送rtcp做准备 + // 设置rtcp发送目标,为后续发送rtcp做准备 [AUTO-TRANSLATED:70929b8e] + // Set RTCP send target, prepare for subsequent RTCP sending pRtcpSockRef->bindPeerAddr((struct sockaddr *)&(dst)); } auto peer_ip = get_peer_ip(); weak_ptr weakSelf = static_pointer_cast(shared_from_this()); - // 设置rtp over udp接收回调处理函数 + // 设置rtp over udp接收回调处理函数 [AUTO-TRANSLATED:6e74b593] + // Set RTP over UDP receive callback handler pRtpSockRef->setOnRead([peer_ip, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { @@ -368,7 +385,8 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) { }); if (pRtcpSockRef) { - // 设置rtcp over udp接收回调处理函数 + // 设置rtcp over udp接收回调处理函数 [AUTO-TRANSLATED:eed55b8e] + // Set RTCP over UDP receive callback handler pRtcpSockRef->setOnRead([peer_ip, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { @@ -384,12 +402,15 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) { } if (track_idx < _sdp_track.size() - 1) { - // 需要继续发送SETUP命令 + // 需要继续发送SETUP命令 [AUTO-TRANSLATED:d7ea1a7a] + // Need to continue sending SETUP command sendSetup(track_idx + 1); return; } - // 所有setup命令发送完毕 - // 发送play命令 + // 所有setup命令发送完毕 [AUTO-TRANSLATED:be499080] + // All SETUP commands have been sent + // 发送play命令 [AUTO-TRANSLATED:47a826d1] + // Send PLAY command if (_speed==0.0f) { sendPause(type_play, 0); } else { @@ -399,7 +420,8 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) { } void RtspPlayer::sendDescribe() { - // 发送DESCRIBE命令后处理函数:handleResDESCRIBE + // 发送DESCRIBE命令后处理函数:handleResDESCRIBE [AUTO-TRANSLATED:3c2b0ffe] + // Handle the response to the DESCRIBE command: handleResDESCRIBE _on_response = std::bind(&RtspPlayer::handleResDESCRIBE, this, placeholders::_1); sendRtspRequest("DESCRIBE", _play_url, { "Accept", "application/sdp" }); } @@ -409,14 +431,16 @@ void RtspPlayer::sendOptions() { if (!handleResponse("OPTIONS", parser)) { return; } - // 获取服务器支持的命令 + // 获取服务器支持的命令 [AUTO-TRANSLATED:8a6a12f1] + // Get the commands supported by the server _supported_cmd.clear(); auto public_val = split(parser["Public"], ","); for (auto &cmd : public_val) { trim(cmd); _supported_cmd.emplace(cmd); } - // 发送Describe请求,获取sdp + // 发送Describe请求,获取sdp [AUTO-TRANSLATED:f2e291d1] + // Send Describe request to get SDP sendDescribe(); }; sendRtspRequest("OPTIONS", _play_url); @@ -425,17 +449,20 @@ void RtspPlayer::sendOptions() { void RtspPlayer::sendKeepAlive() { _on_response = [](const Parser &parser) {}; if (_supported_cmd.find("GET_PARAMETER") != _supported_cmd.end()) { - // 支持GET_PARAMETER,用此命令保活 + // 支持GET_PARAMETER,用此命令保活 [AUTO-TRANSLATED:b45cd737] + // Support GET_PARAMETER, use this command to keep alive sendRtspRequest("GET_PARAMETER", _control_url); } else { - // 不支持GET_PARAMETER,用OPTIONS命令保活 + // 不支持GET_PARAMETER,用OPTIONS命令保活 [AUTO-TRANSLATED:3391350c] + // Do not support GET_PARAMETER, use OPTIONS command to keep alive sendRtspRequest("OPTIONS", _play_url); } } void RtspPlayer::sendPause(int type, uint32_t seekMS) { _on_response = std::bind(&RtspPlayer::handleResPAUSE, this, placeholders::_1, type); - // 开启或暂停rtsp + // 开启或暂停rtsp [AUTO-TRANSLATED:8ba5b594] + // Start or pause RTSP switch (type) { case type_pause: sendRtspRequest("PAUSE", _control_url, {}); break; case type_play: @@ -476,14 +503,17 @@ void RtspPlayer::handleResPAUSE(const Parser &parser, int type) { } if (type == type_pause) { - // 暂停成功! + // 暂停成功! [AUTO-TRANSLATED:782cea77] + // Pause successfully! _rtp_check_timer.reset(); return; } - // play或seek成功 + // play或seek成功 [AUTO-TRANSLATED:ba7b0da3] + // Play or seek successfully uint32_t iSeekTo = 0; - // 修正时间轴 + // 修正时间轴 [AUTO-TRANSLATED:5ab341f9] + // Correct the timeline auto strRange = parser["Range"]; if (strRange.size()) { auto strStart = findSubString(strRange.data(), "npt=", "-"); @@ -499,7 +529,8 @@ void RtspPlayer::handleResPAUSE(const Parser &parser, int type) { void RtspPlayer::onWholeRtspPacket(Parser &parser) { if (!start_with(parser.method(), "RTSP")) { - // 不是rtsp回复,忽略 + // 不是rtsp回复,忽略 [AUTO-TRANSLATED:1dca8f64] + // Not an RTSP response, ignore WarnL << "Not rtsp response: " << parser.method(); return; } @@ -511,7 +542,8 @@ void RtspPlayer::onWholeRtspPacket(Parser &parser) { } parser.clear(); } catch (std::exception &err) { - // 定时器_pPlayTimer为空后表明握手结束了 + // 定时器_pPlayTimer为空后表明握手结束了 [AUTO-TRANSLATED:06a369c2] + // _pPlayTimer is empty after handshake ends onPlayResult_l(SockException(Err_other, err.what()), !_play_check_timer); } } @@ -536,14 +568,16 @@ void RtspPlayer::onRtpPacket(const char *data, size_t len) { } } -// 此处预留rtcp处理函数 +// 此处预留rtcp处理函数 [AUTO-TRANSLATED:30c3afa8] +// Reserved for RTCP processing function void RtspPlayer::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len) { auto rtcp_arr = RtcpHeader::loadFromBytes((char *)data, len); for (auto &rtcp : rtcp_arr) { _rtcp_context[track_idx]->onRtcp(rtcp); if ((RtcpType)rtcp->pt == RtcpType::RTCP_SR) { auto sr = (RtcpSR *)(rtcp); - // 设置rtp时间戳与ntp时间戳的对应关系 + // 设置rtp时间戳与ntp时间戳的对应关系 [AUTO-TRANSLATED:e92f4749] + // Set the correspondence between RTP timestamp and NTP timestamp setNtpStamp(track_idx, sr->rtpts, sr->getNtpUnixStampMS()); } } @@ -611,7 +645,8 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const Str if (!_realm.empty() && !(*this)[Client::kRtspUser].empty()) { if (!_md5_nonce.empty()) { - // MD5认证 + // MD5认证 [AUTO-TRANSLATED:0640fa6a] + // MD5 authentication /* response计算方法如下: RTSP客户端应该使用username + password并计算response如下: @@ -619,6 +654,15 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const Str response = md5( password:nonce:md5(public_method:url) ); (2)当password为ANSI字符串,则 response= md5( md5(username:realm:password):nonce:md5(public_method:url) ); + /* + The response calculation method is as follows: + The RTSP client should use username + password and calculate the response as follows: + (1) When password is MD5 encoded, then + response = md5( password:nonce:md5(public_method:url) ); + (2) When password is ANSI string, then + response= md5( md5(username:realm:password):nonce:md5(public_method:url) ); + + * [AUTO-TRANSLATED:7858b67d] */ string encrypted_pwd = (*this)[Client::kRtspPwd]; if (!(*this)[Client::kRtspPwdIsMD5].as()) { @@ -634,7 +678,8 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const Str printer << "response=\"" << response << "\""; header.emplace("Authorization", printer); } else if (!(*this)[Client::kRtspPwdIsMD5].as()) { - // base64认证 + // base64认证 [AUTO-TRANSLATED:06d26447] + // Base64 authentication auto authStrBase64 = encodeBase64((*this)[Client::kRtspUser] + ":" + (*this)[Client::kRtspPwd]); header.emplace("Authorization", StrPrinter << "Basic " << authStrBase64); } @@ -657,11 +702,13 @@ void RtspPlayer::onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) { auto &ticker = _rtcp_send_ticker[track_idx]; if (ticker.elapsedTime() < _beat_interval_ms) { - // 心跳时间未到 + // 心跳时间未到 [AUTO-TRANSLATED:265d4e62] + // Heartbeat time not reached return; } - // 有些rtsp服务器需要rtcp保活,有些需要发送信令保活; rtcp与rtsp信令轮流心跳,该特性用于兼容issue:#642 + // 有些rtsp服务器需要rtcp保活,有些需要发送信令保活; rtcp与rtsp信令轮流心跳,该特性用于兼容issue:#642 [AUTO-TRANSLATED:a36070c5] + // Some RTSP servers require RTCP keep-alive, some require sending signaling keep-alive; RTCP and RTSP signaling alternate heartbeat, this feature is used to be compatible with issue:#642 auto &rtcp_flag = _send_rtcp[track_idx]; ticker.resetTime(); @@ -672,16 +719,19 @@ void RtspPlayer::onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) { default: rtcp_flag = !rtcp_flag; break; } - // 发送信令保活 + // 发送信令保活 [AUTO-TRANSLATED:0ef0747e] + // Send signaling keep-alive if (!rtcp_flag) { if (track_idx == 0) { - // 两个track无需同时触发发送信令保活 + // 两个track无需同时触发发送信令保活 [AUTO-TRANSLATED:7dde4aec] + // Two tracks do not need to trigger sending signaling keep-alive at the same time sendKeepAlive(); } return; } - // 发送rtcp + // 发送rtcp [AUTO-TRANSLATED:5c7aad87] + // Send RTCP static auto send_rtcp = [](RtspPlayer *thiz, int index, Buffer::Ptr ptr) { if (thiz->_rtp_type == Rtsp::RTP_TCP) { auto &track = thiz->_sdp_track[index]; @@ -703,27 +753,33 @@ void RtspPlayer::onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) { void RtspPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) { if (ex.getErrCode() == Err_shutdown) { - // 主动shutdown的,不触发回调 + // 主动shutdown的,不触发回调 [AUTO-TRANSLATED:21f9c396] + // Active shutdown, do not trigger callback return; } WarnL << ex.getErrCode() << " " << ex.what(); if (!handshake_done) { - // 开始播放阶段 + // 开始播放阶段 [AUTO-TRANSLATED:7ef385fc] + // Start playback stage _play_check_timer.reset(); onPlayResult(ex); - // 是否为性能测试模式 + // 是否为性能测试模式 [AUTO-TRANSLATED:871a0e65] + // Whether it is performance test mode _benchmark_mode = (*this)[Client::kBenchmarkMode].as(); } else if (ex) { - // 播放成功后异常断开回调 + // 播放成功后异常断开回调 [AUTO-TRANSLATED:3fe28e4f] + // Callback for abnormal disconnection after successful playback onShutdown(ex); } else { - // 恢复播放 + // 恢复播放 [AUTO-TRANSLATED:7a99afd6] + // Resume playback onResume(); } if (!ex) { - // 播放成功,恢复rtp接收超时定时器 + // 播放成功,恢复rtp接收超时定时器 [AUTO-TRANSLATED:0ebefcb5] + // Playback successful, restore RTP receive timeout timer _rtp_recv_ticker.resetTime(); auto timeoutMS = (*this)[Client::kMediaTimeoutMS].as(); weak_ptr weakSelf = static_pointer_cast(shared_from_this()); @@ -733,13 +789,15 @@ void RtspPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) { return false; } if (strongSelf->_rtp_recv_ticker.elapsedTime() > timeoutMS) { - // 接收rtp媒体数据包超时 + // 接收rtp媒体数据包超时 [AUTO-TRANSLATED:601b8c0c] + // Receive RTP media data packet timeout strongSelf->onPlayResult_l(SockException(Err_timeout, "receive rtp timeout"), true); return false; } return true; }; - // 创建rtp数据接收超时检测定时器 + // 创建rtp数据接收超时检测定时器 [AUTO-TRANSLATED:edbffc19] + // Create RTP data receive timeout detection timer _rtp_check_timer = std::make_shared(timeoutMS / 2000.0f, lam, getPoller()); } else { sendTeardown(); @@ -806,7 +864,8 @@ bool RtspPlayerImp::onCheckSDP(const std::string &sdp) { } void RtspPlayerImp::onRecvRTP(RtpPacket::Ptr rtp, const SdpTrack::Ptr &track) { - // rtp解复用时可以判断是否为关键帧起始位置 + // rtp解复用时可以判断是否为关键帧起始位置 [AUTO-TRANSLATED:fb7d9b6e] + // When demultiplexing RTP, it can be determined whether it is the starting position of the key frame auto key_pos = _demuxer->inputRtp(rtp); if (_rtsp_media_src) { _rtsp_media_src->onWrite(std::move(rtp), key_pos); diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index cdc6dfb2..58e2cfa5 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -24,7 +24,8 @@ namespace mediakit { -//实现了rtsp播放器协议部分的功能,及数据接收功能 +// 实现了rtsp播放器协议部分的功能,及数据接收功能 [AUTO-TRANSLATED:c1ed5c0f] +// Implemented the rtsp player protocol part functionality, and data receiving functionality class RtspPlayer : public PlayerBase, public toolkit::TcpClient, public RtspSplitter, public RtpReceiver { public: using Ptr = std::shared_ptr; @@ -39,7 +40,8 @@ public: float getPacketLossRate(TrackType type) const override; protected: - //派生类回调函数 + // 派生类回调函数 [AUTO-TRANSLATED:61e20903] + // Derived class callback function virtual bool onCheckSDP(const std::string &sdp) = 0; virtual void onRecvRTP(RtpPacket::Ptr rtp, const SdpTrack::Ptr &track) = 0; uint32_t getProgressMilliSecond() const; @@ -48,6 +50,10 @@ protected: /** * 收到完整的rtsp包回调,包括sdp等content数据 * @param parser rtsp包 + * Callback for receiving a complete rtsp packet, including sdp and other content data + * @param parser rtsp packet + + * [AUTO-TRANSLATED:4d3c2056] */ void onWholeRtspPacket(Parser &parser) override ; @@ -55,6 +61,11 @@ protected: * 收到rtp包回调 * @param data * @param len + * Callback for receiving rtp packet + * @param data + * @param len + + * [AUTO-TRANSLATED:c8f7c9bb] */ void onRtpPacket(const char *data,size_t len) override ; @@ -62,6 +73,11 @@ protected: * rtp数据包排序后输出 * @param rtp rtp数据包 * @param track_idx track索引 + * Output rtp data packets after sorting + * @param rtp rtp data packet + * @param track_idx track index + + * [AUTO-TRANSLATED:8f9ca364] */ void onRtpSorted(RtpPacket::Ptr rtp, int track_idx) override; @@ -69,6 +85,11 @@ protected: * 解析出rtp但还未排序 * @param rtp rtp数据包 * @param track_index track索引 + * Parse out rtp but not yet sorted + * @param rtp rtp data packet + * @param track_index track index + + * [AUTO-TRANSLATED:c1636911] */ void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override; @@ -78,6 +99,13 @@ protected: * @param track sdp相关信息 * @param data rtcp内容 * @param len rtcp内容长度 + * Callback for receiving RTCP packet + * @param track_idx track index + * @param track sdp related information + * @param data rtcp content + * @param len rtcp content length + + * [AUTO-TRANSLATED:1a2cfa4f] */ virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len); @@ -109,27 +137,35 @@ private: void createUdpSockIfNecessary(int track_idx); private: - //是否为性能测试模式 + // 是否为性能测试模式 [AUTO-TRANSLATED:1fde8234] + // Whether it is performance test mode bool _benchmark_mode = false; - //轮流发送rtcp与GET_PARAMETER保活 + // 轮流发送rtcp与GET_PARAMETER保活 [AUTO-TRANSLATED:5b6f9c37] + // Send rtcp and GET_PARAMETER keep-alive in turn bool _send_rtcp[2] = {true, true}; - // 心跳类型 + // 心跳类型 [AUTO-TRANSLATED:c22abb05] + // Heartbeat type uint32_t _beat_type = 0; - // 心跳保护间隔 + // 心跳保护间隔 [AUTO-TRANSLATED:de16d9c9] + // Heartbeat protection interval uint32_t _beat_interval_ms = 0; std::string _play_url; - // rtsp开始倍速 + // rtsp开始倍速 [AUTO-TRANSLATED:9ab84508] + // Rtsp start speed float _speed= 0.0f; std::vector _sdp_track; std::function _on_response; - //RTP端口,trackid idx 为数组下标 + // RTP端口,trackid idx 为数组下标 [AUTO-TRANSLATED:77c186bb] + // RTP port, trackid idx is the array subscript toolkit::Socket::Ptr _rtp_sock[2]; - //RTCP端口,trackid idx 为数组下标 + // RTCP端口,trackid idx 为数组下标 [AUTO-TRANSLATED:446a7861] + // RTCP port, trackid idx is the array subscript toolkit::Socket::Ptr _rtcp_sock[2]; - //rtsp鉴权相关 + // rtsp鉴权相关 [AUTO-TRANSLATED:947dc6a3] + // Rtsp authentication related std::string _md5_nonce; std::string _realm; //rtsp info @@ -139,19 +175,24 @@ private: std::string _control_url; Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP; - //当前rtp时间戳 + // 当前rtp时间戳 [AUTO-TRANSLATED:410f2691] + // Current rtp timestamp uint32_t _stamp[2] = {0, 0}; - //超时功能实现 + // 超时功能实现 [AUTO-TRANSLATED:1d603b3a] + // Timeout function implementation toolkit::Ticker _rtp_recv_ticker; std::shared_ptr _play_check_timer; std::shared_ptr _rtp_check_timer; - //服务器支持的命令 + // 服务器支持的命令 [AUTO-TRANSLATED:f7f589bf] + // Server supported commands std::set _supported_cmd; ////////// rtcp //////////////// - //rtcp发送时间,trackid idx 为数组下标 + // rtcp发送时间,trackid idx 为数组下标 [AUTO-TRANSLATED:bf3248b1] + // Rtcp send time, trackid idx is the array subscript toolkit::Ticker _rtcp_send_ticker[2]; - //统计rtp并发送rtcp + // 统计rtp并发送rtcp [AUTO-TRANSLATED:0ac2b665] + // Statistics rtp and send rtcp std::vector _rtcp_context; }; diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index 6c8818e3..0bb7b474 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -60,7 +60,8 @@ public: std::vector getTracks(bool ready = true) const override; private: - //派生类回调函数 + // 派生类回调函数 [AUTO-TRANSLATED:61e20903] + // Derived class callback function bool onCheckSDP(const std::string &sdp) override; void onRecvRTP(RtpPacket::Ptr rtp, const SdpTrack::Ptr &track) override; diff --git a/src/Rtsp/RtspPusher.cpp b/src/Rtsp/RtspPusher.cpp index c8531f8d..7b644363 100644 --- a/src/Rtsp/RtspPusher.cpp +++ b/src/Rtsp/RtspPusher.cpp @@ -101,15 +101,18 @@ void RtspPusher::publish(const string &url_str) { void RtspPusher::onPublishResult_l(const SockException &ex, bool handshake_done) { DebugL << ex.what(); if (ex.getErrCode() == Err_shutdown) { - //主动shutdown的,不触发回调 + // 主动shutdown的,不触发回调 [AUTO-TRANSLATED:bd97b1c1] + // Actively shutdown, do not trigger callback return; } if (!handshake_done) { - //播放结果回调 + // 播放结果回调 [AUTO-TRANSLATED:a5714269] + // Playback result callback _publish_timer.reset(); onPublishResult(ex); } else { - //播放成功后异常断开回调 + // 播放成功后异常断开回调 [AUTO-TRANSLATED:b5c5fa80] + // Callback for abnormal disconnection after playback success onShutdown(ex); } @@ -119,7 +122,8 @@ void RtspPusher::onPublishResult_l(const SockException &ex, bool handshake_done) } void RtspPusher::onError(const SockException &ex) { - //定时器_pPublishTimer为空后表明握手结束了 + // 定时器_pPublishTimer为空后表明握手结束了 [AUTO-TRANSLATED:630ec31e] + // The timer _pPublishTimer is empty, indicating that the handshake is over onPublishResult_l(ex, !_publish_timer); } @@ -136,7 +140,8 @@ void RtspPusher::onRecv(const Buffer::Ptr &buf){ input(buf->data(), buf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); - //定时器_pPublishTimer为空后表明握手结束了 + // 定时器_pPublishTimer为空后表明握手结束了 [AUTO-TRANSLATED:630ec31e] + // The timer _pPublishTimer is empty, indicating that the handshake is over onPublishResult_l(ex, !_publish_timer); } } @@ -171,7 +176,8 @@ void RtspPusher::sendAnnounce() { if (!src) { throw std::runtime_error("the media source was released"); } - //解析sdp + // 解析sdp [AUTO-TRANSLATED:a2d549e2] + // Parse sdp _sdp_parser.load(src->getSdp()); _track_vec = _sdp_parser.getAvailableTrack(); if (_track_vec.empty()) { @@ -187,7 +193,8 @@ void RtspPusher::sendAnnounce() { void RtspPusher::handleResAnnounce(const Parser &parser) { string authInfo = parser["WWW-Authenticate"]; - //发送DESCRIBE命令后的回复 + // 发送DESCRIBE命令后的回复 [AUTO-TRANSLATED:924afd2e] + // Reply after sending DESCRIBE command if ((parser.status() == "401") && handleAuthenticationFailure(authInfo)) { sendAnnounce(); return; @@ -219,7 +226,8 @@ void RtspPusher::handleResAnnounce(const Parser &parser) { bool RtspPusher::handleAuthenticationFailure(const string ¶ms_str) { if (!_realm.empty()) { - //已经认证过了 + // 已经认证过了 [AUTO-TRANSLATED:3c8ce1d6] + // Already authenticated return false; } @@ -249,7 +257,8 @@ bool RtspPusher::handleAuthenticationFailure(const string ¶ms_str) { return false; } -//有必要的情况下创建udp端口 +// 有必要的情况下创建udp端口 [AUTO-TRANSLATED:b59b7389] +// Create UDP port if necessary void RtspPusher::createUdpSockIfNecessary(int track_idx){ auto &rtpSockRef = _rtp_sock[track_idx]; auto &rtcpSockRef = _rtcp_sock[track_idx]; @@ -312,17 +321,20 @@ void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) { auto &rtcp_sock = _rtcp_sock[track_idx]; auto rtpto = SockUtil::make_sockaddr(get_peer_ip().data(), rtp_port); - //设置rtp发送目标,为后续发送rtp做准备 + // 设置rtp发送目标,为后续发送rtp做准备 [AUTO-TRANSLATED:5ae9bd72] + // Set RTP sending target, prepare for subsequent RTP sending rtp_sock->bindPeerAddr((struct sockaddr *) &(rtpto)); - //设置rtcp发送目标,为后续发送rtcp做准备 + // 设置rtcp发送目标,为后续发送rtcp做准备 [AUTO-TRANSLATED:a487732d] + // Set RTCP sending target, prepare for subsequent RTCP sending auto rtcpto = SockUtil::make_sockaddr(get_peer_ip().data(), rtcp_port); rtcp_sock->bindPeerAddr((struct sockaddr *)&(rtcpto)); auto peer_ip = get_peer_ip(); weak_ptr weakSelf = static_pointer_cast(shared_from_this()); if(rtcp_sock) { - //设置rtcp over udp接收回调处理函数 + // 设置rtcp over udp接收回调处理函数 [AUTO-TRANSLATED:59963785] + // Set RTCP over UDP receive callback handler rtcp_sock->setOnRead([peer_ip, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { @@ -340,7 +352,8 @@ void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) { RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP); if (track_idx < _track_vec.size() - 1) { - //需要继续发送SETUP命令 + // 需要继续发送SETUP命令 [AUTO-TRANSLATED:fddda4c6] + // Need to continue sending SETUP command sendSetup(track_idx + 1); return; } @@ -359,7 +372,8 @@ void RtspPusher::updateRtcpContext(const RtpPacket::Ptr &rtp){ auto &rtcp_ctx = _rtcp_context[track_index]; rtcp_ctx->onRtp(rtp->getSeq(), rtp->getStamp(), rtp->ntp_stamp, rtp->sample_rate, rtp->size() - RtpPacket::kRtpTcpHeaderSize); if (!rtp->ntp_stamp && !rtp->getStamp()) { - // 忽略时间戳都为0的rtp + // 忽略时间戳都为0的rtp [AUTO-TRANSLATED:6b793565] + // Ignore RTP with all timestamps being 0 return; } //send rtcp every 5 second @@ -469,7 +483,8 @@ void RtspPusher::sendRecord() { } }); if (_rtp_type != Rtsp::RTP_TCP) { - /////////////////////////心跳///////////////////////////////// + // ///////////////////////心跳///////////////////////////////// [AUTO-TRANSLATED:4e72777b] + // ///////////////////////Heartbeat///////////////////////////////// _beat_timer.reset(new Timer((*this)[Client::kBeatIntervalMS].as() / 1000.0f, [weak_self]() { auto strong_self = weak_self.lock(); if (!strong_self) { @@ -480,7 +495,8 @@ void RtspPusher::sendRecord() { }, getPoller())); } onPublishResult_l(SockException(Err_success, "success"), false); - //提升发送性能 + // 提升发送性能 [AUTO-TRANSLATED:90630751] + // Improve sending performance setSocketFlags(); }; sendRtspRequest("RECORD", _content_base, {"Range", "npt=0.000-"}); @@ -489,7 +505,8 @@ void RtspPusher::sendRecord() { void RtspPusher::setSocketFlags(){ GET_CONFIG(int, merge_write_ms, General::kMergeWriteMS); if (merge_write_ms > 0) { - //提高发送性能 + // 提高发送性能 [AUTO-TRANSLATED:de96ec30] + // Improve sending performance setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); SockUtil::setNoDelay(getSock()->rawFD(), false); } @@ -519,7 +536,8 @@ void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrC if (!_realm.empty() && !(*this)[Client::kRtspUser].empty()) { if (!_nonce.empty()) { - //MD5认证 + // MD5认证 [AUTO-TRANSLATED:57936f0b] + // MD5 authentication /* response计算方法如下: RTSP客户端应该使用username + password并计算response如下: @@ -527,6 +545,15 @@ void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrC response = md5( password:nonce:md5(public_method:url) ); (2)当password为ANSI字符串,则 response= md5( md5(username:realm:password):nonce:md5(public_method:url) ); + /* + The response calculation method is as follows: + The RTSP client should use username + password and calculate the response as follows: + (1) When password is MD5 encoded, then + response = md5( password:nonce:md5(public_method:url) ); + (2) When password is ANSI string, then + response= md5( md5(username:realm:password):nonce:md5(public_method:url) ); + + * [AUTO-TRANSLATED:7858b67d] */ string encrypted_pwd = (*this)[Client::kRtspPwd]; if (!(*this)[Client::kRtspPwdIsMD5].as()) { @@ -542,7 +569,8 @@ void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrC printer << "response=\"" << response << "\""; header.emplace("Authorization", printer); } else if (!(*this)[Client::kRtspPwdIsMD5].as()) { - // base64认证 + // base64认证 [AUTO-TRANSLATED:06d26447] + // base64 authentication auto authStrBase64 = encodeBase64((*this)[Client::kRtspUser] + ":" + (*this)[Client::kRtspPwd]); header.emplace("Authorization", StrPrinter << "Basic " << authStrBase64); } diff --git a/src/Rtsp/RtspPusher.h b/src/Rtsp/RtspPusher.h index d9bfeeea..eb929f34 100644 --- a/src/Rtsp/RtspPusher.h +++ b/src/Rtsp/RtspPusher.h @@ -71,7 +71,8 @@ private: unsigned int _cseq = 1; Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP; - //rtsp鉴权相关 + // rtsp鉴权相关 [AUTO-TRANSLATED:947dc6a3] + // RTSP authentication related std::string _nonce; std::string _realm; std::string _url; @@ -79,21 +80,27 @@ private: std::string _content_base; SdpParser _sdp_parser; std::vector _track_vec; - //RTP端口,trackid idx 为数组下标 + // RTP端口,trackid idx 为数组下标 [AUTO-TRANSLATED:77c186bb] + // RTP port, trackid idx is the array index toolkit::Socket::Ptr _rtp_sock[2]; - //RTCP端口,trackid idx 为数组下标 + // RTCP端口,trackid idx 为数组下标 [AUTO-TRANSLATED:446a7861] + // RTCP port, trackid idx is the array index toolkit::Socket::Ptr _rtcp_sock[2]; - //超时功能实现 + // 超时功能实现 [AUTO-TRANSLATED:1d603b3a] + // Timeout function implementation toolkit::Timer::Ptr _publish_timer; - //心跳定时器 + // 心跳定时器 [AUTO-TRANSLATED:536ec800] + // Heartbeat timer toolkit::Timer::Ptr _beat_timer; std::weak_ptr _push_src; RtspMediaSource::RingType::RingReader::Ptr _rtsp_reader; std::function _on_res_func; ////////// rtcp //////////////// - //rtcp发送时间,trackid idx 为数组下标 + // rtcp发送时间,trackid idx 为数组下标 [AUTO-TRANSLATED:bf3248b1] + // RTCP send time, trackid idx is the array index toolkit::Ticker _rtcp_send_ticker[2]; - //统计rtp并发送rtcp + // 统计rtp并发送rtcp [AUTO-TRANSLATED:0ac2b665] + // Statistics RTP and send RTCP std::vector _rtcp_context; }; diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 46e62fe2..2cbaf217 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -128,7 +128,7 @@ void RtspSession::onRecv(const Buffer::Ptr &buf) { void RtspSession::onWholeRtspPacket(Parser &parser) { string method = parser.method(); //提取出请求命令字 _cseq = atoi(parser["CSeq"].data()); - if (_content_base.empty() && method != "GET") { + if (_content_base.empty() && method != "GET" && method != "POST" ) { RtspUrl rtsp; rtsp.parse(parser.url()); _content_base = rtsp._url; diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index df35a0cc..f985fd3b 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -29,8 +29,10 @@ class RtspSession : public toolkit::Session, public RtspSplitter, public RtpRece public: using Ptr = std::shared_ptr; using onGetRealm = std::function; - //encrypted为true是则表明是md5加密的密码,否则是明文密码 - //在请求明文密码时如果提供md5密码者则会导致认证失败 + // encrypted为true是则表明是md5加密的密码,否则是明文密码 [AUTO-TRANSLATED:cad96e51] + // `encrypted` being `true` indicates an MD5 encrypted password, otherwise it is a plain text password + // 在请求明文密码时如果提供md5密码者则会导致认证失败 [AUTO-TRANSLATED:8a38bff8] + // When requesting a plain text password, providing an MD5 password will result in authentication failure using onAuth = std::function; RtspSession(const toolkit::Socket::Ptr &sock); @@ -41,11 +43,14 @@ public: protected: /////RtspSplitter override///// - //收到完整的rtsp包回调,包括sdp等content数据 + // 收到完整的rtsp包回调,包括sdp等content数据 [AUTO-TRANSLATED:efbe20df] + // Callback for receiving a complete RTSP packet, including SDP and other content data void onWholeRtspPacket(Parser &parser) override; - //收到rtp包回调 + // 收到rtp包回调 [AUTO-TRANSLATED:119f1cca] + // Callback for receiving an RTP packet void onRtpPacket(const char *data, size_t len) override; - //从rtsp头中获取Content长度 + // 从rtsp头中获取Content长度 [AUTO-TRANSLATED:0e6f033e] + // Get the Content length from the RTSP header ssize_t getContentLength(Parser &parser) override; ////RtpReceiver override//// @@ -53,36 +58,47 @@ protected: void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override; ///////MediaSourceEvent override/////// - // 关闭 + // 关闭 [AUTO-TRANSLATED:92392f02] + // Close bool close(MediaSource &sender) override; - // 播放总人数 + // 播放总人数 [AUTO-TRANSLATED:c42a3161] + // Total number of players int totalReaderCount(MediaSource &sender) override; - // 获取媒体源类型 + // 获取媒体源类型 [AUTO-TRANSLATED:34290a69] + // Get the media source type MediaOriginType getOriginType(MediaSource &sender) const override; - // 获取媒体源url或者文件路径 + // 获取媒体源url或者文件路径 [AUTO-TRANSLATED:fa34d795] + // Get the media source URL or file path std::string getOriginUrl(MediaSource &sender) const override; - // 获取媒体源客户端相关信息 + // 获取媒体源客户端相关信息 [AUTO-TRANSLATED:037ef910] + // Get the media source client related information std::shared_ptr getOriginSock(MediaSource &sender) const override; - // 由于支持断连续推,存在OwnerPoller变更的可能 + // 由于支持断连续推,存在OwnerPoller变更的可能 [AUTO-TRANSLATED:1c863b40] + // Due to support for continuous pushing, there is a possibility of OwnerPoller changes toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; /////Session override//// ssize_t send(toolkit::Buffer::Ptr pkt) override; - //收到RTCP包回调 + // 收到RTCP包回调 [AUTO-TRANSLATED:249f4807] + // Callback for receiving an RTCP packet virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, const char *data, size_t len); - //回复客户端 + // 回复客户端 [AUTO-TRANSLATED:8108ebea] + // Reply to the client virtual bool sendRtspResponse(const std::string &res_code, const StrCaseMap &header = StrCaseMap(), const std::string &sdp = "", const char *protocol = "RTSP/1.0"); protected: - //url解析后保存的相关信息 + // url解析后保存的相关信息 [AUTO-TRANSLATED:1c26e4e3] + // Information related to the URL after parsing MediaInfo _media_info; ////////RTP over udp_multicast//////// - //共享的rtp组播对象 + // 共享的rtp组播对象 [AUTO-TRANSLATED:d4a5cfdd] + // Shared RTP multicast object RtpMultiCaster::Ptr _multicaster; - //Session号 + // Session号 [AUTO-TRANSLATED:4552ec74] + // Session number std::string _sessionid; uint32_t _multicast_ip = 0; @@ -90,122 +106,176 @@ protected: uint16_t _multicast_audio_port = 0; private: - //处理options方法,获取服务器能力 + // 处理options方法,获取服务器能力 [AUTO-TRANSLATED:a51f6d7c] + // Handle the OPTIONS method, get server capabilities void handleReq_Options(const Parser &parser); - //处理describe方法,请求服务器rtsp sdp信息 + // 处理describe方法,请求服务器rtsp sdp信息 [AUTO-TRANSLATED:ed2c8fcb] + // Handle the DESCRIBE method, request server RTSP SDP information void handleReq_Describe(const Parser &parser); - //处理ANNOUNCE方法,请求推流,附带sdp + // 处理ANNOUNCE方法,请求推流,附带sdp [AUTO-TRANSLATED:aa4b4517] + // Handle the ANNOUNCE method, request streaming, with SDP attached void handleReq_ANNOUNCE(const Parser &parser); - //处理record方法,开始推流 + // 处理record方法,开始推流 [AUTO-TRANSLATED:885cf8a9] + // Handle the RECORD method, start streaming void handleReq_RECORD(const Parser &parser); - //处理setup方法,播放和推流协商rtp传输方式用 + // 处理setup方法,播放和推流协商rtp传输方式用 [AUTO-TRANSLATED:cbe5dcfc] + // Handle the SETUP method, used for negotiating RTP transport methods for playback and streaming void handleReq_Setup(const Parser &parser); - //处理play方法,开始或恢复播放 + // 处理play方法,开始或恢复播放 [AUTO-TRANSLATED:f15d151d] + // Handle the PLAY method, start or resume playback void handleReq_Play(const Parser &parser); - //处理pause方法,暂停播放 + // 处理pause方法,暂停播放 [AUTO-TRANSLATED:0c3b8f79] + // Handle the PAUSE method, pause playback void handleReq_Pause(const Parser &parser); - //处理teardown方法,结束播放 + // 处理teardown方法,结束播放 [AUTO-TRANSLATED:64d82572] + // Handle the TEARDOWN method, end playback void handleReq_Teardown(const Parser &parser); - //处理Get方法,rtp over http才用到 + // 处理Get方法,rtp over http才用到 [AUTO-TRANSLATED:c7c51eb6] + // Handle the GET method, only used for RTP over HTTP void handleReq_Get(const Parser &parser); - //处理Post方法,rtp over http才用到 + // 处理Post方法,rtp over http才用到 [AUTO-TRANSLATED:228bdbbe] + // Handle the POST method, only used for RTP over HTTP void handleReq_Post(const Parser &parser); - //处理SET_PARAMETER、GET_PARAMETER方法,一般用于心跳 + // 处理SET_PARAMETER、GET_PARAMETER方法,一般用于心跳 [AUTO-TRANSLATED:b9e333e1] + // Handle the SET_PARAMETER, GET_PARAMETER methods, generally used for heartbeats void handleReq_SET_PARAMETER(const Parser &parser); - //rtsp资源未找到 + // rtsp资源未找到 [AUTO-TRANSLATED:9b779890] + // RTSP resource not found void send_StreamNotFound(); - //不支持的传输模式 + // 不支持的传输模式 [AUTO-TRANSLATED:ef90414c] + // Unsupported transport mode void send_UnsupportedTransport(); - //会话id错误 + // 会话id错误 [AUTO-TRANSLATED:7cf632d3] + // Session ID error void send_SessionNotFound(); - //一般rtsp服务器打开端口失败时触发 + // 一般rtsp服务器打开端口失败时触发 [AUTO-TRANSLATED:82ecb043] + // Triggered when the general RTSP server fails to open the port void send_NotAcceptable(); - //获取track下标 + // 获取track下标 [AUTO-TRANSLATED:36d0b2c2] + // Get the track index int getTrackIndexByTrackType(TrackType type); int getTrackIndexByControlUrl(const std::string &control_url); int getTrackIndexByInterleaved(int interleaved); - //一般用于接收udp打洞包,也用于rtsp推流 + // 一般用于接收udp打洞包,也用于rtsp推流 [AUTO-TRANSLATED:0b55c12f] + // Generally used to receive UDP hole punching packets, also used for RTSP pushing void onRcvPeerUdpData(int interleaved, const toolkit::Buffer::Ptr &buf, const struct sockaddr_storage &addr); - //配合onRcvPeerUdpData使用 + // 配合onRcvPeerUdpData使用 [AUTO-TRANSLATED:811d2d1a] + // Used in conjunction with onRcvPeerUdpData void startListenPeerUdpData(int track_idx); - ////rtsp专有认证相关//// - //认证成功 + // //rtsp专有认证相关//// [AUTO-TRANSLATED:0f021bb5] + // // RTSP specific authentication related //// + // 认证成功 [AUTO-TRANSLATED:e1bafff3] + // Authentication successful void onAuthSuccess(); - //认证失败 + // 认证失败 [AUTO-TRANSLATED:a188326a] + // Authentication failed void onAuthFailed(const std::string &realm, const std::string &why, bool close = true); - //开始走rtsp专有认证流程 + // 开始走rtsp专有认证流程 [AUTO-TRANSLATED:2d773497] + // Start the RTSP specific authentication process void onAuthUser(const std::string &realm, const std::string &authorization); - //校验base64方式的认证加密 + // 校验base64方式的认证加密 [AUTO-TRANSLATED:bde8662f] + // Verify base64 authentication encryption void onAuthBasic(const std::string &realm, const std::string &auth_base64); - //校验md5方式的认证加密 + // 校验md5方式的认证加密 [AUTO-TRANSLATED:0cc37fa7] + // Verify MD5 authentication encryption void onAuthDigest(const std::string &realm, const std::string &auth_md5); - //触发url鉴权事件 + // 触发url鉴权事件 [AUTO-TRANSLATED:776dc4b5] + // Trigger URL authentication event void emitOnPlay(); - //发送rtp给客户端 + // 发送rtp给客户端 [AUTO-TRANSLATED:18602be0] + // Send RTP to the client void sendRtpPacket(const RtspMediaSource::RingDataType &pkt); - //触发rtcp发送 + // 触发rtcp发送 [AUTO-TRANSLATED:4fbe7706] + // Trigger RTCP sending void updateRtcpContext(const RtpPacket::Ptr &rtp); - //回复客户端 + // 回复客户端 [AUTO-TRANSLATED:8108ebea] + // Reply to the client bool sendRtspResponse(const std::string &res_code, const std::initializer_list &header, const std::string &sdp = "", const char *protocol = "RTSP/1.0"); - //设置socket标志 + // 设置socket标志 [AUTO-TRANSLATED:4086e686] + // Set socket flag void setSocketFlags(); private: - //是否已经触发on_play事件 + // 是否已经触发on_play事件 [AUTO-TRANSLATED:49c937ce] + // Whether the on_play event has been triggered bool _emit_on_play = false; bool _send_sr_rtcp[2] = {true, true}; - //断连续推延时 + // 断连续推延时 [AUTO-TRANSLATED:13ad578a] + // Delay in continuous pushing uint32_t _continue_push_ms = 0; - //推流或拉流客户端采用的rtp传输方式 + // 推流或拉流客户端采用的rtp传输方式 [AUTO-TRANSLATED:27411079] + // RTP transport method used by the pushing or pulling client Rtsp::eRtpType _rtp_type = Rtsp::RTP_Invalid; - //收到的seq,回复时一致 + // 收到的seq,回复时一致 [AUTO-TRANSLATED:64544fb4] + // Received seq, consistent when replying int _cseq = 0; - //消耗的总流量 + // 消耗的总流量 [AUTO-TRANSLATED:45ad2785] + // Total traffic consumed uint64_t _bytes_usage = 0; //ContentBase std::string _content_base; - //记录是否需要rtsp专属鉴权,防止重复触发事件 + // 记录是否需要rtsp专属鉴权,防止重复触发事件 [AUTO-TRANSLATED:9cff90b9] + // Record whether RTSP specific authentication is required to prevent duplicate event triggering std::string _rtsp_realm; - //登录认证 + // 登录认证 [AUTO-TRANSLATED:43fdb875] + // Login authentication std::string _auth_nonce; - //用于判断客户端是否超时 + // 用于判断客户端是否超时 [AUTO-TRANSLATED:86cb328a] + // Used to determine if the client has timed out toolkit::Ticker _alive_ticker; - //rtsp推流相关绑定的源 + // rtsp推流相关绑定的源 [AUTO-TRANSLATED:a25078d9] + // Source bound to RTSP pushing RtspMediaSourceImp::Ptr _push_src; - //推流器所有权 + // 推流器所有权 [AUTO-TRANSLATED:e47b4bcb] + // Pusher ownership std::shared_ptr _push_src_ownership; - //rtsp播放器绑定的直播源 + // rtsp播放器绑定的直播源 [AUTO-TRANSLATED:a7e130b5] + // Live source bound to the RTSP player std::weak_ptr _play_src; - //直播源读取器 + // 直播源读取器 [AUTO-TRANSLATED:e1edc193] + // Live source reader RtspMediaSource::RingType::RingReader::Ptr _play_reader; - //sdp里面有效的track,包含音频或视频 + // sdp里面有效的track,包含音频或视频 [AUTO-TRANSLATED:64e2fcdf] + // Valid track in SDP, including audio or video std::vector _sdp_track; - //播放器setup指定的播放track,默认为TrackInvalid表示不指定即音视频都推 + // 播放器setup指定的播放track,默认为TrackInvalid表示不指定即音视频都推 [AUTO-TRANSLATED:c7a0df5e] + // Track specified by the player setup, default is TrackInvalid, which means no specification, both audio and video are pushed TrackType _target_play_track = TrackInvalid; ////////RTP over udp//////// - //RTP端口,trackid idx 为数组下标 + // RTP端口,trackid idx 为数组下标 [AUTO-TRANSLATED:77c186bb] + // RTP port, trackid idx is the array index toolkit::Socket::Ptr _rtp_socks[2]; - //RTCP端口,trackid idx 为数组下标 + // RTCP端口,trackid idx 为数组下标 [AUTO-TRANSLATED:446a7861] + // RTCP port, trackid idx is the array index toolkit::Socket::Ptr _rtcp_socks[2]; - //标记是否收到播放的udp打洞包,收到播放的udp打洞包后才能知道其外网udp端口号 + // 标记是否收到播放的udp打洞包,收到播放的udp打洞包后才能知道其外网udp端口号 [AUTO-TRANSLATED:ad039c25] + // Flag whether the UDP hole punching packet for playback has been received. The external UDP port number can only be known after receiving the UDP hole punching packet for playback. std::unordered_set _udp_connected_flags; ////////RTSP over HTTP //////// - //quicktime 请求rtsp会产生两次tcp连接, - //一次发送 get 一次发送post,需要通过x-sessioncookie关联起来 + // quicktime 请求rtsp会产生两次tcp连接, [AUTO-TRANSLATED:3f72e181] + // QuickTime requests for RTSP will generate two TCP connections, + // 一次发送 get 一次发送post,需要通过x-sessioncookie关联起来 [AUTO-TRANSLATED:f29a653f] + // one for sending GET and one for sending POST. They need to be associated through x-sessioncookie. std::string _http_x_sessioncookie; std::function _on_recv; ////////// rtcp //////////////// - //rtcp发送时间,trackid idx 为数组下标 + // rtcp发送时间,trackid idx 为数组下标 [AUTO-TRANSLATED:bf3248b1] + // RTCP send time, trackid idx is the array index toolkit::Ticker _rtcp_send_tickers[2]; - //统计rtp并发送rtcp + // 统计rtp并发送rtcp [AUTO-TRANSLATED:0ac2b665] + // Count RTP and send RTCP std::vector _rtcp_context; }; /** * 支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 + * RTSP server supporting SSL encryption, which can be used for devices such as Amazon Echo Show to access. + + + * [AUTO-TRANSLATED:7d1eed83] */ using RtspSessionWithSSL = toolkit::SessionWithSSL; diff --git a/src/Rtsp/RtspSplitter.cpp b/src/Rtsp/RtspSplitter.cpp index aaf8bfe7..50bfbb93 100644 --- a/src/Rtsp/RtspSplitter.cpp +++ b/src/Rtsp/RtspSplitter.cpp @@ -27,7 +27,8 @@ const char *RtspSplitter::onSearchPacketTail(const char *data, size_t len) { } if (len > 256 * 1024) { - //rtp大于256KB + // rtp大于256KB [AUTO-TRANSLATED:d8d8a481] + // rtp is greater than 256KB ret = (char *) memchr(data, '$', len); if (!ret) { WarnL << "rtp缓存溢出:" << hexdump(data, 1024); @@ -39,21 +40,26 @@ const char *RtspSplitter::onSearchPacketTail(const char *data, size_t len) { const char *RtspSplitter::onSearchPacketTail_l(const char *data, size_t len) { if(!_enableRecvRtp || data[0] != '$'){ - //这是rtsp包 + // 这是rtsp包 [AUTO-TRANSLATED:cdc2211d] + // This is an rtsp packet _isRtpPacket = false; return HttpRequestSplitter::onSearchPacketTail(data, len); } - //这是rtp包 + // 这是rtp包 [AUTO-TRANSLATED:627d4881] + // This is an rtp packet if(len < 4){ - //数据不够 + // 数据不够 [AUTO-TRANSLATED:72802244] + // Not enough data return nullptr; } uint16_t length = (((uint8_t *)data)[2] << 8) | ((uint8_t *)data)[3]; if(len < (size_t)(length + 4)){ - //数据不够 + // 数据不够 [AUTO-TRANSLATED:72802244] + // Not enough data return nullptr; } - //返回rtp包末尾 + // 返回rtp包末尾 [AUTO-TRANSLATED:2546d5ce] + // Return the end of the rtp packet _isRtpPacket = true; return data + 4 + length; } @@ -74,11 +80,14 @@ ssize_t RtspSplitter::onRecvHeader(const char *data, size_t len) { _parser.parse(data, len); } catch (toolkit::AssertFailedException &ex){ if (!_enableRecvRtp) { - // 还在握手中,直接中断握手 + // 还在握手中,直接中断握手 [AUTO-TRANSLATED:19dfdf7a] + // Still in handshake, interrupt handshake directly throw; } - // 握手已经结束,如果rtsp server存在发送缓存溢出的bug,那么rtsp信令可能跟rtp混在一起 - // 这种情况下,rtsp信令解析异常不中断链接,只丢弃这个包 + // 握手已经结束,如果rtsp server存在发送缓存溢出的bug,那么rtsp信令可能跟rtp混在一起 [AUTO-TRANSLATED:56c28270] + // Handshake has ended, if rtsp server has a send buffer overflow bug, then rtsp signaling may be mixed with rtp + // 这种情况下,rtsp信令解析异常不中断链接,只丢弃这个包 [AUTO-TRANSLATED:93cd60b4] + // In this case, rtsp signaling parsing exception does not interrupt the connection, just discard this packet WarnL << ex.what(); return 0; } diff --git a/src/Rtsp/RtspSplitter.h b/src/Rtsp/RtspSplitter.h index de268975..7e3a06d2 100644 --- a/src/Rtsp/RtspSplitter.h +++ b/src/Rtsp/RtspSplitter.h @@ -21,12 +21,20 @@ public: /** * 是否允许接收rtp包 * @param enable + * Whether to allow receiving rtp packets + * @param enable + + * [AUTO-TRANSLATED:8de8e1ee] */ void enableRecvRtp(bool enable); protected: /** * 收到完整的rtsp包回调,包括sdp等content数据 * @param parser rtsp包 + * Callback for receiving a complete rtsp packet, including sdp and other content data + * @param parser rtsp packet + + * [AUTO-TRANSLATED:4d3c2056] */ virtual void onWholeRtspPacket(Parser &parser) = 0; @@ -34,6 +42,11 @@ protected: * 收到rtp包回调 * @param data * @param len + * Callback for receiving rtp packets + * @param data + * @param len + + * [AUTO-TRANSLATED:c8f7c9bb] */ virtual void onRtpPacket(const char *data,size_t len) = 0; @@ -41,6 +54,12 @@ protected: * 从rtsp头中获取Content长度 * @param parser * @return + * Get the Content length from the rtsp header + * @param parser + * @return + + + * [AUTO-TRANSLATED:f0bc1fb8] */ virtual ssize_t getContentLength(Parser &parser); diff --git a/src/Rtsp/UDPServer.cpp b/src/Rtsp/UDPServer.cpp index 23dd4dc7..1ab42e66 100644 --- a/src/Rtsp/UDPServer.cpp +++ b/src/Rtsp/UDPServer.cpp @@ -33,7 +33,8 @@ Socket::Ptr UDPServer::getSock(SocketHelper &helper, const char* local_ip, int i if (it == _udp_sock_map.end()) { Socket::Ptr sock = helper.createSocket(); if (!sock->bindUdpSock(local_port, local_ip)) { - //分配失败 + // 分配失败 [AUTO-TRANSLATED:a6c6a6e6] + // Allocation failed return nullptr; } diff --git a/src/Shell/ShellCMD.h b/src/Shell/ShellCMD.h index 080c4ff1..3df4ee50 100644 --- a/src/Shell/ShellCMD.h +++ b/src/Shell/ShellCMD.h @@ -22,14 +22,16 @@ public: _parser.reset(new toolkit::OptionParser([](const std::shared_ptr &stream, toolkit::mINI &ini) { MediaSource::for_each_media([&](const MediaSource::Ptr &media) { if (ini.find("list") != ini.end()) { - //列出源 + // 列出源 [AUTO-TRANSLATED:cee6cf16] + // List sources (*stream) << "\t" << media->getUrl() << "\r\n"; return; } toolkit::EventPollerPool::Instance().getPoller()->async([ini, media, stream]() { if (ini.find("kick") != ini.end()) { - //踢出源 + // 踢出源 [AUTO-TRANSLATED:67fdbfaa] + // Kick out sources do { if (!media) { break; diff --git a/src/TS/TSMediaSource.h b/src/TS/TSMediaSource.h index 7915f701..16c9e17a 100644 --- a/src/TS/TSMediaSource.h +++ b/src/TS/TSMediaSource.h @@ -19,7 +19,8 @@ namespace mediakit { -//TS直播数据包 +// TS直播数据包 [AUTO-TRANSLATED:02fb2e8e] +// TS Live Data Packet class TSPacket : public toolkit::BufferOffset{ public: using Ptr = std::shared_ptr; @@ -31,7 +32,8 @@ public: uint64_t time_stamp = 0; }; -//TS直播源 +// TS直播源 [AUTO-TRANSLATED:0d25ead6] +// TS Live Source class TSMediaSource final : public MediaSource, public toolkit::RingDelegate, private PacketCache{ public: using Ptr = std::shared_ptr; @@ -50,6 +52,9 @@ public: /** * 获取媒体源的环形缓冲 + * Get the circular buffer of the media source + + * [AUTO-TRANSLATED:91a762bc] */ const RingType::Ptr &getRing() const { return _ring; @@ -62,6 +67,9 @@ public: /** * 获取播放器个数 + * Get the number of players + + * [AUTO-TRANSLATED:a451c846] */ int readerCount() override { return _ring ? _ring->readerCount() : 0; @@ -71,6 +79,11 @@ public: * 输入TS包 * @param packet TS包 * @param key 是否为关键帧第一个包 + * Input TS packet + * @param packet TS packet + * @param key Whether it is the first packet of the key frame + + * [AUTO-TRANSLATED:cd773549] */ void onWrite(TSPacket::Ptr packet, bool key) override { _speed[TrackVideo] += packet->size(); @@ -86,6 +99,9 @@ public: /** * 情况GOP缓存 + * Clear GOP cache + + * [AUTO-TRANSLATED:d863f8c9] */ void clearCache() override { PacketCache::clearCache(); @@ -102,7 +118,8 @@ private: } strong_self->onReaderChanged(size); }); - //注册媒体源 + // 注册媒体源 [AUTO-TRANSLATED:b87b5ac4] + // Register media source regist(); } @@ -110,9 +127,15 @@ private: * 合并写回调 * @param packet_list 合并写缓存列队 * @param key_pos 是否包含关键帧 + * Merge write callback + * @param packet_list Merge write cache queue + * @param key_pos Whether it contains a key frame + + * [AUTO-TRANSLATED:6e93913e] */ void onFlush(std::shared_ptr > packet_list, bool key_pos) override { - //如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存 + // 如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存 [AUTO-TRANSLATED:66208f94] + // If there is no video, then there is no meaning to the existence of GOP cache, so make sure to clear the GOP cache all the time _ring->write(std::move(packet_list), _have_video ? key_pos : true); } diff --git a/src/TS/TSMediaSourceMuxer.h b/src/TS/TSMediaSourceMuxer.h index 6b8d69ec..fab961d6 100644 --- a/src/TS/TSMediaSourceMuxer.h +++ b/src/TS/TSMediaSourceMuxer.h @@ -63,7 +63,8 @@ public: } bool isEnabled() { - //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + // 缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 [AUTO-TRANSLATED:7cfd4d49] + // Allow the inputFrame function to be triggered even when the cache is not yet cleared, so that the cache can be cleared in time. return _option.ts_demand ? (_clear_cache ? true : _enabled) : true; } diff --git a/srt/NackContext.cpp b/srt/NackContext.cpp index dd693522..9a331dc7 100644 --- a/srt/NackContext.cpp +++ b/srt/NackContext.cpp @@ -68,7 +68,8 @@ void NackContext::drop(uint32_t seq) { for (auto it = _nack_map.begin(); it != _nack_map.end();) { if (!is_cycle) { - // 不回环 + // 不回环 [AUTO-TRANSLATED:abe3c07b] + // No loop if (it->first <= seq) { it = _nack_map.erase(it); } else { diff --git a/srt/Packet.cpp b/srt/Packet.cpp index f3649fca..a1abb742 100644 --- a/srt/Packet.cpp +++ b/srt/Packet.cpp @@ -436,7 +436,8 @@ void HandshakePacket::assignPeerIP(struct sockaddr_storage *addr) { memset(peer_ip_addr, 0, sizeof(peer_ip_addr) * sizeof(peer_ip_addr[0])); if (addr->ss_family == AF_INET) { struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; - // 抓包 奇怪好像是小头端??? + // 抓包 奇怪好像是小头端??? [AUTO-TRANSLATED:40eb164c] + // Packet capture, weird, seems to be from the client side??? storeUint32LE(peer_ip_addr, ipv4->sin_addr.s_addr); } else if (addr->ss_family == AF_INET6) { if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr)) { diff --git a/srt/PacketQueue.cpp b/srt/PacketQueue.cpp index 9dcdf9e5..ce1da952 100644 --- a/srt/PacketQueue.cpp +++ b/srt/PacketQueue.cpp @@ -78,7 +78,8 @@ bool PacketQueue::inputPacket(DataPacket::Ptr pkt, std::list &o } while (_pkt_map.size() > _pkt_cap) { - // 防止回环 + // 防止回环 [AUTO-TRANSLATED:5999c704] + // Prevent circular references it = _pkt_map.find(_pkt_expected_seq); if (it != _pkt_map.end()) { out.push_back(it->second); diff --git a/srt/SrtSession.cpp b/srt/SrtSession.cpp index 8a671c96..19f400f9 100644 --- a/srt/SrtSession.cpp +++ b/srt/SrtSession.cpp @@ -32,7 +32,8 @@ void SrtSession::onRecv(const Buffer::Ptr &buffer) { size_t size = buffer->size(); if (_find_transport) { - //只允许寻找一次transport + // 只允许寻找一次transport [AUTO-TRANSLATED:620078e7] + // Only allow finding transport once _find_transport = false; _transport = querySrtTransport(data, size, getPoller()); if (_transport) { @@ -50,20 +51,25 @@ void SrtSession::onRecv(const Buffer::Ptr &buffer) { } void SrtSession::onError(const SockException &err) { - // udp链接超时,但是srt链接不一定超时,因为可能存在udp链接迁移的情况 - //在udp链接迁移时,新的SrtSession对象将接管SrtSession对象的生命周期 - //本SrtSession对象将在超时后自动销毁 + // udp链接超时,但是srt链接不一定超时,因为可能存在udp链接迁移的情况 [AUTO-TRANSLATED:8673c03c] + // UDP connection timed out, but SRT connection may not time out due to possible UDP connection migration + // 在udp链接迁移时,新的SrtSession对象将接管SrtSession对象的生命周期 [AUTO-TRANSLATED:13f0a9e6] + // When UDP connection migrates, a new SrtSession object will take over the lifecycle of the SrtSession object + // 本SrtSession对象将在超时后自动销毁 [AUTO-TRANSLATED:d0a34ab8] + // This SrtSession object will be automatically destroyed after timeout WarnP(this) << err; if (!_transport) { return; } - // 防止互相引用导致不释放 + // 防止互相引用导致不释放 [AUTO-TRANSLATED:82547e46] + // Prevent mutual reference from causing non-release auto transport = std::move(_transport); getPoller()->async( [transport] { - //延时减引用,防止使用transport对象时,销毁对象 + // 延时减引用,防止使用transport对象时,销毁对象 [AUTO-TRANSLATED:09dd6609] + // Delayed dereference to prevent object destruction when using the transport object //transport->onShutdown(err); }, false); diff --git a/srt/SrtTransport.cpp b/srt/SrtTransport.cpp index f88cf428..d02eb516 100644 --- a/srt/SrtTransport.cpp +++ b/srt/SrtTransport.cpp @@ -10,9 +10,11 @@ namespace SRT { #define SRT_FIELD "srt." -// srt 超时时间 +// srt 超时时间 [AUTO-TRANSLATED:7828ddb5] +// SRT timeout time const std::string kTimeOutSec = SRT_FIELD "timeoutSec"; -// srt 单端口udp服务器 +// srt 单端口udp服务器 [AUTO-TRANSLATED:af5210db] +// SRT single-port UDP server const std::string kPort = SRT_FIELD "port"; const std::string kLatencyMul = SRT_FIELD "latencyMul"; const std::string kPktBufSize = SRT_FIELD "pktBufSize"; @@ -107,7 +109,8 @@ void SrtTransport::inputSockData(uint8_t *buf, int len, struct sockaddr_storage s_control_functions.emplace(ControlPacket::USERDEFINEDTYPE, &SrtTransport::handleUserDefinedType); }); _now = SteadyClock::now(); - // 处理srt数据 + // 处理srt数据 [AUTO-TRANSLATED:b8b065b8] + // Handling SRT data if (DataPacket::isDataPacket(buf, len)) { uint32_t socketId = DataPacket::getSocketID(buf, len); if (socketId == _socket_id) { @@ -492,7 +495,8 @@ void SrtTransport::handleACKACK(uint8_t *buf, int len, struct sockaddr_storage * // clear data for(auto it = _ack_send_timestamp.begin(); it != _ack_send_timestamp.end();){ if(DurationCountMicroseconds(_now-it->second)>5e6){ - // 超过五秒没有ackack 丢弃 + // 超过五秒没有ackack 丢弃 [AUTO-TRANSLATED:9626442f] + // Discard if no ACK for more than five seconds it = _ack_send_timestamp.erase(it); }else{ it++; diff --git a/tests/DeviceHK/DeviceHK.cpp b/tests/DeviceHK/DeviceHK.cpp index 0aa51513..8f76247c 100644 --- a/tests/DeviceHK/DeviceHK.cpp +++ b/tests/DeviceHK/DeviceHK.cpp @@ -176,7 +176,8 @@ void DevChannelHK::onPreview(DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize) { break; } InfoL << "设置解码器成功!" << endl; - //打开音频解码, 需要码流是复合流 + // 打开音频解码, 需要码流是复合流 [AUTO-TRANSLATED:ef6be0e4] + // Open audio decoding, requires the bitstream to be a composite stream if (!PlayM4_PlaySoundShare(m_iPlayHandle)) { WarnL << "PlayM4_PlaySound:" << NET_DVR_GetLastError(); break; diff --git a/tests/bom.cpp b/tests/bom.cpp index eb6ee6f6..88724594 100644 --- a/tests/bom.cpp +++ b/tests/bom.cpp @@ -93,7 +93,8 @@ void process_file(const char *file,bool rm_bom){ } if (have_bom == !rm_bom) { -// DebugL << "无需" << (rm_bom ? "删除" : "添加") << "bom:" << file; +// DebugL << "无需" << (rm_bom ? "删除" : "添加") << "bom:" << file; [AUTO-TRANSLATED:6062a9ca] +// DebugL << "No need to" << (rm_bom ? "remove" : "add") << "bom:" << file; return; } @@ -102,7 +103,8 @@ void process_file(const char *file,bool rm_bom){ InfoL << (rm_bom ? "删除" : "添加") << "bom:" << file; } -/// 这个程序是为了统一添加或删除utf-8 bom头 +// / 这个程序是为了统一添加或删除utf-8 bom头 [AUTO-TRANSLATED:945a36b6] +// / This program is for unified adding or removing utf-8 bom header int main(int argc, char *argv[]) { CMD_main cmd_main; try { @@ -123,26 +125,31 @@ int main(int argc, char *argv[]) { } bool no_filter = filter_set.find("*") != filter_set.end(); - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared()); File::scanDir(path, [&](const string &path, bool isDir) { if (isDir) { return true; } if (!no_filter) { - //开启了过滤器 + // 开启了过滤器 [AUTO-TRANSLATED:331a77dd] + // Filter enabled auto pos = strstr(path.data(), "."); if (pos == nullptr) { - //没有后缀 + // 没有后缀 [AUTO-TRANSLATED:2273522f] + // No suffix return true; } auto ext = pos + 1; if (filter_set.find(ext) == filter_set.end()) { - //后缀不匹配 + // 后缀不匹配 [AUTO-TRANSLATED:7e30f0b4] + // Suffix does not match return true; } } - //该文件匹配 + // 该文件匹配 [AUTO-TRANSLATED:9dce5098] + // File matches process_file(path.data(), rm_bom); return true; }, true); diff --git a/tests/tab.cpp b/tests/tab.cpp index 49fb19c3..bb1e89da 100644 --- a/tests/tab.cpp +++ b/tests/tab.cpp @@ -107,7 +107,8 @@ void process_file(const char *file) { File::saveFile(str, file); } -/// 这个程序是为了统一替换tab为4个空格 +// / 这个程序是为了统一替换tab为4个空格 [AUTO-TRANSLATED:ecb3b523] +// / This program is for unified replacement of tabs with 4 spaces int main(int argc, char *argv[]) { CMD_main cmd_main; try { @@ -127,26 +128,31 @@ int main(int argc, char *argv[]) { } bool no_filter = filter_set.find("*") != filter_set.end(); - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared()); File::scanDir(path, [&](const string &path, bool isDir) { if (isDir) { return true; } if (!no_filter) { - //开启了过滤器 + // 开启了过滤器 [AUTO-TRANSLATED:331a77dd] + // Filter enabled auto pos = strstr(path.data(), "."); if (pos == nullptr) { - //没有后缀 + // 没有后缀 [AUTO-TRANSLATED:2273522f] + // No suffix return true; } auto ext = pos + 1; if (filter_set.find(ext) == filter_set.end()) { - //后缀不匹配 + // 后缀不匹配 [AUTO-TRANSLATED:7e30f0b4] + // Suffix does not match return true; } } - //该文件匹配 + // 该文件匹配 [AUTO-TRANSLATED:9dce5098] + // File matches process_file(path.data()); return true; }, true); diff --git a/tests/test_bench_forward.cpp b/tests/test_bench_forward.cpp index 20a3d2e5..daf816a7 100644 --- a/tests/test_bench_forward.cpp +++ b/tests/test_bench_forward.cpp @@ -99,7 +99,8 @@ public: }; -//此程序为zlm的转推性能测试工具,用于测试拉流代理转推性能 +// 此程序为zlm的转推性能测试工具,用于测试拉流代理转推性能 [AUTO-TRANSLATED:3d384f4f] +// This program is a performance testing tool for zlm's relay push, used to test the relay push performance of the pull stream agent int main(int argc, char *argv[]) { CMD_main cmd_main; try { @@ -120,16 +121,20 @@ int main(int argc, char *argv[]) { auto delay_ms = cmd_main["delay"].as(); auto merge_ms = cmd_main["merge"].as(); - //设置日志 + // 设置日志 [AUTO-TRANSLATED:b1bbb978] + // Set log Logger::Instance().add(std::make_shared("ConsoleChannel", logLevel)); - //启动异步日志线程 + // 启动异步日志线程 [AUTO-TRANSLATED:a3514cc7] + // Start asynchronous log thread Logger::Instance().setWriter(std::make_shared()); - //设置线程数 + // 设置线程数 [AUTO-TRANSLATED:cce432ca] + // Set the number of threads EventPollerPool::setPoolSize(threads); WorkThreadPool::setPoolSize(threads); - //设置合并写 + // 设置合并写 [AUTO-TRANSLATED:e3aaf4f8] + // Set merge write mINI::Instance()[General::kMergeWriteMS] = merge_ms; @@ -137,7 +142,8 @@ int main(int argc, char *argv[]) { std::vector output_urls; auto parse_urls = [&]() { - // 获取输入源列表 + // 获取输入源列表 [AUTO-TRANSLATED:ff8fdf28] + // Get input source list auto inputs = ::split(toolkit::File::loadFile(in_urls), "\n"); for(auto &url : inputs){ if(url.empty() || url.find("://") == std::string::npos) { @@ -146,7 +152,8 @@ int main(int argc, char *argv[]) { auto input_url = ::trim(url); input_urls.emplace_back(input_url); } - // 获取输出源列表 + // 获取输出源列表 [AUTO-TRANSLATED:267eba3a] + // Get output source list auto outputs = ::split(toolkit::File::loadFile(out_urls), "\n"); for(auto &url : outputs){ if(url.empty() || url.find("://") == std::string::npos){ @@ -171,7 +178,8 @@ int main(int argc, char *argv[]) { return -1; } - //推流器map + // 推流器map [AUTO-TRANSLATED:d6af6562] + // Pusher map recursive_mutex mtx; unordered_map proxy_map; unordered_map pusher_map; @@ -179,38 +187,48 @@ int main(int argc, char *argv[]) { auto add_pusher = [&](const MediaSource::Ptr &src, const string &url, int index) { auto pusher = std::make_shared(src); pusher->setOnCreateSocket([](const EventPoller::Ptr &poller) { - //socket关闭互斥锁,提高性能 + // socket关闭互斥锁,提高性能 [AUTO-TRANSLATED:d734e718] + // Socket close mutex, improve performance return Socket::createSocket(poller, false); }); - //设置推流失败监听 + // 设置推流失败监听 [AUTO-TRANSLATED:8e799d62] + // Set push failure listener pusher->setOnPublished([&mtx, &pusher_map, index](const SockException &ex) { if (ex) { - //推流失败,移除之 + // 推流失败,移除之 [AUTO-TRANSLATED:92440807] + // Push failure, remove it lock_guard lck(mtx); pusher_map.erase(index); } }); - //设置推流中途断开监听 + // 设置推流中途断开监听 [AUTO-TRANSLATED:b1cc165d] + // Set push midway disconnection listener pusher->setOnShutdown([&mtx, &pusher_map, index](const SockException &ex) { - //推流中途失败,移除之 + // 推流中途失败,移除之 [AUTO-TRANSLATED:9d79c581] + // Push midway failure, remove it lock_guard lck(mtx); pusher_map.erase(index); }); - //设置rtsp推流方式(在rtsp推流时有效) + // 设置rtsp推流方式(在rtsp推流时有效) [AUTO-TRANSLATED:92584646] + // Set RTSP push mode (effective when pushing RTSP) (*pusher)[Client::kRtpType] = rtp_type; pusher->publish(url); - //保持对象不销毁 + // 保持对象不销毁 [AUTO-TRANSLATED:43ddb698] + // Keep the object from being destroyed lock_guard lck(mtx); pusher_map.emplace(index, std::move(pusher)); - //休眠后再启动下一个推流,防止短时间海量链接 + // 休眠后再启动下一个推流,防止短时间海量链接 [AUTO-TRANSLATED:2f17b482] + // Sleep and then start the next push to prevent massive connections in a short time if (delay_ms > 0) { usleep(1000 * delay_ms); } }; - // 添加转推任务 + // 添加转推任务 [AUTO-TRANSLATED:122a8389] + // Add relay task for(size_t i = 0; i < input_urls.size(); i++) { - //休眠一秒打印 + // 休眠一秒打印 [AUTO-TRANSLATED:338a3b2c] + // Sleep for one second and print sleep(1); auto schema = findSubString(output_urls[i].data(), nullptr, "://"); if (schema != RTSP_SCHEMA && schema != RTMP_SCHEMA) { @@ -223,19 +241,23 @@ int main(int argc, char *argv[]) { option.enable_hls = false; option.enable_mp4 = false; option.modify_stamp = (int)ProtocolOption::kModifyStampRelative; - //添加拉流代理 + // 添加拉流代理 [AUTO-TRANSLATED:aa516f44] + // Add pull stream agent auto tuple = MediaTuple { DEFAULT_VHOST, "app", std::to_string(i), "" }; auto proxy = std::make_shared(tuple, option, -1, nullptr, 1); - //开始拉流代理 + // 开始拉流代理 [AUTO-TRANSLATED:c9fe3c34] + // Start pull stream agent proxy->play(input_urls[i]); proxy_map.emplace(i, std::move(proxy)); } - // 设置退出信号 + // 设置退出信号 [AUTO-TRANSLATED:4f618479] + // Set exit signal static bool exit_flag = false; signal(SIGINT, [](int) { exit_flag = true; }); while (!exit_flag) { - //休眠一秒打印 + // 休眠一秒打印 [AUTO-TRANSLATED:338a3b2c] + // Sleep for one second and print sleep(1); size_t alive_pusher = 0; diff --git a/tests/test_bench_proxy.cpp b/tests/test_bench_proxy.cpp index cea0c4be..65674617 100644 --- a/tests/test_bench_proxy.cpp +++ b/tests/test_bench_proxy.cpp @@ -91,7 +91,8 @@ public: } }; -//此程序为zlm的拉流代理性能测试工具,用于测试拉流代理性能 +// 此程序为zlm的拉流代理性能测试工具,用于测试拉流代理性能 [AUTO-TRANSLATED:365ee033] +// This program is a pull stream proxy performance test tool for zlm, used to test the pull stream proxy performance int main(int argc, char *argv[]) { { CMD_main cmd_main; @@ -114,16 +115,20 @@ int main(int argc, char *argv[]) { auto merge_ms = cmd_main["merge"].as(); auto demand = cmd_main["demand"].as(); - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared("ConsoleChannel", logLevel)); - //启动异步日志线程 + // 启动异步日志线程 [AUTO-TRANSLATED:c93cc6f4] + // Start asynchronous log thread Logger::Instance().setWriter(std::make_shared()); - //设置线程数 + // 设置线程数 [AUTO-TRANSLATED:22ec5cc9] + // Set the number of threads EventPollerPool::setPoolSize(threads); WorkThreadPool::setPoolSize(threads); - //设置合并写 + // 设置合并写 [AUTO-TRANSLATED:7bf3456d] + // Set merge write mINI::Instance()[General::kMergeWriteMS] = merge_ms; mINI::Instance()[Protocol::kRtspDemand] = demand; mINI::Instance()[Protocol::kRtmpDemand] = demand; @@ -142,7 +147,8 @@ int main(int argc, char *argv[]) { (*player)[Client::kRtpType] = rtp_type; player->play(in_url); proxyMap.emplace(stream, player); - //休眠后再启动下一个拉流代理,防止短时间海量链接 + // 休眠后再启动下一个拉流代理,防止短时间海量链接 [AUTO-TRANSLATED:20fc6ab9] + // Sleep before starting the next pull stream proxy to prevent a large number of connections in a short time if (delay_ms > 0) { usleep(1000 * delay_ms); } diff --git a/tests/test_bench_pull.cpp b/tests/test_bench_pull.cpp index dd455f8e..7911935b 100644 --- a/tests/test_bench_pull.cpp +++ b/tests/test_bench_pull.cpp @@ -85,7 +85,8 @@ public: } }; -//此程序用于拉流播放性能测试 +// 此程序用于拉流播放性能测试 [AUTO-TRANSLATED:727e8fdb] +// This program is used for pulling stream playback performance testing int main(int argc, char *argv[]) { CMD_main cmd_main; try { @@ -105,16 +106,20 @@ int main(int argc, char *argv[]) { auto delay_ms = cmd_main["delay"].as(); auto player_count = cmd_main["count"].as(); - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared("ConsoleChannel", logLevel)); - //启动异步日志线程 + // 启动异步日志线程 [AUTO-TRANSLATED:c93cc6f4] + // Start asynchronous log thread Logger::Instance().setWriter(std::make_shared()); - //设置线程数 + // 设置线程数 [AUTO-TRANSLATED:22ec5cc9] + // Set the number of threads EventPollerPool::setPoolSize(threads); WorkThreadPool::setPoolSize(threads); - //播放器map + // 播放器map [AUTO-TRANSLATED:53b5b4c4] + // Player map recursive_mutex mtx; unordered_map player_map; @@ -122,52 +127,66 @@ int main(int argc, char *argv[]) { auto player = std::make_shared(); auto tag = player.get(); player->setOnCreateSocket([](const EventPoller::Ptr &poller) { - //socket关闭互斥锁,提高性能 + // socket关闭互斥锁,提高性能 [AUTO-TRANSLATED:471fc644] + // Socket close mutex, improve performance return Socket::createSocket(poller, false); }); - //设置播放失败监听 + // 设置播放失败监听 [AUTO-TRANSLATED:14e18272] + // Set playback failure listener player->setOnPlayResult([&mtx, &player_map, tag](const SockException &ex) { if (ex) { - //播放失败,移除之 + // 播放失败,移除之 [AUTO-TRANSLATED:e3e1ce5e] + // Playback failed, remove it lock_guard lck(mtx); player_map.erase(tag); } }); - //设置播放中途断开监听 + // 设置播放中途断开监听 [AUTO-TRANSLATED:b1aa9531] + // Set playback interruption listener player->setOnShutdown([&mtx, &player_map, tag](const SockException &ex) { - //播放中途失败,移除之 + // 播放中途失败,移除之 [AUTO-TRANSLATED:5cd7df25] + // Playback interrupted, remove it lock_guard lck(mtx); player_map.erase(tag); }); - //设置为性能测试模式 + // 设置为性能测试模式 [AUTO-TRANSLATED:96c16cc1] + // Set to performance test mode (*player)[Client::kBenchmarkMode] = true; - //设置rtsp拉流方式(在rtsp拉流时有效) + // 设置rtsp拉流方式(在rtsp拉流时有效) [AUTO-TRANSLATED:521f7bbd] + // Set RTSP pull mode (effective when pulling RTSP stream) (*player)[Client::kRtpType] = rtp_type; - //提高压测性能与正确性 + // 提高压测性能与正确性 [AUTO-TRANSLATED:947f0ef8] + // Improve stress test performance and accuracy (*player)[Client::kWaitTrackReady] = false; - //发起播放请求 + // 发起播放请求 [AUTO-TRANSLATED:43e28453] + // Initiate playback request player->play(in_url); - //保持对象不销毁 + // 保持对象不销毁 [AUTO-TRANSLATED:650977d0] + // Keep the object from being destroyed lock_guard lck(mtx); player_map.emplace(tag, std::move(player)); - //休眠后再启动下一个播放,防止短时间海量链接 + // 休眠后再启动下一个播放,防止短时间海量链接 [AUTO-TRANSLATED:96c03767] + // Sleep and then start the next playback to prevent massive connections in a short time if (delay_ms > 0) { usleep(1000 * delay_ms); } }; - //添加这么多播放器 + // 添加这么多播放器 [AUTO-TRANSLATED:b93d3a9f] + // Add so many players for (auto i = 0; i < player_count; ++i) { add_player(); } - // 设置退出信号 + // 设置退出信号 [AUTO-TRANSLATED:02c7fa30] + // Set exit signal static bool exit_flag = false; signal(SIGINT, [](int) { exit_flag = true; }); while (!exit_flag) { - //休眠一秒打印 + // 休眠一秒打印 [AUTO-TRANSLATED:239dc996] + // Sleep for one second and print sleep(1); size_t alive_player = 0; @@ -178,7 +197,8 @@ int main(int argc, char *argv[]) { InfoL << "在线播放器个数:" << alive_player; size_t re_try = player_count - alive_player; while (!exit_flag && re_try--) { - //有些播放器播放失败了,那么我们重试添加 + // 有些播放器播放失败了,那么我们重试添加 [AUTO-TRANSLATED:adcec1af] + // Some players failed to play, so we retry adding them add_player(); } } diff --git a/tests/test_bench_push.cpp b/tests/test_bench_push.cpp index 317b0cdd..7e6c93e9 100644 --- a/tests/test_bench_push.cpp +++ b/tests/test_bench_push.cpp @@ -101,7 +101,8 @@ public: const char *description() const override { return "主程序命令参数"; } }; -// 此程序用于推流性能测试 +// 此程序用于推流性能测试 [AUTO-TRANSLATED:45b48457] +// This program is used for streaming performance testing int main(int argc, char *argv[]) { CMD_main cmd_main; try { @@ -130,16 +131,20 @@ int main(int argc, char *argv[]) { const std::string app = "app"; const std::string stream = "test"; - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared("ConsoleChannel", logLevel)); - //启动异步日志线程 + // 启动异步日志线程 [AUTO-TRANSLATED:c93cc6f4] + // Start asynchronous log thread Logger::Instance().setWriter(std::make_shared()); - //设置线程数 + // 设置线程数 [AUTO-TRANSLATED:22ec5cc9] + // Set the number of threads EventPollerPool::setPoolSize(threads); WorkThreadPool::setPoolSize(threads); - //设置合并写 + // 设置合并写 [AUTO-TRANSLATED:7bf3456d] + // Set merge write mINI::Instance()[General::kMergeWriteMS] = merge_ms; ProtocolOption option; @@ -156,16 +161,20 @@ int main(int argc, char *argv[]) { reader->startReadMP4(0, true, true); src = MediaSource::find(schema, DEFAULT_VHOST, app, stream, false); if (!src) { - // mp4文件不存在 + // mp4文件不存在 [AUTO-TRANSLATED:80188fb8] + // mp4 file does not exist WarnL << "no such file or directory: " << in_url; return -1; } } else { - //添加拉流代理 + // 添加拉流代理 [AUTO-TRANSLATED:aa516f44] + // Add pull stream proxy proxy = std::make_shared(tuple, option); - //rtsp拉流代理方式 + // rtsp拉流代理方式 [AUTO-TRANSLATED:065d328d] + // rtsp pull stream proxy method (*proxy)[Client::kRtpType] = rtp_type; - //开始拉流代理 + // 开始拉流代理 [AUTO-TRANSLATED:6937338d] + // Start pull stream proxy proxy->play(in_url); } @@ -174,7 +183,8 @@ int main(int argc, char *argv[]) { return MediaSource::find(schema, DEFAULT_VHOST, app, stream, false); }; - //推流器map + // 推流器map [AUTO-TRANSLATED:279fcfb0] + // Streamer map recursive_mutex mtx; unordered_map pusher_map; @@ -183,44 +193,55 @@ int main(int argc, char *argv[]) { auto pusher = std::make_shared(src); auto tag = pusher.get(); pusher->setOnCreateSocket([](const EventPoller::Ptr &poller) { - //socket关闭互斥锁,提高性能 + // socket关闭互斥锁,提高性能 [AUTO-TRANSLATED:471fc644] + // Socket close mutex, improve performance return Socket::createSocket(poller, false); }); - //设置推流失败监听 + // 设置推流失败监听 [AUTO-TRANSLATED:4ad49de9] + // Set push stream failure listener pusher->setOnPublished([&mtx, &pusher_map, tag](const SockException &ex) { if (ex) { - //推流失败,移除之 + // 推流失败,移除之 [AUTO-TRANSLATED:97a29246] + // Push stream failed, remove it lock_guard lck(mtx); pusher_map.erase(tag); } }); - //设置推流中途断开监听 + // 设置推流中途断开监听 [AUTO-TRANSLATED:228076ef] + // Set push stream disconnection listener pusher->setOnShutdown([&mtx, &pusher_map, tag](const SockException &ex) { - //推流中途失败,移除之 + // 推流中途失败,移除之 [AUTO-TRANSLATED:00e0928a] + // Push stream failed halfway, remove it lock_guard lck(mtx); pusher_map.erase(tag); }); - //设置rtsp推流方式(在rtsp推流时有效) + // 设置rtsp推流方式(在rtsp推流时有效) [AUTO-TRANSLATED:2dc733df] + // Set rtsp push stream method (effective when rtsp push stream) (*pusher)[Client::kRtpType] = rtp_type; - //发起推流请求,每个推流端的stream_id都不一样 + // 发起推流请求,每个推流端的stream_id都不一样 [AUTO-TRANSLATED:8b356fcb] + // Initiate push stream request, each push stream end has a different stream_id string url = StrPrinter << out_url << "_" << rand_str << "_" << index; pusher->publish(url); - //保持对象不销毁 + // 保持对象不销毁 [AUTO-TRANSLATED:650977d0] + // Keep the object from being destroyed lock_guard lck(mtx); pusher_map.emplace(tag, std::move(pusher)); - //休眠后再启动下一个推流,防止短时间海量链接 + // 休眠后再启动下一个推流,防止短时间海量链接 [AUTO-TRANSLATED:df224fc9] + // Sleep and then start the next push stream to prevent massive connections in a short time if (delay_ms > 0) { usleep(1000 * delay_ms); } }; - // 设置退出信号 + // 设置退出信号 [AUTO-TRANSLATED:02c7fa30] + // Set exit signal static bool exit_flag = false; signal(SIGINT, [](int) { exit_flag = true; }); while (!exit_flag) { - //休眠一秒打印 + // 休眠一秒打印 [AUTO-TRANSLATED:239dc996] + // Sleep for one second and print sleep(1); size_t alive_pusher = 0; @@ -231,7 +252,8 @@ int main(int argc, char *argv[]) { InfoL << "在线推流器个数:" << alive_pusher; auto src = get_src(); for(size_t i = 0; i < pusher_count - alive_pusher && src && !exit_flag; ++i){ - //有些推流器失败了,那么我们重试添加 + // 有些推流器失败了,那么我们重试添加 [AUTO-TRANSLATED:d01fb300] + // Some push streamers failed, so we retry adding add_pusher(get_src(), makeRandStr(8), i); } } diff --git a/tests/test_flv.cpp b/tests/test_flv.cpp index cf838822..c0bd09e0 100644 --- a/tests/test_flv.cpp +++ b/tests/test_flv.cpp @@ -90,9 +90,11 @@ static bool loadFile(const char *path){ } int main(int argc,char *argv[]) { - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared("ConsoleChannel")); - //启动异步日志线程 + // 启动异步日志线程 [AUTO-TRANSLATED:c93cc6f4] + // Start asynchronous log thread Logger::Instance().setWriter(std::make_shared()); loadIniConfig((exeDir() + "config.ini").data()); TcpServer::Ptr rtspSrv(new TcpServer()); diff --git a/tests/test_httpApi.cpp b/tests/test_httpApi.cpp index 5fd1ea4a..b6cfaf07 100644 --- a/tests/test_httpApi.cpp +++ b/tests/test_httpApi.cpp @@ -25,7 +25,8 @@ using namespace toolkit; using namespace mediakit; namespace mediakit { -////////////HTTP配置/////////// +// //////////HTTP配置/////////// [AUTO-TRANSLATED:a281d694] +// //////////HTTP Configuration/////////// namespace Http { #define HTTP_FIELD "http." #define HTTP_PORT 80 @@ -46,7 +47,8 @@ void initEventListener(){ if(strstr(parser.url().data(),"/api/") != parser.url().data()){ return; } - //url以"/api/起始,说明是http api" + // url以"/api/起始,说明是http api" [AUTO-TRANSLATED:8af96c7e] + // URLs starting with "/api/" indicate HTTP API consumed = true;//该http请求已被消费 _StrPrinter printer; @@ -70,13 +72,18 @@ void initEventListener(){ printer << "\r\ncontent:\r\n" << parser.content(); auto contentOut = printer << endl; - ////////////////我们测算异步回复,当然你也可以同步回复///////////////// + // //////////////我们测算异步回复,当然你也可以同步回复///////////////// [AUTO-TRANSLATED:5c112e50] + // //////////////We measure asynchronous responses, but you can also respond synchronously///////////////// EventPollerPool::Instance().getPoller()->async([invoker,contentOut](){ HttpSession::KeyValue headerOut; - //你可以自定义header,如果跟默认header重名,则会覆盖之 - //默认header有:Server,Connection,Date,Content-Type,Content-Length - //请勿覆盖Connection、Content-Length键 - //键名覆盖时不区分大小写 + // 你可以自定义header,如果跟默认header重名,则会覆盖之 [AUTO-TRANSLATED:07b1ecfe] + // You can customize the header; if it has the same name as the default header, it will override it + // 默认header有:Server,Connection,Date,Content-Type,Content-Length [AUTO-TRANSLATED:ca0c35d2] + // Default headers include: Server, Connection, Date, Content-Type, Content-Length + // 请勿覆盖Connection、Content-Length键 [AUTO-TRANSLATED:ef188768] + // Please do not override the Connection and Content-Length keys + // 键名覆盖时不区分大小写 [AUTO-TRANSLATED:32147753] + // Key name overrides are case-insensitive headerOut["TestHeader"] = "HeaderValue"; invoker(200,headerOut,contentOut); }); @@ -85,31 +92,39 @@ void initEventListener(){ } int main(int argc,char *argv[]){ - //设置退出信号处理函数 + // 设置退出信号处理函数 [AUTO-TRANSLATED:4f047770] + // Set the exit signal processing function static semaphore sem; signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set the log Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); - //加载配置文件,如果配置文件不存在就创建一个 + // 加载配置文件,如果配置文件不存在就创建一个 [AUTO-TRANSLATED:761e7479] + // Load the configuration file; if it does not exist, create one loadIniConfig(); initEventListener(); - //加载证书,证书包含公钥和私钥 + // 加载证书,证书包含公钥和私钥 [AUTO-TRANSLATED:fce78641] + // Load the certificate, which includes the public and private keys SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data()); - //信任某个自签名证书 + // 信任某个自签名证书 [AUTO-TRANSLATED:6815fc55] + // Trust a self-signed certificate SSL_Initor::Instance().trustCertificate((exeDir() + "ssl.p12").data()); - //不忽略无效证书证书(例如自签名或过期证书) + // 不忽略无效证书证书(例如自签名或过期证书) [AUTO-TRANSLATED:ee4a34c4] + // Do not ignore invalid certificates (e.g., self-signed or expired certificates) SSL_Initor::Instance().ignoreInvalidCertificate(false); - //开启http服务器 + // 开启http服务器 [AUTO-TRANSLATED:ffab30d4] + // Start the HTTP server TcpServer::Ptr httpSrv(new TcpServer()); httpSrv->start(mINI::Instance()[Http::kPort]);//默认80 - //如果支持ssl,还可以开启https服务器 + // 如果支持ssl,还可以开启https服务器 [AUTO-TRANSLATED:8ef29f9c] + // If SSL is supported, you can also start the HTTPS server TcpServer::Ptr httpsSrv(new TcpServer()); httpsSrv->start(mINI::Instance()[Http::kSSLPort]);//默认443 diff --git a/tests/test_httpClient.cpp b/tests/test_httpClient.cpp index 4040b22d..508f80e6 100644 --- a/tests/test_httpClient.cpp +++ b/tests/test_httpClient.cpp @@ -23,65 +23,84 @@ using namespace toolkit; using namespace mediakit; int main(int argc, char *argv[]) { - //设置退出信号处理函数 + // 设置退出信号处理函数 [AUTO-TRANSLATED:4f047770] + // Set the exit signal processing function static semaphore sem; signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set the log Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); - //加载证书,证书包含公钥和私钥 + // 加载证书,证书包含公钥和私钥 [AUTO-TRANSLATED:fce78641] + // Load the certificate, the certificate contains the public key and private key SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data()); - //信任某个自签名证书 + // 信任某个自签名证书 [AUTO-TRANSLATED:6815fc55] + // Trust a self-signed certificate SSL_Initor::Instance().trustCertificate((exeDir() + "ssl.p12").data()); - //不忽略无效证书证书(例如自签名或过期证书) + // 不忽略无效证书证书(例如自签名或过期证书) [AUTO-TRANSLATED:ee4a34c4] + // Do not ignore invalid certificates (such as self-signed or expired certificates) SSL_Initor::Instance().ignoreInvalidCertificate(false); ///////////////////////////////http downloader/////////////////////// - //下载器map + // 下载器map [AUTO-TRANSLATED:d8583648] + // Downloader map map downloaderMap; - //下载两个文件,一个是http下载,一个https下载 + // 下载两个文件,一个是http下载,一个https下载 [AUTO-TRANSLATED:d6108dc2] + // Download two files, one is HTTP download, and the other is HTTPS download auto urlList = {"http://www.baidu.com/img/baidu_resultlogo@2.png", "https://www.baidu.com/img/baidu_resultlogo@2.png"}; for (auto &url : urlList) { - //创建下载器 + // 创建下载器 [AUTO-TRANSLATED:e67aa738] + // Create a downloader HttpDownloader::Ptr downloader(new HttpDownloader()); downloader->setOnResult([](const SockException &ex, const string &filePath) { DebugL << "=====================HttpDownloader result======================="; - //下载结果回调 + // 下载结果回调 [AUTO-TRANSLATED:a7f68894] + // Download result callback if (!ex) { - //文件下载成功 + // 文件下载成功 [AUTO-TRANSLATED:d753c6d3] + // File download successful InfoL << "download file success:" << filePath; } else { - //下载失败 + // 下载失败 [AUTO-TRANSLATED:b5ee74cc] + // Download failed WarnL << "code:" << ex.getErrCode() << " msg:" << ex.what(); } }); - //断点续传功能,开启后可能会遇到416的错误(因为上次文件已经下载完全) + // 断点续传功能,开启后可能会遇到416的错误(因为上次文件已经下载完全) [AUTO-TRANSLATED:d4feaecb] + // Resume function, enabling it may encounter a 416 error (because the file was downloaded completely last time) downloader->startDownload(url, exeDir() + MD5(url).hexdigest() + ".jpg", true); - //下载器必须被强引用,否则作用域一失效就会导致对象销毁 + // 下载器必须被强引用,否则作用域一失效就会导致对象销毁 [AUTO-TRANSLATED:16e93227] + // The downloader must be strongly referenced, otherwise, it will be destroyed when the scope is invalid downloaderMap.emplace(url, downloader); } ///////////////////////////////http get/////////////////////// - //创建一个Http请求器 + // 创建一个Http请求器 [AUTO-TRANSLATED:0d451bc1] + // Create an HTTP requestor HttpRequester::Ptr requesterGet(new HttpRequester()); - //使用GET方式请求 + // 使用GET方式请求 [AUTO-TRANSLATED:3f701c92] + // Use the GET method to request requesterGet->setMethod("GET"); - //设置http请求头,我们假设设置cookie,当然你也可以设置其他http头 + // 设置http请求头,我们假设设置cookie,当然你也可以设置其他http头 [AUTO-TRANSLATED:233d2c3f] + // Set the HTTP request header, we assume setting the cookie, of course, you can also set other HTTP headers requesterGet->addHeader("Cookie", "SESSIONID=e1aa89b3-f79f-4ac6-8ae2-0cea9ae8e2d7"); - //开启请求,该api会返回当前主机外网ip等信息 + // 开启请求,该api会返回当前主机外网ip等信息 [AUTO-TRANSLATED:efebf262] + // Start the request, this API will return the current host's external network IP and other information requesterGet->startRequester("http://pv.sohu.com/cityjson?ie=utf-8",//url地址 [](const SockException &ex, //网络相关的失败信息,如果为空就代表成功 const Parser &parser) { //http回复body DebugL << "=====================HttpRequester GET==========================="; if (ex) { - //网络相关的错误 + // 网络相关的错误 [AUTO-TRANSLATED:ae96dbe9] + // Network-related errors WarnL << "network err:" << ex.getErrCode() << " " << ex.what(); } else { - //打印http回复信息 + // 打印http回复信息 [AUTO-TRANSLATED:d4611ce2] + // Print HTTP response information _StrPrinter printer; for (auto &pr: parser.getHeader()) { printer << pr.first << ":" << pr.second << "\r\n"; @@ -93,14 +112,18 @@ int main(int argc, char *argv[]) { }); ///////////////////////////////http post/////////////////////// - //创建一个Http请求器 + // 创建一个Http请求器 [AUTO-TRANSLATED:0d451bc1] + // Create an HTTP requestor HttpRequester::Ptr requesterPost(new HttpRequester()); - //使用POST方式请求 + // 使用POST方式请求 [AUTO-TRANSLATED:dc6266f1] + // Use the POST method to request requesterPost->setMethod("POST"); - //设置http请求头 + // 设置http请求头 [AUTO-TRANSLATED:d934a806] + // Set the HTTP request header requesterPost->addHeader("X-Requested-With", "XMLHttpRequest"); requesterPost->addHeader("Origin", "http://fanyi.baidu.com"); - //设置POST参数列表 + // 设置POST参数列表 [AUTO-TRANSLATED:5378230d] + // Set the POST parameter list HttpArgs args; args["query"] = "test"; args["from"] = "en"; @@ -108,16 +131,19 @@ int main(int argc, char *argv[]) { args["transtype"] = "translang"; args["simple_means_flag"] = "3"; requesterPost->setBody(args.make()); - //开启请求 + // 开启请求 [AUTO-TRANSLATED:ccb4bc7f] + // Start the request requesterPost->startRequester("http://fanyi.baidu.com/langdetect",//url地址 [](const SockException &ex, //网络相关的失败信息,如果为空就代表成功 const Parser &parser) { //http回复body DebugL << "=====================HttpRequester POST=========================="; if (ex) { - //网络相关的错误 + // 网络相关的错误 [AUTO-TRANSLATED:ae96dbe9] + // Network-related errors WarnL << "network err:" << ex.getErrCode() << " " << ex.what(); } else { - //打印http回复信息 + // 打印http回复信息 [AUTO-TRANSLATED:d4611ce2] + // Print HTTP response information _StrPrinter printer; for (auto &pr: parser.getHeader()) { printer << pr.first << ":" << pr.second << "\r\n"; @@ -129,11 +155,14 @@ int main(int argc, char *argv[]) { }); ///////////////////////////////http upload/////////////////////// - //创建一个Http请求器 + // 创建一个Http请求器 [AUTO-TRANSLATED:0d451bc1] + // Create an HTTP requestor HttpRequester::Ptr requesterUploader(new HttpRequester()); - //使用POST方式请求 + // 使用POST方式请求 [AUTO-TRANSLATED:dc6266f1] + // Use the POST method to request requesterUploader->setMethod("POST"); - //设置http请求头 + // 设置http请求头 [AUTO-TRANSLATED:d934a806] + // Set the HTTP request header HttpArgs argsUploader; argsUploader["query"] = "test"; argsUploader["from"] = "en"; @@ -145,16 +174,19 @@ int main(int argc, char *argv[]) { HttpMultiFormBody::Ptr body(new HttpMultiFormBody(argsUploader, exePath(), boundary)); requesterUploader->setBody(body); requesterUploader->addHeader("Content-Type", HttpMultiFormBody::multiFormContentType(boundary)); - //开启请求 + // 开启请求 [AUTO-TRANSLATED:ccb4bc7f] + // Start the request requesterUploader->startRequester("http://fanyi.baidu.com/langdetect",//url地址 [](const SockException &ex, //网络相关的失败信息,如果为空就代表成功 const Parser &parser) { //http回复body DebugL << "=====================HttpRequester Uploader=========================="; if (ex) { - //网络相关的错误 + // 网络相关的错误 [AUTO-TRANSLATED:ae96dbe9] + // Network-related errors WarnL << "network err:" << ex.getErrCode() << " " << ex.what(); } else { - //打印http回复信息 + // 打印http回复信息 [AUTO-TRANSLATED:d4611ce2] + // Print HTTP response information _StrPrinter printer; for (auto &pr: parser.getHeader()) { printer << pr.first << ":" << pr.second << "\r\n"; diff --git a/tests/test_pusher.cpp b/tests/test_pusher.cpp index fd368950..33f5c4b8 100644 --- a/tests/test_pusher.cpp +++ b/tests/test_pusher.cpp @@ -22,30 +22,39 @@ using namespace std; using namespace toolkit; using namespace mediakit; -//推流器,保持强引用 +// 推流器,保持强引用 [AUTO-TRANSLATED:b2287a61] +// Streamer, keep a strong reference MediaPusher::Ptr pusher; Timer::Ptr g_timer; -//声明函数 +// 声明函数 [AUTO-TRANSLATED:f3911a32] +// Declare function void rePushDelay(const EventPoller::Ptr &poller,const string &schema,const string &vhost,const string &app, const string &stream, const string &url); -//创建推流器并开始推流 +// 创建推流器并开始推流 [AUTO-TRANSLATED:583100b5] +// Create a streamer and start streaming void createPusher(const EventPoller::Ptr &poller, const string &schema,const string &vhost,const string &app, const string &stream, const string &url) { - //创建推流器并绑定一个MediaSource + // 创建推流器并绑定一个MediaSource [AUTO-TRANSLATED:b0721d46] + // Create a streamer and bind a MediaSource pusher.reset(new MediaPusher(schema,vhost, app, stream,poller)); - //可以指定rtsp推流方式,支持tcp和udp方式,默认tcp + // 可以指定rtsp推流方式,支持tcp和udp方式,默认tcp [AUTO-TRANSLATED:bb0be012] + // You can specify the RTSP streaming method, supporting both TCP and UDP methods, defaulting to TCP // (*pusher)[Client::kRtpType] = Rtsp::RTP_UDP; - //设置推流中断处理逻辑 + // 设置推流中断处理逻辑 [AUTO-TRANSLATED:aa6c0405] + // Set the streaming interruption handling logic pusher->setOnShutdown([poller,schema,vhost, app, stream, url](const SockException &ex) { WarnL << "Server connection is closed:" << ex.getErrCode() << " " << ex.what(); - //重试 + // 重试 [AUTO-TRANSLATED:d96b4814] + // Retry rePushDelay(poller,schema,vhost,app, stream, url); }); - //设置发布结果处理逻辑 + // 设置发布结果处理逻辑 [AUTO-TRANSLATED:0ee98a13] + // Set the publishing result handling logic pusher->setOnPublished([poller,schema,vhost, app, stream, url](const SockException &ex) { if (ex) { WarnL << "Publish fail:" << ex.getErrCode() << " " << ex.what(); - //如果发布失败,就重试 + // 如果发布失败,就重试 [AUTO-TRANSLATED:67aff5bd] + // If publishing fails, retry rePushDelay(poller,schema,vhost,app, stream, url); } else { InfoL << "Publish success,Please play with player:" << url; @@ -54,26 +63,33 @@ void createPusher(const EventPoller::Ptr &poller, const string &schema,const str pusher->publish(url); } -//推流失败或断开延迟2秒后重试推流 +// 推流失败或断开延迟2秒后重试推流 [AUTO-TRANSLATED:bc496634] +// If streaming fails or is disconnected, retry streaming after a 2-second delay void rePushDelay(const EventPoller::Ptr &poller,const string &schema,const string &vhost,const string &app, const string &stream, const string &url) { g_timer = std::make_shared(2.0f,[poller,schema,vhost,app, stream, url]() { InfoL << "Re-Publishing..."; - //重新推流 + // 重新推流 [AUTO-TRANSLATED:edb1e699] + // Re-stream createPusher(poller,schema,vhost,app, stream, url); - //此任务不重复 + // 此任务不重复 [AUTO-TRANSLATED:84deec34] + // This task is not repeated return false; }, poller); } -//这里才是真正执行main函数,你可以把函数名(domain)改成main,然后就可以输入自定义url了 +// 这里才是真正执行main函数,你可以把函数名(domain)改成main,然后就可以输入自定义url了 [AUTO-TRANSLATED:a441f1a2] +// This is where the main function is actually executed, you can change the function name (domain) to main, and then you can enter a custom URL int domain(const string &playUrl, const string &pushUrl) { - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set the log Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); auto poller = EventPollerPool::Instance().getPoller(); - //拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream" - //你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请查看test_rtmpPusherMp4.cpp代码) + // 拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream" [AUTO-TRANSLATED:38b8ab6f] + // Pull a stream and generate an RtmpMediaSource, the source name is "app/stream" + // 你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请查看test_rtmpPusherMp4.cpp代码) [AUTO-TRANSLATED:c94c5914] + // You can also generate RtmpMediaSource in other ways, such as an MP4 file (please refer to the test_rtmpPusherMp4.cpp code) MediaInfo info(pushUrl); ProtocolOption option; @@ -81,21 +97,25 @@ int domain(const string &playUrl, const string &pushUrl) { option.enable_mp4 = false; auto tuple = MediaTuple{DEFAULT_VHOST, "app", "stream", ""}; PlayerProxy::Ptr player(new PlayerProxy(tuple, option, -1, poller)); - //可以指定rtsp拉流方式,支持tcp和udp方式,默认tcp + // 可以指定rtsp拉流方式,支持tcp和udp方式,默认tcp [AUTO-TRANSLATED:5169e341] + // You can specify the RTSP streaming method, supporting both TCP and UDP methods, defaulting to TCP // (*player)[Client::kRtpType] = Rtsp::RTP_UDP; player->play(playUrl.data()); - //监听RtmpMediaSource注册事件,在PlayerProxy播放成功后触发 + // 监听RtmpMediaSource注册事件,在PlayerProxy播放成功后触发 [AUTO-TRANSLATED:da417dbe] + // Listen for RtmpMediaSource registration events, triggered after PlayerProxy playback is successful NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaChanged, [pushUrl,poller](BroadcastMediaChangedArgs) { - //媒体源"app/stream"已经注册,这时方可新建一个RtmpPusher对象并绑定该媒体源 + // 媒体源"app/stream"已经注册,这时方可新建一个RtmpPusher对象并绑定该媒体源 [AUTO-TRANSLATED:3670cbfd] + // The media source "app/stream" has been registered, at this point you can create a new RtmpPusher object and bind it to the media source if (bRegist && pushUrl.find(sender.getSchema()) == 0) { auto tuple = sender.getMediaTuple(); createPusher(poller, sender.getSchema(), tuple.vhost, tuple.app, tuple.stream, pushUrl); } }); - //设置退出信号处理函数 + // 设置退出信号处理函数 [AUTO-TRANSLATED:4f047770] + // Set the exit signal processing function static semaphore sem; signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 sem.wait(); diff --git a/tests/test_pusherMp4.cpp b/tests/test_pusherMp4.cpp index 28ee2f99..e2f9c7c5 100644 --- a/tests/test_pusherMp4.cpp +++ b/tests/test_pusherMp4.cpp @@ -21,13 +21,16 @@ using namespace std; using namespace toolkit; using namespace mediakit; -// 这里才是真正执行main函数,你可以把函数名(domain)改成main,然后就可以输入自定义url了 +// 这里才是真正执行main函数,你可以把函数名(domain)改成main,然后就可以输入自定义url了 [AUTO-TRANSLATED:8438b572] +// This is where the main function is actually executed, you can change the function name (domain) to main, and then you can enter a custom URL int domain(const string &file, const string &url) { - // 设置日志 + // 设置日志 [AUTO-TRANSLATED:50ba02ba] + // Set log Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); - // 关闭所有转协议 + // 关闭所有转协议 [AUTO-TRANSLATED:2a58bc8f] + // Close all protocol conversions mINI::Instance()[Protocol::kEnableMP4] = 0; mINI::Instance()[Protocol::kEnableFMP4] = 0; mINI::Instance()[Protocol::kEnableHls] = 0; @@ -36,47 +39,59 @@ int domain(const string &file, const string &url) { mINI::Instance()[Protocol::kEnableRtsp] = 0; mINI::Instance()[Protocol::kEnableRtmp] = 0; - // 根据url获取媒体协议类型,注意大小写 + // 根据url获取媒体协议类型,注意大小写 [AUTO-TRANSLATED:3cd6622a] + // Get the media protocol type based on the URL, note the case auto schema = strToLower(findSubString(url.data(), nullptr, "://").substr(0, 4)); - // 只开启推流协议对应的转协议 + // 只开启推流协议对应的转协议 [AUTO-TRANSLATED:1c4975ae] + // Only enable the protocol conversion corresponding to the push protocol mINI::Instance()["protocol.enable_" + schema] = 1; - // 从mp4文件加载生成MediaSource对象 + // 从mp4文件加载生成MediaSource对象 [AUTO-TRANSLATED:5e5b04ca] + // Load the MediaSource object from the mp4 file auto tuple = MediaTuple {DEFAULT_VHOST, "live", "stream", ""}; auto reader = std::make_shared(tuple, file); - // 开始加载mp4,ref_self设置为false,这样reader对象设置为nullptr就能注销了,file_repeat可以设置为空,这样文件读完了就停止推流了 + // 开始加载mp4,ref_self设置为false,这样reader对象设置为nullptr就能注销了,file_repeat可以设置为空,这样文件读完了就停止推流了 [AUTO-TRANSLATED:88a5c8ae] + // Start loading the mp4, set ref_self to false, so that the reader object can be set to nullptr to cancel, file_repeat can be set to empty, so that the file is read and the push stream is stopped reader->startReadMP4(100, false, true); auto src = MediaSource::find(schema, DEFAULT_VHOST, "live", "stream", false); if (!src) { - // 文件不存在 + // 文件不存在 [AUTO-TRANSLATED:f97c8ce8] + // File does not exist WarnL << "File not existed: " << file; return -1; } - // 选择一个poller线程绑定 + // 选择一个poller线程绑定 [AUTO-TRANSLATED:82d4a318] + // Select a poller thread to bind auto poller = EventPollerPool::Instance().getPoller(); - // 创建推流器并绑定一个MediaSource + // 创建推流器并绑定一个MediaSource [AUTO-TRANSLATED:c1ab71ca] + // Create a pusher and bind a MediaSource auto pusher = std::make_shared(src, poller); std::weak_ptr weak_src = src; - // src用完了,可以直接置空,防止main函数持有它(MP4Reader持有它即可) + // src用完了,可以直接置空,防止main函数持有它(MP4Reader持有它即可) [AUTO-TRANSLATED:52e6393a] + // src is used up, can be set to empty directly, to prevent the main function from holding it (MP4Reader holds it) src = nullptr; - // 可以指定rtsp推流方式,支持tcp和udp方式,默认tcp + // 可以指定rtsp推流方式,支持tcp和udp方式,默认tcp [AUTO-TRANSLATED:235480a7] + // You can specify the rtsp push method, support tcp and udp methods, default tcp //(*pusher)[Client::kRtpType] = Rtsp::RTP_UDP; - // 设置推流中断处理逻辑 + // 设置推流中断处理逻辑 [AUTO-TRANSLATED:76774d30] + // Set the interrupt handling logic during pushing std::weak_ptr weak_pusher = pusher; pusher->setOnShutdown([poller, url, weak_pusher, weak_src](const SockException &ex) { if (!weak_src.lock()) { - // 媒体注销导致的推流中断,不在重试推流 + // 媒体注销导致的推流中断,不在重试推流 [AUTO-TRANSLATED:625b3e1a] + // Media cancellation causes push interruption, no retry push WarnL << "MediaSource released:" << ex << ", publish stopped"; return; } WarnL << "Server connection is closed:" << ex << ", republish after 2 seconds"; - // 重新推流, 2秒后重试 + // 重新推流, 2秒后重试 [AUTO-TRANSLATED:f8a261a3] + // Repush, retry after 2 seconds poller->doDelayTask(2 * 1000, [weak_pusher, url]() { if (auto strong_push = weak_pusher.lock()) { strong_push->publish(url); @@ -85,7 +100,8 @@ int domain(const string &file, const string &url) { }); }); - // 设置发布结果处理逻辑 + // 设置发布结果处理逻辑 [AUTO-TRANSLATED:ce9de055] + // Set the publish result handling logic pusher->setOnPublished([poller, weak_pusher, url](const SockException &ex) { if (!ex) { InfoL << "Publish success, please play with player:" << url; @@ -93,7 +109,8 @@ int domain(const string &file, const string &url) { } WarnL << "Publish fail:" << ex << ", republish after 2 seconds"; - // 如果发布失败,就重试 + // 如果发布失败,就重试 [AUTO-TRANSLATED:b37fd4aa] + // If the publish fails, retry poller->doDelayTask(2 * 1000, [weak_pusher, url]() { if (auto strong_push = weak_pusher.lock()) { strong_push->publish(url); @@ -104,10 +121,12 @@ int domain(const string &file, const string &url) { pusher->publish(url); // sleep(5); - // reader 置空可以终止推流相关资源 + // reader 置空可以终止推流相关资源 [AUTO-TRANSLATED:02442070] + // reader set to empty can terminate the push-related resources // reader = nullptr; - // 设置退出信号处理函数 + // 设置退出信号处理函数 [AUTO-TRANSLATED:69e8f733] + // Set the exit signal processing function static semaphore sem; signal(SIGINT, [](int) { sem.post(); }); // 设置退出信号 sem.wait(); @@ -115,8 +134,10 @@ int domain(const string &file, const string &url) { } int main(int argc, char *argv[]) { - // 可以使用test_server生成的mp4文件 - // 文件使用绝对路径,推流url支持rtsp和rtmp + // 可以使用test_server生成的mp4文件 [AUTO-TRANSLATED:a9552282] + // You can use the mp4 file generated by test_server + // 文件使用绝对路径,推流url支持rtsp和rtmp [AUTO-TRANSLATED:efc8c3b5] + // File uses absolute path, push URL supports rtsp and rtmp // return domain("/Users/xiongziliang/Downloads/mp4/Quantum.mp4", "rtsp://127.0.0.1/live/rtsp_push"); return domain(argv[1], argv[2]); } diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index bf9167e6..6bd8d8fb 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -52,7 +52,8 @@ static bool loadFile(const char *path, const EventPoller::Ptr &poller) { while (true) { if (2 != fread(&len, 1, 2, fp.get())) { WarnL << "Read rtp size failed"; - // 重新播放 + // 重新播放 [AUTO-TRANSLATED:9f678d55] + // Replay fseek(fp.get(), 0, SEEK_SET); return 1; } @@ -98,10 +99,12 @@ static bool loadFile(const char *path, const EventPoller::Ptr &poller) { #endif // #if defined(ENABLE_RTPPROXY) int main(int argc, char *argv[]) { - // 设置日志 + // 设置日志 [AUTO-TRANSLATED:50ba02ba] + // Set log Logger::Instance().add(std::make_shared("ConsoleChannel")); #if defined(ENABLE_RTPPROXY) - // 启动异步日志线程 + // 启动异步日志线程 [AUTO-TRANSLATED:8340d047] + // Start asynchronous log thread Logger::Instance().setWriter(std::make_shared()); loadIniConfig((exeDir() + "config.ini").data()); TcpServer::Ptr rtspSrv(new TcpServer()); @@ -110,7 +113,8 @@ int main(int argc, char *argv[]) { rtspSrv->start(554); // 默认554 rtmpSrv->start(1935); // 默认1935 httpSrv->start(80); // 默认80 - // 此处选择是否导出调试文件 + // 此处选择是否导出调试文件 [AUTO-TRANSLATED:b147c25b] + // Choose whether to export debug file here // mINI::Instance()[RtpProxy::kDumpDir] = "/Users/xzl/Desktop/"; if (argc == 2) { diff --git a/tests/test_server.cpp b/tests/test_server.cpp index 0389d9ab..1b2ec5c8 100644 --- a/tests/test_server.cpp +++ b/tests/test_server.cpp @@ -33,7 +33,8 @@ using namespace toolkit; using namespace mediakit; namespace mediakit { -////////////HTTP配置/////////// +// //////////HTTP配置/////////// [AUTO-TRANSLATED:a281d694] +// //////////HTTP Configuration/////////// namespace Http { #define HTTP_FIELD "http." #define HTTP_PORT 80 @@ -46,7 +47,8 @@ onceToken token1([](){ },nullptr); }//namespace Http -////////////SHELL配置/////////// +// //////////SHELL配置/////////// [AUTO-TRANSLATED:f023ec45] +// //////////SHELL Configuration/////////// namespace Shell { #define SHELL_FIELD "shell." #define SHELL_PORT 9000 @@ -56,7 +58,8 @@ onceToken token1([](){ },nullptr); } //namespace Shell -////////////RTSP服务器配置/////////// +// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981] +// //////////RTSP Server Configuration/////////// namespace Rtsp { #define RTSP_FIELD "rtsp." #define RTSP_PORT 554 @@ -70,7 +73,8 @@ onceToken token1([](){ } //namespace Rtsp -////////////RTMP服务器配置/////////// +// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f] +// //////////RTMP Server Configuration/////////// namespace Rtmp { #define RTMP_FIELD "rtmp." #define RTMP_PORT 1935 @@ -88,72 +92,91 @@ static mutex s_mtxFlvRecorder; void initEventListener() { static onceToken s_token([]() { - //监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问 + // 监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问 [AUTO-TRANSLATED:cf05f5a1] + // Listen for kBroadcastOnGetRtspRealm event to decide if rtsp link needs authentication (traditional rtsp authentication scheme) to access NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastOnGetRtspRealm, [](BroadcastOnGetRtspRealmArgs) { DebugL << "RTSP是否需要鉴权事件:" << args.getUrl() << " " << args.params; if (string("1") == args.stream) { - // live/1需要认证 - //该流需要认证,并且设置realm + // live/1需要认证 [AUTO-TRANSLATED:c3885602] + // live/1 needs authentication + // 该流需要认证,并且设置realm [AUTO-TRANSLATED:624f8df5] + // This stream needs authentication and sets realm invoker(REALM); } else { - //有时我们要查询redis或数据库来判断该流是否需要认证,通过invoker的方式可以做到完全异步 - //该流我们不需要认证 + // 有时我们要查询redis或数据库来判断该流是否需要认证,通过invoker的方式可以做到完全异步 [AUTO-TRANSLATED:41d2819d] + // Sometimes we need to query redis or database to determine if the stream needs authentication, which can be done asynchronously through invoker + // 该流我们不需要认证 [AUTO-TRANSLATED:67866699] + // This stream does not need authentication invoker(""); } }); - //监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码 + // 监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码 [AUTO-TRANSLATED:3d135a03] + // Listen for kBroadcastOnRtspAuth event to return the correct rtsp authentication username and password NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastOnRtspAuth, [](BroadcastOnRtspAuthArgs) { DebugL << "RTSP播放鉴权:" << args.getUrl() << " " << args.params; DebugL << "RTSP用户:" << user_name << (must_no_encrypt ? " Base64" : " MD5") << " 方式登录"; string user = user_name; - //假设我们异步读取数据库 + // 假设我们异步读取数据库 [AUTO-TRANSLATED:38545bdf] + // Assuming we read the database asynchronously if (user == "test0") { - //假设数据库保存的是明文 + // 假设数据库保存的是明文 [AUTO-TRANSLATED:731d594a] + // Assuming the database stores plaintext invoker(false, "pwd0"); return; } if (user == "test1") { - //假设数据库保存的是密文 + // 假设数据库保存的是密文 [AUTO-TRANSLATED:293ae3d5] + // Assuming the database stores ciphertext auto encrypted_pwd = MD5(user + ":" + REALM + ":" + "pwd1").hexdigest(); invoker(true, encrypted_pwd); return; } if (user == "test2" && must_no_encrypt) { - //假设登录的是test2,并且以base64方式登录,此时我们提供加密密码,那么会导致认证失败 - //可以通过这个方式屏蔽base64这种不安全的加密方式 + // 假设登录的是test2,并且以base64方式登录,此时我们提供加密密码,那么会导致认证失败 [AUTO-TRANSLATED:e2b29961] + // Assuming login is test2 and login in base64 format, in this case we provide encrypted password, which will cause authentication failure + // 可以通过这个方式屏蔽base64这种不安全的加密方式 [AUTO-TRANSLATED:f303f620] + // You can shield this insecure encryption method in this way invoker(true, "pwd2"); return; } - //其他用户密码跟用户名一致 + // 其他用户密码跟用户名一致 [AUTO-TRANSLATED:53aa89d9] + // Other user passwords are the same as usernames invoker(false, user); }); - //监听rtsp/rtmp推流事件,返回结果告知是否有推流权限 + // 监听rtsp/rtmp推流事件,返回结果告知是否有推流权限 [AUTO-TRANSLATED:6c16b087] + // Listen for rtsp/rtmp push stream event, return result to inform whether there is push stream permission NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaPublish, [](BroadcastMediaPublishArgs) { DebugL << "推流鉴权:" << args.getUrl() << " " << args.params; invoker("", ProtocolOption());//鉴权成功 - //invoker("this is auth failed message");//鉴权失败 + // invoker("this is auth failed message");//鉴权失败 [AUTO-TRANSLATED:4131434d] + // invoker("this is auth failed message");//Authentication failed }); - //监听rtsp/rtsps/rtmp/http-flv播放事件,返回结果告知是否有播放权限(rtsp通过kBroadcastOnRtspAuth或此事件都可以实现鉴权) + // 监听rtsp/rtsps/rtmp/http-flv播放事件,返回结果告知是否有播放权限(rtsp通过kBroadcastOnRtspAuth或此事件都可以实现鉴权) [AUTO-TRANSLATED:94fed0b4] + // Listen for rtsp/rtsps/rtmp/http-flv playback event, return result to inform whether there is playback permission (rtsp can implement authentication through kBroadcastOnRtspAuth or this event) NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaPlayed, [](BroadcastMediaPlayedArgs) { DebugL << "播放鉴权:" << args.getUrl() << " " << args.params; invoker("");//鉴权成功 - //invoker("this is auth failed message");//鉴权失败 + // invoker("this is auth failed message");//鉴权失败 [AUTO-TRANSLATED:4131434d] + // invoker("this is auth failed message");//Authentication failed }); - //shell登录事件,通过shell可以登录进服务器执行一些命令 + // shell登录事件,通过shell可以登录进服务器执行一些命令 [AUTO-TRANSLATED:a60f6d9f] + // Shell login event, you can log in to the server through shell to execute some commands NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastShellLogin, [](BroadcastShellLoginArgs) { DebugL << "shell login:" << user_name << " " << passwd; invoker("");//鉴权成功 - //invoker("this is auth failed message");//鉴权失败 + // invoker("this is auth failed message");//鉴权失败 [AUTO-TRANSLATED:4131434d] + // invoker("this is auth failed message");//Authentication failed }); - //监听rtsp、rtmp源注册或注销事件;此处用于测试rtmp保存为flv录像,保存在http根目录下 + // 监听rtsp、rtmp源注册或注销事件;此处用于测试rtmp保存为flv录像,保存在http根目录下 [AUTO-TRANSLATED:c572252d] + // Listen for rtsp/rtmp source registration or cancellation event; this is used to test rtmp saving as flv recording, saved in the http root directory NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaChanged, [](BroadcastMediaChangedArgs) { auto tuple = sender.getMediaTuple(); if (sender.getSchema() == RTMP_SCHEMA && tuple.app == "live") { @@ -177,17 +200,23 @@ void initEventListener() { } }); - //监听播放失败(未找到特定的流)事件 + // 监听播放失败(未找到特定的流)事件 [AUTO-TRANSLATED:eeac767c] + // Listen for playback failure (stream not found) event NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastNotFoundStream, [](BroadcastNotFoundStreamArgs) { /** * 你可以在这个事件触发时再去拉流,这样就可以实现按需拉流 * 拉流成功后,ZLMediaKit会把其立即转发给播放器(最大等待时间约为5秒,如果5秒都未拉流成功,播放器会播放失败) + * You can pull the stream again when this event is triggered, which can implement on-demand pulling + * After pulling the stream successfully, ZLMediaKit will immediately forward it to the player (the maximum waiting time is about 5 seconds, if it still fails to pull the stream within 5 seconds, the player will play failure) + + * [AUTO-TRANSLATED:5a01351c] */ DebugL << "未找到流事件:" << args.getUrl() << " " << args.params; }); - //监听播放或推流结束时消耗流量事件 + // 监听播放或推流结束时消耗流量事件 [AUTO-TRANSLATED:f87ac02d] + // Listen for playback or push stream end event to consume traffic NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastFlowReport, [](BroadcastFlowReportArgs) { DebugL << "播放器(推流器)断开连接事件:" << args.getUrl() << " " << args.params << "\r\n使用流量:" << totalBytes << " bytes,连接时长:" << totalDuration << "秒"; @@ -202,41 +231,56 @@ void initEventListener() { #endif int main(int argc,char *argv[]) { - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared()); Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); - //加载配置文件,如果配置文件不存在就创建一个 + // 加载配置文件,如果配置文件不存在就创建一个 [AUTO-TRANSLATED:761e7479] + // Load configuration file, if the configuration file does not exist, create one loadIniConfig(); initEventListener(); - //这里是拉流地址,支持rtmp/rtsp协议,负载必须是H264+AAC - //如果是其他不识别的音视频将会被忽略(譬如说h264+adpcm转发后会去除音频) + // 这里是拉流地址,支持rtmp/rtsp协议,负载必须是H264+AAC [AUTO-TRANSLATED:3f92b7aa] + // This is the pull stream address, supports rtmp/rtsp protocol, and the payload must be H264+AAC + // 如果是其他不识别的音视频将会被忽略(譬如说h264+adpcm转发后会去除音频) [AUTO-TRANSLATED:ad4be3b6] + // If it is other unrecognized audio and video, it will be ignored (for example, h264+adpcm will remove audio after forwarding) auto urlList = {"rtsp://admin:admin123@192.168.1.64:554/cam/realmonitor?channel=1&subtype=1" - //rtsp链接支持输入用户名密码 + // rtsp链接支持输入用户名密码 [AUTO-TRANSLATED:a0e2ebfa] + // rtsp link supports inputting username and password /*"rtsp://admin:jzan123456@192.168.0.122/"*/}; map proxyMap; int i = 0; for (auto &url : urlList) { - //PlayerProxy构造函数前两个参数分别为应用名(app),流id(streamId) - //比如说应用为live,流id为0,那么直播地址为: + // PlayerProxy构造函数前两个参数分别为应用名(app),流id(streamId) [AUTO-TRANSLATED:5b793e17] + // PlayerProxy constructor's first two parameters are application name (app) and stream id (streamId) + // 比如说应用为live,流id为0,那么直播地址为: [AUTO-TRANSLATED:d1f0844c] + // For example, if the application is live and the stream id is 0, the live address is: - //hls地址 : http://127.0.0.1/live/0/hls.m3u8 - //http-flv地址 : http://127.0.0.1/live/0.flv - //rtsp地址 : rtsp://127.0.0.1/live/0 - //rtmp地址 : rtmp://127.0.0.1/live/0 + // hls地址 : http://127.0.0.1/live/0/hls.m3u8 [AUTO-TRANSLATED:0a684e23] + // hls address: http://127.0.0.1/live/0/hls.m3u8 + // http-flv地址 : http://127.0.0.1/live/0.flv [AUTO-TRANSLATED:ffdf3994] + // http-flv address: http://127.0.0.1/live/0.flv + // rtsp地址 : rtsp://127.0.0.1/live/0 [AUTO-TRANSLATED:784f37f6] + // rtsp address: rtsp://127.0.0.1/live/0 + // rtmp地址 : rtmp://127.0.0.1/live/0 [AUTO-TRANSLATED:6f377de8] + // rtmp address: rtmp://127.0.0.1/live/0 - //录像地址为(当然vlc不支持这么多级的rtmp url,可以用test_player测试rtmp点播): + // 录像地址为(当然vlc不支持这么多级的rtmp url,可以用test_player测试rtmp点播): [AUTO-TRANSLATED:2e45d9a5] + // The recorded address is (of course, vlc does not support so many levels of rtmp url, you can use test_player to test rtmp on-demand): //http://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 auto tuple = MediaTuple{DEFAULT_VHOST, "live", std::string("chn") + to_string(i).data(), ""}; PlayerProxy::Ptr player(new PlayerProxy(tuple, ProtocolOption())); - //指定RTP over TCP(播放rtsp时有效) + // 指定RTP over TCP(播放rtsp时有效) [AUTO-TRANSLATED:1a062656] + // Specify RTP over TCP (effective when playing rtsp) (*player)[Client::kRtpType] = Rtsp::RTP_TCP; - //开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试 + // 开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试 [AUTO-TRANSLATED:c9ad670c] + // Start playing. If playback fails or is interrupted, it will automatically retry several times. The number of retries is configured in the configuration file, and the default is to retry indefinitely player->play(url); - //需要保存PlayerProxy,否则作用域结束就会销毁该对象 + // 需要保存PlayerProxy,否则作用域结束就会销毁该对象 [AUTO-TRANSLATED:939a84c9] + // You need to save PlayerProxy, otherwise the object will be destroyed when the scope ends proxyMap.emplace(to_string(i), player); ++i; } @@ -249,11 +293,14 @@ int main(int argc,char *argv[]) { " rtsp地址 : rtsp://127.0.0.1/live/0\n" " rtmp地址 : rtmp://127.0.0.1/live/0"; - //加载证书,证书包含公钥和私钥 + // 加载证书,证书包含公钥和私钥 [AUTO-TRANSLATED:fce78641] + // Load the certificate, which contains the public key and private key SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data()); - //信任某个自签名证书 + // 信任某个自签名证书 [AUTO-TRANSLATED:6815fc55] + // Trust a self-signed certificate SSL_Initor::Instance().trustCertificate((exeDir() + "ssl.p12").data()); - //不忽略无效证书证书(例如自签名或过期证书) + // 不忽略无效证书证书(例如自签名或过期证书) [AUTO-TRANSLATED:ee4a34c4] + // Do not ignore invalid certificates (such as self-signed or expired certificates) SSL_Initor::Instance().ignoreInvalidCertificate(false); uint16_t shellPort = mINI::Instance()[Shell::kPort]; @@ -263,8 +310,10 @@ int main(int argc,char *argv[]) { uint16_t httpPort = mINI::Instance()[Http::kPort]; uint16_t httpsPort = mINI::Instance()[Http::kSSLPort]; - //简单的telnet服务器,可用于服务器调试,但是不能使用23端口,否则telnet上了莫名其妙的现象 - //测试方法:telnet 127.0.0.1 9000 + // 简单的telnet服务器,可用于服务器调试,但是不能使用23端口,否则telnet上了莫名其妙的现象 [AUTO-TRANSLATED:f9324c6e] + // A simple telnet server, which 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 TcpServer::Ptr shellSrv(new TcpServer()); TcpServer::Ptr rtspSrv(new TcpServer()); TcpServer::Ptr rtmpSrv(new TcpServer()); @@ -273,21 +322,27 @@ int main(int argc,char *argv[]) { shellSrv->start(shellPort); rtspSrv->start(rtspPort);//默认554 rtmpSrv->start(rtmpPort);//默认1935 - //http服务器 + // http服务器 [AUTO-TRANSLATED:10d47f45] + // http server httpSrv->start(httpPort);//默认80 - //如果支持ssl,还可以开启https服务器 + // 如果支持ssl,还可以开启https服务器 [AUTO-TRANSLATED:8ef29f9c] + // If ssl is supported, you can also enable the https server TcpServer::Ptr httpsSrv(new TcpServer()); - //https服务器 + // https服务器 [AUTO-TRANSLATED:a2bb64bd] + // https server httpsSrv->start(httpsPort);//默认443 - //支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 + // 支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 [AUTO-TRANSLATED:ded79b73] + // rtsp server that supports ssl encryption, which can be used for devices such as Amazon Echo Show to access TcpServer::Ptr rtspSSLSrv(new TcpServer()); rtspSSLSrv->start(rtspsPort);//默认322 - //服务器支持动态切换端口(不影响现有连接) + // 服务器支持动态切换端口(不影响现有连接) [AUTO-TRANSLATED:28949b78] + // The server supports dynamic port switching (without affecting existing connections) NoticeCenter::Instance().addListener(ReloadConfigTag,Broadcast::kBroadcastReloadConfig,[&](BroadcastReloadConfigArgs){ - //重新创建服务器 + // 重新创建服务器 [AUTO-TRANSLATED:4e192357] + // Recreate the server if(shellPort != mINI::Instance()[Shell::kPort].as()){ shellPort = mINI::Instance()[Shell::kPort]; shellSrv->start(shellPort); @@ -321,7 +376,8 @@ int main(int argc,char *argv[]) { } }); - //设置退出信号处理函数 + // 设置退出信号处理函数 [AUTO-TRANSLATED:4f047770] + // Set the exit signal processing function static semaphore sem; signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 signal(SIGHUP, [](int) { loadIniConfig(); }); diff --git a/tests/test_sortor.cpp b/tests/test_sortor.cpp index 0d29dfbd..e811b3c1 100644 --- a/tests/test_sortor.cpp +++ b/tests/test_sortor.cpp @@ -18,7 +18,8 @@ using namespace std; using namespace mediakit; void test_real() { - //这个是一次真实的rtp seq记录 + // 这个是一次真实的rtp seq记录 [AUTO-TRANSLATED:a0cbaeff] + // This is a real rtp seq record list input_list = {15125, 15126, 15127, 15128, 15129, 15130, 15131, 15132, 15133, 15134, 15135, 15136, 15137, 15138, 15139, 15140, 15141, 15142, 15143, 15144, 15145, 15146, 15147, 15148, 15149, 15150, 15151, 15152, 15153, 15154, 15155, 15156, 15157, 15158, 15159, 15160, @@ -113,7 +114,8 @@ void test_real() { cout << "输入数据个数:" << input_list.size() << " 抖动缓冲区大小:" << sortor.getJitterSize(); - //清空缓存 + // 清空缓存 [AUTO-TRANSLATED:a7d8287a] + // Clear cache sortor.flush(); cout << " 输出数据个数:" << sorted_list.size() << endl; @@ -156,12 +158,14 @@ void test_rand(){ }); for (int i = 0; i < 1000;) { - //模拟乱序,count是连续倒序次数,最多连续乱序8次 + // 模拟乱序,count是连续倒序次数,最多连续乱序8次 [AUTO-TRANSLATED:76bd8e43] + // Simulate out-of-order, count is the number of consecutive reverse orders, up to 8 consecutive out-of-orders int count = 1 + rand() % 10; for (int j = i + count; j >= i; --j) { #if 1 - //模拟回环 + // 模拟回环 [AUTO-TRANSLATED:c865877b] + // Simulate loopback uint16_t seq = 0xFFFF - 500 + j; #else uint16_t seq = j; @@ -170,29 +174,35 @@ void test_rand(){ #if 1 int input = 0; if (seq != 0xFFFF && seq != 0) { - //模拟回环时丢包 + // 模拟回环时丢包 [AUTO-TRANSLATED:b7a709cd] + // Simulate packet loss during loopback if (seq % (1 + rand() % 100) == 0) { - //模拟重复,概率5%左右 + // 模拟重复,概率5%左右 [AUTO-TRANSLATED:f096bdf1] + // Simulate duplication, about 5% probability sortor.sortPacket(seq, seq); input_list.push_back(seq); ++input; } if (seq % (1 + rand() % 100) != 0) { - //模拟丢包,概率5%左右 + // 模拟丢包,概率5%左右 [AUTO-TRANSLATED:91a54869] + // Simulate packet loss, about 5% probability sortor.sortPacket(seq, seq); input_list.push_back(seq); ++input; } } switch (input) { - //输入0次,丢包 + // 输入0次,丢包 [AUTO-TRANSLATED:fb462a30] + // Input 0 times, packet loss case 0 : drop_list.push_back(seq); break; - //输入1次,正常 + // 输入1次,正常 [AUTO-TRANSLATED:ec648d0a] + // Input 1 time, normal case 1 : break; - //输入2+次,重复包 + // 输入2+次,重复包 [AUTO-TRANSLATED:fbc6549f] + // Input 2+ times, duplicate packets default: repeat_list.push_back(seq); break; @@ -209,7 +219,8 @@ void test_rand(){ << " 丢包个数:" << drop_list.size() << " 重复包个数:" << repeat_list.size(); - //清空缓存 + // 清空缓存 [AUTO-TRANSLATED:a7d8287a] + // Clear cache sortor.flush(); cout << " 输出数据个数:" << sorted_list.size() << endl; @@ -269,13 +280,16 @@ void test_rand(){ #endif } -//该测试程序用于检验rtp排序算法的正确性 +// 该测试程序用于检验rtp排序算法的正确性 [AUTO-TRANSLATED:251b9c45] +// This test program is used to verify the correctness of the rtp sorting algorithm int main(int argc, char *argv[]) { - //测试真实的rtp seq + // 测试真实的rtp seq [AUTO-TRANSLATED:d87b1d7a] + // Test real rtp seq cout << "###### 真实的rtp seq #####" << endl; test_real(); - //模拟rtp乱序、回环、丢包、重复情况 + // 模拟rtp乱序、回环、丢包、重复情况 [AUTO-TRANSLATED:cc92ba9d] + // Simulate rtp out-of-order, loopback, packet loss, and duplication scenarios cout << "###### 模拟的rtp seq #####" << endl; test_rand(); return 0; diff --git a/tests/test_wsClient.cpp b/tests/test_wsClient.cpp index d240bce3..df15fe41 100644 --- a/tests/test_wsClient.cpp +++ b/tests/test_wsClient.cpp @@ -31,32 +31,38 @@ protected: void onRecv(const Buffer::Ptr &pBuf) override { DebugL << pBuf->toString(); } - //被动断开连接回调 + // 被动断开连接回调 [AUTO-TRANSLATED:037fc69f] + // Passive disconnection callback void onError(const SockException &ex) override { WarnL << ex; } - //tcp连接成功后每2秒触发一次该事件 + // tcp连接成功后每2秒触发一次该事件 [AUTO-TRANSLATED:4bcf65bf] + // Triggered every 2 seconds after a successful TCP connection void onManager() override { SockSender::send("echo test!"); DebugL << "send echo test"; } - //连接服务器结果回调 + // 连接服务器结果回调 [AUTO-TRANSLATED:b8306897] + // Server connection result callback void onConnect(const SockException &ex) override{ DebugL << ex; } - //数据全部发送完毕后回调 + // 数据全部发送完毕后回调 [AUTO-TRANSLATED:f927c4c9] + // Callback after all data has been sent void onFlush() override{ DebugL; } }; int main(int argc, char *argv[]) { - //设置退出信号处理函数 + // 设置退出信号处理函数 [AUTO-TRANSLATED:4f047770] + // Set exit signal processing function static semaphore sem; signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); diff --git a/tests/test_wsServer.cpp b/tests/test_wsServer.cpp index 2f4d8493..e2db2bc1 100644 --- a/tests/test_wsServer.cpp +++ b/tests/test_wsServer.cpp @@ -21,6 +21,9 @@ using namespace mediakit; /** * 回显会话 + * Echo Session + + * [AUTO-TRANSLATED:bc2a4e9e] */ class EchoSession : public Session { public: @@ -35,14 +38,16 @@ public: DebugL << getIdentifier() << " " << Session::getIdentifier(); } void onRecv(const Buffer::Ptr &buffer) override { - //回显数据 + // 回显数据 [AUTO-TRANSLATED:36769402] + // Echo Data SockSender::send("from EchoSession:"); send(buffer); } void onError(const SockException &err) override{ WarnL << err.what(); } - //每隔一段时间触发,用来做超时管理 + // 每隔一段时间触发,用来做超时管理 [AUTO-TRANSLATED:823ffe1f] + // Triggered at regular intervals, used for timeout management void onManager() override{ DebugL; } @@ -62,14 +67,16 @@ public: DebugL << getIdentifier() << " " << Session::getIdentifier(); } void onRecv(const Buffer::Ptr &buffer) override { - //回显数据 + // 回显数据 [AUTO-TRANSLATED:36769402] + // Echo Data SockSender::send("from EchoSessionWithUrl:"); send(buffer); } void onError(const SockException &err) override{ WarnL << err.what(); } - //每隔一段时间触发,用来做超时管理 + // 每隔一段时间触发,用来做超时管理 [AUTO-TRANSLATED:823ffe1f] + // Triggered at regular intervals, used for timeout management void onManager() override{ DebugL; } @@ -78,13 +85,18 @@ public: /** * 此对象可以根据websocket 客户端访问的url选择创建不同的对象 + * This object can create different objects based on the URL accessed by the WebSocket client + + * [AUTO-TRANSLATED:57f51c96] */ struct EchoSessionCreator { - //返回的Session必须派生于SendInterceptor,可以返回null(拒绝连接) + // 返回的Session必须派生于SendInterceptor,可以返回null(拒绝连接) [AUTO-TRANSLATED:014d6a8a] + // The returned Session must inherit from SendInterceptor, can return null (refuse connection) Session::Ptr operator()(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock, mediakit::WebSocketHeader::Type &type) { // return nullptr; if (header.url() == "/") { - // 可以指定传输方式 + // 可以指定传输方式 [AUTO-TRANSLATED:81ddc417] + // Transport method can be specified // type = mediakit::WebSocketHeader::BINARY; return std::make_shared >(header, parent, pSock); } @@ -93,7 +105,8 @@ struct EchoSessionCreator { }; int main(int argc, char *argv[]) { - //设置日志 + // 设置日志 [AUTO-TRANSLATED:50372045] + // Set log Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); @@ -101,21 +114,25 @@ int main(int argc, char *argv[]) { { TcpServer::Ptr httpSrv(new TcpServer()); - //http服务器,支持websocket + // http服务器,支持websocket [AUTO-TRANSLATED:74a2bdb0] + // HTTP server, supports WebSocket httpSrv->start >(80);//默认80 TcpServer::Ptr httpsSrv(new TcpServer()); - //https服务器,支持websocket + // https服务器,支持websocket [AUTO-TRANSLATED:bc268bb9] + // HTTPS server, supports WebSocket httpsSrv->start >(443);//默认443 TcpServer::Ptr httpSrvOld(new TcpServer()); - //兼容之前的代码(但是不支持根据url选择生成Session类型) + // 兼容之前的代码(但是不支持根据url选择生成Session类型) [AUTO-TRANSLATED:d14395bd] + // Compatible with previous code (but does not support generating Session type based on URL) httpSrvOld->start >(8080); DebugL << "请打开网页:http://www.websocket-test.com/,进行测试"; DebugL << "连接 ws://127.0.0.1/xxxx,ws://127.0.0.1/ 测试的效果将不同,支持根据url选择不同的处理逻辑"; - //设置退出信号处理函数 + // 设置退出信号处理函数 [AUTO-TRANSLATED:4f047770] + // Set exit signal processing function static semaphore sem; signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 sem.wait(); diff --git a/webrtc/Nack.cpp b/webrtc/Nack.cpp index 525d03ca..91231239 100644 --- a/webrtc/Nack.cpp +++ b/webrtc/Nack.cpp @@ -17,25 +17,35 @@ using namespace toolkit; namespace mediakit { -// RTC配置项目 +// RTC配置项目 [AUTO-TRANSLATED:19940011] +// RTC configuration project namespace Rtc { #define RTC_FIELD "rtc." -//~ nack接收端, rtp发送端 -// rtp重发缓存列队最大长度,单位毫秒 +// ~ nack接收端, rtp发送端 [AUTO-TRANSLATED:e1065811] +// ~ nack receiver, rtp sender +// rtp重发缓存列队最大长度,单位毫秒 [AUTO-TRANSLATED:80bccd26] +// rtp retransmission cache queue maximum length, in milliseconds const string kMaxRtpCacheMS = RTC_FIELD "maxRtpCacheMS"; -// rtp重发缓存列队最大长度,单位个数 +// rtp重发缓存列队最大长度,单位个数 [AUTO-TRANSLATED:7d710bf5] +// rtp retransmission cache queue maximum length, in number const string kMaxRtpCacheSize = RTC_FIELD "maxRtpCacheSize"; -//~ nack发送端,rtp接收端 -//最大保留的rtp丢包状态个数 +// ~ nack发送端,rtp接收端 [AUTO-TRANSLATED:bb169205] +// ~ nack sender, rtp receiver +// 最大保留的rtp丢包状态个数 [AUTO-TRANSLATED:3aeb53f8] +// Maximum number of rtp packet loss states to keep const string kNackMaxSize = RTC_FIELD "nackMaxSize"; -// rtp丢包状态最长保留时间 +// rtp丢包状态最长保留时间 [AUTO-TRANSLATED:f9306375] +// Maximum retention time for rtp packet loss state const string kNackMaxMS = RTC_FIELD "nackMaxMS"; -// nack最多请求重传次数 +// nack最多请求重传次数 [AUTO-TRANSLATED:300be0d0] +// Maximum number of nack retransmission requests const string kNackMaxCount = RTC_FIELD "nackMaxCount"; -// nack重传频率,rtt的倍数 +// nack重传频率,rtt的倍数 [AUTO-TRANSLATED:924d53d2] +// Nack retransmission frequency, multiple of rtt const string kNackIntervalRatio = RTC_FIELD "nackIntervalRatio"; -// nack包中rtp个数,减小此值可以让nack包响应更灵敏 +// nack包中rtp个数,减小此值可以让nack包响应更灵敏 [AUTO-TRANSLATED:12393868] +// Number of rtp in nack packet, reducing this value can make nack packet response more sensitive const string kNackRtpSize = RTC_FIELD "nackRtpSize"; static onceToken token([]() { @@ -54,22 +64,26 @@ void NackList::pushBack(RtpPacket::Ptr rtp) { GET_CONFIG(uint32_t, max_rtp_cache_ms, Rtc::kMaxRtpCacheMS); GET_CONFIG(uint32_t, max_rtp_cache_size, Rtc::kMaxRtpCacheSize); - // 记录rtp + // 记录rtp [AUTO-TRANSLATED:f08e12e2] + // Record rtp auto seq = rtp->getSeq(); _nack_cache_seq.emplace_back(seq); _nack_cache_pkt.emplace(seq, std::move(rtp)); - // 限制rtp缓存最大个数 + // 限制rtp缓存最大个数 [AUTO-TRANSLATED:a6bb50f5] + // Limit the maximum number of rtp cache if (_nack_cache_seq.size() > max_rtp_cache_size) { popFront(); } if (++_cache_ms_check < 100) { - // 每100个rtp包检测下缓存长度,节省cpu资源 + // 每100个rtp包检测下缓存长度,节省cpu资源 [AUTO-TRANSLATED:6399c705] + // Check the cache length every 100 rtp packets to save cpu resources return; } _cache_ms_check = 0; - // 限制rtp缓存最大时长 + // 限制rtp缓存最大时长 [AUTO-TRANSLATED:83e5be93] + // Limit the maximum duration of rtp cache while (getCacheMS() >= max_rtp_cache_ms) { popFront(); } @@ -79,7 +93,8 @@ void NackList::forEach(const FCI_NACK &nack, const function= front_stamp) { return back_stamp - front_stamp; } - // ntp时间戳回退了,非法数据,丢掉 + // ntp时间戳回退了,非法数据,丢掉 [AUTO-TRANSLATED:79ddf252] + // Ntp timestamp has been rolled back, illegal data, discard _nack_cache_seq.pop_front(); } return 0; @@ -133,7 +149,8 @@ int64_t NackList::getNtpStamp(uint16_t seq) { if (it == _nack_cache_pkt.end()) { return -1; } - // 使用ntp时间戳,不会回退 + // 使用ntp时间戳,不会回退 [AUTO-TRANSLATED:2d509f8f] + // Use ntp timestamp, will not roll back return it->second->getStampMS(true); } @@ -145,27 +162,31 @@ NackContext::NackContext() { void NackContext::received(uint16_t seq, bool is_rtx) { if (!_started) { - // 记录第一个seq + // 记录第一个seq [AUTO-TRANSLATED:410c831f] + // Record the first seq _started = true; _nack_seq = seq - 1; } if (seq < _nack_seq && _nack_seq != UINT16_MAX && seq < 1024 && _nack_seq > UINT16_MAX - 1024) { - // seq回环,清空回环前状态 + // seq回环,清空回环前状态 [AUTO-TRANSLATED:4cb8027e] + // Seq loop, clear the state before the loop makeNack(UINT16_MAX, true); _seq.emplace(seq); return; } if (is_rtx || (seq < _nack_seq && _nack_seq != UINT16_MAX)) { - // seq非回环回退包,猜测其为重传包,清空其nack状态 + // seq非回环回退包,猜测其为重传包,清空其nack状态 [AUTO-TRANSLATED:74c7b706] + // Seq non-loop rollback packet, guess it is a retransmission packet, clear its nack state clearNackStatus(seq); return; } auto pr = _seq.emplace(seq); if (!pr.second) { - // seq重复, 忽略 + // seq重复, 忽略 [AUTO-TRANSLATED:95ec10db] + // Seq duplicate, ignore return; } @@ -173,33 +194,40 @@ void NackContext::received(uint16_t seq, bool is_rtx) { auto min_seq = *_seq.begin(); auto diff = max_seq - min_seq; if (diff > (UINT16_MAX >> 1)) { - // 回环后,收到回环前的大值seq, 忽略掉 + // 回环后,收到回环前的大值seq, 忽略掉 [AUTO-TRANSLATED:6a30b91f] + // After the loop, receive a large seq value before the loop, ignore it _seq.erase(max_seq); return; } if (min_seq == (uint16_t)(_nack_seq + 1) && _seq.size() == (size_t)diff + 1) { - // 都是连续的seq,未丢包 + // 都是连续的seq,未丢包 [AUTO-TRANSLATED:62d3ffbd] + // All are continuous seq, no packet loss _seq.clear(); _nack_seq = max_seq; } else { - // seq不连续,有丢包 + // seq不连续,有丢包 [AUTO-TRANSLATED:ba1bfbc2] + // Seq is not continuous, there is packet loss makeNack(max_seq, false); } } void NackContext::makeNack(uint16_t max_seq, bool flush) { - // 尝试移除前面部分连续的seq + // 尝试移除前面部分连续的seq [AUTO-TRANSLATED:04593c1b] + // Try to remove the continuous seq in front eraseFrontSeq(); - // 最多生成5个nack包,防止seq大幅跳跃导致一直循环 + // 最多生成5个nack包,防止seq大幅跳跃导致一直循环 [AUTO-TRANSLATED:9cc5da25] + // Generate at most 5 nack packets to prevent seq from jumping significantly and causing continuous loops auto max_nack = 5u; GET_CONFIG(uint32_t, nack_rtpsize, Rtc::kNackRtpSize); // kNackRtpSize must between 0 and 16 nack_rtpsize = std::min(nack_rtpsize, FCI_NACK::kBitSize); while (_nack_seq != max_seq && max_nack--) { - // 一次不能发送超过16+1个rtp的状态 + // 一次不能发送超过16+1个rtp的状态 [AUTO-TRANSLATED:1954831a] + // Cannot send more than 16+1 rtp states at a time uint16_t nack_rtp_count = std::min(FCI_NACK::kBitSize, max_seq - (uint16_t)(_nack_seq + 1)); if (!flush && nack_rtp_count < nack_rtpsize) { - // 非flush状态下,seq个数不足以发送一次nack + // 非flush状态下,seq个数不足以发送一次nack [AUTO-TRANSLATED:94f561c1] + // In non-flush state, the number of seq is not enough to send a nack break; } vector vec; @@ -209,9 +237,11 @@ void NackContext::makeNack(uint16_t max_seq, bool flush) { } doNack(FCI_NACK(_nack_seq + 1, vec), true); _nack_seq += nack_rtp_count + 1; - // 返回第一个比_last_max_seq大的元素 + // 返回第一个比_last_max_seq大的元素 [AUTO-TRANSLATED:425c4e63] + // Return the first element greater than _last_max_seq auto it = _seq.upper_bound(_nack_seq); - // 移除 <=_last_max_seq 的seq + // 移除 <=_last_max_seq 的seq [AUTO-TRANSLATED:a64ff3fd] + // Remove seq <= _last_max_seq _seq.erase(_seq.begin(), it); } } @@ -232,10 +262,12 @@ void NackContext::doNack(const FCI_NACK &nack, bool record_nack) { } void NackContext::eraseFrontSeq() { - // 前面部分seq是连续的,未丢包,移除之 + // 前面部分seq是连续的,未丢包,移除之 [AUTO-TRANSLATED:ef3eed87] + // The previous part of the sequence is continuous and has no packet loss, remove it. for (auto it = _seq.begin(); it != _seq.end();) { if (*it != (uint16_t)(_nack_seq + 1)) { - // seq不连续,丢包了 + // seq不连续,丢包了 [AUTO-TRANSLATED:dcee49fe] + // The sequence is not continuous, there is packet loss. break; } _nack_seq = *it; @@ -248,11 +280,13 @@ void NackContext::clearNackStatus(uint16_t seq) { if (it == _nack_send_status.end()) { return; } - //收到重传包与第一个nack包间的时间约等于rtt时间 + // 收到重传包与第一个nack包间的时间约等于rtt时间 [AUTO-TRANSLATED:f702811e] + // The time between receiving the retransmitted packet and the first nack packet is approximately equal to the rtt time. auto rtt = getCurrentMillisecond() - it->second.first_stamp; _nack_send_status.erase(it); - // 限定rtt在合理有效范围内 + // 限定rtt在合理有效范围内 [AUTO-TRANSLATED:42fbed04] + // Limit the rtt within a reasonable and valid range. GET_CONFIG(uint32_t, nack_maxms, Rtc::kNackMaxMS); GET_CONFIG(uint32_t, nack_maxcount, Rtc::kNackMaxCount); _rtt = max(10, min(rtt, nack_maxms / nack_maxcount)); @@ -270,7 +304,8 @@ void NackContext::recordNack(const FCI_NACK &nack) { } ++i; } - // 记录太多了,移除一部分早期的记录 + // 记录太多了,移除一部分早期的记录 [AUTO-TRANSLATED:6f4ea62d] + // There are too many records, remove some of the earlier records. GET_CONFIG(uint32_t, nack_maxsize, Rtc::kNackMaxSize); while (_nack_send_status.size() > nack_maxsize) { _nack_send_status.erase(_nack_send_status.begin()); @@ -285,21 +320,26 @@ uint64_t NackContext::reSendNack() { GET_CONFIG(float, nack_intervalratio, Rtc::kNackIntervalRatio); for (auto it = _nack_send_status.begin(); it != _nack_send_status.end();) { if (now - it->second.first_stamp > nack_maxms) { - // 该rtp丢失太久了,不再要求重传 + // 该rtp丢失太久了,不再要求重传 [AUTO-TRANSLATED:a0a1e471] + // This rtp has been lost for too long, no longer require retransmission. it = _nack_send_status.erase(it); continue; } if (now - it->second.update_stamp < nack_intervalratio * _rtt) { - // 距离上次nack不足2倍的rtt,不用再发送nack + // 距离上次nack不足2倍的rtt,不用再发送nack [AUTO-TRANSLATED:0e7edf4d] + // The distance from the last nack is less than 2 times the rtt, no need to send nack again. ++it; continue; } - // 此rtp需要请求重传 + // 此rtp需要请求重传 [AUTO-TRANSLATED:c29d8eb5] + // This rtp needs to request retransmission. nack_rtp.emplace(it->first); - // 更新nack发送时间戳 + // 更新nack发送时间戳 [AUTO-TRANSLATED:16ef9fac] + // Update the nack sending timestamp. it->second.update_stamp = now; if (++(it->second.nack_count) == nack_maxcount) { - // nack次数太多,移除之 + // nack次数太多,移除之 [AUTO-TRANSLATED:1b684a9c] + // Too many nack times, remove it. it = _nack_send_status.erase(it); continue; } @@ -317,12 +357,14 @@ uint64_t NackContext::reSendNack() { } auto inc = *it - pid; if (inc > (ssize_t)FCI_NACK::kBitSize) { - // 新的nack包 + // 新的nack包 [AUTO-TRANSLATED:aec9b818] + // New nack packet. doNack(FCI_NACK(pid, vec), false); pid = -1; continue; } - // 这个包丢了 + // 这个包丢了 [AUTO-TRANSLATED:60f91f2f] + // This packet is lost. vec[inc - 1] = true; ++it; } @@ -330,7 +372,8 @@ uint64_t NackContext::reSendNack() { doNack(FCI_NACK(pid, vec), false); } - // 没有任何包需要重传时返回0,否则返回下次重传间隔(不得低于5ms) + // 没有任何包需要重传时返回0,否则返回下次重传间隔(不得低于5ms) [AUTO-TRANSLATED:c326264d] + // Return 0 when there are no packets to retransmit, otherwise return the next retransmission interval (not less than 5ms). return _nack_send_status.empty() ? 0 : _rtt; } diff --git a/webrtc/Nack.h b/webrtc/Nack.h index 08219e2b..1ebfda71 100644 --- a/webrtc/Nack.h +++ b/webrtc/Nack.h @@ -20,12 +20,16 @@ namespace mediakit { -// RTC配置项目 +// RTC配置项目 [AUTO-TRANSLATED:19940011] +// RTC configuration project namespace Rtc { -//~ nack发送端,rtp接收端 -// 最大保留的rtp丢包状态个数 +// ~ nack发送端,rtp接收端 [AUTO-TRANSLATED:bb169205] +// ~ nack sender, rtp receiver +// 最大保留的rtp丢包状态个数 [AUTO-TRANSLATED:70eee442] +// Maximum number of retained rtp packet loss states extern const std::string kNackMaxSize; -// rtp丢包状态最长保留时间 +// rtp丢包状态最长保留时间 [AUTO-TRANSLATED:f9306375] +// Maximum retention time for rtp packet loss states extern const std::string kNackMaxMS; } // namespace Rtc @@ -69,7 +73,8 @@ private: int _rtt = 50; onNack _cb; std::set _seq; - // 最新nack包中的rtp seq值 + // 最新nack包中的rtp seq值 [AUTO-TRANSLATED:6984d95a] + // RTP seq value in the latest nack packet uint16_t _nack_seq = 0; struct NackStatus { diff --git a/webrtc/RtpExt.cpp b/webrtc/RtpExt.cpp index e186cbf0..935075bd 100644 --- a/webrtc/RtpExt.cpp +++ b/webrtc/RtpExt.cpp @@ -136,7 +136,8 @@ void appendExt(map &ret, uint8_t *ptr, const uint8_t *end) { while (ptr < end) { auto ext = reinterpret_cast(ptr); if (ext->getId() == (uint8_t) RtpExtType::padding) { - //padding,忽略 + // padding,忽略 [AUTO-TRANSLATED:a7fda608] + // padding, ignore ++ptr; continue; } @@ -329,7 +330,8 @@ uint8_t RtpExt::getAudioLevel(bool *vad) const{ } //http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time -//Wire format: 1-byte extension, 3 bytes of data. total 4 bytes extra per packet (plus shared 4 bytes for all extensions present: 2 byte magic word 0xBEDE, 2 byte # of extensions). Will in practice replace the “toffset” extension so we should see no long term increase in traffic as a result. +// Wire format: 1-byte extension, 3 bytes of data. total 4 bytes extra per packet (plus shared 4 bytes for all extensions present: 2 byte magic word 0xBEDE, 2 byte # of extensions). Will in practice replace the “toffset” extension so we should see no long term increase in traffic as a result. [AUTO-TRANSLATED:178290be] +// Wire format: 1-byte extension, 3 bytes of data. total 4 bytes extra per packet (plus shared 4 bytes for all extensions present: 2 byte magic word 0xBEDE, 2 byte # of extensions). Will in practice replace the “toffset” extension so we should see no long term increase in traffic as a result. // //Encoding: Timestamp is in seconds, 24 bit 6.18 fixed point, yielding 64s wraparound and 3.8us resolution (one increment for each 477 bytes going out on a 1Gbps interface). // @@ -374,7 +376,8 @@ string RtpExt::getSdesMid() const { //https://tools.ietf.org/html/draft-ietf-avtext-rid-06 -//用于simulcast +// 用于simulcast [AUTO-TRANSLATED:59b2682f] +// Used for simulcast //3.1. RTCP 'RtpStreamId' SDES Extension // // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -463,7 +466,8 @@ void RtpExt::getVideoTiming(uint8_t &flags, //Values: //0x00: Unspecified. Default value. Treated the same as an absence of an extension. //0x01: Screenshare. Video stream is of a screenshare type. -//0x02: 摄像头? +// 0x02: 摄像头? [AUTO-TRANSLATED:ce2acbbb] +// 0x02: Camera? //Notes: Extension shoud be present only in the last packet of key-frames. // If attached to other packets it should be ignored. // If extension is absent, Unspecified value is assumed. @@ -584,12 +588,14 @@ RtpExt RtpExtContext::changeRtpExtId(const RtpHeader *header, bool is_recv, stri if (is_recv) { auto it = _rtp_ext_id_to_type.find(pr.first); if (it == _rtp_ext_id_to_type.end()) { - //TraceL << "接收rtp时,忽略不识别的rtp ext, id=" << (int) pr.first; + // TraceL << "接收rtp时,忽略不识别的rtp ext, id=" << (int) pr.first; [AUTO-TRANSLATED:284d8a38] + // TraceL << "Receiving rtp, ignoring unrecognized rtp ext, id=" << (int) pr.first; pr.second.clearExt(); continue; } pr.second.setType(it->second); - //重新赋值ext id为 ext type,作为后面处理ext的统一中间类型 + // 重新赋值ext id为 ext type,作为后面处理ext的统一中间类型 [AUTO-TRANSLATED:ab825878] + // Reassign ext id to ext type, as a unified intermediate type for processing ext later pr.second.setExtId((uint8_t) it->second); switch (it->second) { case RtpExtType::sdes_rtp_stream_id : rid = pr.second.getRtpStreamId(); break; @@ -600,11 +606,13 @@ RtpExt RtpExtContext::changeRtpExtId(const RtpHeader *header, bool is_recv, stri pr.second.setType((RtpExtType) pr.first); auto it = _rtp_ext_type_to_id.find((RtpExtType) pr.first); if (it == _rtp_ext_type_to_id.end()) { - //TraceL << "发送rtp时, 忽略不被客户端支持rtp ext:" << pr.second.dumpString(); + // TraceL << "发送rtp时, 忽略不被客户端支持rtp ext:" << pr.second.dumpString(); [AUTO-TRANSLATED:5d9fd8cc] + // TraceL << "Sending rtp, ignoring rtp ext not supported by client:" << pr.second.dumpString(); pr.second.clearExt(); continue; } - //重新赋值ext id为客户端sdp声明的类型 + // 重新赋值ext id为客户端sdp声明的类型 [AUTO-TRANSLATED:06d60796] + // Reassign ext id to the type declared in client sdp pr.second.setExtId(it->second); } if (pr.second.getType() == type) { @@ -620,10 +628,12 @@ RtpExt RtpExtContext::changeRtpExtId(const RtpHeader *header, bool is_recv, stri } auto ssrc = ntohl(header->ssrc); if (rid.empty()) { - //获取rid + // 获取rid [AUTO-TRANSLATED:8ae4dffa] + // Get rid rid = _ssrc_to_rid[ssrc]; } else { - //设置rid + // 设置rid [AUTO-TRANSLATED:5e34819b] + // Set rid auto it = _ssrc_to_rid.find(ssrc); if (it == _ssrc_to_rid.end() || it->second != rid) { _ssrc_to_rid[ssrc] = rid; diff --git a/webrtc/RtpExt.h b/webrtc/RtpExt.h index a7aea281..438840fe 100644 --- a/webrtc/RtpExt.h +++ b/webrtc/RtpExt.h @@ -47,7 +47,8 @@ enum class RtpExtType : uint8_t { class RtcMedia; -//使用次对象的方法前需保证RtpHeader内存未释放 +// 使用次对象的方法前需保证RtpHeader内存未释放 [AUTO-TRANSLATED:0378877f] +// Ensure that the RtpHeader memory has not been released before using the methods of this object class RtpExt { public: template @@ -129,9 +130,11 @@ private: private: OnGetRtp _cb; - //发送rtp时需要修改rtp ext id + // 发送rtp时需要修改rtp ext id [AUTO-TRANSLATED:b92a494b] + // Modify the rtp ext id when sending rtp std::map _rtp_ext_type_to_id; - //接收rtp时需要修改rtp ext id + // 接收rtp时需要修改rtp ext id [AUTO-TRANSLATED:685e7a01] + // Modify the rtp ext id when receiving rtp std::unordered_map _rtp_ext_id_to_type; //ssrc --> rid std::unordered_map _ssrc_to_rid; diff --git a/webrtc/Sdp.cpp b/webrtc/Sdp.cpp index e1f1d045..b23969c1 100644 --- a/webrtc/Sdp.cpp +++ b/webrtc/Sdp.cpp @@ -554,7 +554,8 @@ void SdpAttrRtpMap::parse(const string &str) { if (sscanf(str.data(), "%" SCNu8 " %31[^/]/%" SCNd32 "/%" SCNd32, &pt, buf, &sample_rate, &channel) != 4) { CHECK_SDP(sscanf(str.data(), "%" SCNu8 " %31[^/]/%" SCNd32, &pt, buf, &sample_rate) == 3); if (getTrackType(getCodecId(buf)) == TrackAudio) { - // 未指定通道数时,且为音频时,那么通道数默认为1 + // 未指定通道数时,且为音频时,那么通道数默认为1 [AUTO-TRANSLATED:bd128fbd] + // If the number of channels is not specified and it is audio, the number of channels defaults to 1 channel = 1; } } @@ -655,7 +656,8 @@ void SdpAttrSSRCGroup::parse(const string &str) { string SdpAttrSSRCGroup::toString() const { if (value.empty()) { value = type; - // 最少要求2个ssrc + // 最少要求2个ssrc [AUTO-TRANSLATED:968acb83] + // At least 2 SSRCs are required CHECK(ssrcs.size() >= 2); for (auto &ssrc : ssrcs) { value += ' '; @@ -842,12 +844,15 @@ void RtcSession::loadFrom(const string &str) { for (auto &group : ssrc_groups) { if (group.isFID()) { have_rtx_ssrc = true; - // ssrc-group:FID字段必须包含rtp/rtx的ssrc + // ssrc-group:FID字段必须包含rtp/rtx的ssrc [AUTO-TRANSLATED:3da97d7d] + // The ssrc-group:FID field must contain the SSRCs of rtp/rtx CHECK(group.ssrcs.size() == 2); - // 根据rtp ssrc找到对象 + // 根据rtp ssrc找到对象 [AUTO-TRANSLATED:c0a56b42] + // Find the object based on the RTP SSRC auto it = rtc_ssrc_map.find(group.ssrcs[0]); CHECK(it != rtc_ssrc_map.end()); - // 设置rtx ssrc + // 设置rtx ssrc [AUTO-TRANSLATED:422e2a55] + // Set the RTX SSRC it->second.rtx_ssrc = group.ssrcs[1]; rtc_media.rtp_rtx_ssrc.emplace_back(it->second); } else if (group.isSIM()) { @@ -857,7 +862,8 @@ void RtcSession::loadFrom(const string &str) { } if (!have_rtx_ssrc) { - // 按照sdp顺序依次添加ssrc + // 按照sdp顺序依次添加ssrc [AUTO-TRANSLATED:0996ba7e] + // Add SSRCs in the order of SDP for (auto &attr : ssrc_attr) { if (attr.attribute == "cname") { rtc_media.rtp_rtx_ssrc.emplace_back(rtc_ssrc_map[attr.ssrc]); @@ -871,7 +877,8 @@ void RtcSession::loadFrom(const string &str) { // a=rid:m send // a=rid:l send // a=simulcast:send h;m;l - // 风格的simulcast + // 风格的simulcast [AUTO-TRANSLATED:94ac2d55] + // Style of simulcast unordered_set rid_map; for (auto &rid : simulcast.rids) { rid_map.emplace(rid); @@ -880,56 +887,67 @@ void RtcSession::loadFrom(const string &str) { CHECK(rid.direction == simulcast.direction); CHECK(rid_map.find(rid.rid) != rid_map.end()); } - // simulcast最少要求2种方案 + // simulcast最少要求2种方案 [AUTO-TRANSLATED:31732a7a] + // Simulcast requires at least 2 schemes CHECK(simulcast.rids.size() >= 2); rtc_media.rtp_rids = simulcast.rids; } if (ssrc_group_sim) { - // 指定了a=ssrc-group:SIM + // 指定了a=ssrc-group:SIM [AUTO-TRANSLATED:5732661e] + // a=ssrc-group:SIM is specified for (auto ssrc : ssrc_group_sim->ssrcs) { auto it = rtc_ssrc_map.find(ssrc); CHECK(it != rtc_ssrc_map.end()); rtc_media.rtp_ssrc_sim.emplace_back(it->second); } } else if (!rtc_media.rtp_rids.empty()) { - // 未指定a=ssrc-group:SIM, 但是指定了a=simulcast, 那么只能根据ssrc顺序来对应rid顺序 + // 未指定a=ssrc-group:SIM, 但是指定了a=simulcast, 那么只能根据ssrc顺序来对应rid顺序 [AUTO-TRANSLATED:b198a817] + // a=ssrc-group:SIM is not specified, but a=simulcast is specified, so the RID order can only be matched according to the SSRC order rtc_media.rtp_ssrc_sim = rtc_media.rtp_rtx_ssrc; } if (!rtc_media.supportSimulcast()) { - // 不支持simulcast的情况下,最多一组ssrc + // 不支持simulcast的情况下,最多一组ssrc [AUTO-TRANSLATED:3ea8ed65] + // In the case of not supporting simulcast, there is at most one group of SSRCs CHECK(rtc_media.rtp_rtx_ssrc.size() <= 1); } else { - // simulcast的情况下,要么没有指定ssrc,要么指定的ssrc个数与rid个数一致 + // simulcast的情况下,要么没有指定ssrc,要么指定的ssrc个数与rid个数一致 [AUTO-TRANSLATED:1d45ce03] + // In the case of simulcast, either no SSRC is specified or the number of specified SSRCs is consistent with the number of RIDs // CHECK(rtc_media.rtp_ssrc_sim.empty() || rtc_media.rtp_ssrc_sim.size() == rtc_media.rtp_rids.size()); } auto rtpmap_arr = media.getAllItem('a', "rtpmap"); auto rtcpfb_arr = media.getAllItem('a', "rtcp-fb"); auto fmtp_aar = media.getAllItem('a', "fmtp"); - // 方便根据pt查找rtpmap,一个pt必有一条 + // 方便根据pt查找rtpmap,一个pt必有一条 [AUTO-TRANSLATED:c3673faa] + // Convenient to find rtpmap based on pt, one pt must have one map rtpmap_map; - // 方便根据pt查找rtcp-fb,一个pt可能有多条或0条 + // 方便根据pt查找rtcp-fb,一个pt可能有多条或0条 [AUTO-TRANSLATED:38361f68] + // Convenient to find rtcp-fb based on pt, one pt may have multiple or 0 multimap rtcpfb_map; - // 方便根据pt查找fmtp,一个pt最多一条 + // 方便根据pt查找fmtp,一个pt最多一条 [AUTO-TRANSLATED:be5d652d] + // Convenient to find fmtp based on pt, one pt has at most one map fmtp_map; for (auto &rtpmap : rtpmap_arr) { - // 添加失败,有多条 + // 添加失败,有多条 [AUTO-TRANSLATED:717782c0] + // Add failed, there are multiple CHECK(rtpmap_map.emplace(rtpmap.pt, rtpmap).second, "该pt存在多条a=rtpmap:", (int)rtpmap.pt); } for (auto &rtpfb : rtcpfb_arr) { rtcpfb_map.emplace(rtpfb.pt, rtpfb); } for (auto &fmtp : fmtp_aar) { - // 添加失败,有多条 + // 添加失败,有多条 [AUTO-TRANSLATED:717782c0] + // Add failed, there are multiple CHECK(fmtp_map.emplace(fmtp.pt, fmtp).second, "该pt存在多条a=fmtp:", (int)fmtp.pt); } for (auto &item : mline.fmts) { auto pt = atoi(item.c_str()); CHECK(pt < 0xFF, "invalid payload type: ", item); - // 遍历所有编码方案的pt + // 遍历所有编码方案的pt [AUTO-TRANSLATED:40f2db36] + // Traverse the pt of all encoding schemes rtc_media.plan.emplace_back(); auto &plan = rtc_media.plan.back(); auto rtpmap_it = rtpmap_map.find(pt); @@ -1126,14 +1144,16 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const { rtp_map->codec = p.codec; rtp_map->sample_rate = p.sample_rate; rtp_map->channel = p.channel; - // 添加a=rtpmap + // 添加a=rtpmap [AUTO-TRANSLATED:8bef5d64] + // Add a=rtpmap sdp_media.addAttr(std::move(rtp_map)); for (auto &fb : p.rtcp_fb) { auto rtcp_fb = std::make_shared(); rtcp_fb->pt = p.pt; rtcp_fb->rtcp_type = fb; - // 添加a=rtcp-fb + // 添加a=rtcp-fb [AUTO-TRANSLATED:11754b43] + // Add a=rtcp-fb sdp_media.addAttr(std::move(rtcp_fb)); } @@ -1141,13 +1161,15 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const { auto fmtp = std::make_shared(); fmtp->pt = p.pt; fmtp->fmtp = p.fmtp; - // 添加a=fmtp + // 添加a=fmtp [AUTO-TRANSLATED:594a4425] + // Add a=fmtp sdp_media.addAttr(std::move(fmtp)); } } { - // 添加a=msid字段 + // 添加a=msid字段 [AUTO-TRANSLATED:cf2c1471] + // Add a=msid field if (!m.rtp_rtx_ssrc.empty()) { if (!m.rtp_rtx_ssrc[0].msid.empty()) { auto msid = std::make_shared(); @@ -1159,14 +1181,17 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const { { for (auto &ssrc : m.rtp_rtx_ssrc) { - // 添加a=ssrc字段 + // 添加a=ssrc字段 [AUTO-TRANSLATED:75ca5225] + // Add a=ssrc field CHECK(!ssrc.empty()); addSdpAttrSSRC(ssrc, sdp_media, ssrc.ssrc); if (ssrc.rtx_ssrc) { addSdpAttrSSRC(ssrc, sdp_media, ssrc.rtx_ssrc); - // 生成a=ssrc-group:FID字段 - // 有rtx ssrc + // 生成a=ssrc-group:FID字段 [AUTO-TRANSLATED:22b1f966] + // Generate a=ssrc-group:FID field + // 有rtx ssrc [AUTO-TRANSLATED:fece8076] + // There is rtx ssrc auto group = std::make_shared(); group->type = "FID"; group->ssrcs.emplace_back(ssrc.ssrc); @@ -1178,12 +1203,14 @@ RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const { { if (m.rtp_ssrc_sim.size() >= 2) { - // simulcast 要求 2~3路 + // simulcast 要求 2~3路 [AUTO-TRANSLATED:3237ffca] + // Simulcast requires 2~3 channels auto group = std::make_shared(); for (auto &ssrc : m.rtp_ssrc_sim) { group->ssrcs.emplace_back(ssrc.ssrc); } - // 添加a=ssrc-group:SIM字段 + // 添加a=ssrc-group:SIM字段 [AUTO-TRANSLATED:46b04aae] + // Add a=ssrc-group:SIM field group->type = "SIM"; sdp_media.addAttr(std::move(group)); } @@ -1299,15 +1326,18 @@ void RtcMedia::checkValid() const { bool send_rtp = (direction == RtpDirection::sendonly || direction == RtpDirection::sendrecv); if (!supportSimulcast()) { - // 非simulcast时,检查有没有指定rtp ssrc + // 非simulcast时,检查有没有指定rtp ssrc [AUTO-TRANSLATED:e2d53f8a] + // When not simulcast, check if the RTP SSRC is specified CHECK(!rtp_rtx_ssrc.empty() || !send_rtp); } #if 0 - //todo 发现Firefox(88.0)在mac平台下,开启rtx后没有指定ssrc + // todo 发现Firefox(88.0)在mac平台下,开启rtx后没有指定ssrc [AUTO-TRANSLATED:9112d91a] + // todo Found that Firefox (88.0) on the mac platform does not specify ssrc when rtx is enabled auto rtx_plan = getPlan("rtx"); if (rtx_plan) { - //开启rtx后必须指定rtx_ssrc + // 开启rtx后必须指定rtx_ssrc [AUTO-TRANSLATED:c527f68d] + // RTX must be specified after rtx_ssrc is enabled CHECK(rtp_rtx_ssrc.size() >= 2 || !send_rtp); } #endif @@ -1416,7 +1446,8 @@ void RtcConfigure::RtcTrackConfigure::setDefaultSetting(TrackType type) { ice_renomination = false; switch (type) { case TrackAudio: { - // 此处调整偏好的编码格式优先级 + // 此处调整偏好的编码格式优先级 [AUTO-TRANSLATED:b8719e66] + // Adjust the priority of preferred encoding formats here GET_CONFIG_FUNC(vector, s_preferred_codec, Rtc::kPreferredCodecA, toCodecArray); CHECK(!s_preferred_codec.empty(), "rtc音频偏好codec不能为空"); preferred_codec = s_preferred_codec; @@ -1426,14 +1457,16 @@ void RtcConfigure::RtcTrackConfigure::setDefaultSetting(TrackType type) { { RtpExtType::csrc_audio_level, RtpDirection::sendrecv }, { RtpExtType::abs_send_time, RtpDirection::sendrecv }, { RtpExtType::transport_cc, RtpDirection::sendrecv }, - // rtx重传rtp时,忽略sdes_mid类型的rtp ext,实测发现Firefox在接收rtx时,如果存在sdes_mid的ext,将导致无法播放 + // rtx重传rtp时,忽略sdes_mid类型的rtp ext,实测发现Firefox在接收rtx时,如果存在sdes_mid的ext,将导致无法播放 [AUTO-TRANSLATED:221df025] + // When rtx retransmits rtp, ignore the rtp ext of sdes_mid type. It is found that Firefox cannot play when receiving rtx if there is an ext of sdes_mid //{RtpExtType::sdes_mid,RtpDirection::sendrecv}, { RtpExtType::sdes_rtp_stream_id, RtpDirection::sendrecv }, { RtpExtType::sdes_repaired_rtp_stream_id, RtpDirection::sendrecv } }; break; } case TrackVideo: { - // 此处调整偏好的编码格式优先级 + // 此处调整偏好的编码格式优先级 [AUTO-TRANSLATED:b8719e66] + // Adjust the priority of preferred encoding formats here GET_CONFIG_FUNC(vector, s_preferred_codec, Rtc::kPreferredCodecV, toCodecArray); CHECK(!s_preferred_codec.empty(), "rtc视频偏好codec不能为空"); preferred_codec = s_preferred_codec; @@ -1441,7 +1474,8 @@ void RtcConfigure::RtcTrackConfigure::setDefaultSetting(TrackType type) { rtcp_fb = { SdpConst::kTWCCRtcpFb, SdpConst::kRembRtcpFb, "nack", "ccm fir", "nack pli" }; extmap = { { RtpExtType::abs_send_time, RtpDirection::sendrecv }, { RtpExtType::transport_cc, RtpDirection::sendrecv }, - // rtx重传rtp时,忽略sdes_mid类型的rtp ext,实测发现Firefox在接收rtx时,如果存在sdes_mid的ext,将导致无法播放 + // rtx重传rtp时,忽略sdes_mid类型的rtp ext,实测发现Firefox在接收rtx时,如果存在sdes_mid的ext,将导致无法播放 [AUTO-TRANSLATED:221df025] + // When rtx retransmits rtp, ignore the rtp ext of sdes_mid type. It is found that Firefox cannot play when receiving rtx if there is an ext of sdes_mid //{RtpExtType::sdes_mid,RtpDirection::sendrecv}, { RtpExtType::sdes_rtp_stream_id, RtpDirection::sendrecv }, { RtpExtType::sdes_repaired_rtp_stream_id, RtpDirection::sendrecv }, @@ -1449,7 +1483,8 @@ void RtcConfigure::RtcTrackConfigure::setDefaultSetting(TrackType type) { { RtpExtType::color_space, RtpDirection::sendrecv }, { RtpExtType::video_content_type, RtpDirection::sendrecv }, { RtpExtType::playout_delay, RtpDirection::sendrecv }, - // 手机端推webrtc 会带有旋转角度,rtc协议能正常播放 其他协议拉流画面旋转 + // 手机端推webrtc 会带有旋转角度,rtc协议能正常播放 其他协议拉流画面旋转 [AUTO-TRANSLATED:3f2f9e0e] + // Mobile push webrtc will have a rotation angle, rtc protocol can play normally, other protocols pull stream picture rotation //{RtpExtType::video_orientation, RtpDirection::sendrecv}, { RtpExtType::toffset, RtpDirection::sendrecv }, { RtpExtType::framemarking, RtpDirection::sendrecv } }; @@ -1549,7 +1584,8 @@ shared_ptr RtcConfigure::createAnswer(const RtcSession &offer) const matchMedia(ret, m); } - // 设置音视频端口复用 + // 设置音视频端口复用 [AUTO-TRANSLATED:ffe27d17] + // Set audio and video port multiplexing if (!offer.group.mids.empty()) { for (auto &m : ret->media) { // The remote end has rejected (port 0) the m-section, so it should not be putting its mid in the group attribute. @@ -1565,7 +1601,8 @@ static RtpDirection matchDirection(RtpDirection offer_direction, RtpDirection su switch (offer_direction) { case RtpDirection::sendonly: { if (supported != RtpDirection::recvonly && supported != RtpDirection::sendrecv) { - // 我们不支持接收 + // 我们不支持接收 [AUTO-TRANSLATED:e4ef4034] + // We do not support receiving return RtpDirection::inactive; } return RtpDirection::recvonly; @@ -1573,13 +1610,15 @@ static RtpDirection matchDirection(RtpDirection offer_direction, RtpDirection su case RtpDirection::recvonly: { if (supported != RtpDirection::sendonly && supported != RtpDirection::sendrecv) { - // 我们不支持发送 + // 我们不支持发送 [AUTO-TRANSLATED:6505a226] + // We do not support sending return RtpDirection::inactive; } return RtpDirection::sendonly; } - // 对方支持发送接收,那么最终能力根据配置来决定 + // 对方支持发送接收,那么最终能力根据配置来决定 [AUTO-TRANSLATED:d234d603] + // The other party supports sending and receiving, so the final capability is determined by the configuration case RtpDirection::sendrecv: return (supported == RtpDirection::invalid ? RtpDirection::inactive : supported); case RtpDirection::inactive: return RtpDirection::inactive; default: return RtpDirection::invalid; @@ -1633,20 +1672,24 @@ RETRY: } const RtcCodecPlan *selected_plan = nullptr; for (auto &plan : offer_media.plan) { - // 先检查编码格式是否为偏好 + // 先检查编码格式是否为偏好 [AUTO-TRANSLATED:b7fb32a0] + // First check if the encoding format is preferred if (check_codec && getCodecId(plan.codec) != codec) { continue; } - // 命中偏好的编码格式,然后检查规格 + // 命中偏好的编码格式,然后检查规格 [AUTO-TRANSLATED:a859c839] + // Hit the preferred encoding format, then check the specifications if (check_profile && !onCheckCodecProfile(plan, codec)) { continue; } - // 找到中意的codec + // 找到中意的codec [AUTO-TRANSLATED:4b5eebfd] + // Find the desired codec selected_plan = &plan; break; } if (!selected_plan) { - // offer中该媒体的所有的codec都不支持 + // offer中该媒体的所有的codec都不支持 [AUTO-TRANSLATED:3b57b86f] + // All codecs for this media in the offer are not supported continue; } RtcMedia answer_media; @@ -1672,23 +1715,27 @@ RETRY: answer_media.role = mathDtlsRole(offer_media.role); - // 如果codec匹配失败,那么禁用该track + // 如果codec匹配失败,那么禁用该track [AUTO-TRANSLATED:037de9a8] + // If the codec matching fails, then disable the track answer_media.direction = check_codec ? matchDirection(offer_media.direction, configure.direction) : RtpDirection::inactive; if (answer_media.direction == RtpDirection::invalid) { continue; } if (answer_media.direction == RtpDirection::sendrecv) { - // 如果是收发双向,那么我们拷贝offer sdp的ssrc,确保ssrc一致 + // 如果是收发双向,那么我们拷贝offer sdp的ssrc,确保ssrc一致 [AUTO-TRANSLATED:d4a621f2] + // If it is bidirectional, then we copy the offer sdp ssrc to ensure ssrc consistency answer_media.rtp_rtx_ssrc = offer_media.rtp_rtx_ssrc; } - // 添加媒体plan + // 添加媒体plan [AUTO-TRANSLATED:3f730050] + // Add media plan answer_media.plan.emplace_back(*selected_plan); onSelectPlan(answer_media.plan.back(), codec); set pt_selected = { selected_plan->pt }; - // 添加rtx,red,ulpfec plan + // 添加rtx,red,ulpfec plan [AUTO-TRANSLATED:1abff0c1] + // Add rtx, red, ulpfec plan if (configure.support_red || configure.support_rtx || configure.support_ulpfec) { for (auto &plan : offer_media.plan) { if (!strcasecmp(plan.codec.data(), "rtx")) { @@ -1715,7 +1762,8 @@ RETRY: } } - // 对方和我方都支持的扩展,那么我们才支持 + // 对方和我方都支持的扩展,那么我们才支持 [AUTO-TRANSLATED:a6cd98b2] + // We only support extensions that are supported by both the other party and us for (auto &ext : offer_media.extmap) { auto it = configure.extmap.find(RtpExt::getExtType(ext.ext)); if (it != configure.extmap.end()) { @@ -1732,16 +1780,19 @@ RETRY: auto &rtcp_fb_ref = answer_media.plan[0].rtcp_fb; rtcp_fb_ref.clear(); - // 对方和我方都支持的rtcpfb,那么我们才支持 + // 对方和我方都支持的rtcpfb,那么我们才支持 [AUTO-TRANSLATED:f10450bb] + // We only support rtcpfb that is supported by both the other party and us for (auto &fp : selected_plan->rtcp_fb) { if (configure.rtcp_fb.find(fp) != configure.rtcp_fb.end()) { - // 对方该rtcp被我们支持 + // 对方该rtcp被我们支持 [AUTO-TRANSLATED:3b16e666] + // The other party's rtcp is supported by us rtcp_fb_ref.emplace(fp); } } #if 0 - //todo 此处为添加无效的plan,webrtc sdp通过调节plan pt顺序选择匹配的codec,意味着后面的codec其实放在sdp中是无意义的 + // todo 此处为添加无效的plan,webrtc sdp通过调节plan pt顺序选择匹配的codec,意味着后面的codec其实放在sdp中是无意义的 [AUTO-TRANSLATED:502d0cb2] + // todo This is to add an invalid plan. WebRTC sdp selects the matching codec by adjusting the plan pt order, which means that the subsequent codecs are actually meaningless in the sdp for (auto &plan : offer_media.plan) { if (pt_selected.find(plan.pt) == pt_selected.end()) { answer_media.plan.emplace_back(plan); @@ -1753,13 +1804,15 @@ RETRY: } if (check_profile) { - // 如果是由于检查profile导致匹配失败,那么重试一次,且不检查profile + // 如果是由于检查profile导致匹配失败,那么重试一次,且不检查profile [AUTO-TRANSLATED:897fa4ae] + // If the matching fails due to profile check, retry once and do not check profile check_profile = false; goto RETRY; } if (check_codec) { - // 如果是由于检查codec导致匹配失败,那么重试一次,且不检查codec + // 如果是由于检查codec导致匹配失败,那么重试一次,且不检查codec [AUTO-TRANSLATED:fbd85968] + // If the matching fails due to codec check, retry once and do not check codec check_codec = false; goto RETRY; } @@ -1798,15 +1851,18 @@ static const string kMode { "packetization-mode" }; bool RtcConfigure::onCheckCodecProfile(const RtcCodecPlan &plan, CodecId codec) const { if (_rtsp_audio_plan && codec == getCodecId(_rtsp_audio_plan->codec)) { if (plan.sample_rate != _rtsp_audio_plan->sample_rate || plan.channel != _rtsp_audio_plan->channel) { - // 音频采样率和通道数必须相同 + // 音频采样率和通道数必须相同 [AUTO-TRANSLATED:6e591932] + // Audio sampling rate and number of channels must be the same return false; } return true; } if (_rtsp_video_plan && codec == CodecH264 && getCodecId(_rtsp_video_plan->codec) == CodecH264) { - // h264时,profile-level-id + // h264时,profile-level-id [AUTO-TRANSLATED:94a5f360] + // When h264, profile-level-id if (strcasecmp(_rtsp_video_plan->fmtp[kProfile].data(), const_cast(plan).fmtp[kProfile].data())) { - // profile-level-id 不匹配 + // profile-level-id 不匹配 [AUTO-TRANSLATED:814ec4c4] + // profile-level-id does not match return false; } return true; @@ -1819,10 +1875,16 @@ bool RtcConfigure::onCheckCodecProfile(const RtcCodecPlan &plan, CodecId codec) Single NAI Unit Mode = 0. // Single NAI mode (Only nals from 1-23 are allowed) Non Interleaved Mode = 1,// Non-interleaved Mode: 1-23,24 (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-23,24 (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:b1526114] **/ void RtcConfigure::onSelectPlan(RtcCodecPlan &plan, CodecId codec) const { if (_rtsp_video_plan && codec == CodecH264 && getCodecId(_rtsp_video_plan->codec) == CodecH264) { - // h264时,设置packetization-mod为一致 + // h264时,设置packetization-mod为一致 [AUTO-TRANSLATED:59a00889] + // When h264, set packetization-mod to be consistent auto mode = _rtsp_video_plan->fmtp[kMode]; GET_CONFIG(bool, h264_stap_a, Rtp::kH264StapA); plan.fmtp[kMode] = mode.empty() ? std::to_string(h264_stap_a) : mode; diff --git a/webrtc/Sdp.h b/webrtc/Sdp.h index 25436a9f..b260f852 100644 --- a/webrtc/Sdp.h +++ b/webrtc/Sdp.h @@ -57,23 +57,30 @@ namespace mediakit { enum class RtpDirection { invalid = -1, - // 只发送 + // 只发送 [AUTO-TRANSLATED:d7e7fdb7] + // Send only sendonly, - // 只接收 + // 只接收 [AUTO-TRANSLATED:f75ca789] + // Receive only recvonly, - // 同时发送接收 + // 同时发送接收 [AUTO-TRANSLATED:7f900ba1] + // Send and receive simultaneously sendrecv, - // 禁止发送数据 + // 禁止发送数据 [AUTO-TRANSLATED:6045b47e] + // Prohibit sending data inactive }; enum class DtlsRole { invalid = -1, - // 客户端 + // 客户端 [AUTO-TRANSLATED:915417a2] + // Client active, - // 服务端 + // 服务端 [AUTO-TRANSLATED:03a80b18] + // Server passive, - // 既可作做客户端也可以做服务端 + // 既可作做客户端也可以做服务端 [AUTO-TRANSLATED:5ab1162e] + // Can be used as both client and server actpass, }; @@ -169,7 +176,8 @@ public: // 5.8. Bandwidth ("b=") // b=: - // AS、CT + // AS、CT [AUTO-TRANSLATED:65298206] + // AS, CT std::string bwtype { "AS" }; uint32_t bandwidth { 0 }; @@ -185,10 +193,14 @@ public: // m= ... TrackType type; uint16_t port; - // RTP/AVP:应用场景为视频/音频的 RTP 协议。参考 RFC 3551 - // RTP/SAVP:应用场景为视频/音频的 SRTP 协议。参考 RFC 3711 - // RTP/AVPF: 应用场景为视频/音频的 RTP 协议,支持 RTCP-based Feedback。参考 RFC 4585 - // RTP/SAVPF: 应用场景为视频/音频的 SRTP 协议,支持 RTCP-based Feedback。参考 RFC 5124 + // RTP/AVP:应用场景为视频/音频的 RTP 协议。参考 RFC 3551 [AUTO-TRANSLATED:7a9d7e86] + // RTP/AVP: The application scenario is the RTP protocol for video/audio. Refer to RFC 3551 + // RTP/SAVP:应用场景为视频/音频的 SRTP 协议。参考 RFC 3711 [AUTO-TRANSLATED:7989a619] + // RTP/SAVP: The application scenario is the SRTP protocol for video/audio. Refer to RFC 3711 + // RTP/AVPF: 应用场景为视频/音频的 RTP 协议,支持 RTCP-based Feedback。参考 RFC 4585 [AUTO-TRANSLATED:71241e80] + // RTP/AVPF: The application scenario is the RTP protocol for video/audio, supporting RTCP-based Feedback. Refer to RFC 4585 + // RTP/SAVPF: 应用场景为视频/音频的 SRTP 协议,支持 RTCP-based Feedback。参考 RFC 5124 [AUTO-TRANSLATED:69015267] + // RTP/SAVPF: The application scenario is the SRTP protocol for video/audio, supporting RTCP-based Feedback. Refer to RFC 5124 std::string proto; std::vector fmts; @@ -349,11 +361,16 @@ public: class SdpAttrRtcpFb : public SdpItem { public: // a=rtcp-fb:98 nack pli - // a=rtcp-fb:120 nack 支持 nack 重传,nack (Negative-Acknowledgment) 。 - // a=rtcp-fb:120 nack pli 支持 nack 关键帧重传,PLI (Picture Loss Indication) 。 - // a=rtcp-fb:120 ccm fir 支持编码层关键帧请求,CCM (Codec Control Message),FIR (Full Intra Request ),通常与 nack pli 有同样的效果,但是 nack pli - // 是用于重传时的关键帧请求。 a=rtcp-fb:120 goog-remb 支持 REMB (Receiver Estimated Maximum Bitrate) 。 a=rtcp-fb:120 transport-cc 支持 TCC (Transport - // Congest Control) 。 + // a=rtcp-fb:120 nack 支持 nack 重传,nack (Negative-Acknowledgment) 。 [AUTO-TRANSLATED:08d5c4e2] + // a=rtcp-fb:120 nack supports nack retransmission, nack (Negative-Acknowledgment). + // a=rtcp-fb:120 nack pli 支持 nack 关键帧重传,PLI (Picture Loss Indication) 。 [AUTO-TRANSLATED:c331c1dd] + // a=rtcp-fb:120 nack pli supports nack keyframe retransmission, PLI (Picture Loss Indication). + // a=rtcp-fb:120 ccm fir 支持编码层关键帧请求,CCM (Codec Control Message),FIR (Full Intra Request ),通常与 nack pli 有同样的效果,但是 nack pli [AUTO-TRANSLATED:7090fdc9] + // a=rtcp-fb:120 ccm fir supports keyframe requests for the coding layer, CCM (Codec Control Message), FIR (Full Intra Request), which usually has the same effect as nack pli, but nack pli + // 是用于重传时的关键帧请求。 a=rtcp-fb:120 goog-remb 支持 REMB (Receiver Estimated Maximum Bitrate) 。 a=rtcp-fb:120 transport-cc 支持 TCC (Transport [AUTO-TRANSLATED:ffac8e91] + // is used for keyframe requests during retransmission. a=rtcp-fb:120 goog-remb supports REMB (Receiver Estimated Maximum Bitrate). a=rtcp-fb:120 transport-cc supports TCC (Transport + // Congest Control) 。 [AUTO-TRANSLATED:dcf53e31] + // Congest Control). uint8_t pt; std::string rtcp_type; void parse(const std::string &str) override; @@ -379,12 +396,18 @@ public: // a=ssrc:3245185839 label:0cf7e597-36a2-4480-9796-69bf0955eef5 // a=ssrc: // a=ssrc: : - // cname 是必须的,msid/mslabel/label 这三个属性都是 WebRTC 自创的,或者说 Google 自创的,可以参考 https://tools.ietf.org/html/draft-ietf-mmusic-msid-17, - // 理解它们三者的关系需要先了解三个概念:RTP stream / MediaStreamTrack / MediaStream : - // 一个 a=ssrc 代表一个 RTP stream ; - // 一个 MediaStreamTrack 通常包含一个或多个 RTP stream,例如一个视频 MediaStreamTrack 中通常包含两个 RTP stream,一个用于常规传输,一个用于 nack 重传; - // 一个 MediaStream 通常包含一个或多个 MediaStreamTrack ,例如 simulcast 场景下,一个 MediaStream 通常会包含三个不同编码质量的 MediaStreamTrack ; - // 这种标记方式并不被 Firefox 认可,在 Firefox 生成的 SDP 中一个 a=ssrc 通常只有一行,例如: + // cname 是必须的,msid/mslabel/label 这三个属性都是 WebRTC 自创的,或者说 Google 自创的,可以参考 https://tools.ietf.org/html/draft-ietf-mmusic-msid-17, [AUTO-TRANSLATED:d8cb1baf] + // cname is required, msid/mslabel/label these three attributes are all created by WebRTC, or Google created, you can refer to https://tools.ietf.org/html/draft-ietf-mmusic-msid-17, + // 理解它们三者的关系需要先了解三个概念:RTP stream / MediaStreamTrack / MediaStream : [AUTO-TRANSLATED:7d385cf5] + // understanding the relationship between the three requires understanding three concepts: RTP stream / MediaStreamTrack / MediaStream: + // 一个 a=ssrc 代表一个 RTP stream ; [AUTO-TRANSLATED:ee1ecc6f] + // One a=ssrc represents one RTP stream; + // 一个 MediaStreamTrack 通常包含一个或多个 RTP stream,例如一个视频 MediaStreamTrack 中通常包含两个 RTP stream,一个用于常规传输,一个用于 nack 重传; [AUTO-TRANSLATED:e8ddf0fd] + // A MediaStreamTrack usually contains one or more RTP streams, for example, a video MediaStreamTrack usually contains two RTP streams, one for regular transmission and one for nack retransmission; + // 一个 MediaStream 通常包含一个或多个 MediaStreamTrack ,例如 simulcast 场景下,一个 MediaStream 通常会包含三个不同编码质量的 MediaStreamTrack ; [AUTO-TRANSLATED:31318d43] + // A MediaStream usually contains one or more MediaStreamTrack, for example, in a simulcast scenario, a MediaStream usually contains three MediaStreamTrack of different encoding quality; + // 这种标记方式并不被 Firefox 认可,在 Firefox 生成的 SDP 中一个 a=ssrc 通常只有一行,例如: [AUTO-TRANSLATED:8c2c424c] + // This marking method is not recognized by Firefox, in the SDP generated by Firefox, one a=ssrc usually has only one line, for example: // a=ssrc:3245185839 cname:Cx4i/VTR51etgjT7 uint32_t ssrc; @@ -397,11 +420,14 @@ public: class SdpAttrSSRCGroup : public SdpItem { public: - // a=ssrc-group 定义参考 RFC 5576(https://tools.ietf.org/html/rfc5576) ,用于描述多个 ssrc 之间的关联,常见的有两种: + // a=ssrc-group 定义参考 RFC 5576(https://tools.ietf.org/html/rfc5576) ,用于描述多个 ssrc 之间的关联,常见的有两种: [AUTO-TRANSLATED:a87cbcc6] + // a=ssrc-group definition refers to RFC 5576(https://tools.ietf.org/html/rfc5576), used to describe the association between multiple ssrcs, there are two common types: // a=ssrc-group:FID 2430709021 3715850271 - // FID (Flow Identification) 最初用在 FEC 的关联中,WebRTC 中通常用于关联一组常规 RTP stream 和 重传 RTP stream 。 + // FID (Flow Identification) 最初用在 FEC 的关联中,WebRTC 中通常用于关联一组常规 RTP stream 和 重传 RTP stream 。 [AUTO-TRANSLATED:f2c0fcbb] + // FID (Flow Identification) was originally used in FEC association, and in WebRTC it is usually used to associate a group of regular RTP streams and retransmission RTP streams. // a=ssrc-group:SIM 360918977 360918978 360918980 - // 在 Chrome 独有的 SDP munging 风格的 simulcast 中使用,将三组编码质量由低到高的 MediaStreamTrack 关联在一起。 + // 在 Chrome 独有的 SDP munging 风格的 simulcast 中使用,将三组编码质量由低到高的 MediaStreamTrack 关联在一起。 [AUTO-TRANSLATED:61bf7596] + // Used in Chrome's unique SDP munging style simulcast, associating three groups of MediaStreamTrack from low to high encoding quality. std::string type { "FID" }; std::vector ssrcs; @@ -434,7 +460,8 @@ public: // a=candidate:4 1 udp 2 192.168.1.7 58107 typ host // a=candidate:
    typ std::string foundation; - // 传输媒体的类型,1代表RTP;2代表 RTCP。 + // 传输媒体的类型,1代表RTP;2代表 RTCP。 [AUTO-TRANSLATED:9ec924a6] + // The type of media to be transmitted, 1 represents RTP; 2 represents RTCP. uint32_t component; std::string transport { "udp" }; uint32_t priority; @@ -567,7 +594,8 @@ public: ////////////////////////////////////////////////////////////////// -// ssrc相关信息 +// ssrc相关信息 [AUTO-TRANSLATED:954c641d] +// ssrc related information class RtcSSRC { public: uint32_t ssrc { 0 }; @@ -580,23 +608,27 @@ public: bool empty() const { return ssrc == 0 && cname.empty(); } }; -// rtc传输编码方案 +// rtc传输编码方案 [AUTO-TRANSLATED:8b911508] +// rtc transmission encoding scheme class RtcCodecPlan { public: using Ptr = std::shared_ptr; uint8_t pt; std::string codec; uint32_t sample_rate; - // 音频时有效 + // 音频时有效 [AUTO-TRANSLATED:5b230fc8] + // Valid for audio uint32_t channel = 0; - // rtcp反馈 + // rtcp反馈 [AUTO-TRANSLATED:580378bd] + // RTCP feedback std::set rtcp_fb; std::map fmtp; std::string getFmtp(const char *key) const; }; -// rtc 媒体描述 +// rtc 媒体描述 [AUTO-TRANSLATED:b1711a11] +// RTC media description class RtcMedia { public: TrackType type { TrackType::TrackInvalid }; diff --git a/webrtc/TwccContext.cpp b/webrtc/TwccContext.cpp index 61fa4388..5125d783 100644 --- a/webrtc/TwccContext.cpp +++ b/webrtc/TwccContext.cpp @@ -39,7 +39,8 @@ void TwccContext::onRtp(uint32_t ssrc, uint16_t twcc_ext_seq, uint64_t stamp_ms) } if (needSendTwcc()) { - //其他匹配条件立即发送twcc + // 其他匹配条件立即发送twcc [AUTO-TRANSLATED:959d22b6] + // Send twcc immediately if other matching conditions are met onSendTwcc(ssrc); } } @@ -58,25 +59,30 @@ int TwccContext::checkSeqStatus(uint16_t twcc_ext_seq) const { auto max = _rtp_recv_status.rbegin()->first; auto delta = (int32_t) twcc_ext_seq - (int32_t) max; if (delta > 0 && delta < 0xFFFF / 2) { - //正常增长 + // 正常增长 [AUTO-TRANSLATED:7699c37d] + // Normal growth return (int) ExtSeqStatus::normal; } if (delta < -0xFF00) { - //回环 + // 回环 [AUTO-TRANSLATED:82956e03] + // Loop TraceL << "rtp twcc ext seq looped:" << max << " -> " << twcc_ext_seq; return (int) ExtSeqStatus::looped; } if (delta > 0xFF00) { - //回环后收到前面大的乱序的包,无法处理,丢弃 + // 回环后收到前面大的乱序的包,无法处理,丢弃 [AUTO-TRANSLATED:512cc269] + // Discard packets that are out of order and large after looping back, as they cannot be processed TraceL << "rtp twcc ext seq jumped after looped:" << max << " -> " << twcc_ext_seq; return (int) ExtSeqStatus::jumped; } auto min = _rtp_recv_status.begin()->first; if (min <= twcc_ext_seq || twcc_ext_seq <= max) { - //正常回退 + // 正常回退 [AUTO-TRANSLATED:c8c6803f] + // Normal rollback return (int) ExtSeqStatus::normal; } - //seq莫名的大幅增加或减少,无法处理,丢弃 + // seq莫名的大幅增加或减少,无法处理,丢弃 [AUTO-TRANSLATED:44dbbd28] + // Discard packets with a large increase or decrease in seq, as they cannot be processed TraceL << "rtp twcc ext seq jumped:" << max << " -> " << twcc_ext_seq; return (int) ExtSeqStatus::jumped; } @@ -85,9 +91,11 @@ void TwccContext::onSendTwcc(uint32_t ssrc) { auto max = _rtp_recv_status.rbegin()->first; auto begin = _rtp_recv_status.begin(); auto min = begin->first; - //参考时间戳的最小单位是64ms + // 参考时间戳的最小单位是64ms [AUTO-TRANSLATED:2e701a8c] + // The minimum unit of the reference timestamp is 64ms auto ref_time = begin->second >> 6; - //还原基准时间戳 + // 还原基准时间戳 [AUTO-TRANSLATED:bab53195] + // Restore the baseline timestamp auto last_time = ref_time << 6; FCI_TWCC::TwccPacketStatus status; for (auto seq = min; seq <= max; ++seq) { @@ -95,7 +103,8 @@ void TwccContext::onSendTwcc(uint32_t ssrc) { SymbolStatus symbol = SymbolStatus::not_received; auto it = _rtp_recv_status.find(seq); if (it != _rtp_recv_status.end()) { - //recv delta,单位为250us,1ms等于4x250us + // recv delta,单位为250us,1ms等于4x250us [AUTO-TRANSLATED:46a0e186] + // recv delta, unit is 250us, 1ms equals 4x250us delta = (int16_t) (4 * ((int64_t) it->second - (int64_t) last_time)); if (delta < 0 || delta > 0xFF) { symbol = SymbolStatus::large_delta; diff --git a/webrtc/TwccContext.h b/webrtc/TwccContext.h index 91485bdf..b0346bab 100644 --- a/webrtc/TwccContext.h +++ b/webrtc/TwccContext.h @@ -21,9 +21,11 @@ namespace mediakit { class TwccContext { public: using onSendTwccCB = std::function; - //每个twcc rtcp包最多表明的rtp ext seq增量 + // 每个twcc rtcp包最多表明的rtp ext seq增量 [AUTO-TRANSLATED:530d1e35] + // Maximum RTP ext seq increment indicated by each twcc rtcp packet static constexpr size_t kMaxSeqSize = 20; - //每个twcc rtcp包发送的最大时间间隔,单位毫秒 + // 每个twcc rtcp包发送的最大时间间隔,单位毫秒 [AUTO-TRANSLATED:e45656da] + // Maximum time interval for sending each twcc rtcp packet, in milliseconds static constexpr size_t kMaxTimeDelta = 256; void onRtp(uint32_t ssrc, uint16_t twcc_ext_seq, uint64_t stamp_ms); diff --git a/webrtc/WebRtcEchoTest.cpp b/webrtc/WebRtcEchoTest.cpp index cf7527a8..fcbd1266 100644 --- a/webrtc/WebRtcEchoTest.cpp +++ b/webrtc/WebRtcEchoTest.cpp @@ -39,7 +39,8 @@ void WebRtcEchoTest::onRtcp(const char *buf, size_t len) { sendRtcpPacket(buf, len, true, nullptr); } -//修改mline的a=msid属性,目的是在echo test的情况下,如果offer和answer的msid相同,chrome会忽略远端的track +// 修改mline的a=msid属性,目的是在echo test的情况下,如果offer和answer的msid相同,chrome会忽略远端的track [AUTO-TRANSLATED:f2e3da54] +// Modify the a=msid attribute of mline, the purpose is that in the echo test case, if the offer and answer have the same msid, chrome will ignore the remote track. void WebRtcEchoTest::onCheckSdp(SdpType type, RtcSession &sdp) { if (type == SdpType::answer) { for (auto &m : sdp.media) { diff --git a/webrtc/WebRtcPlayer.cpp b/webrtc/WebRtcPlayer.cpp index 83ff5830..158042d3 100644 --- a/webrtc/WebRtcPlayer.cpp +++ b/webrtc/WebRtcPlayer.cpp @@ -90,8 +90,10 @@ void WebRtcPlayer::onStartWebRTC() { } if (data.is()) { auto &buffer = data.get(); - // PPID 51: 文本string - // PPID 53: 二进制 + // PPID 51: 文本string [AUTO-TRANSLATED:69a8cf81] + // PPID 51: Text string + // PPID 53: 二进制 [AUTO-TRANSLATED:faf00c3e] + // PPID 53: Binary strong_self->sendDatachannel(0, 51, buffer.data(), buffer.size()); } else { WarnL << "Send unknown message type to webrtc player: " << data.type_name(); @@ -102,7 +104,8 @@ void WebRtcPlayer::onStartWebRTC() { void WebRtcPlayer::onDestory() { auto duration = getDuration(); auto bytes_usage = getBytesUsage(); - //流量统计事件广播 + // 流量统计事件广播 [AUTO-TRANSLATED:6b0b1234] + // Traffic statistics event broadcast GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); if (_reader && getSession()) { WarnL << "RTC播放器(" << _media_info.shortUrl() << ")结束播放,耗时(s):" << duration; @@ -119,7 +122,8 @@ void WebRtcPlayer::onRtcConfigure(RtcConfigure &configure) const { return ; } WebRtcTransportImp::onRtcConfigure(configure); - //这是播放 + // 这是播放 [AUTO-TRANSLATED:d93c019e] + // This is playing configure.audio.direction = configure.video.direction = RtpDirection::sendonly; configure.setPlayRtspInfo(playSrc->getSdp()); } diff --git a/webrtc/WebRtcPlayer.h b/webrtc/WebRtcPlayer.h index 96e9e830..a964b380 100644 --- a/webrtc/WebRtcPlayer.h +++ b/webrtc/WebRtcPlayer.h @@ -34,15 +34,19 @@ private: void sendConfigFrames(uint32_t before_seq, uint32_t sample_rate, uint32_t timestamp, uint64_t ntp_timestamp); private: - //媒体相关元数据 + // 媒体相关元数据 [AUTO-TRANSLATED:f4cf8045] + // Media related metadata MediaInfo _media_info; - //播放的rtsp源 + // 播放的rtsp源 [AUTO-TRANSLATED:9963eed1] + // Playing rtsp source std::weak_ptr _play_src; - // rtp 直接转发情况下通常会缺少 sps/pps, 在转发 rtp 前, 先发送一次相关帧信息, 部分情况下是可以播放的 + // rtp 直接转发情况下通常会缺少 sps/pps, 在转发 rtp 前, 先发送一次相关帧信息, 部分情况下是可以播放的 [AUTO-TRANSLATED:65fdf16a] + // In the case of direct RTP forwarding, sps/pps is usually missing. Before forwarding RTP, send the relevant frame information once. In some cases, it can be played. bool _send_config_frames_once { false }; - //播放rtsp源的reader对象 + // 播放rtsp源的reader对象 [AUTO-TRANSLATED:7b305055] + // Reader object for playing rtsp source RtspMediaSource::RingType::RingReader::Ptr _reader; }; diff --git a/webrtc/WebRtcPusher.cpp b/webrtc/WebRtcPusher.cpp index 7b334f87..2d47440c 100644 --- a/webrtc/WebRtcPusher.cpp +++ b/webrtc/WebRtcPusher.cpp @@ -42,14 +42,16 @@ WebRtcPusher::WebRtcPusher(const EventPoller::Ptr &poller, } bool WebRtcPusher::close(MediaSource &sender) { - //此回调在其他线程触发 + // 此回调在其他线程触发 [AUTO-TRANSLATED:c98e7686] + // This callback is triggered in another thread string err = StrPrinter << "close media: " << sender.getUrl(); weak_ptr weak_self = static_pointer_cast(shared_from_this()); getPoller()->async([weak_self, err]() { auto strong_self = weak_self.lock(); if (strong_self) { strong_self->onShutdown(SockException(Err_shutdown, err)); - //主动关闭推流,那么不延时注销 + // 主动关闭推流,那么不延时注销 [AUTO-TRANSLATED:ee7cc580] + // Actively close the stream, then do not delay the logout strong_self->_push_src = nullptr; } }); @@ -91,12 +93,14 @@ void WebRtcPusher::onRecvRtp(MediaTrack &track, const string &rid, RtpPacket::Pt } if (rtp->type == TrackAudio) { - //音频 + // 音频 [AUTO-TRANSLATED:a577d8e1] + // Audio for (auto &pr : _push_src_sim) { pr.second->onWrite(rtp, false); } } else { - //视频 + // 视频 [AUTO-TRANSLATED:904730ac] + // Video std::lock_guard lock(_mtx); auto &src = _push_src_sim[rid]; if (!src) { @@ -121,7 +125,8 @@ void WebRtcPusher::onStartWebRTC() { void WebRtcPusher::onDestory() { auto duration = getDuration(); auto bytes_usage = getBytesUsage(); - //流量统计事件广播 + // 流量统计事件广播 [AUTO-TRANSLATED:6b0b1234] + // Traffic statistics event broadcast GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); if (getSession()) { @@ -132,9 +137,11 @@ void WebRtcPusher::onDestory() { } if (_push_src && _continue_push_ms) { - //取消所有权 + // 取消所有权 [AUTO-TRANSLATED:4895d8fa] + // Cancel ownership _push_src_ownership = nullptr; - //延时10秒注销流 + // 延时10秒注销流 [AUTO-TRANSLATED:e1bb11f9] + // Delay 10 seconds to log out the stream auto push_src = std::move(_push_src); getPoller()->doDelayTask(_continue_push_ms, [push_src]() { return 0; }); } @@ -143,7 +150,8 @@ void WebRtcPusher::onDestory() { void WebRtcPusher::onRtcConfigure(RtcConfigure &configure) const { WebRtcTransportImp::onRtcConfigure(configure); - //这只是推流 + // 这只是推流 [AUTO-TRANSLATED:f877bf98] + // This is just pushing the stream configure.audio.direction = configure.video.direction = RtpDirection::recvonly; } @@ -152,7 +160,8 @@ float WebRtcPusher::getLossRate(MediaSource &sender,TrackType type) { } void WebRtcPusher::OnDtlsTransportClosed(const RTC::DtlsTransport *dtlsTransport) { - //主动关闭推流,那么不等待重推 + // 主动关闭推流,那么不等待重推 [AUTO-TRANSLATED:1ff514d7] + // Actively close the stream, then do not wait for re-pushing _push_src = nullptr; WebRtcTransportImp::OnDtlsTransportClosed(dtlsTransport); } diff --git a/webrtc/WebRtcPusher.h b/webrtc/WebRtcPusher.h index 19b04608..3cf3f94b 100644 --- a/webrtc/WebRtcPusher.h +++ b/webrtc/WebRtcPusher.h @@ -30,24 +30,32 @@ protected: void onRecvRtp(MediaTrack &track, const std::string &rid, RtpPacket::Ptr rtp) override; void onShutdown(const SockException &ex) override; void onRtcpBye() override; - //// dtls相关的回调 //// + // // dtls相关的回调 //// [AUTO-TRANSLATED:31a1f32c] + // // dtls related callbacks //// void OnDtlsTransportClosed(const RTC::DtlsTransport *dtlsTransport) override; protected: ///////MediaSourceEvent override/////// - // 关闭 + // 关闭 [AUTO-TRANSLATED:92392f02] + // Close bool close(MediaSource &sender) override; - // 播放总人数 + // 播放总人数 [AUTO-TRANSLATED:c42a3161] + // Total number of players int totalReaderCount(MediaSource &sender) override; - // 获取媒体源类型 + // 获取媒体源类型 [AUTO-TRANSLATED:34290a69] + // Get media source type MediaOriginType getOriginType(MediaSource &sender) const override; - // 获取媒体源url或者文件路径 + // 获取媒体源url或者文件路径 [AUTO-TRANSLATED:fa34d795] + // Get media source url or file path std::string getOriginUrl(MediaSource &sender) const override; - // 获取媒体源客户端相关信息 + // 获取媒体源客户端相关信息 [AUTO-TRANSLATED:037ef910] + // Get media source client related information std::shared_ptr getOriginSock(MediaSource &sender) const override; - // 由于支持断连续推,存在OwnerPoller变更的可能 + // 由于支持断连续推,存在OwnerPoller变更的可能 [AUTO-TRANSLATED:1c863b40] + // Due to support for discontinuous pushing, there is a possibility of OwnerPoller changes toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; - // 获取丢包率 + // 获取丢包率 [AUTO-TRANSLATED:ec61b378] + // Get packet loss rate float getLossRate(MediaSource &sender,TrackType type) override; private: @@ -56,15 +64,20 @@ private: private: bool _simulcast = false; - //断连续推延时 + // 断连续推延时 [AUTO-TRANSLATED:13ad578a] + // Discontinuous pushing delay uint32_t _continue_push_ms = 0; - //媒体相关元数据 + // 媒体相关元数据 [AUTO-TRANSLATED:f4cf8045] + // Media related metadata MediaInfo _media_info; - //推流的rtsp源 + // 推流的rtsp源 [AUTO-TRANSLATED:4f976bca] + // Rtsp source of the stream RtspMediaSource::Ptr _push_src; - //推流所有权 + // 推流所有权 [AUTO-TRANSLATED:d0ddf5c7] + // Stream ownership std::shared_ptr _push_src_ownership; - //推流的rtsp源,支持simulcast + // 推流的rtsp源,支持simulcast [AUTO-TRANSLATED:44be9120] + // Rtsp source of the stream, supports simulcast std::recursive_mutex _mtx; std::unordered_map _push_src_sim; std::unordered_map > _push_src_sim_ownership; diff --git a/webrtc/WebRtcSession.cpp b/webrtc/WebRtcSession.cpp index ff76dbee..d5fb06e9 100644 --- a/webrtc/WebRtcSession.cpp +++ b/webrtc/WebRtcSession.cpp @@ -31,7 +31,8 @@ static string getUserName(const char *buf, size_t len) { packet->GetMethod() != RTC::StunPacket::Method::BINDING) { return ""; } - //收到binding request请求 + // 收到binding request请求 [AUTO-TRANSLATED:eff4d773] + // Received binding request auto vec = split(packet->GetUsername(), ":"); return vec[0]; } @@ -57,16 +58,19 @@ void WebRtcSession::attachServer(const Server &server) { void WebRtcSession::onRecv_l(const char *data, size_t len) { if (_find_transport) { - // 只允许寻找一次transport + // 只允许寻找一次transport [AUTO-TRANSLATED:446fae53] + // Only allow searching for transport once _find_transport = false; auto user_name = getUserName(data, len); auto transport = WebRtcTransportManager::Instance().getItem(user_name); CHECK(transport); - //WebRtcTransport在其他poller线程上,需要切换poller线程并重新创建WebRtcSession对象 + // WebRtcTransport在其他poller线程上,需要切换poller线程并重新创建WebRtcSession对象 [AUTO-TRANSLATED:7e5534cf] + // WebRtcTransport is on another poller thread, need to switch poller thread and recreate WebRtcSession object if (!transport->getPoller()->isCurrentThread()) { auto sock = Socket::createSocket(transport->getPoller(), false); - //1、克隆socket(fd不变),切换poller线程到WebRtcTransport所在线程 + // 1、克隆socket(fd不变),切换poller线程到WebRtcTransport所在线程 [AUTO-TRANSLATED:f930bfab] + // 1. Clone socket (fd remains unchanged), switch poller thread to the thread where WebRtcTransport is located sock->cloneSocket(*(getSock())); auto server = _server; std::string str(data, len); @@ -74,11 +78,13 @@ void WebRtcSession::onRecv_l(const char *data, size_t len) { auto strong_server = server.lock(); if (strong_server) { auto session = static_pointer_cast(strong_server->createSession(sock)); - //2、创建新的WebRtcSession对象(绑定到WebRtcTransport所在线程),重新处理一遍ice binding request命令 + // 2、创建新的WebRtcSession对象(绑定到WebRtcTransport所在线程),重新处理一遍ice binding request命令 [AUTO-TRANSLATED:c75203bb] + // 2. Create a new WebRtcSession object (bound to the thread where WebRtcTransport is located), reprocess the ice binding request command session->onRecv_l(str.data(), str.size()); } }); - //3、销毁原先的socket和WebRtcSession(原先的对象跟WebRtcTransport不在同一条线程) + // 3、销毁原先的socket和WebRtcSession(原先的对象跟WebRtcTransport不在同一条线程) [AUTO-TRANSLATED:a6d6d63f] + // 3. Destroy the original socket and WebRtcSession (the original object is not on the same thread as WebRtcTransport) throw std::runtime_error("webrtc over tcp change poller: " + getPoller()->getThreadName() + " -> " + sock->getPoller()->getThreadName()); } _transport = std::move(transport); @@ -98,9 +104,12 @@ void WebRtcSession::onRecv(const Buffer::Ptr &buffer) { } void WebRtcSession::onError(const SockException &err) { - //udp链接超时,但是rtc链接不一定超时,因为可能存在链接迁移的情况 - //在udp链接迁移时,新的WebRtcSession对象将接管WebRtcTransport对象的生命周期 - //本WebRtcSession对象将在超时后自动销毁 + // udp链接超时,但是rtc链接不一定超时,因为可能存在链接迁移的情况 [AUTO-TRANSLATED:aaa9672f] + // UDP connection timeout, but RTC connection may not timeout, because there may be connection migration + // 在udp链接迁移时,新的WebRtcSession对象将接管WebRtcTransport对象的生命周期 [AUTO-TRANSLATED:7e7d19df] + // When UDP connection migrates, the new WebRtcSession object will take over the life cycle of the WebRtcTransport object + // 本WebRtcSession对象将在超时后自动销毁 [AUTO-TRANSLATED:bc903a06] + // This WebRtcSession object will be automatically destroyed after timeout WarnP(this) << err; if (!_transport) { @@ -109,9 +118,11 @@ void WebRtcSession::onError(const SockException &err) { auto self = static_pointer_cast(shared_from_this()); auto transport = std::move(_transport); getPoller()->async([transport, self]() mutable { - //延时减引用,防止使用transport对象时,销毁对象 + // 延时减引用,防止使用transport对象时,销毁对象 [AUTO-TRANSLATED:09dd6609] + // Delay decrementing the reference count to prevent the object from being destroyed when using the transport object transport->removeTuple(self.get()); - //确保transport在Session对象前销毁,防止WebRtcTransport::onDestory()时获取不到Session对象 + // 确保transport在Session对象前销毁,防止WebRtcTransport::onDestory()时获取不到Session对象 [AUTO-TRANSLATED:acd8bd77] + // Ensure that the transport is destroyed before the Session object to prevent WebRtcTransport::onDestory() from not being able to get the Session object transport = nullptr; }, false); } @@ -135,15 +146,18 @@ ssize_t WebRtcSession::onRecvHeader(const char *data, size_t len) { const char *WebRtcSession::onSearchPacketTail(const char *data, size_t len) { if (len < 2) { - // 数据不够 + // 数据不够 [AUTO-TRANSLATED:830a2785] + // Not enough data return nullptr; } uint16_t length = (((uint8_t *)data)[0] << 8) | ((uint8_t *)data)[1]; if (len < (size_t)(length + 2)) { - // 数据不够 + // 数据不够 [AUTO-TRANSLATED:830a2785] + // Not enough data return nullptr; } - // 返回rtp包末尾 + // 返回rtp包末尾 [AUTO-TRANSLATED:5134cf6f] + // Return the end of the RTP packet return data + 2 + length; } diff --git a/webrtc/WebRtcTransport.cpp b/webrtc/WebRtcTransport.cpp index dc424738..1dbe2bcd 100644 --- a/webrtc/WebRtcTransport.cpp +++ b/webrtc/WebRtcTransport.cpp @@ -37,28 +37,35 @@ using namespace std; namespace mediakit { -// RTC配置项目 +// RTC配置项目 [AUTO-TRANSLATED:19940011] +// RTC configuration project namespace Rtc { #define RTC_FIELD "rtc." -// rtp和rtcp接受超时时间 +// rtp和rtcp接受超时时间 [AUTO-TRANSLATED:0f318cc0] +// rtp and rtcp receive timeout const string kTimeOutSec = RTC_FIELD "timeoutSec"; -// 服务器外网ip +// 服务器外网ip [AUTO-TRANSLATED:23283ba6] +// Server external network ip const string kExternIP = RTC_FIELD "externIP"; -// 设置remb比特率,非0时关闭twcc并开启remb。该设置在rtc推流时有效,可以控制推流画质 +// 设置remb比特率,非0时关闭twcc并开启remb。该设置在rtc推流时有效,可以控制推流画质 [AUTO-TRANSLATED:412801db] +// Set remb bitrate, when it is not 0, turn off twcc and turn on remb. This setting is valid when rtc pushes the stream, and can control the pushing stream quality const string kRembBitRate = RTC_FIELD "rembBitRate"; // 是否转码G711音频,做到: 出rtc将g711转成aac,入rtc将g711转成opus const string kTranscodeG711 = RTC_FIELD "transcodeG711"; -// webrtc单端口udp服务器 +// webrtc单端口udp服务器 [AUTO-TRANSLATED:d17271ea] +// webrtc single-port udp server const string kPort = RTC_FIELD "port"; const string kTcpPort = RTC_FIELD "tcpPort"; -// 比特率设置 +// 比特率设置 [AUTO-TRANSLATED:2c75f5bc] +// Bitrate setting const string kStartBitrate = RTC_FIELD "start_bitrate"; const string kMaxBitrate = RTC_FIELD "max_bitrate"; const string kMinBitrate = RTC_FIELD "min_bitrate"; -// 数据通道设置 +// 数据通道设置 [AUTO-TRANSLATED:2dc48bc3] +// Data channel setting const string kDataChannelEcho = RTC_FIELD "datachannel_echo"; static onceToken token([]() { @@ -96,21 +103,28 @@ static void translateIPFromEnv(std::vector &v) { } static std::string getServerPrefix() { - //stun_user_name格式: base64(ip+udp_port+tcp_port) + _ + number - //其中ip为二进制char[4], udp_port/tcp_port为大端 uint16. - //number为自增长数,确保短时间内唯一 + // stun_user_name格式: base64(ip+udp_port+tcp_port) + _ + number [AUTO-TRANSLATED:cc3c5902] + // stun_user_name format: base64(ip+udp_port+tcp_port) + _ + number + // 其中ip为二进制char[4], udp_port/tcp_port为大端 uint16. [AUTO-TRANSLATED:92ea5521] + // Where ip is binary char[4], udp_port/tcp_port is big-endian uint16. + // number为自增长数,确保短时间内唯一 [AUTO-TRANSLATED:d31aada9] + // number is an auto-incrementing number, ensuring uniqueness in a short period of time GET_CONFIG(uint16_t, udp_port, Rtc::kPort); GET_CONFIG(uint16_t, tcp_port, Rtc::kTcpPort); char buf[8]; auto host = SockUtil::get_local_ip(); auto addr = SockUtil::make_sockaddr(host.data(), udp_port); - //拷贝ipv4地址 + // 拷贝ipv4地址 [AUTO-TRANSLATED:49c16eed] + // Copy ipv4 address memcpy(buf, &(reinterpret_cast(&addr)->sin_addr), 4); - //拷贝udp端口 + // 拷贝udp端口 [AUTO-TRANSLATED:ebb750d3] + // Copy udp port memcpy(buf + 4, &(reinterpret_cast(&addr)->sin_port), 2); - //tcp端口转大端模式 + // tcp端口转大端模式 [AUTO-TRANSLATED:4f2293de] + // Convert tcp port to big-endian mode addr = SockUtil::make_sockaddr(host.data(), tcp_port); - //拷贝tcp端口 + // 拷贝tcp端口 [AUTO-TRANSLATED:23191878] + // Copy tcp port memcpy(buf + 6, &(reinterpret_cast(&addr)->sin_port), 2); auto ret = encodeBase64(string(buf, 8)) + '_'; InfoL << "MediaServer(" << host << ":" << udp_port << ":" << tcp_port << ") prefix: " << ret; @@ -311,7 +325,8 @@ void WebRtcTransport::OnSctpAssociationMessageReceived( GET_CONFIG(bool, datachannel_echo, Rtc::kDataChannelEcho); if (datachannel_echo) { - // 回显数据 + // 回显数据 [AUTO-TRANSLATED:7868d3a4] + // Echo data _sctp->SendSctpMessage(params, ppid, msg, len); } @@ -374,7 +389,8 @@ string getFingerprint(const string &algorithm_str, const std::shared_ptrmedia[0].fingerprint.algorithm); @@ -389,7 +405,8 @@ void WebRtcTransport::onRtcConfigure(RtcConfigure &configure) const { configure.setDefaultSetting( _ice_server->GetUsernameFragment(), _ice_server->GetPassword(), RtpDirection::sendrecv, fingerprint); - // 开启remb后关闭twcc,因为开启twcc后remb无效 + // 开启remb后关闭twcc,因为开启twcc后remb无效 [AUTO-TRANSLATED:8a8feca2] + // Turn off twcc after turning on remb, because remb is invalid after turning on twcc GET_CONFIG(size_t, remb_bit_rate, Rtc::kRembBitRate); configure.enableTWCC(!remb_bit_rate); } @@ -410,18 +427,21 @@ static void setSdpBitrate(RtcSession &sdp) { std::string WebRtcTransport::getAnswerSdp(const string &offer) { try { - //// 解析offer sdp //// + // // 解析offer sdp //// [AUTO-TRANSLATED:87c1f337] + // // Parse offer sdp //// _offer_sdp = std::make_shared(); _offer_sdp->loadFrom(offer); onCheckSdp(SdpType::offer, *_offer_sdp); _offer_sdp->checkValid(); setRemoteDtlsFingerprint(*_offer_sdp); - //// sdp 配置 //// + // // sdp 配置 //// [AUTO-TRANSLATED:718a72e2] + // // sdp configuration //// RtcConfigure configure; onRtcConfigure(configure); - //// 生成answer sdp //// + // // 生成answer sdp //// [AUTO-TRANSLATED:a139475e] + // // Generate answer sdp //// _answer_sdp = configure.createAnswer(*_offer_sdp); onCheckSdp(SdpType::answer, *_answer_sdp); setSdpBitrate(*_answer_sdp); @@ -476,7 +496,8 @@ void WebRtcTransport::inputSockData(char *buf, int len, RTC::TransportTuple *tup void WebRtcTransport::sendRtpPacket(const char *buf, int len, bool flush, void *ctx) { if (_srtp_session_send) { auto pkt = _packet_pool.obtain2(); - // 预留rtx加入的两个字节 + // 预留rtx加入的两个字节 [AUTO-TRANSLATED:d1eb5cd7] + // Reserve two bytes for rtx joining pkt->setCapacity((size_t)len + SRTP_MAX_TRAILER_LEN + 2); memcpy(pkt->data(), buf, len); onBeforeEncryptRtp(pkt->data(), len, ctx); @@ -490,7 +511,8 @@ void WebRtcTransport::sendRtpPacket(const char *buf, int len, bool flush, void * void WebRtcTransport::sendRtcpPacket(const char *buf, int len, bool flush, void *ctx) { if (_srtp_session_send) { auto pkt = _packet_pool.obtain2(); - // 预留rtx加入的两个字节 + // 预留rtx加入的两个字节 [AUTO-TRANSLATED:d1eb5cd7] + // Reserve two bytes for rtx joining pkt->setCapacity((size_t)len + SRTP_MAX_TRAILER_LEN + 2); memcpy(pkt->data(), buf, len); onBeforeEncryptRtcp(pkt->data(), len, ctx); @@ -557,9 +579,11 @@ void WebRtcTransportImp::onSendSockData(Buffer::Ptr buf, bool flush, RTC::Transp } } - // 一次性发送一帧的rtp数据,提高网络io性能 + // 一次性发送一帧的rtp数据,提高网络io性能 [AUTO-TRANSLATED:fbab421e] + // Send one frame of rtp data at a time to improve network io performance if (tuple->getSock()->sockType() == SockNum::Sock_TCP) { - // 增加tcp两字节头 + // 增加tcp两字节头 [AUTO-TRANSLATED:62159f79] + // Add two-byte header to tcp auto len = buf->size(); char tcp_len[2] = { 0 }; tcp_len[0] = (len >> 8) & 0xff; @@ -594,7 +618,8 @@ bool WebRtcTransportImp::canRecvRtp() const { } void WebRtcTransportImp::onStartWebRTC() { - // 获取ssrc和pt相关信息,届时收到rtp和rtcp时分别可以根据pt和ssrc找到相关的信息 + // 获取ssrc和pt相关信息,届时收到rtp和rtcp时分别可以根据pt和ssrc找到相关的信息 [AUTO-TRANSLATED:39828247] + // Get ssrc and pt related information, so that when receiving rtp and rtcp, you can find the relevant information according to pt and ssrc respectively for (auto &m_answer : _answer_sdp->media) { if (m_answer.type == TrackApplication) { continue; @@ -613,7 +638,8 @@ void WebRtcTransportImp::onStartWebRTC() { // rtp track type --> MediaTrack if (m_answer.direction == RtpDirection::sendonly || m_answer.direction == RtpDirection::sendrecv) { - // 该类型的track 才支持发送 + // 该类型的track 才支持发送 [AUTO-TRANSLATED:b7c1e631] + // This type of track supports sending _type_to_track[m_answer.type] = track; } // send ssrc --> MediaTrack @@ -631,7 +657,8 @@ void WebRtcTransportImp::onStartWebRTC() { // rtx pt --> MediaTrack _pt_to_track.emplace(track->plan_rtx->pt, std::unique_ptr(new WrappedRtxTrack(track))); } - // 记录rtp ext类型与id的关系,方便接收或发送rtp时修改rtp ext id + // 记录rtp ext类型与id的关系,方便接收或发送rtp时修改rtp ext id [AUTO-TRANSLATED:5736bd34] + // Record the relationship between rtp ext type and id, which is convenient for modifying rtp ext id when receiving or sending rtp track->rtp_ext_ctx = std::make_shared(m_answer); weak_ptr weak_track = track; track->rtp_ext_ctx->setOnGetRtp([this, weak_track](uint8_t pt, uint32_t ssrc, const string &rid) { @@ -644,14 +671,18 @@ void WebRtcTransportImp::onStartWebRTC() { size_t index = 0; for (auto &ssrc : m_offer->rtp_ssrc_sim) { - // 记录ssrc对应的MediaTrack + // 记录ssrc对应的MediaTrack [AUTO-TRANSLATED:8e344bc1] + // Record the MediaTrack corresponding to ssrc _ssrc_to_track[ssrc.ssrc] = track; if (m_offer->rtp_rids.size() > index) { - // 支持firefox的simulcast, 提前映射好ssrc和rid的关系 + // 支持firefox的simulcast, 提前映射好ssrc和rid的关系 [AUTO-TRANSLATED:86f3e5bf] + // Support firefox's simulcast, map the relationship between ssrc and rid in advance track->rtp_ext_ctx->setRid(ssrc.ssrc, m_offer->rtp_rids[index]); } else { - // SDP munging没有rid, 它通过group-ssrc:SIM给出ssrc列表; - // 系统又要有rid,这里手工生成rid,并为其绑定ssrc + // SDP munging没有rid, 它通过group-ssrc:SIM给出ssrc列表; [AUTO-TRANSLATED:d6cd0b5b] + // SDP munging does not have rid, it gives the ssrc list through group-ssrc:SIM; + // 系统又要有rid,这里手工生成rid,并为其绑定ssrc [AUTO-TRANSLATED:f4988139] + // The system also needs a rid, so we manually generate a rid and bind it to the ssrc std::string rid = "r" + std::to_string(index); track->rtp_ext_ctx->setRid(ssrc.ssrc, rid); if (ssrc.rtx_ssrc) { @@ -664,7 +695,8 @@ void WebRtcTransportImp::onStartWebRTC() { } void WebRtcTransportImp::onCheckAnswer(RtcSession &sdp) { - // 修改answer sdp的ip、端口信息 + // 修改answer sdp的ip、端口信息 [AUTO-TRANSLATED:ff72ec1e] + // Modify the ip and port information of the answer sdp GET_CONFIG_FUNC(std::vector, extern_ips, Rtc::kExternIP, [](string str) { std::vector ret; if (str.length()) { @@ -689,7 +721,8 @@ void WebRtcTransportImp::onCheckAnswer(RtcSession &sdp) { } if (!canSendRtp()) { - // 设置我们发送的rtp的ssrc + // 设置我们发送的rtp的ssrc [AUTO-TRANSLATED:3704484a] + // Set the ssrc of the rtp we send return; } @@ -698,13 +731,16 @@ void WebRtcTransportImp::onCheckAnswer(RtcSession &sdp) { continue; } if (!m.rtp_rtx_ssrc.empty()) { - // 已经生成了ssrc + // 已经生成了ssrc [AUTO-TRANSLATED:5883cab8] + // The ssrc has been generated continue; } - // 添加answer sdp的ssrc信息 + // 添加answer sdp的ssrc信息 [AUTO-TRANSLATED:ab4c3fad] + // Add the ssrc information to the answer sdp m.rtp_rtx_ssrc.emplace_back(); auto &ssrc = m.rtp_rtx_ssrc.back(); - // 发送的ssrc我们随便定义,因为在发送rtp时会修改为此值 + // 发送的ssrc我们随便定义,因为在发送rtp时会修改为此值 [AUTO-TRANSLATED:ee8d77f0] + // We can define the ssrc we send at will, because it will be modified to this value when sending rtp ssrc.ssrc = m.type + RTP_SSRC_OFFSET; ssrc.cname = RTP_CNAME; ssrc.label = std::string(RTP_LABEL) + '-' + m.mid; @@ -734,11 +770,13 @@ void WebRtcTransportImp::onCheckSdp(SdpType type, RtcSession &sdp) { SdpAttrCandidate::Ptr makeIceCandidate(std::string ip, uint16_t port, uint32_t priority = 100, std::string proto = "udp") { auto candidate = std::make_shared(); - // rtp端口 + // rtp端口 [AUTO-TRANSLATED:b0addb27] + // rtp port candidate->component = 1; candidate->transport = proto; candidate->foundation = proto + "candidate"; - // 优先级,单candidate时随便 + // 优先级,单candidate时随便 [AUTO-TRANSLATED:7c85d820] + // Priority, random when there is only one candidate candidate->priority = priority; candidate->address = std::move(ip); candidate->port = port; @@ -760,7 +798,8 @@ void WebRtcTransportImp::onRtcConfigure(RtcConfigure &configure) const { GET_CONFIG(uint16_t, local_udp_port, Rtc::kPort); GET_CONFIG(uint16_t, local_tcp_port, Rtc::kTcpPort); - // 添加接收端口candidate信息 + // 添加接收端口candidate信息 [AUTO-TRANSLATED:cc9a6a90] + // Add the receiving port candidate information GET_CONFIG_FUNC(std::vector, extern_ips, Rtc::kExternIP, [](string str) { std::vector ret; if (str.length()) { @@ -804,7 +843,8 @@ public: _poller = std::move(poller); _on_nack = std::move(on_nack); setOnSorted(std::move(cb)); - //设置jitter buffer参数 + // 设置jitter buffer参数 [AUTO-TRANSLATED:eede98b6] + // Set jitter buffer parameters GET_CONFIG(uint32_t, nack_maxms, Rtc::kNackMaxMS); GET_CONFIG(uint32_t, nack_max_rtp, Rtc::kNackMaxSize); RtpTrackImp::setParams(nack_max_rtp, nack_maxms, nack_max_rtp / 2); @@ -819,7 +859,8 @@ public: auto seq = rtp->getSeq(); _nack_ctx.received(seq, is_rtx); if (!is_rtx) { - // 统计rtp接受情况,便于生成nack rtcp包 + // 统计rtp接受情况,便于生成nack rtcp包 [AUTO-TRANSLATED:57e0f80d] + // Statistics of rtp reception, which is convenient for generating nack rtcp packets _rtcp_context.onRtp(seq, rtp->getStamp(), rtp->ntp_stamp, sample_rate, len); } return rtp; @@ -899,7 +940,8 @@ void WebRtcTransportImp::onRtcp(const char *buf, size_t len) { switch ((RtcpType)rtcp->pt) { case RtcpType::RTCP_SR: { _alive_ticker.resetTime(); - // 对方汇报rtp发送情况 + // 对方汇报rtp发送情况 [AUTO-TRANSLATED:1389b0c8] + // The other party reports the rtp sending situation RtcpSR *sr = (RtcpSR *)rtcp; auto it = _ssrc_to_track.find(sr->ssrc); if (it != _ssrc_to_track.end()) { @@ -908,7 +950,8 @@ void WebRtcTransportImp::onRtcp(const char *buf, size_t len) { if (!rtp_chn) { WarnL << "未识别的sr rtcp包:" << rtcp->dumpString(); } else { - // 设置rtp时间戳与ntp时间戳的对应关系 + // 设置rtp时间戳与ntp时间戳的对应关系 [AUTO-TRANSLATED:e92f4749] + // Set the correspondence between rtp timestamp and ntp timestamp rtp_chn->setNtpStamp(sr->rtpts, sr->getNtpUnixStampMS()); auto rr = rtp_chn->createRtcpRR(sr, track->answer_ssrc_rtp); sendRtcpPacket(rr->data(), rr->size(), true); @@ -920,7 +963,8 @@ void WebRtcTransportImp::onRtcp(const char *buf, size_t len) { } case RtcpType::RTCP_RR: { _alive_ticker.resetTime(); - // 对方汇报rtp接收情况 + // 对方汇报rtp接收情况 [AUTO-TRANSLATED:77f50a28] + // The other party reports the rtp receiving situation RtcpRR *rr = (RtcpRR *)rtcp; for (auto item : rr->getItemList()) { auto it = _ssrc_to_track.find(item->ssrc); @@ -936,7 +980,8 @@ void WebRtcTransportImp::onRtcp(const char *buf, size_t len) { break; } case RtcpType::RTCP_BYE: { - // 对方汇报停止发送rtp + // 对方汇报停止发送rtp [AUTO-TRANSLATED:96ad0cf3] + // The other party reports the stop sending rtp RtcpBye *bye = (RtcpBye *)rtcp; for (auto ssrc : bye->getSSRC()) { auto it = _ssrc_to_track.find(*ssrc); @@ -947,7 +992,8 @@ void WebRtcTransportImp::onRtcp(const char *buf, size_t len) { _ssrc_to_track.erase(it); } onRtcpBye(); - // bye 会在 sender audio track mute 时出现, 因此不能作为 shutdown 的依据 + // bye 会在 sender audio track mute 时出现, 因此不能作为 shutdown 的依据 [AUTO-TRANSLATED:d9fdfaac] + // Bye will appear when the sender audio track is muted, so it cannot be used as the basis for shutdown break; } case RtcpType::RTCP_PSFB: @@ -967,7 +1013,8 @@ void WebRtcTransportImp::onRtcp(const char *buf, size_t len) { auto &track = it->second; auto &fci = fb->getFci(); track->nack_list.forEach(fci, [&](const RtpPacket::Ptr &rtp) { - // rtp重传 + // rtp重传 [AUTO-TRANSLATED:62a37e46] + // rtp retransmission onSendRtp(rtp, true, true); }); break; @@ -1009,7 +1056,8 @@ void WebRtcTransportImp::createRtpChannel(const string &rid, uint32_t ssrc, Medi ref = std::make_shared( getPoller(), [&track, this, rid](RtpPacket::Ptr rtp) mutable { onSortedRtp(track, rid, std::move(rtp)); }, [&track, weak_self, ssrc](const FCI_NACK &nack) mutable { - // nack发送可能由定时器异步触发 + // nack发送可能由定时器异步触发 [AUTO-TRANSLATED:186d6723] + // Nack sending may be triggered asynchronously by a timer auto strong_self = weak_self.lock(); if (strong_self) { strong_self->onSendNack(track, nack, ssrc); @@ -1027,7 +1075,8 @@ void WebRtcTransportImp::onRtp(const char *buf, size_t len, uint64_t stamp_ms) { _alive_ticker.resetTime(); RtpHeader *rtp = (RtpHeader *)buf; - // 根据接收到的rtp的pt信息,找到该流的信息 + // 根据接收到的rtp的pt信息,找到该流的信息 [AUTO-TRANSLATED:9a97682c] + // Find the information of the stream according to the pt information of the received rtp auto it = _pt_to_track.find(rtp->pt); if (it == _pt_to_track.end()) { WarnL << "unknown rtp pt:" << (int)rtp->pt; @@ -1040,14 +1089,16 @@ void WrappedRtpTrack::inputRtp(const char *buf, size_t len, uint64_t stamp_ms, R #if 0 auto seq = ntohs(rtp->seq); if (track->media->type == TrackVideo && seq % 100 == 0) { - //此处模拟接受丢包 + // 此处模拟接受丢包 [AUTO-TRANSLATED:7e8c2e5c] + // Simulate packet loss here return; } #endif auto ssrc = ntohl(rtp->ssrc); - // 修改ext id至统一 + // 修改ext id至统一 [AUTO-TRANSLATED:0769b0ec] + // Modify the ext id to be unified string rid; auto twcc_ext = track->rtp_ext_ctx->changeRtpExtId(rtp, true, &rid, RtpExtType::transport_cc); @@ -1060,24 +1111,28 @@ void WrappedRtpTrack::inputRtp(const char *buf, size_t len, uint64_t stamp_ms, R _transport.createRtpChannel(rid, ssrc, *track); } - // 解析并排序rtp + // 解析并排序rtp [AUTO-TRANSLATED:d382b65d] + // Parse and sort rtp ref->inputRtp(track->media->type, track->plan_rtp->sample_rate, (uint8_t *)buf, len, false); } void WrappedRtxTrack::inputRtp(const char *buf, size_t len, uint64_t stamp_ms, RtpHeader *rtp) { - // 修改ext id至统一 + // 修改ext id至统一 [AUTO-TRANSLATED:0769b0ec] + // Modify the ext id to be unified string rid; track->rtp_ext_ctx->changeRtpExtId(rtp, true, &rid, RtpExtType::transport_cc); auto &ref = track->rtp_channel[rid]; if (!ref) { - // 再接收到对应的rtp前,丢弃rtx包 + // 再接收到对应的rtp前,丢弃rtx包 [AUTO-TRANSLATED:d4ca6d69] + // Discard rtx packets before receiving the corresponding rtp WarnL << "unknown rtx rtp, rid:" << rid << ", ssrc:" << ntohl(rtp->ssrc) << ", codec:" << track->plan_rtp->codec << ", seq:" << ntohs(rtp->seq); return; } - // 这里是rtx重传包 + // 这里是rtx重传包 [AUTO-TRANSLATED:6efd3766] + // This is the rtx retransmission packet // https://datatracker.ietf.org/doc/html/rfc4588#section-4 auto payload = rtp->getPayloadData(); auto size = rtp->getPayloadSize(len); @@ -1085,9 +1140,11 @@ void WrappedRtxTrack::inputRtp(const char *buf, size_t len, uint64_t stamp_ms, R return; } - // 前两个字节是原始的rtp的seq + // 前两个字节是原始的rtp的seq [AUTO-TRANSLATED:c57ff92d] + // The first two bytes are the original rtp seq auto origin_seq = payload[0] << 8 | payload[1]; - // rtx 转换为 rtp + // rtx 转换为 rtp [AUTO-TRANSLATED:be27f61b] + // rtx converted to rtp rtp->pt = track->plan_rtp->pt; rtp->seq = htons(origin_seq); rtp->ssrc = htonl(ref->getSSRC()); @@ -1116,11 +1173,13 @@ void WebRtcTransportImp::onSendTwcc(uint32_t ssrc, const string &twcc_fci) { void WebRtcTransportImp::onSortedRtp(MediaTrack &track, const string &rid, RtpPacket::Ptr rtp) { if (track.media->type == TrackVideo && _pli_ticker.elapsedTime() > 2000) { - // 定期发送pli请求关键帧,方便非rtc等协议 + // 定期发送pli请求关键帧,方便非rtc等协议 [AUTO-TRANSLATED:b992f020] + // Regularly send pli requests for key frames, which is convenient for non-rtc protocols _pli_ticker.resetTime(); sendRtcpPli(rtp->getSSRC()); - // 开启remb,则发送remb包调节比特率 + // 开启remb,则发送remb包调节比特率 [AUTO-TRANSLATED:20e98cea] + // If remb is enabled, send remb packets to adjust the bitrate GET_CONFIG(size_t, remb_bit_rate, Rtc::kRembBitRate); if (remb_bit_rate && _answer_sdp->supportRtcpFb(SdpConst::kRembRtcpFb)) { sendRtcpRemb(rtp->getSSRC(), remb_bit_rate); @@ -1135,23 +1194,27 @@ void WebRtcTransportImp::onSortedRtp(MediaTrack &track, const string &rid, RtpPa void WebRtcTransportImp::onSendRtp(const RtpPacket::Ptr &rtp, bool flush, bool rtx) { auto &track = _type_to_track[rtp->type]; if (!track) { - // 忽略,对方不支持该编码类型 + // 忽略,对方不支持该编码类型 [AUTO-TRANSLATED:498ee936] + // Ignore, the other party does not support this encoding type return; } if (!rtx) { - // 统计rtp发送情况,好做sr汇报 + // 统计rtp发送情况,好做sr汇报 [AUTO-TRANSLATED:142028b2] + // Statistics of RTP sending, for SR reporting track->rtcp_context_send->onRtp( rtp->getSeq(), rtp->getStamp(), rtp->ntp_stamp, rtp->sample_rate, rtp->size() - RtpPacket::kRtpTcpHeaderSize); track->nack_list.pushBack(rtp); #if 0 - //此处模拟发送丢包 + // 此处模拟发送丢包 [AUTO-TRANSLATED:9612f08e] + // Simulate packet loss here if (rtp->type == TrackVideo && rtp->getSeq() % 100 == 0) { return; } #endif } else { - // 发送rtx重传包 + // 发送rtx重传包 [AUTO-TRANSLATED:ae60e1fd] + // Send RTX retransmission packets // TraceL << "send rtx rtp:" << rtp->getSeq(); } pair ctx { rtx, track.get() }; @@ -1164,31 +1227,37 @@ void WebRtcTransportImp::onBeforeEncryptRtp(const char *buf, int &len, void *ctx auto header = (RtpHeader *)buf; if (!pr->first || !pr->second->plan_rtx) { - // 普通的rtp,或者不支持rtx, 修改目标pt和ssrc + // 普通的rtp,或者不支持rtx, 修改目标pt和ssrc [AUTO-TRANSLATED:e1264971] + // Ordinary RTP, or does not support RTX, modify the target PT and SSRC pr->second->rtp_ext_ctx->changeRtpExtId(header, false); header->pt = pr->second->plan_rtp->pt; header->ssrc = htonl(pr->second->answer_ssrc_rtp); } else { - // 重传的rtp, rtx + // 重传的rtp, rtx [AUTO-TRANSLATED:e863a518] + // Retransmitted RTP, RTX pr->second->rtp_ext_ctx->changeRtpExtId(header, false); header->pt = pr->second->plan_rtx->pt; if (pr->second->answer_ssrc_rtx) { - // 有rtx单独的ssrc,有些情况下,浏览器支持rtx,但是未指定rtx单独的ssrc + // 有rtx单独的ssrc,有些情况下,浏览器支持rtx,但是未指定rtx单独的ssrc [AUTO-TRANSLATED:181cee9a] + // RTX has a separate SSRC, in some cases, the browser supports RTX, but does not specify a separate SSRC for RTX header->ssrc = htonl(pr->second->answer_ssrc_rtx); } else { - // 未单独指定rtx的ssrc,那么使用rtp的ssrc + // 未单独指定rtx的ssrc,那么使用rtp的ssrc [AUTO-TRANSLATED:dcafdd75] + // If RTX SSRC is not specified separately, use the RTP SSRC header->ssrc = htonl(pr->second->answer_ssrc_rtp); } auto origin_seq = ntohs(header->seq); - // seq跟原来的不一样 + // seq跟原来的不一样 [AUTO-TRANSLATED:803f9a5e] + // The sequence is different from the original header->seq = htons(_rtx_seq[pr->second->media->type]); ++_rtx_seq[pr->second->media->type]; auto payload = header->getPayloadData(); auto payload_size = header->getPayloadSize(len); if (payload_size) { - // rtp负载后移两个字节,这两个字节用于存放osn + // rtp负载后移两个字节,这两个字节用于存放osn [AUTO-TRANSLATED:b7eed70e] + // The RTP payload is shifted two bytes, these two bytes are used to store OSN // https://datatracker.ietf.org/doc/html/rfc4588#section-4 memmove(payload + 2, payload, payload_size); } @@ -1332,15 +1401,18 @@ void push_plugin(Session &sender, const WebRtcArgs &args, const onCreateWebRtc & auto push_failed = (bool)src; while (src) { - // 尝试断连后继续推流 + // 尝试断连后继续推流 [AUTO-TRANSLATED:9eaaa6dd] + // Try to continue streaming after disconnecting auto rtsp_src = dynamic_pointer_cast(src); if (!rtsp_src) { - // 源不是rtsp推流产生的 + // 源不是rtsp推流产生的 [AUTO-TRANSLATED:47b87993] + // The source is not generated by RTSP streaming break; } auto ownership = rtsp_src->getOwnership(); if (!ownership) { - // 获取推流源所有权失败 + // 获取推流源所有权失败 [AUTO-TRANSLATED:256190b2] + // Failed to get the ownership of the streaming source break; } push_src = std::move(rtsp_src); @@ -1364,10 +1436,12 @@ void push_plugin(Session &sender, const WebRtcArgs &args, const onCreateWebRtc & cb(*rtc); }; - // rtsp推流需要鉴权 + // rtsp推流需要鉴权 [AUTO-TRANSLATED:c2cbb7ed] + // RTSP streaming requires authentication auto flag = NOTICE_EMIT(BroadcastMediaPublishArgs, Broadcast::kBroadcastMediaPublish, MediaOriginType::rtc_push, info, invoker, sender); if (!flag) { - // 该事件无人监听,默认不鉴权 + // 该事件无人监听,默认不鉴权 [AUTO-TRANSLATED:e1fbc6ae] + // No one is listening to this event, authentication is not enabled by default invoker("", ProtocolOption()); } } @@ -1380,7 +1454,8 @@ void play_plugin(Session &sender, const WebRtcArgs &args, const onCreateWebRtc & cb(WebRtcException(SockException(Err_other, err))); return; } - // webrtc播放的是rtsp的源 + // webrtc播放的是rtsp的源 [AUTO-TRANSLATED:649ae489] + // WebRTC plays the RTSP source info.schema = RTC_SCHEMA; MediaSource::findAsync(info, session_ptr, [=](const MediaSource::Ptr &src_in) mutable { auto src = dynamic_pointer_cast(src_in); @@ -1388,17 +1463,20 @@ void play_plugin(Session &sender, const WebRtcArgs &args, const onCreateWebRtc & cb(WebRtcException(SockException(Err_other, "stream not found"))); return; } - // 还原成rtc,目的是为了hook时识别哪种播放协议 + // 还原成rtc,目的是为了hook时识别哪种播放协议 [AUTO-TRANSLATED:fe8dd2dc] + // Restore to RTC, the purpose is to identify which playback protocol during hooking info.schema = "rtc"; auto rtc = WebRtcPlayer::create(EventPollerPool::Instance().getPoller(), src, info); cb(*rtc); }); }; - // 广播通用播放url鉴权事件 + // 广播通用播放url鉴权事件 [AUTO-TRANSLATED:81e24be4] + // Broadcast a universal playback URL authentication event auto flag = NOTICE_EMIT(BroadcastMediaPlayedArgs, Broadcast::kBroadcastMediaPlayed, info, invoker, sender); if (!flag) { - // 该事件无人监听,默认不鉴权 + // 该事件无人监听,默认不鉴权 [AUTO-TRANSLATED:e1fbc6ae] + // No one is listening to this event, authentication is not enabled by default invoker(""); } } @@ -1430,7 +1508,8 @@ static void setWebRtcArgs(const WebRtcArgs &args, WebRtcInterface &rtc) { auto cand_str = trim(args["cand_udp"]); auto ip_port = toolkit::split(cand_str, ":"); if (ip_port.size() == 2) { - // udp优先 + // udp优先 [AUTO-TRANSLATED:b428f63d] + // UDP priority auto ice_cand = makeIceCandidate(ip_port[0], atoi(ip_port[1].data()), preferred_tcp ? 100 : 120, "udp"); cands.emplace_back(std::move(*ice_cand)); } @@ -1439,13 +1518,15 @@ static void setWebRtcArgs(const WebRtcArgs &args, WebRtcInterface &rtc) { auto cand_str = trim(args["cand_tcp"]); auto ip_port = toolkit::split(cand_str, ":"); if (ip_port.size() == 2) { - // tcp模式 + // tcp模式 [AUTO-TRANSLATED:62fddf91] + // TCP mode auto ice_cand = makeIceCandidate(ip_port[0], atoi(ip_port[1].data()), preferred_tcp ? 120 : 100, "tcp"); cands.emplace_back(std::move(*ice_cand)); } } if (!cands.empty()) { - // udp优先 + // udp优先 [AUTO-TRANSLATED:b428f63d] + // UDP priority rtc.setIceCandidate(std::move(cands)); } } @@ -1453,7 +1534,8 @@ static void setWebRtcArgs(const WebRtcArgs &args, WebRtcInterface &rtc) { static onceToken s_rtc_auto_register([]() { #if !defined (NDEBUG) - // debug模式才开启echo插件 + // debug模式才开启echo插件 [AUTO-TRANSLATED:48fcb116] + // Enable echo plugin only in debug mode WebRtcPluginManager::Instance().registerPlugin("echo", echo_plugin); #endif WebRtcPluginManager::Instance().registerPlugin("push", push_plugin); diff --git a/webrtc/WebRtcTransport.h b/webrtc/WebRtcTransport.h index 4b33ee66..a3553c7c 100644 --- a/webrtc/WebRtcTransport.h +++ b/webrtc/WebRtcTransport.h @@ -28,7 +28,8 @@ namespace mediakit { -//RTC配置项目 +// RTC配置项目 [AUTO-TRANSLATED:65784416] +// RTC configuration project namespace Rtc { extern const std::string kPort; extern const std::string kTcpPort; @@ -73,11 +74,17 @@ public: /** * 创建对象 + * Create object + + * [AUTO-TRANSLATED:830344e4] */ virtual void onCreate(); /** * 销毁对象 + * Destroy object + + * [AUTO-TRANSLATED:1016b97b] */ virtual void onDestory(); @@ -85,11 +92,19 @@ public: * 创建webrtc answer sdp * @param offer offer sdp * @return answer sdp + * Create webrtc answer sdp + * @param offer offer sdp + * @return answer sdp + + * [AUTO-TRANSLATED:d9b027d7] */ std::string getAnswerSdp(const std::string &offer) override final; /** * 获取对象唯一id + * Get object unique id + + * [AUTO-TRANSLATED:9ad519c6] */ const std::string& getIdentifier() const override; const std::string& deleteRandStr() const override; @@ -99,6 +114,12 @@ public: * @param buf 数据指针 * @param len 数据长度 * @param tuple 数据来源 + * Socket receives udp data + * @param buf data pointer + * @param len data length + * @param tuple data source + + * [AUTO-TRANSLATED:1ee86069] */ void inputSockData(char *buf, int len, RTC::TransportTuple *tuple); @@ -108,6 +129,13 @@ public: * @param len rtcp长度 * @param flush 是否flush socket * @param ctx 用户指针 + * Send rtp + * @param buf rtcp content + * @param len rtcp length + * @param flush whether to flush socket + * @param ctx user pointer + + * [AUTO-TRANSLATED:aa833695] */ void sendRtpPacket(const char *buf, int len, bool flush, void *ctx = nullptr); void sendRtcpPacket(const char *buf, int len, bool flush, void *ctx = nullptr); @@ -117,7 +145,8 @@ public: Session::Ptr getSession() const; protected: - //// dtls相关的回调 //// + // // dtls相关的回调 //// [AUTO-TRANSLATED:31a1f32c] + // // dtls related callbacks //// void OnDtlsTransportConnecting(const RTC::DtlsTransport *dtlsTransport) override; void OnDtlsTransportConnected(const RTC::DtlsTransport *dtlsTransport, RTC::SrtpSession::CryptoSuite srtpCryptoSuite, @@ -133,7 +162,8 @@ protected: void OnDtlsTransportApplicationDataReceived(const RTC::DtlsTransport *dtlsTransport, const uint8_t *data, size_t len) override; protected: - //// ice相关的回调 /// + // // ice相关的回调 /// [AUTO-TRANSLATED:30abf693] + // // ice related callbacks /// void OnIceServerSendStunPacket(const RTC::IceServer *iceServer, const RTC::StunPacket *packet, RTC::TransportTuple *tuple) override; void OnIceServerConnected(const RTC::IceServer *iceServer) override; void OnIceServerCompleted(const RTC::IceServer *iceServer) override; @@ -183,7 +213,8 @@ private: std::shared_ptr _srtp_session_send; std::shared_ptr _srtp_session_recv; Ticker _ticker; - // 循环池 + // 循环池 [AUTO-TRANSLATED:b7059f37] + // Cycle pool ResourcePool _packet_pool; #ifdef ENABLE_SCTP @@ -291,26 +322,36 @@ private: private: bool _preferred_tcp = false; uint16_t _rtx_seq[2] = {0, 0}; - //用掉的总流量 + // 用掉的总流量 [AUTO-TRANSLATED:713b61c9] + // Total traffic used uint64_t _bytes_usage = 0; - //保持自我强引用 + // 保持自我强引用 [AUTO-TRANSLATED:c2dc228f] + // Keep self strong reference Ptr _self; - //检测超时的定时器 + // 检测超时的定时器 [AUTO-TRANSLATED:a58e1388] + // Timeout detection timer Timer::Ptr _timer; - //刷新计时器 + // 刷新计时器 [AUTO-TRANSLATED:61eb11e5] + // Refresh timer Ticker _alive_ticker; - //pli rtcp计时器 + // pli rtcp计时器 [AUTO-TRANSLATED:a1a5fd18] + // pli rtcp timer Ticker _pli_ticker; - //twcc rtcp发送上下文对象 + // twcc rtcp发送上下文对象 [AUTO-TRANSLATED:aef6476a] + // twcc rtcp send context object TwccContext _twcc_ctx; - //根据发送rtp的track类型获取相关信息 + // 根据发送rtp的track类型获取相关信息 [AUTO-TRANSLATED:ff31c272] + // Get relevant information based on the track type of the sent rtp MediaTrack::Ptr _type_to_track[2]; - //根据rtcp的ssrc获取相关信息,收发rtp和rtx的ssrc都会记录 + // 根据rtcp的ssrc获取相关信息,收发rtp和rtx的ssrc都会记录 [AUTO-TRANSLATED:6c57cd48] + // Get relevant information based on the ssrc of the rtcp, the ssrc of sending and receiving rtp and rtx will be recorded std::unordered_map _ssrc_to_track; - //根据接收rtp的pt获取相关信息 + // 根据接收rtp的pt获取相关信息 [AUTO-TRANSLATED:39e56d7d] + // Get relevant information based on the pt of the received rtp std::unordered_map> _pt_to_track; std::vector _cands; - //http访问时的host ip + // http访问时的host ip [AUTO-TRANSLATED:e8fe6957] + // Host ip for http access std::string _local_ip; };