CALayer.contents не отображается правильно в AVMutableComposition

У меня есть очень простой метод, который генерирует видео со статическим фоновым изображением, которое покрывает всю композицию видео, и маленькое, частично прозрачное изображение (стиль водяного знака), расположенное внизу видео.

Фоновое изображение отображается правильно и выглядит точно так же, как выглядит в средстве просмотра изображений. Однако изображение, которое должно быть отображено внизу видео, искажено / искажено.

Исходный код можно скачать здесь, на GitHub.

Ожидаемый вывод моего кода (макет нужного видео выхода):

Фактический вывод моего кода (частичный скриншот из iOS Simulator):

Как вы можете видеть, изображение нижнего колонтитула выглядит искаженным под углом 45 градусов и немного волнистым.

Ниже приведен код, который я сейчас использую для создания видео. Я испробовал все возможные комбинации содержимого Gravity для изображения нижнего колонтитула, но не повезло. Каждый другой пример, который я видел, просто устанавливает CGImageRef для слояcontents затем устанавливаетbounds, position а такжеanchorPoint к их соответствующим значениям. Я не видел других примеров, которые устанавливают какие-либо другие свойства. Я прочитал почти всю документацию для AVFoundation, чтобы увидеть, есть ли настройки, которые я пропускаю, но я еще не сталкивался с чем-то очевидным.

Любые предложения будут ценны. Спасибо!

Декларации интерфейса:

CGSize _renderingSize;
float _displayDuration;

AVMutableComposition *mutableComposition;
AVMutableVideoComposition *videoComposition;
AVMutableCompositionTrack *mutableCompositionVideoTrack;
AVAssetExportSession *exporter;

Настройки ViewDidLoad:

_renderingSize = CGSizeMake(640, 360);
_displayDuration = 2.0;

Блок кода рендеринга:

    mutableComposition = [AVMutableComposition composition];
    mutableCompositionVideoTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

    videoComposition = [AVMutableVideoComposition videoComposition];
    videoComposition.renderSize = _renderingSize;
    videoComposition.frameDuration = CMTimeMake(1, 30);

    CALayer *parentLayer = [CALayer layer];
    CALayer *videoLayer = [CALayer layer];
    parentLayer.frame = CGRectMake(0, 0, videoComposition.renderSize.width, videoComposition.renderSize.height);
    videoLayer.frame = CGRectMake(0, 0, videoComposition.renderSize.width, videoComposition.renderSize.height);
    [parentLayer addSublayer:videoLayer];

    videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];

    NSString *path = [[NSBundle mainBundle] pathForResource:@"blank_1080p" ofType:@"mp4"];
    NSURL *url = [NSURL fileURLWithPath:path];
    AVAsset *asset = [AVAsset assetWithURL:url];
    AVAssetTrack *track = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    [mutableCompositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,CMTimeMakeWithSeconds(_displayDuration, 600)) ofTrack:track atTime:kCMTimeZero error:nil];

    CALayer *imageLayer = [CALayer layer];
    imageLayer.bounds = parentLayer.frame;
    imageLayer.anchorPoint = CGPointMake(0.5, 0.5);
    imageLayer.position = CGPointMake(CGRectGetMidX(imageLayer.bounds), CGRectGetMidY(imageLayer.bounds));
    imageLayer.contents = (id)[UIImage imageNamed:@"background.png"].CGImage;
    imageLayer.contentsGravity = kCAGravityResizeAspectFill;
    [parentLayer addSublayer:imageLayer];

    UIImage *testImage = [UIImage imageNamed:@"bannerTextLayer.png"];

    CALayer *footerLayer = [CALayer layer];
    footerLayer.bounds = CGRectMake(0, 0, 540, 40);
    footerLayer.anchorPoint = CGPointMake(0.5, 0.5);
    footerLayer.position = CGPointMake(CGRectGetMidX(footerLayer.bounds), CGRectGetMidY(footerLayer.bounds));
    footerLayer.contents = (id)testImage.CGImage;
    footerLayer.contentsGravity = kCAGravityResizeAspectFill;
    [parentLayer addSublayer:footerLayer];

    AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(_displayDuration, 600));
    videoComposition.instructions = @[instruction];

    exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition presetName:AVAssetExportPresetHighestQuality];
    exporter.outputURL = videoURL ;
    exporter.videoComposition = videoComposition;
    exporter.outputFileType= AVFileTypeMPEG4;
    exporter.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(_displayDuration, 600));
    exporter.shouldOptimizeForNetworkUse = YES;

    [exporter exportAsynchronouslyWithCompletionHandler:^(void){
        switch (exporter.status) {
            case AVAssetExportSessionStatusFailed:{
                NSLog(@"Fail: %@", exporter.error);
                break;
            }
            case AVAssetExportSessionStatusCompleted:{
                NSLog(@"Success");

                dispatch_async(dispatch_get_main_queue(), ^{
                    if (self.moviePlayer)
                        [self.moviePlayer.view removeFromSuperview];

                    self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:videoURL];

                    self.moviePlayer.view.frame = CGRectMake(0, 0, 320, 180);
                    [self.moviePlayer setControlStyle:MPMovieControlStyleNone];

                    [self.previewView addSubview:self.moviePlayer.view];
                    [self.moviePlayer play];

                });

                break;
            }
            default:
                break;
        }
    }];

Ответы на вопрос(1)

Ваш ответ на вопрос