iOS объединяет три видео - вращайте центр видео

У меня есть три видео. Первый от задней камеры. Второй от передней камеры, а третий снова от задней камеры. Видеоролики всегда снимаются в ландшафтном режиме с кнопкой "Домой" справа.

Видеоролики, расположенные сзади, имеют правильную ориентацию Центральное видео, снятое с помощью фронтальной камеры, поворачивается на 180 градусов (вверх ногами). Я безуспешно исследовал и попробовал многочисленные методы для трансформации центрального видео. Я получаю одинаковые результаты каждый раз.

Я очень разочарован всем этим процессом. Все, что я читаю онлайн и комментарии / предложения от рецензента, должно работать, но это не работает. Видео одно и то же, что бы я ни старался для трансформаций. Это постоянно действует так, как будто я не применял никаких преобразований. Ничего такого. Я не понимаю, почему преобразования игнорируются на этом. Я потратил недели на это, и я в конце - это просто не работает.

Вот текущая итерация моего кода:

- (void)mergeVideos2:(NSMutableArray *)assets withCompletion:(void (^)(NSString *))completion {

    AVMutableComposition *mutableComposition = [AVMutableComposition composition];
    AVMutableCompositionTrack *videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo
                                                                                       preferredTrackID:kCMPersistentTrackID_Invalid];
    __block NSMutableArray *instructions = [[NSMutableArray alloc] init];
    __block CGSize size = CGSizeZero;
    __block CMTime time = kCMTimeZero;

    __block AVMutableVideoComposition *mutableVideoComposition = [AVMutableVideoComposition videoComposition];

    __block CGAffineTransform transformflip = CGAffineTransformMakeScale(1, -1);
    //    __block CGAffineTransform transformflip = CGAffineTransformMakeRotation(M_PI);

    __block int32_t commontimescale = 600;

    [assets enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        NSURL *assetUrl = (NSURL *)obj;
        AVAsset *asset = [AVAsset assetWithURL:assetUrl];

        CMTime cliptime = CMTimeConvertScale(asset.duration, commontimescale, kCMTimeRoundingMethod_QuickTime);

        NSLog(@"%s: Number of tracks: %lu", __PRETTY_FUNCTION__, (unsigned long)[[asset tracks] count]);
        AVAssetTrack *assetTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;

        NSError *error;
        [videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, cliptime)
                                       ofTrack:assetTrack
                                        atTime:time
                                         error:&error];
        if (error) {
            NSLog(@"%s: Error - %@", __PRETTY_FUNCTION__, error.debugDescription);
        }

        AVMutableVideoCompositionLayerInstruction *videoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];

        CGAffineTransform transform = assetTrack.preferredTransform;
        [videoLayerInstruction setTransform:CGAffineTransformConcat(transform, transformflip) atTime:time];

        // the main instruction set - this is wrapping the time
        AVMutableVideoCompositionInstruction *videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
        videoCompositionInstruction.timeRange = CMTimeRangeMake(time, assetTrack.timeRange.duration);
        if (videoLayerInstruction != nil)
            videoCompositionInstruction.layerInstructions = @[videoLayerInstruction];
        [instructions addObject:videoCompositionInstruction];

        // time increment variables
        time = CMTimeAdd(time, cliptime);

        if (CGSizeEqualToSize(size, CGSizeZero)) {
            size = assetTrack.naturalSize;;
        }

    }];

    mutableVideoComposition.instructions = instructions;

    // set the frame rate to 9fps
    mutableVideoComposition.frameDuration = CMTimeMake(1, 12);

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths firstObject];
    int number = arc4random_uniform(10000);
    self.outputFile = [documentsDirectory stringByAppendingFormat:@"/export_%i.mov",number];
    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition
                                                                      presetName:AVAssetExportPreset1280x720];

    exporter.outputURL = [NSURL fileURLWithPath:self.outputFile];
    //Set the output file type
    exporter.outputFileType = AVFileTypeQuickTimeMovie;
    exporter.shouldOptimizeForNetworkUse = YES;

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_enter(group);

    [exporter exportAsynchronouslyWithCompletionHandler:^{
        dispatch_group_leave(group);

    }];

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{

        // get the size of the file
        unsigned  long long size= ([[[NSFileManager defaultManager] attributesOfItemAtPath:self.outputFile error:nil] fileSize]);
        NSString *filesize = [NSByteCountFormatter stringFromByteCount:size countStyle:NSByteCountFormatterCountStyleFile];
        NSString *thereturn = [NSString stringWithFormat:@"%@: %@", self.outputFile, filesize];

        NSLog(@"Export File (Final) - %@", self.outputFile);
        completion(thereturn);

    });

}

Есть идеи или предложения?

1 ответ

Решение

Каждый AVAssetTrack имеет preferredTransform имущество. Он содержит информацию о том, как повернуть и перевести видео, чтобы правильно его отобразить, чтобы вам не приходилось догадываться. Используйте предпочтительный преобразование каждого видео в каждой инструкции слоя.

Не устанавливайте "videoCompositionTrack.preferredTransform = ..."

Удалить рампу преобразования "[videoLayerInstruction setTransformRampFromStartTransform:..."

