Hello,
I am trying to create an MP4 by obtaining the content from another source MP4.
The source MP4 would be read with AVAssetReader
and the output written with AVAssetWriter
.
I wanted to do partial tests: first, I placed only the video in the output MP4.
Now, I am trying to place only the audio in the output MP4.
I even managed to get the output MP4 to have the same length (in seconds) as the source MP4.
But the problem is simple: the output MP4 is simply silent.
Naturally, I want it to have audio.
Below are two excerpts from the source code.
- Reading and writing.
Note: The variable videoURL
is from the class where the function writeVideo()
is located. Its assignment happens in another scope, already debugged.
Snippet:
let semaphore = DispatchSemaphore(value: 0)
func writeVideo() {
var audioReaderBuffers = [CMSampleBuffer]()
// File directory
url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("*****/output.mp4")
guard let url = url else { return }
try FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true)
if FileManager.default.fileExists(atPath: url.path()) {
try FileManager.default.removeItem(at: url)
}
if let videoURL = videoURL {
let videoAsset = AVAsset(url: videoURL)
Task {
let audioTrack = try await videoAsset.loadTracks(withMediaType: .audio).first!
let reader = try AVAssetReader(asset: videoAsset)
let audioSettings = [
AVFormatIDKey: kAudioFormatLinearPCM,
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2
] as [String : Any]
let audioOutputTest = try await audioTrack.getAudioSettings()
let readerAudioOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: audioSettings)
reader.add(readerAudioOutput)
reader.startReading()
while let sampleBuffer = readerAudioOutput.copyNextSampleBuffer() {
audioReaderBuffers.append(sampleBuffer)
}
semaphore.signal()
}
}
semaphore.wait()
let audioInput = createAudioInput(sampleBuffer: audioReaderBuffers[0])
let assetWriter = try AVAssetWriter(outputURL: url, fileType: .mp4)
assetWriter.add(audioInput)
assetWriter.startWriting()
assetWriter.startSession(atSourceTime: .zero)
for (index, buffer) in audioReaderBuffers.enumerated() {
while !audioInput.isReadyForMoreMediaData {
usleep(1000)
}
audioInput.append(buffer)
}
assetWriter.finishWriting {
switch assetWriter.status {
case .completed:
print("Operation completed successfully: \(url.absoluteString)")
case .failed:
if let error = assetWriter.error {
print("Error description: \(error.localizedDescription)")
} else {
print("Error not found.")
}
default:
print("Error not found.")
}
}
}
- Below is the
createAudioInput
method:
func createAudioInput(sampleBuffer: CMSampleBuffer) -> AVAssetWriterInput {
let audioSettings = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVSampleRateKey: 48000,
AVEncoderBitRatePerChannelKey: 64000,
AVNumberOfChannelsKey: 1
] as [String : Any]
let audioInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings, sourceFormatHint: sampleBuffer.formatDescription)
audioInput.expectsMediaDataInRealTime = false
return audioInput
}
I await your help, please.