AI: SRT: Report video/audio codec info and frame stats in HTTP API. v7.0.117 (#4554)
This commit is contained in:
parent
f90a96a03d
commit
eb9fca888d
|
|
@ -21,6 +21,10 @@ rtc_server {
|
|||
# @see https://ossrs.io/lts/en-us/docs/v7/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
srt_server {
|
||||
enabled on;
|
||||
listen 10080;
|
||||
}
|
||||
rtsp_server {
|
||||
enabled on;
|
||||
listen 8554;
|
||||
|
|
@ -40,6 +44,10 @@ vhost __defaultVhost__ {
|
|||
# @see https://ossrs.io/lts/en-us/docs/v7/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp on;
|
||||
}
|
||||
srt {
|
||||
enabled on;
|
||||
srt_to_rtmp on;
|
||||
}
|
||||
rtsp {
|
||||
enabled on;
|
||||
rtmp_to_rtsp on;
|
||||
|
|
|
|||
|
|
@ -751,7 +751,7 @@ vhost srt.vhost.srs.com {
|
|||
enabled on;
|
||||
# Whether covert SRT to RTMP stream.
|
||||
# Overwrite by env SRS_VHOST_SRT_TO_RTMP for all vhosts.
|
||||
# Default: on
|
||||
# Default: off
|
||||
srt_to_rtmp on;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ The changelog for SRS.
|
|||
<a name="v7-changes"></a>
|
||||
|
||||
## SRS 7.0 Changelog
|
||||
* v7.0, 2025-11-04, AI: SRT: Report video/audio codec info and frame stats in HTTP API. v7.0.117 (#4554)
|
||||
* v7.0, 2025-11-03, Merge [#4556](https://github.com/ossrs/srs/pull/4556): Fill missing defs for H264/AVC video levels. v7.0.116 (#4556)
|
||||
* v7.0, 2025-10-31, Merge [#4547](https://github.com/ossrs/srs/pull/4547): Add ignore configuration for cursor. v7.0.115 (#4547)
|
||||
* v7.0, 2025-10-30, WebRTC: Use realtime for TWCC timestamp accuracy. v7.0.114
|
||||
|
|
|
|||
|
|
@ -7696,10 +7696,10 @@ bool SrsConfig::get_srt_enabled(std::string vhost)
|
|||
|
||||
bool SrsConfig::get_srt_to_rtmp(std::string vhost)
|
||||
{
|
||||
SRS_OVERWRITE_BY_ENV_BOOL("srs.vhost.srt.srt_to_rtmp"); // SRS_VHOST_SRT_SRT_TO_RTMP
|
||||
SRS_OVERWRITE_BY_ENV_BOOL("srs.vhost.srt.to_rtmp"); // SRS_VHOST_SRT_TO_RTMP
|
||||
SRS_OVERWRITE_BY_ENV_BOOL2("srs.vhost.srt.srt_to_rtmp"); // SRS_VHOST_SRT_SRT_TO_RTMP
|
||||
SRS_OVERWRITE_BY_ENV_BOOL2("srs.vhost.srt.to_rtmp"); // SRS_VHOST_SRT_TO_RTMP
|
||||
|
||||
static bool DEFAULT = true;
|
||||
static bool DEFAULT = false;
|
||||
|
||||
SrsConfDirective *conf = get_srt(vhost);
|
||||
if (!conf) {
|
||||
|
|
|
|||
|
|
@ -696,7 +696,7 @@ srs_error_t SrsMpegtsSrtConn::on_srt_packet(char *buf, int nb_buf)
|
|||
SrsUniquePtr<SrsSrtPacket> packet(new SrsSrtPacket());
|
||||
packet->wrap(buf, nb_buf);
|
||||
|
||||
if ((err = srt_source_->on_packet(packet.get())) != srs_success) {
|
||||
if ((err = srt_source_->on_srt_packet(packet.get())) != srs_success) {
|
||||
return srs_error_wrap(err, "on srt packet");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ using namespace std;
|
|||
#include <srs_kernel_flv.hpp>
|
||||
#include <srs_kernel_pithy_print.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_kernel_ts.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_protocol_format.hpp>
|
||||
#include <srs_protocol_raw_avc.hpp>
|
||||
#include <srs_protocol_rtmp_stack.hpp>
|
||||
|
||||
|
|
@ -337,7 +339,7 @@ srs_error_t SrsSrtFrameBuilder::on_publish()
|
|||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtFrameBuilder::on_packet(SrsSrtPacket *pkt)
|
||||
srs_error_t SrsSrtFrameBuilder::on_srt_packet(SrsSrtPacket *pkt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
|
|
@ -924,6 +926,253 @@ srs_error_t SrsSrtFrameBuilder::on_aac_frame(SrsTsMessage *msg, uint32_t pts, ch
|
|||
return err;
|
||||
}
|
||||
|
||||
ISrsSrtFormat::ISrsSrtFormat()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsSrtFormat::~ISrsSrtFormat()
|
||||
{
|
||||
}
|
||||
|
||||
SrsSrtFormat::SrsSrtFormat()
|
||||
{
|
||||
req_ = NULL;
|
||||
ts_ctx_ = new SrsTsContext();
|
||||
format_ = new SrsRtmpFormat();
|
||||
video_codec_reported_ = false;
|
||||
audio_codec_reported_ = false;
|
||||
|
||||
stat_ = _srs_stat;
|
||||
}
|
||||
|
||||
SrsSrtFormat::~SrsSrtFormat()
|
||||
{
|
||||
req_ = NULL;
|
||||
srs_freep(ts_ctx_);
|
||||
srs_freep(format_);
|
||||
|
||||
stat_ = NULL;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtFormat::initialize(ISrsRequest *req)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
req_ = req;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtFormat::on_srt_packet(SrsSrtPacket *pkt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
char *buf = pkt->data();
|
||||
int nb_buf = pkt->size();
|
||||
|
||||
// Parse TS packets to extract codec information
|
||||
int nb_packet = nb_buf / SRS_TS_PACKET_SIZE;
|
||||
for (int i = 0; i < nb_packet; i++) {
|
||||
char *p = buf + (i * SRS_TS_PACKET_SIZE);
|
||||
SrsUniquePtr<SrsBuffer> stream(new SrsBuffer(p, SRS_TS_PACKET_SIZE));
|
||||
|
||||
// Decode TS packet and call on_ts_message for each message
|
||||
if ((err = ts_ctx_->decode(stream.get(), this)) != srs_success) {
|
||||
// Ignore parse errors, just log and continue
|
||||
srs_warn("srt format parse ts packet err=%s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtFormat::on_ts_message(SrsTsMessage *msg)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Only parse video and audio messages
|
||||
if (msg->channel_->stream_ == SrsTsStreamVideoH264 || msg->channel_->stream_ == SrsTsStreamVideoHEVC) {
|
||||
if (video_codec_reported_) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = parse_video_codec(msg)) != srs_success) {
|
||||
return srs_error_wrap(err, "parse video codec");
|
||||
}
|
||||
|
||||
if (format_->vcodec_) {
|
||||
video_codec_reported_ = true;
|
||||
|
||||
SrsVideoCodecConfig *c = format_->vcodec_;
|
||||
if ((err = stat_->on_video_info(req_, c->id_, c->avc_profile_, c->avc_level_, c->width_, c->height_)) != srs_success) {
|
||||
return srs_error_wrap(err, "stat video info");
|
||||
}
|
||||
|
||||
srs_trace("srt: parsed %s codec, profile=%s, level=%s, %dx%d",
|
||||
srs_video_codec_id2str(c->id_).c_str(),
|
||||
srs_avc_profile2str(c->avc_profile_).c_str(), srs_avc_level2str(c->avc_level_).c_str(),
|
||||
c->width_, c->height_);
|
||||
}
|
||||
} else if (msg->channel_->stream_ == SrsTsStreamAudioAAC) {
|
||||
if (audio_codec_reported_) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = parse_audio_codec(msg)) != srs_success) {
|
||||
return srs_error_wrap(err, "parse audio codec");
|
||||
}
|
||||
|
||||
if (format_->acodec_) {
|
||||
audio_codec_reported_ = true;
|
||||
|
||||
SrsAudioCodecConfig *c = format_->acodec_;
|
||||
SrsAudioChannels channels = c->sound_type_;
|
||||
if (c->id_ == SrsAudioCodecIdAAC) {
|
||||
channels = (c->aac_channels_ == 1) ? SrsAudioChannelsMono : SrsAudioChannelsStereo;
|
||||
}
|
||||
if ((err = stat_->on_audio_info(req_, c->id_, c->sound_rate_, channels, c->aac_object_)) != srs_success) {
|
||||
return srs_error_wrap(err, "stat audio info");
|
||||
}
|
||||
|
||||
srs_trace("srt: parsed %s codec, sample_rate=%dHZ, channels=%d, profile=%s",
|
||||
srs_audio_codec_id2str(c->id_).c_str(),
|
||||
srs_audio_sample_rate2number(c->sound_rate_), (int)channels + 1,
|
||||
srs_aac_object2str(c->aac_object_).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtFormat::parse_video_codec(SrsTsMessage *msg)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Parse the video codec from TS message payload
|
||||
SrsBuffer avs(msg->payload_->bytes(), msg->payload_->length());
|
||||
|
||||
// Parse H.265/HEVC to extract VPS/SPS/PPS
|
||||
// TODO: FIXME: Implement HEVC codec parsing similar to H.264
|
||||
if (msg->channel_->stream_ == SrsTsStreamVideoHEVC) {
|
||||
video_codec_reported_ = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Only parse H.264 video messages
|
||||
if (msg->channel_->stream_ == SrsTsStreamVideoH264) {
|
||||
// Parse H.264 to extract SPS/PPS
|
||||
SrsUniquePtr<SrsRawH264Stream> avc(new SrsRawH264Stream());
|
||||
std::string sps, pps;
|
||||
|
||||
while (!avs.empty()) {
|
||||
char *data = NULL;
|
||||
int data_size = 0;
|
||||
if ((err = avc->annexb_demux(&avs, &data, &data_size)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux annexb");
|
||||
}
|
||||
|
||||
if (data == NULL || data_size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract SPS
|
||||
if (avc->is_sps(data, data_size)) {
|
||||
if ((err = avc->sps_demux(data, data_size, sps)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux sps");
|
||||
}
|
||||
}
|
||||
|
||||
// Extract PPS
|
||||
if (avc->is_pps(data, data_size)) {
|
||||
if ((err = avc->pps_demux(data, data_size, pps)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux pps");
|
||||
}
|
||||
}
|
||||
|
||||
// Skip until we have both SPS and PPS
|
||||
if (sps.empty() || pps.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have both SPS and PPS, parse codec details
|
||||
std::string sh;
|
||||
if ((err = avc->mux_sequence_header(sps, pps, sh)) != srs_success) {
|
||||
return srs_error_wrap(err, "mux sequence header");
|
||||
}
|
||||
|
||||
// Create a temporary media packet to parse codec info
|
||||
char *flv = NULL;
|
||||
int nb_flv = 0;
|
||||
uint32_t dts = 0;
|
||||
if ((err = avc->mux_avc2flv(sh, SrsVideoAvcFrameTypeKeyFrame, SrsVideoAvcFrameTraitSequenceHeader, dts, dts, &flv, &nb_flv)) != srs_success) {
|
||||
return srs_error_wrap(err, "avc to flv");
|
||||
}
|
||||
|
||||
SrsMediaPacket frame;
|
||||
frame.wrap(flv, nb_flv);
|
||||
|
||||
// Parse the video format to extract codec details
|
||||
if ((err = format_->on_video(&frame)) != srs_success) {
|
||||
return srs_error_wrap(err, "format parse video");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtFormat::parse_audio_codec(SrsTsMessage *msg)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Parse the audio codec from TS message payload
|
||||
SrsBuffer avs(msg->payload_->bytes(), msg->payload_->length());
|
||||
|
||||
if (msg->channel_->stream_ == SrsTsStreamAudioAAC) {
|
||||
// Parse AAC to extract audio specific config
|
||||
SrsUniquePtr<SrsRawAacStream> aac(new SrsRawAacStream());
|
||||
|
||||
char *frame = NULL;
|
||||
int frame_size = 0;
|
||||
SrsRawAacStreamCodec codec;
|
||||
if ((err = aac->adts_demux(&avs, &frame, &frame_size, codec)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux adts");
|
||||
}
|
||||
|
||||
if (frame_size <= 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
std::string sh;
|
||||
if ((err = aac->mux_sequence_header(&codec, sh)) != srs_success) {
|
||||
return srs_error_wrap(err, "mux sequence header");
|
||||
}
|
||||
|
||||
// Create a temporary media packet to parse codec info
|
||||
int rtmp_len = sh.size() + 2;
|
||||
char *buf = new char[rtmp_len];
|
||||
SrsBuffer stream(buf, rtmp_len);
|
||||
uint8_t aac_flag = (SrsAudioCodecIdAAC << 4) | (codec.sound_rate_ << 2) | (codec.sound_size_ << 1) | codec.sound_type_;
|
||||
stream.write_1bytes(aac_flag);
|
||||
stream.write_1bytes(0);
|
||||
stream.write_bytes((char *)sh.data(), sh.size());
|
||||
|
||||
SrsMediaPacket frame_pkt;
|
||||
frame_pkt.wrap(buf, rtmp_len);
|
||||
|
||||
// Parse the audio format to extract codec details
|
||||
if ((err = format_->on_audio(&frame_pkt)) != srs_success) {
|
||||
return srs_error_wrap(err, "format parse audio");
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
ISrsSrtSource::ISrsSrtSource()
|
||||
{
|
||||
}
|
||||
|
|
@ -940,6 +1189,7 @@ SrsSrtSource::SrsSrtSource()
|
|||
stream_die_at_ = 0;
|
||||
|
||||
stat_ = _srs_stat;
|
||||
format_ = new SrsSrtFormat();
|
||||
}
|
||||
|
||||
SrsSrtSource::~SrsSrtSource()
|
||||
|
|
@ -950,6 +1200,7 @@ SrsSrtSource::~SrsSrtSource()
|
|||
|
||||
srs_freep(srt_bridge_);
|
||||
srs_freep(req_);
|
||||
srs_freep(format_);
|
||||
|
||||
SrsContextId cid = _source_id;
|
||||
if (cid.empty())
|
||||
|
|
@ -977,6 +1228,11 @@ srs_error_t SrsSrtSource::initialize(ISrsRequest *r)
|
|||
|
||||
req_ = r->copy();
|
||||
|
||||
// Initialize format parser for codec detection
|
||||
if ((err = format_->initialize(req_)) != srs_success) {
|
||||
return srs_error_wrap(err, "format initialize");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1131,10 +1387,18 @@ void SrsSrtSource::on_unpublish()
|
|||
can_publish_ = true;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::on_packet(SrsSrtPacket *packet)
|
||||
srs_error_t SrsSrtSource::on_srt_packet(SrsSrtPacket *packet)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Parse packet to extract codec information for statistics
|
||||
// This is lightweight and only parses until codec info is found
|
||||
if ((err = format_->on_srt_packet(packet)) != srs_success) {
|
||||
// Don't fail on parse errors, just log and continue
|
||||
srs_warn("srt source parse packet err=%s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)consumers_.size(); i++) {
|
||||
ISrsSrtConsumer *consumer = consumers_.at(i);
|
||||
if ((err = consumer->enqueue(packet->copy())) != srs_success) {
|
||||
|
|
@ -1142,7 +1406,7 @@ srs_error_t SrsSrtSource::on_packet(SrsSrtPacket *packet)
|
|||
}
|
||||
}
|
||||
|
||||
if (srt_bridge_ && (err = srt_bridge_->on_packet(packet)) != srs_success) {
|
||||
if (srt_bridge_ && (err = srt_bridge_->on_srt_packet(packet)) != srs_success) {
|
||||
return srs_error_wrap(err, "bridge consume message");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,50 @@ public:
|
|||
virtual void wait(int nb_msgs, srs_utime_t timeout);
|
||||
};
|
||||
|
||||
// The SRT format interface.
|
||||
class ISrsSrtFormat
|
||||
{
|
||||
public:
|
||||
ISrsSrtFormat();
|
||||
virtual ~ISrsSrtFormat();
|
||||
|
||||
public:
|
||||
virtual srs_error_t initialize(ISrsRequest *req) = 0;
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *pkt) = 0;
|
||||
};
|
||||
|
||||
// Lightweight format parser for SRT streams to extract codec information
|
||||
// from MPEG-TS packets and update statistics.
|
||||
class SrsSrtFormat : public ISrsSrtFormat, public ISrsTsHandler
|
||||
{
|
||||
public:
|
||||
SrsSrtFormat();
|
||||
virtual ~SrsSrtFormat();
|
||||
|
||||
public:
|
||||
srs_error_t initialize(ISrsRequest *req);
|
||||
srs_error_t on_srt_packet(SrsSrtPacket *pkt);
|
||||
|
||||
public:
|
||||
// Interface ISrsTsHandler
|
||||
virtual srs_error_t on_ts_message(SrsTsMessage *msg);
|
||||
|
||||
// clang-format off
|
||||
SRS_DECLARE_PRIVATE: // clang-format on
|
||||
srs_error_t parse_video_codec(SrsTsMessage *msg);
|
||||
srs_error_t parse_audio_codec(SrsTsMessage *msg);
|
||||
|
||||
// clang-format off
|
||||
SRS_DECLARE_PRIVATE: // clang-format on
|
||||
ISrsRequest *req_;
|
||||
ISrsStatistic *stat_;
|
||||
SrsTsContext *ts_ctx_;
|
||||
SrsRtmpFormat *format_;
|
||||
// Track whether we've already reported codec info to avoid duplicate updates
|
||||
bool video_codec_reported_;
|
||||
bool audio_codec_reported_;
|
||||
};
|
||||
|
||||
// Collect and build SRT TS packet to AV frames.
|
||||
class SrsSrtFrameBuilder : public ISrsTsHandler
|
||||
{
|
||||
|
|
@ -161,7 +205,7 @@ public:
|
|||
|
||||
public:
|
||||
virtual srs_error_t on_publish();
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *pkt);
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *pkt);
|
||||
virtual void on_unpublish();
|
||||
// Interface ISrsTsHandler
|
||||
public:
|
||||
|
|
@ -270,7 +314,7 @@ public:
|
|||
virtual void on_unpublish();
|
||||
|
||||
public:
|
||||
srs_error_t on_packet(SrsSrtPacket *packet);
|
||||
srs_error_t on_srt_packet(SrsSrtPacket *packet);
|
||||
|
||||
// clang-format off
|
||||
SRS_DECLARE_PRIVATE: // clang-format on
|
||||
|
|
@ -288,6 +332,8 @@ SRS_DECLARE_PRIVATE: // clang-format on
|
|||
// clang-format off
|
||||
SRS_DECLARE_PRIVATE: // clang-format on
|
||||
ISrsSrtBridge *srt_bridge_;
|
||||
// Format parser for extracting codec information and updating statistics
|
||||
ISrsSrtFormat *format_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -327,11 +327,11 @@ void SrsSrtBridge::on_unpublish()
|
|||
// So there is no need to free its components here.
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtBridge::on_packet(SrsSrtPacket *pkt)
|
||||
srs_error_t SrsSrtBridge::on_srt_packet(SrsSrtPacket *pkt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = frame_builder_->on_packet(pkt)) != srs_success) {
|
||||
if ((err = frame_builder_->on_srt_packet(pkt)) != srs_success) {
|
||||
return srs_error_wrap(err, "frame builder on packet");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ public:
|
|||
virtual ~ISrsSrtTarget();
|
||||
|
||||
public:
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *pkt) = 0;
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *pkt) = 0;
|
||||
};
|
||||
|
||||
// A RTMP bridge is used to convert RTMP stream to different protocols,
|
||||
|
|
@ -171,7 +171,7 @@ public:
|
|||
virtual srs_error_t initialize(ISrsRequest *r);
|
||||
virtual srs_error_t on_publish();
|
||||
virtual void on_unpublish();
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *pkt);
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *pkt);
|
||||
virtual srs_error_t on_frame(SrsMediaPacket *frame);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@
|
|||
|
||||
#define VERSION_MAJOR 7
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 116
|
||||
#define VERSION_REVISION 117
|
||||
|
||||
#endif
|
||||
|
|
@ -7152,9 +7152,9 @@ VOID TEST(ConfigSrtVhostTest, CheckSrtToRtmp)
|
|||
MockSrsConfig conf;
|
||||
HELPER_ASSERT_SUCCESS(conf.mock_parse(_MIN_OK_CONF));
|
||||
|
||||
// Default should be true
|
||||
EXPECT_TRUE(conf.get_srt_to_rtmp("__defaultVhost__"));
|
||||
EXPECT_TRUE(conf.get_srt_to_rtmp("test.com"));
|
||||
// Default should be false (off)
|
||||
EXPECT_FALSE(conf.get_srt_to_rtmp("__defaultVhost__"));
|
||||
EXPECT_FALSE(conf.get_srt_to_rtmp("test.com"));
|
||||
}
|
||||
|
||||
// Test default value when vhost exists but no srt section
|
||||
|
|
@ -7163,7 +7163,7 @@ VOID TEST(ConfigSrtVhostTest, CheckSrtToRtmp)
|
|||
HELPER_ASSERT_SUCCESS(conf.mock_parse(_MIN_OK_CONF "vhost test.com{hls{enabled on;}}"));
|
||||
|
||||
// Should return default value when no srt section
|
||||
EXPECT_TRUE(conf.get_srt_to_rtmp("test.com"));
|
||||
EXPECT_FALSE(conf.get_srt_to_rtmp("test.com"));
|
||||
}
|
||||
|
||||
// Test default value when srt section exists but no srt_to_rtmp config
|
||||
|
|
@ -7172,7 +7172,7 @@ VOID TEST(ConfigSrtVhostTest, CheckSrtToRtmp)
|
|||
HELPER_ASSERT_SUCCESS(conf.mock_parse(_MIN_OK_CONF "vhost test.com{srt{enabled on;}}"));
|
||||
|
||||
// Should return default value when no srt_to_rtmp config
|
||||
EXPECT_TRUE(conf.get_srt_to_rtmp("test.com"));
|
||||
EXPECT_FALSE(conf.get_srt_to_rtmp("test.com"));
|
||||
}
|
||||
|
||||
// Test explicit srt_to_rtmp enabled
|
||||
|
|
@ -7206,7 +7206,7 @@ VOID TEST(ConfigSrtVhostTest, CheckSrtToRtmp)
|
|||
MockSrsConfig conf;
|
||||
HELPER_ASSERT_SUCCESS(conf.mock_parse(_MIN_OK_CONF "vhost test.com{srt{srt_to_rtmp;}}"));
|
||||
|
||||
EXPECT_TRUE(conf.get_srt_to_rtmp("test.com")); // Default value
|
||||
EXPECT_FALSE(conf.get_srt_to_rtmp("test.com")); // Default value is false (off)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ MockSrtTarget::~MockSrtTarget()
|
|||
srs_freep(packet_error_);
|
||||
}
|
||||
|
||||
srs_error_t MockSrtTarget::on_packet(SrsSrtPacket *pkt)
|
||||
srs_error_t MockSrtTarget::on_srt_packet(SrsSrtPacket *pkt)
|
||||
{
|
||||
on_packet_count_++;
|
||||
last_packet_ = pkt;
|
||||
|
|
@ -230,8 +230,8 @@ VOID TEST(StreamBridgeTest, ISrsSrtTarget_Interface)
|
|||
char *data = pkt->wrap(188); // TS packet size
|
||||
data[0] = 0x47; // TS sync byte
|
||||
|
||||
// Test on_packet call
|
||||
srs_error_t err = target.on_packet(pkt.get());
|
||||
// Test on_srt_packet call
|
||||
srs_error_t err = target.on_srt_packet(pkt.get());
|
||||
EXPECT_TRUE(err == srs_success);
|
||||
EXPECT_EQ(1, target.on_packet_count_);
|
||||
EXPECT_EQ(pkt.get(), target.last_packet_);
|
||||
|
|
@ -598,7 +598,7 @@ VOID TEST(StreamBridgeTest, SrsSrtBridge_PacketHandling)
|
|||
data[0] = 0x47;
|
||||
|
||||
// Test packet handling (should succeed even without targets)
|
||||
HELPER_EXPECT_SUCCESS(bridge->on_packet(pkt.get()));
|
||||
HELPER_EXPECT_SUCCESS(bridge->on_srt_packet(pkt.get()));
|
||||
}
|
||||
|
||||
// Test SrsSrtBridge frame handling - comprehensive coverage of on_frame() method
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public:
|
|||
public:
|
||||
MockSrtTarget();
|
||||
virtual ~MockSrtTarget();
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *pkt);
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *pkt);
|
||||
void set_packet_error(srs_error_t err);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -845,7 +845,7 @@ MockSrtSourceForPacket::~MockSrtSourceForPacket()
|
|||
srs_freep(last_packet_);
|
||||
}
|
||||
|
||||
srs_error_t MockSrtSourceForPacket::on_packet(SrsSrtPacket *packet)
|
||||
srs_error_t MockSrtSourceForPacket::on_srt_packet(SrsSrtPacket *packet)
|
||||
{
|
||||
on_packet_called_count_++;
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ public:
|
|||
public:
|
||||
MockSrtSourceForPacket();
|
||||
virtual ~MockSrtSourceForPacket();
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *packet);
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *packet);
|
||||
};
|
||||
|
||||
// Mock ISrsAppConfig for testing SrsMpegtsSrtConn HTTP hooks
|
||||
|
|
|
|||
|
|
@ -1359,7 +1359,7 @@ void MockSrtBridge::on_unpublish()
|
|||
on_unpublish_count_++;
|
||||
}
|
||||
|
||||
srs_error_t MockSrtBridge::on_packet(SrsSrtPacket *packet)
|
||||
srs_error_t MockSrtBridge::on_srt_packet(SrsSrtPacket *packet)
|
||||
{
|
||||
on_packet_count_++;
|
||||
return srs_error_copy(on_packet_error_);
|
||||
|
|
@ -1589,8 +1589,8 @@ VOID TEST(SrsSrtSourceTest, OnPacketDistribution)
|
|||
const char *test_data = "Test SRT Packet Data";
|
||||
packet->wrap((char *)test_data, strlen(test_data));
|
||||
|
||||
// Test: on_packet should distribute to all consumers and bridge
|
||||
HELPER_EXPECT_SUCCESS(source->on_packet(packet.get()));
|
||||
// Test: on_srt_packet should distribute to all consumers and bridge
|
||||
HELPER_EXPECT_SUCCESS(source->on_srt_packet(packet.get()));
|
||||
|
||||
// Verify both consumers received the packet
|
||||
EXPECT_EQ(1, consumer1->enqueue_count_);
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public:
|
|||
virtual srs_error_t initialize(ISrsRequest *r);
|
||||
virtual srs_error_t on_publish();
|
||||
virtual void on_unpublish();
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *packet);
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *packet);
|
||||
void set_on_publish_error(srs_error_t err);
|
||||
void set_on_packet_error(srs_error_t err);
|
||||
void reset();
|
||||
|
|
|
|||
|
|
@ -784,10 +784,10 @@ srs_error_t MockSrtSource::on_publish()
|
|||
return SrsSrtSource::on_publish();
|
||||
}
|
||||
|
||||
srs_error_t MockSrtSource::on_packet(SrsSrtPacket *packet)
|
||||
srs_error_t MockSrtSource::on_srt_packet(SrsSrtPacket *packet)
|
||||
{
|
||||
on_packet_count_++;
|
||||
return SrsSrtSource::on_packet(packet);
|
||||
return SrsSrtSource::on_srt_packet(packet);
|
||||
}
|
||||
|
||||
void MockSrtSource::set_can_publish(bool can_publish)
|
||||
|
|
@ -795,6 +795,33 @@ void MockSrtSource::set_can_publish(bool can_publish)
|
|||
can_publish_result_ = can_publish;
|
||||
}
|
||||
|
||||
// Mock SRT format implementation
|
||||
MockSrtFormat::MockSrtFormat()
|
||||
{
|
||||
initialize_count_ = 0;
|
||||
on_srt_packet_count_ = 0;
|
||||
initialize_error_ = srs_success;
|
||||
on_srt_packet_error_ = srs_success;
|
||||
}
|
||||
|
||||
MockSrtFormat::~MockSrtFormat()
|
||||
{
|
||||
srs_freep(initialize_error_);
|
||||
srs_freep(on_srt_packet_error_);
|
||||
}
|
||||
|
||||
srs_error_t MockSrtFormat::initialize(ISrsRequest *req)
|
||||
{
|
||||
initialize_count_++;
|
||||
return srs_error_copy(initialize_error_);
|
||||
}
|
||||
|
||||
srs_error_t MockSrtFormat::on_srt_packet(SrsSrtPacket *pkt)
|
||||
{
|
||||
on_srt_packet_count_++;
|
||||
return srs_error_copy(on_srt_packet_error_);
|
||||
}
|
||||
|
||||
// Mock SRT source manager implementation
|
||||
MockSrtSourceManager::MockSrtSourceManager()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -762,12 +762,30 @@ public:
|
|||
public:
|
||||
virtual bool can_publish();
|
||||
virtual srs_error_t on_publish();
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *packet);
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *packet);
|
||||
|
||||
public:
|
||||
virtual void set_can_publish(bool can_publish);
|
||||
};
|
||||
|
||||
// Mock SRT format for testing
|
||||
class MockSrtFormat : public ISrsSrtFormat
|
||||
{
|
||||
public:
|
||||
int initialize_count_;
|
||||
int on_srt_packet_count_;
|
||||
srs_error_t initialize_error_;
|
||||
srs_error_t on_srt_packet_error_;
|
||||
|
||||
public:
|
||||
MockSrtFormat();
|
||||
virtual ~MockSrtFormat();
|
||||
|
||||
public:
|
||||
virtual srs_error_t initialize(ISrsRequest *req);
|
||||
virtual srs_error_t on_srt_packet(SrsSrtPacket *pkt);
|
||||
};
|
||||
|
||||
// Mock SRT source manager for testing SrsRtcPublishStream
|
||||
class MockSrtSourceManager : public ISrsSrtSourceManager
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ VOID TEST(BasicWorkflowSrtConnTest, ManuallyVerifyForPublisher)
|
|||
SrsUniquePtr<MockSrtSourceManager> mock_srt_sources(new MockSrtSourceManager());
|
||||
MockSrtConnection *mock_srt_conn = new MockSrtConnection();
|
||||
MockSecurity *mock_security = new MockSecurity();
|
||||
MockSrtFormat *mock_format = new MockSrtFormat();
|
||||
|
||||
mock_config->default_vhost_ = new SrsConfDirective();
|
||||
mock_config->default_vhost_->name_ = "vhost";
|
||||
|
|
@ -99,6 +100,11 @@ VOID TEST(BasicWorkflowSrtConnTest, ManuallyVerifyForPublisher)
|
|||
|
||||
// Create MPEG-TS packets to feed the SRT source.
|
||||
MockSrtSource *mock_srt_source = dynamic_cast<MockSrtSource *>(mock_srt_sources->mock_source_.get());
|
||||
// Inject mock format into SRT source
|
||||
if (true) {
|
||||
srs_freep(mock_srt_source->format_);
|
||||
mock_srt_source->format_ = mock_format;
|
||||
}
|
||||
if (true) {
|
||||
// Create a simple MPEG-TS packet (188 bytes)
|
||||
// This is a minimal TS packet structure for testing
|
||||
|
|
@ -153,6 +159,7 @@ VOID TEST(BasicWorkflowSrtConnTest, ManuallyVerifyForPlayer)
|
|||
SrsUniquePtr<MockSrtSourceManager> mock_srt_sources(new MockSrtSourceManager());
|
||||
MockSrtConnection *mock_srt_conn = new MockSrtConnection();
|
||||
MockSecurity *mock_security = new MockSecurity();
|
||||
MockSrtFormat *mock_format = new MockSrtFormat();
|
||||
|
||||
mock_config->default_vhost_ = new SrsConfDirective();
|
||||
mock_config->default_vhost_->name_ = "vhost";
|
||||
|
|
@ -183,7 +190,6 @@ VOID TEST(BasicWorkflowSrtConnTest, ManuallyVerifyForPlayer)
|
|||
conn->security_ = mock_security;
|
||||
|
||||
// Start the SRT connection.
|
||||
MockSrtSource *srt_source = dynamic_cast<MockSrtSource *>(mock_srt_sources->mock_source_.get());
|
||||
if (true) {
|
||||
HELPER_EXPECT_SUCCESS(conn->start());
|
||||
|
||||
|
|
@ -196,7 +202,15 @@ VOID TEST(BasicWorkflowSrtConnTest, ManuallyVerifyForPlayer)
|
|||
EXPECT_STREQ("__defaultVhost__", req->vhost_.c_str());
|
||||
EXPECT_STREQ("live", req->app_.c_str());
|
||||
EXPECT_STREQ("livestream", req->stream_.c_str());
|
||||
EXPECT_EQ(1, (int)srt_source->consumers_.size());
|
||||
}
|
||||
|
||||
// Create MPEG-TS packets to feed the SRT source.
|
||||
MockSrtSource *mock_srt_source = dynamic_cast<MockSrtSource *>(mock_srt_sources->mock_source_.get());
|
||||
EXPECT_EQ(1, (int)mock_srt_source->consumers_.size());
|
||||
// Inject mock format into SRT source
|
||||
if (true) {
|
||||
srs_freep(mock_srt_source->format_);
|
||||
mock_srt_source->format_ = mock_format;
|
||||
}
|
||||
|
||||
// Feed TS packets to the SRT source consumer.
|
||||
|
|
@ -213,8 +227,8 @@ VOID TEST(BasicWorkflowSrtConnTest, ManuallyVerifyForPlayer)
|
|||
|
||||
SrsUniquePtr<SrsSrtPacket> packet1(new SrsSrtPacket());
|
||||
packet1->wrap(ts_packet1, sizeof(ts_packet1));
|
||||
HELPER_EXPECT_SUCCESS(srt_source->on_packet(packet1.get()));
|
||||
EXPECT_EQ(1, srt_source->on_packet_count_);
|
||||
HELPER_EXPECT_SUCCESS(mock_srt_source->on_srt_packet(packet1.get()));
|
||||
EXPECT_EQ(1, mock_srt_source->on_packet_count_);
|
||||
|
||||
// Create second MPEG-TS packet to trigger consumer signal
|
||||
char ts_packet2[188];
|
||||
|
|
@ -226,8 +240,8 @@ VOID TEST(BasicWorkflowSrtConnTest, ManuallyVerifyForPlayer)
|
|||
|
||||
SrsUniquePtr<SrsSrtPacket> packet2(new SrsSrtPacket());
|
||||
packet2->wrap(ts_packet2, sizeof(ts_packet2));
|
||||
HELPER_EXPECT_SUCCESS(srt_source->on_packet(packet2.get()));
|
||||
EXPECT_EQ(2, srt_source->on_packet_count_);
|
||||
HELPER_EXPECT_SUCCESS(mock_srt_source->on_srt_packet(packet2.get()));
|
||||
EXPECT_EQ(2, mock_srt_source->on_packet_count_);
|
||||
|
||||
// Wait for consumer to process the messages.
|
||||
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user