{"version":3,"sources":["../src/video_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { NewVideoStreamResponse, VideoRotation } from '@livekit/rtc-ffi-bindings';\nimport { NewVideoStreamRequest, VideoStreamType } from '@livekit/rtc-ffi-bindings';\nimport type { UnderlyingSource } from 'node:stream/web';\nimport type { FfiEvent } from './ffi_client.js';\nimport { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';\nimport type { Track } from './track.js';\nimport { VideoFrame } from './video_frame.js';\n\nexport type VideoFrameEvent = {\n  frame: VideoFrame;\n  timestampUs: bigint;\n  rotation: VideoRotation;\n};\n\nclass VideoStreamSource implements UnderlyingSource<VideoFrameEvent> {\n  private controller?: ReadableStreamDefaultController<VideoFrameEvent>;\n  private ffiHandle: FfiHandle;\n  private disposed = false;\n\n  constructor(track: Track) {\n    const req = new NewVideoStreamRequest({\n      type: VideoStreamType.VIDEO_STREAM_NATIVE,\n      trackHandle: track.ffi_handle.handle,\n    });\n\n    const res = FfiClient.instance.request<NewVideoStreamResponse>({\n      message: {\n        case: 'newVideoStream',\n        value: req,\n      },\n    });\n\n    this.ffiHandle = new FfiHandle(res.stream!.handle!.id!);\n    FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);\n  }\n\n  private onEvent = (ev: FfiEvent) => {\n    if (!this.controller) {\n      throw new Error('Stream controller not initialized');\n    }\n\n    if (\n      ev.message.case != 'videoStreamEvent' ||\n      ev.message.value.streamHandle != this.ffiHandle.handle\n    ) {\n      return;\n    }\n\n    const streamEvent = ev.message.value.message;\n    switch (streamEvent.case) {\n      case 'frameReceived':\n        const rotation = streamEvent.value.rotation;\n        const timestampUs = streamEvent.value.timestampUs;\n        const frame = VideoFrame.fromOwnedInfo(streamEvent.value.buffer!);\n        const value = { rotation, timestampUs, frame };\n        const videoFrameEvent = {\n          frame: value.frame,\n          timestampUs: value.timestampUs!,\n          rotation: value.rotation!,\n        };\n        this.controller.enqueue(videoFrameEvent);\n        break;\n      case 'eos':\n        FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n        this.controller.close();\n        // Dispose the native handle so the FD is released on stream end,\n        // not just when cancel() is called explicitly by the consumer.\n        // Guard against double-dispose if cancel() is called after EOS\n        // while buffered frames are still in the ReadableStream queue.\n        if (!this.disposed) {\n          this.disposed = true;\n          this.ffiHandle.dispose();\n        }\n        break;\n    }\n  };\n\n  start(controller: ReadableStreamDefaultController<VideoFrameEvent>) {\n    this.controller = controller;\n  }\n\n  cancel() {\n    FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n    if (!this.disposed) {\n      this.disposed = true;\n      this.ffiHandle.dispose();\n    }\n  }\n}\n\nexport class VideoStream extends ReadableStream<VideoFrameEvent> {\n  constructor(track: Track) {\n    super(new VideoStreamSource(track));\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,8BAAuD;AAGvD,wBAAqD;AAErD,yBAA2B;AAQ3B,MAAM,kBAA+D;AAAA,EAKnE,YAAY,OAAc;AAF1B,SAAQ,WAAW;AAmBnB,SAAQ,UAAU,CAAC,OAAiB;AAClC,UAAI,CAAC,KAAK,YAAY;AACpB,cAAM,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAEA,UACE,GAAG,QAAQ,QAAQ,sBACnB,GAAG,QAAQ,MAAM,gBAAgB,KAAK,UAAU,QAChD;AACA;AAAA,MACF;AAEA,YAAM,cAAc,GAAG,QAAQ,MAAM;AACrC,cAAQ,YAAY,MAAM;AAAA,QACxB,KAAK;AACH,gBAAM,WAAW,YAAY,MAAM;AACnC,gBAAM,cAAc,YAAY,MAAM;AACtC,gBAAM,QAAQ,8BAAW,cAAc,YAAY,MAAM,MAAO;AAChE,gBAAM,QAAQ,EAAE,UAAU,aAAa,MAAM;AAC7C,gBAAM,kBAAkB;AAAA,YACtB,OAAO,MAAM;AAAA,YACb,aAAa,MAAM;AAAA,YACnB,UAAU,MAAM;AAAA,UAClB;AACA,eAAK,WAAW,QAAQ,eAAe;AACvC;AAAA,QACF,KAAK;AACH,sCAAU,SAAS,IAAI,iCAAe,UAAU,KAAK,OAAO;AAC5D,eAAK,WAAW,MAAM;AAKtB,cAAI,CAAC,KAAK,UAAU;AAClB,iBAAK,WAAW;AAChB,iBAAK,UAAU,QAAQ;AAAA,UACzB;AACA;AAAA,MACJ;AAAA,IACF;AAvDE,UAAM,MAAM,IAAI,8CAAsB;AAAA,MACpC,MAAM,wCAAgB;AAAA,MACtB,aAAa,MAAM,WAAW;AAAA,IAChC,CAAC;AAED,UAAM,MAAM,4BAAU,SAAS,QAAgC;AAAA,MAC7D,SAAS;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,SAAK,YAAY,IAAI,4BAAU,IAAI,OAAQ,OAAQ,EAAG;AACtD,gCAAU,SAAS,GAAG,iCAAe,UAAU,KAAK,OAAO;AAAA,EAC7D;AAAA,EA2CA,MAAM,YAA8D;AAClE,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,SAAS;AACP,gCAAU,SAAS,IAAI,iCAAe,UAAU,KAAK,OAAO;AAC5D,QAAI,CAAC,KAAK,UAAU;AAClB,WAAK,WAAW;AAChB,WAAK,UAAU,QAAQ;AAAA,IACzB;AAAA,EACF;AACF;AAEO,MAAM,oBAAoB,eAAgC;AAAA,EAC/D,YAAY,OAAc;AACxB,UAAM,IAAI,kBAAkB,KAAK,CAAC;AAAA,EACpC;AACF;","names":[]}