Extraire l’audio sur iOS et macOS avec FFmpeg
Utilisez Swift pour enregistrer la première piste audio d’une vidéo dans un fichier
.m4a ne contenant que de l’audio. Sur macOS, FFmpeg peut copier l’audio
compatible sans réencodage ; sur iOS, AVFoundation exporte l’audio en AAC dans votre application.
Les exemples ci-dessous préservent les fichiers de destination existants et signalent les échecs
d’export.
Introduction à FFmpeg sur les plateformes Apple
FFmpeg peut copier un flux audio compatible sans le réencoder. L’exemple macOS lance un exécutable FFmpeg local via l’API Process de Foundation, qui n’est pas disponible sur iOS. Sur iOS, utilisez le framework AVFoundation intégré d’Apple et son préréglage d’export M4A.
Configurer l’extraction audio sur les plateformes Apple
Le projet FFmpegKit a été arrêté. L’annonce d’arrêt et les informations sur la suite du projet publiées par l’auteur renvoient vers FFmpegKitNext, distribué sous forme de code source. Ces exemples ne nécessitent ni binaires FFmpegKit ni paquets Swift tiers.
Préparer FFmpeg et Swift
Commencez sur un Mac où Xcode est déjà installé et où une distribution FFmpeg fonctionnelle,
comprenant ffprobe, est disponible dans PATH.
Les téléchargements Xcode d’Apple et la
page de téléchargement de FFmpeg présentent les options
d’installation. Ce tutoriel commence une fois ces outils installés ; il n’installe aucun logiciel
système.
Exécutez ces vérifications dans un nouveau répertoire de travail avant d’enregistrer les fichiers Swift :
command -v swiftc >/dev/null &&
command -v ffmpeg >/dev/null &&
command -v ffprobe >/dev/null &&
swiftc --version && ffmpeg -version && ffprobe -version
L’environnement testé comprend macOS 26.6.2, Xcode 27.0, Swift 6.4 en mode de langage Swift 5, FFmpeg et ffprobe 9.0.2, ainsi qu’un simulateur iOS 27.0. Les versions plus anciennes des systèmes d’exploitation et les appareils physiques ne font pas partie du périmètre testé de ce tutoriel. L’installation complète de Xcode fournit le SDK iOS et le simulateur.
Utilisez un fichier MP4 local intact contenant une piste audio AAC, nommé
sample video.mp4 dans le répertoire de travail. Ne lancez qu’une extraction à la fois.
L’exemple iOS utilise un fichier inclus dans le bundle et le stockage Documents propre à
l’application ; il ne couvre ni les fournisseurs de fichiers externes, ni les autorisations du
sélecteur de fichiers, ni les exports en arrière-plan.
Extraire les flux audio des fichiers vidéo
La méthode avec FFmpeg sélectionne le premier flux audio avec -map 0:a:0.
La copie du flux dans un fichier M4A nécessite un codec compatible, tel qu’AAC. La méthode avec
AVFoundation sélectionne la première piste audio et l’exporte avec AVAssetExportPresetAppleM4A.
Elle peut réencoder l’audio et ne produit pas une copie à l’octet près.
Guide pas à pas pour l’extraction audio
Enregistrez ce code dans AudioExtraction.swift. Chaque opération écrit dans un nouveau
répertoire situé à côté du fichier de destination, ne met le résultat à disposition qu’une fois
l’opération réussie et supprime ses fichiers temporaires en cas d’échec.
Fournissez des URL de fichiers locaux que votre application peut lire, ainsi qu’une URL de sortie
dans un répertoire existant accessible en écriture. Le répertoire temporaire est nettoyé lorsque
l’opération réussit normalement ou lève une erreur. L’arrêt forcé du processus peut le laisser
en place.
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)
}
Exemple d’utilisation
Pour un programme macOS en ligne de commande, enregistrez ce point d’entrée dans
ExtractAudio.swift, à côté du fichier précédent. L’exécutable attend la fin de
l’extraction et renvoie un code de sortie non nul en cas d’échec.
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
La commande écrit copied.m4a et encoded.m4a à côté des fichiers
source. Ouvrez les deux dans un lecteur audio et comparez leur bande-son et leur durée à celles du
fichier d’entrée. La copie de flux préserve le flux AAC encodé ; AVFoundation peut le réencoder,
les fichiers n’ont donc pas nécessairement un contenu identique à l’octet près.
Exécuter l’exemple iOS
Dans Xcode, créez un projet iOS App avec l’interface SwiftUI. Utilisez le mode de langage Swift 5
et sélectionnez le simulateur iOS 27 installé. Ajoutez AudioExtraction.swift à la cible de
l’application, puis ajoutez sample video.mp4 à sa phase de compilation
Copy Bundle Resources. N’ajoutez pas le point d’entrée macOS ExtractAudio.swift à cette
cible. Remplacez les fichiers générés pour l’application et la vue de contenu par ce seul fichier
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."
}
}
}
}
Compilez et exécutez l’application. Elle effectue un seul export à l’ouverture de la vue et affiche
Audio saved: encoded.m4a une fois l’export terminé. Utilisez
Share audio pour ouvrir ou enregistrer le résultat dans une autre application.
La source reste dans le bundle ; encoded.m4a se trouve dans le répertoire Documents
de l’application. Au lancement suivant, la présence du fichier de sortie entraîne un refus d’export.
Pour un nouvel export, choisissez un autre nom de fichier de destination ou supprimez volontairement
ce fichier de sortie. Aucune autorisation d’accès au microphone n’est nécessaire, car l’application
lit un fichier.
Résoudre les problèmes courants
- Accès aux fichiers : Vérifiez que le fichier d’entrée existe et que le répertoire parent de la destination est accessible en écriture. Le programme en ligne de commande s’exécute localement dans un terminal ; intégrer FFmpeg dans une application macOS exécutée dans un bac à sable nécessite un travail distinct. Sur iOS, cet exemple se limite délibérément au bundle et au répertoire Documents de l’application.
- Sortie existante : Choisissez une nouvelle destination. Les exemples ne remplacent jamais un fichier existant.
- Aucune piste audio : Une vidéo sans piste audio provoque une erreur et ne produit aucun nouveau fichier de sortie. Une piste contenant du silence reste une piste audio et peut être exportée.
- Codecs non pris en charge : La copie de flux avec FFmpeg peut échouer lorsque le codec audio ne peut pas être utilisé dans un conteneur M4A. Réencodez plutôt en AAC, ou utilisez la méthode avec AVFoundation pour les fichiers d’entrée pris en charge par le framework d’Apple.
- Diagnostic : Examinez localement la sortie stderr de FFmpeg. Pour déboguer AVFoundation,
examinez
session.errorlors du contrôle d’échec, sans afficher les erreurs internes dans l’interface de votre application. - Enregistrements endommagés : Un export réussi n’est pas un contrôle d’intégrité. Les outils multimédias peuvent récupérer des données d’entrée incomplètes ; comparez la durée réelle et le son du fichier de sortie avant de l’utiliser.
Optimiser les performances
- Copiez l’audio compatible avec
-c:a copylorsque vous devez préserver le flux encodé. - Effectuez les attentes bloquantes du processus FFmpeg en dehors du thread principal d’une application macOS.
- Attendez la fin des exports AVFoundation, comme dans les exemples, plutôt que de lire l’intégralité
de l’enregistrement dans une valeur
Data. Les APIexport()etstatusconservées ici génèrent des avertissements de dépréciation avec les SDK actuels ; le guide d’export d’Apple décrit l’API d’export plus récente pour les applications ciblant des versions plus récentes des systèmes d’exploitation.
Ressources complémentaires
Consultez la documentation de FFmpeg sur la sélection des flux et le guide d’export multimédia d’Apple pour en savoir plus sur la sélection des flux et la configuration des exports.
Pour un flux de travail côté serveur, consultez le service d’encodage audio de Transloadit.
