1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

GB28181: Support GB28181-2016 protocol. v5.0.74 (#3201)

01. Support GB config as StreamCaster.
02. Support disable GB by --gb28181=off.
03. Add utests for SIP examples.
04. Wireshark plugin to decode TCP/9000 as rtp.rfc4571
05. Support MPEGPS program stream codec.
06. Add utest for PS stream codec.
07. Decode MPEGPS packet stream.
08. Carry RTP and PS packet as helper in PS message.
09. Support recover from error mode.
10. Support process by a pack of PS/TS messages.
11. Add statistic for recovered and msgs dropped.
12. Recover from err position fastly.
13. Define state machine for GB session.
14. Bind context to GB session.
15. Re-invite when media disconnected.
16. Update GitHub actions with GB28181.
17. Support parse CANDIDATE by env or pip.
18. Support mux GB28181 to RTMP.
19. Support regression test by srs-bench.
This commit is contained in:
Winlin 2022-10-06 17:40:58 +08:00 committed by GitHub
parent 9c81a0e1bd
commit 5a420ece3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
298 changed files with 43343 additions and 763 deletions

View file

@ -266,6 +266,11 @@ bool srs_stream_caster_is_flv(string caster)
return caster == "flv";
}
bool srs_stream_caster_is_gb28181(string caster)
{
return caster == "gb28181";
}
bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req)
{
static bool DEFAULT = true;
@ -325,7 +330,61 @@ srs_error_t srs_config_transform_vhost(SrsConfDirective* root)
++it;
}
}
// SRS5.0, GB28181 allows unused config.
// stream_caster {
// caster gb28181; tcp_enable; rtp_port_min; rtp_port_max; wait_keyframe; rtp_idle_timeout;
// audio_enable; auto_create_channel;
// sip {
// serial; realm; ack_timeout; keepalive_timeout; invite_port_fixed; query_catalog_interval; auto_play;
// }
// }
if (dir->name == "stream_caster") {
for (vector<SrsConfDirective*>::iterator it = dir->directives.begin(); it != dir->directives.end();) {
SrsConfDirective* conf = *it;
if (conf->name == "tcp_enable" || conf->name == "rtp_port_min" || conf->name == "rtp_port_max"
|| conf->name == "wait_keyframe" || conf->name == "rtp_idle_timeout" || conf->name == "audio_enable"
|| conf->name == "auto_create_channel"
) {
srs_warn("transform: Config %s for GB is not used", conf->name.c_str());
it = dir->directives.erase(it); srs_freep(conf); continue;
}
++it;
}
SrsConfDirective* sip = dir->get("sip");
if (sip) {
for (vector<SrsConfDirective*>::iterator it = sip->directives.begin(); it != sip->directives.end();) {
SrsConfDirective* conf = *it;
if (conf->name == "serial" || conf->name == "realm" || conf->name == "ack_timeout"
|| conf->name == "keepalive_timeout" || conf->name == "invite_port_fixed"
|| conf->name == "query_catalog_interval" || conf->name == "auto_play"
) {
srs_warn("transform: Config sip.%s for GB is not used", conf->name.c_str());
it = sip->directives.erase(it); srs_freep(conf); continue;
}
++it;
}
}
}
// SRS 5.0, GB28181 moves config from:
// stream_caster { caster gb28181; host * }
// to:
// stream_caster { caster gb28181; sip { candidate *; } }
if (dir->name == "stream_caster") {
for (vector<SrsConfDirective*>::iterator it = dir->directives.begin(); it != dir->directives.end();) {
SrsConfDirective* conf = *it;
if (conf->name == "host") {
srs_warn("transform: Config move host to sip.candidate for GB");
conf->name = "candidate"; dir->get_or_create("sip")->directives.push_back(conf->copy());
it = dir->directives.erase(it); srs_freep(conf); continue;
}
++it;
}
}
// The bellow is vhost scope configurations.
if (!dir->is_vhost()) {
continue;
}
@ -2437,21 +2496,15 @@ srs_error_t SrsConfig::check_normal_config()
for (int i = 0; stream_caster && i < (int)stream_caster->directives.size(); i++) {
SrsConfDirective* conf = stream_caster->at(i);
string n = conf->name;
if (n != "enabled" && n != "caster" && n != "output"
&& n != "listen" && n != "tcp_enable" && n != "rtp_port_min" && n != "rtp_port_max"
&& n != "rtp_idle_timeout" && n != "sip"
&& n != "audio_enable" && n != "wait_keyframe" && n != "jitterbuffer_enable"
&& n != "host" && n != "auto_create_channel") {
if (n != "enabled" && n != "caster" && n != "output" && n != "listen" && n != "sip") {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", n.c_str());
}
if (n == "sip") {
for (int j = 0; j < (int)conf->directives.size(); j++) {
string m = conf->at(j)->name;
if (m != "enabled" && m != "listen" && m != "ack_timeout" && m != "keepalive_timeout"
&& m != "host" && m != "serial" && m != "realm" && m != "auto_play" && m != "invite_port_fixed"
&& m != "query_catalog_interval") {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", m.c_str());
if (m != "enabled" && m != "listen" && m != "timeout" && m != "reinvite" && m != "candidate") {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.sip.%s", m.c_str());
}
}
}
@ -3789,54 +3842,116 @@ int SrsConfig::get_stream_caster_listen(SrsConfDirective* conf)
return ::atoi(conf->arg0().c_str());
}
bool SrsConfig::get_stream_caster_tcp_enable(SrsConfDirective* conf)
bool SrsConfig::get_stream_caster_sip_enable(SrsConfDirective* conf)
{
static bool DEFAULT = false;
static bool DEFAULT = true;
if (!conf) {
return DEFAULT;
}
conf = conf->get("tcp_enable");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return SRS_CONF_PERFER_FALSE(conf->arg0());
}
int SrsConfig::get_stream_caster_rtp_port_min(SrsConfDirective* conf)
{
static int DEFAULT = 0;
if (!conf) {
return DEFAULT;
}
conf = conf->get("rtp_port_min");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return ::atoi(conf->arg0().c_str());
}
int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* conf)
{
static int DEFAULT = 0;
conf = conf->get("sip");
if (!conf) {
return DEFAULT;
}
conf = conf->get("rtp_port_max");
conf = conf->get("enabled");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return SRS_CONF_PERFER_TRUE(conf->arg0());
}
int SrsConfig::get_stream_caster_sip_listen(SrsConfDirective* conf)
{
static int DEFAULT = 5060;
if (!conf) {
return DEFAULT;
}
conf = conf->get("sip");
if (!conf) {
return DEFAULT;
}
conf = conf->get("listen");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return ::atoi(conf->arg0().c_str());
}
srs_utime_t SrsConfig::get_stream_caster_sip_timeout(SrsConfDirective* conf)
{
static srs_utime_t DEFAULT = 60 * SRS_UTIME_SECONDS;
if (!conf) {
return DEFAULT;
}
conf = conf->get("sip");
if (!conf) {
return DEFAULT;
}
conf = conf->get("timeout");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return ::atof(conf->arg0().c_str()) * SRS_UTIME_SECONDS;
}
srs_utime_t SrsConfig::get_stream_caster_sip_reinvite(SrsConfDirective* conf)
{
static srs_utime_t DEFAULT = 5 * SRS_UTIME_SECONDS;
if (!conf) {
return DEFAULT;
}
conf = conf->get("sip");
if (!conf) {
return DEFAULT;
}
conf = conf->get("reinvite");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return ::atof(conf->arg0().c_str()) * SRS_UTIME_SECONDS;
}
std::string SrsConfig::get_stream_caster_sip_candidate(SrsConfDirective* conf)
{
static string DEFAULT = "*";
if (!conf) {
return DEFAULT;
}
conf = conf->get("candidate");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
string eip = srs_getenv(conf->arg0());
if (!eip.empty()) {
return eip;
}
// If configed as ENV, but no ENV set, use default value.
if (srs_string_starts_with(conf->arg0(), "$")) {
return DEFAULT;
}
return conf->arg0();
}
bool SrsConfig::get_rtc_server_enabled()
{
SrsConfDirective* conf = root->get("rtc_server");

View file

@ -101,6 +101,7 @@ extern bool srs_config_dvr_is_plan_segment(std::string plan);
extern bool srs_config_dvr_is_plan_session(std::string plan);
extern bool srs_stream_caster_is_udp(std::string caster);
extern bool srs_stream_caster_is_flv(std::string caster);
extern bool srs_stream_caster_is_gb28181(std::string caster);
// Whether the dvr_apply active the stream specified by req.
extern bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req);
@ -487,13 +488,16 @@ public:
virtual std::string get_stream_caster_output(SrsConfDirective* conf);
// Get the listen port of stream caster.
virtual int get_stream_caster_listen(SrsConfDirective* conf);
// Get the listen port type of stream caster.
virtual bool get_stream_caster_tcp_enable(SrsConfDirective* conf);
// Get the min udp port for rtp of stream caster rtsp.
virtual int get_stream_caster_rtp_port_min(SrsConfDirective* conf);
// Get the max udp port for rtp of stream caster rtsp.
virtual int get_stream_caster_rtp_port_max(SrsConfDirective* conf);
// Get the sip.enabled configuration.
virtual bool get_stream_caster_sip_enable(SrsConfDirective* conf);
// Get the sip.listen port configuration.
virtual int get_stream_caster_sip_listen(SrsConfDirective* conf);
// Get the sip.timeout configuration.
virtual srs_utime_t get_stream_caster_sip_timeout(SrsConfDirective* conf);
// Get the sip.reinvite configuration.
virtual srs_utime_t get_stream_caster_sip_reinvite(SrsConfDirective* conf);
// Get the candidate for SDP.
virtual std::string get_stream_caster_sip_candidate(SrsConfDirective* conf);
// rtc section
public:
virtual bool get_rtc_server_enabled();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,736 @@
//
// Copyright (c) 2013-2022 The SRS Authors
//
// SPDX-License-Identifier: MIT or MulanPSL-2.0
//
#ifndef SRS_APP_GB28181_HPP
#define SRS_APP_GB28181_HPP
#include <srs_core.hpp>
#include <srs_app_st.hpp>
#include <srs_app_listener.hpp>
#include <srs_protocol_conn.hpp>
#include <srs_protocol_http_conn.hpp>
#include <srs_kernel_ps.hpp>
#include <srs_app_conn.hpp>
#include <sstream>
class SrsConfDirective;
class SrsTcpListener;
class SrsResourceManager;
class SrsTcpConnection;
class SrsCoroutine;
class SrsPackContext;
class SrsBuffer;
class SrsSipMessage;
class SrsLazyGbSession;
class SrsLazyGbSipTcpConn;
class SrsLazyGbMediaTcpConn;
class SrsLazyGbSipTcpConnWrapper;
class SrsLazyGbMediaTcpConnWrapper;
class SrsLazyGbSipTcpReceiver;
class SrsLazyGbSipTcpSender;
class SrsAlonePithyPrint;
class SrsGbMuxer;
class SrsSimpleRtmpClient;
struct SrsRawAacStreamCodec;
class SrsRawH264Stream;
class SrsSharedPtrMessage;
class SrsPithyPrint;
class SrsRawAacStream;
// The state machine for GB session.
// init:
// to connecting: sip is registered. Note that also send invite request if media not connected.
// connecting:
// to established: sip is stable, media is connected.
// established:
// init: media is not connected.
// dispose session: sip is bye.
// Please see SrsLazyGbSession::drive_state for detail.
enum SrsGbSessionState
{
SrsGbSessionStateInit = 0,
SrsGbSessionStateConnecting,
SrsGbSessionStateEstablished,
};
std::string srs_gb_session_state(SrsGbSessionState state);
// The state machine for GB SIP connection.
// init:
// to registered: Got REGISTER SIP message.
// to stable: Got MESSAGE SIP message, when SIP TCP connection re-connect.
// registered:
// to inviting: Sent INVITE SIP message.
// inviting:
// to trying: Got TRYING SIP message.
// to stable: Got INVITE 200 OK and sent INVITE OK response.
// trying:
// to stable: Got INVITE 200 OK and sent INVITE OK response.
// stable:
// to re-inviting: Sent bye to device before re-inviting.
// to bye: Got bye SIP message from device.
// re-inviting:
// to inviting: Got bye OK response from deivce.
// Please see SrsLazyGbSipTcpConn::drive_state for detail.
enum SrsGbSipState
{
SrsGbSipStateInit = 0,
SrsGbSipStateRegistered,
SrsGbSipStateInviting,
SrsGbSipStateTrying,
SrsGbSipStateReinviting,
SrsGbSipStateStable,
SrsGbSipStateBye,
};
std::string srs_gb_sip_state(SrsGbSipState state);
// The interface for GB SIP or HTTP-API connection.
class ISrsGbSipConn
{
public:
ISrsGbSipConn();
virtual ~ISrsGbSipConn();
public:
// Interrupt the transport, because session is disposing.
virtual void interrupt() {}
// Get the device id of device, also used as RTMP stream name.
virtual std::string device_id() { return "livestream"; }
// Get the state of SIP.
virtual SrsGbSipState state() { return SrsGbSipStateInit; }
// Reset the SIP state to registered, for re-inviting.
virtual void reset_to_register() {}
// Whether device is already registered, which might drive the session to connecting state.
virtual bool is_registered() { return false; }
// Whether device is stable state, which means it's sending heartbeat message.
virtual bool is_stable() { return false; }
// Whether device is request to bye, which means there might be no stream ever and so the session should be
// disposed. This is the control event from client device.
virtual bool is_bye() { return false; }
// Send invite to device, for SIP it should be an "INVITE" request message. Output the ssrc as ID of session, for
// media connection to load from SSRC while receiving and handling RTP packets.
virtual srs_error_t invite_request(uint32_t* pssrc) { return srs_success; }
// Change id of coroutine.
virtual void set_cid(const SrsContextId& cid) {}
};
// The wrapper for ISrsGbSipConn.
class ISrsGbSipConnWrapper
{
private:
ISrsGbSipConn dummy_;
public:
ISrsGbSipConnWrapper();
virtual ~ISrsGbSipConnWrapper();
public:
virtual ISrsGbSipConn* resource() { return &dummy_; }
virtual ISrsGbSipConnWrapper* copy() { return new ISrsGbSipConnWrapper(); }
};
// The interface for GB media over TCP or UDP transport.
class ISrsGbMediaConn
{
public:
ISrsGbMediaConn();
virtual ~ISrsGbMediaConn();
public:
// Interrupt the transport, because session is disposing.
virtual void interrupt() {}
// Whether media transport is connected. SRS will invite client to publish stream if not connected.
virtual bool is_connected() { return false; }
// Change id of coroutine.
virtual void set_cid(const SrsContextId& cid) {}
};
// The wrapper for ISrsGbMediaConn.
class ISrsGbMediaConnWrapper
{
private:
ISrsGbMediaConn dummy_;
public:
ISrsGbMediaConnWrapper();
virtual ~ISrsGbMediaConnWrapper();
public:
virtual ISrsGbMediaConn* resource() { return &dummy_; }
virtual ISrsGbMediaConnWrapper* copy() { return new ISrsGbMediaConnWrapper(); }
};
// The main logic object for GB, the session.
class SrsLazyGbSession : public SrsLazyObject, public ISrsResource, public ISrsStartable, public ISrsCoroutineHandler
{
private:
SrsCoroutine* trd_;
SrsContextId cid_;
private:
SrsGbSessionState state_;
ISrsGbSipConnWrapper* sip_;
ISrsGbMediaConnWrapper* media_;
SrsGbMuxer* muxer_;
private:
// The candidate for SDP in configuration.
std::string candidate_;
// The public IP for SDP, generated by SRS.
std::string pip_;
// When wait for SIP and media connecting, timeout if exceed.
srs_utime_t connecting_starttime_;
// The max timeout for connecting.
srs_utime_t connecting_timeout_;
// The time we enter reinviting state.
srs_utime_t reinviting_starttime_;
// The wait time for re-invite.
srs_utime_t reinvite_wait_;
// The number of timeout, dispose session if exceed.
uint32_t nn_timeout_;
private:
SrsAlonePithyPrint* ppp_;
srs_utime_t startime_;
uint64_t total_packs_;
uint64_t total_msgs_;
uint64_t total_recovered_;
uint64_t total_msgs_dropped_;
uint64_t total_reserved_;
private:
uint32_t media_id_;
srs_utime_t media_starttime_;
uint64_t media_msgs_;
uint64_t media_packs_;
uint64_t media_recovered_;
uint64_t media_msgs_dropped_;
uint64_t media_reserved_;
private:
friend class SrsLazyObjectWrapper<SrsLazyGbSession>;
SrsLazyGbSession();
public:
virtual ~SrsLazyGbSession();
public:
// Initialize the GB session.
srs_error_t initialize(SrsConfDirective* conf);
// When got a pack of messages.
void on_ps_pack(SrsPackContext* ctx, SrsPsPacket* ps, const std::vector<SrsTsMessage*>& msgs);
// When got available SIP transport.
void on_sip_transport(ISrsGbSipConnWrapper* sip);
ISrsGbSipConnWrapper* sip_transport();
// When got available media transport.
void on_media_transport(ISrsGbMediaConnWrapper* media);
// Get the candidate for SDP generation, the public IP address for device to connect to.
std::string pip();
// Interface ISrsStartable
public:
virtual srs_error_t start();
// Interface ISrsOneCycleThreadHandler
public:
virtual srs_error_t cycle();
private:
virtual srs_error_t do_cycle();
srs_error_t drive_state();
private:
SrsGbSessionState set_state(SrsGbSessionState v);
// Interface ISrsResource
public:
virtual const SrsContextId& get_id();
virtual std::string desc();
};
// Lazy-sweep wrapper for GB session.
class SrsLazyGbSessionWrapper : public ISrsResource
{
SRS_LAZY_WRAPPER_GENERATOR(SrsLazyGbSession, SrsLazyGbSessionWrapper, SrsLazyGbSession);
};
// The SIP and Media listener for GB.
class SrsGbListener : public ISrsListener, public ISrsTcpHandler
{
private:
SrsConfDirective* conf_;
SrsTcpListener* media_listener_;
SrsTcpListener* sip_listener_;
public:
SrsGbListener();
virtual ~SrsGbListener();
public:
srs_error_t initialize(SrsConfDirective* conf);
srs_error_t listen();
void close();
// Interface ISrsTcpHandler
public:
virtual srs_error_t on_tcp_client(ISrsListener* listener, srs_netfd_t stfd);
};
// A GB28181 TCP SIP connection.
class SrsLazyGbSipTcpConn : public SrsLazyObject, public ISrsResource, public ISrsStartable, public ISrsCoroutineHandler
, public ISrsGbSipConn
{
private:
SrsGbSipState state_;
SrsLazyGbSessionWrapper* session_;
SrsSipMessage* register_;
SrsSipMessage* invite_ok_;
private:
std::string ssrc_str_;
uint32_t ssrc_v_;
private:
SrsConfDirective* conf_;
SrsTcpListener* sip_listener_;
SrsTcpListener* media_listener_;
private:
SrsTcpConnection* conn_;
SrsLazyGbSipTcpReceiver* receiver_;
SrsLazyGbSipTcpSender* sender_;
SrsCoroutine* trd_;
private:
friend class SrsLazyObjectWrapper<SrsLazyGbSipTcpConn>;
SrsLazyGbSipTcpConn();
public:
virtual ~SrsLazyGbSipTcpConn();
public:
// Setup object, to keep empty constructor.
void setup(SrsConfDirective* conf, SrsTcpListener* sip, SrsTcpListener* media, srs_netfd_t stfd);
// Get the SIP device id.
std::string device_id();
// Set the cid of all coroutines.
virtual void set_cid(const SrsContextId& cid);
private:
// Get the sip and media listen port.
void query_ports(int* sip, int* media);
public:
// When got a SIP message.
srs_error_t on_sip_message(SrsSipMessage* msg);
// Enqueue a SIP message to send, which might be a request or response.
void enqueue_sip_message(SrsSipMessage* msg);
private:
void drive_state(SrsSipMessage* msg);
void register_response(SrsSipMessage* msg);
void message_response(SrsSipMessage* msg, http_status status);
void invite_ack(SrsSipMessage* msg);
void bye_response(SrsSipMessage* msg);
public:
srs_error_t invite_request(uint32_t* pssrc);
public:
// Interrupt transport by session.
void interrupt();
// Get the SIP state.
SrsGbSipState state();
// Reset the SIP state to registered, for re-inviting.
void reset_to_register();
// Whether SIP state is registered or more.
bool is_registered();
// Whether SIP state is stable state, established.
bool is_stable();
// Whether SIP is bye bye.
bool is_bye();
private:
SrsGbSipState set_state(SrsGbSipState v);
// Interface ISrsResource
public:
virtual const SrsContextId& get_id();
virtual std::string desc();
// Interface ISrsStartable
public:
virtual srs_error_t start();
// Interface ISrsOneCycleThreadHandler
public:
virtual srs_error_t cycle();
private:
virtual srs_error_t do_cycle();
private:
// Create session if no one, or bind to an existed session.
srs_error_t bind_session(SrsSipMessage* msg, SrsLazyGbSessionWrapper** psession);
};
// Lazy-sweep wrapper for GB SIP TCP connection.
class SrsLazyGbSipTcpConnWrapper : public ISrsResource, public ISrsGbSipConnWrapper
{
SRS_LAZY_WRAPPER_GENERATOR(SrsLazyGbSipTcpConn, ISrsGbSipConnWrapper, ISrsGbSipConn);
};
// Start a coroutine to receive SIP messages.
class SrsLazyGbSipTcpReceiver : public ISrsStartable, public ISrsCoroutineHandler
{
private:
SrsCoroutine* trd_;
SrsTcpConnection* conn_;
SrsLazyGbSipTcpConn* sip_;
public:
SrsLazyGbSipTcpReceiver(SrsLazyGbSipTcpConn* sip, SrsTcpConnection* conn);
virtual ~SrsLazyGbSipTcpReceiver();
public:
// Interrupt the receiver coroutine.
void interrupt();
// Set the cid of all coroutines.
virtual void set_cid(const SrsContextId& cid);
// Interface ISrsStartable
public:
virtual srs_error_t start();
// Interface ISrsOneCycleThreadHandler
public:
virtual srs_error_t cycle();
private:
srs_error_t do_cycle();
};
// Start a coroutine to send out SIP messages.
class SrsLazyGbSipTcpSender : public ISrsStartable, public ISrsCoroutineHandler
{
private:
SrsCoroutine* trd_;
SrsTcpConnection* conn_;
private:
std::vector<SrsSipMessage*> msgs_;
srs_cond_t wait_;
public:
SrsLazyGbSipTcpSender(SrsTcpConnection* conn);
virtual ~SrsLazyGbSipTcpSender();
public:
// Push message to queue, and sender will send out in dedicate coroutine.
void enqueue(SrsSipMessage* msg);
// Interrupt the sender coroutine.
void interrupt();
// Set the cid of all coroutines.
virtual void set_cid(const SrsContextId& cid);
// Interface ISrsStartable
public:
virtual srs_error_t start();
// Interface ISrsOneCycleThreadHandler
public:
virtual srs_error_t cycle();
private:
srs_error_t do_cycle();
};
// The handler for a pack of PS PES packets.
class ISrsPsPackHandler
{
public:
ISrsPsPackHandler();
virtual ~ISrsPsPackHandler();
public:
// When got a pack of PS/TS messages, contains one video frame and optional SPS/PPS message, one or generally more
// audio frames. Note that the ps contains the pack information.
virtual srs_error_t on_ps_pack(SrsPsPacket* ps, const std::vector<SrsTsMessage*>& msgs) = 0;
};
// A GB28181 TCP media connection, for PS stream.
class SrsLazyGbMediaTcpConn : public SrsLazyObject, public ISrsResource, public ISrsStartable, public ISrsCoroutineHandler
, public ISrsPsPackHandler, public ISrsGbMediaConn
{
private:
bool connected_;
SrsLazyGbSessionWrapper* session_;
uint32_t nn_rtcp_;
private:
SrsPackContext* pack_;
SrsTcpConnection* conn_;
SrsCoroutine* trd_;
uint8_t* buffer_;
private:
friend class SrsLazyObjectWrapper<SrsLazyGbMediaTcpConn>;
SrsLazyGbMediaTcpConn();
public:
virtual ~SrsLazyGbMediaTcpConn();
public:
// Setup object, to keep empty constructor.
void setup(srs_netfd_t stfd);
// Whether media is connected.
bool is_connected();
// Interrupt transport by session.
void interrupt();
// Set the cid of all coroutines.
virtual void set_cid(const SrsContextId& cid);
// Interface ISrsResource
public:
virtual const SrsContextId& get_id();
virtual std::string desc();
// Interface ISrsStartable
public:
virtual srs_error_t start();
// Interface ISrsOneCycleThreadHandler
public:
virtual srs_error_t cycle();
private:
virtual srs_error_t do_cycle();
// Interface ISrsPsPackHandler
public:
virtual srs_error_t on_ps_pack(SrsPsPacket* ps, const std::vector<SrsTsMessage*>& msgs);
private:
// Create session if no one, or bind to an existed session.
srs_error_t bind_session(uint32_t ssrc, SrsLazyGbSessionWrapper** psession);
};
// Lazy-sweep wrapper for GB Media TCP connection.
class SrsLazyGbMediaTcpConnWrapper : public ISrsResource, public ISrsGbMediaConnWrapper
{
SRS_LAZY_WRAPPER_GENERATOR(SrsLazyGbMediaTcpConn, ISrsGbMediaConnWrapper, ISrsGbMediaConn);
};
// The queue for mpegts over udp to send packets.
// For the aac in mpegts contains many flv packets in a pes packet,
// we must recalc the timestamp.
class SrsMpegpsQueue
{
private:
// The key: dts, value: msg.
std::map<int64_t, SrsSharedPtrMessage*> msgs;
int nb_audios;
int nb_videos;
public:
SrsMpegpsQueue();
virtual ~SrsMpegpsQueue();
public:
virtual srs_error_t push(SrsSharedPtrMessage* msg);
virtual SrsSharedPtrMessage* dequeue();
};
// Mux GB28181 to RTMP.
class SrsGbMuxer
{
private:
SrsLazyGbSession* session_;
std::string output_;
SrsSimpleRtmpClient* sdk_;
private:
SrsRawH264Stream* avc_;
std::string h264_sps_;
bool h264_sps_changed_;
std::string h264_pps_;
bool h264_pps_changed_;
bool h264_sps_pps_sent_;
private:
SrsRawAacStream* aac_;
std::string aac_specific_config_;
private:
SrsMpegpsQueue* queue_;
SrsPithyPrint* pprint_;
public:
SrsGbMuxer(SrsLazyGbSession* session);
virtual ~SrsGbMuxer();
public:
srs_error_t initialize(std::string output);
srs_error_t on_ts_message(SrsTsMessage* msg);
private:
virtual srs_error_t on_ts_video(SrsTsMessage* msg, SrsBuffer* avs);
virtual srs_error_t write_h264_sps_pps(uint32_t dts, uint32_t pts);
virtual srs_error_t write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts);
virtual srs_error_t on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs);
virtual srs_error_t write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts);
virtual srs_error_t rtmp_write_packet(char type, uint32_t timestamp, char* data, int size);
private:
// Connect to RTMP server.
virtual srs_error_t connect();
// Close the connection to RTMP server.
virtual void close();
};
// Response writer for SIP.
class SrsSipResponseWriter : public SrsHttpResponseWriter
{
public:
SrsSipResponseWriter(ISrsProtocolReadWriter* io);
virtual ~SrsSipResponseWriter();
// Interface ISrsHttpFirstLineWriter
public:
virtual srs_error_t build_first_line(std::stringstream& ss, char* data, int size);
};
// Request writer for SIP.
class SrsSipRequestWriter : public SrsHttpRequestWriter
{
public:
SrsSipRequestWriter(ISrsProtocolReadWriter* io);
virtual ~SrsSipRequestWriter();
// Interface ISrsHttpFirstLineWriter
public:
virtual srs_error_t build_first_line(std::stringstream& ss, char* data, int size);
};
// SIP message, covert with HTTP message.
class SrsSipMessage
{
public:
// SIP message type, request or response.
http_parser_type type_;
// For SIP request.
http_method method_; // For example: HTTP_INVITE
std::string request_uri_; // For example: sip:34020000001320000001@3402000000
std::string request_uri_user_; // For example: 34020000001320000001
std::string request_uri_host_; // For example: 3402000000
// For SIP response.
http_status status_; // For example: HTTP_STATUS_OK
public:
// The Via header field indicates the path taken by the request so far and indicates the path that should be
// followed in routing responses. See https://www.ietf.org/rfc/rfc3261.html#section-20.42
// @param transport TCP or UDP, the transport protocol.
// @param send_by The host and port which send the packet.
// @param branch Transaction identifier, see https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.7
// @param rport For UDP to traverse NATs, see https://www.ietf.org/rfc/rfc3581.html
std::string via_; // For example: SIP/2.0/TCP 192.168.3.82:5060;rport;branch=z9hG4bK0l31rx
std::string via_transport_; // For example: TCP
std::string via_send_by_; // For example: 192.168.3.82:5060
std::string via_send_by_address_; // For example: 192.168.3.82
int via_send_by_port_; // For example: 5060
std::string via_branch_; // For example: branch=z9hG4bK0l31rx
std::string via_rport_; // For example: rport
public:
// See https://www.ietf.org/rfc/rfc3261.html#section-20.20
// See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.3
std::string from_; // For example: <sip:34020000002000000001@3402000000>;tag=SRSk1er282t
std::string from_address_; // For example: <sip:34020000002000000001@3402000000>
std::string from_address_user_; // For example: 34020000002000000001
std::string from_address_host_; // For example: 3402000000
std::string from_tag_; // For example: tag=SRSk1er282t
public:
// See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.2
// See https://www.ietf.org/rfc/rfc3261.html#section-20.39
std::string to_; // For example: <sip:34020000001320000001@3402000000>;tag=SRSk1er282t
std::string to_address_; // For example: <sip:34020000001320000001@3402000000>
std::string to_address_user_; // For example: 34020000001320000001
std::string to_address_host_; // For example: 3402000000
std::string to_tag_; // For example: tag=SRSk1er282t
public:
// See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.4
// See https://www.ietf.org/rfc/rfc3261.html#section-20.8
std::string call_id_; // For example: 854k7337207yxpfj
// See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.8
// See https://www.ietf.org/rfc/rfc3261.html#section-20.10
std::string contact_; // For example: <sip:34020000002000000001@3402000000>
std::string contact_user_; // For example: <sip:34020000002000000001@3402000000>
std::string contact_host_; // For example: 3402000000
std::string contact_host_address_; // For example: 3402000000
int contact_host_port_; // For example: 5060
// See https://www.ietf.org/rfc/rfc3261.html#section-20.19
uint32_t expires_;
// See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.6
// See https://www.ietf.org/rfc/rfc3261.html#section-20.22
uint32_t max_forwards_;
public:
// See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.5
// See https://www.ietf.org/rfc/rfc3261.html#section-20.16
std::string cseq_;
uint32_t cseq_number_;
std::string cseq_method_;
public:
// See https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=469659DC56B9B8187671FF08748CEC89
std::string subject_;
// The content type.
std::string content_type_;
public:
// SIP message body.
std::string body_;
// Escape \r to \\r, \n to \\n.
std::string body_escaped_;
public:
SrsSipMessage();
virtual ~SrsSipMessage();
public:
SrsSipMessage* copy();
const std::string& device_id();
std::string ssrc_domain_id();
SrsSipMessage* set_body(std::string v);
srs_error_t parse(ISrsHttpMessage* m);
private:
srs_error_t parse_via(const std::string& via);
srs_error_t parse_from(const std::string& from);
srs_error_t parse_to(const std::string& to);
srs_error_t parse_cseq(const std::string& cseq);
srs_error_t parse_contact(const std::string& contact);
public:
bool is_register() {
return type_ == HTTP_REQUEST && method_ == HTTP_REGISTER;
}
bool is_message() {
return type_ == HTTP_REQUEST && method_ == HTTP_MESSAGE;
}
bool is_invite() {
return type_ == HTTP_REQUEST && method_ == HTTP_INVITE;
}
bool is_trying() {
return type_ == HTTP_RESPONSE && cseq_method_ == "INVITE" && status_ == HTTP_STATUS_CONTINUE;
}
bool is_invite_ok() {
return type_ == HTTP_RESPONSE && cseq_method_ == "INVITE" && status_ == HTTP_STATUS_OK;
}
bool is_bye() {
return type_ == HTTP_REQUEST && method_ == HTTP_BYE;
}
bool is_bye_ok() {
return type_ == HTTP_RESPONSE && cseq_method_ == "BYE" && status_ == HTTP_STATUS_OK;
}
};
// Recoverable PS context for GB28181.
class SrsRecoverablePsContext
{
public:
SrsPsContext ctx_;
private:
// If decoding error, enter the recover mode. Drop all left bytes util next pack header.
int recover_;
public:
SrsRecoverablePsContext();
virtual ~SrsRecoverablePsContext();
public:
// Decode the PS stream in RTP payload. Note that there might be first reserved bytes of RAW data, which is not
// parsed by previous decoding, we should move to the start of payload bytes.
virtual srs_error_t decode_rtp(SrsBuffer* stream, int reserved, ISrsPsMessageHandler* handler);
private:
// Decode the RTP payload as PS pack stream.
virtual srs_error_t decode(SrsBuffer* stream, ISrsPsMessageHandler* handler);
// When got error, drop data and enter recover mode.
srs_error_t enter_recover_mode(SrsBuffer* stream, ISrsPsMessageHandler* handler, int pos, srs_error_t err);
// Quit Recover mode when got pack header.
void quit_recover_mode(SrsBuffer* stream, ISrsPsMessageHandler* handler);
};
// The PS pack context, for GB28181 to process based on PS pack, which contains a video and audios messages. For large
// video frame, it might be split to multiple PES packets, which must be group to one video frame.
// Please note that a pack might contain multiple audio frames, so size of audio PES packet should not exceed 64KB,
// which is limited by the 16 bits PES_packet_length.
// We also correct the timestamp, or DTS/PTS of video frames, which might be 0 if more than one video PES packets in a
// PS pack stream.
class SrsPackContext : public ISrsPsMessageHandler
{
public:
// Each media transport only use one context, so the context id is the media id.
uint32_t media_id_;
srs_utime_t media_startime_;
uint64_t media_nn_recovered_;
uint64_t media_nn_msgs_dropped_;
uint64_t media_reserved_;
private:
// To process a pack of TS/PS messages.
ISrsPsPackHandler* handler_;
// Note that it might be freed, so never use its fields.
SrsPsPacket* ps_;
// The messages in current PS pack.
std::vector<SrsTsMessage*> msgs_;
public:
SrsPackContext(ISrsPsPackHandler* handler);
virtual ~SrsPackContext();
private:
void clear();
// Interface ISrsPsMessageHandler
public:
virtual srs_error_t on_ts_message(SrsTsMessage* msg);
virtual void on_recover_mode(int nn_recover);
};
// Find the pack header which starts with bytes (00 00 01 ba).
extern bool srs_skip_util_pack(SrsBuffer* stream);
// Parse the address from SIP string, for example:
// sip:bob@ossrs.io:5060
// <sip:bob@ossrs.io:5060>
// Bob <sip:bob@ossrs.io:5060>
// Parsed as:
// user: bob
// host: ossrs.io:5060
// Note that the host can be parsed by srs_parse_hostport as host(ossrs.io) and port(5060).
extern void srs_sip_parse_address(const std::string& address, std::string& user, std::string& host);
// Manager for GB connections.
extern SrsResourceManager* _srs_gb_manager;
#endif

