web-dev-qa-db-fra.com

Comment recevoir NSNotifications de la lecture de vidéo YouTube intégrée UIWebView

Je n'ai reçu aucune notification pour MPMoviePlayerController. Qu'est-ce que je fais mal?

J'utilise la logique suivante.

Je commence à lire une vidéo YouTube dans UIWebView. UIWebView appelle un MPMoviePlayerController standard. Je ne contrôle pas MPMoviePlayerController car je n'ai pas instancié MPMoviePlayerController.

Je lance le clip de YouTube avec la lecture automatique (délai d'une seconde):

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

Mon code est:

- (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];
}

MISE À JOUR: Je crée une application qui lit la vidéo de YouTube. Vous pouvez exécuter la playlist et vous verrez la première vidéo. Une fois la première vidéo terminée, la deuxième vidéo démarre automatiquement, etc.

Je dois prendre en charge ios 4.1 et supérieur.

PDATE2: @ H2CO3 J'essaie d'utiliser votre schéma d'URL, mais cela ne fonctionne pas. La méthode déléguée n'a pas appelé d'événement de sortie. J'ai ajouté mon URL html pour me connecter. C'est:

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

PDATE @Till, je ne peux pas attraper UIMoviePlayerControllerDidExitFullscreenNotification, mais j'ai trouvé MPAVControllerItemPlaybackDidEndNotification. MPAVControllerItemPlaybackDidEndNotification apparaît lorsque la lecture de la vidéo est terminée.

Mais je ne comprends pas comment capturer les notifications OnDone?

51
Voloda2

Il n'y a aucune notification documentée envoyée par le lecteur de film intégré UIWebView.

En fait, l'implémentation fermée utilisée dans le UIWebView diffère du public MPMoviePlayerController à bien des égards (par exemple DRM).

Les classes les plus importantes utilisées pour lire le contenu vidéo dans ce UIWebView sont appelées MPAVController et UIMoviePlayerController. Cette dernière fait apparaître le lecteur comme l'interface plein écran MPMoviePlayerController.

Au cas où vous oseriez risquer un rejet d'Apple, il existe en fait des moyens de réaliser ce que vous recherchez.

NOTE Ceci n'est pas documenté et est sujet à rupture sur chaque nouvelle version iOS. Il fonctionne cependant sur iOS4.3, 5.0 et 5.01, 5.1 et 6.0 et il peut fonctionner également sur d'autres versions.

Je ne suis pas en mesure de tester cette solution sur iOS 4.1 et 4.2, c'est donc à vous de le faire. Je soupçonne fortement que cela fonctionnera.


État plein écran

Si, par exemple, vous avez l'intention de réagir lorsque l'utilisateur appuie sur le bouton DONE , vous pouvez le faire de cette façon:

UPDATE L'ancienne version de cette réponse recommandait d'utiliser UIMoviePlayerControllerDidExitFullscreenNotification alors que cette nouvelle version (mise à jour pour iOS6) recommande d'utiliser UIMoviePlayerControllerWillExitFullscreenNotification.

Niveau C-Language:

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

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

Niveau Objectif-C:

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

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

J'ai rédigé les deux options C-Level et Objective-C-Level parce que la meilleure façon de découvrir tout cela est d'utiliser les fonctions C-Level (CoreFoundation) comme indiqué à la fin de ma réponse. Si l'expéditeur d'une notification n'utilise pas Objective-C (NSNotifications), vous pouvez ne pas être en mesure de les piéger à l'aide de NSNotification-mechanics.


État de lecture

Pour examiner l'état de lecture, recherchez "MPAVControllerPlaybackStateChangedNotification" (tel que rédigé ci-dessus) et examinez le userInfo qui peut ressembler à ceci:

{
    MPAVControllerNewStateParameter = 1;
    MPAVControllerOldStateParameter = 2;
}

Ingénierie inverse supplémentaire

Pour l'ingénierie inverse et l'exploration de toutes les notifications envoyées, utilisez l'extrait de code suivant.

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);
64
Till

Dans iOS 4.3+, vous pouvez utiliser les notifications UIMoviePlayerControllerDidEnterFullscreenNotification et 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
}
32
ChrisJP

Pour autant que je sache, les détails d'implémentation d'UIWebView (et de toutes les classes système créées par Apple) ne doivent pas être pris en compte lors de la création d'une application Cocoa Touch. Peut-être que c'est le cas qu'un lecteur vidéo UIWebView est pas une classe MPMoviePlayerController standard et qu'il pourrait avoir un système de délégation/notification totalement différent, qui n'est pas censé être accessible par l'utilisateur.

Je vous suggère d'utiliser l'élément HTML5 et de détecter l'événement "onended" de cette balise:

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

En fait, à partir de la fonction JavaScript endMovie, vous pouvez rediriger vers une URL bidon que vous pouvez attraper dans votre méthode -webView: shouldStartLoadWithRequest: (UIWebViewDelegate) ainsi être averti que la vidéo est terminée:

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

J'espère que cela t'aides.

16
user529758

Basé sur la réponse @ H2CO3 mais avec iframe API . C'était la seule façon de le faire fonctionner.

Cela n'utilise aucune API privée, ce qui la rend plus pérenne.

Voici le code pour intégrer votre vidéo Youtube. Vérifiez l'API pour plus de façons de personnaliser cela.

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

Et c'est ainsi que vous êtes averti de la fin de la vidéo (méthode 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
Fábio Oliveira

dans ViewDidLoad ajoutez le code suivant

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

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

Les méthodes suivantes permettent d'afficher le message/les fonctions du processus respectif d'entrée/sortie vers/depuis le plein écran

- (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
}
5
Prabhu Natarajan

Cela fonctionne pour moi dans iOS 6.1, il masque/supprime les autres fenêtres lorsque AVPlayerItemDidPlayToEndTimeNotification est reçu:

[[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;
        }
    }
}
4
sandeepmistry
[[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];

}
3
Ritesh Arora

En fait, à des fins d'ingénierie inverse, vous pouvez également utiliser l'API Cocoa comme

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

Dans ce cas, vous recevrez toutes les notifications

1
Serg Dort

Pour iOS8 (J'ai également une vidéo intégrée qui n'est pas une vidéo youtube) la seule solution que j'ai pu mettre au travail était d'attraper l'un des viewWill/DidLayoutSubviews, et en prime, vous n'avez pas besoin de modifier le code HTML ou d'utiliser des API privées:

Donc en gros:

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

Dans mon cas, ma vue Web est à l'intérieur d'un UITableViewCell, j'ai donc dû trouver un moyen de communiquer entre la cellule et le contrôleur de vue, et pour éviter également d'utiliser un indicateur BOOL, j'ai fait ceci:

- (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];
1
Aviel Gross