В этом перечислении просто используйте:

CGAffineTransform transform = assetTrack.preferredTransform;
[videoLayerInstruction setTransform:transform atTime:time];

Я предполагаю, что ваши видео снимаются с теми же размерами, что и ваш вывод, с серединой видео, имеющей обратную ширину и высоту. Если это не так, вам нужно добавить соответствующее масштабирование:

float scaleFactor = ...// i.e. (outputWidth / videoWidth) 
CGAffineTransform scale = CGAffineTransformMakeScale(scaleFactor,scaleFactor)
transform = CGAffineTransformConcat(transform, scale);
[videoLayerInstruction setTransform:transform atTime:time];

РЕДАКТИРОВАТЬ: Похоже, что исходные видео, которые появились в композиции с ног на голову, были с ног на голову, но имели идентичность CGAffineTransform. Этот код работал, чтобы показать их в правильной ориентации:

- (void)mergeVideos2:(NSMutableArray *)assets withCompletion:(void (^)(NSString *))completion {

        AVMutableComposition *mutableComposition = [AVMutableComposition composition];
        AVMutableCompositionTrack *videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo
                                                                                           preferredTrackID:kCMPersistentTrackID_Invalid];
        __block NSMutableArray *instructions = [[NSMutableArray alloc] init];
        __block CMTime time = kCMTimeZero;
        __block AVMutableVideoComposition *mutableVideoComposition = [AVMutableVideoComposition videoComposition];
        __block int32_t commontimescale = 600;

        // Create one layer instruction.  We have one video track, and there should be one layer instruction per video track.
        AVMutableVideoCompositionLayerInstruction *videoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];

        [assets enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

            NSURL *assetUrl = (NSURL *)obj;
            AVAsset *asset = [AVAsset assetWithURL:assetUrl];

            CMTime cliptime = CMTimeConvertScale(asset.duration, commontimescale, kCMTimeRoundingMethod_QuickTime);

            NSLog(@"%s: Number of tracks: %lu", __PRETTY_FUNCTION__, (unsigned long)[[asset tracks] count]);
            AVAssetTrack *assetTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;
            CGSize naturalSize = assetTrack.naturalSize;

            NSError *error;
            //insert the video from the assetTrack into the composition track
            [videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, cliptime)
                                           ofTrack:assetTrack
                                            atTime:time
                                             error:&error];
            if (error) {
                NSLog(@"%s: Error - %@", __PRETTY_FUNCTION__, error.debugDescription);
            }


            CGAffineTransform transform = assetTrack.preferredTransform;

            //set the layer to have this videos transform at the time that this video starts
            if (<* the video is an intermediate video  - has the wrong orientation*>) {
                //these videos have the identity transform, yet they are upside down.
                //we need to rotate them by M_PI radians (180 degrees) and shift the video back into place

                CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(M_PI);
                CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(naturalSize.width, naturalSize.height);
                [videoLayerInstruction setTransform:CGAffineTransformConcat(rotateTransform, translateTransform) atTime:time];

            } else {
                [videoLayerInstruction setTransform:transform atTime:time];
            }

            // time increment variables
            time = CMTimeAdd(time, cliptime);

        }];

        // the main instruction set - this is wrapping the time
        AVMutableVideoCompositionInstruction *videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
        videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,mutableComposition.duration); //make the instruction last for the entire composition
        videoCompositionInstruction.layerInstructions = @[videoLayerInstruction];
        [instructions addObject:videoCompositionInstruction];
        mutableVideoComposition.instructions = instructions;

        // set the frame rate to 9fps
        mutableVideoComposition.frameDuration = CMTimeMake(1, 12);

        //set the rendersize for the video we're about to write
        mutableVideoComposition.renderSize = CGSizeMake(1280,720);

        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths firstObject];
        int number = arc4random_uniform(10000);
        self.outputFile = [documentsDirectory stringByAppendingFormat:@"/export_%i.mov",number];

        //let the rendersize of the video composition dictate size.  use quality preset here
        AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition
                                                                          presetName:AVAssetExportPresetHighestQuality];

        exporter.outputURL = [NSURL fileURLWithPath:self.outputFile];
        //Set the output file type
        exporter.outputFileType = AVFileTypeQuickTimeMovie;
        exporter.shouldOptimizeForNetworkUse = YES;
        exporter.videoComposition = mutableVideoComposition;

        dispatch_group_t group = dispatch_group_create();

        dispatch_group_enter(group);

        [exporter exportAsynchronouslyWithCompletionHandler:^{
            dispatch_group_leave(group);

        }];

        dispatch_group_notify(group, dispatch_get_main_queue(), ^{

            // get the size of the file
            unsigned  long long size= ([[[NSFileManager defaultManager] attributesOfItemAtPath:self.outputFile error:nil] fileSize]);
            NSString *filesize = [NSByteCountFormatter stringFromByteCount:size countStyle:NSByteCountFormatterCountStyleFile];
            NSString *thereturn = [NSString stringWithFormat:@"%@: %@", self.outputFile, filesize];

            NSLog(@"Export File (Final) - %@", self.outputFile);
            completion(thereturn);

        });
    }
Другие вопросы по тегам