iPhone开发mpmovieplayer崩溃

Posted

技术标签:

【中文标题】iPhone开发mpmovieplayer崩溃【英文标题】:iPhone development mpmovieplayer crashing 【发布时间】:2011-11-15 14:18:00 【问题描述】:

我正在开发一个应用程序,它可以让我使用 iPhone 在 iPad 上远程播放不同的视频。我一直在关注视频播放器的苹果示例,但我遇到了一些麻烦。视频播放得很好,我可以从各种视频中播放它,但在它们之间切换几次它会崩溃,我在调试器中得到了这个:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An        AVPlayerItem cannot be associated with more than one instance of AVPlayer'
*** First throw call stack:
(0x380da8bf 0x37c261e5 0x30acbcb5 0x30abc1f7 0x30ac3bf3 0x30c93d55 0x30c95f7b 0x380ad2dd   0x380304dd 0x380303a5 0x37e07fcd 0x31bb0743 0x25e5 0x257c)

这是我用来创建播放器的代码:

MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
if (player) 
    [self setMoviePlayerController:player];
    [self installMovieNotificationObservers];
    [player setContentURL:movieURL];
    [player setMovieSourceType:sourceType];
    [self applyUserSettingsToMoviePlayer];
    [self.view addSubview:self.backgroundView];
    [player.view setFrame:self.view.bounds];
    [player.view setBackgroundColor = [UIColor blackColor];
    [self.view addSubview:player.view];

当当前电影停止时,我使用:

[[self moviePlayerController] stop];

MPMoviePlayerController *player = [self moviePlayerController];
[player.view removeFromSuperview];

[self removeMovieNotificationHandlers];
[self setMoviePlayerController:nil];

编辑: 所以我现在发现每次我第 11 次尝试切换视频时都会发生这种情况。奇怪的!我几乎要拔头发了。

【问题讨论】:

在通过 AirPlay 播放视频并允许将应用程序推送到后台时,我遇到了完全相同的错误。有时,当应用程序重新回到前台时,如果包含播放器的视图已被卸载,则恢复应用程序的尝试会因此错误而崩溃。 有任何解决方案吗?我在该领域有同样的错误。我的应用程序是一个视频应用程序,可以从网络加载不同的视频。我有多个 mpmoviecontroller 同时处于活动状态,但在任何给定时刻只有一个播放。 只是为了增加一点皱纹。这个错误 - 至少在我的情况下,我也希望在 OP 的情况下 - 在“_serverConnectionDiedNotification”被记录后立即被触发。详细信息如下:Info -- notification=Error Domain=AVFoundationErrorDomain Code=-11819 "Cannot Complete Action" UserInfo=0x42df3e0 NSLocalizedRecoverySuggestion=稍后再试。, NSLocalizedDescription=Cannot Complete Action。开始赏金了!先生们! 【参考方案1】:

解决这个问题的方法是在执行 setContentURL 之前停止 MPMoviePlayerController。

    MPMoviePlayerController *streamPlayer;

    [streamPlayer stop];
    [streamPlayer setContentURL:[NSURL URLWithString:selectedStation]];

【讨论】:

【参考方案2】:

在您上面的实现中,ARC 不知道 MPMoviePlayerController 已完成并需要释放。

在您的 .h 文件中定义 MPMoviePlayerController 并使其可通过 @property(和 @synthesize)访问。

@property (strong, nonatomic) MPMoviePlayerController * moviePlayerController;

然后获取 alloc & init 的结果并将其分配给它。即。

self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];

【讨论】:

