### Create a SwiftUI Video Player Source: https://github.com/skiptools/skip-av/blob/main/README.md Implement a reusable SwiftUI View for playing video content using AVKit and SkipAV's VideoPlayer. This example includes basic play/pause and seek functionality. ```swift import SwiftUI import AVKit struct PlayerView: View { @State var player = AVPlayer(playerItem: AVPlayerItem(url: URL(string: "https://skip.dev/assets/introduction.mov")!)) @State var isPlaying: Bool = false var body: some View { VStack { Button { isPlaying ? player.pause() : player.play() isPlaying = !isPlaying player.seek(to: .zero) } label: { Image(systemName: isPlaying ? "stop" : "play") .padding() } VideoPlayer(player: player) } } } ``` -------------------------------- ### Configure Audio Encoding Settings Source: https://context7.com/skiptools/skip-av/llms.txt Defines settings for AVAudioRecorder, including format, sample rate, channels, quality, and bitrate. Shows examples for AAC and Linear PCM formats. ```swift import AVFoundation // High-quality AAC mono recording let settings: [String: Any] = [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), // AAC format AVSampleRateKey: 44100, // 44.1 kHz AVNumberOfChannelsKey: 1, // mono AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue, // 96 AVEncoderBitRateKey: 128000, // 128 kbps ] // Linear PCM stereo settings let pcmSettings: [String: Any] = [ AVFormatIDKey: Int(kAudioFormatLinearPCM), AVSampleRateKey: 48000, AVNumberOfChannelsKey: 2, AVLinearPCMBitDepthKey: 16, AVLinearPCMIsBigEndianKey: false, AVLinearPCMIsFloatKey: false, ] // AVAudioQuality levels: .min(0), .low(32), .medium(64), .high(96), .max(127) let quality = AVAudioQuality.max print("Max quality raw value: \(quality.rawValue)") // 127 ``` -------------------------------- ### VideoPlayer - Basic Usage Source: https://context7.com/skiptools/skip-av/llms.txt Demonstrates how to use the VideoPlayer SwiftUI view for basic video playback with play/pause controls. ```APIDOC ## VideoPlayer — SwiftUI Video Playback View `VideoPlayer` is a SwiftUI `View` that wraps `AVPlayer` and renders video with built-in playback controls. On Android it is backed by `androidx.media3.ui.PlayerView` with ExoPlayer. ```swift import SwiftUI import AVKit struct PlayerView: View { @State var player = AVPlayer( playerItem: AVPlayerItem(url: URL(string: "https://example.com/video.mov")!) ) @State var isPlaying: Bool = false var body: some View { VStack { Button { isPlaying ? player.pause() : player.play() isPlaying.toggle() player.seek(to: .zero) } label: { Image(systemName: isPlaying ? "stop" : "play") .padding() } VideoPlayer(player: player) .frame(height: 300) } } } ``` ``` -------------------------------- ### Record and Playback Audio with AVAudioRecorder Source: https://context7.com/skiptools/skip-av/llms.txt Sets up an AVAudioRecorder to record audio to a file and an AVAudioPlayer to play it back. Requires RECORD_AUDIO permission on Android. ```swift import SwiftUI import AVFoundation struct AudioRecorderView: View { @State var recorder: AVAudioRecorder? @State var player: AVAudioPlayer? @State var isRecording = false @State var message = "" var outputURL: URL { #if SKIP let ctx = ProcessInfo.processInfo.androidContext let f = java.io.File(ctx.filesDir, "capture.m4a") return URL(fileURLWithPath: f.absolutePath) #else return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask) .first!.appendingPathComponent("capture.m4a") #endif } var body: some View { VStack(spacing: 16) { Button(isRecording ? "Stop" : "Record") { isRecording ? stopRecording() : startRecording() } Button("Play Back") { playBack() } Text(message) } .padding() } func startRecording() { let settings: [String: Any] = [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 44100, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue ] do { recorder = try AVAudioRecorder(url: outputURL, settings: settings) recorder?.record() isRecording = true message = "Recording…" } catch { message = "Failed: \(error.localizedDescription)" } } func stopRecording() { recorder?.stop() isRecording = false message = "Saved to \(outputURL.lastPathComponent)" } func playBack() { guard FileManager.default.fileExists(atPath: outputURL.path) else { message = "No recording found." return } do { player = try AVAudioPlayer(contentsOf: outputURL) player?.play() message = "Playing \(player?.duration ?? 0)s" } catch { message = "Playback error: \(error.localizedDescription)" } } } ``` -------------------------------- ### AVAudioPlayer API Source: https://github.com/skiptools/skip-av/blob/main/README.md Provides full support for AVAudioPlayer, enabling initialization with URLs or data, playback control, and property access for volume, rate, and current time. ```APIDOC ## AVAudioPlayer API ### Description Full support for AVAudioPlayer, including initialization, playback control, and property access. ### Methods - `init(contentsOf url: URL) throws` - `init(data: Data) throws` - `func prepareToPlay() -> Bool` - `func play()` - `func pause()` - `func stop()` ### Properties - `var isPlaying: Bool` - `var duration: TimeInterval` - `var numberOfLoops: Int` - `var volume: Double` - `var rate: Double` - `var currentTime: TimeInterval` - `var url: URL?` - `var data: Data?` ``` -------------------------------- ### AVAudioRecorder API Source: https://github.com/skiptools/skip-av/blob/main/README.md Offers full support for AVAudioRecorder, allowing initialization with URLs and settings, recording control, and access to recording status and properties. ```APIDOC ## AVAudioRecorder API ### Description Full support for AVAudioRecorder, including initialization, recording control, and access to recording status and properties. ### Methods - `init(url: URL, settings: [String: Any]) throws` - `func prepareToRecord() -> Bool` - `func record()` - `func pause()` - `func stop()` - `func deleteRecording() -> Bool` ### Properties - `var isRecording: Bool` - `var url: URL` - `var settings: [String: Any]` - `var currentTime: TimeInterval` ### Methods for Power Monitoring - `func peakPower(forChannel channelNumber: Int) -> Float` - `func averagePower(forChannel channelNumber: Int) -> Double` ``` -------------------------------- ### AVPlayer - Programmatic Playback Source: https://context7.com/skiptools/skip-av/llms.txt Illustrates programmatic control of audio/video playback using AVPlayer, including play, pause, seek, volume, and rate adjustments. ```APIDOC ## AVPlayer — Programmatic Video/Audio Playback `AVPlayer` wraps ExoPlayer on Android and drives playback for a single `AVPlayerItem`. Supports play, pause, seek, volume, and rate control. ```swift import AVKit // Initialize with a URL let player = AVPlayer(url: URL(string: "https://example.com/audio.mp3")!) player.volume = 0.8 // 0.0–1.0 player.rate = Float(1.5) // playback speed player.play() // Seek to 30 seconds let thirtySeconds = CMTime(seconds: 30.0, preferredTimescale: 1) player.seek(to: thirtySeconds) // Check playback status switch player.timeControlStatus { case .playing: print("Currently playing") case .paused: print("Paused") case .waitingToPlayAtSpecifiedRate: print("Buffering... reason: \(player.reasonForWaitingToPlay?.rawValue ?? "unknown")") } player.pause() // Replace current item let newItem = AVPlayerItem(url: URL(string: "https://example.com/other.mp4")!) player.replaceCurrentItem(with: newItem) player.play() ``` ``` -------------------------------- ### AVPlayerItem API Source: https://github.com/skiptools/skip-av/blob/main/README.md Offers medium support for AVPlayerItem, specifically for initialization with a URL. ```APIDOC ## AVPlayerItem API ### Description Medium support for AVPlayerItem, primarily for initialization. ### Initializers - `init(url: URL)` ``` -------------------------------- ### Swift AVAudioRecorder and AVAudioPlayer Implementation Source: https://github.com/skiptools/skip-av/blob/main/README.md This Swift code implements audio recording and playback using AVAudioRecorder and AVAudioPlayer. It includes platform-specific logic for Android using '#if SKIP'. Ensure necessary permissions are requested for audio recording on Android. ```swift import SwiftUI import AVFoundation struct AudioPlayground: View { @State var isRecording: Bool = false @State var errorMessage: String? = nil @State var audioRecorder: AVAudioRecorder? @State var audioPlayer: AVAudioPlayer? var body: some View { #if SKIP let context = androidx.compose.ui.platform.LocalContext.current #endif return VStack { Button(isRecording ? "Stop Recording" : "Start Recording") { self.isRecording ? self.stopRecording() : self.startRecording() } Button("Play Recording") { try? self.playRecording() } if let errorMessage { Text(errorMessage) .foregroundColor(.red) } } .padding() #if SKIP .onAppear { requestAudioRecordingPermission(context: context) } #endif } var captureURL: URL { get { #if SKIP let context = ProcessInfo.processInfo.androidContext let file = java.io.File(context.filesDir, "recording.m4a") return URL(fileURLWithPath: file.absolutePath) #else return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask) .first!.appendingPathComponent("recording.m4a") #endif } } func startRecording() { do { #if !SKIP setupAudioSession() #endif self.audioRecorder = try AVAudioRecorder(url: captureURL, settings: [AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 12000, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue]) } catch { print(error.localizedDescription) } audioRecorder?.record() isRecording = true } func stopRecording() { isRecording = false audioRecorder?.stop() } func playRecording() throws { do { guard FileManager.default.fileExists(atPath: captureURL.path) else { errorMessage = "Recording file does not exist." return } audioPlayer = try AVAudioPlayer(contentsOf: captureURL) audioPlayer?.play() errorMessage = "" } catch { logger.error("Could not play audio: \(error.localizedDescription)") errorMessage = "Could not play audio: \(error.localizedDescription)" } } #if SKIP func requestAudioRecordingPermission(context: android.content.Context) { guard let activity = context as? android.app.Activity else { return } // You must also list these permissions in your Manifest.xml let permissions = listOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) androidx.core.app.ActivityCompat.requestPermissions(activity, permissions.toTypedArray(), 1) } #else func setupAudioSession() { let session = AVAudioSession.sharedInstance() do { try session.setCategory(.playAndRecord, mode: .default) try session.setActive(true) } catch { errorMessage = "Failed to setup audio session: \(error.localizedDescription)" } } #endif } ``` -------------------------------- ### VideoPlayer - Fullscreen Support (Android) Source: https://context7.com/skiptools/skip-av/llms.txt Shows how to enable and manage fullscreen mode for the VideoPlayer on Android using a binding. ```APIDOC ## VideoPlayer — Fullscreen Support (Android-only) An Android-specific initializer accepts a `Binding` to track and respond to fullscreen mode changes triggered by the player's controls. ```swift import SwiftUI import AVKit struct FullscreenPlayerView: View { @State var player = AVPlayer(url: URL(string: "https://example.com/video.mov")!) @State var isFullscreen: Bool = false var body: some View { VideoPlayer(player: player, isFullscreen: $isFullscreen) .frame(height: isFullscreen ? .infinity : 300) .onChange(of: isFullscreen) { print("Fullscreen mode: \(full)") } } } ``` ``` -------------------------------- ### AVAudioPlayerDelegate for Playback Events Source: https://context7.com/skiptools/skip-av/llms.txt Implement AVAudioPlayerDelegate to receive callbacks for playback completion and decode errors. Useful for managing audio playback state and handling errors gracefully. ```swift import AVFoundation class AudioController: AVAudioPlayerDelegate { var player: AVAudioPlayer? func loadAndPlay(url: URL) { do { player = try AVAudioPlayer(contentsOf: url) player?.delegate = self player?.play() } catch { print("Error: \(error.localizedDescription)") } } func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { print("Finished playing. Success: \(flag)") } func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) { print("Decode error: \(error?.localizedDescription ?? "unknown")") } } ``` -------------------------------- ### VideoPlayer API Source: https://github.com/skiptools/skip-av/blob/main/README.md Provides high support for VideoPlayer, allowing initialization with an AVPlayer instance. ```APIDOC ## VideoPlayer API ### Description High support for VideoPlayer, with initialization capabilities. ### Initializers - `init(player: AVPlayer?)` ``` -------------------------------- ### SwiftUI VideoPlayer Fullscreen (Android) Source: https://context7.com/skiptools/skip-av/llms.txt Android-specific initializer for VideoPlayer that supports fullscreen mode tracking via a Binding. Requires SwiftUI and AVKit imports. ```swift import SwiftUI import AVKit struct FullscreenPlayerView: View { @State var player = AVPlayer(url: URL(string: "https://example.com/video.mov")!) @State var isFullscreen: Bool = false var body: some View { VideoPlayer(player: player, isFullscreen: $isFullscreen) .frame(height: isFullscreen ? .infinity : 300) .onChange(of: isFullscreen) { print("Fullscreen mode: \(full)") } } } ``` -------------------------------- ### AVPlayer API Source: https://github.com/skiptools/skip-av/blob/main/README.md Provides medium support for AVPlayer, including initialization and basic playback controls like play, pause, and seek. ```APIDOC ## AVPlayer API ### Description Medium support for AVPlayer, covering initialization and essential playback controls. ### Initializers - `init()` - `init(playerItem: AVPlayerItem?)` - `init(url: URL)` ### Methods - `func play()` - `func pause()` - `func seek(to time: CMTime)` ``` -------------------------------- ### Monitor Audio Amplitude with AVAudioRecorder Source: https://context7.com/skiptools/skip-av/llms.txt Exposes peak and average power per channel while recording, useful for building level meters. Shows how to check recording status and duration. ```swift import AVFoundation let settings: [String: Any] = [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 44100, AVNumberOfChannelsKey: 1, ] if let recorder = try? AVAudioRecorder(url: outputURL, settings: settings) { recorder.record() // Poll amplitude (e.g., from a Timer) let peak = recorder.peakPower(forChannel: 0) // Float, 0.0–1.0 let avg = recorder.averagePower(forChannel: 0) // Double, 0.0–1.0 print("Peak: \(peak), Avg: \(avg)") print("Recording duration: \(recorder.currentTime)s") print("Is recording: \(recorder.isRecording)") // Delete the recording file let deleted = recorder.deleteRecording() print("Deleted: \(deleted)") } ``` -------------------------------- ### SwiftUI VideoPlayer with Controls Source: https://context7.com/skiptools/skip-av/llms.txt A SwiftUI View for video playback with built-in controls. On Android, it uses ExoPlayer. Requires SwiftUI and AVKit imports. ```swift import SwiftUI import AVKit struct PlayerView: View { @State var player = AVPlayer( playerItem: AVPlayerItem(url: URL(string: "https://example.com/video.mov")!) ) @State var isPlaying: Bool = false var body: some View { VStack { Button { isPlaying ? player.pause() : player.play() isPlaying.toggle() player.seek(to: .zero) } label: { Image(systemName: isPlaying ? "stop" : "play") .padding() } VideoPlayer(player: player) .frame(height: 300) } } } ``` -------------------------------- ### AVQueuePlayer for Multi-Item Playback Source: https://context7.com/skiptools/skip-av/llms.txt Manage sequential playback of multiple AVPlayerItems. Supports adding, inserting, advancing, removing, and clearing items from the playback queue. ```swift import AVKit let items = [ AVPlayerItem(url: URL(string: "https://example.com/track1.mp3")!), AVPlayerItem(url: URL(string: "https://example.com/track2.mp3")!), AVPlayerItem(url: URL(string: "https://example.com/track3.mp3")!), ] let queuePlayer = AVQueuePlayer(items: items) queuePlayer.play() // Add an item after a specific one let bonus = AVPlayerItem(url: URL(string: "https://example.com/bonus.mp3")!) queuePlayer.insert(bonus, after: items[1]) // Skip to next queuePlayer.advanceToNextItem() // Inspect queue let remaining = queuePlayer.items() print("Remaining items: \(remaining.count)") // Remove a specific item queuePlayer.remove(items[2]) // Clear everything queuePlayer.removeAllItems() ``` -------------------------------- ### AVAudioPlayer for Audio File Playback Source: https://context7.com/skiptools/skip-av/llms.txt Play audio from a file URL or Data. Supports playback controls like play, pause, stop, looping, volume, rate, and seeking. Includes cross-platform file path determination. ```swift import AVFoundation // Determine file path cross-platform #if SKIP let context = ProcessInfo.processInfo.androidContext let file = java.io.File(context.filesDir, "recording.m4a") let audioURL = URL(fileURLWithPath: file.absolutePath) #else let audioURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask) .first!.appendingPathComponent("recording.m4a") #endif do { let player = try AVAudioPlayer(contentsOf: audioURL) player.volume = 1.0 // full volume player.numberOfLoops = -1 // infinite loop (-1), or N times player.rate = Float(1.0) // normal speed let ready = player.prepareToPlay() print("Ready: \(ready), Duration: \(player.duration)s") player.play() // Seek to 5 seconds in player.currentTime = 5.0 print("Now at: \(player.currentTime)s") player.pause() player.stop() } catch { print("Failed to load audio: \(error.localizedDescription)") } ``` -------------------------------- ### Request RECORD_AUDIO Permission on Android Source: https://context7.com/skiptools/skip-av/llms.txt This Swift code snippet demonstrates how to request RECORD_AUDIO, READ_EXTERNAL_STORAGE, and WRITE_EXTERNAL_STORAGE permissions on Android within a SwiftUI view. Ensure these permissions are also declared in your AndroidManifest.xml. The request is triggered when the view appears. ```swift import SwiftUI import AVFoundation struct PermissionAwareView: View { var body: some View { Text("Audio Recorder") #if SKIP .onAppear { requestPermissions() } #endif } #if SKIP func requestPermissions() { let context = ProcessInfo.processInfo.androidContext guard let activity = context as? android.app.Activity else { return } // Also add to AndroidManifest.xml: // let permissions = listOf( android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE ) androidx.core.app.ActivityCompat.requestPermissions( activity, permissions.toTypedArray(), 1 // requestCode ) } #endif } ``` -------------------------------- ### AVPlayerItem Notifications Source: https://context7.com/skiptools/skip-av/llms.txt Observe playback lifecycle events for an AVPlayerItem using NotificationCenter publishers. Useful for reacting to end of playback, time jumps, or stalls. ```swift import SwiftUI import AVKit struct EventAwarePlayerView: View { @State var player = AVPlayer( playerItem: AVPlayerItem(url: URL(string: "https://example.com/video.mp4")!) ) var body: some View { VideoPlayer(player: player) .onReceive( NotificationCenter.default.publisher(for: AVPlayerItem.didPlayToEndTimeNotification) ) { _ in print("Playback finished — rewinding to start") player.seek(to: .zero) player.play() } .onReceive( NotificationCenter.default.publisher(for: AVPlayerItem.timeJumpedNotification) ) { _ in print("Seek or time jump occurred") } .onReceive( NotificationCenter.default.publisher(for: AVPlayerItem.playbackStalledNotification) ) { _ in print("Playback stalled — buffering") } } } ``` -------------------------------- ### AVPlayer Programmatic Control Source: https://context7.com/skiptools/skip-av/llms.txt Programmatically control video and audio playback using AVPlayer. Supports play, pause, seek, volume, and rate adjustments. On Android, it wraps ExoPlayer. Requires AVKit import. ```swift import AVKit // Initialize with a URL let player = AVPlayer(url: URL(string: "https://example.com/audio.mp3")!) player.volume = 0.8 // 0.0–1.0 player.rate = Float(1.5) // playback speed player.play() // Seek to 30 seconds let thirtySeconds = CMTime(seconds: 30.0, preferredTimescale: 1) player.seek(to: thirtySeconds) // Check playback status switch player.timeControlStatus { case .playing: print("Currently playing") case .paused: print("Paused") case .waitingToPlayAtSpecifiedRate: print("Buffering... reason: \(player.reasonForWaitingToPlay?.rawValue ?? "unknown")") } player.pause() // Replace current item let newItem = AVPlayerItem(url: URL(string: "https://example.com/other.mp4")!) player.replaceCurrentItem(with: newItem) player.play() ``` -------------------------------- ### Add SkipAV Dependency to Package.swift Source: https://github.com/skiptools/skip-av/blob/main/README.md Include the SkipAV framework in your Swift Package Manager project by adding its Git URL as a dependency. ```swift let package = Package( name: "my-package", products: [ .library(name: "MyProduct", targets: ["MyTarget"]) ], dependencies: [ .package(url: "https://source.skip.dev/skip-av.git", from: "1.0.0") ], targets: [ .target(name: "MyTarget", dependencies: [ .product(name: "SkipAV", package: "skip-av") ]) ] ) ``` -------------------------------- ### AVPlayerLooper for Seamless Looping Source: https://context7.com/skiptools/skip-av/llms.txt Enable continuous looping of a single AVPlayerItem using AVQueuePlayer. The loop is active as long as the AVPlayerLooper instance is retained. ```swift import AVKit let item = AVPlayerItem(url: URL(string: "https://example.com/loop.mp4")!) let queuePlayer = AVQueuePlayer(playerItem: item, false) // Retain the looper — releasing it stops looping var looper: AVPlayerLooper? = AVPlayerLooper(player: queuePlayer, templateItem: item) queuePlayer.play() // Stop looping by releasing the looper looper = nil ``` -------------------------------- ### Represent Time with CMTime Source: https://context7.com/skiptools/skip-av/llms.txt CMTime represents a rational time value (value/timescale) used by AVPlayer.seek(to:) and other time-based APIs. Demonstrates creating CMTime from seconds or raw values and seeking. ```swift import AVKit // Create from seconds let fiveSeconds = CMTime(seconds: 5.0, preferredTimescale: 600) print("Value: \(fiveSeconds.value), Scale: \(fiveSeconds.timescale)") // Value: 3000, Scale: 600 // Create from raw value/timescale let twoMinutes = CMTime(value: 120_000, timescale: 1000) // Seek to zero (start) player.seek(to: CMTime.zero) // Seek to 90 seconds player.seek(to: CMTime(seconds: 90.0, preferredTimescale: 600)) ``` -------------------------------- ### Handle AVPlayerItem Notifications in SwiftUI Source: https://github.com/skiptools/skip-av/blob/main/README.md Attach event handlers to the VideoPlayer to receive notifications from AVPlayerItem, such as when playback ends or the time jumps. ```swift VideoPlayer(player: player) .onReceive(NotificationCenter.default.publisher(for: AVPlayerItem.didPlayToEndTimeNotification)) { event in logger.info("didPlayToEndTimeNotification: \(event)") } .onReceive(NotificationCenter.default.publisher(for: AVPlayerItem.timeJumpedNotification)) { event in logger.info("timeJumpedNotification: \(event)") } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.