#pragma once
#include <stdint.h>
#include <atomic>
#include <deque>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/types/optional.h"
#include "api/frame_transformer_interface.h"
#include "api/rtp_sender_interface.h"
#include "api/rtp_receiver_interface.h"
#include "api/scoped_refptr.h"
#include "livekit/webrtc.h"
#include "rtc_base/synchronization/mutex.h"
#include "rust/cxx.h"
namespace livekit_ffi {
class PeerConnectionFactory;
class RtpSender;
class RtpReceiver;
enum class VideoPublishTimingStage : int32_t;
enum class VideoSubscribeTimingStage : int32_t;
struct VideoPublishTimingObserverWrapper;
struct VideoSubscribeTimingObserverWrapper;
}
namespace livekit_ffi {
constexpr uint8_t kPacketTrailerMagic[4] = {'L', 'K', 'T', 'S'};
constexpr size_t kTrailerEnvelopeSize = 5;
constexpr uint8_t kTagTimestampUs = 0x01; constexpr uint8_t kTagFrameId = 0x02;
constexpr size_t kTimestampTlvSize = 10; constexpr size_t kFrameIdTlvSize = 6;
constexpr size_t kPacketTrailerMinSize =
kTimestampTlvSize + kTrailerEnvelopeSize;
constexpr size_t kPacketTrailerMaxSize =
kTimestampTlvSize + kFrameIdTlvSize + kTrailerEnvelopeSize;
struct PacketTrailerMetadata {
uint64_t user_timestamp;
uint32_t frame_id;
uint32_t ssrc; };
class PacketTrailerTransformer : public webrtc::FrameTransformerInterface {
public:
enum class Direction { kSend, kReceive };
explicit PacketTrailerTransformer(Direction direction);
~PacketTrailerTransformer() override = default;
void Transform(
std::unique_ptr<webrtc::TransformableFrameInterface> frame) override;
void RegisterTransformedFrameCallback(
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> callback) override;
void RegisterTransformedFrameSinkCallback(
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> callback,
uint32_t ssrc) override;
void UnregisterTransformedFrameCallback() override;
void UnregisterTransformedFrameSinkCallback(uint32_t ssrc) override;
void set_enabled(bool enabled);
bool enabled() const;
std::optional<PacketTrailerMetadata> lookup_frame_metadata(uint32_t rtp_timestamp);
void store_frame_metadata(int64_t capture_timestamp_us,
uint64_t user_timestamp,
uint32_t frame_id);
void set_publish_timing_observer(
rust::Box<VideoPublishTimingObserverWrapper> observer);
void clear_publish_timing_observer();
void emit_publish_timing(VideoPublishTimingStage stage,
uint64_t user_timestamp,
uint32_t frame_id) const;
void set_subscribe_timing_observer(
rust::Box<VideoSubscribeTimingObserverWrapper> observer);
void clear_subscribe_timing_observer();
void emit_subscribe_timing(VideoSubscribeTimingStage stage,
uint64_t user_timestamp,
uint32_t frame_id) const;
private:
void TransformSend(
std::unique_ptr<webrtc::TransformableFrameInterface> frame);
void TransformReceive(
std::unique_ptr<webrtc::TransformableFrameInterface> frame);
void emit_subscribe_timing(VideoSubscribeTimingStage stage,
uint64_t user_timestamp,
uint32_t frame_id,
uint64_t timestamp_us) const;
bool publish_timing_enabled() const;
bool subscribe_timing_enabled() const;
PacketTrailerMetadata LookupSendMetadata(
const webrtc::TransformableFrameInterface& frame,
uint32_t ssrc,
uint32_t rtp_timestamp) const;
std::vector<uint8_t> AppendTrailer(
webrtc::ArrayView<const uint8_t> data,
uint64_t user_timestamp,
uint32_t frame_id);
std::optional<PacketTrailerMetadata> ExtractTrailer(
webrtc::ArrayView<const uint8_t> data,
std::vector<uint8_t>& out_data);
const Direction direction_;
std::atomic<bool> enabled_{true};
mutable webrtc::Mutex mutex_;
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> callback_;
std::unordered_map<uint32_t,
webrtc::scoped_refptr<webrtc::TransformedFrameCallback>>
sink_callbacks_;
mutable webrtc::Mutex send_map_mutex_;
mutable std::unordered_map<int64_t, PacketTrailerMetadata> send_map_;
mutable std::deque<int64_t> send_map_order_;
static constexpr size_t kMaxSendMapEntries = 300;
mutable webrtc::Mutex recv_map_mutex_;
mutable std::unordered_map<uint32_t, PacketTrailerMetadata> recv_map_;
mutable std::deque<uint32_t> recv_map_order_;
static constexpr size_t kMaxRecvMapEntries = 300;
mutable uint32_t recv_active_ssrc_{0};
mutable webrtc::Mutex publish_timing_observer_mutex_;
std::atomic<bool> publish_timing_enabled_{false};
mutable std::shared_ptr<rust::Box<VideoPublishTimingObserverWrapper>>
publish_timing_observer_;
mutable webrtc::Mutex subscribe_timing_observer_mutex_;
std::atomic<bool> subscribe_timing_enabled_{false};
mutable std::shared_ptr<rust::Box<VideoSubscribeTimingObserverWrapper>>
subscribe_timing_observer_;
};
class PacketTrailerHandler {
public:
PacketTrailerHandler(
std::shared_ptr<RtcRuntime> rtc_runtime,
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender);
PacketTrailerHandler(
std::shared_ptr<RtcRuntime> rtc_runtime,
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver);
~PacketTrailerHandler() = default;
void set_enabled(bool enabled) const;
bool enabled() const;
uint64_t lookup_timestamp(uint32_t rtp_timestamp) const;
uint32_t last_lookup_frame_id() const;
void store_frame_metadata(int64_t capture_timestamp_us,
uint64_t user_timestamp,
uint32_t frame_id) const;
void set_publish_timing_observer(
rust::Box<VideoPublishTimingObserverWrapper> observer) const;
void clear_publish_timing_observer() const;
void emit_publish_timing(VideoPublishTimingStage stage,
uint64_t user_timestamp,
uint32_t frame_id) const;
void set_subscribe_timing_observer(
rust::Box<VideoSubscribeTimingObserverWrapper> observer) const;
void clear_subscribe_timing_observer() const;
void emit_subscribe_timing(VideoSubscribeTimingStage stage,
uint64_t user_timestamp,
uint32_t frame_id) const;
webrtc::scoped_refptr<PacketTrailerTransformer> transformer() const;
private:
std::shared_ptr<RtcRuntime> rtc_runtime_;
webrtc::scoped_refptr<PacketTrailerTransformer> transformer_;
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender_;
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver_;
mutable uint32_t last_frame_id_{0};
};
std::shared_ptr<PacketTrailerHandler> new_packet_trailer_sender(
std::shared_ptr<PeerConnectionFactory> peer_factory,
std::shared_ptr<RtpSender> sender);
std::shared_ptr<PacketTrailerHandler> new_packet_trailer_receiver(
std::shared_ptr<PeerConnectionFactory> peer_factory,
std::shared_ptr<RtpReceiver> receiver);
}