AVPlayer的多个video

我正在开发iPad的iOS应用程序,需要在屏幕的某个部分播放video。 我有几个video文件需要按照编译时没有给出的顺序播放。 它看起来好像只是一个video播放。 当从一个video到另一个video时,这两个video的最后一帧或最后一帧显示有一些延迟,但是没有内容的闪烁或白色屏幕应该是不错的。 video不包含audio。 考虑到内存使用情况非常重要。 这些video具有非常高的分辨率,并且可以同时播放几个不同的video序列。

为了获得这个,我已经尝试了几个解决scheme。 如下所列:

AVPlayer与AVComposition与其中的所有video

在这个解决scheme中,我有一个AVPlayer,它只能在AVPlayerItem上使用,AVPlayerItem使用包含所有放在一起的video的AVComposition。 当我去看具体的video的时候,我试图在下一个video开始的时候进行合成。这个解决scheme的问题是,当寻找播放器的时候会很快显示出它正在寻找的一些帧,这是不可接受的。 似乎没有办法直接跳到组合中的特定时间。 我试图通过在刚刚完成的video中制作最后一帧的图像来解决这个问题,然后在AVPLayer的前面显示该图像,最后在查找完成后将其删除。 我正在使用AVAssetImageGenerator制作图像,但出于某种原因,图像的质量与video不一样,所以在显示和隐藏图像时会有显着的变化。 另一个问题是,AVPlayer使用大量的内存,因为一个AVPlayerItem保存所有的video。

AVPlayer有多个AVPlayerItems

此解决scheme为每个video使用AVPlayerItem,并在切换到新video时replaceAVPlayer的项目。 这个问题是,当切换一个AVPlayer的项目时,它会显示一个白色的屏幕短时间加载新的项目。 为了解决这个问题,可以使用在加载的同时在最后一帧放置图像的解决scheme,但是仍然存在图像和video的质量不同且显着的问题。

3.两个AVPlayers彼此轮stream播放AVPlayerItem

我试过的下一个解决scheme是将两个AVPlayer放在一起,然后轮stream播放AVPlayerItems。 所以当玩家完成游戏时,它将停留在video的最后一帧。 另一个AVPlayer将被带到前面(其项目设置为零,所以它是透明的),并且下一个AVPlayerItem将被插入到该AVPlayer中。 一旦它被加载,它将开始播放,两个video之间的顺利交易的幻想将完好无损。 这个解决scheme的问题是内存使用情况。 在某些情况下,我需要同时在屏幕上播放两个video,这将导致4个AVPlayers同时载入一个AVPlayerItem。 由于video的分辨率可能很高,因此记忆体太多了。


有没有人有一些想法,build议,意见或有关整体的问题和上述张贴的解决办法。

所以这个项目现在已经在App Store中了,现在是时候回到这个线程,分享我的发现,揭示我最终做了什么。

什么没有奏效

我在大AV组合中使用的所有video的第一个选项还不够好,因为没有一个小擦洗故障跳到组合中的特定时间。 此外,由于API无法为暂停提供帧保证,所以在合成中的两个video之间准确地暂停video有问题。

第三个有两个AVPlayers,让他们轮stream在实践中效果很好。 特别是在iPad 4或iPhone 5上。尽pipe内存中有多个video同时消耗了太多的内存,但RAM数量较less的设备仍是个问题。 特别是因为我不得不处理非常高分辨率的video。

我最终做了什么

那么,左边是选项编号2.需要时为video创buildAVPlayerItem并将其提供给AVPlayer。 这个解决scheme的好处是内存消耗。 通过延迟创buildAVPlayerItems,并在不再需要的时刻抛弃它们,可以将内存消耗保持在最低限度,这对于支持内存有限的旧设备非常重要。 这个解决scheme的问题是,当从一个video到下一个video时,在下一个video被加载到内存中的瞬间出现空白屏幕。 解决这个问题的想法是在AVPlayer背后放置一个可以显示玩家缓冲的图像。 我知道我需要的是像素完美的图像,因此我捕获了video的最后一帧和第一帧的精确副本的图像。 这个解决scheme在实践中运作良好

这个解决scheme的问题

