Audio auf iOS & macOS mit FFmpeg extrahieren
Speichern Sie mit Swift die erste Audiospur eines Videos als reine Audiodatei im Format
.m4a. Auf macOS kann FFmpeg kompatibles Audio ohne erneutes Encoding
kopieren; auf iOS exportiert AVFoundation AAC-Audio innerhalb Ihrer App.
Die folgenden Beispiele erhalten vorhandene Zieldateien und melden fehlgeschlagene Exporte.
Einführung in FFmpeg auf Apple-Plattformen
FFmpeg kann einen kompatiblen Audiostream ohne erneutes Encoding kopieren. Das macOS-Beispiel startet eine lokale ausführbare FFmpeg-Datei über die Process-API von Foundation, die auf iOS nicht verfügbar ist. Verwenden Sie für iOS das integrierte AVFoundation-Framework von Apple und dessen Exportvoreinstellung für Apple M4A.
Audioextraktion auf Apple-Plattformen einrichten
FFmpegKit wurde eingestellt. Die Mitteilung des Autors zur Einstellung und Informationen zur Fortführung verweisen auf FFmpegKitNext, das als Quellcode bereitgestellt wird. Diese Beispiele benötigen weder FFmpegKit-Binärdateien noch Swift-Pakete von Drittanbietern.
FFmpeg und Swift vorbereiten
Beginnen Sie mit einem Mac, auf dem Xcode bereits installiert ist und eine funktionierende
FFmpeg-Distribution einschließlich ffprobe über
PATH verfügbar ist. Apples
Xcode-Downloads und die
FFmpeg-Downloadseite führen Installationsmöglichkeiten auf.
Diese Anleitung setzt voraus, dass diese Werkzeuge installiert sind; sie installiert keine
Systemsoftware.
Führen Sie diese Prüfungen in einem neuen Arbeitsverzeichnis aus, bevor Sie die Swift-Dateien speichern:
command -v swiftc >/dev/null &&
command -v ffmpeg >/dev/null &&
command -v ffprobe >/dev/null &&
swiftc --version && ffmpeg -version && ffprobe -version
Die getestete Umgebung umfasst macOS 26.6.2, Xcode 27.0, Swift 6.4 im Swift-5-Sprachmodus, FFmpeg und ffprobe 9.0.2 sowie einen Simulator für iOS 27.0. Ältere Betriebssystemversionen und physische Geräte liegen außerhalb des getesteten Umfangs dieser Anleitung. Die vollständige Xcode-Installation stellt das iOS-SDK und den Simulator bereit.
Verwenden Sie eine intakte lokale MP4-Datei mit einer AAC-Audiospur, die im Arbeitsverzeichnis
unter dem Namen sample video.mp4 liegt. Führen Sie jeweils nur eine Extraktion aus.
Das iOS-Beispiel verwendet eine Datei im App-Bundle und den app-eigenen Documents-Speicher;
externe Dateianbieter, Berechtigungen für die Dateiauswahl und Exporte im Hintergrund werden
nicht behandelt.
Audiostreams aus Videodateien extrahieren
Die FFmpeg-Variante wählt mit -map 0:a:0 den ersten Audiostream aus.
Das Kopieren eines Streams in M4A erfordert einen kompatiblen Codec wie AAC.
Die AVFoundation-Variante wählt die erste Audiospur aus und exportiert sie mit
AVAssetExportPresetAppleM4A. Dabei kann das Audio neu codiert werden; es entsteht keine
bytegenaue Kopie.
Schrittweise Anleitung zur Audioextraktion
Speichern Sie diesen Code als AudioExtraction.swift. Jeder Vorgang schreibt in ein
neues Verzeichnis neben dem Ziel, stellt das Ergebnis erst nach erfolgreichem Abschluss bereit
und entfernt bei einem Fehler seine temporären Dateien.
Übergeben Sie lokale Datei-URLs, die Ihre App lesen kann, und eine Ausgabe-URL in einem
vorhandenen beschreibbaren Verzeichnis. Das temporäre Verzeichnis wird nach einem regulären
Erfolg oder einem ausgelösten Fehler entfernt. Wird der Prozess zwangsweise beendet, kann es
zurückbleiben.
import AVFoundation
import Foundation
enum AudioExtractionError: Error {
case invalidPaths, outputExists, noAudio, exportUnavailable, exportFailed
}
private func extractionDirectory(inputURL: URL, outputURL: URL) throws -> URL {
let files = FileManager.default
guard inputURL.isFileURL, outputURL.isFileURL,
files.isReadableFile(atPath: inputURL.path),
outputURL.pathExtension.lowercased() == "m4a" else {
throw AudioExtractionError.invalidPaths
}
guard !files.fileExists(atPath: outputURL.path) else {
throw AudioExtractionError.outputExists
}
let directory = outputURL.deletingLastPathComponent()
.appendingPathComponent("audio-\(UUID().uuidString)", isDirectory: true)
try files.createDirectory(at: directory, withIntermediateDirectories: false)
return directory
}
#if os(macOS)
// This blocking function belongs on a worker queue when called from a macOS UI.
func extractAudioWithFFmpeg(from videoURL: URL, to outputURL: URL, ffmpegURL: URL) throws {
let directory = try extractionDirectory(inputURL: videoURL, outputURL: outputURL)
defer { try? FileManager.default.removeItem(at: directory) }
let temporaryOutput = directory.appendingPathComponent("audio.m4a")
let process = Process()
process.executableURL = ffmpegURL
process.environment = [:]
process.arguments = [
"-nostdin", "-hide_banner", "-loglevel", "error", "-n",
"-protocol_whitelist", "file,pipe", "-i", videoURL.path,
"-map", "0:a:0", "-vn", "-c:a", "copy", "-f", "ipod", temporaryOutput.path
]
// In this command-line example, diagnostics go to stderr without a pipe that could fill up.
process.standardError = FileHandle.standardError
try process.run()
process.waitUntilExit()
guard process.terminationReason == .exit, process.terminationStatus == 0 else {
throw AudioExtractionError.exportFailed
}
try FileManager.default.moveItem(at: temporaryOutput, to: outputURL)
}
#endif
func extractAudioWithAVFoundation(from videoURL: URL, to outputURL: URL) async throws {
let directory = try extractionDirectory(inputURL: videoURL, outputURL: outputURL)
defer { try? FileManager.default.removeItem(at: directory) }
let temporaryOutput = directory.appendingPathComponent("audio.m4a")
let asset = AVURLAsset(url: videoURL)
guard let sourceTrack = try await asset.loadTracks(withMediaType: .audio).first else {
throw AudioExtractionError.noAudio
}
let composition = AVMutableComposition()
guard let track = composition.addMutableTrack(
withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid
) else {
throw AudioExtractionError.exportUnavailable
}
let timeRange = try await sourceTrack.load(.timeRange)
try track.insertTimeRange(timeRange, of: sourceTrack, at: .zero)
guard let session = AVAssetExportSession(
asset: composition, presetName: AVAssetExportPresetAppleM4A
), session.supportedFileTypes.contains(.m4a) else {
throw AudioExtractionError.exportUnavailable
}
session.outputURL = temporaryOutput
session.outputFileType = .m4a
await session.export()
guard session.status == .completed else {
throw AudioExtractionError.exportFailed
}
try FileManager.default.moveItem(at: temporaryOutput, to: outputURL)
}
Anwendungsbeispiel
Speichern Sie für ein macOS-Kommandozeilenprogramm diesen Einstiegspunkt als
ExtractAudio.swift neben der vorherigen Datei. Das Programm wartet auf den Abschluss
der Extraktion und gibt bei einem Fehler einen Exit-Status ungleich null zurück.
import Foundation
@main
struct ExtractAudio {
static func main() async {
let arguments = CommandLine.arguments
guard arguments.count == 4 || arguments.count == 5 else {
FileHandle.standardError.write(Data(
"Usage: extract-audio avfoundation|ffmpeg input output.m4a [ffmpeg-path]\n".utf8
))
exit(1)
}
do {
let input = URL(fileURLWithPath: arguments[2])
let output = URL(fileURLWithPath: arguments[3])
switch arguments[1] {
case "ffmpeg" where arguments.count == 5:
try extractAudioWithFFmpeg(
from: input, to: output, ffmpegURL: URL(fileURLWithPath: arguments[4])
)
case "avfoundation" where arguments.count == 4:
try await extractAudioWithAVFoundation(from: input, to: output)
default:
throw AudioExtractionError.invalidPaths
}
print("Audio extraction successful: \(output.path)")
} catch {
FileHandle.standardError.write(Data("Audio extraction failed.\n".utf8))
exit(1)
}
}
}
swiftc -parse-as-library AudioExtraction.swift ExtractAudio.swift -o extract-audio &&
./extract-audio ffmpeg "sample video.mp4" copied.m4a "$(command -v ffmpeg)" &&
./extract-audio avfoundation "sample video.mp4" encoded.m4a
Der Befehl schreibt copied.m4a und encoded.m4a neben die
Quelldateien. Öffnen Sie beide Dateien in einem Audioplayer und vergleichen Sie ihre Tonspur und
Dauer mit der Eingabe. Beim Kopieren des Streams bleibt der codierte AAC-Stream erhalten;
AVFoundation kann ihn neu codieren. Die Dateien müssen daher nicht bytegleich sein.
Das iOS-Beispiel ausführen
Erstellen Sie in Xcode ein iOS-App-Projekt mit SwiftUI-Oberfläche. Verwenden Sie den
Swift-5-Sprachmodus und wählen Sie den installierten Simulator für iOS 27 aus. Fügen Sie
AudioExtraction.swift zum App-Target hinzu und
sample video.mp4 zu dessen Build-Phase „Copy Bundle Resources“.
Fügen Sie den macOS-Einstiegspunkt ExtractAudio.swift nicht zu diesem Target hinzu.
Ersetzen Sie die generierten Dateien für die App und die Inhaltsansicht durch diese einzelne
Datei AudioDemoApp.swift:
import Foundation
import SwiftUI
@main
struct AudioDemoApp: App {
var body: some Scene {
WindowGroup { AudioDemoView() }
}
}
struct AudioDemoView: View {
@State private var started = false
@State private var message = "Extracting audio…"
@State private var output: URL?
var body: some View {
VStack(spacing: 16) {
Text(message)
if let output {
ShareLink("Share audio", item: output)
}
}
.padding()
.task {
guard !started else { return }
started = true
do {
guard let input = Bundle.main.url(
forResource: "sample video", withExtension: "mp4"
) else { throw AudioExtractionError.invalidPaths }
let documents = try FileManager.default.url(
for: .documentDirectory, in: .userDomainMask,
appropriateFor: nil, create: true
)
let destination = documents.appendingPathComponent("encoded.m4a")
try await extractAudioWithAVFoundation(from: input, to: destination)
output = destination
message = "Audio saved: encoded.m4a"
} catch {
message = "Extraction failed. Check the input and destination."
}
}
}
}
Kompilieren und starten Sie die App. Sie exportiert einmal beim Öffnen der Ansicht und zeigt
nach Abschluss Audio saved: encoded.m4a an. Verwenden Sie
Share audio, um das Ergebnis in einer anderen
App zu öffnen oder zu speichern. Die Quelle bleibt im Bundle;
encoded.m4a liegt im Documents-Verzeichnis der App. Beim nächsten Start wird
der Export wegen der vorhandenen Ausgabedatei verweigert. Wählen Sie für einen weiteren Export
einen neuen Zieldateinamen oder entfernen Sie die vorhandene Ausgabedatei gezielt.
Eine Mikrofonberechtigung ist nicht nötig, da die App eine Datei liest.
Häufige Probleme beheben
- Dateizugriff: Prüfen Sie, ob die Eingabe vorhanden und das übergeordnete Zielverzeichnis beschreibbar ist. Die CLI ist ein lokales Terminalprogramm; FFmpeg in eine macOS-App mit Sandbox einzubinden, erfordert zusätzliche Arbeit. Auf iOS bleibt dieses Beispiel bewusst innerhalb des App-Bundles und des Documents-Verzeichnisses.
- Vorhandene Ausgabe: Wählen Sie ein neues Ziel. Die Beispiele ersetzen niemals eine vorhandene Datei.
- Keine Audiospur: Ein Video ohne Audiospur führt zu einem Fehler und erzeugt keine neue Ausgabe. Eine Spur, die Stille enthält, ist dennoch eine Audiospur und kann exportiert werden.
- Nicht unterstützte Codecs: Das Kopieren eines Streams mit FFmpeg kann fehlschlagen, wenn der Audiocodec nicht in M4A gespeichert werden kann. Codieren Sie stattdessen mit AAC neu oder verwenden Sie die AVFoundation-Variante für Eingaben, die Apples Framework unterstützt.
- Diagnose: Untersuchen Sie die stderr-Ausgabe von FFmpeg lokal. Prüfen Sie zur Fehlersuche
mit AVFoundation
session.erroran der Fehlerprüfstelle, ohne interne Fehler in der Benutzeroberfläche Ihrer App anzuzeigen. - Beschädigte Aufnahmen: Ein erfolgreicher Export ist keine Integritätsprüfung. Medienwerkzeuge können unvollständige Eingaben wiederherstellen; vergleichen Sie die tatsächliche Dauer und den Ton der Ausgabe, bevor Sie sie verwenden.
Leistung optimieren
- Kopieren Sie kompatibles Audio mit
-c:a copy, wenn der codierte Stream erhalten bleiben muss. - Führen Sie blockierende Wartevorgänge auf den FFmpeg-Prozess außerhalb des Hauptthreads einer macOS-App aus.
- Warten Sie wie in den Beispielen asynchron auf AVFoundation-Exporte, statt die gesamte Aufnahme
in einen Wert vom Typ
Dataeinzulesen. Die weiterhin verwendeten APIsexport()undstatuserzeugen mit aktuellen SDKs Veraltungswarnungen; Apples Exportanleitung beschreibt die neuere Export-API für Anwendungen, die auf neuere Betriebssystemversionen ausgerichtet sind.
Weitere Ressourcen
Weitere Informationen zur Auswahl von Streams und zur Konfiguration von Exporten finden Sie in der FFmpeg-Dokumentation zur Streamauswahl und in Apples Anleitung zum Medienexport.
Für einen serverseitigen Workflow steht der Dienst für Audio-Encoding von Transloadit zur Verfügung.
