Как получить NSNotifications от UIWebView встроенного воспроизведения видео YouTube

Я не получил никаких уведомлений для MPMoviePlayerController. Что я делаю не так?

Я использую следующую логику.

Я начинаю играть видео youtube в UIWebView. UIWebView называет стандартный MPMoviePlayerController. Я не контролирую MPMoviePlayerController потому что я не инстанцировать MPMoviePlayerController.

я запускаю клип youtube с автозапуском (задержка 1 секунда):

[self performSelector:@selector(touchInView:) withObject:b afterDelay:1];

мой код:

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackDidFinish:) name:MPMoviePlayerDidExitFullscreenNotification object:nil];

    [self embedYouTube];
}

- (void)loadStateDidChange:(NSNotification*)notification
{
    NSLog(@"________loadStateDidChange");
}

- (void)playbackDidFinish:(NSNotification*)notification
{
    NSLog(@"________DidExitFullscreenNotification");
}

- (void)embedYouTube
{
    CGRect frame = CGRectMake(25, 89, 161, 121);
    NSString *urlString = [NSString stringWithString:@"http://www.youtube.com/watch?v=sh29Pm1Rrc0"];

    NSString *embedHTML = @"<html><head>
    <body style="margin:0">
    <embed id="yt" src="%@" type="application/x-shockwave-flash" 
    width="%0.0f" height="%0.0f"></embed>
    </body></html>";
    NSString *html = [NSString stringWithFormat:embedHTML, urlString, frame.size.width, frame.size.height];
    UIWebView *videoView = [[UIWebView alloc] initWithFrame:frame];
    videoView.delegate = self;

    for (id subview in videoView.subviews)
        if ([[subview class] isSubclassOfClass: [UIScrollView class]])
            ((UIScrollView *)subview).bounces = NO;

            [videoView loadHTMLString:html baseURL:nil];
    [self.view addSubview:videoView];
    [videoView release];
}

- (void)webViewDidFinishLoad:(UIWebView *)_webView 
{
    UIButton *b = [self findButtonInView:_webView];
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(touchInView:) object:b];
    [self performSelector:@selector(touchInView:) withObject:b afterDelay:1];
}

- (UIButton *)findButtonInView:(UIView *)view 
{
    UIButton *button = nil;

    if ([view isMemberOfClass:[UIButton class]]) {
        return (UIButton *)view;
    }

    if (view.subviews && [view.subviews count] > 0) 
    {
        for (UIView *subview in view.subviews) 
        {
            button = [self findButtonInView:subview];
            if (button) return button;
        }
    }
    return button;
}

- (void)touchInView:(UIButton*)b
{
    [b sendActionsForControlEvents:UIControlEventTouchUpInside];
}

обновление: я создаю приложение, которое играет видео с youtube. Вы можете запустить список воспроизведения, и вы увидите первое видео. Когда первое видео закончилось, второе видео начинает воспроизводиться автоматически и так далее.

мне нужно поддерживать ios 4.1 и выше.

обновление 2: @H2CO3 я пытаюсь использовать вашу url-схему, но она не работает. Метод Delegate не вызывал событие exit. Я добавил свой HTML url в журнал. Это:

<html><head>    <body style="margin:0">    
<script>function endMovie() 
{document.location.href="somefakeurlscheme://video-ended";} 
 </script>      <embed id="yt" src="http://www.youtube.com/watch?v=sh29Pm1Rrc0"        
 onended="endMovie()" type="application/x-shockwave-flash"  
 width="161" height="121"></embed>  
 </body></html>

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
  if ([[[request URL] absoluteString] hasPrefix:@"somefakeurlscheme://video-ended"]) 
  {
    [self someMethodSupposedToDetectVideoEndedEvent];
    return NO; // prevent really loading the URL
   }
  return YES; // else load the URL as desired
}

UPDATE3 @Till, я не могу поймать UIMoviePlayerControllerDidExitFullscreennotification, но я нашел MPAVControllerItemPlaybackDidEndnotification. MPAVControllerItemPlaybackDidEndnotification появляется после завершения воспроизведения видео.

но я не понимаю, как я могу поймать уведомления onDone?

9 ответов


нет документированных уведомлений, отправленных UIWebView встроенный видео-плеер.

фактически, закрытая реализация, используемая в UIWebView отличается от общественности MPMoviePlayerController во многих аспектах (например, DRM).

наиболее важные классы, используемые для воспроизведения видеоконтента в этом UIWebView называют MPAVController и UIMoviePlayerController. Последний делает игрока похожим на MPMoviePlayerController полноэкранный интерфейс.

в случае, если вы решитесь рисковать отказ от Apple, на самом деле есть способы все еще достичь того, что вы ищете.

Примечание Это не документировано и может быть нарушено в каждом новом выпуске iOS. Однако он работает на ios4.3, 5.0 и 5.01, 5.1 и 6.0, и это мая работа над другими версиями, а также.

