web-dev-qa-db-ja.com

UIWebView埋め込みYouTubeビデオ再生からNSNotificationを受信する方法

MPMoviePlayerControllerに関する通知を受信しませんでした。私は何を間違えていますか?

次のロジックを使用します。

UIWebViewでYouTubeビデオを再生し始めています。 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];
}

PDATE: YouTubeのビデオを再生するアプリケーションを作成しています。プレイリストを実行すると、最初のビデオが表示されます。最初のビデオが終了すると、2番目のビデオが自動的に再生を開始します。

IOS 4.1以降をサポートする必要があります。

PDATE2: @ H2CO3 url-schemeを使用しようとしていますが、機能しません。デリゲートメソッドは、終了イベントで呼び出されませんでした。 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
}

PDATE @ Till、UIMoviePlayerControllerDidExitFullscreenNotificationをキャッチできませんでしたが、MPAVControllerItemPlaybackDidEndNotificationが見つかりました。ビデオの再生が終了すると、MPAVControllerItemPlaybackDidEndNotificationが表示されます。

しかし、onDone通知をどのようにキャッチすればよいのかわかりませんか?

51
Voloda2

UIWebView埋め込みムービープレーヤーによって送信される文書化された通知はありません。

実際、UIWebView内で使用されるクローズド実装は、多くの点でパブリックMPMoviePlayerControllerと異なります(例:DRM)。

そのUIWebView内でビデオコンテンツを再生するために使用される最も重要なクラスは、MPAVControllerおよびUIMoviePlayerControllerと呼ばれます。後者の場合、プレーヤーはMPMoviePlayerControllerフルスクリーンインターフェイスのように表示されます。

Appleによる拒否のリスクを敢えて負う場合、実際にまだ探しているものを達成する方法があります。

[〜#〜] note [〜#〜]これは文書化されておらず、すべての新しいiOSリリースで破損する可能性があります。ただし、iOS4.3、5.0および5.01、5.1および6.0で動作し、mayは他のバージョンでも動作します。

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レベルとObjective-Cレベルの両方のオプションをドラフトしました。これらすべてについて実際に調べる最良の方法は、回答の最後に示すようにCレベル(CoreFoundation)関数を使用することです。通知の送信者がObjective-C(NSNotifications)を使用していない場合、mayNSNotification-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);
64
Till

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

私の知る限り、UIWebView(およびAppleが作成したすべてのシステムクラス)の実装の詳細は、Cocoa Touchアプリケーションを作成する際には依存しません。たぶん、UIWebViewのビデオプレーヤーがnotの標準MPMoviePlayerControllerクラスであり、ユーザーがアクセスできないはずのまったく異なる委任/通知システムを持っている場合があります。

HTML5要素を使用して、このタグの「保留」イベントを検出することをお勧めします。

<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関数から、-webView:shouldStartLoadWithRequest:(UIWebViewDelegate)メソッドでキャッチできる偽のURLにリダイレクトして、ビデオが終了したことを通知できます。

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

お役に立てれば。

16
user529758

@ 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
 }
6
Fábio Oliveira

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

これは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;
        }
    }
}
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

実際には、リバースエンジニアリングの目的で、次のようなCocoa APIを使用することもできます。

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

この場合、すべての通知を受け取ります

1
Serg Dort

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

私の場合、私のWebビューはUITableViewCell内にあるため、セルとView Controllerの間で通信する方法を見つける必要があり、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];
1
Aviel Gross