我需要一个简单的事情:在旋转和应用视频的同时播放视频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));
然后我需要应用视频合成.最初,我正在考虑创建一个AVVideoComposition
带有2个指令 - 一个AVVideoCompositionLayerInstruction
用于轮换,另一个用于CIFilter
应用.但是,我得到一个异常抛出的说法"期望视频合成仅包含AVCoreImageFilterVideoCompositionInstruction",这意味着Apple不允许合并这两条指令.结果,我结合了两个过滤,这里是代码:
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,它们是在上面评论中标记的点上拍摄的:
如您所见,旋转成功,结果帧的范围也是正确的.
当我尝试在播放器中播放此视频时,问题就出现了.这是我得到的:
所以似乎所有的帧都缩放并向下移动.绿色区域是空框架信息,当我夹到范围以使框架无限大时,它显示边框像素而不是绿色.我有一种感觉,玩家在旋转之前仍然需要一些旧的尺寸信息AVPlayerItem
,这就是为什么在上面的第一个代码片段中我记录了尺寸和变换,有日志:
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将旋转信息放在视频中并修复它).因此,在旋转之前,缩放和移动视频看起来像是"纵横填充"和旧分辨率信息的组合.顺便说一下,纵向显示纵向视频帧是因为缩放以填充具有不同宽高比的播放器区域,但这是预期的行为.
让我知道你对此的想法,如果你知道如何更好地完成我需要的东西,那么知道它会很棒.
更新:AVPlayerItem
在播放过程中更容易"更改" 视频尺寸 - 设置renderSize
视频合成的属性(可以使用AVMutableVideoComposition
类完成).
我的老答案如下:
经过大量调试后,我理解了问题并找到了解决方案.我最初猜测AVPlayer仍然认为视频是原始大小是正确的.在下面的图片中,它解释了发生了什么:
至于解决,我无法找到一个方法来改变内部的视频大小AVAsset
或AVPlayerItem
.因此,我只是操纵视频以适应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;