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

@@ -30,7 +30,8 @@ void FlvMuxer::start(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr
}
if (!poller->isCurrentThread()) {
weak_ptr<FlvMuxer> weak_self = getSharedPtr();
//延时两秒启动录制目的是为了等待config帧收集完毕
// 延时两秒启动录制目的是为了等待config帧收集完毕 [AUTO-TRANSLATED:d359f59d]
// Start recording after a delay of two seconds, the purpose is to wait for the config frame to be collected.
poller->doDelayTask(2000, [weak_self, poller, media, start_pts]() {
auto strong_self = weak_self.lock();
if (strong_self) {
@@ -91,7 +92,8 @@ BufferRaw::Ptr FlvMuxer::obtainBuffer(const void *data, size_t len) {
}
void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &src) {
//发送flv文件头
// 发送flv文件头 [AUTO-TRANSLATED:ee2c5556]
// Send the flv file header.
auto buffer = obtainBuffer();
buffer->setCapacity(sizeof(FLVHeader));
buffer->setSize(sizeof(FLVHeader));
@@ -105,7 +107,8 @@ void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &src) {
header->length = htonl(FLVHeader::kFlvHeaderLength);
header->have_video = src->haveVideo();
header->have_audio = src->haveAudio();
//memset时已经赋值为0
// memset时已经赋值为0 [AUTO-TRANSLATED:0f71eef1]
// It has already been assigned to 0 during memset.
//header->previous_tag_size0 = 0;
//flv header
@@ -167,13 +170,15 @@ void FlvRecorder::startRecord(const EventPoller::Ptr &poller, const RtmpMediaSou
const string &file_path) {
stop();
lock_guard<recursive_mutex> lck(_file_mtx);
//开辟文件写缓存
// 开辟文件写缓存 [AUTO-TRANSLATED:22d1c17f]
// Allocate file write cache.
std::shared_ptr<char> fileBuf(new char[FILE_BUF_SIZE], [](char *ptr) {
if (ptr) {
delete[] ptr;
}
});
//新建文件
// 新建文件 [AUTO-TRANSLATED:f3d512a6]
// Create a new file.
_file.reset(File::create_file(file_path, "wb"), [fileBuf](FILE *fp) {
if (fp) {
fflush(fp);
@@ -184,7 +189,8 @@ void FlvRecorder::startRecord(const EventPoller::Ptr &poller, const RtmpMediaSou
throw std::runtime_error(StrPrinter << "打开文件失败:" << file_path);
}
//设置文件写缓存
// 设置文件写缓存 [AUTO-TRANSLATED:a767e55c]
// Set the file write cache.
setvbuf(_file.get(), fileBuf.get(), _IOFBF, FILE_BUF_SIZE);
start(poller, media);
}

View File

@@ -31,7 +31,8 @@ void FlvPlayer::play(const string &url) {
void FlvPlayer::onResponseHeader(const string &status, const HttpClient::HttpHeader &header) {
if (status != "200" && status != "206") {
// http状态码不符合预期
// http状态码不符合预期 [AUTO-TRANSLATED:2b6996f7]
// HTTP status code does not meet expectations
throw invalid_argument("bad http status code:" + status);
}
@@ -56,7 +57,8 @@ void FlvPlayer::onResponseCompleted(const SockException &ex) {
void FlvPlayer::onResponseBody(const char *buf, size_t size) {
if (!_benchmark_mode) {
// 性能测试模式不做数据解析节省cpu
// 性能测试模式不做数据解析节省cpu [AUTO-TRANSLATED:53e4af73]
// Performance test mode does not parse data to save CPU
FlvSplitter::input(buf, size);
}
}

View File

@@ -17,17 +17,21 @@ namespace mediakit {
const char *FlvSplitter::onSearchPacketTail(const char *data, size_t len) {
if (!_flv_started) {
//还没获取到flv头
// 还没获取到flv头 [AUTO-TRANSLATED:d1c8deaa]
// Not yet got the flv header
if (len < sizeof(FLVHeader)) {
//数据不够
// 数据不够 [AUTO-TRANSLATED:72802244]
// Insufficient data
return nullptr;
}
return data + sizeof(FLVHeader);
}
//获取到flv头处理tag数据
// 获取到flv头处理tag数据 [AUTO-TRANSLATED:a15a91da]
// Got the flv header, processing tag data
if (len < sizeof(RtmpTagHeader)) {
//数据不够
// 数据不够 [AUTO-TRANSLATED:72802244]
// Insufficient data
return nullptr;
}
return data + sizeof(RtmpTagHeader);
@@ -35,7 +39,8 @@ const char *FlvSplitter::onSearchPacketTail(const char *data, size_t len) {
ssize_t FlvSplitter::onRecvHeader(const char *data, size_t len) {
if (!_flv_started) {
//获取到flv头了
// 获取到flv头了 [AUTO-TRANSLATED:1da417c0]
// Got the flv header
auto header = reinterpret_cast<const FLVHeader *>(data);
if (memcmp(header->flv, "FLV", 3)) {
throw std::invalid_argument("不是flv容器格式");
@@ -57,7 +62,8 @@ ssize_t FlvSplitter::onRecvHeader(const char *data, size_t len) {
return 0;
}
//获取到flv头处理tag数据
// 获取到flv头处理tag数据 [AUTO-TRANSLATED:a15a91da]
// Got the flv header, processing tag data
auto tag = reinterpret_cast<const RtmpTagHeader *>(data);
auto data_size = load_be24(tag->data_size);
_type = tag->type;

View File

@@ -74,7 +74,8 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track) {
case CodecG711U: flvAudioType = (uint8_t)RtmpAudioCodec::g711u; break;
case CodecOpus: {
flvAudioType = (uint8_t)RtmpAudioCodec::opus;
// opus不通过flags获取音频相关信息
// opus不通过flags获取音频相关信息 [AUTO-TRANSLATED:0ddf328b]
// opus does not get audio information through flags
iSampleRate = 44100;
iSampleBit = 16;
iChannel = 2;
@@ -82,7 +83,8 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track) {
}
case CodecAAC: {
flvAudioType = (uint8_t)RtmpAudioCodec::aac;
// aac不通过flags获取音频相关信息
// aac不通过flags获取音频相关信息 [AUTO-TRANSLATED:63ac5081]
// aac does not get audio information through flags
iSampleRate = 44100;
iSampleBit = 16;
iChannel = 2;

View File

@@ -81,7 +81,8 @@ public:
uint8_t chunk_id : 6;
#else
uint8_t chunk_id : 6;
//0、1、2、3分别对应 12、8、4、1长度
// 0、1、2、3分别对应 12、8、4、1长度 [AUTO-TRANSLATED:31d67e40]
// 0, 1, 2, 3 correspond to lengths of 12, 8, 4, 1 respectively
uint8_t fmt : 2;
#endif
uint8_t time_stamp[3];
@@ -99,27 +100,37 @@ public:
//File version (for example, 0x01 for FLV version 1)
uint8_t version;
#if __BYTE_ORDER == __BIG_ENDIAN
//保留,置0
// 保留,置0 [AUTO-TRANSLATED:46985374]
// Preserve, set to 0
uint8_t : 5;
//是否有音频
// 是否有音频 [AUTO-TRANSLATED:9467870a]
// Whether there is audio
uint8_t have_audio: 1;
//保留,置0
// 保留,置0 [AUTO-TRANSLATED:46985374]
// Preserve, set to 0
uint8_t : 1;
//是否有视频
// 是否有视频 [AUTO-TRANSLATED:42d0ed81]
// Whether there is video
uint8_t have_video: 1;
#else
//是否有视频
// 是否有视频 [AUTO-TRANSLATED:42d0ed81]
// Whether there is video
uint8_t have_video: 1;
//保留,置0
// 保留,置0 [AUTO-TRANSLATED:46985374]
// Preserve, set to 0
uint8_t : 1;
//是否有音频
// 是否有音频 [AUTO-TRANSLATED:9467870a]
// Whether there is audio
uint8_t have_audio: 1;
//保留,置0
// 保留,置0 [AUTO-TRANSLATED:46985374]
// Preserve, set to 0
uint8_t : 5;
#endif
//The length of this header in bytes,固定为9
// The length of this header in bytes,固定为9 [AUTO-TRANSLATED:126988fc]
// The length of this header in bytes, fixed to 9
uint32_t length;
//固定为0
// 固定为0 [AUTO-TRANSLATED:d266c0a7]
// Fixed to 0
uint32_t previous_tag_size0;
};
@@ -185,12 +196,16 @@ public:
void clear();
// video config frame和key frame都返回true
// 用于gop缓存定位
// video config frame和key frame都返回true [AUTO-TRANSLATED:de025c52]
// video config frame and key frame both return true
// 用于gop缓存定位 [AUTO-TRANSLATED:828204e5]
// Used for gop cache positioning
bool isVideoKeyFrame() const;
// aac config或h264/h265 config返回true支持增强型rtmp
// 用于缓存解码配置信息
// aac config或h264/h265 config返回true支持增强型rtmp [AUTO-TRANSLATED:221955ec]
// aac config or h264/h265 config returns true, supports enhanced rtmp
// 用于缓存解码配置信息 [AUTO-TRANSLATED:19304f64]
// Used to cache decoding configuration information
bool isConfigFrame() const;
int getRtmpCodecId() const;
@@ -207,12 +222,16 @@ private:
RtmpPacket &operator=(const RtmpPacket &that);
private:
//对象个数统计
// 对象个数统计 [AUTO-TRANSLATED:3b43e8c2]
// Object count statistics
toolkit::ObjectStatistic<RtmpPacket> _statistic;
};
/**
* rtmp metadata基类用于描述rtmp格式信息
* rtmp metadata base class, used to describe rtmp format information
* [AUTO-TRANSLATED:8ced489c]
*/
class Metadata {
public:
@@ -231,6 +250,9 @@ protected:
/**
* metadata中除音视频外的其他描述部分
* Other descriptive parts in metadata besides audio and video
* [AUTO-TRANSLATED:e11f031f]
*/
class TitleMeta : public Metadata {
public:
@@ -256,7 +278,8 @@ public:
AudioMeta(const AudioTrack::Ptr &audio);
};
//根据音频track获取flags
// 根据音频track获取flags [AUTO-TRANSLATED:a25fdd07]
// Get flags based on audio track
uint8_t getAudioRtmpFlags(const Track::Ptr &track);
////////////////// rtmp video //////////////////////////
@@ -284,7 +307,8 @@ enum class RtmpVideoCodec : uint32_t {
h264 = 7, // avc
h265 = 12, // 国内扩展
// 增强型rtmp FourCC
// 增强型rtmp FourCC [AUTO-TRANSLATED:442b77fb]
// Enhanced rtmp FourCC
fourcc_vp9 = MKBETAG('v', 'p', '0', '9'),
fourcc_av1 = MKBETAG('a', 'v', '0', '1'),
fourcc_hevc = MKBETAG('h', 'v', 'c', '1')

View File

@@ -26,6 +26,9 @@ public:
/**
* 设置rtmp环形缓存
* Set rtmp ring buffer
* [AUTO-TRANSLATED:0a25f795]
*/
void setRtmpRing(const RingType::Ptr &ring) {
_ring = ring;
@@ -34,6 +37,11 @@ public:
/**
* 输入rtmp包
* @param rtmp rtmp包
* Input rtmp packet
* @param rtmp rtmp packet
* [AUTO-TRANSLATED:3a0f0599]
*/
virtual void inputRtmp(const RtmpPacket::Ptr &rtmp) {
if (_ring) {

View File

@@ -19,12 +19,14 @@ size_t RtmpDemuxer::trackCount(const AMFValue &metadata) {
size_t ret = 0;
metadata.object_for_each([&](const string &key, const AMFValue &val) {
if (key == "videocodecid") {
// 找到视频
// 找到视频 [AUTO-TRANSLATED:e66249fc]
// Find video
++ret;
return;
}
if (key == "audiocodecid") {
// 找到音频
// 找到音频 [AUTO-TRANSLATED:126ce656]
// Find audio
++ret;
return;
}
@@ -60,12 +62,14 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val) {
return;
}
if (key == "videocodecid") {
// 找到视频
// 找到视频 [AUTO-TRANSLATED:e66249fc]
// Find video
videocodecid = &val;
return;
}
if (key == "audiocodecid") {
// 找到音频
// 找到音频 [AUTO-TRANSLATED:126ce656]
// Find audio
audiocodecid = &val;
return;
}
@@ -79,12 +83,14 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val) {
}
});
if (videocodecid) {
// 有视频
// 有视频 [AUTO-TRANSLATED:8d6ad811]
// Has video
ret = true;
makeVideoTrack(*videocodecid, videodatarate * 1024);
}
if (audiocodecid) {
// 有音频
// 有音频 [AUTO-TRANSLATED:8f9ac7f1]
// Has audio
ret = true;
makeAudioTrack(*audiocodecid, audiosamplerate, audiochannels, audiosamplesize, audiodatarate * 1024);
}
@@ -93,7 +99,8 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val) {
}
if (ret) {
// metadata中存在track相关的描述那么我们根据metadata判断有多少个track
// metadata中存在track相关的描述那么我们根据metadata判断有多少个track [AUTO-TRANSLATED:47e02e95]
// If there is a track-related description in the metadata, we determine the number of tracks based on the metadata
addTrackCompleted();
}
return ret;
@@ -140,15 +147,18 @@ void RtmpDemuxer::makeVideoTrack(const Track::Ptr &track, int bit_rate) {
if (_video_rtmp_decoder) {
return;
}
// 生成Track对象
// 生成Track对象 [AUTO-TRANSLATED:8c7aee28]
// Generate Track object
_video_track = dynamic_pointer_cast<VideoTrack>(track);
if (!_video_track) {
return;
}
// 生成rtmpCodec对象以便解码rtmp
// 生成rtmpCodec对象以便解码rtmp [AUTO-TRANSLATED:a3c81353]
// Generate rtmpCodec object to decode rtmp
_video_rtmp_decoder = Factory::getRtmpDecoderByTrack(_video_track);
if (!_video_rtmp_decoder) {
// 找不到相应的rtmp解码器该track无效
// 找不到相应的rtmp解码器该track无效 [AUTO-TRANSLATED:bbea0d74]
// Cannot find the corresponding rtmp decoder, the track is invalid
_video_track.reset();
return;
}
@@ -161,15 +171,18 @@ void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec, int sample_rate, in
if (_audio_rtmp_decoder) {
return;
}
// 生成Track对象
// 生成Track对象 [AUTO-TRANSLATED:8c7aee28]
// Generate Track object
_audio_track = dynamic_pointer_cast<AudioTrack>(Factory::getAudioTrackByAmf(audioCodec, sample_rate, channels, sample_bit));
if (!_audio_track) {
return;
}
// 生成rtmpCodec对象以便解码rtmp
// 生成rtmpCodec对象以便解码rtmp [AUTO-TRANSLATED:a3c81353]
// Generate rtmpCodec object to decode rtmp
_audio_rtmp_decoder = Factory::getRtmpDecoderByTrack(_audio_track);
if (!_audio_rtmp_decoder) {
// 找不到相应的rtmp解码器该track无效
// 找不到相应的rtmp解码器该track无效 [AUTO-TRANSLATED:bbea0d74]
// Cannot find the corresponding rtmp decoder, the track is invalid
_audio_track.reset();
return;
}

View File

@@ -31,12 +31,21 @@ public:
/**
* 开始解复用
* @param pkt rtmp包
* Start demultiplexing
* @param pkt rtmp packet
* [AUTO-TRANSLATED:3a6f81de]
*/
void inputRtmp(const RtmpPacket::Ptr &pkt);
/**
* 获取节目总时长
* @return 节目总时长,单位秒
* Get the total duration of the program
* @return Total duration of the program, in seconds
* [AUTO-TRANSLATED:6b2ec56c]
*/
float getDuration() const;

View File

@@ -32,6 +32,13 @@ namespace mediakit {
* 其中metadata是非必须的有些编码格式也没有config帧(比如MP3)
* 只要生成了这三要素那么要实现rtmp推流、rtmp服务器就很简单了
* rtmp推拉流协议中先传递metadata然后传递config帧然后一直传递普通帧
* Data abstraction of rtmp media source
* rtmp has three key elements: metadata, config frame, and ordinary frame
* Metadata is optional, and some encoding formats do not have config frames (such as MP3)
* As long as these three elements are generated, it is very simple to implement rtmp push stream and rtmp server
* In the rtmp push and pull stream protocol, metadata is transmitted first, then config frame, and then ordinary frames are continuously transmitted
* [AUTO-TRANSLATED:72d515c8]
*/
class RtmpMediaSource : public MediaSource, public toolkit::RingDelegate<RtmpPacket::Ptr>, private PacketCache<RtmpPacket>{
public:
@@ -45,6 +52,13 @@ public:
* @param app 应用名
* @param stream_id 流id
* @param ring_size 可以设置固定的环形缓冲大小0则自适应
* Constructor
* @param vhost Virtual host name
* @param app Application name
* @param stream_id Stream id
* @param ring_size You can set a fixed ring buffer size, 0 is adaptive
* [AUTO-TRANSLATED:5dd23423]
*/
RtmpMediaSource(const MediaTuple& tuple, int ring_size = RTMP_GOP_SIZE): MediaSource(RTMP_SCHEMA, tuple), _ring_size(ring_size) {}
@@ -58,6 +72,9 @@ public:
/**
* 获取媒体源的环形缓冲
* Get the ring buffer of the media source
* [AUTO-TRANSLATED:75ac76b6]
*/
const RingType::Ptr &getRing() const {
return _ring;
@@ -71,6 +88,10 @@ public:
/**
* 获取播放器个数
* @return
* Get the number of players
* @return
* [AUTO-TRANSLATED:0ba31e32]
*/
int readerCount() override {
return _ring ? _ring->readerCount() : 0;
@@ -78,6 +99,9 @@ public:
/**
* 获取metadata
* Get metadata
* [AUTO-TRANSLATED:270cc022]
*/
template <typename FUNC>
void getMetaData(const FUNC &func) const {
@@ -89,6 +113,9 @@ public:
/**
* 获取所有的config帧
* Get all config frames
* [AUTO-TRANSLATED:cc3f5b85]
*/
template <typename FUNC>
void getConfigFrame(const FUNC &func) {
@@ -100,17 +127,27 @@ public:
/**
* 设置metadata
* Set metadata
* [AUTO-TRANSLATED:e32234cf]
*/
virtual void setMetaData(const AMFValue &metadata);
/**
* 输入rtmp包
* @param pkt rtmp包
* Input rtmp packet
* @param pkt rtmp packet
* [AUTO-TRANSLATED:ac020e50]
*/
void onWrite(RtmpPacket::Ptr pkt, bool = true) override;
/**
* 获取当前时间戳
* Get the current timestamp
* [AUTO-TRANSLATED:42e38069]
*/
uint32_t getTimeStamp(TrackType trackType) override;
@@ -132,9 +169,15 @@ private:
* 批量flush rtmp包时触发该函数
* @param rtmp_list rtmp包列表
* @param key_pos 是否包含关键帧
* Trigger this function when batch flushing rtmp packets
* @param rtmp_list rtmp packet list
* @param key_pos Whether it contains key frames
* [AUTO-TRANSLATED:581fe3a4]
*/
void onFlush(std::shared_ptr<toolkit::List<RtmpPacket::Ptr> > rtmp_list, bool key_pos) override {
//如果不存在视频那么就没有存在GOP缓存的意义所以is_key一直为true确保一直清空GOP缓存
// 如果不存在视频那么就没有存在GOP缓存的意义所以is_key一直为true确保一直清空GOP缓存 [AUTO-TRANSLATED:5818a8d8]
// If there is no video, then there is no point in having a GOP cache, so is_key is always true to ensure that the GOP cache is always cleared
_ring->write(std::move(rtmp_list), _have_video ? key_pos : true);
}

View File

@@ -6,11 +6,13 @@ namespace mediakit {
uint32_t RtmpMediaSource::getTimeStamp(TrackType trackType) {
assert(trackType >= TrackInvalid && trackType < TrackMax);
if (trackType != TrackInvalid) {
// 获取某track的时间戳
// 获取某track的时间戳 [AUTO-TRANSLATED:2735ed10]
// Get the timestamp of a track
return _track_stamps[trackType];
}
// 获取所有track的最小时间戳
// 获取所有track的最小时间戳 [AUTO-TRANSLATED:179a75cd]
// Get the minimum timestamp of all tracks
uint32_t ret = UINT32_MAX;
for (auto &stamp : _track_stamps) {
if (stamp > 0 && stamp < ret) {
@@ -47,7 +49,8 @@ void RtmpMediaSource::setMetaData(const AMFValue &metadata) {
void RtmpMediaSource::onWrite(RtmpPacket::Ptr pkt, bool /*= true*/) {
bool is_video = pkt->type_id == MSG_VIDEO;
_speed[is_video ? TrackVideo : TrackAudio] += pkt->size();
// 保存当前时间戳
// 保存当前时间戳 [AUTO-TRANSLATED:2b09ff42]
// Save the current timestamp
switch (pkt->type_id) {
case MSG_VIDEO: _track_stamps[TrackVideo] = pkt->time_stamp, _have_video = true; break;
case MSG_AUDIO: _track_stamps[TrackAudio] = pkt->time_stamp, _have_audio = true; break;
@@ -58,7 +61,8 @@ void RtmpMediaSource::onWrite(RtmpPacket::Ptr pkt, bool /*= true*/) {
std::lock_guard<std::recursive_mutex> lock(_mtx);
_config_frame_map[pkt->type_id] = pkt;
if (!_ring) {
// 注册后收到config帧更新到各播放器
// 注册后收到config帧更新到各播放器 [AUTO-TRANSLATED:7200693d]
// After registration, receive the config frame and update it to each player
return;
}
}
@@ -73,8 +77,10 @@ void RtmpMediaSource::onWrite(RtmpPacket::Ptr pkt, bool /*= true*/) {
strong_self->onReaderChanged(size);
};
// GOP默认缓冲512组RTMP包每组RTMP包时间戳相同(如果开启合并写了那么每组为合并写时间内的RTMP包),
// 每次遇到关键帧第一个RTMP包则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开)
// GOP默认缓冲512组RTMP包每组RTMP包时间戳相同(如果开启合并写了那么每组为合并写时间内的RTMP包), [AUTO-TRANSLATED:4a372774]
// GOP defaults to buffering 512 groups of RTMP packets, each group of RTMP packets has the same timestamp (if merge writing is enabled, then each group is the RTMP packet within the merge writing time),
// 每次遇到关键帧第一个RTMP包则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开) [AUTO-TRANSLATED:dee67297]
// Every time a key frame's first RTMP packet is encountered, the GOP cache will be cleared (because there is a new key frame, which can also achieve instant opening)
_ring = std::make_shared<RingType>(_ring_size, std::move(lam));
if (_metadata) {
regist();
@@ -93,7 +99,8 @@ RtmpMediaSourceImp::RtmpMediaSourceImp(const MediaTuple &tuple, int ringSize)
void RtmpMediaSourceImp::setMetaData(const AMFValue &metadata) {
if (!_demuxer->loadMetaData(metadata)) {
// 该metadata无效需要重新生成
// 该metadata无效需要重新生成 [AUTO-TRANSLATED:44a65c0c]
// This metadata is invalid and needs to be regenerated
_metadata = metadata;
_recreate_metadata = true;
}
@@ -102,12 +109,14 @@ void RtmpMediaSourceImp::setMetaData(const AMFValue &metadata) {
void RtmpMediaSourceImp::onWrite(RtmpPacket::Ptr pkt, bool /*= true*/) {
if (!_all_track_ready || _muxer->isEnabled()) {
// 未获取到所有Track后或者开启转协议那么需要解复用rtmp
// 未获取到所有Track后或者开启转协议那么需要解复用rtmp [AUTO-TRANSLATED:76f6f56e]
// If all Tracks are not obtained, or protocol conversion is enabled, then demultiplexing rtmp is required
_demuxer->inputRtmp(pkt);
}
GET_CONFIG(bool, directProxy, Rtmp::kDirectProxy);
if (directProxy) {
//直接代理模式才直接使用原始rtmp
// 直接代理模式才直接使用原始rtmp [AUTO-TRANSLATED:ece580ea]
// Only direct proxy mode uses the original rtmp directly
RtmpMediaSource::onWrite(std::move(pkt));
}
}
@@ -123,7 +132,8 @@ void RtmpMediaSourceImp::setProtocolOption(const ProtocolOption &option) {
_muxer = std::make_shared<MultiMediaSourceMuxer>(_tuple, _demuxer->getDuration(), _option);
_muxer->setMediaListener(getListener());
_muxer->setTrackListener(std::static_pointer_cast<RtmpMediaSourceImp>(shared_from_this()));
// 让_muxer对象拦截一部分事件(比如说录像相关事件)
// 让_muxer对象拦截一部分事件(比如说录像相关事件) [AUTO-TRANSLATED:7d27c400]
// Let the _muxer object intercept some events (such as recording related events)
MediaSource::setListener(_muxer);
for (auto &track : _demuxer->getTracks(false)) {
@@ -158,7 +168,8 @@ void RtmpMediaSourceImp::onAllTrackReady() {
_all_track_ready = true;
if (_recreate_metadata) {
// 更新metadata
// 更新metadata [AUTO-TRANSLATED:396f34c7]
// Update metadata
for (auto &track : _muxer->getTracks()) {
Metadata::addTrack(_metadata, track);
}
@@ -168,10 +179,12 @@ void RtmpMediaSourceImp::onAllTrackReady() {
void RtmpMediaSourceImp::setListener(const std::weak_ptr<MediaSourceEvent> &listener) {
if (_muxer) {
//_muxer对象不能处理的事件再给listener处理
// _muxer对象不能处理的事件再给listener处理 [AUTO-TRANSLATED:47858305]
// Events that the _muxer object cannot handle are then handled by the listener
_muxer->setMediaListener(listener);
} else {
// 未创建_muxer对象事件全部给listener处理
// 未创建_muxer对象事件全部给listener处理 [AUTO-TRANSLATED:54efbd82]
// If the _muxer object is not created, all events are handled by the listener
MediaSource::setListener(listener);
}
}

View File

@@ -34,26 +34,45 @@ public:
* @param app 应用名
* @param id 流id
* @param ringSize 环形缓存大小
* Constructor
* @param vhost Virtual host
* @param app Application name
* @param id Stream id
* @param ringSize Ring buffer size
* [AUTO-TRANSLATED:7679d212]
*/
RtmpMediaSourceImp(const MediaTuple& tuple, int ringSize = RTMP_GOP_SIZE);
/**
* 设置metadata
* Set metadata
* [AUTO-TRANSLATED:e32234cf]
*/
void setMetaData(const AMFValue &metadata) override;
/**
* 输入rtmp并解析
* Input rtmp and parse
* [AUTO-TRANSLATED:de255b79]
*/
void onWrite(RtmpPacket::Ptr pkt, bool = true) override;
/**
* 获取观看总人数,包括(hls/rtsp/rtmp)
* Get total number of viewers, including (hls/rtsp/rtmp)
* [AUTO-TRANSLATED:19a26d5a]
*/
int totalReaderCount() override;
/**
* 设置协议转换
* Set protocol conversion
* [AUTO-TRANSLATED:f7206bf3]
*/
void setProtocolOption(const ProtocolOption &option);
@@ -63,11 +82,17 @@ public:
/**
* _demuxer触发的添加Track事件
* _demuxer triggered add Track event
* [AUTO-TRANSLATED:80dbcf16]
*/
bool addTrack(const Track::Ptr &track) override;
/**
* _demuxer触发的Track添加完毕事件
* _demuxer triggered Track add complete event
* [AUTO-TRANSLATED:939cb312]
*/
void addTrackCompleted() override;
@@ -75,12 +100,20 @@ public:
/**
* _muxer触发的所有Track就绪的事件
* _muxer triggered all Track ready event
* [AUTO-TRANSLATED:1d34b7e0]
*/
void onAllTrackReady() override;
/**
* 设置事件监听器
* @param listener 监听器
* Set event listener
* @param listener Listener
* [AUTO-TRANSLATED:d829419b]
*/
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override;

View File

@@ -76,7 +76,8 @@ public:
}
bool isEnabled() {
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
// 缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存 [AUTO-TRANSLATED:7cfd4d49]
// The inputFrame function is still allowed to be triggered when the cache has not been cleared, so that the cache can be cleared in time.
return _option.rtmp_demand ? (_clear_cache ? true : _enabled) : true;
}

View File

@@ -24,7 +24,8 @@ RtmpMuxer::RtmpMuxer(const TitleMeta::Ptr &title) {
bool RtmpMuxer::addTrack(const Track::Ptr &track) {
if (_track_existed[track->getTrackType()]) {
// rtmp不支持多个同类型track
// rtmp不支持多个同类型track [AUTO-TRANSLATED:c69a7864]
// rtmp does not support multiple tracks of the same type
WarnL << "Already add a track kind of: " << track->getTrackTypeStr() << ", ignore track: " << track->getCodecName();
return false;
}
@@ -36,13 +37,16 @@ bool RtmpMuxer::addTrack(const Track::Ptr &track) {
return false;
}
// 标记已经存在该类型track
// 标记已经存在该类型track [AUTO-TRANSLATED:ed79ebb5]
// Mark that a track of this type already exists
_track_existed[track->getTrackType()] = true;
// 设置rtmp输出环形缓存
// 设置rtmp输出环形缓存 [AUTO-TRANSLATED:d65af70c]
// Set the rtmp output circular buffer
encoder->setRtmpRing(_rtmp_ring);
// 添加metadata
// 添加metadata [AUTO-TRANSLATED:eaf2f5ae]
// Add metadata
Metadata::addTrack(_metadata, track);
return true;
}

View File

@@ -24,44 +24,72 @@ public:
/**
* 构造函数
* Constructor
* [AUTO-TRANSLATED:41469869]
*/
RtmpMuxer(const TitleMeta::Ptr &title);
/**
* 获取完整的SDP字符串
* @return SDP字符串
* Get the complete SDP string
* @return SDP string
* [AUTO-TRANSLATED:f5d1b0a6]
*/
const AMFValue &getMetadata() const ;
/**
* 获取rtmp环形缓存
* @return
* Get the rtmp ring buffer
* @return
* [AUTO-TRANSLATED:81679f3c]
*/
RtmpRing::RingType::Ptr getRtmpRing() const;
/**
* 添加ready状态的track
* Add a ready state track
* [AUTO-TRANSLATED:2d8138b3]
*/
bool addTrack(const Track::Ptr & track) override;
/**
* 写入帧数据
* @param frame 帧
* Write frame data
* @param frame frame
* [AUTO-TRANSLATED:b7c92013]
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* 刷新输出所有frame缓存
* Flush all frame buffers
* [AUTO-TRANSLATED:adaea568]
*/
void flush() override;
/**
* 重置所有track
* Reset all tracks
* [AUTO-TRANSLATED:f203fa3e]
*/
void resetTracks() override ;
/**
* 生成config包
* Generate config package
* [AUTO-TRANSLATED:8f851364]
*/
void makeConfigPacket();

View File

@@ -58,7 +58,8 @@ void RtmpPlayer::play(const string &url) {
_stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL);
auto app_second = findSubString(_stream_id.data(), nullptr, "/");
if (!app_second.empty() && app_second.find('?') == std::string::npos) {
// _stream_id存在多级不包含'?', 说明分割符'/'不是url参数的一部分
// _stream_id存在多级不包含'?', 说明分割符'/'不是url参数的一部分 [AUTO-TRANSLATED:ef820905]
// _stream_id exists at multiple levels; it does not contain '?', indicating that the delimiter '/' is not part of the url parameter
_app += "/" + app_second;
_stream_id.erase(0, app_second.size() + 1);
}
@@ -92,33 +93,40 @@ void RtmpPlayer::play(const string &url) {
}
void RtmpPlayer::onError(const SockException &ex){
//定时器_pPlayTimer为空后表明握手结束了
// 定时器_pPlayTimer为空后表明握手结束了 [AUTO-TRANSLATED:6e2661f4]
// The timer _pPlayTimer is empty after the handshake is finished
onPlayResult_l(ex, !_play_timer);
}
void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) {
if (ex.getErrCode() == Err_shutdown) {
//主动shutdown的不触发回调
// 主动shutdown的不触发回调 [AUTO-TRANSLATED:bd97b1c1]
// Active shutdown does not trigger a callback
return;
}
WarnL << ex.getErrCode() << " " << ex;
if (!handshake_done) {
//开始播放阶段
// 开始播放阶段 [AUTO-TRANSLATED:a246c5ee]
// Start playback stage
_play_timer.reset();
//是否为性能测试模式
// 是否为性能测试模式 [AUTO-TRANSLATED:1fde8234]
// Whether it is a performance test mode
_benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
onPlayResult(ex);
} else if (ex) {
//播放成功后异常断开回调
// 播放成功后异常断开回调 [AUTO-TRANSLATED:b5c5fa80]
// Callback for abnormal disconnection after successful playback
onShutdown(ex);
} else {
//恢复播放
// 恢复播放 [AUTO-TRANSLATED:19a73f21]
// Resume playback
onResume();
}
if (!ex) {
//播放成功恢复rtmp接收超时定时器
// 播放成功恢复rtmp接收超时定时器 [AUTO-TRANSLATED:29b58110]
// After successful playback, restore the rtmp receive timeout timer
_rtmp_recv_ticker.resetTime();
auto timeout_ms = (*this)[Client::kMediaTimeoutMS].as<uint64_t>();
weak_ptr<RtmpPlayer> weak_self = static_pointer_cast<RtmpPlayer>(shared_from_this());
@@ -128,14 +136,16 @@ void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) {
return false;
}
if (strong_self->_rtmp_recv_ticker.elapsedTime() > timeout_ms) {
//接收rtmp媒体数据超时
// 接收rtmp媒体数据超时 [AUTO-TRANSLATED:e14bc1fe]
// Receive rtmp media data timeout
SockException ex(Err_timeout, "receive rtmp timeout");
strong_self->onPlayResult_l(ex, true);
return false;
}
return true;
};
//创建rtmp数据接收超时检测定时器
// 创建rtmp数据接收超时检测定时器 [AUTO-TRANSLATED:d255312b]
// Create an rtmp data receive timeout detection timer
_rtmp_recv_timer = std::make_shared<Timer>(timeout_ms / 2000.0f, lam, getPoller());
} else {
shutdown(SockException(Err_shutdown,"teardown"));
@@ -158,14 +168,16 @@ void RtmpPlayer::onConnect(const SockException &err) {
void RtmpPlayer::onRecv(const Buffer::Ptr &buf){
try {
if (_benchmark_mode && !_play_timer) {
//在性能测试模式下如果rtmp握手完毕后不再解析rtmp包
// 在性能测试模式下如果rtmp握手完毕后不再解析rtmp包 [AUTO-TRANSLATED:a39356cc]
// In performance test mode, if the rtmp handshake is complete, the rtmp packet will no longer be parsed
_rtmp_recv_ticker.resetTime();
return;
}
onParseRtmp(buf->data(), buf->size());
} catch (exception &e) {
SockException ex(Err_other, e.what());
//定时器_pPlayTimer为空后表明握手结束了
// 定时器_pPlayTimer为空后表明握手结束了 [AUTO-TRANSLATED:6e2661f4]
// The timer _pPlayTimer is empty after the handshake is finished
onPlayResult_l(ex, !_play_timer);
}
}
@@ -182,15 +194,20 @@ void RtmpPlayer::send_connect() {
AMFValue obj(AMF_OBJECT);
obj.set("app", _app);
obj.set("tcUrl", _tc_url);
//未使用代理
// 未使用代理 [AUTO-TRANSLATED:fa1ef5d7]
// No proxy used
obj.set("fpad", false);
//参考librtmp,什么作用?
// 参考librtmp,什么作用? [AUTO-TRANSLATED:c6e3349f]
// Refer to librtmp, what is the role?
obj.set("capabilities", 15);
//SUPPORT_VID_CLIENT_SEEK 支持seek
// SUPPORT_VID_CLIENT_SEEK 支持seek [AUTO-TRANSLATED:81d2bb06]
// SUPPORT_VID_CLIENT_SEEK supports seek
obj.set("videoFunction", 1);
//只支持aac
// 只支持aac [AUTO-TRANSLATED:ab086b5b]
// Only supports aac
obj.set("audioCodecs", (double) (0x0400));
//只支持H264
// 只支持H264 [AUTO-TRANSLATED:d8fb8696]
// Only supports H264
obj.set("videoCodecs", (double) (0x0080));
AMFValue fourCcList(AMF_STRICT_ARRAY);
@@ -257,7 +274,8 @@ void RtmpPlayer::send_pause(bool pause) {
if (!pause) {
onPlayResult_l(SockException(Err_success, "resum rtmp success"), true);
} else {
//暂停播放
// 暂停播放 [AUTO-TRANSLATED:09cc521a]
// Pause playback
_rtmp_recv_timer.reset();
}
}
@@ -309,7 +327,8 @@ void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) {
auto level = val["level"];
auto code = val["code"].as_string();
if (level.type() == AMF_STRING) {
// warning 不应该断开
// warning 不应该断开 [AUTO-TRANSLATED:6db13b98]
// warning should not be disconnected
if (level.as_string() != "status" && level.as_string() != "warning") {
throw std::runtime_error(StrPrinter << "onStatus 失败:" << level.as_string() << " " << code << endl);
}
@@ -335,18 +354,22 @@ void RtmpPlayer::onStreamDry(uint32_t stream_index) {
void RtmpPlayer::onMediaData_l(RtmpPacket::Ptr chunk_data) {
_rtmp_recv_ticker.resetTime();
if (!_play_timer) {
//已经触发了onPlayResult事件直接触发onMediaData事件
// 已经触发了onPlayResult事件直接触发onMediaData事件 [AUTO-TRANSLATED:5c12bd46]
// The onPlayResult event has been triggered, directly trigger the onMediaData event
onRtmpPacket(chunk_data);
return;
}
if (chunk_data->isConfigFrame()) {
//输入配置帧以便初始化完成各个track
// 输入配置帧以便初始化完成各个track [AUTO-TRANSLATED:2f571d31]
// Input configuration frame to initialize each track
onRtmpPacket(chunk_data);
} else {
//先触发onPlayResult事件这个时候解码器才能初始化完毕
// 先触发onPlayResult事件这个时候解码器才能初始化完毕 [AUTO-TRANSLATED:403c9195]
// Trigger the onPlayResult event first, at this time the decoder can be initialized
onPlayResult_l(SockException(Err_success, "play rtmp success"), false);
//触发onPlayResult事件后再把帧数据输入到解码器
// 触发onPlayResult事件后再把帧数据输入到解码器 [AUTO-TRANSLATED:bf058334]
// After triggering the onPlayResult event, input the frame data to the decoder
onRtmpPacket(chunk_data);
}
}
@@ -383,7 +406,8 @@ void RtmpPlayer::onRtmpChunk(RtmpPacket::Ptr packet) {
case MSG_VIDEO: {
auto idx = chunk_data.type_id % 2;
if (_now_stamp_ticker[idx].elapsedTime() > 500) {
//计算播放进度时间轴用
// 计算播放进度时间轴用 [AUTO-TRANSLATED:383fd62c]
// Used to calculate the playback progress timeline
_now_stamp[idx] = chunk_data.time_stamp;
}
if (!_metadata_got) {

View File

@@ -24,7 +24,8 @@
namespace mediakit {
//实现了rtmp播放器协议部分的功能及数据接收功能
// 实现了rtmp播放器协议部分的功能及数据接收功能 [AUTO-TRANSLATED:1548a233]
// Implemented the rtmp player protocol part functionality, and data receiving functionality
class RtmpPlayer : public PlayerBase, public toolkit::TcpClient, public RtmpProtocol {
public:
using Ptr = std::shared_ptr<RtmpPlayer>;
@@ -44,7 +45,8 @@ protected:
protected:
void onMediaData_l(RtmpPacket::Ptr chunk_data);
//在获取config帧后才触发onPlayResult_l(而不是收到play命令回复)所以此时所有track都初始化完毕了
// 在获取config帧后才触发onPlayResult_l(而不是收到play命令回复)所以此时所有track都初始化完毕了 [AUTO-TRANSLATED:c2d8c7a2]
// Trigger onPlayResult_l after getting the config frame (instead of receiving the play command reply), so all tracks are initialized at this time
void onPlayResult_l(const toolkit::SockException &ex, bool handshake_done);
//form Tcpclient
@@ -83,10 +85,12 @@ private:
bool _paused = false;
bool _metadata_got = false;
//是否为性能测试模式
// 是否为性能测试模式 [AUTO-TRANSLATED:1fde8234]
// Whether it is performance test mode
bool _benchmark_mode = false;
//播放进度控制
// 播放进度控制 [AUTO-TRANSLATED:62b3ee44]
// Playback progress control
uint32_t _seek_ms = 0;
uint32_t _fist_stamp[2] = {0, 0};
uint32_t _now_stamp[2] = {0, 0};
@@ -94,13 +98,17 @@ private:
std::deque<std::function<void(AMFValue &dec)> > _deque_on_status;
std::unordered_map<int, std::function<void(AMFDecoder &dec)> > _map_on_result;
//rtmp接收超时计时器
// rtmp接收超时计时器 [AUTO-TRANSLATED:292af22c]
// Rtmp receive timeout timer
toolkit::Ticker _rtmp_recv_ticker;
//心跳发送定时器
// 心跳发送定时器 [AUTO-TRANSLATED:04ac8e65]
// Heartbeat send timer
std::shared_ptr<toolkit::Timer> _beat_timer;
//播放超时定时器
// 播放超时定时器 [AUTO-TRANSLATED:74d33688]
// Playback timeout timer
std::shared_ptr<toolkit::Timer> _play_timer;
//rtmp接收超时定时器
// rtmp接收超时定时器 [AUTO-TRANSLATED:8c121e8c]
// Rtmp receive timeout timer
std::shared_ptr<toolkit::Timer> _rtmp_recv_timer;
};

View File

@@ -43,9 +43,11 @@ public:
}
private:
//派生类回调函数
// 派生类回调函数 [AUTO-TRANSLATED:61e20903]
// Derived class callback function
bool onMetadata(const AMFValue &val) override {
//无metadata或metadata中无track信息时需要从数据包中获取track
// 无metadata或metadata中无track信息时需要从数据包中获取track [AUTO-TRANSLATED:92a71803]
// When there is no metadata or no track information in the metadata, it is necessary to obtain the track from the data packet
_wait_track_ready = this->Super::operator[](Client::kWaitTrackReady).template as<bool>() || RtmpDemuxer::trackCount(val) == 0;
onCheckMeta_l(val);
return true;
@@ -53,7 +55,8 @@ private:
void onRtmpPacket(RtmpPacket::Ptr chunkData) override {
if (!_demuxer) {
//有些rtmp流没metadata
// 有些rtmp流没metadata [AUTO-TRANSLATED:2509786f]
// Some rtmp streams do not have metadata
onCheckMeta_l(TitleMeta().getMetadata());
}
_demuxer->inputRtmp(chunkData);

View File

@@ -36,7 +36,8 @@ static string openssl_HMACsha256(const void *key, size_t key_len, const void *da
unsigned int out_len;
#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER > 0x10100000L)
//openssl 1.1.0新增api老版本api作废
// openssl 1.1.0新增api老版本api作废 [AUTO-TRANSLATED:4c7b59a8]
// OpenSSL 1.1.0 added new APIs, old APIs are deprecated
HMAC_CTX *ctx = HMAC_CTX_new();
HMAC_CTX_reset(ctx);
HMAC_Init_ex(ctx, key, (int)key_len, EVP_sha256(), NULL);
@@ -171,10 +172,12 @@ void RtmpProtocol::sendInvoke(const string &cmd, const AMFValue &val) {
void RtmpProtocol::sendRequest(int cmd, const string& str) {
if (cmd <= MSG_SET_PEER_BW) {
// 若 cmd 属于 Protocol Control Messages ,则应使用 chunk id 2 发送
// 若 cmd 属于 Protocol Control Messages ,则应使用 chunk id 2 发送 [AUTO-TRANSLATED:3f17e21f]
// If cmd belongs to Protocol Control Messages, it should be sent using chunk id 2
sendRtmp(cmd, _stream_index, str, 0, CHUNK_NETWORK);
} else {
// 否则使用 chunk id 发送(任意值3-128参见 obs 及 ffmpeg 选取 3)
// 否则使用 chunk id 发送(任意值3-128参见 obs 及 ffmpeg 选取 3) [AUTO-TRANSLATED:65f8d861]
// Otherwise, use chunk id to send (any value 3-128, see obs and ffmpeg select 3)
sendRtmp(cmd, _stream_index, str, 0, CHUNK_SYSTEM);
}
}
@@ -210,14 +213,17 @@ void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::P
auto strErr = StrPrinter << "不支持发送该类型的块流 ID:" << chunk_id << endl;
throw std::runtime_error(strErr);
}
//是否有扩展时间戳
// 是否有扩展时间戳 [AUTO-TRANSLATED:85bae69f]
// Does it have an extended timestamp
bool ext_stamp = stamp >= 0xFFFFFF;
//rtmp头
// rtmp头 [AUTO-TRANSLATED:915b278c]
// RTMP header
BufferRaw::Ptr buffer_header = obtainBuffer();
buffer_header->setCapacity(sizeof(RtmpHeader));
buffer_header->setSize(sizeof(RtmpHeader));
//对rtmp头赋值如果使用整形赋值在arm android上可能由于数据对齐导致总线错误的问题
// 对rtmp头赋值如果使用整形赋值在arm android上可能由于数据对齐导致总线错误的问题 [AUTO-TRANSLATED:90c79d70]
// Assign values to the RTMP header. If using integer assignment, it may cause bus errors on ARM Android due to data alignment issues
RtmpHeader *header = (RtmpHeader *) buffer_header->data();
header->fmt = 0;
header->chunk_id = chunk_id;
@@ -225,20 +231,24 @@ void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::P
set_be24(header->time_stamp, ext_stamp ? 0xFFFFFF : stamp);
set_be24(header->body_size, (uint32_t)buf->size());
set_le32(header->stream_index, stream_index);
//发送rtmp头
// 发送rtmp头 [AUTO-TRANSLATED:3c038cd5]
// Send RTMP header
onSendRawData(std::move(buffer_header));
//扩展时间戳字段
// 扩展时间戳字段 [AUTO-TRANSLATED:6f79a475]
// Extended timestamp field
BufferRaw::Ptr buffer_ext_stamp;
if (ext_stamp) {
//生成扩展时间戳
// 生成扩展时间戳 [AUTO-TRANSLATED:cd22977a]
// Generate extended timestamp
buffer_ext_stamp = obtainBuffer();
buffer_ext_stamp->setCapacity(4);
buffer_ext_stamp->setSize(4);
set_be32(buffer_ext_stamp->data(), stamp);
}
//生成一个字节的flag标明是什么chunkId
// 生成一个字节的flag标明是什么chunkId [AUTO-TRANSLATED:fbdbf476]
// Generate a one-byte flag to indicate what chunkId it is
BufferRaw::Ptr buffer_flags = obtainBuffer();
buffer_flags->setCapacity(1);
buffer_flags->setSize(1);
@@ -254,7 +264,8 @@ void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::P
totalSize += 1;
}
if (ext_stamp) {
//扩展时间戳
// 扩展时间戳 [AUTO-TRANSLATED:f263b9bf]
// Extended timestamp
onSendRawData(buffer_ext_stamp);
totalSize += 4;
}
@@ -275,12 +286,15 @@ void RtmpProtocol::onParseRtmp(const char *data, size_t size) {
}
const char *RtmpProtocol::onSearchPacketTail(const char *data,size_t len){
//移动拷贝提高性能
// 移动拷贝提高性能 [AUTO-TRANSLATED:1e6fce8d]
// Move copy to improve performance
auto next_step_func(std::move(_next_step_func));
//执行下一步
// 执行下一步 [AUTO-TRANSLATED:4199a3e3]
// Execute the next step
auto ret = next_step_func(data, len);
if (!_next_step_func) {
//为设置下一步,恢复之
// 为设置下一步,恢复之 [AUTO-TRANSLATED:b5494ef3]
// Set the next step, restore it
next_step_func.swap(_next_step_func);
}
return ret;
@@ -288,7 +302,8 @@ const char *RtmpProtocol::onSearchPacketTail(const char *data,size_t len){
////for client////
void RtmpProtocol::startClientSession(const function<void()> &func, bool complex) {
//发送 C0C1
// 发送 C0C1 [AUTO-TRANSLATED:5da06136]
// Send C0C1
char handshake_head = HANDSHAKE_PLAINTEXT;
onSendRawData(obtainBuffer(&handshake_head, 1));
RtmpHandshake c1(0);
@@ -297,25 +312,30 @@ void RtmpProtocol::startClientSession(const function<void()> &func, bool complex
}
onSendRawData(obtainBuffer((char *) (&c1), sizeof(c1)));
_next_step_func = [this, func](const char *data, size_t len) {
//等待 S0+S1+S2
// 等待 S0+S1+S2 [AUTO-TRANSLATED:3f95dd6d]
// Wait for S0+S1+S2
return handle_S0S1S2(data, len, func);
};
}
const char* RtmpProtocol::handle_S0S1S2(const char *data, size_t len, const function<void()> &func) {
if (len < 1 + 2 * C1_HANDSHARK_SIZE) {
//数据不够
// 数据不够 [AUTO-TRANSLATED:72802244]
// Not enough data
return nullptr;
}
if (data[0] != HANDSHAKE_PLAINTEXT) {
throw std::runtime_error("only plaintext[0x03] handshake supported");
}
//发送 C2
// 发送 C2 [AUTO-TRANSLATED:e51c339e]
// Send C2
const char *pcC2 = data + 1;
onSendRawData(obtainBuffer(pcC2, C1_HANDSHARK_SIZE));
//握手结束
// 握手结束 [AUTO-TRANSLATED:9df763ff]
// Handshake finished
_next_step_func = [this](const char *data, size_t len) {
//握手结束并且开始进入解析命令模式
// 握手结束并且开始进入解析命令模式 [AUTO-TRANSLATED:3b4e0886]
// Handshake finished and start entering command parsing mode
return handle_rtmp(data, len);
};
func();
@@ -347,24 +367,30 @@ const char * RtmpProtocol::handle_C0C1(const char *data, size_t len) {
}
void RtmpProtocol::handle_C1_simple(const char *data){
//发送S0
// 发送S0 [AUTO-TRANSLATED:79eca118]
// Send S0
char handshake_head = HANDSHAKE_PLAINTEXT;
onSendRawData(obtainBuffer(&handshake_head, 1));
//发送S1
// 发送S1 [AUTO-TRANSLATED:a86847cf]
// Send S1
RtmpHandshake s1(0);
onSendRawData(obtainBuffer((char *) &s1, C1_HANDSHARK_SIZE));
//发送S2
// 发送S2 [AUTO-TRANSLATED:dbcf960a]
// Send S2
onSendRawData(obtainBuffer(data + 1, C1_HANDSHARK_SIZE));
//等待C2
// 等待C2 [AUTO-TRANSLATED:b9900831]
// Wait for C2
_next_step_func = [this](const char *data, size_t len) {
//握手结束并且开始进入解析命令模式
// 握手结束并且开始进入解析命令模式 [AUTO-TRANSLATED:3b4e0886]
// Handshake finished and start entering command parsing mode
return handle_C2(data, len);
};
}
#ifdef ENABLE_OPENSSL
void RtmpProtocol::handle_C1_complex(const char *data){
//参考自:http://blog.csdn.net/win_lin/article/details/13006803
// 参考自:http://blog.csdn.net/win_lin/article/details/13006803 [AUTO-TRANSLATED:97a05bd3]
// Refer to: http://blog.csdn.net/win_lin/article/details/13006803
//skip c0,time,version
const char *c1_start = data + 1;
const char *schema_start = c1_start + 8;
@@ -384,7 +410,8 @@ void RtmpProtocol::handle_C1_complex(const char *data){
send_complex_S0S1S2(0, digest);
// InfoL << "schema0";
} catch (std::exception &) {
//貌似flash从来都不用schema1
// 貌似flash从来都不用schema1 [AUTO-TRANSLATED:2c6d140f]
// It seems that flash never uses schema1
// WarnL << "try rtmp complex schema0 failed:" << ex.what();
try {
/* c1s1 schema1
@@ -449,6 +476,13 @@ string RtmpProtocol::get_C1_digest(const uint8_t *ptr,char **digestPos){
random-data: (offset)bytes
digest-data: 32bytes
random-data: (764-4-offset-32)bytes
*764bytes digest structure
offset: 4bytes
random-data: (offset)bytes
digest-data: 32bytes
random-data: (764-4-offset-32)bytes
* [AUTO-TRANSLATED:99455c22]
*/
int offset = 0;
for (int i = 0; i < C1_OFFSET_SIZE; ++i) {
@@ -467,6 +501,13 @@ string RtmpProtocol::get_C1_key(const uint8_t *ptr){
key-data: 128bytes
random-data: (764-offset-128-4)bytes
offset: 4bytes
*764bytes key structure
random-data: (offset)bytes
key-data: 128bytes
random-data: (764-offset-128-4)bytes
offset: 4bytes
* [AUTO-TRANSLATED:25b8d7c7]
*/
int offset = 0;
for (int i = C1_SCHEMA_SIZE - C1_OFFSET_SIZE; i < C1_SCHEMA_SIZE; ++i) {
@@ -479,8 +520,10 @@ string RtmpProtocol::get_C1_key(const uint8_t *ptr){
}
void RtmpProtocol::send_complex_S0S1S2(int schemeType,const string &digest){
//S1S2计算参考自:https://github.com/hitYangfei/golang/blob/master/rtmpserver.go
//发送S0
// S1S2计算参考自:https://github.com/hitYangfei/golang/blob/master/rtmpserver.go [AUTO-TRANSLATED:8a372a12]
// S1S2 calculation refers to: https://github.com/hitYangfei/golang/blob/master/rtmpserver.go
// 发送S0 [AUTO-TRANSLATED:79eca118]
// Send S0
char handshake_head = HANDSHAKE_PLAINTEXT;
onSendRawData(obtainBuffer(&handshake_head, 1));
//S1
@@ -518,29 +561,36 @@ void RtmpProtocol::send_complex_S0S1S2(int schemeType,const string &digest){
string s2_digest = openssl_HMACsha256(s2_key.data(), s2_key.size(), &s2, sizeof(s2) - C1_DIGEST_SIZE);
memcpy((char *) &s2 + C1_HANDSHARK_SIZE - C1_DIGEST_SIZE, s2_digest.data(), C1_DIGEST_SIZE);
onSendRawData(obtainBuffer((char *) &s2, sizeof(s2)));
//等待C2
// 等待C2 [AUTO-TRANSLATED:b9900831]
// Wait for C2
_next_step_func = [this](const char *data, size_t len) {
return handle_C2(data, len);
};
}
#endif //ENABLE_OPENSSL
//发送复杂握手c0c1
// 发送复杂握手c0c1 [AUTO-TRANSLATED:f6f646dc]
// Send complex handshake c0c1
void RtmpHandshake::create_complex_c0c1() {
#ifdef ENABLE_OPENSSL
memcpy(zero, "\x80\x00\x07\x02", 4);
//digest随机偏移长度
// digest随机偏移长度 [AUTO-TRANSLATED:758606f0]
// Digest random offset length
auto offset_value = rand() % (C1_SCHEMA_SIZE - C1_OFFSET_SIZE - C1_DIGEST_SIZE);
//设置digest偏移长度
// 设置digest偏移长度 [AUTO-TRANSLATED:1a4caf92]
// Set digest offset length
auto offset_ptr = random + C1_SCHEMA_SIZE;
offset_ptr[0] = offset_ptr[1] = offset_ptr[2] = offset_value / 4;
offset_ptr[3] = offset_value - 3 * (offset_value / 4);
//去除digest后的剩余随机数据
// 去除digest后的剩余随机数据 [AUTO-TRANSLATED:133cc74e]
// Remove the remaining random data after digest
string str((char *) this, sizeof(*this));
str.erase(8 + C1_SCHEMA_SIZE + C1_OFFSET_SIZE + offset_value, C1_DIGEST_SIZE);
//获取摘要
// 获取摘要 [AUTO-TRANSLATED:2a7aab55]
// Get digest
auto digest_value = openssl_HMACsha256(FPKey, C1_FPKEY_SIZE, str.data(), str.size());
//插入摘要
// 插入摘要 [AUTO-TRANSLATED:3f12641b]
// Insert digest
memcpy(random + C1_SCHEMA_SIZE + C1_OFFSET_SIZE + offset_value, digest_value.data(), digest_value.size());
#endif
}
@@ -554,7 +604,8 @@ const char* RtmpProtocol::handle_C2(const char *data, size_t len) {
return handle_rtmp(data, len);
};
//握手结束,进入命令模式
// 握手结束,进入命令模式 [AUTO-TRANSLATED:2b22d604]
// Handshake ends, enter command mode
return handle_rtmp(data + C1_HANDSHARK_SIZE, len - C1_HANDSHARK_SIZE);
}
@@ -569,8 +620,10 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) {
_now_chunk_id = header->chunk_id;
switch (_now_chunk_id) {
case 0: {
//0 值表示二字节形式,并且 ID 范围 64 - 319
//(第二个字节 + 64)。
// 0 值表示二字节形式,并且 ID 范围 64 - 319 [AUTO-TRANSLATED:46207a57]
// 0 value represents two-byte form, and ID range is 64 - 319
// (第二个字节 + 64)。 [AUTO-TRANSLATED:ab640c90]
// (second byte + 64).
if (len < 2) {
//need more data
return ptr;
@@ -581,8 +634,10 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) {
}
case 1: {
//1 值表示三字节形式,并且 ID 范围为 64 - 65599
//((第三个字节) * 256 + 第二个字节 + 64)。
// 1 值表示三字节形式,并且 ID 范围为 64 - 65599 [AUTO-TRANSLATED:e7f24d54]
// 1 value represents three-byte form, and ID range is 64 - 65599
// ((第三个字节) * 256 + 第二个字节 + 64)。 [AUTO-TRANSLATED:6e631032]
// ((third byte) * 256 + second byte + 64).
if (len < 3) {
//need more data
return ptr;
@@ -592,7 +647,8 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) {
break;
}
//带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。
// 带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。 [AUTO-TRANSLATED:b0987fdb]
// Block stream IDs with a value of 2 are reserved for lower-level protocol control messages and commands.
default : break;
}
@@ -607,10 +663,12 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) {
if (!now_packet) {
now_packet = RtmpPacket::create();
if (last_packet) {
//恢复chunk上下文
// 恢复chunk上下文 [AUTO-TRANSLATED:84bf0621]
// Restore chunk context
*now_packet = *last_packet;
}
//绝对时间戳标记复位
// 绝对时间戳标记复位 [AUTO-TRANSLATED:a0d50bad]
// Absolute timestamp marker reset
now_packet->is_abs_stamp = false;
}
auto &chunk_data = *now_packet;
@@ -654,7 +712,8 @@ const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) {
//frame is ready
_now_stream_index = chunk_data.stream_index;
chunk_data.time_stamp = time_stamp + (chunk_data.is_abs_stamp ? 0 : chunk_data.time_stamp);
//保存chunk上下文
// 保存chunk上下文 [AUTO-TRANSLATED:4ed4fbb0]
// Save chunk context
last_packet = now_packet;
if (chunk_data.body_size) {
handle_chunk(std::move(now_packet));
@@ -715,7 +774,8 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) {
}
case CONTROL_STREAM_BEGIN: {
//开始播放
// 开始播放 [AUTO-TRANSLATED:1d82b293]
// Start playback
if (chunk_data.buffer.size() < 4) {
WarnL << "CONTROL_STREAM_BEGIN: Not enough data:" << chunk_data.buffer.size();
break;
@@ -727,7 +787,8 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) {
}
case CONTROL_STREAM_EOF: {
//暂停
// 暂停 [AUTO-TRANSLATED:73a3f145]
// Pause
if (chunk_data.buffer.size() < 4) {
throw std::runtime_error("CONTROL_STREAM_EOF: Not enough data.");
}
@@ -738,7 +799,8 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) {
}
case CONTROL_STREAM_DRY: {
//停止播放
// 停止播放 [AUTO-TRANSLATED:1292c1b5]
// Stop playback
if (chunk_data.buffer.size() < 4) {
throw std::runtime_error("CONTROL_STREAM_DRY: Not enough data.");
}
@@ -754,8 +816,10 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) {
}
case MSG_WIN_SIZE: {
//如果窗口太小会导致发送sendAcknowledgement时无限递归https://github.com/ZLMediaKit/ZLMediaKit/issues/1839
//窗口太大也可能导致fms服务器认为播放器心跳超时
// 如果窗口太小会导致发送sendAcknowledgement时无限递归https://github.com/ZLMediaKit/ZLMediaKit/issues/1839 [AUTO-TRANSLATED:05267962]
// If the window is too small, it will cause infinite recursion when sending sendAcknowledgement: https://github.com/ZLMediaKit/ZLMediaKit/issues/1839
// 窗口太大也可能导致fms服务器认为播放器心跳超时 [AUTO-TRANSLATED:30147e88]
// If the window is too large, it may also cause the fms server to consider the player heartbeat timeout
_windows_size = min(max(load_be32(&chunk_data.buffer[0]), 32 * 1024U), 1280 * 1024U);
TraceL << "MSG_WIN_SIZE:" << _windows_size;
break;
@@ -784,7 +848,8 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) {
ts |= (*ptr << 24);
ptr += 1;
ptr += 3;
//参考FFmpeg多拷贝了4个字节
// 参考FFmpeg多拷贝了4个字节 [AUTO-TRANSLATED:d8aae4ae]
// Reference FFmpeg copied 4 more bytes
size += 4;
if (ptr + size > ptr_tail) {
break;

View File

@@ -28,7 +28,8 @@ public:
virtual ~RtmpProtocol();
void onParseRtmp(const char *data, size_t size);
//作为客户端发送c0c1等待s0s1s2并且回调
// 作为客户端发送c0c1等待s0s1s2并且回调 [AUTO-TRANSLATED:fed23902]
// Send c0c1 as a client, wait for s0s1s2 and callback
void startClientSession(const std::function<void()> &cb, bool complex = true);
protected:
@@ -103,7 +104,8 @@ private:
std::function<const char * (const char *data, size_t len)> _next_step_func;
////////////Chunk////////////
std::unordered_map<int, std::pair<RtmpPacket::Ptr/*now*/, RtmpPacket::Ptr/*last*/> > _map_chunk_data;
//循环池
// 循环池 [AUTO-TRANSLATED:cf2e86c5]
// Thread pool
toolkit::ResourcePool<toolkit::BufferRaw> _packet_pool;
};

View File

@@ -46,15 +46,18 @@ void RtmpPusher::teardown() {
void RtmpPusher::onPublishResult_l(const SockException &ex, bool handshake_done) {
DebugL << ex.what();
if (ex.getErrCode() == Err_shutdown) {
//主动shutdown的不触发回调
// 主动shutdown的不触发回调 [AUTO-TRANSLATED:bd97b1c1]
// Actively shutdown, no callback triggered
return;
}
if (!handshake_done) {
//播放结果回调
// 播放结果回调 [AUTO-TRANSLATED:a5714269]
// Playback result callback
_publish_timer.reset();
onPublishResult(ex);
} else {
//播放成功后异常断开回调
// 播放成功后异常断开回调 [AUTO-TRANSLATED:b5c5fa80]
// Callback for abnormal disconnection after successful playback
onShutdown(ex);
}
@@ -71,7 +74,8 @@ void RtmpPusher::publish(const string &url) {
_stream_id = findSubString(url.data(), (host_url + "/" + _app + "/").data(), NULL);
auto app_second = findSubString(_stream_id.data(), nullptr, "/");
if (!app_second.empty() && app_second.find('?') == std::string::npos) {
// _stream_id存在多级不包含'?', 说明分割符'/'不是url参数的一部分
// _stream_id存在多级不包含'?', 说明分割符'/'不是url参数的一部分 [AUTO-TRANSLATED:ef820905]
// _stream_id has multiple levels; does not contain '?', indicating that the delimiter '/' is not part of the URL parameters
_app += "/" + app_second;
_stream_id.erase(0, app_second.size() + 1);
}
@@ -104,7 +108,8 @@ void RtmpPusher::publish(const string &url) {
}
void RtmpPusher::onError(const SockException &ex){
//定时器_pPublishTimer为空后表明握手结束了
// 定时器_pPublishTimer为空后表明握手结束了 [AUTO-TRANSLATED:630ec31e]
// The timer _pPublishTimer is empty, indicating that the handshake is over
onPublishResult_l(ex, !_publish_timer);
}
@@ -130,7 +135,8 @@ void RtmpPusher::onRecv(const Buffer::Ptr &buf){
onParseRtmp(buf->data(), buf->size());
} catch (exception &e) {
SockException ex(Err_other, e.what());
//定时器_pPublishTimer为空后表明握手结束了
// 定时器_pPublishTimer为空后表明握手结束了 [AUTO-TRANSLATED:630ec31e]
// The timer _pPublishTimer is empty, indicating that the handshake is over
onPublishResult_l(ex, !_publish_timer);
}
}
@@ -243,14 +249,16 @@ void RtmpPusher::send_metaData(){
}
});
onPublishResult_l(SockException(Err_success, "success"), false);
//提升发送性能
// 提升发送性能 [AUTO-TRANSLATED:90630751]
// Improve sending performance
setSocketFlags();
}
void RtmpPusher::setSocketFlags(){
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
if (mergeWriteMS > 0) {
//提高发送性能
// 提高发送性能 [AUTO-TRANSLATED:de96ec30]
// Improve sending performance
setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
SockUtil::setNoDelay(getSock()->rawFD(), false);
}

View File

@@ -68,7 +68,8 @@ private:
std::deque<std::function<void(AMFValue &dec)> > _deque_on_status;
std::unordered_map<int, std::function<void(AMFDecoder &dec)> > _map_on_result;
//推流超时定时器
// 推流超时定时器 [AUTO-TRANSLATED:7d2dcb86]
// Stream timeout timer
std::shared_ptr<toolkit::Timer> _publish_timer;
std::weak_ptr<RtmpMediaSource> _publish_src;
RtmpMediaSource::RingType::RingReader::Ptr _rtmp_reader;

View File

@@ -66,17 +66,23 @@ private:
}
///////MediaSourceEvent override///////
// 关闭
// 关闭 [AUTO-TRANSLATED:92392f02]
// Close
bool close(MediaSource &sender) override;
// 播放总人数
// 播放总人数 [AUTO-TRANSLATED:c42a3161]
// Total number of plays
int totalReaderCount(MediaSource &sender) override;
// 获取媒体源类型
// 获取媒体源类型 [AUTO-TRANSLATED:34290a69]
// Get media source type
MediaOriginType getOriginType(MediaSource &sender) const override;
// 获取媒体源url或者文件路径
// 获取媒体源url或者文件路径 [AUTO-TRANSLATED:fa34d795]
// Get media source url or file path
std::string getOriginUrl(MediaSource &sender) const override;
// 获取媒体源客户端相关信息
// 获取媒体源客户端相关信息 [AUTO-TRANSLATED:037ef910]
// Get media source client related information
std::shared_ptr<SockInfo> getOriginSock(MediaSource &sender) const override;
// 由于支持断连续推存在OwnerPoller变更的可能
// 由于支持断连续推存在OwnerPoller变更的可能 [AUTO-TRANSLATED:1c863b40]
// Due to support for discontinuous pushing, there may be changes in OwnerPoller
toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override;
void setSocketFlags();
@@ -87,11 +93,14 @@ private:
private:
bool _set_meta_data = false;
double _recv_req_id = 0;
//断连续推延时
// 断连续推延时 [AUTO-TRANSLATED:13ad578a]
// Discontinuous pushing delay
uint32_t _continue_push_ms = 0;
//消耗的总流量
// 消耗的总流量 [AUTO-TRANSLATED:45ad2785]
// Total traffic consumed
uint64_t _total_bytes = 0;
//数据接收超时计时器
// 数据接收超时计时器 [AUTO-TRANSLATED:3fba518a]
// Data reception timeout timer
toolkit::Ticker _ticker;
MediaInfo _media_info;
std::weak_ptr<RtmpMediaSource> _play_src;
@@ -104,6 +113,10 @@ private:
/**
* 支持ssl加密的rtmp服务器
* Supports ssl encrypted rtmp server
* [AUTO-TRANSLATED:21d167ba]
*/
using RtmpSessionWithSSL = toolkit::SessionWithSSL<RtmpSession>;