@@ -10,22 +10,28 @@ import (
1010 "path/filepath"
1111 "sort"
1212 "strings"
13-
14- "github.com/GetStream/getstream-go/v3"
13+ "time"
1514)
1615
1716// TrackInfo represents a single track with its metadata (deduplicated across segments)
1817type TrackInfo struct {
19- UserID string `json:"userId"` // participant_id from timing metadata
20- SessionID string `json:"sessionId"` // user_session_id from timing metadata
21- TrackID string `json:"trackId"` // track_id from segment
22- TrackType string `json:"trackType"` // "audio" or "video" (cleaned from TRACK_TYPE_*)
23- IsScreenshare bool `json:"isScreenshare"` // true if this is a screenshare track
24- Codec string `json:"codec"` // codec info
25- SegmentCount int `json:"segmentCount"` // number of segments for this track
26- Segments []* SegmentInfo `json:"segments"` // list of filenames (for JSON output only)
27-
28- ConcatenatedContainerPath string
18+ CallType string `json:"callType"` // call_type from timing metadata
19+ CallID string `json:"callId"` // call_id from timing metadata
20+ CallSessionID string `json:"callSessionId"` // call_session_id from timing metadata
21+ CallStartTime time.Time `json:"callStartTime"` // call_start_time from timing metadata
22+ UserID string `json:"userId"` // participant_id from timing metadata
23+ SessionID string `json:"sessionId"` // user_session_id from timing metadata
24+ TrackID string `json:"trackId"` // track_id from segment
25+ TrackType string `json:"trackType"` // track_type from segment
26+ TrackKind string `json:"trackKind"` // "audio" or "video" (cleaned from TRACK_TYPE_*)
27+ IsScreenshare bool `json:"isScreenshare"` // true if this is a screenshare track
28+ Codec string `json:"codec"` // codec info
29+ SegmentCount int `json:"segmentCount"` // number of segments for this track
30+ TrackStartTime time.Time `json:"trackStartTime"` // first_rtp_unix_timestamp from segment
31+ TrackEndTime time.Time `json:"trackEndTime"` // last_rtp_unix_timestamp from segment
32+ Segments []* SegmentInfo `json:"segments"` // list of filenames (for JSON output only)
33+
34+ ConcatenatedTrackFileInfo * TrackFileInfo
2935}
3036
3137type SegmentInfo struct {
@@ -35,7 +41,13 @@ type SegmentInfo struct {
3541 SdpPath string
3642 ContainerPath string
3743 ContainerExt string
38- FFMpegOffset int64
44+ }
45+
46+ type TrackFileInfo struct {
47+ Name string
48+ StartAt time.Time
49+ EndAt time.Time
50+ MaxFrameDimension SegmentFrameDimension
3951}
4052
4153// RecordingMetadata contains all tracks and session information
@@ -47,11 +59,11 @@ type RecordingMetadata struct {
4759
4860// MetadataParser handles parsing of raw recording files
4961type MetadataParser struct {
50- logger * getstream. DefaultLogger
62+ logger * ProcessingLogger
5163}
5264
5365// NewMetadataParser creates a new metadata parser
54- func NewMetadataParser (logger * getstream. DefaultLogger ) * MetadataParser {
66+ func NewMetadataParser (logger * ProcessingLogger ) * MetadataParser {
5567 return & MetadataParser {
5668 logger : logger ,
5769 }
@@ -197,27 +209,42 @@ func (p *MetadataParser) parseTimingMetadataFile(data []byte) ([]*TrackInfo, err
197209 // Use a map to deduplicate tracks by unique key
198210 trackMap := make (map [string ]* TrackInfo )
199211
200- processSegment := func (segment * SegmentMetadata , trackType string ) {
212+ processSegment := func (segment * SegmentMetadata , trackKind string ) {
201213 key := fmt .Sprintf ("%s|%s|%s|%s" ,
202214 sessionMetadata .ParticipantID ,
203215 sessionMetadata .UserSessionID ,
204216 segment .TrackID ,
205- trackType )
217+ trackKind )
206218
207219 if existingTrack , exists := trackMap [key ]; exists {
208220 existingTrack .Segments = append (existingTrack .Segments , & SegmentInfo {metadata : segment })
209221 existingTrack .SegmentCount ++
222+
223+ ts , te := time .UnixMilli (segment .FirstRtpUnixTimestamp ), time .UnixMilli (segment .LastRtpUnixTimestamp )
224+ if ts .Before (existingTrack .TrackStartTime ) {
225+ existingTrack .TrackStartTime = ts
226+ }
227+ if te .After (existingTrack .TrackEndTime ) {
228+ existingTrack .TrackEndTime = te
229+ }
210230 } else {
211231 // Create new track
212232 track := & TrackInfo {
213- UserID : sessionMetadata .ParticipantID ,
214- SessionID : sessionMetadata .UserSessionID ,
215- TrackID : segment .TrackID ,
216- TrackType : p .cleanTrackType (segment .TrackType ),
217- IsScreenshare : p .isScreenshareTrack (segment .TrackType ),
218- Codec : segment .Codec ,
219- SegmentCount : 1 ,
220- Segments : []* SegmentInfo {{metadata : segment }},
233+ CallType : sessionMetadata .CallType ,
234+ CallID : sessionMetadata .CallID ,
235+ CallSessionID : sessionMetadata .CallSessionID ,
236+ CallStartTime : sessionMetadata .CallStartTime ,
237+ UserID : sessionMetadata .ParticipantID ,
238+ SessionID : sessionMetadata .UserSessionID ,
239+ TrackID : segment .TrackID ,
240+ TrackType : segment .TrackType ,
241+ TrackKind : p .cleanTrackType (segment .TrackType ),
242+ IsScreenshare : p .isScreenshareTrack (segment .TrackType ),
243+ Codec : segment .Codec ,
244+ SegmentCount : 1 ,
245+ TrackStartTime : time .UnixMilli (segment .FirstRtpUnixTimestamp ),
246+ TrackEndTime : time .UnixMilli (segment .LastRtpUnixTimestamp ),
247+ Segments : []* SegmentInfo {{metadata : segment }},
221248 }
222249 trackMap [key ] = track
223250 }
@@ -254,9 +281,9 @@ func (p *MetadataParser) isScreenshareTrack(trackType string) bool {
254281func (p * MetadataParser ) cleanTrackType (trackType string ) string {
255282 switch trackType {
256283 case "TRACK_TYPE_AUDIO" , "TRACK_TYPE_SCREEN_SHARE_AUDIO" :
257- return "audio"
284+ return trackKindAudio
258285 case "TRACK_TYPE_VIDEO" , "TRACK_TYPE_SCREEN_SHARE" :
259- return "video"
286+ return trackKindVideo
260287 default :
261288 return strings .ToLower (trackType )
262289 }
@@ -298,20 +325,20 @@ func (p *MetadataParser) extractUniqueSessions(tracks []*TrackInfo) []string {
298325// Only one filter (userID, sessionID, or trackID) can be specified at a time
299326// Empty values are ignored, specific values must match
300327// If all are empty, all tracks are returned
301- func FilterTracks (tracks []* TrackInfo , userID , sessionID , trackID , trackType , mediaFilter string ) []* TrackInfo {
328+ func FilterTracks (tracks []* TrackInfo , userID , sessionID , trackID , trackKind , mediaType string ) []* TrackInfo {
302329 filtered := make ([]* TrackInfo , 0 )
303330
304331 for _ , track := range tracks {
305- if trackType != "" && track .TrackType != trackType {
306- continue // Skip tracks with wrong TrackType
332+ if trackKind != "" && track .TrackKind != trackKind {
333+ continue // Skip tracks with wrong trackKind
307334 }
308335
309336 // Apply media type filtering if specified
310- if mediaFilter != "" && mediaFilter != "both" {
311- if mediaFilter == "user" && track .IsScreenshare {
337+ if mediaType != "" && mediaType != mediaTypeBoth {
338+ if mediaType == mediaTypeUser && track .IsScreenshare {
312339 continue // Skip display tracks when only user requested
313340 }
314- if mediaFilter == "display" && ! track .IsScreenshare {
341+ if mediaType == mediaTypeDisplay && ! track .IsScreenshare {
315342 continue // Skip user tracks when only display requested
316343 }
317344 }
0 commit comments