View file

@ -40,6 +40,9 @@ using namespace std;
#ifdef SRS_RTC
#include <srs_app_rtc_network.hpp>
#endif
#ifdef SRS_GB28181
#include <srs_app_gb28181.hpp>
#endif
SrsSignalManager* SrsSignalManager::instance = NULL;
@ -336,7 +339,10 @@ SrsServer::SrsServer()
stream_caster_flv_listener_ = new SrsHttpFlvListener();
stream_caster_mpegts_ = new SrsUdpCasterListener();
exporter_listener_ = new SrsTcpListener(this);
#ifdef SRS_GB28181
stream_caster_gb28181_ = new SrsGbListener();
#endif
// donot new object in constructor,
// for some global instance is not ready now,
// new these objects in initialize instead.
@ -392,6 +398,9 @@ void SrsServer::destroy()
srs_freep(stream_caster_flv_listener_);
srs_freep(stream_caster_mpegts_);
srs_freep(exporter_listener_);
#ifdef SRS_GB28181
srs_freep(stream_caster_gb28181_);
#endif
}
void SrsServer::dispose()
@ -408,6 +417,9 @@ void SrsServer::dispose()
stream_caster_flv_listener_->close();
stream_caster_mpegts_->close();
exporter_listener_->close();
#ifdef SRS_GB28181
stream_caster_gb28181_->close();
#endif
// Fast stop to notify FFMPEG to quit, wait for a while then fast kill.
ingester->dispose();
@ -436,6 +448,9 @@ void SrsServer::gracefully_dispose()
stream_caster_flv_listener_->close();
stream_caster_mpegts_->close();
exporter_listener_->close();
#ifdef SRS_GB28181
stream_caster_gb28181_->close();
#endif
srs_trace("listeners closed");
// Fast stop to notify FFMPEG to quit, wait for a while then fast kill.
@ -632,6 +647,15 @@ srs_error_t SrsServer::listen()
if ((err = stream_caster_flv_listener_->initialize(conf)) != srs_success) {
return srs_error_wrap(err, "initialize");
}
} else if (srs_stream_caster_is_gb28181(caster)) {
#ifdef SRS_GB28181
listener = stream_caster_gb28181_;
if ((err = stream_caster_gb28181_->initialize(conf)) != srs_success) {
return srs_error_wrap(err, "initialize");
}
#else
return srs_error_new(ERROR_STREAM_CASTER_ENGINE, "Please enable GB by: ./configure --gb28181=on");
#endif
} else {
return srs_error_new(ERROR_STREAM_CASTER_ENGINE, "invalid caster %s", caster.c_str());
}
@ -1318,6 +1342,17 @@ srs_error_t SrsServerAdapter::run(SrsWaitGroup* wg)
return srs_error_wrap(err, "start");
}
#ifdef SRS_GB28181
if ((err = _srs_gb_manager->start()) != srs_success) {
return srs_error_wrap(err, "start manager");
}
#endif
SrsSweepGc* gc = dynamic_cast<SrsSweepGc*>(_srs_gc);
if ((err = gc->start()) != srs_success) {
return srs_error_wrap(err, "start gc");
}
return err;
}

