在 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
根据您提供的大量代码和信息,这已经是我所能做到的最具体的了。希望这对您有所帮助!
我创建了一个带有自定义 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
根据您提供的大量代码和信息,这已经是我所能做到的最具体的了。希望这对您有所帮助!