滚动滚动时,Avplayer项目在uitableview上进行缓冲

发布于 2025-01-22 08:51:32 字数 602 浏览 1 评论 0原文

我已经创建了一个使用自定义的ViewCells的UitableView。 UitaiteView由通过Internet加载的图像和视频组成。当用户滚动时,在一个uitaiteviewcell中滚动时,我加载了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的CurrentItem上,因此当我滚动到特定的uitableviewcell时,视频开始播放?我查看了avplayeritem上的 preferredforwardbufferduration 属性,但似乎没有任何区别。任何帮助或建议都感谢!

I have created an UITableView with custom UITableViewCells. UITableView consists of images and videos that load via internet. While user is scrolling in one of the UITableViewCell i load AVPlayer to play a hls video. I set URL in hls format to the avplayer item in order to play the item.

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

The video plays but there is a delay of about 3 to 5 seconds from the moment [self.player play] is triggered. How do i pre buffer the video to the currentitem of avplayer so when i scroll to the specific UITableViewCell the video starts playing instantly? I looked at preferredForwardBufferDuration property on AVPlayerItem but does not seem to make any difference. Any help or suggestions appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

未央 2025-01-29 08:51:32

avplayer在实例化时开始流M3U8。 (我通过监视Xcode的调试导航器中的网络图来注意到这一点。我在不调用的情况下实例化了Avplayer,而网络正在加载。)而不是加载avplayer < /code>一旦单元格变得可见,实例化 avplayer play content < em>当它变为可见时。

可以通过首先让一些数据结构保存avplayer项目来实现这一点。我会推荐一个nsmutabledictionary,因为我们可以设置您想要的视频URL的键,并且该对象可以是已加载带有URL的avplayer。有两种填充结构的方法。您可以在-viewDidload之类的方法中立即加载所有内容,也可以在-scrollviewDidsCroll中动态加载项目:确定我们是否靠近带有视频的单元格。如果没有很多内容,我会使用前者,并且用户几乎可以保证观看所有视频。如果我们有很多内容可以加载,或者用户可能无法观看所有视频,我将使用后者。这是一个带有uitaiteViewController的示例。

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

这与您提供的代码和信息的数量一样具体。希望这有帮助!

AVPlayer begins streaming m3u8 when it is instantiated. (I noticed this by monitoring the Network graph in the Debug navigator in Xcode. I instantiated an AVPlayer without calling -[play] and the network was under load.) Instead of loading the AVPlayer once the cell becomes visible, instantiate the AVPlayer before the cell is visible and play the content when it becomes visible.

This can be implemented by first having some data structure hold the AVPlayer items. I would recommend a NSMutableDictionary, as we can set the key to the video's URL we want and the object can be the AVPlayer already loaded with the URL. There are two ways to populate the structure. You could load it all at once in a method like -viewDidLoad or load items in dynamically in -scrollViewDidScroll: to determine if we are close to a cell with a video. I would use the former if there is not a lot of content and the user is almost guaranteed to watch all the videos. I would use the latter if we have a lot of content to load or if the user might not watch all the videos. Here is an example with a 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

This is about as specific as I can make with the amount of code and information you provided. Hope this helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文