internal import CPlaydate /// The cached `playdate->graphics->videostream` C API table. private var streamAPI: UnsafePointer { Playdate.videoStreamAPI.unsafelyUnwrapped } extension Graphics { /// Streams video (and audio) from a file or network connection. /// Wraps `LCDStreamPlayer`. public final class StreamPlayer { let pointer: OpaquePointer /// Retains the active source so it outlives the stream. private var retainedSource: AnyObject? public init() { pointer = streamAPI.pointee.newPlayer.unsafelyUnwrapped().unsafelyUnwrapped } deinit { streamAPI.pointee.freePlayer.unsafelyUnwrapped(pointer) } /// Sets the sizes of the stream's video and audio buffers, in bytes. public func setBufferSize(video: Int, audio: Int) { streamAPI.pointee.setBufferSize.unsafelyUnwrapped(pointer, Int32(video), Int32(audio)) } /// Streams from an open file. public func setFile(_ file: File.Handle) { retainedSource = file streamAPI.pointee.setFile.unsafelyUnwrapped(pointer, file.pointer) } /// Streams from an HTTP connection. public func setHTTPConnection(_ connection: Network.HTTPConnection) { retainedSource = connection streamAPI.pointee.setHTTPConnection.unsafelyUnwrapped(pointer, connection.pointer) } /// Streams from a TCP connection. public func setTCPConnection(_ connection: Network.TCPConnection) { retainedSource = connection streamAPI.pointee.setTCPConnection.unsafelyUnwrapped(pointer, connection.pointer) } /// The player used for the stream's audio track. Owned by the stream. /// The same wrapper is returned on every access, so callbacks /// registered on it stay valid for the stream's lifetime. public var filePlayer: Sound.FilePlayer? { guard let player = streamAPI.pointee.getFilePlayer.unsafelyUnwrapped(pointer) else { return nil } if let cached = cachedFilePlayer, cached.pointer == player { return cached } let wrapper = Sound.FilePlayer(pointer: player, isOwned: false) cachedFilePlayer = wrapper return wrapper } private var cachedFilePlayer: Sound.FilePlayer? /// The player used for the stream's video track. Owned by the stream. public var videoPlayer: VideoPlayer? { guard let player = streamAPI.pointee.getVideoPlayer.unsafelyUnwrapped(pointer) else { return nil } return VideoPlayer(pointer: player, isOwned: false) } /// Advances the stream. Returns `true` if a frame was drawn. @discardableResult public func update() -> Bool { streamAPI.pointee.update.unsafelyUnwrapped(pointer) } /// The number of video frames currently buffered. public var bufferedFrameCount: Int { Int(streamAPI.pointee.getBufferedFrameCount.unsafelyUnwrapped(pointer)) } /// The total number of bytes read from the source. public var bytesRead: UInt32 { streamAPI.pointee.getBytesRead.unsafelyUnwrapped(pointer) } } }