View file

@ -40,6 +40,7 @@ class SrsWaitGroup;
class SrsMultipleTcpListeners;
class SrsHttpFlvListener;
class SrsUdpCasterListener;
class SrsGbListener;
// Convert signal to io,
// @see: st-1.9/docs/notes.html
@ -136,6 +137,10 @@ private:
// Exporter server listener, over TCP. Please note that metrics request of HTTP is served by this
// listener, and it might be reused by HTTP API.
SrsTcpListener* exporter_listener_;
#ifdef SRS_GB28181
// Stream Caster for GB28181.
SrsGbListener* stream_caster_gb28181_;
#endif
private:
// Signal manager which convert gignal to io message.
SrsSignalManager* signal_manager;

View file

@ -25,6 +25,9 @@
#ifdef SRS_SRT
#include <srs_app_srt_source.hpp>
#endif
#ifdef SRS_GB28181
#include <srs_app_gb28181.hpp>
#endif
#include <stdlib.h>
#include <string>
@ -329,6 +332,9 @@ srs_error_t srs_global_initialize()
_srs_rtc_manager = new SrsResourceManager("RTC", true);
_srs_rtc_dtls_certificate = new SrsDtlsCertificate();
#endif
#ifdef SRS_GB28181
_srs_gb_manager = new SrsResourceManager("GB", true);
#endif
_srs_gc = new SrsSweepGc();

