如何在 IOS 中制作慢动作视频
Posted
技术标签:
【中文标题】如何在 IOS 中制作慢动作视频【英文标题】:How to do Slow Motion video in IOS 【发布时间】:2013-06-22 05:31:43 【问题描述】:我必须在视频文件中与音频一起在一些帧之间执行“慢动作”,并且需要将渐变视频存储作为新视频。
Ref:http://www.youtube.com/watch?v=BJ3_xMGzauk(从 0 到 10 秒观看)
根据我的分析,我发现 AVFoundation 框架很有帮助。
参考: http://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/00_Introduction.html
从上面的链接复制粘贴:
" 编辑 AV Foundation 使用合成从现有的媒体片段(通常是一个或多个视频和音频轨道)创建新资产。您使用可变组合来添加和删除轨道,并调整它们的时间顺序。您还可以设置音轨的相对音量和斜坡;并设置视频轨道的不透明度和不透明度渐变。作品是保存在内存中的媒体片段的集合。当您使用导出会话导出合成时,它会折叠到一个文件中。 在 iOS 4.1 及更高版本上,您还可以使用资产写入器从样本缓冲区或静止图像等媒体创建资产。
"
问题: 我可以使用 AVFoundation 框架对视频/音频文件进行“慢动作”吗?或者有没有其他可用的包?如果我想单独处理音频和视频,请指导我怎么做?
更新 :: AV 导出会话代码:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *outputURL = paths[0];
NSFileManager *manager = [NSFileManager defaultManager];
[manager createDirectoryAtPath:outputURL withIntermediateDirectories:YES attributes:nil error:nil];
outputURL = [outputURL stringByAppendingPathComponent:@"output.mp4"];
// Remove Existing File
[manager removeItemAtPath:outputURL error:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:self.inputAsset presetName:AVAssetExportPresetLowQuality];
exportSession.outputURL = [NSURL fileURLWithPath:outputURL]; // output path;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void)
if (exportSession.status == AVAssetExportSessionStatusCompleted)
[self writeVideoToPhotoLibrary:[NSURL fileURLWithPath:outputURL]];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:[NSURL fileURLWithPath:outputURL] completionBlock:^(NSURL *assetURL, NSError *error)
if (error)
NSLog(@"Video could not be saved");
];
else
NSLog(@"error: %@", [exportSession error]);
];
【问题讨论】:
谁能帮帮我? 如果需要更多信息,请询问我。 很高兴看到有关如何减慢视频和音频速度的答案,但是您如何使音频中的音高也发生变化?你发现了吗? 【参考方案1】:我会使用 ffmpeg 从初始视频中提取所有帧,然后使用 AVAssetWriter 收集在一起,但帧速率较低。为了获得更饱满的慢动作,您可能需要应用一些模糊效果,甚至在现有帧之间生成帧,这将由两帧混合而成。
【讨论】:
感谢您的回复。 ios 的 ffmpeg 功能/api 是否有任何限制? 对于你的情况,据我所知没有限制。 您或任何人可以向我推荐一些代码示例或参考链接吗?我的互联网搜索几乎无能为力。 AVFoundation 框架优于提取帧,因为它易于使用,也适用于慢动作请检查上面的代码,我已经编写它工作正常【参考方案2】:您可以使用 AVFoundation 和 CoreMedia 框架来缩放视频。 看看 AVMutableCompositionTrack 方法:
- (void)scaleTimeRange:(CMTimeRange)timeRange toDuration:(CMTime)duration;
示例:
AVURLAsset* videoAsset = nil; //self.inputAsset;
//create mutable composition
AVMutableComposition *mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *videoInsertError = nil;
BOOL videoInsertResult = [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
atTime:kCMTimeZero
error:&videoInsertError];
if (!videoInsertResult || nil != videoInsertError)
//handle error
return;
//slow down whole video by 2.0
double videoScaleFactor = 2.0;
CMTime videoDuration = videoAsset.duration;
[compositionVideoTrack scaleTimeRange:CMTimeRangeMake(kCMTimeZero, videoDuration)
toDuration:CMTimeMake(videoDuration.value*videoScaleFactor, videoDuration.timescale)];
//export
AVAssetExportSession* assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPresetLowQuality];
(可能来自 videoAsset 的音轨也应该添加到 mixComposition)
【讨论】:
感谢您的回复。视频缩放工作正常。但音频被静音。正如你提到的那样实现了音轨,但它对我不起作用,我将代码粘贴在这里:pastebin.com/UN3mtpH9。你能检查我的代码,让我知道我做错了什么吗? @vrmarks - 请告诉我如何在慢动作视频中添加音轨? 能否请您告诉我如何添加具有刻度时间范围的音频。因为音频没有变慢,所以正在播放原始音频。当我使用下面的代码时。 AVMutableCompositionTrack compositionCommentaryTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio;[compositionCommentaryTrack scaleTimeRange:CMTimeRangeMake(kCMTimeZero, videoDuration) toDuration:CMTimeMake(videoDuration.valuevideoScaleFactor, videoDuration.timescale)]; 抱歉回复晚了。我无法使 AVFoundation/AVMutableTrack 使用方法 scaleTimeRange:toDuration: 来缩放音频。一种可能的解决方案是使用带有预设名称:AVAssetExportPresetAppleM4A 和 outputFileType:AVFileTypeAppleM4A 的 AVAssetExportSession 导出音轨。然后使用其他 api 减慢音频并将更新的音轨添加回视频。 @2vision2 dirac.dspdimension.com/Dirac3_Technology_Home_Page/License.html 最后 4 个免费许可证类型。示例 Dirac3-Mobile/Time Stretching 示例它在 Documents 文件夹中生成减慢的音频文件(检查 iPhoneTestAppDelegate.mm 中的因素:107 时间)【参考方案3】:我已经实现了在视频中添加慢动作,包括音频以及正确的输出方向。
- (void)SlowMotion:(NSURL *)URl
AVURLAsset* videoAsset = [AVURLAsset URLAssetWithURL:URl options:nil]; //self.inputAsset;
AVAsset *currentAsset = [AVAsset assetWithURL:URl];
AVAssetTrack *vdoTrack = [[currentAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
//create mutable composition
AVMutableComposition *mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *videoInsertError = nil;
BOOL videoInsertResult = [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
atTime:kCMTimeZero
error:&videoInsertError];
if (!videoInsertResult || nil != videoInsertError)
//handle error
return;
NSError *audioInsertError =nil;
BOOL audioInsertResult =[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
ofTrack:[[currentAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]
atTime:kCMTimeZero
error:&audioInsertError];
if (!audioInsertResult || nil != audioInsertError)
//handle error
return;
CMTime duration =kCMTimeZero;
duration=CMTimeAdd(duration, currentAsset.duration);
//slow down whole video by 2.0
double videoScaleFactor = 2.0;
CMTime videoDuration = videoAsset.duration;
[compositionVideoTrack scaleTimeRange:CMTimeRangeMake(kCMTimeZero, videoDuration)
toDuration:CMTimeMake(videoDuration.value*videoScaleFactor, videoDuration.timescale)];
[compositionAudioTrack scaleTimeRange:CMTimeRangeMake(kCMTimeZero, videoDuration)
toDuration:CMTimeMake(videoDuration.value*videoScaleFactor, videoDuration.timescale)];
[compositionVideoTrack setPreferredTransform:vdoTrack.preferredTransform];
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *outputFilePath = [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:@"slowMotion.mov"]];
if ([[NSFileManager defaultManager] fileExistsAtPath:outputFilePath])
[[NSFileManager defaultManager] removeItemAtPath:outputFilePath error:nil];
NSURL *_filePath = [NSURL fileURLWithPath:outputFilePath];
//export
AVAssetExportSession* assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPresetLowQuality];
assetExport.outputURL=_filePath;
assetExport.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
[assetExport exportAsynchronouslyWithCompletionHandler:^
switch ([assetExport status])
case AVAssetExportSessionStatusFailed:
NSLog(@"Export session faiied with error: %@", [assetExport error]);
dispatch_async(dispatch_get_main_queue(), ^
// completion(nil);
);
break;
case AVAssetExportSessionStatusCompleted:
NSLog(@"Successful");
NSURL *outputURL = assetExport.outputURL;
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputURL])
[self writeExportedVideoToAssetsLibrary:outputURL];
dispatch_async(dispatch_get_main_queue(), ^
// completion(_filePath);
);
break;
default:
break;
];
- (void)writeExportedVideoToAssetsLibrary :(NSURL *)url
NSURL *exportURL = url;
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:exportURL])
[library writeVideoAtPathToSavedPhotosAlbum:exportURL completionBlock:^(NSURL *assetURL, NSError *error)
dispatch_async(dispatch_get_main_queue(), ^
if (error)
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[error localizedDescription]
message:[error localizedRecoverySuggestion]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
if(!error)
// [activityView setHidden:YES];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Sucess"
message:@"video added to gallery successfully"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
#if !TARGET_IPHONE_SIMULATOR
[[NSFileManager defaultManager] removeItemAtURL:exportURL error:nil];
#endif
);
];
else
NSLog(@"Video could not be exported to assets library.");
【讨论】:
当 videoScaleFactor 小于 1 时,它不能在 IOS 9.1 中合成视频。有什么想法吗? 应该至少有一个视频,否则 AVmutable composer 将无法获得任何资产并且会导致应用崩溃 @objectiveCoder 你的代码很棒,帮助我解决了这个问题,但是使用这种方法导出时音频音高似乎没有改变。您是否可以将任何属性添加到导出中,使其行为类似于您在播放器项上设置asset.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed
时的行为?
知道如何在Photos
框架中使用PHAsset
来实现这一点吗?
有什么方法可以将视频转换为快速通道的快速动作吗?真的需要帮助【参考方案4】:
swift中的一个例子:
我
var asset: AVAsset?
func configureAssets()
let videoAsset = AVURLAsset(url: Bundle.main.url(forResource: "sample", withExtension: "m4v")!)
let audioAsset = AVURLAsset(url: Bundle.main.url(forResource: "sample", withExtension: "m4a")!)
// let audioAsset2 = AVURLAsset(url: Bundle.main.url(forResource: "audio2", withExtension: "m4a")!)
let comp = AVMutableComposition()
let videoAssetSourceTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo).first! as AVAssetTrack
let audioAssetSourceTrack = videoAsset.tracks(withMediaType: AVMediaTypeAudio).first! as AVAssetTrack
// let audioAssetSourceTrack2 = audioAsset2.tracks(withMediaType: AVMediaTypeAudio).first! as AVAssetTrack
let videoCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
let audioCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)
do
try videoCompositionTrack.insertTimeRange(
CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(9 , 600)),
of: videoAssetSourceTrack,
at: kCMTimeZero)
try audioCompositionTrack.insertTimeRange(
CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(9, 600)),
of: audioAssetSourceTrack,
at: kCMTimeZero)
//
// try audioCompositionTrack.insertTimeRange(
// CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(3, 600)),
// of: audioAssetSourceTrack2,
// at: CMTimeMakeWithSeconds(7, 600))
let videoScaleFactor = Int64(2.0)
let videoDuration: CMTime = videoAsset.duration
videoCompositionTrack.scaleTimeRange(CMTimeRangeMake(kCMTimeZero, videoDuration), toDuration: CMTimeMake(videoDuration.value * videoScaleFactor, videoDuration.timescale))
audioCompositionTrack.scaleTimeRange(CMTimeRangeMake(kCMTimeZero, videoDuration), toDuration: CMTimeMake(videoDuration.value * videoScaleFactor, videoDuration.timescale))
videoCompositionTrack.preferredTransform = videoAssetSourceTrack.preferredTransform
catch print(error)
asset = comp
二
func createFileFromAsset(_ asset: AVAsset)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
let filePath = documentsDirectory.appendingPathComponent("rendered-audio.m4v")
deleteFile(filePath)
if let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetLowQuality)
exportSession.canPerformMultiplePassesOverSourceMediaData = true
exportSession.outputURL = filePath
exportSession.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
exportSession.outputFileType = AVFileTypeQuickTimeMovie
exportSession.exportAsynchronously
_ in
print("finished: \(filePath) : \(exportSession.status.rawValue) ")
func deleteFile(_ filePath:URL)
guard FileManager.default.fileExists(atPath: filePath.path) else
return
do
try FileManager.default.removeItem(atPath: filePath.path)
catch
fatalError("Unable to delete file: \(error) : \(#function).")
【讨论】:
你知道如何让音频的音调也随着速度下降吗?音频变慢,但音调不跟随。scaleTimeRange(...)
方法不支持音高转换。所以减慢的音频的音调不会跟随,但我想你知道。许多人为此推荐了狄拉克。您可能可以对此进行一些研究。我还没有和狄拉克合作过,所以我不知道它是如何工作的。祝你好运!【参考方案5】:
带或不带音轨的慢 + 快
我已尝试并能够减慢资产。
compositionVideoTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)
成功了。
我开设了一个课程,可以帮助您从AVAsset
生成slower
视频。
+ 点是您也可以将其设为faster
,另一个 + 点是它也会处理音频。
这是我的自定义类示例:
import UIKit
import AVFoundation
enum SpeedoMode
case Slower
case Faster
class VSVideoSpeeder: NSObject
/// Singleton instance of `VSVideoSpeeder`
static var shared: VSVideoSpeeder =
return VSVideoSpeeder()
()
/// Range is b/w 1x, 2x and 3x. Will not happen anything if scale is out of range. Exporter will be nil in case url is invalid or unable to make asset instance.
func scaleAsset(fromURL url: URL, by scale: Int64, withMode mode: SpeedoMode, completion: @escaping (_ exporter: AVAssetExportSession?) -> Void)
/// Check the valid scale
if scale < 1 || scale > 3
/// Can not proceed, Invalid range
completion(nil)
return
/// Asset
let asset = AVAsset(url: url)
/// Video Tracks
let videoTracks = asset.tracks(withMediaType: AVMediaType.video)
if videoTracks.count == 0
/// Can not find any video track
completion(nil)
return
/// Get the scaled video duration
let scaledVideoDuration = (mode == .Faster) ? CMTimeMake(asset.duration.value / scale, asset.duration.timescale) : CMTimeMake(asset.duration.value * scale, asset.duration.timescale)
let timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
/// Video track
let videoTrack = videoTracks.first!
let mixComposition = AVMutableComposition()
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
/// Audio Tracks
let audioTracks = asset.tracks(withMediaType: AVMediaType.audio)
if audioTracks.count > 0
/// Use audio if video contains the audio track
let compositionAudioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
/// Audio track
let audioTrack = audioTracks.first!
do
try compositionAudioTrack?.insertTimeRange(timeRange, of: audioTrack, at: kCMTimeZero)
compositionAudioTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)
catch _
/// Ignore audio error
do
try compositionVideoTrack?.insertTimeRange(timeRange, of: videoTrack, at: kCMTimeZero)
compositionVideoTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)
/// Keep original transformation
compositionVideoTrack?.preferredTransform = videoTrack.preferredTransform
/// Initialize Exporter now
let outputFileURL = URL(fileURLWithPath: "/Users/thetiger/Desktop/scaledVideo.mov")
/// Note:- Please use directory path if you are testing with device.
if FileManager.default.fileExists(atPath: outputFileURL.absoluteString)
try FileManager.default.removeItem(at: outputFileURL)
let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
exporter?.outputURL = outputFileURL
exporter?.outputFileType = AVFileType.mov
exporter?.shouldOptimizeForNetworkUse = true
exporter?.exportAsynchronously(completionHandler:
completion(exporter)
)
catch let error
print(error.localizedDescription)
completion(nil)
return
我将 1x、2x 和 3x 作为有效比例。类包含正确的验证和处理。下面是这个函数的使用示例。
let url = Bundle.main.url(forResource: "1", withExtension: "mp4")!
VSVideoSpeeder.shared.scaleAsset(fromURL: url, by: 3, withMode: SpeedoMode.Slower) (exporter) in
if let exporter = exporter
switch exporter.status
case .failed: do
print(exporter.error?.localizedDescription ?? "Error in exporting..")
case .completed: do
print("Scaled video has been generated successfully!")
case .unknown: break
case .waiting: break
case .exporting: break
case .cancelled: break
else
/// Error
print("Exporter is not initialized.")
此行将处理音频缩放
compositionAudioTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)
【讨论】:
为什么有 3x 或更多的限制?是否有应用程序可以进一步减慢/加快视频播放速度? @RoiMulia 没有限制它只在我的功能中......您可以根据您的要求使用。 @AnandGautam 不要根据你的要求投反对票。 您的视频网址是什么?你确定它是一个有效的网址吗? @AnandGautam 你给outputFileURL
分配了什么?如果您使用的是设备,则它不能是桌面 URL。您应该就 SO 提出问题,而不是对工作代码投反对票。【参考方案6】:
斯威夫特 5
这是@TheTiger 转换为 SwiftUI 的代码:
import UIKit
import AVFoundation
enum SpeedoMode
case Slower
case Faster
class VSVideoSpeeder: NSObject
/// Singleton instance of `VSVideoSpeeder`
static var shared: VSVideoSpeeder =
return VSVideoSpeeder()
()
/// Range is b/w 1x, 2x and 3x. Will not happen anything if scale is out of range. Exporter will be nil in case url is invalid or unable to make asset instance.
func scaleAsset(fromURL url: URL, by scale: Int64, withMode mode: SpeedoMode, completion: @escaping (_ exporter: AVAssetExportSession?) -> Void)
/// Check the valid scale
if scale < 1 || scale > 3
/// Can not proceed, Invalid range
completion(nil)
return
/// Asset
let asset = AVAsset(url: url)
/// Video Tracks
let videoTracks = asset.tracks(withMediaType: AVMediaType.video)
if videoTracks.count == 0
/// Can not find any video track
completion(nil)
return
/// Get the scaled video duration
let scaledVideoDuration = (mode == .Faster) ? CMTimeMake(value: asset.duration.value / scale, timescale: asset.duration.timescale) : CMTimeMake(value: asset.duration.value * scale, timescale: asset.duration.timescale)
let timeRange = CMTimeRangeMake(start: CMTime.zero, duration: asset.duration)
/// Video track
let videoTrack = videoTracks.first!
let mixComposition = AVMutableComposition()
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
/// Audio Tracks
let audioTracks = asset.tracks(withMediaType: AVMediaType.audio)
if audioTracks.count > 0
/// Use audio if video contains the audio track
let compositionAudioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
/// Audio track
let audioTrack = audioTracks.first!
do
try compositionAudioTrack?.insertTimeRange(timeRange, of: audioTrack, at: CMTime.zero)
compositionAudioTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)
catch _
/// Ignore audio error
do
try compositionVideoTrack?.insertTimeRange(timeRange, of: videoTrack, at: CMTime.zero)
compositionVideoTrack?.scaleTimeRange(timeRange, toDuration: scaledVideoDuration)
/// Keep original transformation
compositionVideoTrack?.preferredTransform = videoTrack.preferredTransform
/// Initialize Exporter now
let outputFileURL = URL(fileURLWithPath: "/Users/thetiger/Desktop/scaledVideo.mov")
/// Note:- Please use directory path if you are testing with device.
if FileManager.default.fileExists(atPath: outputFileURL.absoluteString)
try FileManager.default.removeItem(at: outputFileURL)
let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
exporter?.outputURL = outputFileURL
exporter?.outputFileType = AVFileType.mov
exporter?.shouldOptimizeForNetworkUse = true
exporter?.exportAsynchronously(completionHandler:
completion(exporter)
)
catch let error
print(error.localizedDescription)
completion(nil)
return
使用相同的用例:
let url = Bundle.main.url(forResource: "1", withExtension: "mp4")!
VSVideoSpeeder.shared.scaleAsset(fromURL: url, by: 3, withMode: SpeedoMode.Slower) (exporter) in
if let exporter = exporter
switch exporter.status
case .failed: do
print(exporter.error?.localizedDescription ?? "Error in exporting..")
case .completed: do
print("Scaled video has been generated successfully!")
case .unknown: break
case .waiting: break
case .exporting: break
case .cancelled: break
else
/// Error
print("Exporter is not initialized.")
【讨论】:
【参考方案7】:在 iOS swift 中创建“慢动作”视频并不容易,我遇到了许多“慢动作”,知道它们不起作用或者其中的一些代码已被贬值。所以我终于找到了一种在 Swift 中制作慢动作的方法。 注意:此代码可用于 120fps 也大于此。 您可以像我一样制作慢动作音频
这是“我为实现慢动作而创建的代码 sn-p”
如果此代码有效,请给我一个 UPVOTE。
func slowMotion(pathUrl: URL)
let videoAsset = AVURLAsset.init(url: pathUrl, options: nil)
let currentAsset = AVAsset.init(url: pathUrl)
let vdoTrack = currentAsset.tracks(withMediaType: .video)[0]
let mixComposition = AVMutableComposition()
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
let videoInsertError: Error? = nil
var videoInsertResult = false
do
try compositionVideoTrack?.insertTimeRange(
CMTimeRangeMake(start: .zero, duration: videoAsset.duration),
of: videoAsset.tracks(withMediaType: .video)[0],
at: .zero)
videoInsertResult = true
catch let videoInsertError
if !videoInsertResult || videoInsertError != nil
//handle error
return
var duration: CMTime = .zero
duration = CMTimeAdd(duration, currentAsset.duration)
//MARK: You see this constant (videoScaleFactor) this helps in achieving the slow motion that you wanted. This increases the time scale of the video that makes slow motion
// just increase the videoScaleFactor value in order to play video in higher frames rates(more slowly)
let videoScaleFactor = 2.0
let videoDuration = videoAsset.duration
compositionVideoTrack?.scaleTimeRange(
CMTimeRangeMake(start: .zero, duration: videoDuration),
toDuration: CMTimeMake(value: videoDuration.value * Int64(videoScaleFactor), timescale: videoDuration.timescale))
compositionVideoTrack?.preferredTransform = vdoTrack.preferredTransform
let dirPaths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).map(\.path)
let docsDir = dirPaths[0]
let outputFilePath = URL(fileURLWithPath: docsDir).appendingPathComponent("slowMotion\(UUID().uuidString).mp4").path
if FileManager.default.fileExists(atPath: outputFilePath)
do
try FileManager.default.removeItem(atPath: outputFilePath)
catch
let filePath = URL(fileURLWithPath: outputFilePath)
let assetExport = AVAssetExportSession(
asset: mixComposition,
presetName: AVAssetExportPresetHighestQuality)
assetExport?.outputURL = filePath
assetExport?.outputFileType = .mp4
assetExport?.exportAsynchronously(completionHandler:
switch assetExport?.status
case .failed:
print("asset output media url = \(String(describing: assetExport?.outputURL))")
print("Export session faiied with error: \(String(describing: assetExport?.error))")
DispatchQueue.main.async(execute:
// completion(nil);
)
case .completed:
print("Successful")
let outputURL = assetExport!.outputURL
print("url path = \(String(describing: outputURL))")
phphotoLibrary.shared().performChanges(
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL!)
) saved, error in
if saved
print("video successfully saved in photos gallery view video in photos gallery")
if (error != nil)
print("error in saing video \(String(describing: error?.localizedDescription))")
DispatchQueue.main.async(execute:
// completion(_filePath);
)
case .none:
break
case .unknown:
break
case .waiting:
break
case .exporting:
break
case .cancelled:
break
case .some(_):
break
)
【讨论】:
以上是关于如何在 IOS 中制作慢动作视频的主要内容,如果未能解决你的问题,请参考以下文章