Как получать NSNotification от встроенного UIWebView воспроизведения видео YouTube - PullRequest
51 голосов
/ 15 декабря 2011

Я не получил никаких уведомлений для 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];
}

ОБНОВЛЕНИЕ: I 'Я создаю приложение, которое воспроизводит видео с YouTube.Вы можете запустить плейлист, и вы увидите первое видео.Когда первое видео закончилось, второе видео начинает воспроизводиться автоматически и так далее.

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

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

<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
}

ОБНОВЛЕНИЕ3 @ До, я не могу поймать UIMoviePlayerControllerDidExitFullscreenNotification, но я обнаружил MPAVControllerItemPlaybackDidEndNotification.MPAVControllerItemPlaybackDidEndNotification отображается после завершения воспроизведения видео.

Но я не понимаю, как мне ловить уведомления на Дону?

Ответы [ 9 ]

64 голосов
/ 18 декабря 2011

Нет документированных уведомлений, отправленных встроенным проигрывателем фильмов 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, так что вам решать.Я очень подозреваю, что это будет работать.


Полноэкранное состояние

Если, например, вы намереваетесь отреагировать на пользователя, нажав DONE *Кнопка 1030 *, вы можете сделать это следующим образом:

ОБНОВЛЕНИЕ В старой версии этого ответа рекомендуется использовать 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);

Уровень Objective-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-mechanics.


ВоспроизведениеСостояние

Для проверки состояния воспроизведения обратите внимание на "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);
32 голосов
/ 01 февраля 2012

В 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
}
16 голосов
/ 18 декабря 2011

Насколько я знаю, на детали реализации 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>

Фактически, из функции JavaScript endMovie вы можете перенаправить на фиктивный 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
}

Надеюсь, это поможет.

6 голосов
/ 24 сентября 2013

На основе ответа @ 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
 }
5 голосов
/ 29 октября 2013

в 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
}
4 голосов
/ 13 февраля 2013

Это работает для меня в 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;
        }
    }
}
3 голосов
/ 07 октября 2014
[[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];

}
1 голос
/ 01 декабря 2015

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

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

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

1 голос
/ 27 октября 2014

Для 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];
...