AI automatically translates all comments in the code into English (#3917)

This commit is contained in:
alex
2024-09-19 14:53:50 +08:00
committed by GitHub
parent 046de691cb
commit 4152dcd409
279 changed files with 10602 additions and 3038 deletions

View File

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

View File

@@ -41,12 +41,16 @@ public:
/**
* MultiMediaSourceMuxer类的包装方便初学者使用
* Wrapper class for MultiMediaSourceMuxer, making it easier for beginners to use.
* [AUTO-TRANSLATED:101887bd]
*/
class DevChannel : public MultiMediaSourceMuxer{
public:
using Ptr = std::shared_ptr<DevChannel>;
//fDuration<=0为直播否则为点播
// fDuration<=0为直播否则为点播 [AUTO-TRANSLATED:e3b6029a]
// fDuration<=0 for live streaming, otherwise for on-demand
DevChannel(const MediaTuple& tuple, float duration = 0, const ProtocolOption &option = ProtocolOption())
: MultiMediaSourceMuxer(tuple, duration, option) {}
@@ -54,6 +58,11 @@ public:
* 初始化视频Track
* 相当于MultiMediaSourceMuxer::addTrack(VideoTrack::Ptr );
* @param info 视频相关信息
* Initialize the video Track
* Equivalent to MultiMediaSourceMuxer::addTrack(VideoTrack::Ptr );
* @param info Video related information
* [AUTO-TRANSLATED:6845d52d]
*/
bool initVideo(const VideoInfo &info);
@@ -61,6 +70,11 @@ public:
* 初始化音频Track
* 相当于MultiMediaSourceMuxer::addTrack(AudioTrack::Ptr );
* @param info 音频相关信息
* Initialize the audio Track
* Equivalent to MultiMediaSourceMuxer::addTrack(AudioTrack::Ptr );
* @param info Audio related information
* [AUTO-TRANSLATED:5be9d272]
*/
bool initAudio(const AudioInfo &info);
@@ -70,6 +84,13 @@ public:
* @param len 数据指针长度
* @param dts 解码时间戳单位毫秒等于0时内部会自动生成时间戳
* @param pts 播放时间戳单位毫秒等于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;

View File

@@ -24,9 +24,11 @@ bool MediaSink::addTrack(const Track::Ptr &track_in) {
return false;
}
if (!_enable_audio) {
// 关闭音频时,加快单视频流注册速度
// 关闭音频时,加快单视频流注册速度 [AUTO-TRANSLATED:4d5a361d]
// Speed up single video stream registration when audio is off
if (track_in->getTrackType() == TrackAudio) {
// 音频被全局忽略
// 音频被全局忽略 [AUTO-TRANSLATED:a8134a0b]
// Audio is globally ignored
InfoL << "Audio disabled, audio track ignored";
return false;
}
@@ -35,7 +37,8 @@ bool MediaSink::addTrack(const Track::Ptr &track_in) {
WarnL << "All track is ready, add track too late: " << track_in->getCodecName();
return false;
}
// 克隆Track只拷贝其数据不拷贝其数据转发关系
// 克隆Track只拷贝其数据不拷贝其数据转发关系 [AUTO-TRANSLATED:09edaa31]
// Clone Track, only copy its data, not its data forwarding relationship
auto track = track_in->clone();
CHECK(track, "Clone track failed: ", track_in->getCodecName());
auto index = track->getIndex();
@@ -55,11 +58,13 @@ bool MediaSink::addTrack(const Track::Ptr &track_in) {
GET_CONFIG(uint32_t, kMaxUnreadyFrame, General::kUnreadyFrameCache);
if (frame_unread.size() > kMaxUnreadyFrame) {
// 未就绪的的track不能缓存太多的帧否则可能内存溢出
// 未就绪的的track不能缓存太多的帧否则可能内存溢出 [AUTO-TRANSLATED:23958376]
// Unready tracks cannot cache too many frames, otherwise memory may overflow
frame_unread.clear();
WarnL << "Cached frame of unready track(" << frame->getCodecName() << ") is too much, now cleared";
}
// 还有Track未就绪先缓存之
// 还有Track未就绪先缓存之 [AUTO-TRANSLATED:f96eadfa]
// There are still unready tracks, cache them first
frame_unread.emplace_back(Frame::getCacheAbleFrame(frame));
return true;
});
@@ -86,7 +91,8 @@ bool MediaSink::inputFrame(const Frame::Ptr &frame) {
it->second.second = true;
auto ret = it->second.first->inputFrame(frame);
if (_mute_audio_maker && frame->getTrackType() == TrackVideo) {
// 视频驱动产生静音音频
// 视频驱动产生静音音频 [AUTO-TRANSLATED:2a8c789c]
// Video driver generates silent audio
_mute_audio_maker->inputFrame(frame);
}
checkTrackIfReady();
@@ -97,7 +103,8 @@ void MediaSink::checkTrackIfReady() {
if (!_all_track_ready && !_track_ready_callback.empty()) {
for (auto &pr : _track_map) {
if (pr.second.second && pr.second.first->ready()) {
// Track由未就绪状态转换成就绪状态我们就触发onTrackReady回调
// Track由未就绪状态转换成就绪状态我们就触发onTrackReady回调 [AUTO-TRANSLATED:f8975e53]
// When a Track transitions from an unready state to a ready state, we trigger the onTrackReady callback
auto it = _track_ready_callback.find(pr.first);
if (it != _track_ready_callback.end()) {
it->second();
@@ -130,31 +137,36 @@ void MediaSink::checkTrackIfReady() {
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;
}
@@ -181,9 +193,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();
@@ -195,13 +209,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); });
@@ -213,7 +230,8 @@ void MediaSink::emitAllTrackReady() {
}
void MediaSink::onAllTrackReady_l() {
// 是否添加静音音频
// 是否添加静音音频 [AUTO-TRANSLATED:bbfbfe73]
// Whether to add silent audio
if (_add_mute_audio) {
addMuteAudioTrack();
}
@@ -263,11 +281,13 @@ static uint8_t ADTS_CONFIG[2] = { 0x15, 0x88 };
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;
}
auto audio_idx = frame->dts() / MUTE_ADTS_DATA_MS;

View File

@@ -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:
@@ -62,6 +76,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:
@@ -69,6 +87,10 @@ public:
/**
* 输入frame
* @param frame
* Input frame
* @param frame
* [AUTO-TRANSLATED:7aaa5bba]
*/
bool inputFrame(const Frame::Ptr &frame) override;
@@ -76,6 +98,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;
@@ -83,48 +110,79 @@ 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<Track::Ptr> getTracks(bool trackReady = true) const override;
/**
* 判断是否已经触发onAllTrackReady事件
* Determine whether the onAllTrackReady event has been triggered
* [AUTO-TRANSLATED:fb8b4c71]
*/
bool isAllTrackReady() const;
/**
* 设置是否开启音频
* Set whether to enable audio
* [AUTO-TRANSLATED:0e9a3ef0]
*/
void enableAudio(bool flag);
/**
* 设置单音频
* Set single audio
* [AUTO-TRANSLATED:48fc734a]
*/
void setOnlyAudio();
/**
* 设置是否开启添加静音音频
* Set whether to enable adding mute audio
* [AUTO-TRANSLATED:49efef10]
*/
void enableMuteAudio(bool flag);
/**
* 是否有视频track
* Whether there is a video track
* [AUTO-TRANSLATED:4c4d651d]
*/
bool haveVideo() const;
@@ -133,33 +191,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();
@@ -185,6 +264,9 @@ class MediaSinkDelegate : public MediaSink {
public:
/**
* 设置track监听器
* Set track listener
* [AUTO-TRANSLATED:cedc97d7]
*/
void setTrackListener(TrackListener *listener);

View File

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

View File

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

View File

@@ -35,7 +35,8 @@ public:
class FramePacedSender : public FrameWriterInterface, public std::enable_shared_from_this<FramePacedSender> {
public:
using OnFrame = std::function<void(const Frame::Ptr &frame)>;
// 最小缓存100ms数据
// 最小缓存100ms数据 [AUTO-TRANSLATED:7b2fcb0d]
// Minimum cache 100ms data
static constexpr auto kMinCacheMS = 100;
FramePacedSender(uint32_t paced_sender_ms, OnFrame cb) {
@@ -73,21 +74,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()) {
@@ -181,7 +186,8 @@ void MultiMediaSourceMuxer::forEachRtpSender(const std::function<void(const std:
MultiMediaSourceMuxer::MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_sec, const ProtocolOption &option): _tuple(tuple) {
if (!option.stream_replace.empty()) {
// 支持在on_publish hook中替换stream_id
// 支持在on_publish hook中替换stream_id [AUTO-TRANSLATED:375eb2ff]
// Support replacing stream_id in on_publish hook
_tuple.stream = option.stream_replace;
}
_poller = EventPollerPool::Instance().getPoller();
@@ -212,7 +218,8 @@ MultiMediaSourceMuxer::MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_
_fmp4 = dynamic_pointer_cast<FMP4MediaSourceMuxer>(Recorder::createRecorder(Recorder::type_fmp4, _tuple, option));
}
//音频相关设置
// 音频相关设置 [AUTO-TRANSLATED:6ee58d57]
// Audio related settings
enableAudio(option.enable_audio);
enableMuteAudio(option.add_mute_audio);
}
@@ -221,7 +228,8 @@ void MultiMediaSourceMuxer::setMediaListener(const std::weak_ptr<MediaSourceEven
setDelegate(listener);
auto self = shared_from_this();
//拦截事件
// 拦截事件 [AUTO-TRANSLATED:100ca068]
// Intercept events
if (_rtmp) {
_rtmp->setListener(self);
}
@@ -274,61 +282,72 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) {
try {
return listener->totalReaderCount(sender);
} catch (MediaSourceEvent::NotImplemented &) {
//listener未重载totalReaderCount
// listener未重载totalReaderCount [AUTO-TRANSLATED:f098007e]
// Listener did not reload totalReaderCount
return totalReaderCount();
}
}
//此函数可能跨线程调用
// 此函数可能跨线程调用 [AUTO-TRANSLATED:e8c5f74d]
// This function may be called across threads
bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) {
CHECK(getOwnerPoller(MediaSource::NullMediaSource())->isCurrentThread(), "Can only call setupRecord in it's owner poller");
onceToken token(nullptr, [&]() {
if (_option.mp4_as_player && type == Recorder::type_mp4) {
//开启关闭mp4录制触发观看人数变化相关事件
// 开启关闭mp4录制触发观看人数变化相关事件 [AUTO-TRANSLATED:b63a8deb]
// Turn on/off mp4 recording, trigger events related to changes in the number of viewers
onReaderChanged(sender, totalReaderCount());
}
});
switch (type) {
case Recorder::type_hls : {
if (start && !_hls) {
//开始录制
// 开始录制 [AUTO-TRANSLATED:36d99250]
// Start recording
_option.hls_save_path = custom_path;
auto hls = dynamic_pointer_cast<HlsRecorder>(makeRecorder(sender, getTracks(), type, _option));
if (hls) {
//设置HlsMediaSource的事件监听器
// 设置HlsMediaSource的事件监听器 [AUTO-TRANSLATED:69990c92]
// Set the event listener for HlsMediaSource
hls->setListener(shared_from_this());
}
_hls = hls;
} else if (!start && _hls) {
//停止录制
// 停止录制 [AUTO-TRANSLATED:3dee9292]
// Stop recording
_hls = nullptr;
}
return true;
}
case Recorder::type_mp4 : {
if (start && !_mp4) {
//开始录制
// 开始录制 [AUTO-TRANSLATED:36d99250]
// Start recording
_option.mp4_save_path = custom_path;
_option.mp4_max_second = max_second;
_mp4 = makeRecorder(sender, getTracks(), type, _option);
} else if (!start && _mp4) {
//停止录制
// 停止录制 [AUTO-TRANSLATED:3dee9292]
// Stop recording
_mp4 = nullptr;
}
return true;
}
case Recorder::type_hls_fmp4: {
if (start && !_hls_fmp4) {
//开始录制
// 开始录制 [AUTO-TRANSLATED:36d99250]
// Start recording
_option.hls_save_path = custom_path;
auto hls = dynamic_pointer_cast<HlsFMP4Recorder>(makeRecorder(sender, getTracks(), type, _option));
if (hls) {
//设置HlsMediaSource的事件监听器
// 设置HlsMediaSource的事件监听器 [AUTO-TRANSLATED:69990c92]
// Set the event listener for HlsMediaSource
hls->setListener(shared_from_this());
}
_hls_fmp4 = hls;
} else if (!start && _hls_fmp4) {
//停止录制
// 停止录制 [AUTO-TRANSLATED:3dee9292]
// Stop recording
_hls_fmp4 = nullptr;
}
return true;
@@ -361,7 +380,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;
@@ -388,7 +408,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);
@@ -414,7 +435,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);
@@ -430,12 +452,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;
@@ -462,7 +486,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;
}
}
@@ -474,7 +499,8 @@ std::shared_ptr<MultiMediaSourceMuxer> MultiMediaSourceMuxer::getMuxer(MediaSour
bool MultiMediaSourceMuxer::onTrackReady(const Track::Ptr &track) {
auto &stamp = _stamps[track->getIndex()];
if (_dur_sec > 0.01) {
// 点播
// 点播 [AUTO-TRANSLATED:f0b0f74a]
// On-demand
stamp.setPlayBack();
}
@@ -570,7 +596,8 @@ void MultiMediaSourceMuxer::createGopCacheIfNeed() {
auto src = std::make_shared<MediaSourceForMuxer>(weak_self.lock());
_ring = std::make_shared<RingType>(1024, [weak_self, src](int size) {
if (auto strong_self = weak_self.lock()) {
// 切换到归属线程
// 切换到归属线程 [AUTO-TRANSLATED:abcf859b]
// Switch to the owning thread
strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() {
strong_self->onReaderChanged(*src, strong_self->totalReaderCount());
});
@@ -607,7 +634,8 @@ void MultiMediaSourceMuxer::resetTracks() {
bool MultiMediaSourceMuxer::onTrackFrame(const Frame::Ptr &frame_in) {
auto frame = frame_in;
if (_option.modify_stamp != ProtocolOption::kModifyStampOff) {
// 时间戳不采用原始的绝对时间戳
// 时间戳不采用原始的绝对时间戳 [AUTO-TRANSLATED:8beb3bf7]
// Timestamp does not use the original absolute timestamp
frame = std::make_shared<FrameStamp>(frame, _stamps[frame->getIndex()], _option.modify_stamp);
}
return _paced_sender ? _paced_sender->inputFrame(frame) : onTrackFrame_l(frame);
@@ -641,17 +669,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());
}
}
@@ -661,8 +692,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) ||
(_ts ? _ts->isEnabled() : false) ||
@@ -673,7 +706,8 @@ bool MultiMediaSourceMuxer::isEnabled(){
_mp4;
if (_is_enable) {
//无人观看时,不刷新计时器,因为无人观看时每次都会检查一遍所以刷新计数器无意义且浪费cpu
// 无人观看时,不刷新计时器,因为无人观看时每次都会检查一遍所以刷新计数器无意义且浪费cpu [AUTO-TRANSLATED:03ab47cf]
// When no one is watching, do not refresh the timer, because each time no one is watching, it will be checked, so refreshing the counter is meaningless and wastes cpu
_last_check.resetTime();
}
}

View File

@@ -41,33 +41,54 @@ public:
/**
* 设置事件监听器
* @param listener 监听器
* Set event listener
* @param listener Listener
* [AUTO-TRANSLATED:d829419b]
*/
void setMediaListener(const std::weak_ptr<MediaSourceEvent> &listener);
/**
* 设置Track就绪事件监听器
* @param listener 事件监听器
* Set Track ready event listener
* @param listener Event listener
* [AUTO-TRANSLATED:64262ac5]
*/
void setTrackListener(const std::weak_ptr<Listener> &listener);
/**
* 返回总的消费者个数
* Return the total number of consumers
* [AUTO-TRANSLATED:5eaac131]
*/
int totalReaderCount() const;
/**
* 判断是否生效(是否正在转其他协议)
* Determine whether it is effective (whether it is being converted to another protocol)
* [AUTO-TRANSLATED:ca92165c]
*/
bool isEnabled();
/**
* 设置MediaSource时间戳
* @param stamp 时间戳
* Set MediaSource timestamp
* @param stamp Timestamp
* [AUTO-TRANSLATED:a75cc2fa]
*/
void setTimeStamp(uint32_t stamp);
/**
* 重置track
* Reset track
* [AUTO-TRANSLATED:95dc0b4f]
*/
void resetTracks() override;
@@ -77,6 +98,11 @@ public:
* 观看总人数
* @param sender 事件发送者
* @return 观看总人数
* Total number of viewers
* @param sender Event sender
* @return Total number of viewers
* [AUTO-TRANSLATED:f4d7146c]
*/
int totalReaderCount(MediaSource &sender) override;
@@ -86,6 +112,13 @@ public:
* @param start 开始或停止
* @param custom_path 开启录制时,指定自定义路径
* @return 是否设置成功
* Set recording status
* @param type Recording type
* @param start Start or stop
* @param custom_path Specify a custom path when recording is enabled
* @return Whether the setting is successful
* [AUTO-TRANSLATED:cb1fd8a9]
*/
bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) override;
@@ -93,6 +126,11 @@ public:
* 获取录制状态
* @param type 录制类型
* @return 录制状态
* Get recording status
* @param type Recording type
* @return Recording status
* [AUTO-TRANSLATED:798afa71]
*/
bool isRecording(MediaSource &sender, Recorder::type type) override;
@@ -103,12 +141,24 @@ public:
* @param ssrc rtp的ssrc
* @param is_udp 是否为udp
* @param cb 启动成功或失败回调
* Start sending ps-rtp stream
* @param dst_url Target ip or domain name
* @param dst_port Target port
* @param ssrc rtp's ssrc
* @param is_udp Whether it is udp
* @param cb Start success or failure callback
* [AUTO-TRANSLATED:620416c2]
*/
void startSendRtp(MediaSource &sender, const MediaSourceEvent::SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb) override;
/**
* 停止ps-rtp发送
* @return 是否成功
* Stop ps-rtp sending
* @return Whether it is successful
* [AUTO-TRANSLATED:b91e2055]
*/
bool stopSendRtp(MediaSource &sender, const std::string &ssrc) override;
@@ -116,16 +166,27 @@ public:
* 获取所有Track
* @param trackReady 是否筛选过滤未就绪的track
* @return 所有Track
* Get all Tracks
* @param trackReady Whether to filter out unready tracks
* @return All Tracks
* [AUTO-TRANSLATED:53755f5d]
*/
std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const override;
/**
* 获取所属线程
* Get the thread it belongs to
* [AUTO-TRANSLATED:a4dc847e]
*/
toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override;
/**
* 获取本对象
* Get this object
* [AUTO-TRANSLATED:5e119bb3]
*/
std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) const override;
@@ -142,17 +203,29 @@ protected:
* 某track已经准备好其ready()状态返回true
* 此时代表可以获取其例如sps pps等相关信息了
* @param track
* A certain track is ready, its ready() status returns true,
* This means that you can get information such as sps pps, etc.
* @param track
* [AUTO-TRANSLATED:05659d48]
*/
bool onTrackReady(const Track::Ptr & track) override;
/**
* 所有Track已经准备好
* All Tracks are ready,
* [AUTO-TRANSLATED:c54d02e2]
*/
void onAllTrackReady() override;
/**
* 某Track输出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);
@@ -182,7 +255,8 @@ private:
toolkit::EventPoller::Ptr _poller;
RingType::Ptr _ring;
//对象个数统计
// 对象个数统计 [AUTO-TRANSLATED:3b43e8c2]
// Object count statistics
toolkit::ObjectStatistic<MultiMediaSourceMuxer> _statistic;
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -39,7 +39,8 @@ bool loadIniConfig(const char *ini_path) {
return false;
}
}
////////////广播名称///////////
// //////////广播名称/////////// [AUTO-TRANSLATED:439b2d74]
// //////////Broadcast Name///////////
namespace Broadcast {
const string kBroadcastMediaChanged = "kBroadcastMediaChanged";
const string kBroadcastRecordMP4 = "kBroadcastRecordMP4";
@@ -68,7 +69,8 @@ const string kBroadcastPlayerCountChanged = "kBroadcastPlayerCountChanged";
} // namespace Broadcast
// 通用配置项目
// 通用配置项目 [AUTO-TRANSLATED:ca344202]
// General Configuration Items
namespace General {
#define GENERAL_FIELD "general."
const string kMediaServerId = GENERAL_FIELD "mediaServerId";
@@ -165,7 +167,8 @@ static onceToken token([]() {
});
} // !Protocol
////////////HTTP配置///////////
// //////////HTTP配置/////////// [AUTO-TRANSLATED:a281d694]
// //////////HTTP Configuration///////////
namespace Http {
#define HTTP_FIELD "http."
const string kSendBufSize = HTTP_FIELD "sendBufSize";
@@ -208,7 +211,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";
@@ -216,7 +220,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";
@@ -227,7 +232,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;
@@ -237,7 +243,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";
@@ -253,13 +260,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";
@@ -273,14 +283,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([]() {
@@ -290,7 +304,8 @@ static onceToken token([]() {
});
} // namespace MultiCast
////////////录像配置///////////
// //////////录像配置/////////// [AUTO-TRANSLATED:19de3e96]
// //////////Recording Configuration///////////
namespace Record {
#define RECORD_FIELD "record."
const string kAppName = RECORD_FIELD "appName";
@@ -310,7 +325,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";
@@ -336,7 +352,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";
@@ -441,7 +458,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);
@@ -491,7 +509,8 @@ private:
MemThreadInfo *ptr;
};
// 该变量主要确保线程退出后才能释放MemThreadInfo变量
// 该变量主要确保线程退出后才能释放MemThreadInfo变量 [AUTO-TRANSLATED:a72494b0]
// This variable mainly ensures that the MemThreadInfo variable can be released only after the thread exits
static thread_local MemThreadInfoLocal s_thread_mem_info;
uint64_t getTotalMemUsage() {

View File

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

View File

@@ -22,6 +22,12 @@ namespace mediakit {
* 本项目采用类MIT协议用户在履行MIT协议义务的同时应当同时遵循保留ZLMediaKit软件版权信息的义务。
* 用户不得去除ZLMediaKit提供的各种服务中包括但不限于 "title"、"Server"、"User-Agent" 等字段中 "ZLMediaKit" 的信息。
* 否则本项目主要权利人(项目发起人、主要作者)保留声索起诉的权利。
* This project adopts a class MIT license. Users, while fulfilling the obligations of the MIT license, should also follow the obligation to retain the copyright information of ZLMediaKit software.
* Users may not remove the "ZLMediaKit" information from the various services provided by ZLMediaKit, including but not limited to the "title", "Server", "User-Agent" fields.
* Otherwise, the main rights holder of this project (project initiator, main author) reserves the right to claim and sue.
* [AUTO-TRANSLATED:f214f734]
*/
#if !defined(ENABLE_VERSION)
const char kServerName[] = "ZLMediaKit-8.0(build in " __DATE__ " " __TIME__ ")";

View File

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