Live video on the AT Protocol
1package rtcrec
2
3import (
4 "context"
5 "fmt"
6 "io"
7 "time"
8
9 "github.com/fxamacker/cbor/v2"
10 "github.com/pion/webrtc/v4"
11 "stream.place/streamplace/pkg/log"
12)
13
14type WebRTCRecording struct {
15 Events []WebRTCEvent `json:"events,omitempty"`
16}
17
18type WebRTCEvent struct {
19 Offer *Offer `json:"offer,omitempty"`
20 CreateAnswer *CreateAnswer `json:"answer,omitempty"`
21 SetRemoteDescription *SetRemoteDescription `json:"setRemoteDescription,omitempty"`
22 SetLocalDescription *SetLocalDescription `json:"setLocalDescription,omitempty"`
23 LocalDescription *LocalDescription `json:"localDescription,omitempty"`
24 ICEConnectionStateChange *ICEConnectionStateChange `json:"iceConnectionStateChange,omitempty"`
25 ConnectionStateChange *ConnectionStateChange `json:"connectionStateChange,omitempty"`
26 Track *Track `json:"track,omitempty"`
27 TrackRead *TrackRead `json:"trackRead,omitempty"`
28 TrackCodec *TrackCodec `json:"trackCodec,omitempty"`
29 TrackKind *TrackKind `json:"trackKind,omitempty"`
30 TrackPayloadType *TrackPayloadType `json:"trackPayloadType,omitempty"`
31 TrackSSRC *TrackSSRC `json:"trackSSRC,omitempty"`
32 AddTransceiverFromKind *AddTransceiverFromKind `json:"addTransceiverFromKind,omitempty"`
33 ICEGatheringState *ICEGatheringState `json:"iceGatheringState,omitempty"`
34 DataChannel *DataChannel `json:"dataChannel,omitempty"`
35 NegotiationNeeded *NegotiationNeeded `json:"negotiationNeeded,omitempty"`
36 Time time.Time `json:"time,omitempty"`
37}
38
39func (e *WebRTCEvent) Detail() WebRTCEventDetail {
40 if e.Offer != nil {
41 return e.Offer
42 }
43 if e.CreateAnswer != nil {
44 return e.CreateAnswer
45 }
46 return nil
47}
48
49type WebRTCEventDetail interface{}
50
51type Offer struct {
52 SDPOffer string `json:"sdpOffer,omitempty"`
53}
54
55type CreateAnswer struct {
56 SDPAnswer string `json:"sdpAnswer,omitempty"`
57}
58
59type SetRemoteDescription struct {
60 SDPRemoteDescription string `json:"sdpRemoteDescription,omitempty"`
61}
62
63type SetLocalDescription struct {
64 SDPLocalDescription string `json:"sdpRemoteDescription,omitempty"`
65}
66
67type LocalDescription struct {
68 SDPLocalDescription string `json:"sdpLocalDescription,omitempty"`
69}
70
71type ICEConnectionStateChange struct {
72 ICEConnectionState webrtc.ICEConnectionState `json:"iceConnectionState,omitempty"`
73}
74
75type ConnectionStateChange struct {
76 ConnectionState webrtc.PeerConnectionState `json:"connectionState,omitempty"`
77}
78
79type Track struct {
80 ID string `json:"id,omitempty"`
81 Kind webrtc.RTPCodecType `json:"kind,omitempty"`
82 SSRC webrtc.SSRC `json:"ssrc,omitempty"`
83 PayloadType webrtc.PayloadType `json:"payloadType,omitempty"`
84 StreamID string `json:"streamId,omitempty"`
85 Msid string `json:"msid,omitempty"`
86 RID string `json:"rid,omitempty"`
87}
88
89type TrackRead struct {
90 SSRC webrtc.SSRC `json:"ssrc,omitempty"`
91 Data []byte `json:"data,omitempty"`
92 Count int `json:"count,omitempty"`
93 Err string `json:"err,omitempty"`
94}
95
96type TrackCodec struct {
97 SSRC webrtc.SSRC `json:"ssrc,omitempty"`
98 Codec webrtc.RTPCodecParameters `json:"codec,omitempty"`
99}
100
101type TrackKind struct {
102 SSRC webrtc.SSRC `json:"ssrc,omitempty"`
103 Kind webrtc.RTPCodecType `json:"kind,omitempty"`
104}
105
106type TrackPayloadType struct {
107 SSRC webrtc.SSRC `json:"ssrc,omitempty"`
108 PayloadType webrtc.PayloadType `json:"payloadType,omitempty"`
109}
110
111type TrackSSRC struct {
112 SSRC webrtc.SSRC `json:"ssrc,omitempty"`
113}
114
115type RecorderStream struct {
116 encoder *cbor.Encoder
117}
118
119func MakeWebRTCEncoder(w io.Writer) (*RecorderStream, error) {
120 opts := cbor.CoreDetEncOptions()
121 opts.Time = cbor.TimeRFC3339Nano
122 em, err := opts.EncMode()
123 if err != nil {
124 return nil, fmt.Errorf("failed to create encoder mode: %w", err)
125 }
126 encoder := em.NewEncoder(w)
127
128 return &RecorderStream{
129 encoder: encoder,
130 }, nil
131}
132
133func (s *RecorderStream) Event(event WebRTCEvent) {
134 err := s.encoder.Encode(event)
135 if err != nil {
136 log.Log(context.Background(), "error encoding event", "error", err)
137 }
138}
139
140type AddTransceiverFromKind struct {
141 Kind webrtc.RTPCodecType `json:"kind,omitempty"`
142}
143
144type ICEGatheringState struct {
145 State webrtc.ICEGatheringState `json:"state,omitempty"`
146}
147
148type DataChannel struct {
149 Label string `json:"label,omitempty"`
150}
151
152type NegotiationNeeded struct{}