View file

@ -9,6 +9,6 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 0
#define VERSION_REVISION 73
#define VERSION_REVISION 74
#endif

View file

@ -307,7 +307,15 @@
XX(ERROR_HTTPS_HANDSHAKE , 4042, "HttpsHandshake", "Failed to do handshake for HTTPS") \
XX(ERROR_HTTPS_READ , 4043, "HttpsRead", "Failed to read data from HTTPS stream") \
XX(ERROR_HTTPS_WRITE , 4044, "HttpsWrite", "Failed to write data to HTTPS stream") \
XX(ERROR_HTTPS_KEY_CRT , 4045, "HttpsSslFile", "Failed to load SSL key or crt file for HTTPS")
XX(ERROR_HTTPS_KEY_CRT , 4045, "HttpsSslFile", "Failed to load SSL key or crt file for HTTPS") \
XX(ERROR_GB_SIP_HEADER , 4046, "GbHeaderCallId", "Missing field of SIP header for GB28181") \
XX(ERROR_GB_SIP_MESSAGE , 4047, "GbHeaderCallId", "Invalid SIP message for GB28181") \
XX(ERROR_GB_PS_HEADER , 4048, "GbPsHeader", "Invalid PS header for GB28181") \
XX(ERROR_GB_PS_PSE , 4049, "GbPsPSE", "Invalid PS PSE for GB28181") \
XX(ERROR_GB_PS_MEDIA , 4050, "GbPsMedia", "Invalid PS Media packet for GB28181") \
XX(ERROR_GB_SSRC_GENERATE , 4051, "GbSsrcGenerate", "Failed to generate SSRC for GB28181") \
XX(ERROR_GB_CONFIG , 4052, "GbConfig", "Invalid configuration for GB28181") \
XX(ERROR_GB_TIMEOUT , 4053, "GbTimeout", "SIP or media connection timeout for GB28181") \
/**************************************************/
/* RTC protocol error. */

View file

