### Clone HaishinKit Repository and Open Examples (Shell) Source: https://github.com/haishinkit/haishinkit.swift/blob/main/README.md This shell command sequence clones the HaishinKit Swift repository from GitHub and then opens the example project in Xcode. This is a quick way to get started with the library and explore its functionalities. ```shell git clone https://github.com/HaishinKit/HaishinKit.swift.git cd HaishinKit.swift open Examples/Examples.xcodeproj ``` -------------------------------- ### Live Mixing Setup with MediaMixer Source: https://github.com/haishinkit/haishinkit.swift/blob/main/HaishinKit/Sources/Docs.docc/index.md Demonstrates how to set up and start a MediaMixer for live streaming. It attaches audio and video capture devices and associates a stream object with the mixer. Ensure necessary permissions for camera and microphone access. ```swift let mixer = MediaMixer() Task { do { // Attaches the microphone device. try await mixer.attachAudio(AVCaptureDevice.default(for: .audio)) } catch { print(error) } do { // Attaches the camera device. try await mixer.attachVideo(AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)) } catch { print(error) } // Associates the stream object with the MediaMixer. await mixer.addOutput(stream) await mixer.startRunning() } ``` -------------------------------- ### SRT Publishing with HaishinKit Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt Stream video and audio via the SRT protocol. This example shows how to set up an SRT connection, attach media devices, and start publishing. It utilizes `SRTConnection` and `SRTStream` from HaishinKit and SRTHaishinKit. ```swift import HaishinKit import SRTHaishinKit import AVFoundation // Enable SRT debug logging (optional) await SRTLogger.shared.setLevel(.debug) let mixer = MediaMixer() let connection = SRTConnection() let stream = SRTStream(connection: connection) let previewView = MTHKView(frame: view.bounds) Task { // Attach devices try await mixer.attachAudio(AVCaptureDevice.default(for: .audio)) try await mixer.attachVideo(AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)) // Connect mixer to stream await mixer.addOutput(stream) // Add preview await stream.addOutput(previewView) } view.addSubview(previewView) Task { do { // Connect with SRT options as query parameters // Supported options: latency, maxbw, passphrase, etc. try await connection.connect("srt://server.com:9998?latency=500&passphrase=secret") // Start publishing await stream.publish() print("SRT publishing started") } catch { print("SRT error: (error)") } } ``` -------------------------------- ### Install Xcode Command Line Tools Source: https://github.com/haishinkit/haishinkit.swift/blob/main/fastlane/README.md Ensures the latest version of Xcode command line tools are installed on your system. This is a prerequisite for many development tasks. ```shell xcode-select --install ``` -------------------------------- ### Attach Camera to HTTP Stream (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-0.3.x-APIs-to-0.4.x Demonstrates how to attach a camera to an HTTP stream. The example shows the migration from using AVMixer.deviceWithPosition in older versions (0.3.x) to DeviceUtil.deviceWithPosition in newer versions (0.4.x). ```swift httpStream.attachCamera(DeviceUtil.deviceWithPosition(.Back)) ``` -------------------------------- ### Testing SRT Streaming with FFmpeg/FFplay Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt Verify SRT streaming functionality by using FFmpeg and FFplay for publishing and playback. This section provides command-line examples for setting up an SRT listener and publisher, as well as testing RTMP streams. ```bash # Start SRT listener for testing HaishinKit publishing ffplay -i 'srt://0.0.0.0:9998?mode=listener' # Start SRT publisher for testing HaishinKit playback ffmpeg -stream_loop -1 -re -i input.mp4 -c copy -f mpegts 'srt://0.0.0.0:9998?mode=listener' # Test RTMP with local server (nginx-rtmp, etc.) ffplay rtmp://localhost/live/streamKey ``` -------------------------------- ### VideoSettings Configuration Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.4.x-APIs-to-1.5.5 This section details how to configure video output settings for streaming. It covers parameters like resolution, bitrate, and H.264 profile, with examples for versions 1.4.0 and 1.5.0. ```APIDOC ## VideoSettings Configuration ### Version 1.4.0 This version uses a dictionary to configure video settings. ```swift stream.videoSettings = [ .width: 640, // video output width .height: 360, // video output height .bitrate: 160 * 1000, // video output bitrate .profileLevel: kVTProfileLevel_H264_Baseline_3_1, // H264 Profile require "import VideoToolbox" .maxKeyFrameIntervalDuration: 2, // key frame / sec ] stream.videoSettings[.bitrate] = 160 * 1000 ``` ### Version 1.5.0 This version introduces a dedicated `VideoCodecSettings` struct for configuration. ```swift stream.videoSettings = VideoCodecSettings( videoSize: .init(width: 854, height: 480), profileLevel: kVTProfileLevel_H264_Baseline_3_1 as String, bitRate: 640 * 1000, maxKeyFrameIntervalDuration: 2, scalingMode: .trim, bitRateMode: .average, allowFrameReordering: nil, isHardwareEncoderEnabled: true ) stream.videoSettings.bitRate = 640 * 1000 ``` ``` -------------------------------- ### Configure Camera Unit (2.0.x) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Provides an example of configuring a video unit within the MediaMixer in HaishinKit 2.0.x. This allows for fine-grained control over camera settings like focus point and mode, using a closure for configuration. ```swift try await mixer.configuration(video: 0) { unit in guard let device = unit.device else { return } try device.lockForConfiguration() device.focusPointOfInterest = pointOfInterest device.focusMode = .continuousAutoFocus device.unlockForConfiguration() } ``` -------------------------------- ### Start Recording in HaishinKit (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.3.x-APIs-to-1.4.0 Shows the methods for initiating video recording. Version 1.3.0 sets recording settings via a dictionary and then calls `startRecording()`. Version 1.4.0 allows passing the recording settings directly as an argument to the `startRecording()` method. ```swift stream.recordSettings = [ AVMediaType.audio: [ ], AVMediaType.video: [ ] ] stream.startRecording() ``` ```swift stream.startRecording([ AVMediaType.audio: [ ], AVMediaType.video: [ ] ]) ``` -------------------------------- ### Initialize and Configure MediaMixer for Streaming Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt This Swift code initializes the MediaMixer for capturing and mixing audio/video. It demonstrates attaching audio and video devices, configuring video mixing settings, setting the output screen size and frame rate, and starting the mixer. Error handling for device attachment is included. ```swift import HaishinKit import AVFoundation // Create a mixer with multi-camera support let mixer = MediaMixer(captureSessionMode: .multi) Task { // Attach the default microphone do { try await mixer.attachAudio(AVCaptureDevice.default(for: .audio)) } catch { print("Failed to attach audio: \(error)") } // Attach the back camera to track 0 do { let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) try await mixer.attachVideo(backCamera, track: 0) { videoUnit in videoUnit.isVideoMirrored = false } } catch { print("Failed to attach video: \(error)") } // Configure video mixing settings var videoSettings = await mixer.videoMixerSettings videoSettings.mode = .offscreen // Enable screen composition videoSettings.mainTrack = 0 // Use track 0 as main video await mixer.setVideoMixerSettings(videoSettings) // Configure screen size for output await mixer.screen.size = CGSize(width: 1280, height: 720) await mixer.screen.backgroundColor = UIColor.black.cgColor // Set frame rate try await mixer.setFrameRate(30.0) // Enable HDR mode (optional) try await mixer.setDynamicRangeMode(.hdr) // Start the mixer await mixer.startRunning() // Add output destinations (stream, view, recorder) await mixer.addOutput(stream) await mixer.addOutput(previewView) } ``` -------------------------------- ### AudioSettings Configuration Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.4.x-APIs-to-1.5.5 This section describes how to configure audio output settings. It includes bitrate and format options, with examples for versions 1.4.0 and 1.5.0. ```APIDOC ## AudioSettings Configuration ### Version 1.4.0 This version uses a dictionary for audio settings. ```swift rtmpStream.audioSettings = [ .bitrate: 32 * 1000, ] ``` ### Version 1.5.0 This version uses the `AudioCodecSettings` struct for configuration. ```swift stream.audioSettings = AudioCodecSettings( bitRate: 64 * 1000, format: .aac ) ``` ``` -------------------------------- ### Start and Stop Recording Streams (HaishinKit Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.2.x-APIs-to-1.3.0 Demonstrates how to initiate and terminate stream recording using HaishinKit. Includes methods for publishing with local recording and standalone recording initiated via the recorder delegate. ```swift // startRecording (1.2.x) rtmpStream.publish("streamName", .localRecord) // stopRecording (1.2.x) rtmpStream.publish() // startRecording (1.3.0) rtmpStream.mixer.recorder.delegate = self rtmpStream.startRecording() // stopRecording (1.3.0) rtmpStream.stopRecording() ``` -------------------------------- ### RTMP Authentication with HaishinKit Source: https://github.com/haishinkit/haishinkit.swift/blob/main/RTMPHaishinKit/Sources/Docs.docc/index.md This example shows how to perform FMLE-compatible authentication when connecting to an RTMP server using HaishinKit. It demonstrates how to include username and password directly in the connection URL. Note that this may not work with all services due to custom authentication methods. ```swift var connection = RTMPConnection() connection.connect("rtmp://username:password@localhost/appName/instanceName") ``` -------------------------------- ### Run 'review' Action with fastlane Source: https://github.com/haishinkit/haishinkit.swift/blob/main/fastlane/README.md Executes the 'review' action using fastlane, which is used to review Pull Requests. The 'bundle exec' prefix is optional and depends on your project setup. ```shell [bundle exec] fastlane review ``` -------------------------------- ### Configure Audio Settings in HaishinKit (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.4.x-APIs-to-1.5.5 Shows how to configure audio output settings for streaming with HaishinKit. This involves setting the audio bitrate and format, with examples for both dictionary-based and object-based configurations. ```swift rtmpStream.audioSettings = [ .bitrate: 32 * 1000, ] ``` ```swift stream.audioSettings = AudioCodecSettings( bitRate: 64 * 1000, format: .aac ) ``` -------------------------------- ### Recording Functionality (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.6.0-APIs-to-1.7.x Demonstrates how to control and initiate media recording using HaishinKit. It shows how to check recording status, set a delegate, and start the recording process with specified settings. API changes between versions are noted. ```swift stream.mixer.recorder.isRuning stream.mixer.recorder.delegate = self stream.startRecording(settings) ``` ```swift stream.isRecording stream.startRecording(self, settings: settings) ``` -------------------------------- ### Registering Session Factories for RTMP and SRT Source: https://github.com/haishinkit/haishinkit.swift/blob/main/HaishinKit/Sources/Docs.docc/index.md Initializes and registers session factories for RTMP and SRT protocols with the SessionBuilderFactory. This allows the application to create sessions for these protocols. This setup is typically done once at the application's start. ```swift import HaishinKit import RTMPHaishinKit import SRTHaishinKit Task { await SessionBuilderFactory.shared.register(RTMPSessionFactory()) await SessionBuilderFactory.shared.register(SRTSessionFactory()) } ``` -------------------------------- ### SRT Playback with HaishinKit Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt Play SRT streams using `SRTConnection` and `AudioPlayer`. This example demonstrates connecting to an SRT stream and playing it back, requiring an `AudioPlayer` instance for audio output. ```swift import HaishinKit import SRTHaishinKit import AVFoundation let connection = SRTConnection() let stream = SRTStream(connection: connection) let audioPlayer = AudioPlayer(audioEngine: AVAudioEngine()) let playerView = MTHKView(frame: view.bounds) Task { @MainActor in await stream.addOutput(playerView) view.addSubview(playerView) } Task { // Attach audio player (required for playback) await stream.attachAudioPlayer(audioPlayer) do { // Connect and play try await connection.connect("srt://server.com:9998?mode=caller") await stream.play() print("SRT playback started") } catch { print("SRT playback error: (error)") } } ``` -------------------------------- ### Attaching Video Tracks for Picture-in-Picture with Swift Source: https://github.com/haishinkit/haishinkit.swift/blob/main/HaishinKit/Sources/Docs.docc/videomixing.md Shows how to set up and attach multiple video tracks, typically for a picture-in-picture effect. This involves getting default back and front camera devices and attaching them to specific track numbers using the mixer. ```swift Task { let back = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) try? await mixer.attachVideo(back, track: 0) let front = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) try? await mixer.attachVideo(front, track: 1) } ``` -------------------------------- ### MediaMixer capture behavior change in HaishinKit Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-2.0.x-APIs-to-2.1.0 Demonstrates the change in MediaMixer's capture behavior. In 2.0.x, capture started automatically under certain conditions. In 2.1.0, manual invocation of startRunning() is required. ```swift import HaishinKit let mixer = MediaMixer() // Capture was automatically started under certain conditions. await mixer.attachVideo(AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)) await mixer.addOutput(view) ``` ```swift import HaishinKit let mixer = MediaMixer() await mixer.attachVideo(AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)) await mixer.addOutput(view) // Calling startRunning() is now required. await mixer.startRunning() ``` -------------------------------- ### Record Stream (1.9.x) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Illustrates how to record a stream using IOStreamRecorder in HaishinKit version 1.9.x. It covers adding the recorder as an observer, starting and stopping recording, and handling delegate methods for errors and completion. ```swift stream = RTMPStream() recorder = IOStreamRecorder() stream.addObserver(recorder) recorder.delegate = self recorder.startRunning() recorder.stopRunning() extension IngestViewController: IOStreamRecorderDelegate { // MARK: IOStreamRecorderDelegate func recorder(_ recorder: IOStreamRecorder, errorOccured error: IOStreamRecorder.Error) { logger.error(error) } func recorder(_ recorder: IOStreamRecorder, finishWriting writer: AVAssetWriter) { PHPhotoLibrary.shared().performChanges({() -> Void in PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: writer.outputURL) }, completionHandler: { _, error -> Void in try? FileManager.default.removeItem(at: writer.outputURL) }) } } ``` -------------------------------- ### Install HaishinKit with Swift Package Manager Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt This code snippet shows how to add HaishinKit as a dependency to your Swift project using the Swift Package Manager. Ensure you are using a compatible version of Swift. ```swift // Package.swift dependencies: [ .package(url: "https://github.com/HaishinKit/HaishinKit.swift", from: "2.0.0") ] ``` -------------------------------- ### Configure AVAudioSession for Streaming (Swift) Source: https://github.com/haishinkit/haishinkit.swift/blob/main/README.md This snippet demonstrates how to set up and activate an AVAudioSession for audio and video recording and playback on iOS. It configures the session category and mode, enabling Bluetooth and default speaker options. Ensure this is done before starting any streaming operations. ```swift import AVFoundation let session = AVAudioSession.sharedInstance() do { try session.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker, .allowBluetooth]) try session.setActive(true) } catch { print(error) } ``` -------------------------------- ### Torch Control with HaishinKit Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt Manage the device's torch (flashlight) during live streaming with HaishinKit. This example shows how to toggle the torch on/off and includes a function to disable the torch when switching to the front camera, as the torch is typically only available on the back camera. ```swift import HaishinKit // Toggle torch Task { let isEnabled = await mixer.isTorchEnabled await mixer.setTorchEnabled(!isEnabled) } // Note: Torch is only available on back camera // Switching to front camera should disable torch func switchToFrontCamera() { Task { var settings = await mixer.videoMixerSettings settings.mainTrack = 1 // Front camera await mixer.setVideoMixerSettings(settings) // Disable torch when using front camera await mixer.setTorchEnabled(false) } } ``` -------------------------------- ### Recording with IOStream (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.7.0-APIs-to-1.8.x Illustrates how to start and stop recording using IOStream. Version 1.7.1 uses `isRecording` and `startRecording`/`stopRecording` methods directly on the stream. Version 1.8.0 introduces `IOStreamRecorder` for managing recordings and recommends setting audio channels to a maximum of 2 for local saving. ```swift // Start recording. stream.isRecording stream.startRecording(self, settings: settings) // Stop recording. stream.stopRecording() ``` ```swift let channels = max(stream.audioInputFormats[0]?.channels ?? 1, 2) stream.audioMixerSettings = .init(sampleRate: 0, channels: channels) ``` ```swift // Start recordings recorder = IOStreamRecorder() recorder.delegate = self // Subscribes stream sample data. stream.addObserver(recorder) recorder.startRunning() // Stop recording. recorder.stopRunning() // stream.removeObserver(recorder) ``` -------------------------------- ### Configure Multiple Views with MediaMixer (2.0.0) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Shows how to configure multiple MTHKView instances with a MediaMixer in HaishinKit 2.0.0. This setup allows for different video tracks to be assigned to different views, enabling simultaneous monitoring or display of various video sources. ```swift mixer = MediaMixer() view0 = MTHKView() view1 = MTHKView() view0.videoTrackId = 0 view1.videoTrackId = UInt8.max mixer.addOutput(view0) mixer.addOutput(view1) ``` -------------------------------- ### Record Stream (2.0.0) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Demonstrates recording a stream using HKStreamRecorder in HaishinKit version 2.0.0. This asynchronous approach involves starting and stopping the recording, and then saving the recorded video to the photo library. ```swift stream = RTMPStream() // or SRTStream() recorder = HKStreamRecorder() stream.addOutput(recorder) do { try await recorder.startRecording() } catch { print(error) } do { let outputURL = try await recorder.stopRecording() PHPhotoLibrary.shared().performChanges({() -> Void in PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL) }, completionHandler: { _, error -> Void in try? FileManager.default.removeItem(at: outputURL) }) } catch { print(error) } ``` -------------------------------- ### Handle Recorder Errors and Finish Writing (HaishinKit Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.2.x-APIs-to-1.3.0 Provides an example of implementing the AVRecorderDelegate to handle recording errors and process the finished asset writer. This includes saving the recorded video to the photo library and cleaning up temporary files. ```swift extension ViewController: AVRecorderDelegate { // MARK: AVRecorderDelegate func recorder(_ recorder: AVRecorder, errorOccured error: AVRecorder.Error) { logger.error(error) } func recorder(_ recorder: AVRecorder, finishWriting writer: AVAssetWriter) { PHPhotoLibrary.shared().performChanges({ PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: writer.outputURL) }, completionHandler: { _, error -> Void in do { try FileManager.default.removeItem(at: writer.outputURL) } catch { print(error) } }) } } ``` -------------------------------- ### Multi-Camera Streaming with HaishinKit Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt Configure HaishinKit's MediaMixer to capture from multiple cameras simultaneously. This example demonstrates attaching back and front cameras to different tracks and setting the main track for multitasking camera access on iPad. ```swift import HaishinKit import AVFoundation // Create mixer with multi-camera support let mixer = MediaMixer(captureSessionMode: .multi) Task { // Configure for multitasking camera access (iPad) await mixer.configuration { session in if session.isMultitaskingCameraAccessSupported { session.isMultitaskingCameraAccessEnabled = true } } // Attach back camera to track 0 let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) try await mixer.attachVideo(backCamera, track: 0) { videoUnit in videoUnit.isVideoMirrored = false } // Attach front camera to track 1 let frontCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) try await mixer.attachVideo(frontCamera, track: 1) { videoUnit in videoUnit.isVideoMirrored = true } // Set main track (which camera is primary) var settings = await mixer.videoMixerSettings settings.mainTrack = 0 // Back camera is main await mixer.setVideoMixerSettings(settings) await mixer.startRunning() } // Switch main camera func switchCamera() { Task { var settings = await mixer.videoMixerSettings settings.mainTrack = settings.mainTrack == 0 ? 1 : 0 await mixer.setVideoMixerSettings(settings) } } ``` -------------------------------- ### Implement Network Statistics and Bitrate Strategy (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Introduces `NetworkMonitorReport` and `HKStreamBitRateStrategy` in version 2.0.0. `NetworkMonitorReport` structures network statistics, while `HKStreamBitRateStrategy` defines an interface for adjusting bitrate based on network conditions. An example actor `MyStreamBitRateStrategy` is provided. ```swift /// The struct represents a network statistics. public struct NetworkMonitorReport: Sendable { /// The statistics of total incoming bytes. public let totalBytesIn: Int /// The statistics of total outgoing bytes. public let totalBytesOut: Int /// The statistics of outgoing queue bytes per second. public let currentQueueBytesOut: Int /// The statistics of incoming bytes per second. public let currentBytesInPerSecond: Int /// The statistics of outgoing bytes per second. public let currentBytesOutPerSecond: Int } /// A type with a network bitrate strategy representation. public protocol HKStreamBitRateStrategy: Sendable { /// The mamimum video bitRate. var mamimumVideoBitRate: Int { get } /// The mamimum audio bitRate. var mamimumAudioBitRate: Int { get } /// Adjust a bitRate. func adjustBitrate(_ event: NetworkMonitorEvent, stream: some HKStream) async } /// An enumeration that indicate the network monitor event. public enum NetworkMonitorEvent: Sendable { /// To update statistics. case status(report: NetworkMonitorReport) /// To publish sufficient bandwidth occured. case publishInsufficientBWOccured(report: NetworkMonitorReport) /// To reset statistics. case reset } final final actor MyStreamBitRateStrategy: HKStreamBitRateStrategy { func adjustBitrate(_ event: NetworkMonitorEvent, stream: some HKStream) async { } } var stream: (any HKStream) var strategy = MyStreamBitRateStrategy() async stream.setBitrateStrategy(strategy) ``` -------------------------------- ### Playback RTMP Stream with HaishinKit Source: https://github.com/haishinkit/haishinkit.swift/blob/main/RTMPHaishinKit/Sources/Docs.docc/index.md This code snippet illustrates how to play an RTMP stream using HaishinKit. It sets up an RTMP connection and stream, attaches an audio player and a view for video output, and then connects to the RTMP server to start playback. Error handling for connection and playback is provided. ```swift let connection = RTMPConnection() let stream = RTMPStream(connection: connection) let audioPlayer = AudioPlayer(AVAudioEngine()) let hkView = MTHKView(frame: view.bounds) Task { MainActor in await stream.addOutput(hkView) } Task { // requires attachAudioPlayer await stream.attachAudioPlayer(audioPlayer) do { try await connection.connect("rtmp://localhost/appName/instanceName") try await stream.play(streamName) } catch RTMPConnection.Error.requestFailed(let response) { print(response) } catch RTMPStream.Error.requestFailed(let response) { print(response) } catch { print(error) } } ``` -------------------------------- ### Record Streams Locally with StreamRecorder in Swift Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt Explains how to use StreamRecorder for local recording of streams to MP4 or MOV files, either during streaming or as a standalone recording. It covers setting movie fragment intervals for crash-safe recording, adding the recorder to a mixer, starting and stopping recordings, handling errors, and saving recordings to the Photos library. Dependencies include HaishinKit and Photos. ```swift import HaishinKit import Photos let recorder = StreamRecorder() // Set movie fragment interval for crash-safe recording (optional) await recorder.setMovieFragmentInterval(10.0) // Write every 10 seconds // Add recorder to mixer await mixer.addOutput(recorder) // Start recording with custom path Task { do { // Auto-generated path in documents directory try await recorder.startRecording() // Or specify custom path // try await recorder.startRecording(URL(string: "recordings/my_video.mp4")) print("Recording started: (await recorder.outputURL)") } catch StreamRecorder.Error.fileAlreadyExists(let url) { print("File exists: (url)") } catch { print("Recording error: (error)") } } // Monitor recording errors Task { for await error in await recorder.error { print("Recording error: (error)") } } // Stop recording and save to Photos Task { do { let outputURL = try await recorder.stopRecording() // Save to Photos library try await PHPhotoLibrary.shared().performChanges { PHAssetCreationRequest.creationRequestForAssetFromVideo(atFileURL: outputURL) } // Clean up temp file try FileManager.default.removeItem(at: outputURL) print("Recording saved to Photos") } catch { print("Stop recording error: (error)") } } ``` -------------------------------- ### Display Live Camera Preview with MTHKView in Swift Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt Demonstrates how to use MTHKView for Metal-accelerated rendering of live camera previews or stream playback. It covers view creation, video gravity configuration, adding custom video effects, and connecting to a mixer or stream for output. Dependencies include HaishinKit and AVFoundation. ```swift import HaishinKit import AVFoundation // Create preview view let previewView = MTHKView(frame: CGRect(x: 0, y: 0, width: 375, height: 667)) // Configure video gravity previewView.videoGravity = .resizeAspect // Options: .resize, .resizeAspect, .resizeAspectFill // Add custom video effects class GrayscaleEffect: VideoEffect { func execute(_ image: CIImage) -> CIImage { let filter = CIFilter(name: "CIPhotoEffectMono")! filter.setValue(image, forKey: kCIInputImageKey) return filter.outputImage ?? image } } let grayscale = GrayscaleEffect() previewView.registerVideoEffect(grayscale) // Add to view hierarchy view.addSubview(previewView) // Connect to mixer for camera preview Task { await mixer.addOutput(previewView) } // Or connect to stream for playback preview Task { await stream.addOutput(previewView) } // Remove effect later previewView.unregisterVideoEffect(grayscale) ``` -------------------------------- ### Run 'document' Action with fastlane Source: https://github.com/haishinkit/haishinkit.swift/blob/main/fastlane/README.md Executes the 'document' action using fastlane, which is responsible for creating documentation. The 'bundle exec' prefix is optional. ```shell [bundle exec] fastlane document ``` -------------------------------- ### Add Camera and Microphone Usage Descriptions to Info.plist (XML) Source: https://github.com/haishinkit/haishinkit.swift/blob/main/README.md This XML snippet shows the necessary keys to add to your application's Info.plist file to request user permission for accessing the camera and microphone. Providing clear usage descriptions is crucial for app store review and user trust. ```xml NSCameraUsageDescription your usage description here NSMicrophoneUsageDescription your usage description here ``` -------------------------------- ### Publish RTMP Stream with HaishinKit Source: https://github.com/haishinkit/haishinkit.swift/blob/main/RTMPHaishinKit/Sources/Docs.docc/index.md This snippet demonstrates how to set up and publish an RTMP stream using HaishinKit. It involves initializing media mixer, RTMP connection, and stream objects, attaching audio and video inputs, and connecting to an RTMP server. Error handling for connection and publishing is included. ```swift let mixer = MediaMixer() let connection = RTMPConnection() let stream = RTMPStream(connection: connection) let hkView = MTHKView(frame: view.bounds) Task { do { try await mixer.attachAudio(AVCaptureDevice.default(for: .audio)) } catch { print(error) } do { try await mixer.attachVideo(AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)) } catch { print(error) } await mixer.addOutput(stream) } Task { MainActor in await stream.addOutput(hkView) // add ViewController#view view.addSubview(hkView) } Task { do { try await connection.connect("rtmp://localhost/appName/instanceName") try await stream.publish(streamName) } catch RTMPConnection.Error.requestFailed(let response) { print(response) } catch RTMPStream.Error.requestFailed(let response) { print(response) } catch { print(error) } } ``` -------------------------------- ### Compositing Images with ImageScreenObject in Swift Source: https://github.com/haishinkit/haishinkit.swift/blob/main/HaishinKit/Sources/Docs.docc/videomixing.md Demonstrates how to overlay a static image onto camera footage using HaishinKit's ImageScreenObject. It involves creating an ImageScreenObject, loading an image from resources, setting its alignment and margins, and adding it to the mixer's screen. ```swift let imageScreenObject = ImageScreenObject() let imageURL = URL(fileURLWithPath: Bundle.main.path(forResource: "game_jikkyou", ofType: "png") ?? "") if let provider = CGDataProvider(url: imageURL as CFURL) { imageScreenObject.verticalAlignment = .bottom imageScreenObject.layoutMargin = .init(top: 0, left: 0, bottom: 16, right: 0) imageScreenObject.cgImage = CGImage( pngDataProviderSource: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent ) } else { print("no image") } try? await mixer.screen.addChild(imageScreenObject) ``` -------------------------------- ### RTMPStreamDelegate Protocol Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.4.x-APIs-to-1.5.5 Defines the delegate methods for RTMPStream to inform its delegate about various events, such as bandwidth changes, incoming audio/video, statistics updates, and codec errors. Examples are provided for versions 1.4.0 and 1.5.0. ```APIDOC ## RTMPStreamDelegate Protocol ### Version 1.4.0 Delegate methods for `RTMPStream`. ```swift /// The interface a RTMPStream uses to inform its delegate. public protocol RTMPStreamDelegate: AnyObject { /// Tells the receiver to publish insufficient bandwidth occured. func rtmpStream(_ stream: RTMPStream, publishInsufficientBWOccured connection: RTMPConnection) /// Tells the receiver to publish sufficient bandwidth occured. func rtmpStream(_ stream: RTMPStream, publishSufficientBWOccured connection: RTMPConnection) /// Tells the receiver to playback an audio packet incoming. func rtmpStream(_ stream: RTMPStream, didOutput audio: AVAudioBuffer, presentationTimeStamp: CMTime) /// Tells the receiver to playback a video packet incoming. func rtmpStream(_ stream: RTMPStream, didOutput video: CMSampleBuffer) /// Tells the receiver to update statistics. func rtmpStream(_ stream: RTMPStream, updatedStats connection: RTMPConnection) #if os(iOS) /// Tells the receiver to session was interrupted. func rtmpStream(_ stream: RTMPStream, sessionWasInterrupted session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason) /// Tells the receiver to session interrupted ended. func rtmpStream(_ stream: RTMPStream, sessionInterruptionEnded session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason) #endif /// Tells the receiver to video codec error occured. func rtmpStream(_ stream: RTMPStream, videoCodecErrorOccurred error: VideoCodec.Error) /// Tells the receiver to the stream opend. func rtmpStreamDidClear(_ stream: RTMPStream) } ``` ### Version 1.5.0 This version introduces `RTMPConnectionDelegate` and `NetStreamDelegate` protocols, and the `RTMPStreamDelegate` protocol might have evolved or been superseded. The provided snippet for 1.5.0 focuses on the other two protocols. *Note: The specific changes to `RTMPStreamDelegate` itself in 1.5.0 are not detailed in the provided text, but related delegate protocols are shown.* ``` -------------------------------- ### Play Stream with Audio Player (2.0.x) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Shows how to set up and play a stream in HaishinKit version 2.0.x, including attaching an AudioPlayer for audio playback. The AudioPlayer wraps an AVAudioEngine for managing audio. ```swift audioPlayer = AudioPlauer(AVAudioEngine()) stream = RTMPStream() stream.attachAudioPlayer(audioPlayer) stream.play("streamName") ``` -------------------------------- ### Add Screen Composition and Overlays with HaishinKit in Swift Source: https://context7.com/haishinkit/haishinkit.swift/llms.txt Shows how to configure screen composition and add overlays like text and picture-in-picture using HaishinKit's Screen object and ScreenObjects. It covers setting screen size, background color, adding text overlays with custom attributes, and embedding a picture-in-picture view from a second camera. Dependencies include HaishinKit. ```swift import HaishinKit Task { @ScreenActor in // Configure screen size await mixer.screen.size = CGSize(width: 1280, height: 720) await mixer.screen.backgroundColor = UIColor.black.cgColor // Add text overlay (watermark, timestamp, etc.) let textOverlay = TextScreenObject() textOverlay.string = "LIVE" textOverlay.attributes = [ .font: UIFont.boldSystemFont(ofSize: 24), .foregroundColor: UIColor.red ] textOverlay.horizontalAlignment = .right textOverlay.verticalAlignment = .top textOverlay.layoutMargin = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 20) try await mixer.screen.addChild(textOverlay) // Add picture-in-picture from second camera let pipView = VideoTrackScreenObject() pipView.track = 1 // Second camera track pipView.size = CGSize(width: 320, height: 180) pipView.cornerRadius = 8.0 pipView.horizontalAlignment = .right pipView.verticalAlignment = .bottom pipView.layoutMargin = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 20) try await mixer.screen.addChild(pipView) // Register video effects on the main video track let chromaKey = ChromaKeyProcessor() _ = await mixer.screen.registerVideoEffect(chromaKey) } // Update text dynamically (e.g., for timestamps) Task { @ScreenActor in textOverlay.string = DateFormatter.localizedString(from: Date(), dateStyle: .none, timeStyle: .medium) } ``` -------------------------------- ### Configure Capture Settings in HaishinKit (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.3.x-APIs-to-1.4.0 Demonstrates how to configure capture settings like FPS and session preset for video streaming. It shows the syntax for version 1.3.0 using a dictionary and for version 1.4.0 using direct property assignments and methods. Version 1.4.0 also includes more granular control over video mirroring, stabilization, autofocus, and exposure. ```swift stream.captureSettings = [ .fps: 30, // FPS .sessionPreset: AVCaptureSession.Preset.medium, // input video width/height // .isVideoMirrored: false, // .continuousAutofocus: false, // use camera autofocus mode // .continuousExposure: false, // use camera exposure mode // .preferredVideoStabilizationMode: AVCaptureVideoStabilizationMode.auto ] ``` ```swift stream.frameRate = 30.0 stream.sessionPreset = .medium stream.videoCapture(for: 0).isVideoMirrored = false // stream.videoCapture(for: 0).preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto let device = AVCaptureDevice.default(for: AVMediaType.video) // or stream.videoCapture(for: 0).device do { try device.lockForConfiguration() device.exposurePointOfInterest = point device.exposureMode = continuousExposure ? .continuousAutoExposure : .autoExpose device.unlockForConfiguration() } catch let error as NSError { logger.error("while locking device for exposurePointOfInterest: (error)") } ``` -------------------------------- ### Initialize RTMPConnection Actor (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Demonstrates the initialization of the RTMPConnection actor in version 2.0.x. It accepts various parameters for configuring the RTMP connection, such as SWF URL, page URL, flash version, timeouts, chunk size, and quality of service. ```swift public actor RTMPConnection { /// The URL of .swf. public let swfUrl: String? /// The URL of an HTTP referer. public let pageUrl: String? /// The name of application. public let flashVer: String /// The time to wait for TCP/IP Handshake done. public let timeout: Int /// The RTMP request timeout value. Defaul value is 500 msec. public let requestTimeout: UInt64 /// The outgoing RTMPChunkSize. public let chunkSize: Int /// The dispatchQos for socket. public let qualityOfService: DispatchQoS /// Creates a new connection. public init( swfUrl: String? = nil, pageUrl: String? = nil, flashVer: String = RTMPConnection.defaultFlashVer, timeout: Int = RTMPConnection.defaultTimeout, requestTimeout: UInt64 = RTMPConnection.defaultRequestTimeout, chunkSize: Int = RTMPConnection.defaultChunkSizeS, qualityOfService: DispatchQoS = .userInitiated) { self.swfUrl = swfUrl self.pageUrl = pageUrl self.flashVer = flashVer self.timeout = timeout self.requestTimeout = requestTimeout self.chunkSize = chunkSize self.qualityOfService = qualityOfService } } ``` -------------------------------- ### Attach Video and Audio (2.0.x) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Shows the process of attaching video and audio input devices using MediaMixer in HaishinKit version 2.0.x. This asynchronous method handles potential errors during device attachment. ```swift mixer = MediaMixer() stream = RTMPStream() // or SRTStream do { try await mixer.attachVideo(AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)) } catch { logger.warn(error) } do { try await mixer.attachAudio(AVCaptureDevice.default(for: .audio)) } catch { logger.warn(error) } mixer.addOutput(stream) ``` -------------------------------- ### Configuring and Displaying a Secondary Video Track in Swift Source: https://github.com/haishinkit/haishinkit.swift/blob/main/HaishinKit/Sources/Docs.docc/videomixing.md Details how to configure a secondary video track (e.g., front camera feed) to be displayed on top of a primary track. This includes setting its corner radius, track number, alignment, margins, size, and optionally applying CIFilter effects. ```swift Task { let videoScreenObject = VideoTrackScreenObject() videoScreenObject.cornerRadius = 32.0 videoScreenObject.track = 1 videoScreenObject.horizontalAlignment = .right videoScreenObject.layoutMargin = .init(top: 16, left: 0, bottom: 0, right: 16) videoScreenObject.size = .init(width: 160 * 2, height: 90 * 2) // You can add a CIFilter-based filter using the registerVideoEffect API. _ = videoScreenObject.registerVideoEffect(MonochromeEffect()) try? await mixer.screen.addChild(videoScreenObject) } ``` -------------------------------- ### Apply Video Effects with Offscreen Mode in HaishinKit.swift Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.8.x-APIs-to-1.9.0 This code snippet shows how to apply video effects by enabling offscreen rendering mode and starting the screen capture in HaishinKit.swift version 1.9.0. This replaces the default VisualEffect application from older versions. ```swift stream.videoMixerSettings.mode = .offscreen stream.screen.startRunning() ``` -------------------------------- ### Use ffplay for SRT Listener Source: https://github.com/haishinkit/haishinkit.swift/blob/main/SRTHaishinKit/Sources/Docs.docc/index.md Demonstrates how to use ffplay to act as an SRT listener, which can be used to test media publishing from HaishinKit. Replace YOUR_IP_ADDRESS with the actual IP address of the machine running ffplay. ```shell $ ffplay -i 'srt://${YOUR_IP_ADDRESS}?mode=listener' ``` -------------------------------- ### Configure SRT Socket Options via URL Source: https://github.com/haishinkit/haishinkit.swift/blob/main/SRTHaishinKit/Sources/Docs.docc/index.md Specifies SRT socket options by appending them as query parameters to the connection URL. This allows for flexible configuration of SRT connections without direct code modification for each option. Example: '?key=value'. ```swift try await connection.connect("srt://host:port?key=value") ``` -------------------------------- ### Configure RTMP Stream Settings (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-0.11.x-APIs-to-1.0.0 Demonstrates how to configure capture, audio, and video settings for an RTMPStream. This includes parameters like FPS, resolution, bitrate, and H.264 profile. Note that 'import VideoToolbox' is required for H.264 profile settings. ```swift var rtmpStream = RTMPStream(connection: rtmpConnection) rtmpStream.captureSettings = [ "fps": 30, // FPS "sessionPreset": AVCaptureSession.Preset.medium.rawValue, // input video width/height "continuousAutofocus": false, // use camera autofocus mode "continuousExposure": false, // use camera exposure mode // .preferredVideoStabilizationMode: AVCaptureVideoStabilizationMode.auto ] rtmpStream.audioSettings = [ "muted": false, // mute audio "bitrate": 32 * 1000, "sampleRate": sampleRate, ] rtmpStream.videoSettings = [ "width": 640, // video output width "height": 360, // video output height "bitrate": 160 * 1000, // video output bitrate "profileLevel": kVTProfileLevel_H264_Baseline_3_1, // H264 Profile require "import VideoToolbox" "maxKeyFrameIntervalDuration": 2, // key frame / sec ] ``` ```swift var rtmpStream = RTMPStream(connection: rtmpConnection) rtmpStream.captureSettings = [ .fps: 30, // FPS .sessionPreset: AVCaptureSession.Preset.medium, // input video width/height .continuousAutofocus: false, // use camera autofocus mode .continuousExposure: false, // use camera exposure mode // .preferredVideoStabilizationMode: AVCaptureVideoStabilizationMode.auto ] rtmpStream.audioSettings = [ .muted: false, // mute audio .bitrate: 32 * 1000, .sampleRate: sampleRate, ] rtmpStream.videoSettings = [ .width: 640, // video output width .height: 360, // video output height .bitrate: 160 * 1000, // video output bitrate .profileLevel: kVTProfileLevel_H264_Baseline_3_1, // H264 Profile require "import VideoToolbox" .maxKeyFrameIntervalDuration: 2, // key frame / sec ] ``` -------------------------------- ### Attach Screen Capture in HaishinKit iOS (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.3.x-APIs-to-1.4.0 Details how to enable screen capturing on iOS. Version 1.3.0 uses `attachScreen` with `ScreenCaptureSession`. Version 1.4.0 introduces `IOUIScreenCaptureUnit`, requiring explicit delegate assignment and starting the capture unit before publishing the stream. ```swift stream.attachScreen(ScreenCaptureSession(shared: UIApplication.shared)) ``` ```swift screen = IOUIScreenCaptureUnit(shared: UIApplication.shared) screen.delegate = stream screen.startRunning() stream.publish("streamName") ``` -------------------------------- ### Connecting a HaishinKit Session Source: https://github.com/haishinkit/haishinkit.swift/blob/main/HaishinKit/Sources/Docs.docc/index.md Establishes a connection for a HaishinKit session, which can be used for either publishing or playback. A callback is provided to handle disconnection events. ```swift try session.connect { print("on disconnected") } ``` -------------------------------- ### Connect and Publish with RTMPStream (Swift) Source: https://github.com/haishinkit/haishinkit.swift/wiki/Moving-from-1.9.x-APIs-to-2.0.0 Demonstrates connecting to an RTMP server and publishing a stream using `RTMPConnection` and `RTMPStream` in version 2.0.0. It includes error handling for connection and stream request failures, utilizing async/await syntax. ```swift connection = RTMPConnection() stream = RTMPStream(connection: connection) do { let response = try await connection.connect(preference.uri ?? "") try await stream.publish(Preference.default.streamName) // or play } catch RTMPConnection.Error.requestFailed(let response) { logger.warn(response) } catch RTMPStream.Error.requestFailed(let response) { logger.warn(response) } catch { logger.warn(error) } ```