【问题标题】:AVPlayer plays video composition result incorrectlyAVPlayer 播放视频合成结果不正确
【发布时间】:2017-02-02 00:47:30
【问题描述】:

我需要一个简单的东西:一边旋转一边播放视频并在视频上应用CIFilter

首先,我创建播放器项目:

AVPlayerItem *item = [AVPlayerItem playerItemWithURL:videoURL];

// DEBUG LOGGING
AVAssetTrack *track = [[item.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
NSLog(@"Natural size is: %@", NSStringFromCGSize(track.naturalSize));
NSLog(@"Preffered track transform is: %@", NSStringFromCGAffineTransform(track.preferredTransform));
NSLog(@"Preffered asset transform is: %@", NSStringFromCGAffineTransform(item.asset.preferredTransform));

然后我需要应用视频合成。最初,我想创建一个带有 2 条指令的 AVVideoComposition - 一条是用于旋转的 AVVideoCompositionLayerInstruction,另一条是 CIFilter 应用程序。但是,我收到了一个异常,说 “期望视频合成只包含 AVCoreImageFilterVideoCompositionInstruction”,这意味着 Apple 不允许组合这 2 个指令。结果,我将两者结合在了过滤下,代码如下:

AVAsset *asset = playerItem.asset;
CGAffineTransform rotation = [self transformForItem:playerItem];

AVVideoComposition *composition = [AVVideoComposition videoCompositionWithAsset:asset applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest * _Nonnull request) {
    // Step 1: get the input frame image (screenshot 1)
    CIImage *sourceImage = request.sourceImage;

    // Step 2: rotate the frame
    CIFilter *transformFilter = [CIFilter filterWithName:@"CIAffineTransform"];
    [transformFilter setValue:sourceImage forKey: kCIInputImageKey];
    [transformFilter setValue: [NSValue valueWithCGAffineTransform: rotation] forKey: kCIInputTransformKey];
    sourceImage = transformFilter.outputImage;
    CGRect extent = sourceImage.extent;
    CGAffineTransform translation = CGAffineTransformMakeTranslation(-extent.origin.x, -extent.origin.y);
    [transformFilter setValue:sourceImage forKey: kCIInputImageKey];
    [transformFilter setValue: [NSValue valueWithCGAffineTransform: translation] forKey: kCIInputTransformKey];
    sourceImage = transformFilter.outputImage;

    // Step 3: apply the custom filter chosen by the user
    extent = sourceImage.extent;
    sourceImage = [sourceImage imageByClampingToExtent];
    [filter setValue:sourceImage forKey:kCIInputImageKey];
    sourceImage = filter.outputImage;
    sourceImage = [sourceImage imageByCroppingToRect:extent];

    // Step 4: finish processing the frame (screenshot 2)
    [request finishWithImage:sourceImage context:nil];
}];

playerItem.videoComposition = composition;

我在调试期间制作的屏幕截图显示图像已成功旋转并应用了过滤器(在此示例中,它是一个不改变图像的身份过滤器)。以下是截图 1 和截图 2,它们是在上面的 cmets 中标记的点拍摄的:

如您所见,旋转成功,生成的帧的范围也是正确的。

当我尝试在播放器中播放此视频时,问题就出现了。这是我得到的:

似乎所有的帧都按比例缩小并向下移动。绿色区域是空帧信息,当我夹紧到范围以使帧无限大小时,它显示边框像素而不是绿色。我有一种感觉,玩家在旋转之前仍然会从 AVPlayerItem 获取一些旧的尺寸信息,这就是为什么在上面的第一个代码 sn-p 中我记录了尺寸和变换,有日志:

Natural size is: {1920, 1080}
Preffered track transform is: [0, 1, -1, 0, 1080, 0]
Preffered asset transform is: [1, 0, 0, 1, 0, 0]

播放器是这样设置的:

layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
layer.needsDisplayOnBoundsChange = YES;

请注意最重要的事情:这只发生在应用程序本身使用相机横向 iPhone[6s]方向录制并保存在设备上的视频以前的存储。该应用程序以纵向模式录制的视频完全没有问题(顺便说一句,纵向视频的大小和转换日志就像横向视频一样!奇怪......也许iphone将旋转信息放入视频并修复它)。因此,缩放和移动视频似乎是“方面填充”和旋转前旧分辨率信息的组合。顺便说一句,纵向视频帧的显示部分是因为缩放以填充具有不同纵横比的播放器区域,但这是预期的行为。

让我知道你对此的想法,如果你知道如何更好地完成我需要的事情,那么很高兴知道。

【问题讨论】:

    标签: ios avfoundation avplayer avvideocomposition


    【解决方案1】:

    更新:有一种更简单的方法可以在播放期间“更改”AVPlayerItem 视频尺寸 - 设置视频合成的 renderSize 属性(可以使用 AVMutableVideoComposition 完成类)。

    下面是我的旧答案:


    经过大量调试,我理解了问题并找到了解决方案。我最初的猜测 AVPlayer 仍然认为视频是原始大小 是正确的。下图中解释了发生了什么:

    至于解决方案,我找不到在AVAssetAVPlayerItem 中更改视频大小的方法。所以我只是操纵视频以适应AVPlayer 所期望的大小和比例,然后在具有正确纵横比和标志的播放器中播放以缩放和填充播放器区域 - 一切看起来都很好。这是图形解释:

    下面是需要插入问题中提到的applyingCIFiltersWithHandler 块的附加代码:

    ... after Step 3 in the question codes above
    
    // make the frame the same aspect ratio as the original input frame
    // by adding empty spaces at the top and the bottom of the extent rectangle
    CGFloat newHeight = originalExtent.size.height * originalExtent.size.height / extent.size.height;
    CGFloat inset = (extent.size.height - newHeight) / 2;
    extent = CGRectInset(extent, 0, inset);
    sourceImage = [sourceImage imageByCroppingToRect:extent];
    
    // scale down to the original frame size
    CGFloat scale = originalExtent.size.height / newHeight;
    CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scale, scale);
    [transformFilter setValue:sourceImage forKey: kCIInputImageKey];
    [transformFilter setValue: [NSValue valueWithCGAffineTransform: scaleTransform] forKey: kCIInputTransformKey];
    sourceImage = transformFilter.outputImage;
    
    // translate the frame to make it's origin start at (0, 0)
    CGAffineTransform translation = CGAffineTransformMakeTranslation(0, -inset * scale);
    [transformFilter setValue:sourceImage forKey: kCIInputImageKey];
    [transformFilter setValue: [NSValue valueWithCGAffineTransform: translation] forKey: kCIInputTransformKey];
    sourceImage = transformFilter.outputImage;
    

    【讨论】:

      猜你喜欢
      • 2013-07-02
      • 1970-01-01
      • 2016-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多