Подтвердить что ты не робот

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

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

UPDATE: Я создаю приложение, которое воспроизводит видео на YouTube. Вы можете запустить плейлист, и вы увидите первое видео. Когда первое видео закончилось, второе видео начинает играть автоматически и так далее.

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

UPDATE2: @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 @До сих пор я не мог поймать UIMoviePlayerControllerDidExitFullscreenNotification, но нашел MPAVControllerItemPlaybackDidEndNotification. MPAVControllerItemPlaybackDidEndNotification появляется, когда воспроизведение видео заканчивается.

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

4b9b3361

Ответ 1

Нет зарегистрированных документов, отправленных встроенным проигрывателем 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, вы можете сделать это следующим образом:

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

На основе ответа @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 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

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

Ответ 6

Это работает для меня в 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;
        }
    }
}

Ответ 7

[[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];

}

Ответ 8

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

Ответ 9

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

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

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