使用AVPlayer的多个视频

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

为了获得这个,我已经尝试了一些解决方案。 他们列在下面:

1.具有AVComposition的AVPlayer及其中的所有视频

在这个解决方案中,我有一个AVPlayer,它只会在AVPlayerItem上使用,AVPlayerItem使用一个包含所有放在一起的视频的AVComposition。 当去特定的视频时,我会寻找下一个视频开始的时间。这个解决方案的问题在于,当寻找玩家将快速显示它正在寻找的一些帧时,这是不可接受的。 看起来没有办法直接跳到组合中的特定时间。 我试图通过制作刚刚完成的视频中最后一帧的图像来解决这个问题,然后在寻找时在AVPLayer前显示,最后在搜索完成后将其删除。 我使用AVAssetImageGenerator制作图像,但出于某种原因,图像的质量与视频不一样,因此在显示和隐藏图像时会有显着变化。 另一个问题是AVPlayer使用大量内存,因为单个AVPlayerItem可以容纳所有视频。

2.具有多个AVPlayerItems的AVPlayer

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

3.两个AVPlayers轮流播放AVPlayerItem

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


有没有人有一些想法,建议,意见或有关整体问题和上述公布的解决方案。


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

什么没有奏效

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

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

我最终做了什么

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

这个解决方案的问题

尽管如此,我认为UIImageView中的图像位置与AVPlayer中视频的位置不同,如果视频/图像不是其原始大小或模块4的缩放比例。 换句话说,我在使用UIImageView和AVPlayer处理半像素时遇到了问题。 它似乎没有相同的方式。

我如何修复它

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

最后的反思

这个位置问题对我来说非常明显(因此非常重要),主要原因是我的应用程序正在播放的视频内容是绘制动画,其中很多内容位于固定位置,只有部分图片正在移动。 如果所有内容仅移动一个像素,则会产生非常明显且难看的故障。 在今年的WWDC上,我与AVFoundation的专家苹果工程师讨论过这个问题。 当我向他介绍这个问题时,他的建议基本上是采用选项3,但我向他解释说,这是不可能的,因为内存消耗,我已经尝试了解决方案。 有鉴于此,他说我选择了正确的解决方案,并要求我在缩放视频时提交UIImage / AVPlayer定位的错误报告。


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

它被设计用于在队列中播放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];

我还没有测试过,看看这是否能减少你所提到的闪烁问题,但看起来这将是一条路。


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

下面是以集合视图为例,它将一次播放8个视图(请注意,不需要任何类型的内存管理;您可以使用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方法将图像放置在无法播放视频的位置,因为它存储在iCloud中,而不是设备。

无论如何,这是起点; 一旦你明白了这是如何工作的,你可以做所有你想要的事情,而不会出现你所描述的任何毛病,记忆方面的问题。

链接地址: http://www.djcxy.com/p/56883.html

上一篇: Multiple videos with AVPlayer

下一篇: AVPlayer not rendering to its AVPlayerLayer