сбой разработки iPhone mpmovieplayer - PullRequest
12 голосов
/ 15 ноября 2011

Я работаю над приложением, которое позволит мне удаленно воспроизводить различные видео на iPad с iPhone.Я следовал примеру яблок для видеоплеера, но у меня были некоторые проблемы.Видео воспроизводится очень хорошо, и я могу заставить его воспроизводиться на разных видео, но переключаясь между ними несколько раз, он падает, и я получаю это в отладчике:

*** 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-й раз.странно!Я практически выдергиваю волосы.

Ответы [ 8 ]

7 голосов
/ 21 октября 2012

Что решило эту проблему для меня, так это остановка MPMoviePlayerController перед выполнением setContentURL.

    MPMoviePlayerController *streamPlayer;

    [streamPlayer stop];
    [streamPlayer setContentURL:[NSURL URLWithString:selectedStation]];
1 голос
/ 31 мая 2012

У меня была точно такая же проблема.Ничего не случилось с моим, и я думаю, с вашим кодом :) Просто сломанный видеофайл был моей проблемой.Изменение типа * .mov на m4a, например, исправило это.Может быть, один или несколько файлов, которые вы воспроизводите, повреждены?Попытайтесь выяснить, какие файлы приводят к сбою, и чем, если вы можете попытаться быстро переместиться назад на позицию воспроизведения одного из них во время воспроизведения - это должно привести к сбою в нескольких попытках.Вот как я нашел плохие файлы.Кстати, все мои плохие файлы - это фильмы .mov, сделанные с помощью Snapz Pro X :)

1 голос
/ 02 февраля 2012

Вы должны просто сохранить moviePlayerController и, если вы хотите воспроизвести другое видео, просто используйте

[self.moviePlayerController setContentURL:movieURL];

, затем в ответном уведомлении:

- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
    self.moviePlayer = nil;
    [self initanothermovieplayerandplay];
}

и, пожалуйста, не удаляйтеОбработчик уведомлений из центра уведомлений, делайте это только в методе dealloc вашего VC.

Теперь давайте добавим некоторое затухание после завершения воспроизведения фильма:

- (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];
                 }
}
1 голос
/ 15 ноября 2011

В вышеописанной реализации ARC не знает, что MPMoviePlayerController завершен и его необходимо освободить.

Определите MPMoviePlayerController в вашем файле .h и сделайте его доступным через @property (и @синтезировать).

@property (strong, nonatomic) MPMoviePlayerController * moviePlayerController;

Затем возьмите результат вашего alloc & init и присвойте его этому.IE

self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
0 голосов
/ 21 ноября 2012

Эта ошибка, похоже, возникает по разным причинам, но я обнаружил, что класс MPMoviePlayerController выходит из строя, если вы вызываете методы в определенном порядке. С канала IRC:

"очевидно, если вы вызываете prepareToPlay WHILE, устанавливая тип источника и НЕ установка представления пока вызывает этот сбой "

Итак, я исправил это, просто убедившись, что я позвонил prepareToPlay: LAST (или от второго к последнему, с последним значением play:).

Это также странно, потому что мой оригинальный код работал в iOS 5.1, но эта проблема неожиданно проявилась, когда я начал использовать iOS 6.0 SDK. Возможно, это ошибка в коде MPMoviePlayerController, поэтому я собираюсь подать отчет о радаре, так как вызов prepareToPlay: перед настройкой вида / установки sourceFileType не должен вызывать исключение (или, по крайней мере, исключение что, похоже, не имеет ничего общего с фактической ошибкой)

0 голосов
/ 31 октября 2012

У меня была такая же проблема.Мое решение использует prepareToPlay:

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

if (player) {
   [player prepareToPlay];
   //...
}
0 голосов
/ 18 августа 2012
...
player = [[MPMoviePlayerController alloc] initWithContentURL: [NSURL URLWithString:...
...

но я не подключил интернет к телефону (wi-fi):)

0 голосов
/ 02 февраля 2012

Не уверен, что это так, но у нас было много проблем, потому что MPMoviePlayer - это синглтон где-то под капотом. Что мы сделали, так это то, что мы реализовали нашу собственную MoviePlayer оболочку, которую можно использовать из UIView (на самом деле у нас есть ровно один подкласс UIView MoviePlayerView для показа фильмов) и гарантируем, что существует только один экземпляр MPMoviePlayerController. Код выглядит следующим образом (он содержит некоторые специальные вещи, нам нужно показывать превью / превью, как мы хотим и т. Д., Вы должны очистить, а также некоторые операторы release):

//  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
...