我有这个问题,但UIImageView内的图像的位置是不是AVPlayer内的video的位置相同,如果video/图像不是它的本地大小或模块4的缩放比例。 换句话说,我在使用UIImageView和AVPlayer处理半像素时遇到了问题。 这似乎不是相同的方式。

我如何修复它

我尝试了很多东西,因为我的应用程序是以不同大小的交互方式使用video。 我试图改变AVPlayerLayer和CALayer的放大滤镜和缩小滤镜,使用相同的algorithm,但没有真正改变任何东西。 最后,我创build了一个iPad应用程序,它可以自动截取我需要的所有尺寸的video截图,然后在video缩放到一定尺寸时使用正确的图像。 这给了我在展示特定video的所有尺寸的像素完美的图像。 不是一个完美的工具链,但结果是完美的。

最后的反思

这个位置问题对我来说是非常明显的(因此非常重要),主要原因是我的应用程序正在播放的video内容是绘制animation,其中很多内容处于固定位置,只有一部分图片正在移动。 如果所有的内容只移动一个像素,就会产生一个非常明显和丑陋的故障。 在今年的WWDC上,我和一位苹果公司的AVFoundation专家讨论了这个问题。 当我向他介绍这个问题的时候,他的build议基本上是select3,但是我向他解释说这是不可能的,因为内存消耗,我已经试过了这个解决scheme。 有鉴于此,他表示,我select了正确的解决scheme,并要求我在video缩放时提交UIImage / AVPlayer定位的错误报告。

您可能已经看过这个,但是您是否已经检出了AVQueuePlayer 文档

它被devise用于在队列中播放AVPlayerItems ,并且是AVPlayer的直接子类,所以只需以相同的方式使用它。 你把它设置如下:

 AVPlayerItem *firstItem = [AVPlayerItem playerItemWithURL: firstItemURL]; AVPlayerItem *secondItem = [AVPlayerItem playerItemWithURL: secondItemURL]; AVQueuePlayer *player = [AVQueuePlayer queuePlayerWithItems:[NSArray arrayWithObjects:firstItem, secondItem, nil]]; [player play]; 

如果您想在运行时将新项目添加到队列中,只需使用以下方法:

 [player insertItem:thirdPlayerItem afterItem:firstPlayerItem]; 

我还没有testing,看看这是否减less了你提到的闪烁问题,但似乎这是将要走的路。

更新 – https://youtu.be/7QlaO7WxjGg

下面是以集合视图为例,每次播放8个视图(请注意,不需要任何内存pipe理,您可以使用ARC):

  - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = (UICollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath]; // Enumerate array of visible cells' indexPaths to find a match if ([self.collectionView.indexPathsForVisibleItems indexOfObjectPassingTest:^BOOL(NSIndexPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { return (obj.item == indexPath.item); }]) dispatch_async(dispatch_get_main_queue(), ^{ [self drawLayerForPlayerForCell:cell atIndexPath:indexPath]; }); return cell; } - (void)drawPosterFrameForCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { [self.imageManager requestImageForAsset:AppDelegate.sharedAppDelegate.assetsFetchResults[indexPath.item] targetSize:AssetGridThumbnailSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage *result, NSDictionary *info) { cell.contentView.layer.contents = (__bridge id)result.CGImage; }]; } - (void)drawLayerForPlayerForCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { cell.contentView.layer.sublayers = nil; [self.imageManager requestPlayerItemForVideo:(PHAsset *)self.assetsFetchResults[indexPath.item] options:nil resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) { dispatch_sync(dispatch_get_main_queue(), ^{ if([[info objectForKey:PHImageResultIsInCloudKey] boolValue]) { [self drawPosterFrameForCell:cell atIndexPath:indexPath]; } else { AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:[AVPlayer playerWithPlayerItem:playerItem]]; [playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; [playerLayer setBorderColor:[UIColor whiteColor].CGColor]; [playerLayer setBorderWidth:1.0f]; [playerLayer setFrame:cell.contentView.layer.bounds]; [cell.contentView.layer addSublayer:playerLayer]; [playerLayer.player play]; } }); }]; } 

drawPosterFrameForCell方法将图像放置在无法播放video的位置,因为它存储在iCloud上,而不是设备上。

无论如何,这是起点; 一旦你明白这是如何工作的,你可以做所有你想要的事情,没有任何你所描述的记忆方面的小故障。