я не могу протестировать это решение на iOS 4.1 и 4.2, так что это зависит от вас. Я очень подозреваю, что это будет работа.


Полноэкранный Государства

если, например, вы собираетесь реагировать на пользователя, нажав сделал кнопка, вы можете сделать это таким образом:

обновление старую версию этого ответа рекомендуется использовать UIMoviePlayerControllerDidExitFullscreenNotification в то время как эта новая версия (обновленная для iOS6) рекомендует использовать UIMoviePlayerControllerWillExitFullscreenNotification.

Языка C Уровень:

void PlayerWillExitFullscreen (CFNotificationCenterRef center,
                 void *observer,
                 CFStringRef name,
                 const void *object,
                 CFDictionaryRef userInfo)
{
    //do something...
}

CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), 
    NULL, 
    PlayerWillExitFullscreen, 
    CFSTR("UIMoviePlayerControllerWillExitFullscreenNotification"), 
    NULL,  
    CFNotificationSuspensionBehaviorDeliverImmediately);

Цель-C Уровень:

- (void)playerWillExitFullscreen:(NSNotification *)notification
{
    //do something...
}

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(playerWillExitFullscreen:)
                                             name:@"UIMoviePlayerControllerWillExitFullscreenNotification" 
                                           object:nil];

я набросал оба варианта, C-Level и Objective-C-Level, потому что лучший способ узнать обо всем этом-использовать функции C-Level (CoreFoundation), как показано в конце моего ответа. Если отправитель уведомления не использует Objective-C (NSNotifications), вы мая на самом деле не удастся поймать их с помощью nsnotification-механики.


воспроизведение Государство

для изучения состояния воспроизведения обратите внимание на "MPAVControllerPlaybackStateChangedNotification" (как описано выше) и изучить userInfo, который может выглядеть так:

{
    MPAVControllerNewStateParameter = 1;
    MPAVControllerOldStateParameter = 2;
}

Дальнейшее Обратное Проектирование

для обратного проектирования и изучения всех отправленных уведомлений используйте следующий фрагмент.

void MyCallBack (CFNotificationCenterRef center,
                 void *observer,
                 CFStringRef name,
                 const void *object,
                 CFDictionaryRef userInfo)
{
    NSLog(@"name: %@", name);
    NSLog(@"userinfo: %@", userInfo);
}

CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), 
    NULL, 
    MyCallBack, 
    NULL, 
    NULL,  
    CFNotificationSuspensionBehaviorDeliverImmediately);

В iOS 4.3+ вы можете использовать UIMoviePlayerControllerDidEnterFullscreenNotification и UIMoviePlayerControllerDidExitFullscreenNotification уведомления:

-(void)viewDidLoad
{

    ...

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeStarted:) name:@"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeFinished:) name:@"UIMoviePlayerControllerDidExitFullscreenNotification" object:nil];
}

-(void)youTubeStarted:(NSNotification *)notification{
    // your code here
}

-(void)youTubeFinished:(NSNotification *)notification{
    // your code here
}

насколько я знаю, детали реализации UIWebView (и все системные классы, сделанные Apple) не следует полагаться при создании приложения Cocoa Touch. Возможно, это тот случай, когда видеоплеер UIWebView не стандартный класс MPMoviePlayerController, и он может иметь совершенно другую систему делегирования / уведомления, которая не должна быть доступна пользователю.

Я предлагаю вам использовать элемент HTML5 и обнаружить "событие onended" этого тега:

<html>
    <body>
        <script>
function endMovie() {
    // detect the event here
    document.location.href="somefakeurlscheme://video-ended";
}
        </script>
        <video src="http://youtube.com/watch?v=aiugvdk755f" onended="endMovie()"></video>
    </body>
</html>

на самом деле, из функции Endmovie JavaScript, вы можете перенаправить на поддельный URL, который вы можете поймать в вашем-webView:shouldStartLoadWithRequest: (UIWebViewDelegate) метод таким образом получить уведомление, что видео закончилось:

- (BOOL) webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)req {
    if ([[[req URL] absoluteString] hasPrefix:@"somefakeurlscheme://video-ended"]) {
        [self someMethodSupposedToDetectVideoEndedEvent];
        return NO; // prevent really loading the URL
    }
    return YES; // else load the URL as desired
}

надеюсь, что это помогает.


на основе ответа @H2CO3, но с iframe API. Это был единственный способ заставить его работать.

это не использует собственный API, который делает его более перспективным.

вот код для вставки вашего видео на Youtube. Проверьте API для получения дополнительных способов настройки этого.

