如何在下一页更改 url 的 AVAsset 并返回上一页而不会出现内存问题 iOS

How to change url of AVAsset on next page and come to the previous page without having memory issues iOS

我正在构建一个应用程序,场景是这样的:

我有 3 个视图控制器 VC1、VC2、VC3。

场景 1 我在 VC2 上有一个 AVPlayer,它使用保存在 userDefaults 中的 "NSURL" 来播放 video.Let 我们说这个 url 的关键是 "videoURL"。现在如果我弹出 VC2 和回到 VC1,dealloc() 被调用,所有东西都被释放,视频停止 playing.So,如果我更改 VC1 上的 "videoURL",然后将 VC2 推到导航堆栈上,我会得到一个新的 AVPlayer到目前为止,前一个是 deallocated.No 个问题。

场景2 如果我在 VC2 上更改 "videoURL",我会得到一个使用 "replaceCurrentItemWithPlayerItem" 的新 playerItem 来播放新的 video.No 问题。

场景 3 现在如果我将 VC3 推入堆栈,我不知道如何播放新的 URL 如果我弹回 VC3.The 当我推 VC3 时视频不会停止播放,即使我设置了 AVPlayer在推送 VC3.And 之前为 nil 当我停止视频并推送 VC3 时,我面临内存问题。

实施方案 3 的正确方法是什么?

您将不得不向视图控制器 2 的 viewWillDisappear 方法添加逻辑以停止和释放 AVPlayer,并将创建 AVPlayer 并开始播放的逻辑移动到 viewWillAppear(这样当您弹出视图控制器 3 时然后返回视图控制器 2 你可以再次播放视图控制器 2 的视频。)

如何操作取决于您管理 AVPlayer 的方式。 Post 设置 AVPlayer 并将其播放层安装到 VC2 的层层次结构中的 VC2 代码。 (编辑您的原始问题以包含该信息。不要 post 在评论中编写代码。它几乎不可读。)

这是我为场景 3 所做的:

-(void)goToNextPage
{
//This stops the video....
 [player replaceCurrentItemWithPlayerItem:nil];
}

现在我正在创建一个 delegate.So 现在 VC2 充当 VC3.So 的委托 当我在 VC3 上时,我写了一个代码来生成一个新的 Url,并通过它到 VC2 中的 属性。

所以我得出一个结论,只要在memory.Also中加载了VC2,我就不需要在VC2中释放AVPlayer,与之相关的观察者也不需要删除。

这里是 AVPlayer 的完整演示及其observers.I相信它会对每个开始使用 AVFoundation 的人和观察者有所帮助。

//VC1

-(IBAction)gotoPlayer:(id)sender
{

  PlayerViewController *playerVC = [self.storyboard instantiateViewControllerWithIdentifier:@"PlayerViewController"];
  [self.navigationController pushViewController:playerVC animated:YES];
}

//VC2.h

@interface PlayerViewController : UIViewController<ChangeUrlDelegate>
@property (nonatomic,strong) NSURL *url;

-(void)playWithURL:(NSURL*)url_;

@end

//VC2.m

 -(void)viewDidLoad
{
    [self setPlayerDataOnPageArrival];
}


-(void)viewDidAppear:(BOOL)animated
{
 if(self.url)
    [self playWithURL:self.url];
 else{
      NSString *path = [[NSBundle mainBundle]pathForResource:@"sample_mpeg4" ofType:@"mp4"];
      PlayerSource *source_ = [[PlayerSource alloc]initWithURL:[[NSURL alloc] initFileURLWithPath:path]];
      [self playWithURL:source_.sourceURL];
    }
}


-(void)dealloc
{

  NSLog(@"In playerViewController dealloc");
  [playerItem_ removeObserver:self forKeyPath:@"status"];
  [player_ removeTimeObserver:self.timeObserver];
  [[NSNotificationCenter defaultCenter]removeObserver:self   name:self.itemEndObserver object:playerItem_];
}


#pragma mark ChangeURLViewController delegate methods

-(void)newURL:(NSURL *)url_
{
  self.url = url_;
}


#pragma mark player methods