@ -0,0 +1,491 @@
//
// Copyright (c) 2013-2022 The SRS Authors
//
// SPDX-License-Identifier: MIT or MulanPSL-2.0
//
#include <srs_kernel_ps.hpp>
#include <srs_kernel_error.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_log.hpp>
#include <srs_kernel_stream.hpp>
#include <srs_core_autofree.hpp>
#include <srs_kernel_rtc_rtp.hpp>
#include <srs_kernel_utility.hpp>
#include <string>
using namespace std;
// The minimum required bytes to parse a PS packet.
#define SRS_PS_MIN_REQUIRED 32
SrsPsDecodeHelper::SrsPsDecodeHelper()
{;
rtp_seq_ = 0;
rtp_ts_ = 0;
rtp_pt_ = 0;
pack_id_ = 0;
pack_first_seq_ = 0;
pack_pre_msg_last_seq_ = 0;
pack_nn_msgs_ = 0;
ctx_ = NULL;
ps_ = NULL;
}
ISrsPsMessageHandler::ISrsPsMessageHandler()
{
}
ISrsPsMessageHandler::~ISrsPsMessageHandler()
{
}
SrsPsContext::SrsPsContext()
{
last_ = NULL;
current_ = NULL;
helper_.ctx_ = this;
detect_ps_integrity_ = false;
}
SrsPsContext::~SrsPsContext()
{
srs_freep(last_);
srs_freep(current_);
}
void SrsPsContext::set_detect_ps_integrity(bool v)
{
detect_ps_integrity_ = v;
}
SrsTsMessage* SrsPsContext::last()
{
if (!last_) {
last_ = new SrsTsMessage();
last_->ps_helper_ = &helper_;
}
return last_;
}
SrsTsMessage* SrsPsContext::reap()
{
SrsTsMessage* msg = last_;
last_ = new SrsTsMessage();
last_->ps_helper_ = &helper_;
return msg;
}
srs_error_t SrsPsContext::decode(SrsBuffer* stream, ISrsPsMessageHandler* handler)
{
srs_error_t err = srs_success;
// Decode PS packet one by one.
while (!stream->empty()) {
// If new PS packet but not enough packet, ignore.
if (detect_ps_integrity_ && stream->left() >= 4) {
uint8_t* p = (uint8_t*)stream->head();
if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && stream->left() <= SRS_PS_MIN_REQUIRED) {
break;
}
}
// Try to decode the stream by one PS packet.
// See Table 2-32 Program Stream pack, hls-mpeg-ts-iso13818-1.pdf, page 73
if ((err = do_decode(stream, handler)) != srs_success) {
return srs_error_wrap(err, "decode");
}
// Use unit start as 1, however it has no effect because PES_packet_length should never be 0 for PS packet.
const int payload_unit_start_indicator = 1;
if (!last()->completed(payload_unit_start_indicator)) {
continue; // Ignore if message not completed.
}
// Reap the last completed PS message.
SrsTsMessage* msg = reap();
SrsAutoFree(SrsTsMessage, msg);
if (msg->sid == SrsTsPESStreamIdProgramStreamMap) {
if (!msg->payload || !msg->payload->length()) {
return srs_error_new(ERROR_GB_PS_HEADER, "empty PSM payload");
}
// Decode PSM(Program Stream map) from PES packet payload.
SrsBuffer buf(msg->payload->bytes(), msg->payload->length());
SrsPsPsmPacket psm;
if ((err = psm.decode(&buf)) != srs_success) {
return srs_error_wrap(err, "decode psm");
}
srs_info("PS: Ignore PSM for video=%#x, audio=%#x", psm.video_elementary_stream_id_, psm.audio_elementary_stream_id_);
//context_->set(psm.video_elementary_stream_id_, SrsTsPidApplyVideo);
//context_->set(psm.audio_elementary_stream_id_, SrsTsPidApplyAudio);
} else if (msg->is_video() || msg->is_audio()) {
// Update the total messages in pack.
helper_.pack_pre_msg_last_seq_ = helper_.rtp_seq_;
helper_.pack_nn_msgs_++;
//srs_error("PS: Got message %s, dts=%" PRId64 ", payload=%dB", msg->is_video() ? "Video" : "Audio", msg->dts/9000, msg->PES_packet_length);
if (handler && (err = handler->on_ts_message(msg)) != srs_success) {
return srs_error_wrap(err, "handle PS message");
}
} else {
srs_info("PS: Ignore message sid=%#x", msg->sid);
}
}
return err;
}
srs_error_t SrsPsContext::do_decode(SrsBuffer* stream, ISrsPsMessageHandler* handler)
{
srs_error_t err = srs_success;
// If last message not completed, the bytes in stream must be payload.
if (!last()->fresh()) {
if ((err = last()->dump(stream, NULL)) != srs_success) {
return srs_error_wrap(err, "dump pes");
}
return err;
}
// For PS pack, must always start with 00 00 01 XX.
// See Table 2-32 Program Stream pack, hls-mpeg-ts-iso13818-1.pdf, page 73
if (!stream->require(4)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
}
uint8_t* p = (uint8_t*)stream->head();
// For normal mode, should start with 00 00 01, for pack or system header or PES packet.
if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x01) {
return srs_error_new(ERROR_GB_PS_HEADER, "Invalid PS stream %#x %#x %#x", p[0], p[1], p[2]);
}
// If pack start code, it's a net PS pack stream.
if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xba) {
srs_freep(current_);
}
if (!current_) {
current_ = new SrsPsPacket(this);
current_->id_ |= helper_.rtp_seq_; // The low 16 bits is reserved for RTP seq.
helper_.pack_id_ = current_->id_;
helper_.pack_first_seq_ = helper_.rtp_seq_;
// Set the helper for decoder, pass-by values.
helper_.ps_ = current_;
helper_.pack_nn_msgs_ = 0;
}
// Try to decode the PS pack stream.
int pos = stream->pos();
if ((err = current_->decode(stream)) != srs_success) {
err = srs_error_wrap(err, "decode start=%d, pos=%d, left=%d", pos, stream->pos(), stream->left());
stream->skip(pos - stream->pos());
return err;
}
return err;
}
SrsPsPacket::SrsPsPacket(SrsPsContext* context)
{
context_ = context;
has_pack_header_ = has_system_header_ = false;
static uint32_t gid = 0;
id_ = ((gid++) << 16) & 0xffff0000;
pack_start_code_ = 0;
system_clock_reference_base_ = 0;
system_clock_reference_extension_ = 0;
program_mux_rate_ = 0;
pack_stuffing_length_ = 0;
system_header_start_code_ = 0;
header_length_ = 0;
rate_bound_ = 0;
audio_bound_ = 0;
CSPS_flag_ = 0;
system_audio_lock_flag_ = 0;
system_video_lock_flag_ = 0;
video_bound_ = 0;
packet_rate_restriction_flag_ = 0;
audio_stream_id_ = 0;
audio_buffer_bound_scale_ = 0;
audio_buffer_size_bound_ = 0;
video_stream_id_ = 0;
video_buffer_bound_scale_ = 0;
video_buffer_size_bound_ = 0;
}
SrsPsPacket::~SrsPsPacket()
{
}
srs_error_t SrsPsPacket::decode(SrsBuffer* stream)
{
srs_error_t err = srs_success;
// Program Stream pack header.
if (!stream->require(4)) return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
uint8_t* p = (uint8_t*)stream->head();
if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xba) {
if ((err = decode_pack(stream)) != srs_success) {
return srs_error_wrap(err, "pack");
}
has_pack_header_ = true;
}
// Program stream system header.
if (stream->empty()) return err; // Parsed done, OK.
if (!stream->require(4)) return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
p = (uint8_t*)stream->head();
if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xbb) {
if ((err = decode_system(stream)) != srs_success) {
return srs_error_wrap(err, "system");
}
has_system_header_ = true;
}
// Packet start code prefix.
while (!stream->empty()) {
if (!stream->require(4)) return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
p = (uint8_t*)stream->head();
if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x01) break;
if (p[3] == 0xba || p[3] == 0xbb) break; // Reparse for pack or system header.
SrsMpegPES pes;
if ((err = pes.decode(stream)) != srs_success) {
return srs_error_wrap(err, "decode pes");
}
SrsTsMessage* lm = context_->last();
// The stream id should never change for PS stream.
if (lm->sid != (SrsTsPESStreamId)0 && lm->sid != (SrsTsPESStreamId)pes.stream_id) {
return srs_error_new(ERROR_GB_PS_PSE, "PS stream id change from %#x to %#x", lm->sid, pes.stream_id);
}
lm->sid = (SrsTsPESStreamId)pes.stream_id;
if (pes.PTS_DTS_flags == 0x02 || pes.PTS_DTS_flags == 0x03) {
lm->dts = pes.dts;
lm->pts = pes.pts;
}
if (pes.has_payload_) {
// The size of PS message, should be always a positive value.
lm->PES_packet_length = pes.nb_payload_;
if ((err = lm->dump(stream, &pes.nb_bytes)) != srs_success) {
return srs_error_wrap(err, "dump pes");
}
}
// Use unit start as 1, however it has no effect because PES_packet_length should never be 0 for PS packet.
const int payload_unit_start_indicator = 1;
if (lm->completed(payload_unit_start_indicator)) {
return err; // OK, got one message, let PS context handle it.
}
}
return err;
}
srs_error_t SrsPsPacket::decode_pack(SrsBuffer* stream)
{
srs_error_t err = srs_success;
// 14 bytes fixed header.
if (!stream->require(14)) {
return srs_error_new(ERROR_GB_PS_HEADER, "ps requires 14 only %d bytes", stream->left());
}
pack_start_code_ = stream->read_4bytes();
srs_assert(pack_start_code_ == 0x000001ba);
uint64_t r0 = stream->read_4bytes();
uint16_t r1 = stream->read_2bytes();
system_clock_reference_extension_ = (r1 >> 1) & 0x1ff;
system_clock_reference_base_ = 0x00
| ((uint64_t) ((r0 >> 27) & 0x07) << 30) // 3bits
| ((uint64_t) ((r0 >> 11) & 0x7fff) << 15) // 15bits
| ((uint64_t) (r0 & 0x03ff) << 5) // 10bits
| (uint64_t) ((r1 >> 11) & 0x1f); // 5bits
program_mux_rate_ = stream->read_3bytes();
program_mux_rate_ = (program_mux_rate_ >> 2) & 0x3fffff;
pack_stuffing_length_ = stream->read_1bytes();
pack_stuffing_length_ &= 0x07;
//srs_warn("PS: New pack header clock=%" PRId64 ", rate=%d", system_clock_reference_base_, program_mux_rate_);
if (!stream->require(pack_stuffing_length_)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", pack_stuffing_length_, stream->left());
}
stream->skip(pack_stuffing_length_);
return err;
}
srs_error_t SrsPsPacket::decode_system(SrsBuffer* stream)
{
srs_error_t err = srs_success;
system_header_start_code_ = stream->read_4bytes();
srs_assert(system_header_start_code_ == 0x000001bb);
if (!stream->require(8)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires 8 only %d bytes", stream->left());
}
header_length_ = stream->read_2bytes();
if (!stream->require(header_length_)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", header_length_, stream->left());
}
SrsBuffer b(stream->head(), header_length_);
stream->skip(header_length_);
rate_bound_ = b.read_3bytes();
rate_bound_ = (rate_bound_ >> 1) & 0x3fffff;
CSPS_flag_ = b.read_1bytes();
audio_bound_ = (CSPS_flag_ >> 2) & 0x3f;
CSPS_flag_ &= 0x01;
video_bound_ = b.read_1bytes();
system_audio_lock_flag_ = (video_bound_ >> 7) & 0x01;
system_video_lock_flag_ = (video_bound_ >> 6) & 0x01;
video_bound_ &= 0x1f;
packet_rate_restriction_flag_ = b.read_1bytes();
packet_rate_restriction_flag_ = (packet_rate_restriction_flag_ >> 5) & 0x01;
//srs_warn("PS: New system header rate_bound=%d, video_bound=%d, audio_bound=%d", rate_bound_, video_bound_, audio_bound_);
// Parse stream_id and buffer information.
while (!b.empty()) {
uint8_t r2 = (uint8_t) b.head()[0];
if ((r2 & 0x80) != 0x80) break;
if (!b.require(3)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires 3 only %d bytes", b.left());
}
SrsTsPESStreamId stream_id = (SrsTsPESStreamId)(uint8_t)b.read_1bytes();
uint16_t buffer_size_bound = b.read_2bytes();
uint8_t buffer_bound_scale = (uint8_t)((buffer_size_bound>>13) & 0x01);
buffer_size_bound &= 0x1fff;
if (((stream_id>>4) & 0x0f) == SrsTsPESStreamIdVideoChecker) {
video_stream_id_ = stream_id;
video_buffer_bound_scale_ = buffer_bound_scale;
video_buffer_size_bound_ = buffer_size_bound;
} else if (((stream_id>>5) & 0x07) == SrsTsPESStreamIdAudioChecker) {
audio_stream_id_ = stream_id;
audio_buffer_bound_scale_ = buffer_bound_scale;
audio_buffer_size_bound_ = buffer_size_bound;
} else {
srs_info("PS: Ignore stream_id=%#x, buffer_bound_scale=%d, buffer_size_bound=%d", stream_id, buffer_bound_scale, buffer_size_bound);
}
}
return err;
}
SrsPsPsmPacket::SrsPsPsmPacket()
{
current_next_indicator_ = 0;
program_stream_map_version_ = 0;
program_stream_info_length_ = 0;
elementary_stream_map_length_ = 0;
video_stream_type_ = 0;
video_elementary_stream_id_ = 0;
video_elementary_stream_info_length_ = 0;
audio_stream_type_ = 0;
audio_elementary_stream_id_ = 0;
audio_elementary_stream_info_length_ = 0;
CRC_32_ = 0;
}
SrsPsPsmPacket::~SrsPsPsmPacket()
{
}
srs_error_t SrsPsPsmPacket::decode(SrsBuffer* stream)
{
srs_error_t err = srs_success;
// From first util program_stream_info_length field, at least 4 bytes.
if (!stream->require(4)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
}
uint16_t r0 = stream->read_2bytes();
if ((r0&0x01) != 0x01) {
return srs_error_new(ERROR_GB_PS_HEADER, "invalid marker of 0x%#x", r0);
}
program_stream_map_version_ = (uint8_t)(r0&0x1f);
current_next_indicator_ = (uint8_t)((r0>>7) & 0x01);
if (!current_next_indicator_) {
return srs_error_new(ERROR_GB_PS_HEADER, "invalid indicator of 0x%#x", r0);
}
program_stream_info_length_ = stream->read_2bytes();
if (!stream->require(program_stream_info_length_)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", program_stream_info_length_, stream->left());
}
stream->skip(program_stream_info_length_);
// The number of ES map count, 2 bytes.
if (!stream->require(2)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires 2 only %d bytes", stream->left());
}
elementary_stream_map_length_ = stream->read_2bytes();
if (!stream->require(elementary_stream_map_length_)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", elementary_stream_map_length_, stream->left());
}
SrsBuffer b(stream->head(), elementary_stream_map_length_);
stream->skip(elementary_stream_map_length_);
while (!b.empty()) {
if (!b.require(4)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", b.left());
}
SrsTsStream stream_type = (SrsTsStream)(uint8_t)b.read_1bytes();
uint8_t elementary_stream_id = b.read_1bytes();
uint16_t elementary_stream_info_length = b.read_2bytes();
if (!b.require(elementary_stream_info_length)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", elementary_stream_info_length, b.left());
}
// Descriptor defined as bellow section, but we ignore it:
// Table 2-40 Video stream descriptor, hls-mpeg-ts-iso13818-1.pdf, page 82
// Table 2-42 Audio stream descriptor, hls-mpeg-ts-iso13818-1.pdf, page 83
b.skip(elementary_stream_info_length);
srs_info("PS: Ignore %d bytes descriptor for stream=%#x", elementary_stream_info_length, stream_type);
if (stream_type == SrsTsStreamVideoH264) {
video_stream_type_ = stream_type;
video_elementary_stream_id_ = elementary_stream_id;
video_elementary_stream_info_length_ = elementary_stream_info_length;
} else if (stream_type == SrsTsStreamAudioAAC) {
audio_stream_type_ = stream_type;
audio_elementary_stream_id_ = elementary_stream_id;
audio_elementary_stream_info_length_ = elementary_stream_info_length;
} else {
srs_trace("PS: Ignore stream_type=%#x, es_id=%d, es_info=%d", stream_type, elementary_stream_id, elementary_stream_info_length);
}
}
// The last CRC32.
if (!stream->require(4)) {
return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
}
CRC_32_ = stream->read_4bytes();
return err;
}

