AI: SRT: Report video/audio codec info and frame stats in HTTP API. v7.0.117 (#4554)

This commit is contained in:
OSSRS-AI 2025-11-04 10:25:28 -05:00 committed by winlin
parent f90a96a03d
commit eb9fca888d
20 changed files with 419 additions and 41 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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

View File

@ -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");
}

View File

@ -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);
};

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 116
#define VERSION_REVISION 117
#endif

View File

@ -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)
}
}

View File

@ -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

View File

@ -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);
};

View File

@ -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_++;

View File

@ -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

View File

@ -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_);

View File

@ -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();

View File

@ -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()
{

View File

@ -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
{

View File

@ -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);