AI: Cover protocol HTTP/HTTPS/RTMP/RTC by utests. (#4493)
Co-authored-by: OSSRS-AI <winlinam@gmail.com>
This commit is contained in:
parent
49594b1846
commit
b39aae1447
2
trunk/configure
vendored
2
trunk/configure
vendored
|
|
@ -381,7 +381,7 @@ if [[ $SRS_UTEST == YES ]]; then
|
|||
"srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2" "srs_utest_protocol3"
|
||||
"srs_utest_st" "srs_utest_rtc2" "srs_utest_rtc3" "srs_utest_fmp4" "srs_utest_source_lock"
|
||||
"srs_utest_stream_token" "srs_utest_rtc_recv_track" "srs_utest_st2" "srs_utest_hevc_structs"
|
||||
"srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3")
|
||||
"srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3" "srs_utest_protocol4")
|
||||
# Always include SRT utest
|
||||
MODULE_FILES+=("srs_utest_srt")
|
||||
if [[ $SRS_GB28181 == YES ]]; then
|
||||
|
|
|
|||
3
trunk/research/httpmock/go.mod
Normal file
3
trunk/research/httpmock/go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module httpmock
|
||||
|
||||
go 1.23.0
|
||||
7
trunk/research/httpmock/main.go
Normal file
7
trunk/research/httpmock/main.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, mock HTTP")
|
||||
}
|
||||
24
trunk/research/httpmock/main_test.go
Normal file
24
trunk/research/httpmock/main_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHttpMock(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("Hello, World!"))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
resp, err := http.Get(s.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("Expected status code %d, got %d", http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
|
@ -701,6 +701,11 @@ long srs_rand_integer()
|
|||
return random();
|
||||
}
|
||||
|
||||
long srs_rand_integer(long min, long max)
|
||||
{
|
||||
return min + (srs_rand_integer() % (max - min + 1));
|
||||
}
|
||||
|
||||
bool srs_is_digit_number(string str)
|
||||
{
|
||||
if (str.empty()) {
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ extern std::string srs_rand_gen_str(int len);
|
|||
|
||||
// Generate random value, use srandom(now_us) to init seed if not initialized.
|
||||
extern long srs_rand_integer();
|
||||
extern long srs_rand_integer(long min, long max);
|
||||
|
||||
// Whether string is digit number
|
||||
// is_digit("0") is true
|
||||
|
|
|
|||
|
|
@ -93,65 +93,6 @@ const SrsContextId &srs_context_set_cid_of(srs_thread_t trd, const SrsContextId
|
|||
return v;
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
SrsConsoleLog::SrsConsoleLog(SrsLogLevel l, bool u)
|
||||
{
|
||||
level_ = l;
|
||||
utc_ = u;
|
||||
|
||||
buffer_ = new char[SRS_BASIC_LOG_SIZE];
|
||||
}
|
||||
|
||||
SrsConsoleLog::~SrsConsoleLog()
|
||||
{
|
||||
srs_freepa(buffer_);
|
||||
}
|
||||
|
||||
srs_error_t SrsConsoleLog::initialize()
|
||||
{
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
void SrsConsoleLog::reopen()
|
||||
{
|
||||
}
|
||||
|
||||
void SrsConsoleLog::log(SrsLogLevel level, const char *tag, const SrsContextId &context_id, const char *fmt, va_list args)
|
||||
{
|
||||
if (level < level_ || level >= SrsLogLevelDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
if (!srs_log_header(buffer_, SRS_BASIC_LOG_SIZE, utc_, level >= SrsLogLevelWarn, tag, context_id, srs_log_level_strings[level], &size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Something not expected, drop the log.
|
||||
int r0 = vsnprintf(buffer_ + size, SRS_BASIC_LOG_SIZE - size, fmt, args);
|
||||
if (r0 <= 0 || r0 >= SRS_BASIC_LOG_SIZE - size) {
|
||||
return;
|
||||
}
|
||||
size += r0;
|
||||
|
||||
// Add errno and strerror() if error.
|
||||
if (level == SrsLogLevelError && errno != 0) {
|
||||
r0 = snprintf(buffer_ + size, SRS_BASIC_LOG_SIZE - size, "(%s)", strerror(errno));
|
||||
|
||||
// Something not expected, drop the log.
|
||||
if (r0 <= 0 || r0 >= SRS_BASIC_LOG_SIZE - size) {
|
||||
return;
|
||||
}
|
||||
size += r0;
|
||||
}
|
||||
|
||||
if (level >= SrsLogLevelWarn) {
|
||||
fprintf(stderr, "%s\n", buffer_);
|
||||
} else {
|
||||
fprintf(stdout, "%s\n", buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
bool srs_log_header(char *buffer, int size, bool utc, bool dangerous, const char *tag, SrsContextId cid, const char *level, int *psize)
|
||||
{
|
||||
// clock time
|
||||
|
|
|
|||
|
|
@ -38,26 +38,6 @@ private:
|
|||
// Set the context id of specified thread, not self.
|
||||
extern const SrsContextId &srs_context_set_cid_of(srs_thread_t trd, const SrsContextId &v);
|
||||
|
||||
// The basic console log, which write log to console.
|
||||
class SrsConsoleLog : public ISrsLog
|
||||
{
|
||||
private:
|
||||
SrsLogLevel level_;
|
||||
bool utc_;
|
||||
|
||||
private:
|
||||
char *buffer_;
|
||||
|
||||
public:
|
||||
SrsConsoleLog(SrsLogLevel l, bool u);
|
||||
virtual ~SrsConsoleLog();
|
||||
// Interface ISrsLog
|
||||
public:
|
||||
virtual srs_error_t initialize();
|
||||
virtual void reopen();
|
||||
virtual void log(SrsLogLevel level, const char *tag, const SrsContextId &context_id, const char *fmt, va_list args);
|
||||
};
|
||||
|
||||
// Generate the log header.
|
||||
// @param dangerous Whether log is warning or error, log the errno if true.
|
||||
// @param utc Whether use UTC time format in the log header.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,15 @@ using namespace std;
|
|||
#include <srs_app_srt_server.hpp>
|
||||
#include <srt/srt.h>
|
||||
|
||||
// For RTMP test server
|
||||
#include <srs_protocol_conn.hpp>
|
||||
#include <srs_protocol_rtmp_conn.hpp>
|
||||
#include <srs_protocol_rtmp_stack.hpp>
|
||||
|
||||
// For TCP test server and client
|
||||
#include <srs_app_listener.hpp>
|
||||
#include <srs_app_st.hpp>
|
||||
|
||||
// Temporary disk config.
|
||||
std::string _srs_tmp_file_prefix = "/tmp/srs-utest-";
|
||||
// Temporary network config.
|
||||
|
|
@ -262,6 +271,7 @@ int MockProtectedBuffer::alloc(int size)
|
|||
|
||||
SrsCoroutineChan::SrsCoroutineChan()
|
||||
{
|
||||
trd_ = NULL;
|
||||
lock_ = srs_mutex_new();
|
||||
}
|
||||
|
||||
|
|
@ -293,5 +303,717 @@ SrsCoroutineChan *SrsCoroutineChan::copy()
|
|||
|
||||
SrsCoroutineChan *cp = new SrsCoroutineChan();
|
||||
cp->args_ = args_;
|
||||
cp->trd_ = trd_;
|
||||
return cp;
|
||||
}
|
||||
|
||||
extern string mock_http_response(int status, string content);
|
||||
|
||||
SrsHttpTestServer::SrsHttpTestServer(string response_body) : response_body_(response_body)
|
||||
{
|
||||
trd_ = new SrsSTCoroutine("http-test", this);
|
||||
fd_ = NULL;
|
||||
ip_ = "127.0.0.1";
|
||||
// Generate random port in range [30000, 60000]
|
||||
port_ = srs_rand_integer(30000, 60000);
|
||||
}
|
||||
|
||||
SrsHttpTestServer::~SrsHttpTestServer()
|
||||
{
|
||||
close();
|
||||
srs_freep(trd_);
|
||||
srs_close_stfd(fd_);
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpTestServer::start()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = srs_tcp_listen(ip_, port_, &fd_)) != srs_success) {
|
||||
return srs_error_wrap(err, "listen %s:%d", ip_.c_str(), port_);
|
||||
}
|
||||
|
||||
return trd_->start();
|
||||
}
|
||||
|
||||
void SrsHttpTestServer::close()
|
||||
{
|
||||
if (trd_) {
|
||||
trd_->stop();
|
||||
}
|
||||
srs_close_stfd(fd_);
|
||||
}
|
||||
|
||||
string SrsHttpTestServer::url()
|
||||
{
|
||||
return "http://" + ip_ + ":" + srs_strconv_format_int(port_);
|
||||
}
|
||||
|
||||
int SrsHttpTestServer::get_port()
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpTestServer::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
srs_netfd_t cfd = srs_accept(fd_, NULL, NULL, SRS_UTIME_NO_TIMEOUT);
|
||||
if (cfd == NULL) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = do_cycle(cfd);
|
||||
srs_close_stfd(cfd);
|
||||
srs_freep(err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpTestServer::do_cycle(srs_netfd_t cfd)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsStSocket skt(cfd);
|
||||
skt.set_recv_timeout(1 * SRS_UTIME_SECONDS);
|
||||
skt.set_send_timeout(1 * SRS_UTIME_SECONDS);
|
||||
|
||||
while (true) {
|
||||
if ((err = trd_->pull()) != srs_success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
if ((err = skt.read(buf, 1024, NULL)) != srs_success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Generate proper HTTP response
|
||||
string res = mock_http_response(200, response_body_);
|
||||
if ((err = skt.write((char *)res.data(), (int)res.length(), NULL)) != srs_success) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsHttpsTestServer::SrsHttpsTestServer(string response_body, string key_file, string cert_file)
|
||||
: response_body_(response_body), ssl_key_file_(key_file), ssl_cert_file_(cert_file)
|
||||
{
|
||||
trd_ = new SrsFastCoroutine("https-test", this);
|
||||
fd_ = NULL;
|
||||
ip_ = "127.0.0.1";
|
||||
// Generate random port in range [30000, 60000]
|
||||
port_ = srs_rand_integer(30000, 60000);
|
||||
}
|
||||
|
||||
SrsHttpsTestServer::~SrsHttpsTestServer()
|
||||
{
|
||||
close();
|
||||
srs_freep(trd_);
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpsTestServer::start()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = srs_tcp_listen(ip_, port_, &fd_)) != srs_success) {
|
||||
return srs_error_wrap(err, "listen %s:%d", ip_.c_str(), port_);
|
||||
}
|
||||
|
||||
if ((err = trd_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "start coroutine");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsHttpsTestServer::close()
|
||||
{
|
||||
if (trd_) {
|
||||
trd_->stop();
|
||||
}
|
||||
if (fd_) {
|
||||
srs_close_stfd(fd_);
|
||||
fd_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
string SrsHttpsTestServer::url()
|
||||
{
|
||||
return "https://" + ip_ + ":" + srs_strconv_format_int(port_);
|
||||
}
|
||||
|
||||
int SrsHttpsTestServer::get_port()
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpsTestServer::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (true) {
|
||||
if ((err = trd_->pull()) != srs_success) {
|
||||
return srs_error_wrap(err, "pull");
|
||||
}
|
||||
|
||||
srs_netfd_t client_fd = srs_accept(fd_, NULL, NULL, SRS_UTIME_NO_TIMEOUT);
|
||||
if (client_fd == NULL) {
|
||||
return srs_error_new(ERROR_SOCKET_ACCEPT, "accept failed");
|
||||
}
|
||||
|
||||
if ((err = handle_client(client_fd)) != srs_success) {
|
||||
srs_warn("handle client failed, err=%s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpsTestServer::handle_client(srs_netfd_t client_fd)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsStSocket *skt = new SrsStSocket(client_fd);
|
||||
SrsUniquePtr<SrsStSocket> skt_uptr(skt);
|
||||
|
||||
// Create SSL connection
|
||||
SrsSslConnection *ssl = new SrsSslConnection(skt);
|
||||
SrsUniquePtr<SrsSslConnection> ssl_uptr(ssl);
|
||||
|
||||
// Perform SSL handshake
|
||||
if ((err = ssl->handshake(ssl_key_file_, ssl_cert_file_)) != srs_success) {
|
||||
return srs_error_wrap(err, "ssl handshake");
|
||||
}
|
||||
|
||||
// Read HTTP request (simplified - just read some data)
|
||||
char buf[4096];
|
||||
ssize_t nread = 0;
|
||||
if ((err = ssl->read(buf, sizeof(buf), &nread)) != srs_success) {
|
||||
return srs_error_wrap(err, "read request");
|
||||
}
|
||||
|
||||
// Send HTTP response
|
||||
string response = mock_http_response(200, response_body_);
|
||||
if ((err = ssl->write((void *)response.data(), response.length(), NULL)) != srs_success) {
|
||||
return srs_error_wrap(err, "write response");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsRtmpTestServer::SrsRtmpTestServer(string app, string stream) : app_(app), stream_(stream)
|
||||
{
|
||||
trd_ = new SrsSTCoroutine("rtmp-test", this);
|
||||
fd_ = NULL;
|
||||
ip_ = "127.0.0.1";
|
||||
enable_publish_ = true;
|
||||
enable_play_ = true;
|
||||
// Generate random port in range [30000, 60000]
|
||||
port_ = srs_rand_integer(30000, 60000);
|
||||
}
|
||||
|
||||
SrsRtmpTestServer::~SrsRtmpTestServer()
|
||||
{
|
||||
close();
|
||||
srs_freep(trd_);
|
||||
srs_close_stfd(fd_);
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpTestServer::start()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = srs_tcp_listen(ip_, port_, &fd_)) != srs_success) {
|
||||
return srs_error_wrap(err, "listen %s:%d", ip_.c_str(), port_);
|
||||
}
|
||||
|
||||
return trd_->start();
|
||||
}
|
||||
|
||||
void SrsRtmpTestServer::close()
|
||||
{
|
||||
if (trd_) {
|
||||
trd_->stop();
|
||||
}
|
||||
srs_close_stfd(fd_);
|
||||
}
|
||||
|
||||
string SrsRtmpTestServer::url()
|
||||
{
|
||||
return "rtmp://" + ip_ + ":" + srs_strconv_format_int(port_) + "/" + app_ + "/" + stream_;
|
||||
}
|
||||
|
||||
int SrsRtmpTestServer::get_port()
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
void SrsRtmpTestServer::enable_publish(bool v)
|
||||
{
|
||||
enable_publish_ = v;
|
||||
}
|
||||
|
||||
void SrsRtmpTestServer::enable_play(bool v)
|
||||
{
|
||||
enable_play_ = v;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpTestServer::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
srs_netfd_t cfd = srs_accept(fd_, NULL, NULL, SRS_UTIME_NO_TIMEOUT);
|
||||
if (cfd == NULL) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = do_cycle(cfd);
|
||||
srs_close_stfd(cfd);
|
||||
srs_freep(err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpTestServer::do_cycle(srs_netfd_t cfd)
|
||||
{
|
||||
return handle_rtmp_client(cfd);
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpTestServer::handle_rtmp_client(srs_netfd_t cfd)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsStSocket skt(cfd);
|
||||
skt.set_recv_timeout(5 * SRS_UTIME_SECONDS);
|
||||
skt.set_send_timeout(5 * SRS_UTIME_SECONDS);
|
||||
|
||||
// Create RTMP server to handle the client
|
||||
SrsRtmpServer rtmp(&skt);
|
||||
|
||||
// Perform RTMP handshake
|
||||
if ((err = rtmp.handshake()) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp handshake");
|
||||
}
|
||||
|
||||
// Handle connect app
|
||||
SrsRequest req;
|
||||
if ((err = rtmp.connect_app(&req)) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp connect app");
|
||||
}
|
||||
|
||||
// Respond to connect app
|
||||
if ((err = rtmp.response_connect_app(&req, ip_.c_str())) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp response connect app");
|
||||
}
|
||||
|
||||
// Set window ack size
|
||||
if ((err = rtmp.set_window_ack_size(2500000)) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp set window ack size");
|
||||
}
|
||||
|
||||
// Set peer bandwidth
|
||||
if ((err = rtmp.set_peer_bandwidth(2500000, 2)) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp set peer bandwidth");
|
||||
}
|
||||
|
||||
// Send onBWDone
|
||||
if ((err = rtmp.on_bw_done()) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp on bw done");
|
||||
}
|
||||
|
||||
// Identify client (play or publish)
|
||||
int stream_id = 1; // Use a fixed stream ID for testing
|
||||
SrsRtmpConnType type = SrsRtmpConnUnknown;
|
||||
string stream_name;
|
||||
srs_utime_t duration = 0;
|
||||
|
||||
if ((err = rtmp.identify_client(stream_id, type, stream_name, duration)) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp identify client");
|
||||
}
|
||||
|
||||
// Set chunk size
|
||||
if ((err = rtmp.set_chunk_size(4096)) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp set chunk size");
|
||||
}
|
||||
|
||||
// Handle based on client type
|
||||
if (srs_client_type_is_publish(type)) {
|
||||
if (!enable_publish_) {
|
||||
return srs_error_new(ERROR_RTMP_ACCESS_DENIED, "publish not enabled");
|
||||
}
|
||||
// For publish, we just accept it and don't send any response
|
||||
// The client will start sending media data
|
||||
} else {
|
||||
if (!enable_play_) {
|
||||
return srs_error_new(ERROR_RTMP_ACCESS_DENIED, "play not enabled");
|
||||
}
|
||||
// For play, send play start response
|
||||
if ((err = rtmp.start_play(stream_id)) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp start play");
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsTestTcpServer::SrsTestTcpServer(string ip)
|
||||
{
|
||||
trd_ = NULL;
|
||||
listener_ = NULL;
|
||||
conn_ = NULL;
|
||||
ip_ = ip;
|
||||
// Generate random port in range [30000, 60000]
|
||||
port_ = 30000 + (srs_rand_integer() % (60000 - 30000 + 1));
|
||||
}
|
||||
|
||||
SrsTestTcpServer::~SrsTestTcpServer()
|
||||
{
|
||||
close();
|
||||
srs_freep(conn_);
|
||||
}
|
||||
|
||||
srs_error_t SrsTestTcpServer::start()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
listener_ = new SrsTcpListener(this);
|
||||
listener_->set_endpoint(ip_, port_);
|
||||
|
||||
if ((err = listener_->listen()) != srs_success) {
|
||||
return srs_error_wrap(err, "tcp listen %s:%d", ip_.c_str(), port_);
|
||||
}
|
||||
|
||||
// Get the actual port that was assigned
|
||||
port_ = listener_->port();
|
||||
|
||||
trd_ = new SrsSTCoroutine("tcp-test", this);
|
||||
if ((err = trd_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "start tcp test server");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsTestTcpServer::close()
|
||||
{
|
||||
if (listener_) {
|
||||
listener_->close();
|
||||
srs_freep(listener_);
|
||||
}
|
||||
|
||||
if (trd_) {
|
||||
trd_->stop();
|
||||
srs_freep(trd_);
|
||||
}
|
||||
}
|
||||
|
||||
int SrsTestTcpServer::get_port()
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
SrsTcpConnection *SrsTestTcpServer::get_connection()
|
||||
{
|
||||
return conn_;
|
||||
}
|
||||
|
||||
srs_error_t SrsTestTcpServer::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (true) {
|
||||
if ((err = trd_->pull()) != srs_success) {
|
||||
return srs_error_wrap(err, "tcp test server");
|
||||
}
|
||||
|
||||
// Just wait for connections, the listener handles them via on_tcp_client
|
||||
srs_usleep(10 * SRS_UTIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsTestTcpServer::on_tcp_client(ISrsListener *listener, srs_netfd_t stfd)
|
||||
{
|
||||
srs_freep(conn_);
|
||||
conn_ = new SrsTcpConnection(stfd);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
SrsTestTcpClient::SrsTestTcpClient(string host, int port, srs_utime_t timeout)
|
||||
{
|
||||
client_ = NULL;
|
||||
conn_ = NULL;
|
||||
host_ = host;
|
||||
port_ = port;
|
||||
timeout_ = timeout;
|
||||
}
|
||||
|
||||
SrsTestTcpClient::~SrsTestTcpClient()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
srs_error_t SrsTestTcpClient::connect()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
close(); // Close any existing connection
|
||||
|
||||
client_ = new SrsTcpClient(host_, port_, timeout_);
|
||||
if ((err = client_->connect()) != srs_success) {
|
||||
return srs_error_wrap(err, "tcp client connect %s:%d", host_.c_str(), port_);
|
||||
}
|
||||
|
||||
// Create SrsTcpConnection from the connected client
|
||||
// We need to get the file descriptor from the client
|
||||
// Since SrsTcpClient doesn't expose the fd directly, we'll create a new connection
|
||||
srs_netfd_t stfd = NULL;
|
||||
if ((err = srs_tcp_connect(host_, port_, timeout_, &stfd)) != srs_success) {
|
||||
return srs_error_wrap(err, "tcp connect for connection %s:%d", host_.c_str(), port_);
|
||||
}
|
||||
|
||||
conn_ = new SrsTcpConnection(stfd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsTestTcpClient::close()
|
||||
{
|
||||
srs_freep(client_);
|
||||
srs_freep(conn_);
|
||||
}
|
||||
|
||||
SrsTcpConnection *SrsTestTcpClient::get_connection()
|
||||
{
|
||||
return conn_;
|
||||
}
|
||||
|
||||
srs_error_t SrsTestTcpClient::write(void *buf, size_t size, ssize_t *nwrite)
|
||||
{
|
||||
if (!client_) {
|
||||
return srs_error_new(ERROR_SOCKET_WRITE, "client not connected");
|
||||
}
|
||||
return client_->write(buf, size, nwrite);
|
||||
}
|
||||
|
||||
srs_error_t SrsTestTcpClient::read(void *buf, size_t size, ssize_t *nread)
|
||||
{
|
||||
if (!client_) {
|
||||
return srs_error_new(ERROR_SOCKET_READ, "client not connected");
|
||||
}
|
||||
return client_->read(buf, size, nread);
|
||||
}
|
||||
|
||||
SrsUdpTestServer::SrsUdpTestServer(string host)
|
||||
{
|
||||
host_ = host;
|
||||
lfd_ = NULL;
|
||||
trd_ = NULL;
|
||||
socket_ = NULL;
|
||||
started_ = false;
|
||||
// Generate random port in range [30000, 60000]
|
||||
port_ = 30000 + (srs_rand_integer() % (60000 - 30000 + 1));
|
||||
}
|
||||
|
||||
SrsUdpTestServer::~SrsUdpTestServer()
|
||||
{
|
||||
stop();
|
||||
srs_freep(socket_);
|
||||
srs_close_stfd(lfd_);
|
||||
}
|
||||
|
||||
srs_error_t SrsUdpTestServer::start()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (started_) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Create UDP socket
|
||||
if ((err = srs_udp_listen(host_, port_, &lfd_)) != srs_success) {
|
||||
return srs_error_wrap(err, "udp listen %s:%d", host_.c_str(), port_);
|
||||
}
|
||||
|
||||
// Get the actual port that was assigned
|
||||
int actual_fd = srs_netfd_fileno(lfd_);
|
||||
sockaddr_storage addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
if (getsockname(actual_fd, (sockaddr *)&addr, &addrlen) == 0) {
|
||||
if (addr.ss_family == AF_INET) {
|
||||
port_ = ntohs(((sockaddr_in *)&addr)->sin_port);
|
||||
} else if (addr.ss_family == AF_INET6) {
|
||||
port_ = ntohs(((sockaddr_in6 *)&addr)->sin6_port);
|
||||
}
|
||||
}
|
||||
|
||||
// Create socket wrapper
|
||||
socket_ = new SrsStSocket(lfd_);
|
||||
|
||||
// Start coroutine to handle packets
|
||||
trd_ = new SrsSTCoroutine("udp-test", this);
|
||||
if ((err = trd_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "start udp test server");
|
||||
}
|
||||
|
||||
started_ = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsUdpTestServer::stop()
|
||||
{
|
||||
started_ = false;
|
||||
if (trd_) {
|
||||
trd_->stop();
|
||||
srs_freep(trd_);
|
||||
}
|
||||
}
|
||||
|
||||
int SrsUdpTestServer::get_port()
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
SrsStSocket *SrsUdpTestServer::get_socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
srs_error_t SrsUdpTestServer::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (started_) {
|
||||
if ((err = trd_->pull()) != srs_success) {
|
||||
return srs_error_wrap(err, "udp test server");
|
||||
}
|
||||
|
||||
// Simple echo server - receive and echo back using recvfrom/sendto
|
||||
char buf[1024];
|
||||
sockaddr_storage from;
|
||||
int fromlen = sizeof(from);
|
||||
|
||||
ssize_t nread = srs_recvfrom(lfd_, buf, sizeof(buf), (sockaddr *)&from, &fromlen, 10 * SRS_UTIME_MILLISECONDS);
|
||||
if (nread <= 0) {
|
||||
continue; // Timeout or error, continue
|
||||
}
|
||||
|
||||
// Echo back the data
|
||||
ssize_t nwrite = srs_sendto(lfd_, buf, nread, (sockaddr *)&from, fromlen, 10 * SRS_UTIME_MILLISECONDS);
|
||||
if (nwrite <= 0) {
|
||||
continue; // Error sending, continue
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsUdpTestClient::SrsUdpTestClient(string host, int port, srs_utime_t timeout)
|
||||
{
|
||||
host_ = host;
|
||||
port_ = port;
|
||||
timeout_ = timeout;
|
||||
stfd_ = NULL;
|
||||
socket_ = NULL;
|
||||
memset(&server_addr_, 0, sizeof(server_addr_));
|
||||
server_addrlen_ = 0;
|
||||
}
|
||||
|
||||
SrsUdpTestClient::~SrsUdpTestClient()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
srs_error_t SrsUdpTestClient::connect()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
close();
|
||||
|
||||
// Create UDP socket
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock == -1) {
|
||||
return srs_error_new(ERROR_SOCKET_CREATE, "create udp socket");
|
||||
}
|
||||
|
||||
stfd_ = srs_netfd_open_socket(sock);
|
||||
if (stfd_ == NULL) {
|
||||
::close(sock);
|
||||
return srs_error_new(ERROR_ST_OPEN_SOCKET, "open udp socket");
|
||||
}
|
||||
|
||||
// Setup server address
|
||||
sockaddr_in *addr = (sockaddr_in *)&server_addr_;
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = htons(port_);
|
||||
if (inet_pton(AF_INET, host_.c_str(), &addr->sin_addr) <= 0) {
|
||||
close();
|
||||
return srs_error_new(ERROR_SYSTEM_IP_INVALID, "invalid ip %s", host_.c_str());
|
||||
}
|
||||
server_addrlen_ = sizeof(sockaddr_in);
|
||||
|
||||
// Create socket wrapper
|
||||
socket_ = new SrsStSocket(stfd_);
|
||||
socket_->set_recv_timeout(timeout_);
|
||||
socket_->set_send_timeout(timeout_);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsUdpTestClient::close()
|
||||
{
|
||||
srs_freep(socket_);
|
||||
srs_close_stfd(stfd_);
|
||||
}
|
||||
|
||||
SrsStSocket *SrsUdpTestClient::get_socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
srs_error_t SrsUdpTestClient::sendto(void *buf, size_t size, ssize_t *nwrite)
|
||||
{
|
||||
if (!stfd_) {
|
||||
return srs_error_new(ERROR_SOCKET_WRITE, "udp client not connected");
|
||||
}
|
||||
|
||||
ssize_t nb_write = srs_sendto(stfd_, buf, size, (sockaddr *)&server_addr_, server_addrlen_, timeout_);
|
||||
if (nb_write <= 0) {
|
||||
if (nb_write < 0 && errno == ETIME) {
|
||||
return srs_error_new(ERROR_SOCKET_TIMEOUT, "sendto timeout %d ms", srsu2msi(timeout_));
|
||||
}
|
||||
return srs_error_new(ERROR_SOCKET_WRITE, "sendto failed");
|
||||
}
|
||||
|
||||
if (nwrite) {
|
||||
*nwrite = nb_write;
|
||||
}
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t SrsUdpTestClient::recvfrom(void *buf, size_t size, ssize_t *nread)
|
||||
{
|
||||
if (!stfd_) {
|
||||
return srs_error_new(ERROR_SOCKET_READ, "udp client not connected");
|
||||
}
|
||||
|
||||
sockaddr_storage from;
|
||||
int fromlen = sizeof(from);
|
||||
ssize_t nb_read = srs_recvfrom(stfd_, buf, size, (sockaddr *)&from, &fromlen, timeout_);
|
||||
if (nb_read <= 0) {
|
||||
if (nb_read < 0 && errno == ETIME) {
|
||||
return srs_error_new(ERROR_SOCKET_TIMEOUT, "recvfrom timeout %d ms", srsu2msi(timeout_));
|
||||
}
|
||||
return srs_error_new(ERROR_SOCKET_READ, "recvfrom failed");
|
||||
}
|
||||
|
||||
if (nread) {
|
||||
*nread = nb_read;
|
||||
}
|
||||
return srs_success;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ using namespace std;
|
|||
#include <srs_app_log.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
|
||||
// Include headers for TCP test classes
|
||||
#include <srs_app_listener.hpp>
|
||||
#include <srs_protocol_conn.hpp>
|
||||
|
||||
// we add an empty macro for upp to show the smart tips.
|
||||
#define VOID
|
||||
|
||||
|
|
@ -139,6 +143,10 @@ private:
|
|||
std::vector<void *> args_;
|
||||
srs_mutex_t lock_;
|
||||
|
||||
public:
|
||||
// The thread to run the coroutine.
|
||||
ISrsCoroutine *trd_;
|
||||
|
||||
public:
|
||||
SrsCoroutineChan();
|
||||
virtual ~SrsCoroutineChan();
|
||||
|
|
@ -192,36 +200,32 @@ public:
|
|||
// // The coroutine will be stopped and wait for it to terminate.
|
||||
// // So maybe it won't execute all your code there.
|
||||
//
|
||||
// Enjoiy the sugar for coroutines.
|
||||
#define SRS_COROUTINE_GO_IMPL(context, id, code_block) \
|
||||
class AnonymousCoroutineHandler##id : public ISrsCoroutineHandler \
|
||||
{ \
|
||||
private: \
|
||||
SrsCoroutineChan *ctx_; \
|
||||
\
|
||||
public: \
|
||||
AnonymousCoroutineHandler##id(SrsCoroutineChan *c) \
|
||||
{ \
|
||||
/* Copy the context so that we can pop it in different coroutines. */ \
|
||||
ctx_ = c->copy(); \
|
||||
} \
|
||||
~AnonymousCoroutineHandler##id() \
|
||||
{ \
|
||||
srs_freep(ctx_); \
|
||||
} \
|
||||
\
|
||||
public: \
|
||||
virtual srs_error_t cycle() \
|
||||
{ \
|
||||
SrsCoroutineChan &ctx = *ctx_; \
|
||||
(void)ctx; \
|
||||
code_block; \
|
||||
return srs_success; \
|
||||
} \
|
||||
}; \
|
||||
AnonymousCoroutineHandler##id handler##id(context); \
|
||||
SrsSTCoroutine st##id("anonymous", &handler##id); \
|
||||
srs_error_t err_coroutine##id = st##id.start(); \
|
||||
// Warning: Donot use this macro unless you don't need to debug the code block,
|
||||
// because it's impossible to debug it. Accordingly, you should use it when the
|
||||
// code block is very simple.
|
||||
#define SRS_COROUTINE_GO_IMPL(context, id, code_block) \
|
||||
class AnonymousCoroutineHandler##id : public ISrsCoroutineHandler \
|
||||
{ \
|
||||
private: \
|
||||
SrsCoroutineChan *ctx_; \
|
||||
\
|
||||
public: \
|
||||
AnonymousCoroutineHandler##id() : ctx_(NULL) {} \
|
||||
~AnonymousCoroutineHandler##id() { srs_freep(ctx_); } \
|
||||
void set_ctx(SrsCoroutineChan *c) { ctx_ = c->copy(); } \
|
||||
virtual srs_error_t cycle() \
|
||||
{ \
|
||||
SrsCoroutineChan &ctx = *ctx_; \
|
||||
(void)ctx; \
|
||||
code_block; \
|
||||
return srs_success; \
|
||||
} \
|
||||
}; \
|
||||
AnonymousCoroutineHandler##id handler##id; \
|
||||
SrsSTCoroutine st##id("anonymous", &handler##id); \
|
||||
(context)->trd_ = &st##id; \
|
||||
handler##id.set_ctx(context); \
|
||||
srs_error_t err_coroutine##id = st##id.start(); \
|
||||
srs_assert(err_coroutine##id == srs_success)
|
||||
|
||||
// A helper to create a anonymous coroutine like goroutine in Go.
|
||||
|
|
@ -274,4 +278,197 @@ public:
|
|||
#define SRS_COROUTINE_GO_CTX2(ctx, id, code_block) \
|
||||
SRS_COROUTINE_GO_IMPL(ctx, id, code_block)
|
||||
|
||||
// Simple HTTP test server similar to Go's httptest.NewServer
|
||||
// This is a simplified version that uses the raw socket approach like MockOnCycleThread4
|
||||
// but with proper HTTP response formatting
|
||||
class SrsHttpTestServer : public ISrsCoroutineHandler
|
||||
{
|
||||
private:
|
||||
ISrsCoroutine *trd_;
|
||||
srs_netfd_t fd_;
|
||||
string response_body_;
|
||||
string ip_;
|
||||
int port_;
|
||||
|
||||
public:
|
||||
SrsHttpTestServer(string response_body);
|
||||
virtual ~SrsHttpTestServer();
|
||||
|
||||
public:
|
||||
virtual srs_error_t start();
|
||||
virtual void close();
|
||||
virtual string url();
|
||||
virtual int get_port();
|
||||
|
||||
// Interface ISrsCoroutineHandler
|
||||
public:
|
||||
virtual srs_error_t cycle();
|
||||
|
||||
private:
|
||||
virtual srs_error_t do_cycle(srs_netfd_t cfd);
|
||||
};
|
||||
|
||||
// Simple HTTPS test server similar to Go's httptest.NewServer but with SSL support
|
||||
class SrsHttpsTestServer : public ISrsCoroutineHandler
|
||||
{
|
||||
private:
|
||||
ISrsCoroutine *trd_;
|
||||
srs_netfd_t fd_;
|
||||
string response_body_;
|
||||
string ssl_key_file_;
|
||||
string ssl_cert_file_;
|
||||
string ip_;
|
||||
int port_;
|
||||
|
||||
public:
|
||||
SrsHttpsTestServer(string response_body, string key_file = "./conf/server.key", string cert_file = "./conf/server.crt");
|
||||
virtual ~SrsHttpsTestServer();
|
||||
virtual srs_error_t start();
|
||||
virtual void close();
|
||||
virtual string url();
|
||||
virtual int get_port();
|
||||
|
||||
// Interface ISrsCoroutineHandler
|
||||
private:
|
||||
virtual srs_error_t cycle();
|
||||
|
||||
private:
|
||||
srs_error_t handle_client(srs_netfd_t client_fd);
|
||||
};
|
||||
|
||||
// Simple RTMP test server similar to SrsHttpTestServer but for RTMP protocol
|
||||
// This server handles basic RTMP handshake and connect app operations
|
||||
class SrsRtmpTestServer : public ISrsCoroutineHandler
|
||||
{
|
||||
private:
|
||||
ISrsCoroutine *trd_;
|
||||
srs_netfd_t fd_;
|
||||
string app_;
|
||||
string stream_;
|
||||
string ip_;
|
||||
int port_;
|
||||
bool enable_publish_;
|
||||
bool enable_play_;
|
||||
|
||||
public:
|
||||
SrsRtmpTestServer(string app = "live", string stream = "test");
|
||||
virtual ~SrsRtmpTestServer();
|
||||
|
||||
public:
|
||||
virtual srs_error_t start();
|
||||
virtual void close();
|
||||
virtual string url();
|
||||
virtual int get_port();
|
||||
virtual void enable_publish(bool v = true);
|
||||
virtual void enable_play(bool v = true);
|
||||
|
||||
// Interface ISrsCoroutineHandler
|
||||
public:
|
||||
virtual srs_error_t cycle();
|
||||
|
||||
private:
|
||||
virtual srs_error_t do_cycle(srs_netfd_t cfd);
|
||||
virtual srs_error_t handle_rtmp_client(srs_netfd_t cfd);
|
||||
};
|
||||
|
||||
// Test TCP server for testing SrsTcpConnection
|
||||
class SrsTestTcpServer : public ISrsCoroutineHandler, public ISrsTcpHandler
|
||||
{
|
||||
private:
|
||||
ISrsCoroutine *trd_;
|
||||
SrsTcpListener *listener_;
|
||||
string ip_;
|
||||
int port_;
|
||||
SrsTcpConnection *conn_;
|
||||
|
||||
public:
|
||||
SrsTestTcpServer(string ip = "127.0.0.1");
|
||||
virtual ~SrsTestTcpServer();
|
||||
|
||||
public:
|
||||
virtual srs_error_t start();
|
||||
virtual void close();
|
||||
virtual int get_port();
|
||||
virtual SrsTcpConnection *get_connection();
|
||||
|
||||
// Interface ISrsCoroutineHandler
|
||||
public:
|
||||
virtual srs_error_t cycle();
|
||||
|
||||
// Interface ISrsTcpHandler
|
||||
public:
|
||||
virtual srs_error_t on_tcp_client(ISrsListener *listener, srs_netfd_t stfd);
|
||||
};
|
||||
|
||||
// Test TCP client for testing SrsTcpConnection
|
||||
class SrsTestTcpClient
|
||||
{
|
||||
private:
|
||||
SrsTcpClient *client_;
|
||||
SrsTcpConnection *conn_;
|
||||
string host_;
|
||||
int port_;
|
||||
srs_utime_t timeout_;
|
||||
|
||||
public:
|
||||
SrsTestTcpClient(string host, int port, srs_utime_t timeout = 1 * SRS_UTIME_SECONDS);
|
||||
virtual ~SrsTestTcpClient();
|
||||
|
||||
public:
|
||||
virtual srs_error_t connect();
|
||||
virtual void close();
|
||||
virtual SrsTcpConnection *get_connection();
|
||||
virtual srs_error_t write(void *buf, size_t size, ssize_t *nwrite);
|
||||
virtual srs_error_t read(void *buf, size_t size, ssize_t *nread);
|
||||
};
|
||||
|
||||
// Test UDP server for testing UDP socket communication
|
||||
class SrsUdpTestServer : public ISrsCoroutineHandler
|
||||
{
|
||||
private:
|
||||
srs_netfd_t lfd_;
|
||||
ISrsCoroutine *trd_;
|
||||
SrsStSocket *socket_;
|
||||
string host_;
|
||||
int port_;
|
||||
bool started_;
|
||||
|
||||
public:
|
||||
SrsUdpTestServer(string host);
|
||||
virtual ~SrsUdpTestServer();
|
||||
|
||||
public:
|
||||
virtual srs_error_t start();
|
||||
virtual void stop();
|
||||
virtual int get_port();
|
||||
virtual SrsStSocket *get_socket();
|
||||
|
||||
public:
|
||||
virtual srs_error_t cycle();
|
||||
};
|
||||
|
||||
// Test UDP client for testing UDP socket communication
|
||||
class SrsUdpTestClient
|
||||
{
|
||||
private:
|
||||
srs_netfd_t stfd_;
|
||||
SrsStSocket *socket_;
|
||||
string host_;
|
||||
int port_;
|
||||
srs_utime_t timeout_;
|
||||
sockaddr_storage server_addr_;
|
||||
int server_addrlen_;
|
||||
|
||||
public:
|
||||
SrsUdpTestClient(string host, int port, srs_utime_t timeout = 1 * SRS_UTIME_SECONDS);
|
||||
virtual ~SrsUdpTestClient();
|
||||
|
||||
public:
|
||||
virtual srs_error_t connect();
|
||||
virtual void close();
|
||||
virtual SrsStSocket *get_socket();
|
||||
virtual srs_error_t sendto(void *buf, size_t size, ssize_t *nwrite);
|
||||
virtual srs_error_t recvfrom(void *buf, size_t size, ssize_t *nread);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using namespace std;
|
|||
#include <srs_app_st.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_kernel_buffer.hpp>
|
||||
#include <srs_kernel_codec.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_protocol_amf0.hpp>
|
||||
|
|
@ -400,21 +401,668 @@ VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamBasic)
|
|||
(void)is_pps3;
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolHttpClientTest, SrsHttpClientBasic)
|
||||
VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamVpsDemux)
|
||||
{
|
||||
SrsHttpClient client;
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Test basic initialization - should not crash
|
||||
// We can't easily test actual HTTP requests without a server
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Test header setting
|
||||
SrsHttpClient *result = client.set_header("User-Agent", "SRS-Test");
|
||||
EXPECT_TRUE(result != NULL);
|
||||
EXPECT_EQ(&client, result); // Should return self for chaining
|
||||
// Test VPS demux with valid VPS data
|
||||
if (true) {
|
||||
// Create VPS NALU data (NALU type 32 = 0x40)
|
||||
unsigned char vps_data[] = {0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xac, 0x09};
|
||||
std::string vps_output;
|
||||
|
||||
// Test multiple headers
|
||||
client.set_header("Content-Type", "application/json");
|
||||
client.set_header("Accept", "application/json");
|
||||
HELPER_EXPECT_SUCCESS(hevc.vps_demux((char *)vps_data, sizeof(vps_data), vps_output));
|
||||
|
||||
// Should copy the VPS data
|
||||
EXPECT_EQ(sizeof(vps_data), vps_output.length());
|
||||
EXPECT_EQ(0, memcmp(vps_data, vps_output.data(), sizeof(vps_data)));
|
||||
}
|
||||
|
||||
// Test VPS demux with empty data - should fail
|
||||
if (true) {
|
||||
std::string vps_output;
|
||||
HELPER_EXPECT_FAILED(hevc.vps_demux(NULL, 0, vps_output));
|
||||
}
|
||||
|
||||
// Test VPS demux with minimal data
|
||||
if (true) {
|
||||
unsigned char minimal_vps[] = {0x40};
|
||||
std::string vps_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.vps_demux((char *)minimal_vps, sizeof(minimal_vps), vps_output));
|
||||
EXPECT_EQ(sizeof(minimal_vps), vps_output.length());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamSpsDemux)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Test SPS demux with valid SPS data
|
||||
if (true) {
|
||||
// Create SPS NALU data (NALU type 33 = 0x42)
|
||||
unsigned char sps_data[] = {0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xa0, 0x02, 0x80, 0x80, 0x2d, 0x16, 0x59, 0x59, 0xa4, 0x93, 0x2b, 0xc0, 0x5a, 0x70, 0x80, 0x00, 0x01, 0xf4, 0x80, 0x00, 0x5d, 0xc0, 0x47, 0xe1, 0x81, 0x65, 0x80};
|
||||
std::string sps_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.sps_demux((char *)sps_data, sizeof(sps_data), sps_output));
|
||||
|
||||
// Should copy the SPS data
|
||||
EXPECT_EQ(sizeof(sps_data), sps_output.length());
|
||||
EXPECT_EQ(0, memcmp(sps_data, sps_output.data(), sizeof(sps_data)));
|
||||
}
|
||||
|
||||
// Test SPS demux with insufficient data (less than 4 bytes) - should succeed but return empty
|
||||
if (true) {
|
||||
unsigned char short_sps[] = {0x42, 0x01, 0x01};
|
||||
std::string sps_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.sps_demux((char *)short_sps, sizeof(short_sps), sps_output));
|
||||
EXPECT_TRUE(sps_output.empty());
|
||||
}
|
||||
|
||||
// Test SPS demux with exactly 4 bytes
|
||||
if (true) {
|
||||
unsigned char min_sps[] = {0x42, 0x01, 0x01, 0x01};
|
||||
std::string sps_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.sps_demux((char *)min_sps, sizeof(min_sps), sps_output));
|
||||
EXPECT_EQ(sizeof(min_sps), sps_output.length());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamPpsDemux)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Test PPS demux with valid PPS data
|
||||
if (true) {
|
||||
// Create PPS NALU data (NALU type 34 = 0x44)
|
||||
unsigned char pps_data[] = {0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89, 0x00};
|
||||
std::string pps_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.pps_demux((char *)pps_data, sizeof(pps_data), pps_output));
|
||||
|
||||
// Should copy the PPS data
|
||||
EXPECT_EQ(sizeof(pps_data), pps_output.length());
|
||||
EXPECT_EQ(0, memcmp(pps_data, pps_output.data(), sizeof(pps_data)));
|
||||
}
|
||||
|
||||
// Test PPS demux with empty data - should fail
|
||||
if (true) {
|
||||
std::string pps_output;
|
||||
HELPER_EXPECT_FAILED(hevc.pps_demux(NULL, 0, pps_output));
|
||||
}
|
||||
|
||||
// Test PPS demux with minimal data
|
||||
if (true) {
|
||||
unsigned char minimal_pps[] = {0x44};
|
||||
std::string pps_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.pps_demux((char *)minimal_pps, sizeof(minimal_pps), pps_output));
|
||||
EXPECT_EQ(sizeof(minimal_pps), pps_output.length());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamMuxSequenceHeader)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Test mux_sequence_header with valid HEVC VPS, SPS, and PPS data
|
||||
if (true) {
|
||||
// Create valid HEVC VPS data that will pass demux validation
|
||||
// VPS NALU: forbidden_zero_bit(1) + nal_unit_type(6) + nuh_layer_id(6) + nuh_temporal_id_plus1(3) + RBSP
|
||||
// NALU type 32 (VPS) = 0x40 (32 << 1), nuh_layer_id=0, nuh_temporal_id_plus1=1
|
||||
unsigned char vps_raw[] = {
|
||||
0x40, 0x01, // NALU header: type=32(VPS), layer_id=0, temporal_id=1
|
||||
0x0C, 0x01, 0xFF, 0xFF, 0x01, 0x60, 0x00, 0x00, // VPS RBSP data
|
||||
0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||
0x03, 0x00, 0x5D, 0xAC, 0x09};
|
||||
std::string vps_data((char *)vps_raw, sizeof(vps_raw));
|
||||
|
||||
// Create valid HEVC SPS data that will pass demux validation
|
||||
// SPS NALU: NALU type 33 (SPS) = 0x42 (33 << 1)
|
||||
unsigned char sps_raw[] = {
|
||||
0x42, 0x01, // NALU header: type=33(SPS), layer_id=0, temporal_id=1
|
||||
0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, // SPS RBSP data
|
||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5D,
|
||||
0xA0, 0x02, 0x80, 0x80, 0x2D, 0x16, 0x59, 0x59,
|
||||
0xA4, 0x93, 0x2B, 0xC0, 0x5A, 0x70, 0x80};
|
||||
std::string sps_data((char *)sps_raw, sizeof(sps_raw));
|
||||
|
||||
// Create valid HEVC PPS data
|
||||
// PPS NALU: NALU type 34 (PPS) = 0x44 (34 << 1)
|
||||
std::vector<std::string> pps_list;
|
||||
unsigned char pps_raw[] = {
|
||||
0x44, 0x01, // NALU header: type=34(PPS), layer_id=0, temporal_id=1
|
||||
0xC1, 0x73, 0xD1, 0x89, 0x00 // PPS RBSP data
|
||||
};
|
||||
pps_list.push_back(std::string((char *)pps_raw, sizeof(pps_raw)));
|
||||
|
||||
std::string hvcC_output;
|
||||
|
||||
// This should now succeed with valid HEVC data and cover the configuration record generation
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_sequence_header(vps_data, sps_data, pps_list, hvcC_output));
|
||||
|
||||
// Should produce non-empty HEVCDecoderConfigurationRecord
|
||||
EXPECT_FALSE(hvcC_output.empty());
|
||||
EXPECT_GT(hvcC_output.length(), 23); // Minimum HEVC configuration record size
|
||||
|
||||
// Verify HEVCDecoderConfigurationRecord structure
|
||||
const char *data = hvcC_output.data();
|
||||
EXPECT_EQ(0x01, (unsigned char)data[0]); // configurationVersion = 1
|
||||
|
||||
// Verify the configuration record contains our VPS, SPS, PPS data
|
||||
// The exact structure depends on the implementation, but it should be significantly larger than header
|
||||
EXPECT_GT(hvcC_output.length(), vps_data.length() + sps_data.length() + pps_list[0].length());
|
||||
}
|
||||
|
||||
// Test mux_sequence_header with empty VPS - should fail
|
||||
if (true) {
|
||||
std::string empty_vps;
|
||||
std::string sps_data = std::string("\x42\x01\x01\x01\x60", 5);
|
||||
std::vector<std::string> pps_list;
|
||||
pps_list.push_back(std::string("\x44\x01", 2));
|
||||
std::string hvcC_output;
|
||||
|
||||
srs_error_t mux_err = hevc.mux_sequence_header(empty_vps, sps_data, pps_list, hvcC_output);
|
||||
HELPER_EXPECT_FAILED(mux_err); // Should fail due to empty VPS
|
||||
}
|
||||
|
||||
// Test mux_sequence_header with empty PPS list - should fail
|
||||
if (true) {
|
||||
std::string vps_data = std::string("\x40\x01\x0c", 3);
|
||||
std::string sps_data = std::string("\x42\x01\x01\x01\x60", 5);
|
||||
std::vector<std::string> empty_pps_list;
|
||||
std::string hvcC_output;
|
||||
|
||||
srs_error_t mux_err = hevc.mux_sequence_header(vps_data, sps_data, empty_pps_list, hvcC_output);
|
||||
HELPER_EXPECT_FAILED(mux_err); // Should fail due to empty PPS list
|
||||
}
|
||||
|
||||
// Test mux_sequence_header with multiple PPS
|
||||
if (true) {
|
||||
std::string vps_data = std::string("\x40\x01", 2);
|
||||
std::string sps_data = std::string("\x42\x01\x01\x01", 4);
|
||||
|
||||
std::vector<std::string> pps_list;
|
||||
pps_list.push_back(std::string("\x44\x01", 2));
|
||||
pps_list.push_back(std::string("\x44\x02", 2));
|
||||
pps_list.push_back(std::string("\x44\x03", 2));
|
||||
|
||||
std::string hvcC_output;
|
||||
|
||||
// This may fail due to HEVC validation but shouldn't crash
|
||||
srs_error_t mux_err = hevc.mux_sequence_header(vps_data, sps_data, pps_list, hvcC_output);
|
||||
srs_freep(mux_err); // Don't assert success since HEVC validation is complex
|
||||
|
||||
// Test passed if no crash occurred
|
||||
EXPECT_TRUE(true);
|
||||
}
|
||||
|
||||
// Test mux_sequence_header with different HEVC profile/level to cover more configuration record generation
|
||||
if (true) {
|
||||
// Create VPS with different profile space and tier flag
|
||||
unsigned char vps_raw2[] = {
|
||||
0x40, 0x01, // NALU header: type=32(VPS)
|
||||
0x2C, 0x01, 0xFF, 0xFF, 0x02, 0x20, 0x00, 0x00, // VPS RBSP with different profile_space=1, tier_flag=1
|
||||
0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||
0x03, 0x00, 0x78, 0xAC, 0x09};
|
||||
std::string vps_data2((char *)vps_raw2, sizeof(vps_raw2));
|
||||
|
||||
// Create SPS with different parameters
|
||||
unsigned char sps_raw2[] = {
|
||||
0x42, 0x01, // NALU header: type=33(SPS)
|
||||
0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, // SPS RBSP with different profile_idc
|
||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xA0,
|
||||
0x03, 0x50, 0x80, 0x32, 0x16, 0x59, 0x59, 0xA4,
|
||||
0x93, 0x2B, 0xC0, 0x40, 0x40, 0x40, 0x80};
|
||||
std::string sps_data2((char *)sps_raw2, sizeof(sps_raw2));
|
||||
|
||||
std::vector<std::string> pps_list2;
|
||||
unsigned char pps_raw2[] = {
|
||||
0x44, 0x01, // NALU header: type=34(PPS)
|
||||
0xC2, 0x73, 0xD1, 0x89, 0x10 // PPS RBSP data
|
||||
};
|
||||
pps_list2.push_back(std::string((char *)pps_raw2, sizeof(pps_raw2)));
|
||||
|
||||
std::string hvcC_output2;
|
||||
|
||||
// This should succeed and generate different configuration record
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_sequence_header(vps_data2, sps_data2, pps_list2, hvcC_output2));
|
||||
|
||||
// Should produce non-empty HEVCDecoderConfigurationRecord
|
||||
EXPECT_FALSE(hvcC_output2.empty());
|
||||
EXPECT_GT(hvcC_output2.length(), 23); // Minimum HEVC configuration record size
|
||||
|
||||
// Verify HEVCDecoderConfigurationRecord structure
|
||||
const char *data2 = hvcC_output2.data();
|
||||
EXPECT_EQ(0x01, (unsigned char)data2[0]); // configurationVersion = 1
|
||||
|
||||
// The second byte should contain profile_space, tier_flag, and profile_idc
|
||||
// This tests the bit manipulation code: temp8bits |= ((hevc_info->general_profile_space_ & 0x03) << 6);
|
||||
uint8_t profile_byte = (unsigned char)data2[1];
|
||||
uint8_t profile_space = (profile_byte >> 6) & 0x03;
|
||||
uint8_t tier_flag = (profile_byte >> 5) & 0x01;
|
||||
uint8_t profile_idc = profile_byte & 0x1f;
|
||||
|
||||
// These values should be extracted from the VPS/SPS parsing
|
||||
EXPECT_GE(profile_space, 0);
|
||||
EXPECT_LE(profile_space, 3);
|
||||
EXPECT_GE(tier_flag, 0);
|
||||
EXPECT_LE(tier_flag, 1);
|
||||
EXPECT_GE(profile_idc, 0);
|
||||
EXPECT_LE(profile_idc, 31);
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamMuxIpbFrame)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Test mux_ipb_frame with valid frame data
|
||||
if (true) {
|
||||
// Create test HEVC frame data (IDR slice)
|
||||
unsigned char frame_data[] = {0x26, 0x01, 0xaf, 0x06, 0xb8, 0x63, 0xef, 0x3a, 0x7f, 0x3c, 0x00, 0x01, 0x00, 0x80};
|
||||
std::string ibp_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)frame_data, sizeof(frame_data), ibp_output));
|
||||
|
||||
// Should produce frame with 4-byte length prefix + frame data
|
||||
EXPECT_EQ(4 + sizeof(frame_data), ibp_output.length());
|
||||
|
||||
// Check length prefix (big-endian)
|
||||
const char *data = ibp_output.data();
|
||||
uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3];
|
||||
EXPECT_EQ(sizeof(frame_data), length);
|
||||
|
||||
// Check frame data follows length prefix
|
||||
EXPECT_EQ(0, memcmp(frame_data, data + 4, sizeof(frame_data)));
|
||||
}
|
||||
|
||||
// Test mux_ipb_frame with empty frame - should still work
|
||||
if (true) {
|
||||
unsigned char empty_frame[] = {};
|
||||
std::string ibp_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)empty_frame, 0, ibp_output));
|
||||
|
||||
// Should produce only 4-byte length prefix with zero length
|
||||
EXPECT_EQ(4, ibp_output.length());
|
||||
|
||||
const char *data = ibp_output.data();
|
||||
uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3];
|
||||
EXPECT_EQ(0, length);
|
||||
}
|
||||
|
||||
// Test mux_ipb_frame with large frame
|
||||
if (true) {
|
||||
unsigned char large_frame[1000];
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
large_frame[i] = i % 256;
|
||||
}
|
||||
std::string ibp_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)large_frame, sizeof(large_frame), ibp_output));
|
||||
|
||||
// Should produce frame with 4-byte length prefix + frame data
|
||||
EXPECT_EQ(4 + sizeof(large_frame), ibp_output.length());
|
||||
|
||||
// Check length prefix
|
||||
const char *data = ibp_output.data();
|
||||
uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3];
|
||||
EXPECT_EQ(sizeof(large_frame), length);
|
||||
}
|
||||
|
||||
// Test mux_ipb_frame with different HEVC NALU types
|
||||
if (true) {
|
||||
// Test with P-frame (TRAIL_R NALU type 1)
|
||||
unsigned char p_frame[] = {0x02, 0x01, 0x50, 0x80, 0x12, 0x34, 0x56, 0x78};
|
||||
std::string ibp_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)p_frame, sizeof(p_frame), ibp_output));
|
||||
|
||||
// Verify output format
|
||||
EXPECT_EQ(4 + sizeof(p_frame), ibp_output.length());
|
||||
const char *data = ibp_output.data();
|
||||
uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3];
|
||||
EXPECT_EQ(sizeof(p_frame), length);
|
||||
EXPECT_EQ(0, memcmp(p_frame, data + 4, sizeof(p_frame)));
|
||||
}
|
||||
|
||||
// Test mux_ipb_frame with B-frame (TSA_N NALU type 2)
|
||||
if (true) {
|
||||
unsigned char b_frame[] = {0x04, 0x01, 0x60, 0x90, 0xab, 0xcd, 0xef};
|
||||
std::string ibp_output;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)b_frame, sizeof(b_frame), ibp_output));
|
||||
|
||||
// Verify output format
|
||||
EXPECT_EQ(4 + sizeof(b_frame), ibp_output.length());
|
||||
const char *data = ibp_output.data();
|
||||
uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3];
|
||||
EXPECT_EQ(sizeof(b_frame), length);
|
||||
EXPECT_EQ(0, memcmp(b_frame, data + 4, sizeof(b_frame)));
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamMuxAvc2flv)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Test mux_avc2flv with sequence header
|
||||
if (true) {
|
||||
std::string video_data = std::string("\x01\x64\x00\x20\xff\xe1\x00\x19\x67\x64\x00\x20", 12);
|
||||
int8_t frame_type = 1; // keyframe
|
||||
int8_t avc_packet_type = 0; // sequence header
|
||||
uint32_t dts = 1000;
|
||||
uint32_t pts = 1000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
// Should produce FLV packet with 5-byte header + video data
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
EXPECT_EQ(5 + video_data.length(), nb_flv);
|
||||
|
||||
if (flv_data) {
|
||||
// Check FLV header format
|
||||
EXPECT_EQ((frame_type << 4) | 12, (uint8_t)flv_data[0]); // frame_type | SrsVideoCodecIdHEVC(12)
|
||||
EXPECT_EQ(avc_packet_type, flv_data[1]); // AVCPacketType
|
||||
|
||||
// Check composition time (CTS = PTS - DTS = 0)
|
||||
uint32_t cts = ((unsigned char)flv_data[2] << 16) | ((unsigned char)flv_data[3] << 8) | (unsigned char)flv_data[4];
|
||||
EXPECT_EQ(0, cts);
|
||||
|
||||
// Check video data follows header
|
||||
EXPECT_EQ(0, memcmp(video_data.data(), flv_data + 5, video_data.length()));
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv with NALU frame
|
||||
if (true) {
|
||||
std::string video_data = std::string("\x00\x00\x00\x0e\x26\x01\xaf\x06\xb8\x63\xef\x3a\x7f\x3c\x00\x01\x00\x80", 18);
|
||||
int8_t frame_type = 1; // keyframe
|
||||
int8_t avc_packet_type = 1; // NALU
|
||||
uint32_t dts = 2000;
|
||||
uint32_t pts = 2100; // PTS > DTS
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
EXPECT_EQ(5 + video_data.length(), nb_flv);
|
||||
|
||||
if (flv_data) {
|
||||
// Check composition time (CTS = PTS - DTS = 100)
|
||||
uint32_t cts = ((unsigned char)flv_data[2] << 16) | ((unsigned char)flv_data[3] << 8) | (unsigned char)flv_data[4];
|
||||
EXPECT_EQ(100, cts);
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv with inter frame
|
||||
if (true) {
|
||||
std::string video_data = std::string("\x00\x00\x00\x08\x02\x01\xd0\x80\x93\x25\x88\x84", 12);
|
||||
int8_t frame_type = 2; // inter frame
|
||||
int8_t avc_packet_type = 1; // NALU
|
||||
uint32_t dts = 3000;
|
||||
uint32_t pts = 3000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
if (flv_data) {
|
||||
// Check frame type in FLV header
|
||||
EXPECT_EQ((frame_type << 4) | 12, (uint8_t)flv_data[0]); // inter frame | HEVC codec
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv with empty video data
|
||||
if (true) {
|
||||
std::string empty_video_data;
|
||||
int8_t frame_type = 1; // keyframe
|
||||
int8_t avc_packet_type = 0; // sequence header
|
||||
uint32_t dts = 4000;
|
||||
uint32_t pts = 4000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(empty_video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
// Should produce FLV packet with 5-byte header only
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
EXPECT_EQ(5, nb_flv);
|
||||
|
||||
if (flv_data) {
|
||||
// Check FLV header format
|
||||
EXPECT_EQ((frame_type << 4) | 12, (uint8_t)flv_data[0]); // frame_type | SrsVideoCodecIdHEVC(12)
|
||||
EXPECT_EQ(avc_packet_type, flv_data[1]); // AVCPacketType
|
||||
|
||||
// Check composition time (CTS = PTS - DTS = 0)
|
||||
uint32_t cts = ((unsigned char)flv_data[2] << 16) | ((unsigned char)flv_data[3] << 8) | (unsigned char)flv_data[4];
|
||||
EXPECT_EQ(0, cts);
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv with large composition time offset
|
||||
if (true) {
|
||||
std::string video_data = std::string("\x00\x00\x00\x04\x26\x01\xaf\x06", 8);
|
||||
int8_t frame_type = 1; // keyframe
|
||||
int8_t avc_packet_type = 1; // NALU
|
||||
uint32_t dts = 5000;
|
||||
uint32_t pts = 10000; // Large PTS offset
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
if (flv_data) {
|
||||
// Check composition time (CTS = PTS - DTS = 5000)
|
||||
uint32_t cts = ((unsigned char)flv_data[2] << 16) | ((unsigned char)flv_data[3] << 8) | (unsigned char)flv_data[4];
|
||||
EXPECT_EQ(5000, cts);
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamMuxAvc2flvEnhanced)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Test mux_avc2flv_enhanced with sequence header
|
||||
if (true) {
|
||||
std::string video_data = std::string("\x01\x64\x00\x20\xff\xe1\x00\x19\x67\x64\x00\x20", 12);
|
||||
int8_t frame_type = 1; // keyframe
|
||||
int8_t packet_type = 0; // sequence start
|
||||
uint32_t dts = 1000;
|
||||
uint32_t pts = 1000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
// Should produce enhanced FLV packet with 5-byte header + video data
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
EXPECT_EQ(5 + video_data.length(), nb_flv);
|
||||
|
||||
if (flv_data) {
|
||||
// Check enhanced FLV header format
|
||||
uint8_t header_byte = flv_data[0];
|
||||
EXPECT_TRUE((header_byte & 0x80) != 0); // SRS_FLV_IS_EX_HEADER bit set
|
||||
EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // frame type (3 bits, mask out EX_HEADER bit)
|
||||
EXPECT_EQ(packet_type, header_byte & 0x0f); // packet type
|
||||
|
||||
// Check HEVC fourcc 'hvc1'
|
||||
EXPECT_EQ('h', flv_data[1]);
|
||||
EXPECT_EQ('v', flv_data[2]);
|
||||
EXPECT_EQ('c', flv_data[3]);
|
||||
EXPECT_EQ('1', flv_data[4]);
|
||||
|
||||
// Check video data follows header
|
||||
EXPECT_EQ(0, memcmp(video_data.data(), flv_data + 5, video_data.length()));
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv_enhanced with coded frames
|
||||
if (true) {
|
||||
std::string video_data = std::string("\x00\x00\x00\x0e\x26\x01\xaf\x06\xb8\x63\xef\x3a\x7f\x3c\x00\x01\x00\x80", 18);
|
||||
int8_t frame_type = 1; // keyframe
|
||||
int8_t packet_type = 1; // coded frames
|
||||
uint32_t dts = 2000;
|
||||
uint32_t pts = 2000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
EXPECT_EQ(5 + video_data.length(), nb_flv);
|
||||
|
||||
if (flv_data) {
|
||||
// Check enhanced header with coded frames packet type
|
||||
uint8_t header_byte = flv_data[0];
|
||||
EXPECT_EQ(packet_type, header_byte & 0x0f); // coded frames packet type
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv_enhanced with inter frame
|
||||
if (true) {
|
||||
std::string video_data = std::string("\x00\x00\x00\x08\x02\x01\xd0\x80\x93\x25\x88\x84", 12);
|
||||
int8_t frame_type = 2; // inter frame
|
||||
int8_t packet_type = 1; // coded frames
|
||||
uint32_t dts = 3000;
|
||||
uint32_t pts = 3000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
if (flv_data) {
|
||||
// Check inter frame type in enhanced header
|
||||
uint8_t header_byte = flv_data[0];
|
||||
EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // inter frame type (3 bits, mask out EX_HEADER bit)
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv_enhanced with sequence end packet
|
||||
if (true) {
|
||||
std::string video_data; // Empty for sequence end
|
||||
int8_t frame_type = 1; // keyframe
|
||||
int8_t packet_type = 2; // sequence end
|
||||
uint32_t dts = 4000;
|
||||
uint32_t pts = 4000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
EXPECT_EQ(5, nb_flv); // Should produce 5-byte header only
|
||||
|
||||
if (flv_data) {
|
||||
// Check enhanced FLV header format for sequence end
|
||||
uint8_t header_byte = flv_data[0];
|
||||
EXPECT_TRUE((header_byte & 0x80) != 0); // SRS_FLV_IS_EX_HEADER bit set
|
||||
EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // frame type
|
||||
EXPECT_EQ(packet_type, header_byte & 0x0f); // sequence end packet type
|
||||
|
||||
// Check HEVC fourcc 'hvc1'
|
||||
EXPECT_EQ('h', flv_data[1]);
|
||||
EXPECT_EQ('v', flv_data[2]);
|
||||
EXPECT_EQ('c', flv_data[3]);
|
||||
EXPECT_EQ('1', flv_data[4]);
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv_enhanced with different frame types
|
||||
if (true) {
|
||||
std::string video_data = std::string("\x00\x00\x00\x06\x04\x01\x70\x80\x12\x34", 10);
|
||||
int8_t frame_type = 3; // disposable inter frame
|
||||
int8_t packet_type = 1; // coded frames
|
||||
uint32_t dts = 5000;
|
||||
uint32_t pts = 5000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
if (flv_data) {
|
||||
// Check disposable inter frame type in enhanced header
|
||||
uint8_t header_byte = flv_data[0];
|
||||
EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // disposable inter frame type
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Test mux_avc2flv_enhanced with empty video data
|
||||
if (true) {
|
||||
std::string empty_video_data;
|
||||
int8_t frame_type = 1; // keyframe
|
||||
int8_t packet_type = 0; // sequence start
|
||||
uint32_t dts = 6000;
|
||||
uint32_t pts = 6000;
|
||||
char *flv_data = NULL;
|
||||
int nb_flv = 0;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(empty_video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv));
|
||||
|
||||
// Should produce enhanced FLV packet with 5-byte header only
|
||||
EXPECT_TRUE(flv_data != NULL);
|
||||
EXPECT_EQ(5, nb_flv);
|
||||
|
||||
if (flv_data) {
|
||||
// Check enhanced FLV header format
|
||||
uint8_t header_byte = flv_data[0];
|
||||
EXPECT_TRUE((header_byte & 0x80) != 0); // SRS_FLV_IS_EX_HEADER bit set
|
||||
EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // frame type
|
||||
EXPECT_EQ(packet_type, header_byte & 0x0f); // packet type
|
||||
|
||||
// Check HEVC fourcc 'hvc1'
|
||||
EXPECT_EQ('h', flv_data[1]);
|
||||
EXPECT_EQ('v', flv_data[2]);
|
||||
EXPECT_EQ('c', flv_data[3]);
|
||||
EXPECT_EQ('1', flv_data[4]);
|
||||
|
||||
delete[] flv_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolStreamTest, SrsFastStreamBasic)
|
||||
|
|
@ -456,25 +1104,6 @@ VOID TEST(ProtocolLogTest, SrsThreadContextBasic)
|
|||
EXPECT_EQ(0, new_id.compare(set_result));
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolLogTest, SrsConsoleLogBasic)
|
||||
{
|
||||
// SrsConsoleLog requires parameters: level and utc flag
|
||||
SrsConsoleLog console_log(SrsLogLevelTrace, false);
|
||||
|
||||
// Test basic functionality - should not crash
|
||||
// We can't easily test actual logging without capturing output
|
||||
|
||||
// Test initialization
|
||||
srs_error_t err = console_log.initialize();
|
||||
HELPER_EXPECT_SUCCESS(err);
|
||||
|
||||
// Test reopen - should not crash
|
||||
console_log.reopen();
|
||||
|
||||
// The console log should be constructible and destructible without issues
|
||||
EXPECT_TRUE(true); // Just verify we can create and destroy the object
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRtmpConnTest, SrsBasicRtmpClientBasic)
|
||||
{
|
||||
// Test basic RTMP client construction
|
||||
|
|
@ -508,37 +1137,6 @@ VOID TEST(ProtocolStTest, SrsStSocketBasic)
|
|||
EXPECT_FALSE(is_not_never_timeout);
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolConnTest, SrsSslConnectionInterface)
|
||||
{
|
||||
// Test SSL connection interface
|
||||
// We can't easily test full SSL functionality without certificates
|
||||
|
||||
// Create a TCP connection first (with invalid fd for testing)
|
||||
SrsTcpConnection *tcp = new SrsTcpConnection(NULL);
|
||||
|
||||
// Create SSL connection wrapper
|
||||
SrsSslConnection *ssl = new SrsSslConnection(tcp);
|
||||
|
||||
// The SSL connection should be created
|
||||
EXPECT_TRUE(ssl != NULL);
|
||||
|
||||
// Test timeout methods exist
|
||||
ssl->set_recv_timeout(1000 * SRS_UTIME_MILLISECONDS);
|
||||
srs_utime_t timeout = ssl->get_recv_timeout();
|
||||
EXPECT_EQ(1000 * SRS_UTIME_MILLISECONDS, timeout);
|
||||
|
||||
ssl->set_send_timeout(2000 * SRS_UTIME_MILLISECONDS);
|
||||
srs_utime_t send_timeout = ssl->get_send_timeout();
|
||||
EXPECT_EQ(2000 * SRS_UTIME_MILLISECONDS, send_timeout);
|
||||
|
||||
// Test byte counters
|
||||
EXPECT_EQ(0, ssl->get_recv_bytes());
|
||||
EXPECT_EQ(0, ssl->get_send_bytes());
|
||||
|
||||
// Clean up
|
||||
delete ssl; // This will also delete the tcp connection
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRtpTest, SrsRtpVideoBuilderBasic)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
|
@ -827,71 +1425,6 @@ VOID TEST(ProtocolRtmpConnTest, SrsBasicRtmpClientOperations)
|
|||
// integration tests with actual RTMP server connections.
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolHttpClientTest, SrsHttpClientInitialization)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsHttpClient client;
|
||||
|
||||
// Test initialization with HTTP
|
||||
HELPER_EXPECT_SUCCESS(client.initialize("http", "127.0.0.1", 8080, 5000 * SRS_UTIME_MILLISECONDS));
|
||||
|
||||
// Test initialization with HTTPS
|
||||
HELPER_EXPECT_SUCCESS(client.initialize("https", "example.com", 443, 10000 * SRS_UTIME_MILLISECONDS));
|
||||
|
||||
// Test header setting and chaining
|
||||
SrsHttpClient *result1 = client.set_header("User-Agent", "SRS-Test/1.0");
|
||||
EXPECT_TRUE(result1 != NULL);
|
||||
EXPECT_EQ(&client, result1); // Should return self for chaining
|
||||
|
||||
SrsHttpClient *result2 = client.set_header("Accept", "application/json");
|
||||
EXPECT_TRUE(result2 != NULL);
|
||||
EXPECT_EQ(&client, result2);
|
||||
|
||||
// Test multiple header settings
|
||||
client.set_header("Content-Type", "application/json");
|
||||
client.set_header("Authorization", "Bearer token123");
|
||||
client.set_header("X-Custom-Header", "custom-value");
|
||||
|
||||
// Test timeout setting
|
||||
client.set_recv_timeout(3000 * SRS_UTIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolHttpClientTest, SrsHttpClientRequests)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsHttpClient client;
|
||||
HELPER_EXPECT_SUCCESS(client.initialize("http", "127.0.0.1", 8080, 1000 * SRS_UTIME_MILLISECONDS));
|
||||
|
||||
// Set headers for testing
|
||||
client.set_header("User-Agent", "SRS-UTest");
|
||||
client.set_header("Accept", "application/json");
|
||||
|
||||
// Test GET request - will fail without server but shouldn't crash
|
||||
ISrsHttpMessage *get_msg = NULL;
|
||||
srs_error_t get_err = client.get("/api/test", "", &get_msg);
|
||||
srs_freep(get_err); // Expected to fail without server
|
||||
EXPECT_TRUE(get_msg == NULL);
|
||||
|
||||
// Test POST request - will fail without server but shouldn't crash
|
||||
ISrsHttpMessage *post_msg = NULL;
|
||||
std::string post_data = "{\"test\":\"data\"}";
|
||||
srs_error_t post_err = client.post("/api/submit", post_data, &post_msg);
|
||||
srs_freep(post_err); // Expected to fail without server
|
||||
EXPECT_TRUE(post_msg == NULL);
|
||||
|
||||
// Test requests with different paths
|
||||
ISrsHttpMessage *root_msg = NULL;
|
||||
srs_error_t get_root_err = client.get("/", "", &root_msg);
|
||||
srs_freep(get_root_err);
|
||||
EXPECT_TRUE(root_msg == NULL);
|
||||
|
||||
srs_error_t post_empty_err = client.post("/empty", "", &post_msg);
|
||||
srs_freep(post_empty_err);
|
||||
EXPECT_TRUE(post_msg == NULL);
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRtcStunTest, SrsCrc32IeeeBasic)
|
||||
{
|
||||
// Test CRC32 IEEE calculation with known values
|
||||
|
|
@ -2095,3 +2628,137 @@ VOID TEST(ProtocolSdpTest, SrsSdpUpdateMsid)
|
|||
EXPECT_STREQ("new_stream_id", audio_descs[0]->ssrc_infos_[0].msid_.c_str());
|
||||
EXPECT_STREQ("new_stream_id", audio_descs[0]->ssrc_infos_[0].mslabel_.c_str());
|
||||
}
|
||||
|
||||
VOID TEST(RawHEVCStreamTest, VpsDemux)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Test vps_demux method with valid VPS data
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Create mock VPS NALU data (HEVC VPS type 32)
|
||||
char vps_data[] = {
|
||||
(char)((32 << 1) | 0), // HEVC VPS NALU type 32, layer_id=0
|
||||
(char)1, // layer_id bits + temporal_id=1
|
||||
0x01, 0x02, 0x03, 0x04 // Mock VPS payload
|
||||
};
|
||||
int vps_size = sizeof(vps_data);
|
||||
|
||||
string vps_output;
|
||||
HELPER_ASSERT_SUCCESS(hevc.vps_demux(vps_data, vps_size, vps_output));
|
||||
|
||||
// Verify VPS output matches input
|
||||
EXPECT_EQ(vps_size, (int)vps_output.length());
|
||||
EXPECT_EQ(0, memcmp(vps_data, vps_output.data(), vps_size));
|
||||
}
|
||||
|
||||
// Test vps_demux with empty frame (should fail)
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
string vps_output;
|
||||
HELPER_EXPECT_FAILED(hevc.vps_demux(NULL, 0, vps_output));
|
||||
}
|
||||
|
||||
// Test vps_demux with negative frame size (should fail)
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
char dummy_data[] = {0x01, 0x02};
|
||||
string vps_output;
|
||||
HELPER_EXPECT_FAILED(hevc.vps_demux(dummy_data, -1, vps_output));
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(RawHEVCStreamTest, SpsDemux)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Test sps_demux method with valid SPS data
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Create mock SPS NALU data (HEVC SPS type 33)
|
||||
char sps_data[] = {
|
||||
(char)((33 << 1) | 0), // HEVC SPS NALU type 33, layer_id=0
|
||||
(char)1, // layer_id bits + temporal_id=1
|
||||
0x10, 0x20, 0x30, 0x40, 0x50 // Mock SPS payload (>= 4 bytes total)
|
||||
};
|
||||
int sps_size = sizeof(sps_data);
|
||||
|
||||
string sps_output;
|
||||
HELPER_ASSERT_SUCCESS(hevc.sps_demux(sps_data, sps_size, sps_output));
|
||||
|
||||
// Verify SPS output matches input
|
||||
EXPECT_EQ(sps_size, (int)sps_output.length());
|
||||
EXPECT_EQ(0, memcmp(sps_data, sps_output.data(), sps_size));
|
||||
}
|
||||
|
||||
// Test sps_demux with frame size < 4 (should succeed but return empty)
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
char small_data[] = {0x01, 0x02}; // Only 2 bytes
|
||||
string sps_output;
|
||||
HELPER_ASSERT_SUCCESS(hevc.sps_demux(small_data, 2, sps_output));
|
||||
|
||||
// Should return empty string for frames < 4 bytes
|
||||
EXPECT_EQ(0, (int)sps_output.length());
|
||||
}
|
||||
|
||||
// Test sps_demux with exactly 4 bytes
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
char sps_data[] = {0x42, 0x01, 0x01, 0x01}; // Exactly 4 bytes
|
||||
string sps_output;
|
||||
HELPER_ASSERT_SUCCESS(hevc.sps_demux(sps_data, 4, sps_output));
|
||||
|
||||
// Should return the 4 bytes
|
||||
EXPECT_EQ(4, (int)sps_output.length());
|
||||
EXPECT_EQ(0, memcmp(sps_data, sps_output.data(), 4));
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(RawHEVCStreamTest, PpsDemux)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Test pps_demux method with valid PPS data
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
// Create mock PPS NALU data (HEVC PPS type 34)
|
||||
unsigned char pps_data[] = {
|
||||
(unsigned char)((34 << 1) | 0), // HEVC PPS NALU type 34, layer_id=0
|
||||
(unsigned char)1, // layer_id bits + temporal_id=1
|
||||
0xA0, 0xB0, 0xC0 // Mock PPS payload
|
||||
};
|
||||
int pps_size = sizeof(pps_data);
|
||||
|
||||
string pps_output;
|
||||
HELPER_ASSERT_SUCCESS(hevc.pps_demux((char *)pps_data, pps_size, pps_output));
|
||||
|
||||
// Verify PPS output matches input
|
||||
EXPECT_EQ(pps_size, (int)pps_output.length());
|
||||
EXPECT_EQ(0, memcmp(pps_data, pps_output.data(), pps_size));
|
||||
}
|
||||
|
||||
// Test pps_demux with empty frame (should fail)
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
string pps_output;
|
||||
HELPER_EXPECT_FAILED(hevc.pps_demux(NULL, 0, pps_output));
|
||||
}
|
||||
|
||||
// Test pps_demux with negative frame size (should fail)
|
||||
if (true) {
|
||||
SrsRawHEVCStream hevc;
|
||||
|
||||
char dummy_data[] = {0x01, 0x02};
|
||||
string pps_output;
|
||||
HELPER_EXPECT_FAILED(hevc.pps_demux(dummy_data, -1, pps_output));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2416
trunk/src/utest/srs_utest_protocol4.cpp
Normal file
2416
trunk/src/utest/srs_utest_protocol4.cpp
Normal file
File diff suppressed because it is too large
Load Diff
15
trunk/src/utest/srs_utest_protocol4.hpp
Normal file
15
trunk/src/utest/srs_utest_protocol4.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Copyright (c) 2013-2025 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
|
||||
#ifndef SRS_UTEST_PROTOCOL4_HPP
|
||||
#define SRS_UTEST_PROTOCOL4_HPP
|
||||
|
||||
/*
|
||||
#include <srs_utest_protocol4.hpp>
|
||||
*/
|
||||
#include <srs_utest.hpp>
|
||||
|
||||
#endif
|
||||
|
|
@ -7,9 +7,11 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_app_http_conn.hpp>
|
||||
#include <srs_app_listener.hpp>
|
||||
#include <srs_core_deprecated.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_protocol_http_stack.hpp>
|
||||
#include <srs_protocol_st.hpp>
|
||||
#include <srs_protocol_utility.hpp>
|
||||
|
||||
|
|
@ -1332,150 +1334,6 @@ VOID TEST(TCPServerTest, CoverUtility)
|
|||
}
|
||||
}
|
||||
|
||||
class MockOnCycleThread4 : public ISrsCoroutineHandler
|
||||
{
|
||||
public:
|
||||
SrsSTCoroutine trd;
|
||||
srs_netfd_t fd;
|
||||
MockOnCycleThread4() : trd("mock", this)
|
||||
{
|
||||
fd = NULL;
|
||||
};
|
||||
virtual ~MockOnCycleThread4()
|
||||
{
|
||||
trd.stop();
|
||||
srs_close_stfd(fd);
|
||||
}
|
||||
virtual srs_error_t start(string ip, int port)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
if ((err = srs_tcp_listen(ip, port, &fd)) != srs_success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return trd.start();
|
||||
}
|
||||
virtual srs_error_t do_cycle(srs_netfd_t cfd)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsStSocket skt(cfd);
|
||||
skt.set_recv_timeout(1 * SRS_UTIME_SECONDS);
|
||||
skt.set_send_timeout(1 * SRS_UTIME_SECONDS);
|
||||
|
||||
while (true) {
|
||||
if ((err = trd.pull()) != srs_success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
if ((err = skt.read(buf, 1024, NULL)) != srs_success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
string res = mock_http_response(200, "OK");
|
||||
if ((err = skt.write((char *)res.data(), (int)res.length(), NULL)) != srs_success) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
virtual srs_error_t cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
srs_netfd_t cfd = srs_accept(fd, NULL, NULL, SRS_UTIME_NO_TIMEOUT);
|
||||
if (cfd == NULL) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = do_cycle(cfd);
|
||||
srs_close_stfd(cfd);
|
||||
srs_freep(err);
|
||||
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
VOID TEST(HTTPClientTest, HTTPClientUtility)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Typical HTTP POST.
|
||||
if (true) {
|
||||
MockOnCycleThread4 trd;
|
||||
HELPER_ASSERT_SUCCESS(trd.start("127.0.0.1", 8080));
|
||||
|
||||
SrsHttpClient client;
|
||||
HELPER_ASSERT_SUCCESS(client.initialize("http", "127.0.0.1", 8080, 1 * SRS_UTIME_SECONDS));
|
||||
|
||||
ISrsHttpMessage *res = NULL;
|
||||
HELPER_ASSERT_SUCCESS(client.post("/api/v1", "", &res));
|
||||
SrsUniquePtr<ISrsHttpMessage> res_uptr(res);
|
||||
|
||||
ISrsHttpResponseReader *br = res->body_reader();
|
||||
ASSERT_FALSE(br->eof());
|
||||
|
||||
ssize_t nn = 0;
|
||||
char buf[1024];
|
||||
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
|
||||
HELPER_ASSERT_SUCCESS(br->read(buf, sizeof(buf), &nn));
|
||||
ASSERT_EQ(2, nn);
|
||||
EXPECT_STREQ("OK", buf);
|
||||
}
|
||||
|
||||
// Typical HTTP GET.
|
||||
if (true) {
|
||||
MockOnCycleThread4 trd;
|
||||
HELPER_ASSERT_SUCCESS(trd.start("127.0.0.1", 8080));
|
||||
|
||||
SrsHttpClient client;
|
||||
HELPER_ASSERT_SUCCESS(client.initialize("http", "127.0.0.1", 8080, 1 * SRS_UTIME_SECONDS));
|
||||
|
||||
ISrsHttpMessage *res = NULL;
|
||||
HELPER_ASSERT_SUCCESS(client.get("/api/v1", "", &res));
|
||||
SrsUniquePtr<ISrsHttpMessage> res_uptr(res);
|
||||
|
||||
ISrsHttpResponseReader *br = res->body_reader();
|
||||
ASSERT_FALSE(br->eof());
|
||||
|
||||
ssize_t nn = 0;
|
||||
char buf[1024];
|
||||
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
|
||||
HELPER_ASSERT_SUCCESS(br->read(buf, sizeof(buf), &nn));
|
||||
ASSERT_EQ(2, nn);
|
||||
EXPECT_STREQ("OK", buf);
|
||||
}
|
||||
|
||||
// Set receive timeout and Kbps ample.
|
||||
if (true) {
|
||||
MockOnCycleThread4 trd;
|
||||
HELPER_ASSERT_SUCCESS(trd.start("127.0.0.1", 8080));
|
||||
|
||||
SrsHttpClient client;
|
||||
HELPER_ASSERT_SUCCESS(client.initialize("http", "127.0.0.1", 8080, 1 * SRS_UTIME_SECONDS));
|
||||
client.set_recv_timeout(1 * SRS_UTIME_SECONDS);
|
||||
client.set_header("agent", "srs");
|
||||
|
||||
ISrsHttpMessage *res = NULL;
|
||||
HELPER_ASSERT_SUCCESS(client.get("/api/v1", "", &res));
|
||||
SrsUniquePtr<ISrsHttpMessage> res_uptr(res);
|
||||
|
||||
ISrsHttpResponseReader *br = res->body_reader();
|
||||
ASSERT_FALSE(br->eof());
|
||||
|
||||
ssize_t nn = 0;
|
||||
char buf[1024];
|
||||
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
|
||||
HELPER_ASSERT_SUCCESS(br->read(buf, sizeof(buf), &nn));
|
||||
ASSERT_EQ(2, nn);
|
||||
EXPECT_STREQ("OK", buf);
|
||||
|
||||
client.kbps_sample("SRS", 0);
|
||||
}
|
||||
}
|
||||
|
||||
class MockConnectionManager : public ISrsResourceManager
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -45,6 +45,34 @@ VOID TEST(StTest, AnonymouseMultipleCoroutines)
|
|||
srs_usleep(50 * SRS_UTIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
VOID TEST(StTest, AnonymouseCoroutinePull)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
if (true) {
|
||||
SrsCoroutineChan ctx;
|
||||
ctx.push(&counter);
|
||||
|
||||
SRS_COROUTINE_GO_CTX(&ctx, {
|
||||
int *counter = (int *)ctx.pop();
|
||||
srs_assert(ctx.trd_);
|
||||
while (ctx.trd_->pull() == srs_success) {
|
||||
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
||||
}
|
||||
(*counter)++;
|
||||
});
|
||||
|
||||
// Coroutine not terminated, so the counter is not increased.
|
||||
EXPECT_TRUE(counter == 0);
|
||||
|
||||
// Wait for coroutine to run and terminated, or it will crash
|
||||
// because the ctx.pop is called after coroutine terminated.
|
||||
srs_usleep(50 * SRS_UTIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(counter == 1);
|
||||
}
|
||||
|
||||
VOID TEST(StTest, AnonymouseCoroutineWithContext)
|
||||
{
|
||||
int counter = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user