ios 在AVFoundation中合并时,如何为视频设置单独的转换?

pdkcd3nj  于 2023-10-21  发布在  iOS
关注(0)|答案(1)|浏览(107)

我希望合并几个视频在一起(所有来自不同的来源)在Swift与AVFoundation。生成的视频应该是肖像格式。
我写的函数将视频合并到一个视频中。然而,从移动的手机(如iPhone)拍摄的视频似乎是以横向方式导出的,而其余的则是以纵向方式导出的。然后,视频将被向上拉伸,以适应肖像宽高比。iPhone似乎将视频保存为横向(即使是纵向),然后系统使用元数据将其显示为纵向。
为了解决这个问题,我尝试检测视频是否是横向的(或另一个旋转),然后手动将其转换为纵向。然而,当我这样做时,似乎转换应用于整个轨道,这导致整个构图呈现在风景中,一些视频呈现在风景中,另一些则呈现在肖像中。我不知道如何只对一个视频进行转换。我试过使用多个轨道,但只有一个视频显示,其余的轨道被忽略。下面是导出视频的示例(它的渲染方式如下,它应该渲染为9:16,但经过变换后,它渲染为16:9,请注意,第二个剪辑失真了,尽管它最初是以纵向记录的)。

下面是我的代码:

  1. private static func mergeVideos(
  2. videoPaths: [URL],
  3. outputURL: URL,
  4. handler: @escaping (_ path: URL)-> Void
  5. ) {
  6. let videoComposition = AVMutableComposition()
  7. var lastTime: CMTime = .zero
  8. guard let videoCompositionTrack = videoComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
  9. for path in videoPaths {
  10. let assetVideo = AVAsset(url: path)
  11. getTracks(assetVideo, .video) { videoTracks in
  12. // Add video track
  13. do {
  14. try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: assetVideo.duration), of: videoTracks[0], at: lastTime)
  15. // Apply the original transform
  16. if let assetVideoTrack = assetVideo.tracks(withMediaType: AVMediaType.video).last {
  17. let t = assetVideoTrack.preferredTransform
  18. let size = assetVideoTrack.naturalSize
  19. let videoAssetOrientation: CGImagePropertyOrientation
  20. if size.width == t.tx && size.height == t.ty {
  21. print("down")
  22. videoAssetOrientation = .down
  23. videoCompositionTrack.preferredTransform = CGAffineTransform(rotationAngle: .pi) // 180 degrees
  24. } else if t.tx == 0 && t.ty == 0 {
  25. print("up")
  26. videoCompositionTrack.preferredTransform = assetVideoTrack.preferredTransform
  27. videoAssetOrientation = .up
  28. } else if t.tx == 0 && t.ty == size.width {
  29. print("left")
  30. videoAssetOrientation = .left
  31. videoCompositionTrack.preferredTransform = CGAffineTransform(rotationAngle: .pi / 2) // 90 degrees to the right
  32. } else {
  33. print("right")
  34. videoAssetOrientation = .right
  35. videoCompositionTrack.preferredTransform = CGAffineTransform(rotationAngle: -.pi / 2) // 90 degrees to the left
  36. }
  37. }
  38. } catch {
  39. print("Failed to insert video track")
  40. return
  41. }
  42. self.getTracks(assetVideo, .audio) { audioTracks in
  43. // Add audio track only if it exists
  44. if !audioTracks.isEmpty {
  45. do {
  46. try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: assetVideo.duration), of: audioTracks[0], at: lastTime)
  47. } catch {
  48. print("Failed to insert audio track")
  49. return
  50. }
  51. }
  52. // Update time
  53. lastTime = CMTimeAdd(lastTime, assetVideo.duration)
  54. }
  55. }
  56. }
  57. guard let exporter = AVAssetExportSession(asset: videoComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
  58. exporter.outputURL = outputURL
  59. exporter.outputFileType = AVFileType.mp4
  60. exporter.shouldOptimizeForNetworkUse = true
  61. exporter.exportAsynchronously(completionHandler: {
  62. switch exporter.status {
  63. case .failed:
  64. print("Export failed \(exporter.error!)")
  65. case .completed:
  66. print("completed export")
  67. handler(outputURL)
  68. default:
  69. break
  70. }
  71. })
  72. }

有人知道我在这里错过了什么吗?任何帮助都非常感谢。

92vpleto

92vpleto1#

转换为视频CompositionTrack影响整个轨迹。你可以使用AVVideoComposition来做,它使用AVVideoCompositionInstruction来定时做视频处理。
下面是代码,没有不重要的部分,并将videoComposition重命名为mainCompositon以避免混淆:

  1. private static func mergeVideos(
  2. videoPaths: [URL],
  3. outputURL: URL,
  4. handler: @escaping (_ path: URL)-> Void
  5. ) {
  6. let mainComposition = AVMutableComposition()
  7. var lastTime: CMTime = .zero
  8. guard let videoCompositionTrack = mainComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
  9. let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoCompositionTrack)
  10. for path in videoPaths {
  11. let assetVideo = AVAsset(url: path)
  12. getTracks(assetVideo, .video) { videoTracks in
  13. // Add video track
  14. do {
  15. try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: assetVideo.duration), of: videoTracks[0], at: lastTime)
  16. // Apply the original transform
  17. if let assetVideoTrack = assetVideo.tracks(withMediaType: AVMediaType.video).last {
  18. let t = assetVideoTrack.preferredTransform
  19. layerInstruction.setTransform(t, at: lastTime) // apply transfrom to track at time.
  20. }
  21. } catch {
  22. print("Failed to insert video track")
  23. return
  24. }
  25. // deal with audio part ...
  26. }
  27. }
  28. let videoCompostion = AVMutableVideoComposition()
  29. let instruction = AVMutableVideoCompositionInstruction()
  30. instruction.timeRange = CMTimeRange(start: .zero, end: lastTime)
  31. videoCompostion.instructions = [instruction]
  32. guard let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
  33. // assign videoComposition to exporter
  34. exporter.videoComposition = videoCompostion
  35. // other export part ...
  36. }

PS.你最好添加getTracks(_:, _:)方法来完成代码。

展开查看全部

相关问题