即使我打开了 ARC?当我尝试发布一个版本时,它只是说它在 ARC 模式下是不允许的。我应该关掉电弧吗? 嘎...弧。我还是习惯了。您需要您的父母(视图控制器?)明确保留您的 MPMoviePlayer,然后 ARC 将知道如何处理它。我会更改我的答案以反映这一点。 仍然没有运气 :-( 可能是在另一个视频停止之前尝试添加视频吗?只有在它们之间切换几次之后才会发生,直到它们看起来一切正常。我开始了插曲检查泄漏但一无所获。还有什么我可以用来调试这个问题的吗?我已经启用了 NSZombieEnabled 但这没有帮助,我猜它没有发布已经发布的东西。【参考方案3】:

你应该只保留moviePlayerController,如果你想播放另一个视频,只需使用

[self.moviePlayerController setContentURL:movieURL];

然后在您的通知回调中:

- (void) moviePlayBackDidFinish:(NSNotification*)notification

    self.moviePlayer = nil;
    [self initanothermovieplayerandplay];

请不要从通知中心删除通知处理程序,仅在您的 VC 的 dealloc 方法中执行此操作。

现在让我们在电影播放完成后添加一些淡入淡出:

- (void) moviePlayBackDidFinish:(NSNotification*)notification

    [UIView animateWithDuration:1
                      delay: 0.0
                    options: UIViewAnimationOptionCurveEaseIn
                 animations:^
                     // one second to fade out the view
                     self.moviePlayer.view.alpha = 0.0;
                 
                 completion:^(BOOL finished)
                       self.moviePlayer = nil;
                       [self initanothermovieplayerandplay];
                 

【讨论】:

当然!除了 setContentURL,我还添加了 prepareToPlay,以便自动播放。如果我不准备……它不会。问题是,正如@Jeff B 所说,它不会每次都发生。是在几次调用之后才发生此错误。 @ferostar 我在答案中添加了一些代码,让我知道它是否有帮助。 感谢代码!但我相信除了将事物设为 nil 并重新创建它之外,必须有一些可能的东西,MPMovie ......应该能够从一个视频顺利过渡到另一个视频,而不需要这样的东西,你不觉得吗? 正如 Allen 所说,将 movieplayer 实例设置为 nil 很重要 - 仅此一项就解决了我的播放器中我什至没有尝试重用的问题。【参考方案4】:

我遇到了完全相同的问题。 我的没有什么问题,我猜你的代码:) 只是一个损坏的视频文件是我的问题。 例如,将 *.mov 类型更改为 m4a 即可修复它。也许您播放的一个或多个文件已损坏? 尝试找出导致崩溃的文件,而不是在播放时尝试快速向后移动其中一个文件的播放位置 - 这应该会导致几次尝试崩溃。 这就是我找到坏文件的方式。顺便说一句,我所有的坏文件都是用 Snapz Pro X 制作的电影 .mov :)

【讨论】:

有趣。我会检查一下。 这似乎是对的。在删除了一些随机来源的测试视频等后,我们已经停止看到崩溃,但崩溃的“共同症状”是 CPU 使用率高,并且在开始播放视频时应用程序冻结。我们还解决了高 CPU 问题,但现在还不完全清楚是什么导致了这个问题以及究竟是什么消除了它。我们只是到处开枪,很可能得到了它。但一周以上的测试将证明其中至少一种方法有效。可悲的是,我还没有找到延长赏金的方法。让我看看。 顺便说一句,我们试图从服务器播放 m3u8,直到上周初才遇到任何问题。这些视频都以 MP4 格式来源,都来自同一个客户端 - 因此很可能会被录制,并使用相同的设置进行编码。我们知道它们都是 1080p,平均速度约为 8-10mbps。也许所有这些都指向了一个内容问题——因为上周是我们第一次上传一些随机视频来测试系统。并且视频播放/服务代码在过去几周没有改变。 所以这可能最接近我们遇到此问题的原因。但是还没有具体的证据,这让我现在不愿意关闭赏金。一周的测试会很棒。 :( 我在某处读过关于启动多个赏金的内容,但还没有弄清楚。 附言。 CPU 使用率修复是我们几周来对播放代码所做的第一次更改,那是在我们遇到问题之后。【参考方案5】:

不确定这里是否是这种情况,但我们遇到了很多问题,因为 MPMoviePlayer 在引擎盖下的某个地方是一个单例。 我们所做的是,我们实现了我们自己的 MoviePlayer 包装器,它可以从 UIView 中使用(实际上我们只有一个 UIView MoviePlayerView 的子类来显示电影)并确保只有一个 MPMoviePlayerController 实例存在。代码是这样的(它包含一些特殊的东西,我们需要以我们想要的方式显示预览/拇指等。你应该清理以及一些发布声明):

//  MoviePlayer.h

#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "Logger.h"

@class MoviePlayerView;

@interface MoviePlayer : NSObject

  @private
  MPMoviePlayerController *controller;
  MoviePlayerView *currentView;


@property (nonatomic, readonly) MPMoviePlayerController *controller;

+(MoviePlayer *) instance;

-(void) playMovie:(NSURL*)movieURL onView:(MoviePlayerView *)view;
-(void) stopMovie;

@end



//  MoviePlayer.m




#import "MoviePlayer.h"
#import "MoviePlayerView.h"

@implementation MoviePlayer

@synthesize controller;

static MoviePlayer *player = nil;

#pragma mark Singleton management

+(MoviePlayer *) instance

  @synchronized([MoviePlayer class])
  
    if (player == nil) 
    
      player = [[super allocWithZone:NULL] init];
      player->controller = [[MPMoviePlayerController alloc] init];
      player->controller.shouldAutoplay = NO;
      player->controller.scalingMode = MPMovieScalingModeAspectFit;
      player->currentView = nil;
    
    return player;
  


+(id) allocWithZone:(NSZone *)zone

  return [[self instance] retain];


-(id) copyWithZone:(NSZone *)zone

  return self;


-(id) retain

  return self;


-(NSUInteger) retainCount

  return NSUIntegerMax; 


-(oneway void) release

  // singleton will never be released


-(id) autorelease

  return self;


#pragma mark MoviePlayer implementations

-(void) stopMovie

  @synchronized(self)
  
    if (controller.view.superview)
    
      [controller.view removeFromSuperview];
    
    if (controller.playbackState != MPMoviePlaybackStateStopped)
    
      [controller pause];
      [controller stop];
    
    if (currentView)
    
      NSNotificationCenter *ntfc = [NSNotificationCenter defaultCenter];
      [ntfc removeObserver:currentView name:MPMoviePlayerLoadStateDidChangeNotification object:controller];
      [ntfc removeObserver:currentView name:MPMoviePlayerPlaybackStateDidChangeNotification object:controller];
      currentView = nil;
    
  


-(void) playMovie:(NSURL*)movieURL onView:(MoviePlayerView *)view

  @synchronized(self)
  
    [self stopMovie];
    currentView = view;

    NSNotificationCenter *ntfc = [NSNotificationCenter defaultCenter];
    [ntfc addObserver:currentView 
             selector:@selector(loadStateDidChange:)
                 name:MPMoviePlayerLoadStateDidChangeNotification 
               object:controller];
    [ntfc addObserver:currentView 
             selector:@selector(playbackStateDidChange:)
                 name:MPMoviePlayerPlaybackStateDidChangeNotification
               object:controller];

    [controller setContentURL:movieURL];
    controller.view.frame = view.bounds;
    [view addSubview: controller.view];
    [controller play];
  


@end


//  MoviePlayerView.h

#import <UIKit/UIKit.h>
#import "MoviePlayer.h"


@interface MoviePlayerView : MediaView 

  NSURL *movieURL;
  NSURL *thumbnailURL;
  UIImageView *previewImage;
  UIView *iconView;
  BOOL hasPreviewImage;


-(id) initWithFrame:(CGRect)frame thumbnailURL:(NSURL *)thumbnail movieURL:(NSURL *)movie;
-(void) loadStateDidChange:(NSNotification *)ntf;
-(void) playbackStateDidChange:(NSNotification *)ntf;

@end



//  MoviePlayerView.m

#import "MoviePlayerView.h"

@interface MoviePlayerView()
-(void) initView;
-(void) initController;
-(void) playMovie;
-(void) setActivityIcon;
-(void) setMovieIcon:(float)alpha;
-(void) clearIcon;
-(CGPoint) centerPoint;
@end

@implementation MoviePlayerView

-(id) initWithFrame:(CGRect)frame thumbnailURL:(NSURL *)thumbnail movieURL:(NSURL *)movie

  self = [super initWithFrame:frame];
  if (self) 
  
    movieURL = [movie retain];
    thumbnailURL = [thumbnail retain];
    [self initView];
    [self initController];
    hasPreviewImage = NO;
    loadingFinished = YES;
  
  return self;


-(void) dealloc

  [iconView release];
  [previewImage release];
  [movieURL release];
  [super dealloc];


-(void)initView

  self.backgroundColor = [UIColor blackColor];

  // add preview image view and icon view
  previewImage = [[UIImageView alloc] initWithFrame:self.bounds];
  [previewImage setContentMode:UIViewContentModeScaleAspectFit];
  UIImage *img = nil;
  if (thumbnailURL)
  
    img = [ImageUtils loadImageFromURL:thumbnailURL];
    if (img)
    
      previewImage.image = img;
      hasPreviewImage = YES;
    
  
  [self addSubview:previewImage];
  [self setMovieIcon:(hasPreviewImage ? 0.8f : 0.3f)];


-(void)initController

  UITapGestureRecognizer *rec = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(playMovie)];
  [self addGestureRecognizer:rec];
  [rec release];