View file

@ -0,0 +1,314 @@
//
// Copyright (c) 2013-2022 The SRS Authors
//
// SPDX-License-Identifier: MIT or MulanPSL-2.0
//
#ifndef SRS_KERNEL_PS_HPP
#define SRS_KERNEL_PS_HPP
#include <srs_core.hpp>
#include <srs_kernel_ts.hpp>
class SrsPsPacket;
class SrsPsContext;
// The helper for PS decoding.
struct SrsPsDecodeHelper
{
public:
// For debugging to get the RTP packet source. Not used in context.
uint16_t rtp_seq_;
// For debugging to get the RTP packet timestamp. Not used in context.
uint32_t rtp_ts_;
// For debugging to get the RTP packet payload type. Not used in context.
uint8_t rtp_pt_;
public:
// For debugging, current pack id. Not used in context.
uint32_t pack_id_;
// For debugging, the first sequence of current pack. Not used in context.
uint16_t pack_first_seq_;
// For debugging, the last sequence of previous message in current pack. Not used in context.
uint16_t pack_pre_msg_last_seq_;
// For debugging, the number of messages in pack. Not used in context.
uint16_t pack_nn_msgs_;
public:
// The PS context for decoding.
SrsPsContext* ctx_;
// The PS packet for decoding.
SrsPsPacket* ps_;
public:
SrsPsDecodeHelper();
};
// The PS message handler.
class ISrsPsMessageHandler : public ISrsTsHandler
{
public:
ISrsPsMessageHandler();
virtual ~ISrsPsMessageHandler();
public:
// When enter recover mode, user should drop all messages in pack. The nn_recover indicates the number of retry
// during recovery, that is 0 for the first time, and 1 for the second time.
virtual void on_recover_mode(int nn_recover) = 0;
};
// The PS context, to process PS PES stream.
class SrsPsContext
{
public:
SrsPsDecodeHelper helper_;
private:
// The last decoding PS(TS) message.
SrsTsMessage* last_;
// The current parsing PS packet context.
SrsPsPacket* current_;
// Whether detect PS packet header integrity.
bool detect_ps_integrity_;
public:
SrsPsContext();
virtual ~SrsPsContext();
public:
// Set whether detecting PS header integrity.
void set_detect_ps_integrity(bool v);
// Get the last PS(TS) message. Create one if not exists.
SrsTsMessage* last();
// Reap the last message and create a fresh one.
SrsTsMessage* reap();
public:
// Feed with ts packets, decode as ts message, callback handler if got one ts message.
// A ts video message can be decoded to NALUs by SrsRawH264Stream::annexb_demux.
// A ts audio message can be decoded to RAW frame by SrsRawAacStream::adts_demux.
// @param handler The ts message handler to process the msg.
// @remark We will consume all bytes in stream.
virtual srs_error_t decode(SrsBuffer* stream, ISrsPsMessageHandler* handler);
private:
srs_error_t do_decode(SrsBuffer* stream, ISrsPsMessageHandler* handler);
};
// The packet in ps stream.
// 2.5.3.3 Pack layer of Program Stream, hls-mpeg-ts-iso13818-1.pdf, page 73
class SrsPsPacket
{
public:
SrsPsContext* context_;
public:
// The global ID of pack header. The low 16 bits is reserved for seq. Automatically generate the high bits in
// constructor.
uint32_t id_;
// Whether the PS pack stream has pack and system header.
bool has_pack_header_;
bool has_system_header_;
// Table 2-33 Program Stream pack header, hls-mpeg-ts-iso13818-1.pdf, page 73
public:
// 4B
// The pack_start_code is the bit string '0000 0000 0000 0000 0000 0001 1011 1010' (0x000001BA). It identifies the
// beginning of a pack.
uint32_t pack_start_code_; // 32bits
// 6B
// 2bits const '01'
// 3bits system_clock_reference_base [32..30]
// 1bit marker_bit '1'
// 15bits system_clock_reference_base [29..15]
// 1bit marker_bit '1'
// 22bits system_clock_reference_base [14..0]
// 1bit marker_bit '1'
// 9bits system_clock_reference_extension
// 1bit marker_bit '1'
// The system clock reference (SCR) is a 42-bit field coded in two parts. The first part,
// system_clock_reference_base, is a 33-bit field whose value is given by SCR_base(i) as given in equation 2-19.
// The second part, system_clock_reference_extension, is a 9-bit field whose value is given by SCR_ext(i), as given
// in equation 2-20. The SCR indicates the intended time of arrival of the byte containing the last bit of the
// system_clock_reference_base at the input of the program target decoder.
uint64_t system_clock_reference_base_; // 32bits
uint16_t system_clock_reference_extension_; // 9bits
// 3B
// 22bits program_mux_rate
// 1bit marker_bit '1'
// 1bit marker_bit '1'
// This is a 22-bit integer specifying the rate at which the P-STD receives the Program Stream during the pack in
// which it is included. The value of program_mux_rate is measured in units of 50 bytes/second. The value 0 is
// forbidden. The value represented in program_mux_rate is used to define the time of arrival of bytes at the input
// to the P-STD in 2.5.2. The value encoded in the program_mux_rate field may vary from pack to pack in an ITU-T
// Rec. H.222.0 | ISO/IEC 13818-1 program multiplexed stream.
uint32_t program_mux_rate_; // 22bits
// 1B
// 5bits reserved
// 3bits pack_stuffing_length
// A 3-bit integer specifying the number of stuffing bytes which follow this field.
uint8_t pack_stuffing_length_; // 3bits
// Table 2-34 Program Stream system header, hls-mpeg-ts-iso13818-1.pdf, page 74
public:
// 4B
// The system_header_start_code is the bit string '0000 0000 0000 0000 0000 0001 1011 1011' (0x000001BB). It
// identifies the beginning of a system header.
uint32_t system_header_start_code_;
// 2B
// This 16-bit field indicates the length in bytes of the system header following the header_length field. Future
// extensions of this Specification may extend the system header.
uint16_t header_length_;
// 3B
// 1bit marker_bit '1'
// 22bits rate_bound
// 1bit marker_bit '1'
// A 22-bit field. The rate_bound is an integer value greater than or equal to the maximum value of the
// program_mux_rate field coded in any pack of the Program Stream. It may be used by a decoder to assess whether it
// is capable of decoding the entire stream.
uint32_t rate_bound_;
// 1B
// 6bits audio_bound
// 1bit fixed_flag
// 1bit CSPS_flag
// A 6-bit field. The audio_bound is an integer in the inclusive range from 0 to 32 and is set to a value greater
// than or equal to the maximum number of ISO/IEC 13818-3 and ISO/IEC 11172-3 audio streams in the Program Stream
// for which the decoding processes are simultaneously active. For the purpose of this subclause, the decoding
// process of an ISO/IEC 13818-3 or ISO/IEC 11172-3 audio stream is active if the STD buffer is not empty or if a
// Presentation Unit is being presented in the P-STD model.
uint8_t audio_bound_;
// The CSPS_flag is a 1-bit field. If its value is set to '1' the Program Stream meets the constraints defined in
// 2.7.9.
uint8_t CSPS_flag_;
// 1B
// 1bit system_audio_lock_flag
// 1bit system_video_lock_flag
// 1bit marker_bit '1'
// 5bits video_bound
// The system_audio_lock_flag is a 1-bit field indicating that there is a specified, constant rational relationship
// between the audio sampling rate and the system_clock_frequency in the system target decoder. The
// system_clock_frequency is defined in 2.5.2.1 and the audio sampling rate is specified in ISO/IEC 13818-3. The
// system_audio_lock_flag may only be set to '1' if, for all presentation units in all audio elementary streams in
// the Program Stream, the ratio of system_clock_frequency to the actual audio sampling rate, SCASR, is constant and
// equal to the value indicated in the following table at the nominal sampling rate indicated in the audio stream.
uint8_t system_audio_lock_flag_;
// The system_video_lock_flag is a 1-bit field indicating that there is a specified, constant rational relationship
// between the video frame rate and the system clock frequency in the system target decoder. Subclause 2.5.2.1
// defines system_clock_frequency and the video frame rate is specified in ITU-T Rec. H.262 | ISO/IEC 13818-2. The
// system_video_lock_flag may only be set to '1' if, for all presentation units in all video elementary streams in
// the ITU-T Rec. H.222.0 | ISO/IEC 13818-1 program, the ratio of system_clock_frequency to the actual video frame
// rate, SCFR, is constant and equal to the value indicated in the following table at the nominal frame rate
// indicated in the video stream.
uint8_t system_video_lock_flag_;
// The video_bound is a 5-bit integer in the inclusive range from 0 to 16 and is set to a value greater than or
// equal to the maximum number of ITU-T Rec. H.262 | ISO/IEC 13818-2 and ISO/IEC 11172-2 streams in the Program
// Stream of which the decoding processes are simultaneously active. For the purpose of this subclause, the decoding
// process of an ITU-T Rec. H.262 | ISO/IEC 13818-2 and ISO/IEC 11172-2 video stream is active if the P-STD buffer
// is not empty, or if a Presentation Unit is being presented in the P-STD model, or if the reorder buffer is not
// empty.
uint8_t video_bound_;
// 1B
// 1bit packet_rate_restriction_flag
// 5bits reserved_bits
// The packet_rate_restriction_flag is a 1-bit flag. If the CSPS flag is set to '1', the
// packet_rate_restriction_flag indicates which constraint is applicable to the packet rate, as specified in 2.7.9.
// If the CSPS flag is set to value of '0', then the meaning of the packet_rate_restriction_flag is undefined.
uint8_t packet_rate_restriction_flag_;
// 3B
// 8bits stream_id
// 2bits fixed '11'
// 1bit buffer_bound_scale
// 13bits buffer_size_bound
// Has some audio or video stream, by the next bit is 1.
// Note that we ignore other streams except audio and video.
//
// The stream_id is an 8-bit field that indicates the coding and elementary stream number of the stream to which the
// following P-STD_buffer_bound_scale and P-STD_buffer_size_bound fields refer.
// If stream_id equals '1011 1000' the P-STD_buffer_bound_scale and P-STD_buffer_size_bound fields following the
// stream_id refer to all audio streams in the Program Stream.
// If stream_id equals '1011 1001' the P-STD_buffer_bound_scale and P-STD_buffer_size_bound fields following the
// stream_id refer to all video streams in the Program Stream.
// If the stream_id takes on any other value it shall be a byte value greater than or equal to '1011 1100' and shall
// be interpreted as referring to the stream coding and elementary stream number according to Table 2-18.
//
uint8_t audio_stream_id_;
uint8_t audio_buffer_bound_scale_;
uint16_t audio_buffer_size_bound_;
//
uint8_t video_stream_id_;
uint8_t video_buffer_bound_scale_;
uint16_t video_buffer_size_bound_;
public:
SrsPsPacket(SrsPsContext* context);
virtual ~SrsPsPacket();
public:
virtual srs_error_t decode(SrsBuffer* stream);
private:
virtual srs_error_t decode_pack(SrsBuffer* stream);
virtual srs_error_t decode_system(SrsBuffer* stream);
};
// The Program Stream Map (PSM) provides a description of the elementary streams in the Program Stream and their
// relationship to one another.
// 2.5.4 Program Stream map, hls-mpeg-ts-iso13818-1.pdf, page 77
class SrsPsPsmPacket
{
public:
// 2B
// 1bit current_next_indicator
// 2bits reserved
// 5bits program_stream_map_version
// 7bits reserved
// 1bit marker_bit '1'
// This is a 1-bit field, when set to '1' indicates that the Program Stream Map sent is currently applicable. When
// the bit is set to '0', it indicates that the Program Stream Map sent is not yet applicable and shall be the next
// table to become valid.
uint8_t current_next_indicator_;
// This 5-bit field is the version number of the whole Program Stream Map. The version number shall be incremented
// by 1 modulo 32 whenever the definition of the Program Stream Map changes. When the current_next_indicator is set
// to '1', then the program_stream_map_version shall be that of the currently applicable Program Stream Map. When
// the current_next_indicator is set to '0', then the program_stream_map_version shall be that of the next
// applicable Program Stream Map.
uint8_t program_stream_map_version_;
// 2B + [program_stream_info_length_]B
// The program_stream_info_length is a 16-bit field indicating the total length of the descriptors immediately
// following this field.
uint16_t program_stream_info_length_;
// 2B
// This is a 16-bit field specifying the total length, in bytes, of all elementary stream information in this
// program stream map. It includes the stream_type, elementary_stream_id, and elementary_stream_info_length fields.
uint16_t elementary_stream_map_length_;
// 4B + [elementary_stream_info_length_]B
// 8bits stream_type
// 8bits elementary_stream_id
// 16bits elementary_stream_info_length
// This 8-bit field specifies the type of the stream according to Table 2-29. The stream_type field shall only
// identify elementary streams contained in PES packets. A value of 0x05 is prohibited.
// The elementary_stream_id is an 8-bit field indicating the value of the stream_id field in the PES packet headers
// of PES packets in which this elementary stream is stored.
// The elementary_stream_info_length is a 16-bit field indicating the length in bytes of the descriptors immediately
// following this field.
// Definition for the descriptor() fields may be found in 2.6.
//
uint8_t video_stream_type_;
uint8_t video_elementary_stream_id_;
uint16_t video_elementary_stream_info_length_;
//
uint8_t audio_stream_type_;
uint8_t audio_elementary_stream_id_;
uint16_t audio_elementary_stream_info_length_;
// 4B
// This is a 32-bit field that contains the CRC value that gives a zero output of the registers in the decoder
// defined in Annex A after processing the entire program stream map.
uint32_t CRC_32_;
public:
SrsPsPsmPacket();
virtual ~SrsPsPsmPacket();
public:
virtual srs_error_t decode(SrsBuffer* stream);
};
#endif

