This PR introduces a major refactoring to replace `SrsSharedPtrMessage` with `SrsMediaPacket` throughout the SRS codebase, providing a more unified and cleaner approach to media packet handling. --------- Co-authored-by: OSSRS-AI <winlinam@gmail.com>
987 lines
28 KiB
C++
987 lines
28 KiB
C++
//
|
|
// Copyright (c) 2013-2025 The SRS Authors
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
|
|
#ifndef SRS_APP_RTC_SOURCE_HPP
|
|
#define SRS_APP_RTC_SOURCE_HPP
|
|
|
|
#include <srs_core.hpp>
|
|
|
|
#include <inttypes.h>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <srs_app_hourglass.hpp>
|
|
#include <srs_app_rtc_sdp.hpp>
|
|
#include <srs_app_stream_bridge.hpp>
|
|
#include <srs_core_autofree.hpp>
|
|
#include <srs_kernel_rtc_rtp.hpp>
|
|
#include <srs_protocol_format.hpp>
|
|
#include <srs_protocol_st.hpp>
|
|
|
|
class ISrsRequest;
|
|
class SrsMetaCache;
|
|
class SrsMediaPacket;
|
|
class SrsCommonMessage;
|
|
class SrsMessageArray;
|
|
class SrsRtcSource;
|
|
class SrsFrameToRtcBridge;
|
|
class SrsAudioTranscoder;
|
|
class SrsRtpPacket;
|
|
class SrsNaluSample;
|
|
class SrsRtcSourceDescription;
|
|
class SrsRtcTrackDescription;
|
|
class SrsRtcConnection;
|
|
class SrsRtpRingBuffer;
|
|
class SrsRtpNackForReceiver;
|
|
class SrsJsonObject;
|
|
class SrsErrorPithyPrint;
|
|
class SrsRtcFrameBuilder;
|
|
class SrsLiveSource;
|
|
class SrsRtpVideoBuilder;
|
|
|
|
// Firefox defaults as 109, Chrome is 111.
|
|
const int kAudioPayloadType = 111;
|
|
// Firefox defaults as 126, Chrome is 102.
|
|
const int kVideoPayloadType = 102;
|
|
// Chrome HEVC defaults as 49.
|
|
const int KVideoPayloadTypeHevc = 49;
|
|
|
|
// Audio jitter buffer size (in packets)
|
|
const int AUDIO_JITTER_BUFFER_SIZE = 100;
|
|
// Sliding window size for continuous processing
|
|
const int SLIDING_WINDOW_SIZE = 10;
|
|
// Maximum waiting time for out-of-order packets (in ms)
|
|
const int MAX_AUDIO_WAIT_MS = 100;
|
|
|
|
class SrsNtp
|
|
{
|
|
public:
|
|
uint64_t system_ms_;
|
|
uint64_t ntp_;
|
|
uint32_t ntp_second_;
|
|
uint32_t ntp_fractions_;
|
|
|
|
public:
|
|
SrsNtp();
|
|
virtual ~SrsNtp();
|
|
|
|
public:
|
|
static SrsNtp from_time_ms(uint64_t ms);
|
|
static SrsNtp to_time_ms(uint64_t ntp);
|
|
|
|
public:
|
|
static uint64_t kMagicNtpFractionalUnit;
|
|
};
|
|
|
|
// When RTC stream publish and re-publish.
|
|
class ISrsRtcSourceChangeCallback
|
|
{
|
|
public:
|
|
ISrsRtcSourceChangeCallback();
|
|
virtual ~ISrsRtcSourceChangeCallback();
|
|
|
|
public:
|
|
virtual void on_stream_change(SrsRtcSourceDescription *desc) = 0;
|
|
};
|
|
|
|
// The RTC stream consumer, consume packets from RTC stream source.
|
|
class SrsRtcConsumer
|
|
{
|
|
private:
|
|
// Because source references to this object, so we should directly use the source ptr.
|
|
SrsRtcSource *source_;
|
|
|
|
private:
|
|
std::vector<SrsRtpPacket *> queue;
|
|
// when source id changed, notice all consumers
|
|
bool should_update_source_id;
|
|
// The cond wait for mw.
|
|
srs_cond_t mw_wait;
|
|
bool mw_waiting;
|
|
int mw_min_msgs;
|
|
|
|
private:
|
|
// The callback for stream change event.
|
|
ISrsRtcSourceChangeCallback *handler_;
|
|
|
|
public:
|
|
SrsRtcConsumer(SrsRtcSource *s);
|
|
virtual ~SrsRtcConsumer();
|
|
|
|
public:
|
|
// When source id changed, notice client to print.
|
|
virtual void update_source_id();
|
|
// Put RTP packet into queue.
|
|
// @note We do not drop packet here, but drop it in sender.
|
|
srs_error_t enqueue(SrsRtpPacket *pkt);
|
|
// For RTC, we only got one packet, because there is not many packets in queue.
|
|
virtual srs_error_t dump_packet(SrsRtpPacket **ppkt);
|
|
// Wait for at-least some messages incoming in queue.
|
|
virtual void wait(int nb_msgs);
|
|
|
|
public:
|
|
void set_handler(ISrsRtcSourceChangeCallback *h) { handler_ = h; } // SrsRtcConsumer::set_handler()
|
|
void on_stream_change(SrsRtcSourceDescription *desc);
|
|
};
|
|
|
|
class SrsRtcSourceManager : public ISrsHourGlass
|
|
{
|
|
private:
|
|
srs_mutex_t lock;
|
|
std::map<std::string, SrsSharedPtr<SrsRtcSource> > pool;
|
|
SrsHourGlass *timer_;
|
|
|
|
public:
|
|
SrsRtcSourceManager();
|
|
virtual ~SrsRtcSourceManager();
|
|
|
|
public:
|
|
virtual srs_error_t initialize();
|
|
// interface ISrsHourGlass
|
|
private:
|
|
virtual srs_error_t setup_ticks();
|
|
virtual srs_error_t notify(int event, srs_utime_t interval, srs_utime_t tick);
|
|
|
|
public:
|
|
// create source when fetch from cache failed.
|
|
// @param r the client request.
|
|
// @param pps the matched source, if success never be NULL.
|
|
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsRtcSource> &pps);
|
|
|
|
public:
|
|
// Get the exists source, NULL when not exists.
|
|
virtual SrsSharedPtr<SrsRtcSource> fetch(ISrsRequest *r);
|
|
};
|
|
|
|
// Global singleton instance.
|
|
extern SrsRtcSourceManager *_srs_rtc_sources;
|
|
|
|
// A publish stream interface, for source to callback with.
|
|
class ISrsRtcPublishStream
|
|
{
|
|
public:
|
|
ISrsRtcPublishStream();
|
|
virtual ~ISrsRtcPublishStream();
|
|
|
|
public:
|
|
// Request keyframe(PLI) from publisher, for fresh consumer.
|
|
virtual void request_keyframe(uint32_t ssrc, SrsContextId cid) = 0;
|
|
// Get context id.
|
|
virtual const SrsContextId &context_id() = 0;
|
|
};
|
|
|
|
class ISrsRtcSourceEventHandler
|
|
{
|
|
public:
|
|
ISrsRtcSourceEventHandler();
|
|
virtual ~ISrsRtcSourceEventHandler();
|
|
|
|
public:
|
|
// stream unpublish, sync API.
|
|
virtual void on_unpublish() = 0;
|
|
// no player subscribe this stream, sync API
|
|
virtual void on_consumers_finished() = 0;
|
|
};
|
|
|
|
// A Source is a stream, to publish and to play with, binding to SrsRtcPublishStream and SrsRtcPlayStream.
|
|
class SrsRtcSource : public ISrsFastTimer
|
|
{
|
|
private:
|
|
// For publish, it's the publish client id.
|
|
// For edge, it's the edge ingest id.
|
|
// when source id changed, for example, the edge reconnect,
|
|
// invoke the on_source_changed() to let all clients know.
|
|
SrsContextId _source_id;
|
|
// previous source id.
|
|
SrsContextId _pre_source_id;
|
|
ISrsRequest *req;
|
|
ISrsRtcPublishStream *publish_stream_;
|
|
// Steam description for this steam.
|
|
SrsRtcSourceDescription *stream_desc_;
|
|
|
|
private:
|
|
#ifdef SRS_FFMPEG_FIT
|
|
// Collect and build WebRTC RTP packets to AV frames.
|
|
SrsRtcFrameBuilder *frame_builder_;
|
|
#endif
|
|
// The Source bridge, bridge stream to other source.
|
|
ISrsStreamBridge *bridge_;
|
|
|
|
private:
|
|
// To delivery stream to clients.
|
|
std::vector<SrsRtcConsumer *> consumers;
|
|
// Whether stream is created, that is, SDP is done.
|
|
bool is_created_;
|
|
// Whether stream is delivering data, that is, DTLS is done.
|
|
bool is_delivering_packets_;
|
|
// Notify stream event to event handler
|
|
std::vector<ISrsRtcSourceEventHandler *> event_handlers_;
|
|
|
|
private:
|
|
// The PLI for RTC2RTMP.
|
|
srs_utime_t pli_for_rtmp_;
|
|
srs_utime_t pli_elapsed_;
|
|
|
|
private:
|
|
// The last die time, while die means neither publishers nor players.
|
|
srs_utime_t stream_die_at_;
|
|
|
|
public:
|
|
SrsRtcSource();
|
|
virtual ~SrsRtcSource();
|
|
|
|
public:
|
|
virtual srs_error_t initialize(ISrsRequest *r);
|
|
|
|
public:
|
|
// Whether stream is dead, which is no publisher or player.
|
|
virtual bool stream_is_dead();
|
|
|
|
private:
|
|
void init_for_play_before_publishing();
|
|
|
|
public:
|
|
// Update the authentication information in request.
|
|
virtual void update_auth(ISrsRequest *r);
|
|
|
|
private:
|
|
// The stream source changed.
|
|
virtual srs_error_t on_source_changed();
|
|
|
|
public:
|
|
// Get current source id.
|
|
virtual SrsContextId source_id();
|
|
virtual SrsContextId pre_source_id();
|
|
|
|
public:
|
|
void set_bridge(ISrsStreamBridge *bridge);
|
|
|
|
public:
|
|
// Create consumer
|
|
// @param consumer, output the create consumer.
|
|
virtual srs_error_t create_consumer(SrsRtcConsumer *&consumer);
|
|
// Dumps packets in cache to consumer.
|
|
// @param ds, whether dumps the sequence header.
|
|
// @param dm, whether dumps the metadata.
|
|
// @param dg, whether dumps the gop cache.
|
|
virtual srs_error_t consumer_dumps(SrsRtcConsumer *consumer, bool ds = true, bool dm = true, bool dg = true);
|
|
virtual void on_consumer_destroy(SrsRtcConsumer *consumer);
|
|
// Whether we can publish stream to the source, return false if it exists.
|
|
// @remark Note that when SDP is done, we set the stream is not able to publish.
|
|
virtual bool can_publish();
|
|
// For RTC, the stream is created when SDP is done, and then do DTLS
|
|
virtual void set_stream_created();
|
|
// When start publish stream.
|
|
virtual srs_error_t on_publish();
|
|
// When stop publish stream.
|
|
virtual void on_unpublish();
|
|
|
|
public:
|
|
// For event handler
|
|
void subscribe(ISrsRtcSourceEventHandler *h);
|
|
void unsubscribe(ISrsRtcSourceEventHandler *h);
|
|
|
|
public:
|
|
// Get and set the publisher, passed to consumer to process requests such as PLI.
|
|
ISrsRtcPublishStream *publish_stream();
|
|
void set_publish_stream(ISrsRtcPublishStream *v);
|
|
// Consume the shared RTP packet, user must free it.
|
|
srs_error_t on_rtp(SrsRtpPacket *pkt);
|
|
// Set and get stream description for source
|
|
bool has_stream_desc();
|
|
void set_stream_desc(SrsRtcSourceDescription *stream_desc);
|
|
std::vector<SrsRtcTrackDescription *> get_track_desc(std::string type, std::string media_type);
|
|
// interface ISrsFastTimer
|
|
private:
|
|
srs_error_t on_timer(srs_utime_t interval);
|
|
};
|
|
|
|
#ifdef SRS_FFMPEG_FIT
|
|
|
|
// Convert AV frame to RTC RTP packets.
|
|
class SrsRtcRtpBuilder
|
|
{
|
|
private:
|
|
ISrsRequest *req;
|
|
SrsFrameToRtcBridge *bridge_;
|
|
// The format, codec information.
|
|
SrsRtmpFormat *format;
|
|
// The metadata cache.
|
|
SrsMetaCache *meta;
|
|
// The video builder, convert frame to RTP packets.
|
|
SrsRtpVideoBuilder *video_builder_;
|
|
|
|
private:
|
|
SrsAudioCodecId latest_codec_;
|
|
SrsAudioTranscoder *codec_;
|
|
bool keep_bframe;
|
|
bool keep_avc_nalu_sei;
|
|
bool merge_nalus;
|
|
uint16_t audio_sequence;
|
|
|
|
private:
|
|
uint32_t audio_ssrc_;
|
|
uint8_t audio_payload_type_;
|
|
|
|
private:
|
|
SrsSharedPtr<SrsRtcSource> source_;
|
|
// Lazy initialization flags
|
|
bool audio_initialized_;
|
|
bool video_initialized_;
|
|
|
|
public:
|
|
SrsRtcRtpBuilder(SrsFrameToRtcBridge *bridge, SrsSharedPtr<SrsRtcSource> source);
|
|
virtual ~SrsRtcRtpBuilder();
|
|
|
|
private:
|
|
// Lazy initialization methods
|
|
srs_error_t initialize_audio_track(SrsAudioCodecId codec);
|
|
srs_error_t initialize_video_track(SrsVideoCodecId codec);
|
|
|
|
public:
|
|
virtual srs_error_t initialize(ISrsRequest *r);
|
|
virtual srs_error_t on_publish();
|
|
virtual void on_unpublish();
|
|
virtual srs_error_t on_frame(SrsMediaPacket *frame);
|
|
|
|
private:
|
|
virtual srs_error_t on_audio(SrsMediaPacket *msg);
|
|
|
|
private:
|
|
srs_error_t init_codec(SrsAudioCodecId codec);
|
|
srs_error_t transcode(SrsParsedAudioPacket *audio);
|
|
srs_error_t package_opus(SrsParsedAudioPacket *audio, SrsRtpPacket *pkt);
|
|
|
|
private:
|
|
virtual srs_error_t on_video(SrsMediaPacket *msg);
|
|
|
|
private:
|
|
srs_error_t filter(SrsMediaPacket *msg, SrsFormat *format, bool &has_idr, std::vector<SrsNaluSample *> &samples);
|
|
srs_error_t package_stap_a(SrsMediaPacket *msg, SrsRtpPacket *pkt);
|
|
srs_error_t package_nalus(SrsMediaPacket *msg, const std::vector<SrsNaluSample *> &samples, std::vector<SrsRtpPacket *> &pkts);
|
|
srs_error_t package_single_nalu(SrsMediaPacket *msg, SrsNaluSample *sample, std::vector<SrsRtpPacket *> &pkts);
|
|
srs_error_t package_fu_a(SrsMediaPacket *msg, SrsNaluSample *sample, int fu_payload_size, std::vector<SrsRtpPacket *> &pkts);
|
|
srs_error_t consume_packets(std::vector<SrsRtpPacket *> &pkts);
|
|
};
|
|
|
|
// Video packet cache for RTP packet management
|
|
// TODO: Maybe should use SrsRtpRingBuffer?
|
|
class SrsRtcFrameBuilderVideoPacketCache
|
|
{
|
|
private:
|
|
const static uint16_t cache_size_ = 512;
|
|
struct RtcPacketCache {
|
|
bool in_use;
|
|
uint16_t sn;
|
|
uint32_t ts;
|
|
uint32_t rtp_ts;
|
|
SrsRtpPacket *pkt;
|
|
};
|
|
RtcPacketCache cache_pkts_[cache_size_];
|
|
|
|
public:
|
|
SrsRtcFrameBuilderVideoPacketCache();
|
|
virtual ~SrsRtcFrameBuilderVideoPacketCache();
|
|
|
|
public:
|
|
SrsRtpPacket *get_packet(uint16_t sequence_number);
|
|
void store_packet(SrsRtpPacket *pkt);
|
|
void clear_all();
|
|
SrsRtpPacket *take_packet(uint16_t sequence_number);
|
|
|
|
public:
|
|
// Find next lost sequence number starting from current_sn
|
|
// Returns: lost_sn if found, -1 if complete frame found (sets end_sn), -2 if cache overflow
|
|
int32_t find_next_lost_sn(uint16_t current_sn, uint16_t header_sn, uint16_t &end_sn);
|
|
// Check if frame is complete by verifying FU-A start/end fragment counts match
|
|
bool check_frame_complete(const uint16_t start, const uint16_t end);
|
|
|
|
private:
|
|
bool is_slot_in_use(uint16_t sequence_number);
|
|
uint32_t get_rtp_timestamp(uint16_t sequence_number);
|
|
inline uint16_t cache_index(uint16_t sequence_number)
|
|
{
|
|
return sequence_number % cache_size_;
|
|
}
|
|
};
|
|
|
|
// Video frame detector for managing frame boundaries and packet loss detection
|
|
class SrsRtcFrameBuilderVideoFrameDetector
|
|
{
|
|
private:
|
|
SrsRtcFrameBuilderVideoPacketCache *video_cache_;
|
|
uint16_t header_sn_;
|
|
uint16_t lost_sn_;
|
|
int64_t rtp_key_frame_ts_;
|
|
|
|
public:
|
|
SrsRtcFrameBuilderVideoFrameDetector(SrsRtcFrameBuilderVideoPacketCache *cache);
|
|
virtual ~SrsRtcFrameBuilderVideoFrameDetector();
|
|
|
|
public:
|
|
void on_keyframe_start(SrsRtpPacket *pkt);
|
|
srs_error_t detect_frame(uint16_t received, uint16_t &frame_start, uint16_t &frame_end, bool &frame_ready);
|
|
srs_error_t detect_next_frame(uint16_t next_head, uint16_t &next_start, uint16_t &next_end, bool &next_ready);
|
|
void on_keyframe_detached();
|
|
bool is_lost_sn(uint16_t received);
|
|
};
|
|
|
|
// Audio packet cache for RTP packet jitter buffer management
|
|
class SrsRtcFrameBuilderAudioPacketCache
|
|
{
|
|
private:
|
|
// Audio jitter buffer, map sequence number to packet
|
|
std::map<uint16_t, SrsRtpPacket *> audio_buffer_;
|
|
// Last processed sequence number
|
|
uint16_t last_audio_seq_num_;
|
|
// Last time we processed the jitter buffer
|
|
srs_utime_t last_audio_process_time_;
|
|
// Whether the cache has been initialized
|
|
bool initialized_;
|
|
// Timeout for waiting out-of-order packets (in microseconds)
|
|
srs_utime_t timeout_;
|
|
|
|
public:
|
|
SrsRtcFrameBuilderAudioPacketCache();
|
|
virtual ~SrsRtcFrameBuilderAudioPacketCache();
|
|
|
|
public:
|
|
// Set timeout for waiting out-of-order packets (in microseconds)
|
|
void set_timeout(srs_utime_t timeout);
|
|
// Process audio packet through jitter buffer
|
|
// Returns packets ready for transcoding in order
|
|
srs_error_t process_packet(SrsRtpPacket *src, std::vector<SrsRtpPacket *> &ready_packets);
|
|
// Clear all cached packets
|
|
void clear_all();
|
|
};
|
|
|
|
// Collect and build WebRTC RTP packets to AV frames.
|
|
class SrsRtcFrameBuilder
|
|
{
|
|
private:
|
|
ISrsStreamBridge *bridge_;
|
|
|
|
private:
|
|
bool is_first_audio_;
|
|
SrsAudioTranscoder *audio_transcoder_;
|
|
SrsVideoCodecId video_codec_;
|
|
|
|
private:
|
|
SrsRtcFrameBuilderAudioPacketCache *audio_cache_;
|
|
SrsRtcFrameBuilderVideoPacketCache *video_cache_;
|
|
SrsRtcFrameBuilderVideoFrameDetector *frame_detector_;
|
|
|
|
private:
|
|
// The state for timestamp sync state. -1 for init. 0 not sync. 1 sync.
|
|
int sync_state_;
|
|
|
|
private:
|
|
// For OBS WHIP, send (VPS/)SPS/PPS in dedicated RTP packet.
|
|
SrsRtpPacket *obs_whip_vps_;
|
|
SrsRtpPacket *obs_whip_sps_;
|
|
SrsRtpPacket *obs_whip_pps_;
|
|
|
|
public:
|
|
SrsRtcFrameBuilder(ISrsStreamBridge *bridge);
|
|
virtual ~SrsRtcFrameBuilder();
|
|
|
|
public:
|
|
srs_error_t initialize(ISrsRequest *r, SrsAudioCodecId audio_codec, SrsVideoCodecId video_codec);
|
|
virtual srs_error_t on_publish();
|
|
virtual void on_unpublish();
|
|
virtual srs_error_t on_rtp(SrsRtpPacket *pkt);
|
|
|
|
private:
|
|
srs_error_t packet_audio(SrsRtpPacket *pkt);
|
|
srs_error_t transcode_audio(SrsRtpPacket *pkt);
|
|
void packet_aac(SrsCommonMessage *audio, char *data, int len, uint32_t pts, bool is_header);
|
|
|
|
private:
|
|
srs_error_t packet_video(SrsRtpPacket *pkt);
|
|
srs_error_t packet_video_key_frame(SrsRtpPacket *pkt);
|
|
srs_error_t packet_sequence_header_avc(SrsRtpPacket *pkt);
|
|
srs_error_t do_packet_sequence_header_avc(SrsRtpPacket *pkt, SrsNaluSample *sps, SrsNaluSample *pps);
|
|
srs_error_t packet_sequence_header_hevc(SrsRtpPacket *pkt);
|
|
srs_error_t do_packet_sequence_header_hevc(SrsRtpPacket *pkt, SrsNaluSample *vps, SrsNaluSample *sps, SrsNaluSample *pps);
|
|
|
|
private:
|
|
srs_error_t packet_video_rtmp(const uint16_t start, const uint16_t end);
|
|
int calculate_packet_payload_size(SrsRtpPacket *pkt);
|
|
void write_packet_payload_to_buffer(SrsRtpPacket *pkt, SrsBuffer &payload, int &nalu_len);
|
|
};
|
|
|
|
#endif
|
|
|
|
// TODO: FIXME: Rename it.
|
|
class SrsCodecPayload
|
|
{
|
|
public:
|
|
std::string type_;
|
|
uint8_t pt_;
|
|
// for publish, equals to PT of itself;
|
|
// for subscribe, is the PT of publisher;
|
|
uint8_t pt_of_publisher_;
|
|
std::string name_;
|
|
int sample_;
|
|
|
|
std::vector<std::string> rtcp_fbs_;
|
|
|
|
private:
|
|
// The cached codec ID, corresponding to name_.
|
|
// For video, you can convert it to type SrsVideoCodecId
|
|
// For audio, you can convert it to type SrsAudioCodecId
|
|
// Note: Set up to -1, which means not initialized/cached yet
|
|
// Note: Won't copy codec_, it will be recalculated when codec(bool) is called
|
|
int8_t codec_;
|
|
|
|
public:
|
|
SrsCodecPayload();
|
|
SrsCodecPayload(uint8_t pt, std::string encode_name, int sample);
|
|
virtual ~SrsCodecPayload();
|
|
|
|
public:
|
|
// Get codec ID with context information about whether it's video or audio
|
|
// Returns the numeric codec ID, with caching for performance
|
|
int8_t codec(bool video);
|
|
virtual SrsCodecPayload *copy();
|
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
|
};
|
|
|
|
// TODO: FIXME: Rename it.
|
|
class SrsVideoPayload : public SrsCodecPayload
|
|
{
|
|
public:
|
|
H264SpecificParam h264_param_;
|
|
H265SpecificParam h265_param_;
|
|
|
|
public:
|
|
SrsVideoPayload();
|
|
SrsVideoPayload(uint8_t pt, std::string encode_name, int sample);
|
|
virtual ~SrsVideoPayload();
|
|
|
|
public:
|
|
virtual SrsVideoPayload *copy();
|
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
|
virtual SrsMediaPayloadType generate_media_payload_type_h265();
|
|
|
|
public:
|
|
srs_error_t set_h264_param_desc(std::string fmtp);
|
|
srs_error_t set_h265_param_desc(std::string fmtp);
|
|
};
|
|
|
|
// TODO: FIXME: Rename it.
|
|
class SrsAudioPayload : public SrsCodecPayload
|
|
{
|
|
struct SrsOpusParameter {
|
|
int minptime;
|
|
bool use_inband_fec;
|
|
bool stereo;
|
|
bool usedtx;
|
|
|
|
SrsOpusParameter()
|
|
{
|
|
minptime = 0;
|
|
use_inband_fec = false;
|
|
stereo = false;
|
|
usedtx = false;
|
|
}
|
|
};
|
|
|
|
public:
|
|
int channel_;
|
|
SrsOpusParameter opus_param_;
|
|
// AAC configuration hex string for SDP fmtp line
|
|
std::string aac_config_hex_;
|
|
|
|
public:
|
|
SrsAudioPayload();
|
|
SrsAudioPayload(uint8_t pt, std::string encode_name, int sample, int channel);
|
|
virtual ~SrsAudioPayload();
|
|
|
|
public:
|
|
virtual SrsAudioPayload *copy();
|
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
|
|
|
public:
|
|
srs_error_t set_opus_param_desc(std::string fmtp);
|
|
};
|
|
|
|
// TODO: FIXME: Rename it.
|
|
class SrsRedPayload : public SrsCodecPayload
|
|
{
|
|
public:
|
|
int channel_;
|
|
|
|
public:
|
|
SrsRedPayload();
|
|
SrsRedPayload(uint8_t pt, std::string encode_name, int sample, int channel);
|
|
virtual ~SrsRedPayload();
|
|
|
|
public:
|
|
virtual SrsRedPayload *copy();
|
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
|
};
|
|
|
|
class SrsRtxPayloadDes : public SrsCodecPayload
|
|
{
|
|
public:
|
|
uint8_t apt_;
|
|
|
|
public:
|
|
SrsRtxPayloadDes();
|
|
SrsRtxPayloadDes(uint8_t pt, uint8_t apt);
|
|
virtual ~SrsRtxPayloadDes();
|
|
|
|
public:
|
|
virtual SrsRtxPayloadDes *copy();
|
|
virtual SrsMediaPayloadType generate_media_payload_type();
|
|
};
|
|
|
|
class SrsRtcTrackDescription
|
|
{
|
|
public:
|
|
// type: audio, video
|
|
std::string type_;
|
|
// track_id
|
|
std::string id_;
|
|
// ssrc is the primary ssrc for this track,
|
|
// if sdp has ssrc-group, it is the first ssrc of the ssrc-group
|
|
uint32_t ssrc_;
|
|
// rtx ssrc is the second ssrc of "FEC" src-group,
|
|
// if no rtx ssrc, rtx_ssrc_ = 0.
|
|
uint32_t fec_ssrc_;
|
|
// rtx ssrc is the second ssrc of "FID" src-group,
|
|
// if no rtx ssrc, rtx_ssrc_ = 0.
|
|
uint32_t rtx_ssrc_;
|
|
// key: rtp header extension id, value: rtp header extension uri.
|
|
std::map<int, std::string> extmaps_;
|
|
// Whether this track active. default: active.
|
|
bool is_active_;
|
|
// direction
|
|
std::string direction_;
|
|
// mid is used in BOUNDLE
|
|
std::string mid_;
|
|
// msid_: track stream id
|
|
std::string msid_;
|
|
|
|
// meida payload, such as opus, h264.
|
|
SrsCodecPayload *media_;
|
|
SrsCodecPayload *red_;
|
|
SrsCodecPayload *rtx_;
|
|
SrsCodecPayload *ulpfec_;
|
|
|
|
public:
|
|
SrsRtcTrackDescription();
|
|
virtual ~SrsRtcTrackDescription();
|
|
|
|
public:
|
|
// whether or not the track has ssrc.
|
|
// for example:
|
|
// we need check track has the ssrc in the ssrc_group, then add ssrc_group to the track,
|
|
bool has_ssrc(uint32_t ssrc);
|
|
|
|
public:
|
|
void add_rtp_extension_desc(int id, std::string uri);
|
|
void del_rtp_extension_desc(std::string uri);
|
|
void set_direction(std::string direction);
|
|
void set_codec_payload(SrsCodecPayload *payload);
|
|
// auxiliary paylod include red, rtx, ulpfec.
|
|
void create_auxiliary_payload(const std::vector<SrsMediaPayloadType> payload_types);
|
|
void set_rtx_ssrc(uint32_t ssrc);
|
|
void set_fec_ssrc(uint32_t ssrc);
|
|
void set_mid(std::string mid);
|
|
int get_rtp_extension_id(std::string uri);
|
|
|
|
public:
|
|
SrsRtcTrackDescription *copy();
|
|
};
|
|
|
|
class SrsRtcSourceDescription
|
|
{
|
|
public:
|
|
// the id for this stream;
|
|
std::string id_;
|
|
|
|
SrsRtcTrackDescription *audio_track_desc_;
|
|
std::vector<SrsRtcTrackDescription *> video_track_descs_;
|
|
|
|
public:
|
|
SrsRtcSourceDescription();
|
|
virtual ~SrsRtcSourceDescription();
|
|
|
|
public:
|
|
SrsRtcSourceDescription *copy();
|
|
SrsRtcTrackDescription *find_track_description_by_ssrc(uint32_t ssrc);
|
|
};
|
|
|
|
class SrsRtcRecvTrack
|
|
{
|
|
protected:
|
|
SrsRtcTrackDescription *track_desc_;
|
|
|
|
protected:
|
|
SrsRtcConnection *session_;
|
|
SrsRtpRingBuffer *rtp_queue_;
|
|
SrsRtpNackForReceiver *nack_receiver_;
|
|
|
|
private:
|
|
// By config, whether no copy.
|
|
bool nack_no_copy_;
|
|
|
|
protected:
|
|
// Latest sender report ntp and rtp time.
|
|
SrsNtp last_sender_report_ntp_;
|
|
int64_t last_sender_report_rtp_time_;
|
|
|
|
// Prev sender report ntp and rtp time.
|
|
SrsNtp last_sender_report_ntp1_;
|
|
int64_t last_sender_report_rtp_time1_;
|
|
|
|
double rate_;
|
|
uint64_t last_sender_report_sys_time_;
|
|
|
|
public:
|
|
SrsRtcRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *stream_descs, bool is_audio);
|
|
virtual ~SrsRtcRecvTrack();
|
|
|
|
public:
|
|
// SrsRtcSendTrack::set_nack_no_copy
|
|
void set_nack_no_copy(bool v) { nack_no_copy_ = v; }
|
|
bool has_ssrc(uint32_t ssrc);
|
|
uint32_t get_ssrc();
|
|
void update_rtt(int rtt);
|
|
void update_send_report_time(const SrsNtp &ntp, uint32_t rtp_time);
|
|
int64_t cal_avsync_time(uint32_t rtp_time);
|
|
srs_error_t send_rtcp_rr();
|
|
srs_error_t send_rtcp_xr_rrtr();
|
|
bool set_track_status(bool active);
|
|
bool get_track_status();
|
|
std::string get_track_id();
|
|
|
|
public:
|
|
// Note that we can set the pkt to NULL to avoid copy, for example, if the NACK cache the pkt and
|
|
// set to NULL, nack nerver copy it but set the pkt to NULL.
|
|
srs_error_t on_nack(SrsRtpPacket **ppkt);
|
|
|
|
public:
|
|
virtual srs_error_t on_rtp(SrsSharedPtr<SrsRtcSource> &source, SrsRtpPacket *pkt) = 0;
|
|
virtual srs_error_t check_send_nacks() = 0;
|
|
|
|
protected:
|
|
virtual srs_error_t do_check_send_nacks(uint32_t &timeout_nacks);
|
|
};
|
|
|
|
class SrsRtcAudioRecvTrack : public SrsRtcRecvTrack, public ISrsRtpPacketDecodeHandler
|
|
{
|
|
public:
|
|
SrsRtcAudioRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc);
|
|
virtual ~SrsRtcAudioRecvTrack();
|
|
|
|
public:
|
|
virtual void on_before_decode_payload(SrsRtpPacket *pkt, SrsBuffer *buf, ISrsRtpPayloader **ppayload, SrsRtpPacketPayloadType *ppt);
|
|
|
|
public:
|
|
virtual srs_error_t on_rtp(SrsSharedPtr<SrsRtcSource> &source, SrsRtpPacket *pkt);
|
|
virtual srs_error_t check_send_nacks();
|
|
};
|
|
|
|
class SrsRtcVideoRecvTrack : public SrsRtcRecvTrack, public ISrsRtpPacketDecodeHandler
|
|
{
|
|
public:
|
|
SrsRtcVideoRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *stream_descs);
|
|
virtual ~SrsRtcVideoRecvTrack();
|
|
|
|
public:
|
|
virtual void on_before_decode_payload(SrsRtpPacket *pkt, SrsBuffer *buf, ISrsRtpPayloader **ppayload, SrsRtpPacketPayloadType *ppt);
|
|
|
|
public:
|
|
virtual srs_error_t on_rtp(SrsSharedPtr<SrsRtcSource> &source, SrsRtpPacket *pkt);
|
|
virtual srs_error_t check_send_nacks();
|
|
};
|
|
|
|
// RTC jitter for TS or sequence number, only reset the base, and keep in original order.
|
|
template <typename T, typename ST>
|
|
class SrsRtcJitter
|
|
{
|
|
private:
|
|
ST threshold_;
|
|
typedef ST (*PFN)(const T &, const T &);
|
|
PFN distance_;
|
|
|
|
private:
|
|
// The value about packet.
|
|
T pkt_base_;
|
|
T pkt_last_;
|
|
// The value after corrected.
|
|
T correct_base_;
|
|
T correct_last_;
|
|
// The base timestamp by config, start from it.
|
|
T base_;
|
|
// Whether initialized. Note that we should not use correct_base_(0) as init state, because it might flip back.
|
|
bool init_;
|
|
|
|
public:
|
|
SrsRtcJitter(T base, ST threshold, PFN distance)
|
|
{
|
|
threshold_ = threshold;
|
|
distance_ = distance;
|
|
base_ = base;
|
|
|
|
pkt_base_ = pkt_last_ = 0;
|
|
correct_last_ = correct_base_ = 0;
|
|
init_ = false;
|
|
}
|
|
virtual ~SrsRtcJitter()
|
|
{
|
|
}
|
|
|
|
public:
|
|
T correct(T value)
|
|
{
|
|
if (!init_) {
|
|
init_ = true;
|
|
correct_base_ = base_;
|
|
pkt_base_ = value;
|
|
srs_trace("RTC: Jitter init base=%u, value=%u", base_, value);
|
|
}
|
|
|
|
if (true) {
|
|
ST distance = distance_(value, pkt_last_);
|
|
if (distance > threshold_ || distance < -1 * threshold_) {
|
|
srs_trace("RTC: Jitter rebase value=%u, last=%u, distance=%d, pkt-base=%u/%u, correct-base=%u/%u",
|
|
value, pkt_last_, distance, pkt_base_, value, correct_base_, correct_last_);
|
|
pkt_base_ = value;
|
|
correct_base_ = correct_last_;
|
|
}
|
|
}
|
|
|
|
pkt_last_ = value;
|
|
correct_last_ = correct_base_ + value - pkt_base_;
|
|
|
|
return correct_last_;
|
|
}
|
|
};
|
|
|
|
// For RTC timestamp jitter.
|
|
class SrsRtcTsJitter
|
|
{
|
|
private:
|
|
SrsRtcJitter<uint32_t, int32_t> *jitter_;
|
|
|
|
public:
|
|
SrsRtcTsJitter(uint32_t base);
|
|
virtual ~SrsRtcTsJitter();
|
|
|
|
public:
|
|
uint32_t correct(uint32_t value);
|
|
};
|
|
|
|
// For RTC sequence jitter.
|
|
class SrsRtcSeqJitter
|
|
{
|
|
private:
|
|
SrsRtcJitter<uint16_t, int16_t> *jitter_;
|
|
|
|
public:
|
|
SrsRtcSeqJitter(uint16_t base);
|
|
virtual ~SrsRtcSeqJitter();
|
|
|
|
public:
|
|
uint16_t correct(uint16_t value);
|
|
};
|
|
|
|
class SrsRtcSendTrack
|
|
{
|
|
public:
|
|
// send track description
|
|
SrsRtcTrackDescription *track_desc_;
|
|
|
|
protected:
|
|
// The owner connection for this track.
|
|
SrsRtcConnection *session_;
|
|
// NACK ARQ ring buffer.
|
|
SrsRtpRingBuffer *rtp_queue_;
|
|
|
|
protected:
|
|
// The jitter to correct ts and sequence number.
|
|
SrsRtcTsJitter *jitter_ts_;
|
|
SrsRtcSeqJitter *jitter_seq_;
|
|
|
|
private:
|
|
// By config, whether no copy.
|
|
bool nack_no_copy_;
|
|
// The pithy print for special stage.
|
|
SrsErrorPithyPrint *nack_epp;
|
|
|
|
public:
|
|
SrsRtcSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc, bool is_audio);
|
|
virtual ~SrsRtcSendTrack();
|
|
|
|
public:
|
|
// SrsRtcSendTrack::set_nack_no_copy
|
|
void set_nack_no_copy(bool v) { nack_no_copy_ = v; }
|
|
bool has_ssrc(uint32_t ssrc);
|
|
SrsRtpPacket *fetch_rtp_packet(uint16_t seq);
|
|
bool set_track_status(bool active);
|
|
bool get_track_status();
|
|
std::string get_track_id();
|
|
|
|
protected:
|
|
void rebuild_packet(SrsRtpPacket *pkt);
|
|
|
|
public:
|
|
// Note that we can set the pkt to NULL to avoid copy, for example, if the NACK cache the pkt and
|
|
// set to NULL, nack nerver copy it but set the pkt to NULL.
|
|
srs_error_t on_nack(SrsRtpPacket **ppkt);
|
|
|
|
public:
|
|
virtual srs_error_t on_rtp(SrsRtpPacket *pkt) = 0;
|
|
virtual srs_error_t on_rtcp(SrsRtpPacket *pkt) = 0;
|
|
virtual srs_error_t on_recv_nack(const std::vector<uint16_t> &lost_seqs);
|
|
};
|
|
|
|
class SrsRtcAudioSendTrack : public SrsRtcSendTrack
|
|
{
|
|
public:
|
|
SrsRtcAudioSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc);
|
|
virtual ~SrsRtcAudioSendTrack();
|
|
|
|
public:
|
|
virtual srs_error_t on_rtp(SrsRtpPacket *pkt);
|
|
virtual srs_error_t on_rtcp(SrsRtpPacket *pkt);
|
|
};
|
|
|
|
class SrsRtcVideoSendTrack : public SrsRtcSendTrack
|
|
{
|
|
public:
|
|
SrsRtcVideoSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc);
|
|
virtual ~SrsRtcVideoSendTrack();
|
|
|
|
public:
|
|
virtual srs_error_t on_rtp(SrsRtpPacket *pkt);
|
|
virtual srs_error_t on_rtcp(SrsRtpPacket *pkt);
|
|
};
|
|
|
|
class SrsRtcSSRCGenerator
|
|
{
|
|
private:
|
|
static SrsRtcSSRCGenerator *_instance;
|
|
|
|
private:
|
|
uint32_t ssrc_num;
|
|
|
|
private:
|
|
SrsRtcSSRCGenerator();
|
|
virtual ~SrsRtcSSRCGenerator();
|
|
|
|
public:
|
|
static SrsRtcSSRCGenerator *instance();
|
|
uint32_t generate_ssrc();
|
|
};
|
|
|
|
#endif
|