在 Objective-C 中使用 SCRecorder 以修改后的播放速度保存视频

Save video with modified playback speed using SCRecorder in Objective-C

使用SCRecorder想在录制后保存视频,用户选择不同的播放速度例如:2x,3x。使用AVPlayer,可以使用这段代码实现:

//create mutable composition
AVMutableComposition *mixComposition = [AVMutableComposition composition];

AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
                                                                               preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *videoInsertError = nil;
BOOL videoInsertResult = [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
                                                        ofTrack:[[asset 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 = asset.duration;

[compositionVideoTrack scaleTimeRange:CMTimeRangeMake(kCMTimeZero, videoDuration)
                           toDuration:CMTimeMake(videoDuration.value*videoScaleFactor, videoDuration.timescale)];

//export
AVAssetExportSession* assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition
                                                                  //   presetName:AVAssetExportPresetLowQuality];

但是,不知道如何使用 SCRecorder 库实现同样的效果。请指导。 提前致谢。

SCVideoConfiguration 有一个 属性 timeScale。

您不能尝试 SCAssetExportSession。它将 SCVideoConfiguration 作为输入。

您还可以使用 SCAssetExportSession,它是 AVAssetExportSession 的 SCRecorder 对应项。(SCRecorder 文档)

 /* The time scale of the video
 A value more than 1 will make the buffers last longer, it creates
 a slow motion effect. A value less than 1 will make the buffers be
 shorter, it creates a timelapse effect.

 Only used in SCRecorder.
 */
@property (assign, nonatomic) CGFloat timeScale;

终于得到了我自己的答案:

- (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.");
}

}