View file

@ -68,6 +68,7 @@ SrsTsMessage::SrsTsMessage(SrsTsChannel* c, SrsTsPacket* p)
{
channel = c;
packet = p;
ps_helper_ = NULL;
dts = pts = 0;
sid = (SrsTsPESStreamId)0x00;
@ -108,7 +109,7 @@ srs_error_t SrsTsMessage::dump(SrsBuffer* stream, int* pnb_bytes)
stream->skip(nb_bytes);
}
*pnb_bytes = nb_bytes;
if (pnb_bytes) *pnb_bytes = nb_bytes;
return err;
}
@ -123,7 +124,9 @@ bool SrsTsMessage::completed(int8_t payload_unit_start_indicator)
bool SrsTsMessage::fresh()
{
return payload->length() == 0;
// Note that both must be 0. For PS stream, the payload might be empty but PES_packet_length is not, see
// PsPacketDecodePrivateStream of KernelPSTest. For TS stream, both should be 0 in the same time.
return PES_packet_length == 0 && payload->length() == 0;
}
bool SrsTsMessage::is_audio()
@ -1566,6 +1569,11 @@ srs_error_t SrsMpegPES::decode(SrsBuffer* stream)
// PES_packet_data_byte
// }
// For PS, the PES packet should never be empty, because there is no continuity for PS packet.
if (PES_packet_length <= 0) {
return srs_error_new(ERROR_GB_PS_PSE, "ts: Invalid PES_packet_length=%d for PS", PES_packet_length);
}
// The pos_packet equals to stream pos, so the PES_packet_length is actually the payload length.
nb_payload_ = PES_packet_length;
has_payload_ = true;

View file

@ -219,6 +219,8 @@ public:
// For decoder only, the ts message does not use them, for user to get the channel and packet.
SrsTsChannel* channel;
SrsTsPacket* packet;
// For decoder only, the ts message does not use them, to get the RTP packet source.
void* ps_helper_;
public:
// The audio cache buffer start pts, to flush audio if full.
// @remark the pts is not the adjust one, it's the orignal pts.
@ -228,6 +230,9 @@ public:
bool write_pcr;
// Whether got discontinuity ts, for example, sequence header changed.
bool is_discontinuity;
public:
// The chunk id of TS packet.
uint8_t continuity_counter;
public:
// The timestamp in 90khz
int64_t dts;
@ -237,8 +242,6 @@ public:
SrsTsPESStreamId sid;
// The size of payload, 0 indicates the length() of payload.
uint16_t PES_packet_length;
// The chunk id.
uint8_t continuity_counter;
// The payload bytes.
SrsSimpleStream* payload;
public:

View file

@ -2857,46 +2857,6 @@ VOID TEST(ConfigMainTest, CheckStreamCaster)
EXPECT_EQ(8080, conf.get_stream_caster_listen(arr.at(0)));
}
if (true) {
MockSrsConfig conf;
HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;"));
vector<SrsConfDirective*> arr = conf.get_stream_casters();
ASSERT_EQ(1, (int)arr.size());
EXPECT_EQ(0, (int)conf.get_stream_caster_rtp_port_min(arr.at(0)));
}
if (true) {
MockSrsConfig conf;
HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {rtp_port_min 8080;}"));
vector<SrsConfDirective*> arr = conf.get_stream_casters();
ASSERT_EQ(1, (int)arr.size());
EXPECT_EQ(8080, conf.get_stream_caster_rtp_port_min(arr.at(0)));
}
if (true) {
MockSrsConfig conf;
HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;"));
vector<SrsConfDirective*> arr = conf.get_stream_casters();
ASSERT_EQ(1, (int)arr.size());
EXPECT_EQ(0, (int)conf.get_stream_caster_rtp_port_max(arr.at(0)));
}
if (true) {
MockSrsConfig conf;
HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {rtp_port_max 8080;}"));
vector<SrsConfDirective*> arr = conf.get_stream_casters();
ASSERT_EQ(1, (int)arr.size());
EXPECT_EQ(8080, conf.get_stream_caster_rtp_port_max(arr.at(0)));
}
}
VOID TEST(ConfigMainTest, CheckVhostConfig2)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
//
// Copyright (c) 2013-2022 The SRS Authors
//
// SPDX-License-Identifier: MIT or MulanPSL-2.0
//
#ifndef SRS_UTEST_GB28181_HPP
#define SRS_UTEST_GB28181_HPP
/*
#include <srs_utest_gb28181.hpp>
*/
#include <srs_utest_http.hpp>
#endif

View file

@ -377,6 +377,40 @@ srs_error_t MockTsHandler::on_ts_message(SrsTsMessage* m)
return srs_success;
}
MockPsHandler::MockPsHandler()
{
}
MockPsHandler::~MockPsHandler()
{
clear();
}
srs_error_t MockPsHandler::on_ts_message(SrsTsMessage* m)
{
msgs_.push_back(m->detach());
return srs_success;
}
void MockPsHandler::on_recover_mode(int nn_recover)
{
}
void MockPsHandler::on_recover_done(srs_utime_t duration)
{
}
MockPsHandler* MockPsHandler::clear()
{
for (vector<SrsTsMessage*>::iterator it = msgs_.begin(); it != msgs_.end(); ++it) {
SrsTsMessage* msg = *it;
srs_freep(msg);
}
msgs_.clear();
return this;
}
void mock_print_err(srs_error_t err)
{
fprintf(stderr, "err %s\n", srs_error_desc(err).c_str());

View file

@ -13,10 +13,13 @@
#include <srs_utest.hpp>
#include <string>
#include <vector>
#include <srs_kernel_file.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_protocol_stream.hpp>
#include <srs_kernel_ts.hpp>
#include <srs_kernel_ps.hpp>
#include <srs_kernel_stream.hpp>
class MockSrsFile
@ -129,5 +132,19 @@ public:
virtual srs_error_t on_ts_message(SrsTsMessage* m);
};
class MockPsHandler : public ISrsPsMessageHandler
{
public:
std::vector<SrsTsMessage*> msgs_;
public:
MockPsHandler();
virtual ~MockPsHandler();
public:
virtual srs_error_t on_ts_message(SrsTsMessage* m);
virtual void on_recover_mode(int nn_recover);
virtual void on_recover_done(srs_utime_t duration);
MockPsHandler* clear();
};
#endif

View file

