mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-06-28 21:52:22 +08:00
AI automatically translates all comments in the code into English (#3917)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user