-(void)setPlayerDataOnPageArrival{

NSString *path = [[NSBundle mainBundle]pathForResource:@"sample_mpeg4" ofType:@"mp4"];
PlayerSource *source_ = [[PlayerSource alloc]initWithURL:[[NSURL alloc] initFileURLWithPath:path]];
self.url = source_.sourceURL;
asset_ = [AVAsset assetWithURL:self.url];

playerItem_ = [AVPlayerItem playerItemWithAsset:asset_];
NSLog(@"observer added");
[playerItem_ addObserver:self forKeyPath:@"status" options:0 context:nil];

player_ = [AVPlayer playerWithPlayerItem:playerItem_];
playerLayer = [AVPlayerLayer playerLayerWithPlayer:player_];

}


-(void)playWithURL:(NSURL*)url_{

[playerLayer setFrame:self.playerView.frame];

if(self.playerView.layer.sublayers.count == 0)
    [self.playerView.layer addSublayer:playerLayer];
else{

    AVPlayerItem *newPlayerItem_ = [AVPlayerItem playerItemWithURL:url_];
    [player_ replaceCurrentItemWithPlayerItem:newPlayerItem_];

    NSLog(@"rate--->%f",player_.rate);
    if(player_.rate == 0 && player_.status == 1)
        player_.rate = 1;
    }
}


-(void)addPlayerItemTimeObserver{

CMTime interval = CMTimeMakeWithSeconds(0.01,NSEC_PER_SEC);
 //   __weak PlayerViewController *weakSelf = self;
    self.timeObserver = [player_ addPeriodicTimeObserverForInterval:interval queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

    //NSLog(@"in addPlayerItemTimeObserver");

    CGFloat currentTime = CMTimeGetSeconds(time);
    NSLog(@"currentTime--->%f",currentTime);
    }];
}


-(void)addItemEndObserverForPlayerItem{

//When playback completes,an AVPlayerItem posts a notification called "AVPlayerItemDidPlayToEndTimeNotification"

self.itemEndObserver = AVPlayerItemDidPlayToEndTimeNotification;

[[NSNotificationCenter defaultCenter]addObserverForName:self.itemEndObserver object:nil queue:NULL usingBlock:^(NSNotification * _Nonnull note) {

    NSLog(@"in item end observer");
    }];
}



#pragma mark player status observer

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{

NSLog(@"In observer");

if (playerItem_.status == AVPlayerItemStatusReadyToPlay) {

    NSLog(@"success");
    [self addPlayerItemTimeObserver];
    [self addItemEndObserverForPlayerItem];
    [player_ play];
}

else if(player_.status == AVPlayerItemStatusFailed){

    NSLog(@"failed");
}

else if(player_.status == AVPlayerItemStatusUnknown){

    NSLog(@"Unknown");
    }  
}

#pragma mark action methods

-(IBAction)goToNextPage:(id)sender{

[player_ replaceCurrentItemWithPlayerItem:nil];
ChangeURLViewController *changeUrlVC_ = [self.storyboard instantiateViewControllerWithIdentifier:@"ChangeURLViewController"];
changeUrlVC_.delegate=self;
self.url = nil;
[self.navigationController pushViewController:changeUrlVC_ animated:YES];

}


-(IBAction)changeURL:(id)sender{

NSString *path = [[NSBundle mainBundle]pathForResource:@"sample_mpeg4" ofType:@"mp4"];
PlayerSource *source_ = [[PlayerSource alloc]initWithURL:[[NSURL alloc] initFileURLWithPath:path]];
[self playWithURL:source_.sourceURL];
}

//VC3.h

@protocol ChangeUrlDelegate <NSObject>

  -(void)newURL:(NSURL*)url_;

@end

@interface ChangeURLViewController : UIViewController

  @property (weak,nonatomic) id <ChangeUrlDelegate> delegate;

@end

//VC3.m

-(void)dealloc{

    NSLog(@"In changeURL dealloc");
}

-(IBAction)changeURLAction:(id)sender{

     NSString *path = [[NSBundle mainBundle]pathForResource:@"sample_mpeg4" ofType:@"mp4"];
    PlayerSource *source_ = [[PlayerSource alloc]initWithURL:[[NSURL alloc] initFileURLWithPath:path]];
    NSLog(@"delegate---->%@",self.delegate);
    [self.delegate newURL:source_.sourceURL];
    [self.navigationController popViewControllerAnimated:YES];

    }

注意:请注意,您应该在每个视图控制器中使用 dealloc() 并且它应该超时调用视图控制器从内存中删除(弹出导航堆栈)。