我有一個使用 AVAudioRecorder 創建的音頻 url (.m4a)。我想在 Instagram 上分享該音頻,因此我將音頻轉換為視頻。問題是在轉換之后,當我使用 UIActivityViewController 將視頻 url 保存到檔案應用程式時,我可以重播視頻,查看時間(例如 7 秒)并毫無問題地聽到音頻。出現帶有聲音圖示的黑屏。
但是當我使用 UIActivityViewController 將視頻保存到照片庫時,視頻顯示了 7 秒但沒有播放,視頻全是灰色的,并且沒有顯示聲音圖示。
為什么視頻在檔案應用程式中成功保存/播放,但在照片庫中保存而不播放?
let asset: AVURLAsset = AVURLAsset(url: audioURL)
let mixComposition = AVMutableComposition()
guard let compositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: CMPersistentTrackID()) else { return }
let track = asset.tracks(withMediaType: .audio)
guard let assetTrack = track.first else { return }
do {
try compositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: assetTrack.timeRange.duration), of: assetTrack, at: .zero)
} catch {
print(error.localizedDescription)
}
guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetPassthrough) else { return }
let dirPath = NSTemporaryDirectory().appending("\(UUID().uuidString).mov")
let outputFileURL = URL(fileURLWithPath: dirPath)
exporter.outputFileType = .mov
exporter.outputURL = outputFileURL
exporter.shouldOptimizeForNetworkUse = true
exporter.exportAsynchronously {
switch exporter.status {
// ...
guard let videoURL = exporter.outputURL else { return }
// present UIActivityViewController to save videoURL and then save it to the Photos Library via 'Save Video`
}
}
uj5u.com熱心網友回復:
正如 Lance 正確指出的那樣,問題在于雖然匯出了.movor.mp4格式的檔案,但沒有視頻,它只是一個音頻播放。
再多讀一點,例如 .mp4 只是一種數字多媒體容器格式,可以很好地用于音頻,因此可以將音頻檔案保存為 .mp4 / .mov。
需要向 中添加一個空的視頻軌道AVMutableComposition才能成功。Lance 已經發布了一個很好的解決方案,效果非常好,并且self sustained不僅僅是我提出的依賴于 1 秒空白視頻的替代解決方案。
作業原理概述
- 您會得到一個 1 秒長的空白視頻檔案,解析度為您想要的,例如 1920 x 1080
- 您從此視頻資產中檢索視頻軌道
- 從音頻檔案中檢索音軌
- 創建一個
AVMutableComposition將用于合并音頻和視頻軌道的 AVMutableCompositionTrack使用音軌配置一個并將其添加到主AVMutableCompositionAVMutableVideoComposition用視頻軌道配置一個- 使用和
AVAssetExportSession匯出最終視頻AVMutableCompositionAVMutableVideoComposition
代碼
在下面的大部分代碼中,您將看到多個保護陳述句。您可以創建一個守衛,但是,了解此類任務發生故障的位置可能很有用,因為匯出失敗可能有多種原因。
配置音軌
private func configureAudioTrack(_ audioURL: URL,
inComposition composition: AVMutableComposition) -> AVMutableCompositionTrack?
{
// Initialize an AVURLAsset with your audio file
let audioAsset: AVURLAsset = AVURLAsset(url: audioURL)
let trackTimeRange = CMTimeRange(start: .zero,
duration: audioAsset.duration)
// Get the audio track from the audio asset
guard let sourceAudioTrack = audioAsset.tracks(withMediaType: .audio).first
else
{
manageError(nil, withMessage: "Error retrieving audio track from source file")
return nil
}
// Insert a new video track to the AVMutableComposition
guard let audioTrack = composition.addMutableTrack(withMediaType: .audio,
preferredTrackID: CMPersistentTrackID())
else
{
// manage your error
return nil
}
do {
// Inset the contents of the audio source into the new audio track
try audioTrack.insertTimeRange(trackTimeRange,
of: sourceAudioTrack,
at: .zero)
}
catch {
// manage your error
}
return audioTrack
}
配置視頻軌道
private func configureVideoTrack(inComposition composition: AVMutableComposition) -> AVMutableCompositionTrack?
{
// Initialize a video asset with the empty video file
guard let blankMoviePathURL = Bundle.main.url(forResource: "blank",
withExtension: ".mp4"),
let videoAsset = AVAsset(url: blankMoviePathURL)
else
{
// manage errors
return nil
}
// Get the video track from the empty video
guard let sourceVideoTrack = videoAsset.tracks(withMediaType: .video).first
else
{
// manage errors
return nil
}
// Insert a new video track to the AVMutableComposition
guard let videoTrack = composition.addMutableTrack(withMediaType: .video,
preferredTrackID: kCMPersistentTrackID_Invalid)
else
{
// manage errors
return nil
}
let trackTimeRange = CMTimeRange(start: .zero,
duration: composition.duration)
do {
// Inset the contents of the video source into the new audio track
try videoTrack.insertTimeRange(trackTimeRange,
of: sourceVideoTrack,
at: .zero)
}
catch {
// manage errors
}
return videoTrack
}
配置視頻合成
// Configure the video properties like resolution and fps
private func createVideoComposition(with videoCompositionTrack: AVMutableCompositionTrack) -> AVMutableVideoComposition
{
let videoComposition = AVMutableVideoComposition()
// Set the fps
videoComposition.frameDuration = CMTime(value: 1,
timescale: 25)
// Video dimensions
videoComposition.renderSize = CGSize(width: 1920, height: 1080)
// Specify the duration of the video composition
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRange(start: .zero, duration: .indefinite)
// Add the video composition track to a new layer
let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoCompositionTrack)
let transform = videoCompositionTrack.preferredTransform
layerInstruction.setTransform(transform, at: .zero)
// Apply the layer configuration instructions
instruction.layerInstructions = [layerInstruction]
videoComposition.instructions = [instruction]
return videoComposition
}
配置 AVAssetExportSession
private func configureAVAssetExportSession(with composition: AVMutableComposition,
videoComposition: AVMutableVideoComposition) -> AVAssetExportSession?
{
// Configure export session
guard let exporter = AVAssetExportSession(asset: composition,
presetName: AVAssetExportPresetHighestQuality)
else
{
// Manage your errors
return nil
}
// Configure where the exported file will be stored
let documentsURL = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]
let fileName = "\(UUID().uuidString).mov"
let dirPath = documentsURL.appendingPathComponent(fileName)
let outputFileURL = dirPath
// Apply exporter settings
exporter.videoComposition = videoComposition
exporter.outputFileType = .mov
exporter.outputURL = outputFileURL
exporter.shouldOptimizeForNetworkUse = true
return exporter
}
在這里,一件重要的事情是不要將匯出器設定present quality為電影AVAssetExportPresetHighestQuality,AVAssetExportPresetLowQuality例如,AVAssetExportPresetPassthrough根據檔案,
以當前格式匯出資產的預設,除非另有禁止。
所以你仍然會得到一個音頻 mp4 或 mov 檔案,因為當前的合成格式是音頻。我沒有對此進行廣泛的測驗,但這是來自一些測驗。
最后,您可以將上述所有功能組合在一起,如下所示:
func generateMovie(with audioURL: URL)
{
delegate?.audioMovieExporterDidStart(self)
let composition = AVMutableComposition()
// Configure the audio and video tracks in the new composition
guard let _ = configureAudioTrack(audioURL, inComposition: composition),
let videoCompositionTrack = configureVideoTrack(inComposition: composition)
else
{
// manage error
return
}
let videoComposition = createVideoComposition(with: videoCompositionTrack)
if let exporter = configureAVAssetExportSession(with: composition,
videoComposition: videoComposition)
{
exporter.exportAsynchronously
{
switch exporter.status {
case .completed:
guard let videoURL = exporter.outputURL
else
{
// manage errors
return
}
// notify someone the video is ready at videoURL
default:
// manege error
}
}
}
}
最后的想法
- 你可以在這里試駕一個作業樣本
- 如果您希望使用它,我將其轉換為一個簡單的庫,您可以在其中配置方向、fps 甚至為視頻設定背景顏色 - 可在同一鏈接中獲得
- 如果您只想要空白視頻,可以從這里獲取
uj5u.com熱心網友回復:
因此,盡管我的問題中的代碼確實將音頻檔案轉換為視頻檔案,但似乎仍然沒有video track. 我知道這是一個事實,因為在我從我的問題中獲得了出口商的 videoURL 之后,我嘗試向它添加水印,并且在水印代碼中它不斷崩潰
let videoTrack = asset.tracks(withMediaType: AVMediaType.video)[0]
基本上,我的問題中的代碼將音頻轉換為視頻,但不添加視頻軌道。
我假設正在發生的事情是當檔案應用程式讀取檔案時,它知道它是一個 .mov 或 .mp4 檔案,然后即使缺少視頻軌道,它也會播放音軌。
相反,當照片應用程式讀取檔案時,它也知道它是 .mov 或 .mp4 檔案,但如果沒有視頻軌道,它就不會播放任何內容。
我必須結合這 2 個答案才能讓音頻在照片應用程式中作為視頻播放。
1st-我將我的應用程式圖示(您可以添加任何影像)作為 1 個影像添加到影像陣列中,以使用如何將 UIImage 陣列匯出為電影中的代碼制作視頻軌道?由@scootermg 回答。
@scootermg 的答案的代碼方便地在 @dldnh 的這個GitHub 上的 1 個檔案中。在他的代碼中,在ImageAnimator類中,在render函式中,我沒有保存到庫中,而是videoWriter's output URL在完成處理程式中回傳了。
第二 - 我使用Swift Merge 音頻和視頻檔案中的代碼將我剛剛制作的應用程式圖示視頻與我的問題中的音頻 url 組合成一個由@TungFam回答的視頻
在 TungFam 回答的 mixCompostion 中,我使用了音頻 url 的資產持續時間作為視頻的長度。
do {
try mutableCompositionVideoTrack[0].insertTimeRange(CMTimeRangeMake(start: .zero,
duration: aAudioAssetTrack.timeRange.duration),
of: aVideoAssetTrack,
at: .zero)
try mutableCompositionAudioTrack[0].insertTimeRange(CMTimeRangeMake(start: .zero,
duration: aAudioAssetTrack.timeRange.duration),
of: aAudioAssetTrack,
at: .zero)
if let aAudioOfVideoAssetTrack = aAudioOfVideoAssetTrack {
try mutableCompositionAudioOfVideoTrack[0].insertTimeRange(CMTimeRangeMake(start: .zero,
duration: aAudioAssetTrack.timeRange.duration),
of: aAudioOfVideoAssetTrack,
at: .zero)
}
} catch {
print(error.localizedDescription)
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/431083.html
標籤:IOS 迅速 网址 uiactivityviewcontroller
下一篇:使用WITH作為聚合值