<html>
  <body>
  <!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
  <div id="player"></div>

  <script>
  // 2. This code loads the IFrame Player API code asynchronously.
    var tag = document.createElement('script');

    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    // 3. This function creates an <iframe> (and YouTube player)
    //    after the API code downloads.
    var player;
    function onYouTubeIframeAPIReady() {
      player = new YT.Player('player', {
        height: '480',
        width: '640',
        videoId: 'aiugvdk755f',
        events: {
          'onStateChange': onPlayerStateChange
        }
      });
    }
    // 5. The API calls this function when the player's state changes.
    function onPlayerStateChange(event) {
      if (event.data == YT.PlayerState.ENDED) {
        endedMovie();
      }
    }
    function endedMovie() {
      // detect the event here
      document.location.href="somefakeurlscheme://video-ended";
    }
  </script>
  </body>
</html>

и вот как вы получаете уведомление о том, что видео закончилось (метод UIWebViewDelegate).

- (BOOL) webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)req {
    if ([[[req URL] absoluteString] hasPrefix:@"somefakeurlscheme://video-ended"]) {
        [self someMethodSupposedToDetectVideoEndedEvent];
        return NO; // prevent really loading the URL
    }
    return YES; // else load the URL as desired
 }

в ViewDidLoad добавьте следующий код

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(VideoExitFullScreen:) name:@"UIMoviePlayerControllerDidExitFullscreenNotification" object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(VideoEnterFullScreen:) name:@"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil];

следующие методы предназначены для отображения сообщения / функций для соответствующего процесса входа/выхода в / из полноэкранного

- (void)VideoExitFullScreen:(id)sender{
// Your respective content/function for Exit from full screen
}

- (void)VideoEnterFullScreen:(id)sender{
// Your respective content/function for Enter to full screen
}

это работает для меня в iOS 6.1, он скрывает / удаляет другие окна при получении AVPlayerItemDidPlayToEndTimeNotification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemEnded:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

...

- (void)playerItemEnded:(NSNotification *)notification
{    
    for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
        if (window != self.window) {
            window.hidden = YES;
        }
    }
}

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeStarted:) name:UIWindowDidBecomeVisibleNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeFinished:) name:UIWindowDidBecomeHiddenNotification object:nil];


-(void)youTubeStarted:(NSNotification *)notification
 {
   // Entered Fullscreen code goes here..
   AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
   appDelegate.fullScreenVideoIsPlaying = YES;
   NSLog(@"%f %f",webViewForWebSite.frame.origin.x,webViewForWebSite.frame.origin.y);

 }

 -(void)youTubeFinished:(NSNotification *)notification{
   // Left fullscreen code goes here...
   AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
   appDelegate.fullScreenVideoIsPlaying = NO;

   //CODE BELOW FORCES APP BACK TO PORTRAIT ORIENTATION ONCE YOU LEAVE VIDEO.
   [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO];
   //present/dismiss viewcontroller in order to activate rotating.
   UIViewController *mVC = [[UIViewController alloc] init];
   [self presentViewController:mVC animated:NO completion:Nil];
   //  [self presentModalViewController:mVC animated:NO];
   [self dismissViewControllerAnimated:NO completion:Nil];
   //   [self dismissModalViewControllerAnimated:NO];

}

для iOS8 (также у меня есть встроенное видео, которое не является видео youtube) единственное решение, которое я мог бы получить, чтобы работать, чтобы поймать один из viewWill/DidLayoutSubviews, и в качестве дополнительного бонуса вам не нужно менять HTML или использовать какие-либо частные API :

так в основном:

@property (nonatomic) BOOL showingVideoFromWebView;
...
...

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
 navigationType:(UIWebViewNavigationType)navigationType {
    if (navigationType == UIWebViewNavigationTypeOther) {
        //Was "other" in my case... Might be UIWebViewNavigationTypeLinkClicked
        self.showingVideoFromWebView = YES;
    }
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    // Do whatever...
    // Note: This will get called both when video is entering fullscreen AND exiting!
    self.showingVideoFromWebView = NO;
}

в моем случае мой веб-вид находится внутри UITableViewCell поэтому мне пришлось найти способ связи между ячейкой и контроллером представления, а также избежать использования флага BOOL, который я сделал это:

- (BOOL)webView:(UIWebView *)webView shouldStartLoad.....
... if (opening video check....) {
    [[NSNotificationCenter defaultCenter] addObserverForName:@"webViewEmbedVidChangedState" object:nil queue:nil usingBlock:^(NSNotification *note) {
        // Do whatever need to be done when the video is either 
        // entering fullscreen or exiting fullscreen....
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"webViewEmbedVidChangedState" object:nil];
    }];
}

- (void)viewWillLayoutSubviews.....
    [[NSNotificationCenter defaultCenter] postNotificationName:@"webViewEmbedVidChangedState" object:nil];

на самом деле для целей обратного проектирования вы также можете использовать Cocoa API как

   [[NSNotificationCenter defaultCenter] addObserver:self
                                            selector:@selector(handleNotification:)
                                                name:nil
                                              object:nil];

в этом случае вы получите все уведомления