This PR adds separate audio and video frame counting to the HTTP API
(`/api/v1/streams/`) for better stream observability. The API now
reports three frame fields:
- `frames` - Total frames (video + audio)
- `video_frames` - Video frames/packets only
- `audio_frames` - Audio frames/packets only
This enhancement provides better visibility into stream composition and
helps detect issues with CBR/VBR streams, audio/video sync problems, and
codec-specific behavior.
**Before:**
```json
{
"streams": [
{
"frames": 0, // video frames.
}
]
}
```
**After:**
```json
{
"streams": [
{
"frames": 6912, // video frames.
"audio_frames": 5678, // audio frames.
"video_frames": 1234, // video frames.
}
]
}
```
Frame Counting Strategy
- All protocols report frames every N frames to balance accuracy and
performance
- Frames are counted at the protocol-specific message/packet level:
- RTMP: Counts RTMP messages (video/audio)
- WebRTC: Counts RTP packets (video/audio)
- SRT: Counts MPEG-TS messages (H.264/HEVC/AAC)
327 lines
10 KiB
C++
327 lines
10 KiB
C++
//
|
|
// Copyright (c) 2013-2025 The SRS Authors
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
|
|
#ifndef SRS_UTEST_AI16_HPP
|
|
#define SRS_UTEST_AI16_HPP
|
|
|
|
/*
|
|
#include <srs_utest_ai16.hpp>
|
|
*/
|
|
#include <srs_utest.hpp>
|
|
|
|
#include <srs_app_config.hpp>
|
|
#include <srs_app_http_hooks.hpp>
|
|
#include <srs_app_http_stream.hpp>
|
|
#include <srs_app_rtmp_source.hpp>
|
|
#include <srs_app_server.hpp>
|
|
#include <srs_protocol_http_conn.hpp>
|
|
#include <srs_protocol_rtmp_stack.hpp>
|
|
#include <srs_utest_ai11.hpp>
|
|
|
|
// Mock SrsHttpxConn for testing SrsLiveStream - inherits from real SrsHttpxConn
|
|
class MockHttpxConnForLiveStream : public SrsHttpxConn
|
|
{
|
|
public:
|
|
bool enable_stat_called_;
|
|
|
|
public:
|
|
MockHttpxConnForLiveStream();
|
|
virtual ~MockHttpxConnForLiveStream();
|
|
|
|
public:
|
|
void set_enable_stat(bool v);
|
|
};
|
|
|
|
// Mock SrsHttpConn for testing SrsLiveStream - inherits from real SrsHttpConn
|
|
class MockHttpConnForLiveStream : public SrsHttpConn
|
|
{
|
|
public:
|
|
MockHttpxConnForLiveStream *mock_handler_;
|
|
|
|
public:
|
|
MockHttpConnForLiveStream();
|
|
virtual ~MockHttpConnForLiveStream();
|
|
|
|
public:
|
|
virtual ISrsHttpConnOwner *handler();
|
|
};
|
|
|
|
// Mock SrsHttpMessage for testing SrsLiveStream
|
|
class MockHttpMessageForLiveStream : public SrsHttpMessage
|
|
{
|
|
public:
|
|
MockHttpConnForLiveStream *mock_conn_;
|
|
|
|
public:
|
|
MockHttpMessageForLiveStream();
|
|
virtual ~MockHttpMessageForLiveStream();
|
|
|
|
public:
|
|
virtual ISrsConnection *connection();
|
|
virtual std::string path();
|
|
};
|
|
|
|
// Mock ISrsAppConfig for testing SrsLiveStream::http_hooks_on_play() and http_hooks_on_stop()
|
|
class MockAppConfigForLiveStreamHooks : public MockAppConfig
|
|
{
|
|
public:
|
|
bool http_hooks_enabled_;
|
|
SrsConfDirective *on_play_directive_;
|
|
SrsConfDirective *on_stop_directive_;
|
|
|
|
public:
|
|
MockAppConfigForLiveStreamHooks();
|
|
virtual ~MockAppConfigForLiveStreamHooks();
|
|
|
|
public:
|
|
virtual bool get_vhost_http_hooks_enabled(std::string vhost);
|
|
virtual SrsConfDirective *get_vhost_on_play(std::string vhost);
|
|
virtual SrsConfDirective *get_vhost_on_stop(std::string vhost);
|
|
};
|
|
|
|
// Mock ISrsHttpHooks for testing SrsLiveStream::http_hooks_on_play() and http_hooks_on_stop()
|
|
class MockHttpHooksForLiveStream : public ISrsHttpHooks
|
|
{
|
|
public:
|
|
std::vector<std::pair<std::string, ISrsRequest *> > on_play_calls_;
|
|
int on_play_count_;
|
|
srs_error_t on_play_error_;
|
|
std::vector<std::pair<std::string, ISrsRequest *> > on_stop_calls_;
|
|
int on_stop_count_;
|
|
|
|
public:
|
|
MockHttpHooksForLiveStream();
|
|
virtual ~MockHttpHooksForLiveStream();
|
|
|
|
public:
|
|
virtual srs_error_t on_connect(std::string url, ISrsRequest *req);
|
|
virtual void on_close(std::string url, ISrsRequest *req, int64_t send_bytes, int64_t recv_bytes);
|
|
virtual srs_error_t on_publish(std::string url, ISrsRequest *req);
|
|
virtual void on_unpublish(std::string url, ISrsRequest *req);
|
|
virtual srs_error_t on_play(std::string url, ISrsRequest *req);
|
|
virtual void on_stop(std::string url, ISrsRequest *req);
|
|
virtual srs_error_t on_dvr(SrsContextId cid, std::string url, ISrsRequest *req, std::string file);
|
|
virtual srs_error_t on_hls(SrsContextId cid, std::string url, ISrsRequest *req, std::string file, std::string ts_url,
|
|
std::string m3u8, std::string m3u8_url, int sn, srs_utime_t duration);
|
|
virtual srs_error_t on_hls_notify(SrsContextId cid, std::string url, ISrsRequest *req, std::string ts_url, int nb_notify);
|
|
virtual srs_error_t discover_co_workers(std::string url, std::string &host, int &port);
|
|
virtual srs_error_t on_forward_backend(std::string url, ISrsRequest *req, std::vector<std::string> &rtmp_urls);
|
|
void reset();
|
|
};
|
|
|
|
// Mock ISrsAsyncCallWorker for testing SrsHttpStreamServer
|
|
class MockAsyncCallWorker : public ISrsAsyncCallWorker
|
|
{
|
|
public:
|
|
int execute_count_;
|
|
std::vector<ISrsAsyncCallTask *> tasks_;
|
|
|
|
public:
|
|
MockAsyncCallWorker();
|
|
virtual ~MockAsyncCallWorker();
|
|
|
|
public:
|
|
virtual srs_error_t execute(ISrsAsyncCallTask *t);
|
|
virtual srs_error_t start();
|
|
virtual void stop();
|
|
};
|
|
|
|
// Mock ISrsLiveStream for testing SrsHttpStreamDestroy
|
|
class MockLiveStreamForDestroy : public ISrsLiveStream
|
|
{
|
|
public:
|
|
bool alive_;
|
|
bool expired_;
|
|
|
|
public:
|
|
MockLiveStreamForDestroy();
|
|
virtual ~MockLiveStreamForDestroy();
|
|
|
|
public:
|
|
virtual srs_error_t serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r);
|
|
virtual srs_error_t update_auth(ISrsRequest *r);
|
|
virtual bool alive();
|
|
virtual void expire();
|
|
};
|
|
|
|
// Mock ISrsStatistic for testing SrsLiveStream::serve_http_impl
|
|
class MockStatisticForLiveStream : public ISrsStatistic
|
|
{
|
|
public:
|
|
int on_client_count_;
|
|
srs_error_t on_client_error_;
|
|
|
|
public:
|
|
MockStatisticForLiveStream();
|
|
virtual ~MockStatisticForLiveStream();
|
|
|
|
public:
|
|
virtual void on_disconnect(std::string id, srs_error_t err);
|
|
virtual srs_error_t on_client(std::string id, ISrsRequest *req, ISrsExpire *conn, SrsRtmpConnType type);
|
|
virtual srs_error_t on_video_info(ISrsRequest *req, SrsVideoCodecId vcodec, int avc_profile, int avc_level, int width, int height);
|
|
virtual srs_error_t on_audio_info(ISrsRequest *req, SrsAudioCodecId acodec, SrsAudioSampleRate asample_rate, SrsAudioChannels asound_type, SrsAacObjectType aac_object);
|
|
virtual void on_stream_publish(ISrsRequest *req, std::string publisher_id);
|
|
virtual void on_stream_close(ISrsRequest *req);
|
|
virtual void kbps_add_delta(std::string id, ISrsKbpsDelta *delta);
|
|
virtual void kbps_sample();
|
|
virtual srs_error_t on_video_frames(ISrsRequest *req, int nb_frames);
|
|
virtual srs_error_t on_audio_frames(ISrsRequest *req, int nb_frames);
|
|
virtual std::string server_id();
|
|
virtual std::string service_id();
|
|
virtual std::string service_pid();
|
|
virtual SrsStatisticVhost *find_vhost_by_id(std::string vid);
|
|
virtual SrsStatisticStream *find_stream(std::string sid);
|
|
virtual SrsStatisticStream *find_stream_by_url(std::string url);
|
|
virtual SrsStatisticClient *find_client(std::string client_id);
|
|
virtual srs_error_t dumps_vhosts(SrsJsonArray *arr);
|
|
virtual srs_error_t dumps_streams(SrsJsonArray *arr, int start, int count);
|
|
virtual srs_error_t dumps_clients(SrsJsonArray *arr, int start, int count);
|
|
virtual srs_error_t dumps_metrics(int64_t &send_bytes, int64_t &recv_bytes, int64_t &nstreams, int64_t &nclients, int64_t &total_nclients, int64_t &nerrs);
|
|
};
|
|
|
|
// Mock ISrsBufferCache for testing SrsHttpStreamDestroy
|
|
class MockBufferCacheForDestroy : public ISrsBufferCache
|
|
{
|
|
public:
|
|
bool alive_;
|
|
bool stopped_;
|
|
|
|
public:
|
|
MockBufferCacheForDestroy();
|
|
virtual ~MockBufferCacheForDestroy();
|
|
|
|
public:
|
|
virtual srs_error_t start();
|
|
virtual void stop();
|
|
virtual bool alive();
|
|
virtual srs_error_t dump_cache(ISrsLiveConsumer *consumer, SrsRtmpJitterAlgorithm jitter);
|
|
virtual srs_error_t update_auth(ISrsRequest *r);
|
|
};
|
|
|
|
// Mock ISrsBufferEncoder for testing SrsLiveStream::streaming_send_messages
|
|
class MockBufferEncoderForStreamingSend : public ISrsBufferEncoder
|
|
{
|
|
public:
|
|
int write_audio_count_;
|
|
int write_video_count_;
|
|
int write_metadata_count_;
|
|
std::vector<int64_t> audio_timestamps_;
|
|
std::vector<int64_t> video_timestamps_;
|
|
std::vector<int64_t> metadata_timestamps_;
|
|
srs_error_t write_error_;
|
|
|
|
public:
|
|
MockBufferEncoderForStreamingSend();
|
|
virtual ~MockBufferEncoderForStreamingSend();
|
|
|
|
public:
|
|
virtual srs_error_t initialize(SrsFileWriter *w, ISrsBufferCache *c);
|
|
virtual srs_error_t write_audio(int64_t timestamp, char *data, int size);
|
|
virtual srs_error_t write_video(int64_t timestamp, char *data, int size);
|
|
virtual srs_error_t write_metadata(int64_t timestamp, char *data, int size);
|
|
virtual bool has_cache();
|
|
virtual srs_error_t dump_cache(ISrsLiveConsumer *consumer, SrsRtmpJitterAlgorithm jitter);
|
|
void reset();
|
|
};
|
|
|
|
// Mock ISrsAppConfig for testing SrsHttpStreamServer
|
|
class MockAppConfigForHttpStreamServer : public MockAppConfig
|
|
{
|
|
public:
|
|
bool http_remux_enabled_;
|
|
std::string http_remux_mount_;
|
|
SrsConfDirective *vhost_directive_;
|
|
|
|
public:
|
|
MockAppConfigForHttpStreamServer();
|
|
virtual ~MockAppConfigForHttpStreamServer();
|
|
|
|
public:
|
|
virtual bool get_vhost_http_remux_enabled(std::string vhost);
|
|
virtual std::string get_vhost_http_remux_mount(std::string vhost);
|
|
virtual SrsConfDirective *get_vhost(std::string vhost, bool try_default_vhost = true);
|
|
virtual bool get_vhost_enabled(SrsConfDirective *conf);
|
|
};
|
|
|
|
// Mock SrsHttpMessage for testing SrsHttpStreamServer::dynamic_match
|
|
class MockHttpMessageForDynamicMatch : public SrsHttpMessage
|
|
{
|
|
public:
|
|
std::string path_;
|
|
std::string ext_;
|
|
std::string host_;
|
|
MockHttpConn *mock_conn_;
|
|
|
|
public:
|
|
MockHttpMessageForDynamicMatch();
|
|
virtual ~MockHttpMessageForDynamicMatch();
|
|
|
|
public:
|
|
virtual std::string path();
|
|
virtual std::string ext();
|
|
virtual std::string host();
|
|
};
|
|
|
|
// Mock SrsHttpMessage for testing HTTP API response functions
|
|
class MockHttpMessageForApiResponse : public SrsHttpMessage
|
|
{
|
|
public:
|
|
MockHttpConn *mock_conn_;
|
|
bool is_jsonp_;
|
|
std::string callback_;
|
|
std::string path_;
|
|
std::map<std::string, std::string> query_params_;
|
|
|
|
public:
|
|
MockHttpMessageForApiResponse();
|
|
virtual ~MockHttpMessageForApiResponse();
|
|
|
|
public:
|
|
virtual bool is_jsonp();
|
|
virtual std::string query_get(std::string key);
|
|
virtual std::string path();
|
|
};
|
|
|
|
// Mock ISrsSignalHandler for testing SrsGoApiRaw
|
|
class MockSignalHandler : public ISrsSignalHandler
|
|
{
|
|
public:
|
|
int signal_received_;
|
|
int signal_count_;
|
|
|
|
public:
|
|
MockSignalHandler();
|
|
virtual ~MockSignalHandler();
|
|
|
|
public:
|
|
virtual void on_signal(int signo);
|
|
void reset();
|
|
};
|
|
|
|
// Mock ISrsAppConfig for testing SrsGoApiRaw
|
|
class MockAppConfigForRawApi : public MockAppConfig
|
|
{
|
|
public:
|
|
bool raw_api_;
|
|
bool allow_reload_;
|
|
bool allow_query_;
|
|
bool allow_update_;
|
|
srs_error_t raw_to_json_error_;
|
|
|
|
public:
|
|
MockAppConfigForRawApi();
|
|
virtual ~MockAppConfigForRawApi();
|
|
|
|
public:
|
|
virtual bool get_raw_api();
|
|
virtual bool get_raw_api_allow_reload();
|
|
virtual bool get_raw_api_allow_query();
|
|
virtual bool get_raw_api_allow_update();
|
|
virtual srs_error_t raw_to_json(SrsJsonObject *obj);
|
|
};
|
|
|
|
#endif
|