@ -0,0 +1,315 @@
//
// Copyright (c) 2013-2022 The SRS Authors
//
// SPDX-License-Identifier: MIT or MulanPSL-2.0
//
#include <srs_utest_kernel2.hpp>
#include <srs_kernel_ps.hpp>
#include <srs_kernel_error.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_rtc_rtcp.hpp>
#include <srs_app_gb28181.hpp>
VOID TEST(KernelPSTest, PsPacketDecodeNormal)
{
srs_error_t err = srs_success;
MockPsHandler handler;
SrsPsContext context;
// Payload of GB28181 camera PS stream, the first packet:
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=0, Time=0
if (true) {
SrsRtpPacket rtp;
if (true) {
string raw = string(
"\x80\x60\x00\x00\x00\x00\x00\x00\x0b\xeb\xdf\xa1\x00\x00\x01\xba" \
"\x44\x68\x6e\x4c\x94\x01\x01\x30\x13\xfe\xff\xff\x00\x00\xa0\x05" \
"\x00\x00\x01\xbb\x00\x12\x80\x98\x09\x04\xe1\x7f\xe0\xe0\x80\xc0" \
"\xc0\x08\xbd\xe0\x80\xbf\xe0\x80\x00\x00\x01\xbc\x00\x5e\xfc\xff" \
"\x00\x24\x40\x0e\x48\x4b\x01\x00\x16\x9b\xa5\x22\x2e\xf7\x00\xff" \
"\xff\xff\x41\x12\x48\x4b\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09" \
"\x0a\x0b\x0c\x0d\x0e\x0f\x00\x30\x1b\xe0\x00\x1c\x42\x0e\x07\x10" \
"\x10\xea\x02\x80\x01\xe0\x11\x30\x00\x00\x1c\x20\x2a\x0a\x7f\xff" \
"\x00\x00\x07\x08\x1f\xfe\x50\x3c\x0f\xc0\x00\x0c\x43\x0a\x00\x90" \
"\xfe\x02\xb1\x13\x01\xf4\x03\xff\xcb\x85\x54\xb9\x00\x00\x01\xe0" \
"\x00\x26\x8c\x80\x07\x21\x1a\x1b\x93\x25\xff\xfc\x00\x00\x00\x01" \
"\x67\x4d\x00\x1e\x9d\xa8\x28\x0f\x69\xb8\x08\x08\x0a\x00\x00\x03" \
"\x00\x02\x00\x00\x03\x00\x65\x08\x00\x00\x01\xe0\x00\x0e\x8c\x00" \
"\x03\xff\xff\xfc\x00\x00\x00\x01\x68\xee\x3c\x80\x00\x00\x01\xe0" \
"\x00\x0e\x8c\x00\x02\xff\xfc\x00\x00\x00\x01\x06\xe5\x01\xba\x80" \
"\x00\x00\x01\xe0\x35\x62\x8c\x00\x02\xff\xf8\x00\x00\x00\x01\x65", 256) + string(1156, 'x');
SrsBuffer b((char*)raw.data(), raw.length());
HELPER_ASSERT_SUCCESS(rtp.decode(&b));
}
SrsRtpRawPayload* rtp_raw = dynamic_cast<SrsRtpRawPayload*>(rtp.payload());
SrsBuffer b((char*)rtp_raw->payload, rtp_raw->nn_payload);
// There should be three video messages.
HELPER_ASSERT_SUCCESS(context.decode(&b, &handler));
EXPECT_EQ(3, handler.msgs_.size());
}
// We use the first packet bytes to mock the RTP packet seq 1 to 8.
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=1, Time=0
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=2, Time=0
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=3, Time=0
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=4, Time=0
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=5, Time=0
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=6, Time=0
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=7, Time=0
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=8, Time=0
for (int i = 0; i < 8; i++) {
SrsRtpPacket rtp;
if (true) {
string raw = string("\x80\x60\x00\x01\x00\x00\x00\x00\x0b\xeb\xdf\xa1", 12) + string(1400, 'x');
SrsBuffer b((char*)raw.data(), raw.length());
HELPER_ASSERT_SUCCESS(rtp.decode(&b));
}
SrsRtpRawPayload* rtp_raw = dynamic_cast<SrsRtpRawPayload*>(rtp.payload());
SrsBuffer b((char*)rtp_raw->payload, rtp_raw->nn_payload);
// Bytes continuity for the large video frame, got nothing message yet.
HELPER_ASSERT_SUCCESS(context.decode(&b, handler.clear()));
EXPECT_EQ(0, handler.msgs_.size());
}
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=9, Time=0
if (true) {
SrsRtpPacket rtp;
if (true) {
string raw = string("\x80\x60\x00\x09\x00\x00\x00\x00\x0b\xeb\xdf\xa1", 12) + string(1300, 'x')
+ string("\x00\x00\x01\xbd\x00\x6a\x8c\x80\x07\x21\x1a\x1b\x93\x25\xff\xf8" \
"\x00\x02\x00\x17\x00\x01\x80\x00\x00\xff\xa0\x05\xe0\xf1\xf0\x50" \
"\x18\x52\xd6\x5c\xa2\x78\x90\x23\xf9\xf6\x64\xba\xc7\x90\x5e\xd3" \
"\x80\x2f\x29\xad\x06\xee\x14\x62\xec\x6f\x77\xaa\x71\x80\xb3\x50" \
"\xb8\xd1\x85\x7f\x44\x30\x4f\x44\xfd\xcd\x21\xe6\x55\x36\x08\x6c" \
"\xb8\xd1\x85\x7f\x44\x30\x4f\x44\xfd\xcd\x21\xe6\x55\x36\x08\x6c" \
"\xc9\xf6\x5c\x74", 100);
SrsBuffer b((char*)raw.data(), raw.length());
HELPER_ASSERT_SUCCESS(rtp.decode(&b));
}
SrsRtpRawPayload* rtp_raw = dynamic_cast<SrsRtpRawPayload*>(rtp.payload());
SrsBuffer b((char*)rtp_raw->payload, rtp_raw->nn_payload);
// There should be one large video message, might be an I frame.
HELPER_ASSERT_SUCCESS(context.decode(&b, handler.clear()));
EXPECT_EQ(1, handler.msgs_.size());
}
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=10, Time=0
if (true) {
SrsRtpPacket rtp;
if (true) {
string raw("\x80\x60\x00\x0a\x00\x00\x00\x00\x0b\xeb\xdf\xa1" \
"\x57\xb3\xa3\xbc\x16\x2c\x3c\x9e\x69\x89\x48\xa4", 24);
SrsBuffer b((char*)raw.data(), raw.length());
HELPER_ASSERT_SUCCESS(rtp.decode(&b));
}
SrsRtpRawPayload* rtp_raw = dynamic_cast<SrsRtpRawPayload*>(rtp.payload());
SrsBuffer b((char*)rtp_raw->payload, rtp_raw->nn_payload);
// There should be a message of private stream, we ignore it, so we won't get it in callback.
HELPER_ASSERT_SUCCESS(context.decode(&b, handler.clear()));
EXPECT_EQ(0, handler.msgs_.size());
}
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=11, Time=3600
if (true) {
SrsRtpPacket rtp;
if (true) {
string raw = string("\x80\x60\x00\x0b\x00\x00\x0e\x10\x0b\xeb\xdf\xa1", 12)
+ string("\x00\x00\x01\xc0" \
"\x00\x82\x8c\x80\x09\x21\x1a\x1b\xa3\x51\xff\xff\xff\xf8\xff\xf9" \
"\x50\x40\x0e\xdf\xfc\x01\x2c\x2e\x84\x28\x23\x0a\x85\x82\xa2\x40" \
"\x90\x50\x2c\x14\x0b\x05\x42\x41\x30\x90\x44\x28\x16\x08\x84\x82", 52)
+ string(84, 'x');
SrsBuffer b((char*)raw.data(), raw.length());
HELPER_ASSERT_SUCCESS(rtp.decode(&b));
}
SrsRtpRawPayload* rtp_raw = dynamic_cast<SrsRtpRawPayload*>(rtp.payload());
SrsBuffer b((char*)rtp_raw->payload, rtp_raw->nn_payload);
// There should be one audio message.
HELPER_ASSERT_SUCCESS(context.decode(&b, handler.clear()));
EXPECT_EQ(1, handler.msgs_.size());
}
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=12, Time=3600
if (true) {
SrsRtpPacket rtp;
if (true) {
string raw = string("\x80\x60\x00\x0c\x00\x00\x0e\x10\x0b\xeb\xdf\xa1", 12)
+ string("\x00\x00\x01\xc0" \
"\x00\x8a\x8c\x80\x09\x21\x1a\x1b\xb3\x7d\xff\xff\xff\xf8\xff\xf9" \
"\x50\x40\x0f\xdf\xfc\x01\x2c\x2e\x88\x2a\x13\x0a\x09\x82\x41\x10" \
"\x90\x58\x26\x14\x13\x05\x02\xc2\x10\xa0\x58\x4a\x14\x0a\x85\x02", 52)
+ string(92, 'x');
SrsBuffer b((char*)raw.data(), raw.length());
HELPER_ASSERT_SUCCESS(rtp.decode(&b));
}
SrsRtpRawPayload* rtp_raw = dynamic_cast<SrsRtpRawPayload*>(rtp.payload());
SrsBuffer b((char*)rtp_raw->payload, rtp_raw->nn_payload);
// There should be another audio message.
HELPER_ASSERT_SUCCESS(context.decode(&b, handler.clear()));
EXPECT_EQ(1, handler.msgs_.size());
}
// PT=DynamicRTP-Type-96, SSRC=0xBEBDFA1, Seq=13, Time=3600
if (true) {
SrsRtpPacket rtp;
if (true) {
string raw = string("\x80\x60\x00\x0d\x00\x00\x0e\x10\x0b\xeb\xdf\xa1", 12)
+ string("\x00\x00\x01\xba" \
"\x44\x68\x6e\xbd\x14\x01\x01\x30\x13\xfe\xff\xff\x00\x00\xa0\x06" \
"\x00\x00\x01\xe0\x03\x4a\x8c\x80\x08\x21\x1a\x1b\xaf\x45\xff\xff" \
"\xf8\x00\x00\x00\x01\x61\xe0\x08\xbf\x3c\xb6\x63\x68\x4b\x7f\xea", 52)
+ string(816, 'x');
SrsBuffer b((char*)raw.data(), raw.length());
HELPER_ASSERT_SUCCESS(rtp.decode(&b));
}
SrsRtpRawPayload* rtp_raw = dynamic_cast<SrsRtpRawPayload*>(rtp.payload());
SrsBuffer b((char*)rtp_raw->payload, rtp_raw->nn_payload);
// There should be another audio message.
HELPER_ASSERT_SUCCESS(context.decode(&b, handler.clear()));
EXPECT_EQ(1, handler.msgs_.size());
}
}
VOID TEST(KernelPSTest, PsPacketHeaderClockDecode)
{
srs_error_t err = srs_success;
SrsPsContext context;
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x44\x68\x6e\x4c\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x00686c992, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x64\x68\x6e\x4c\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x10686c992, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\x68\x6e\x4c\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18686c992, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe8\x6e\x4c\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e86c992, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\x6e\x4c\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e96c992, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\x4c\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9ec992, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xcc\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9ed992, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xdc\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9edb92, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xdd\x94\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9edbb2, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xdd\x9c\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9edbb3, pkt.system_clock_reference_base_);
EXPECT_EQ(0, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xdd\x9e\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9edbb3, pkt.system_clock_reference_base_);
EXPECT_EQ(0x100, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xdd\x9f\x01"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9edbb3, pkt.system_clock_reference_base_);
EXPECT_EQ(0x180, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xdd\x9f\x11"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9edbb3, pkt.system_clock_reference_base_);
EXPECT_EQ(0x188, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xdd\x9f\x19"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9edbb3, pkt.system_clock_reference_base_);
EXPECT_EQ(0x18c, pkt.system_clock_reference_extension_);
}
if(true) {
SrsPsPacket pkt(&context);
SrsBuffer b((char*)"\x00\x00\x01\xba"/*start*/ "\x74\xe9\xee\xdd\x9f\x1b"/*clock*/ "\x01\x30\x13\xf8"/*mux*/, 14);
HELPER_ASSERT_SUCCESS(pkt.decode(&b));
EXPECT_EQ(0x18e9edbb3, pkt.system_clock_reference_base_);
EXPECT_EQ(0x18d, pkt.system_clock_reference_extension_);
}
}

View file

@ -0,0 +1,18 @@
//
// Copyright (c) 2013-2022 The SRS Authors
//
// SPDX-License-Identifier: MIT or MulanPSL-2.0
//
#ifndef SRS_UTEST_KERNEL2_HPP
#define SRS_UTEST_KERNEL2_HPP
/*
#include <srs_utest_kernel2.hpp>
*/
#include <srs_utest.hpp>
#include <srs_utest_kernel.hpp>
#endif