// // Copyright (c) 2013-2025 The SRS Authors // // SPDX-License-Identifier: MIT // #ifndef SRS_APP_RTC_SOURCE_HPP #define SRS_APP_RTC_SOURCE_HPP #include #include #include #include #include #include #include #include #include #include #include #include class ISrsRequest; class SrsMetaCache; class SrsSharedPtrMessage; class SrsCommonMessage; class SrsMessageArray; class SrsRtcSource; class SrsFrameToRtcBridge; class SrsAudioTranscoder; class SrsRtpPacket; class SrsSample; 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 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 > 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 &pps); public: // Get the exists source, NULL when not exists. virtual SrsSharedPtr 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 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 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 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 source_; // Lazy initialization flags bool audio_initialized_; bool video_initialized_; public: SrsRtcRtpBuilder(SrsFrameToRtcBridge *bridge, SrsSharedPtr 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(SrsSharedPtrMessage *frame); private: virtual srs_error_t on_audio(SrsSharedPtrMessage *msg); private: srs_error_t init_codec(SrsAudioCodecId codec); srs_error_t transcode(SrsAudioFrame *audio); srs_error_t package_opus(SrsAudioFrame *audio, SrsRtpPacket *pkt); private: virtual srs_error_t on_video(SrsSharedPtrMessage *msg); private: srs_error_t filter(SrsSharedPtrMessage *msg, SrsFormat *format, bool &has_idr, std::vector &samples); srs_error_t package_stap_a(SrsSharedPtrMessage *msg, SrsRtpPacket *pkt); srs_error_t package_nalus(SrsSharedPtrMessage *msg, const std::vector &samples, std::vector &pkts); srs_error_t package_single_nalu(SrsSharedPtrMessage *msg, SrsSample *sample, std::vector &pkts); srs_error_t package_fu_a(SrsSharedPtrMessage *msg, SrsSample *sample, int fu_payload_size, std::vector &pkts); srs_error_t consume_packets(std::vector &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 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 &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, SrsSample *sps, SrsSample *pps); srs_error_t packet_sequence_header_hevc(SrsRtpPacket *pkt); srs_error_t do_packet_sequence_header_hevc(SrsRtpPacket *pkt, SrsSample *vps, SrsSample *sps, SrsSample *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 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 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 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 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 &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 &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 &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 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 *jitter_; public: SrsRtcTsJitter(uint32_t base); virtual ~SrsRtcTsJitter(); public: uint32_t correct(uint32_t value); }; // For RTC sequence jitter. class SrsRtcSeqJitter { private: SrsRtcJitter *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 &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