-(void)playMovie

  [[MoviePlayer instance] playMovie:movieURL onView:self];
  [self setActivityIcon];


-(void) loadStateDidChange:(NSNotification *)ntf

  MPMoviePlayerController *controller = [ntf object];
  switch (controller.loadState)
  
    case MPMovieLoadStatePlayable:
    
      [self clearIcon];
      [controller setFullscreen:YES animated:YES];
      break;
    
    case MPMovieLoadStateStalled:
    
      [self setActivityIcon];
      break;
    
    default:
    
      break; // nothing to be done
    
  


-(void) playbackStateDidChange:(NSNotification *)ntf

  MPMoviePlayerController *controller = [ntf object];
  switch (controller.playbackState) 
  
    case MPMoviePlaybackStatePlaying:
    
      [self clearIcon];
      break;
    
    case MPMoviePlaybackStateStopped:
    
      [self setMovieIcon:(hasPreviewImage ? 0.8f : 0.3f)];
      break;
    
    case MPMoviePlaybackStatePaused:
    
      [self setMovieIcon:0.8f];
      break;
    
    case MPMoviePlaybackStateInterrupted:
    
      [self setActivityIcon];
      break;
    
    default:
    
      break; // nothing to be done
    
  


-(void) setActivityIcon

  [self clearIcon];
  iconView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
  iconView.center = [self centerPoint];
  [self addSubview:iconView];
  [iconView performSelector:@selector(startAnimating)];


-(void) setMovieIcon:(float)alpha

  [self clearIcon];
  iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon_movie.png"]];
  iconView.center = [self centerPoint];
  iconView.alpha = alpha;
  [self addSubview:iconView];


-(void) clearIcon

  if (iconView)
  
    SEL stop = @selector(stopAnimating);
    if ([iconView respondsToSelector:stop])
    
      [iconView performSelector:stop];
    
    [iconView removeFromSuperview];
    [iconView release];
    iconView = nil;
  


-(CGPoint) centerPoint

  return CGPointMake(roundf(self.bounds.size.width / 2.0f), roundf(self.bounds.size.height / 2.0f));


-(void)resize

  for (UIView *view in [self subviews]) 
  
    if (view == iconView)
    
      iconView.center = [self centerPoint];
      continue;
    
    view.frame = self.bounds;
  
  [self addCaptionLabel];


-(void) layoutSubviews

  [super layoutSubviews];
  [self resize];


@end

【讨论】:

【参考方案6】:
...
player = [[MPMoviePlayerController alloc] initWithContentURL: [NSURL URLWithString:...
...

但我没有给手机提供互联网连接 (wi-fi) :)

【讨论】:

【参考方案7】:

我遇到了同样的问题。我的解决方案是使用 prepareToPlay:

MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];

if (player) 
   [player prepareToPlay];
   //...

【讨论】:

【参考方案8】:

这个错误似乎有很多不同的原因,但我发现的原因是,如果你以特定的顺序调用方法,MPMoviePlayerController 类会出错。来自 IRC 频道:

"显然,如果您调用 prepareToPlay WHILE 设置源类型和 尚未设置视图会导致此崩溃"

所以我通过确保我调用prepareToPlay: LAST(或倒数第二个,最后一个是play:)来解决此问题。

这也很奇怪,因为我的原始代码在 ios 5.1 中工作,但是当我开始使用 iOS 6.0 sdk 时,这个问题突然出现。这可能是 MPMoviePlayerController 代码中的错误,因此我将提交一份雷达报告,因为在设置视图/设置 sourceFileType 之前调用 prepareToPlay: 不应引发异常(或至少是异常这似乎与实际错误无关)

【讨论】:

以上是关于iPhone开发mpmovieplayer崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MPMoviePlayer 播放期间启用 iPhone 自动锁定?

不使用 MPMoviePlayer 在 iPhone 上动画图像(如电影)的方法

iPhone:MPMoviePlayer 最初在几分之一秒内显示黑色背景

横向模式下的 iPhone MPMoviePlayer

MPMoviePlayer 声音在模拟器和 ipad 设备中工作,但在 iPhone 设备中不工作

帮我选择合适的 iPhone 音频类 - MPMoviePlayer vs AVAudioPlayer vs MPMusicPlayer