在 UITableView 上滚动时 Avplayer 项目预缓冲

Avplayer item pre buffering while scrolling on UITableView

我创建了一个带有自定义 UITableViewCells 的 UITableView。 UITableView 由通过互联网加载的图像和视频组成。当用户在其中一个 UITableViewCell 中滚动时,我加载 AVPlayer 来播放 hls 视频。为了播放该项目,我将 hls 格式的 URL 设置为 avplayer 项目。

self.playerController = [[AVPlayerViewController alloc] init];
NSURL *videoURL = @"https://playeritemvideourl";
self.player = [AVPlayer playerWithURL:url];
self.playerController.player = self.player;
[self.player play];

视频播放,但从触发 [self.player play] 的那一刻起有大约 3 到 5 秒的延迟。我如何将视频预先缓冲到 avplayer 的当前项目,以便当我滚动到特定的 UITableViewCell 时,视频立即开始播放?我在 AVPlayerItem 上查看了 preferredForwardBufferDuration 属性 但似乎没有任何区别。任何帮助或建议表示赞赏!

AVPlayer 在实例化时开始流式传输 m3u8。 (我通过在 Xcode 中的调试导航器中监视网络图注意到了这一点。我在没有调用 -[play] 的情况下实例化了一个 AVPlayer,并且网络处于负载状态。)而不是加载 AVPlayer 一旦单元格变得可见,实例化 AVPlayer 单元格可见并且播放 内容当它变得可见时。

这可以通过首先让一些数据结构保存 AVPlayer 项来实现。我会推荐 NSMutableDictionary,因为我们可以将密钥设置为我们想要的视频 URL,而对象可以是已经加载了 URL 的 AVPlayer。有两种方法来填充结构。您可以使用 -viewDidLoad 之类的方法一次加载所有内容,或者在 -scrollViewDidScroll: 中动态加载项目以确定我们是否靠近包含视频的单元格。如果内容不多并且用户几乎可以保证观看所有视频,我会使用前者。如果我们有很多内容要加载或者用户可能不会观看所有视频,我会使用前者。这是一个 UITableViewController.

的例子

MyTableViewController.h

#import <UIKit/UIKit.h>
#import <AVKit/AVKit.h>

@interface MyTableViewController : UITableViewController
{
    NSMutableDictionary *mediaPlayers; // stores the media players, key = NSString with URL, value = AVPlayer
    // you don't need mediaKeyIndexPaths or mediaKeyURLs if you are taking the load-all-at-once approach
    NSMutableSet *mediaKeyIndexPaths; // set that stores the key NSIndexPaths that trigger loading a media player
    NSDictionary *mediaKeyURLs; // dictionary that maps a key NSIndexPath to a URL (NSString), key = NSIndexPath, value = NSString
}

@property (strong, nonatomic) AVPlayerViewController *playerController;

@end

MyTableViewController.m

#import "MyTableViewController.h"

@implementation MyTableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // if you want to load all items at once, you can forget mediaKeyIndexPaths and mediaKeyURLs
    // and instead just load all of your media here
    mediaPlayers = [[NSMutableDictionary alloc] init];
    
    // if taking the load-all-at-once approach, load mediaPlayers like this
//  NSString *videoURLString = @"https://playeritemvideourl";
//  AVPlayer *player = [AVPlayer playerWithURL:videoURLString];
//  [mediaPlayers setObject:player forKey:@"https://playeritemvideourl"];

    // lets say that the cell with the media in it is defined here
    NSIndexPath *dummyMediaIndexPath = [NSIndexPath indexPathForRow:40 inSection:0];
    // calculate an index path that, when visible, will trigger loading the media at dummyMediaIndexPath
    NSIndexPath *dummyKeyIndexPath = [NSIndexPath indexPathForRow:dummyMediaIndexPath.row-10
                                                  inSection:dummyMediaIndexPath.section];
    
    // add the key index path to the set of key index paths
    mediaKeyIndexPaths = [[NSMutableSet alloc] initWithObjects:dummyKeyIndexPath, nil];
    // define mediaKeyURLs mapping the key dummyKeyIndexPath to the value of the URL string we want
    mediaKeyURLs = [[NSDictionary alloc] initWithObjectsAndKeys:
                    @"https://playeritemvideourl", dummyKeyIndexPath,
                    nil];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 100;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TextCell" forIndexPath:indexPath];
    
    // this is the row of dummyMediaIndexPath defined in -viewDidLoad
    if (indexPath.row == 40)
    {
        self.playerController = [[AVPlayerViewController alloc] init];
        NSString *videoURLString = @"https://playeritemvideourl";
        AVPlayer *player = [mediaPlayers objectForKey:videoURLString]; // load player with URL
        if (!player)
        {
            player = [AVPlayer playerWithURL:[NSURL URLWithString:videoURLString]];
            NSLog(@"Video with URL: %@ was not preloaded. Loading now.", videoURLString);
        }
        self.playerController.player = player;
//      [player play];
// present your playerController here
        [cell setText:@"Player cell"];
    }
    else
    {
        [cell setText:[NSString stringWithFormat:@"Cell #%li", (long)indexPath.row]];
    }
    
    return cell;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // get visible rows
    NSArray *visibleRows = [self.tableView indexPathsForVisibleRows];
    // traverse through rows
    for (NSIndexPath *i in visibleRows)
    {
        // we use an NSSet to quickly determine if i is contained in mediaKeyIndexPaths
        if ([mediaKeyIndexPaths containsObject:i])
        {
            [mediaKeyIndexPaths removeObject:i]; // we only need to load a player once
            NSString *videoURLString = [mediaKeyURLs objectForKey:i];
            NSLog(@"Preloading URL: %@", videoURLString); // for information purposes only
            AVPlayer *player = [AVPlayer playerWithURL:[NSURL URLWithString:videoURLString]];
            [mediaPlayers setObject:player forKey:videoURLString];
        }
    }
}

@end

根据您提供的大量代码和信息,这已经是我所能做到的最具体的了。希望这对您有所帮助!