封装WKWebView播放YouTuBe视频的播放器

作者: 皮乐皮儿 | 来源:发表于2017-05-15 12:54 被阅读294次

    ps:上个项目做过有关YouTube视频播放的功能,之前在GitHub上搜的是使用UIWebView加载html的方式播放,由于UIWebView自身的缺陷问题,对内存消耗是个大问题,iOS8之后推出的WebKit框架中的WKWebView极大程度上解决了这个难题,于是乎,笔者就想到用WKWebView代替UIWebView去封装一个YouTube视频播放控件,花了一些时间搞了搞。
    YouTube视频播放前提是要能翻墙

    定制播放控件

    1.YouTube采用的是网页加载html文件完成功能,所以本地搞个Html文件,在html中做的主要是调用Youtube的iframe api,初始化播放器,并且规定和客户端交互的scheme,简单的代码如下:
     var tag = document.createElement('script');
                tag.src = "https://www.youtube.com/iframe_api";
                var firstScriptTag = document.getElementsByTagName('script')[0];
                firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
                //这个函数创建一个< iframe >(和YouTube播放器)zPUdFID3lmo   M7lc1UVf-VE
                var player;
                var errorCode = -1;//用来记录错误码
                function onYouTubeIframeAPIReady() {
                    player = new YT.Player('player',%@);
                    player.setSize(window.innerWidth, window.innerHeight);
                }
    

    html中的一些播放控制事件定义:

     function onPlayerReady(event) {
                    event.target.playVideo();
                    console.log('开始');
                    window.location.href = 'wabridge://onPlayerReady?data=' + event.data;
                }
                //当播放器的状态改变时,这个函数调用这个函数
                var done = false;
                function onPlayerStateChange(event) {
                    window.location.href = 'wabridge://onPlayerStateChange?data=' + event.data + "&errorCode=" + errorCode;
                }
                //此事件在播放器出错时触发。
                function onPlayerError(event) {
                    console.log('播放失败');
                        errorCode = event.data;
                    //window.location.href = 'wabridge://onPlayerError?data=' + event.data;
                }
    

    另外在OC端代码中,webviewJavascriptBridge代码中作如下处理:

    #define urlScheme @"wabridge"
    
    - (void)webView:(WKWebView *)webView
    decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
    decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
        if (webView != _webView) { return; }
        NSURL *url = navigationAction.request.URL;
        __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
        if ([_base isCorrectProcotocolScheme:url]) {
            if ([_base isBridgeLoadedURL:url]) {
                [_base injectJavascriptFile];
            } else if ([_base isQueueMessageURL:url]) {
                [self WKFlushMessageQueue];
            }else {
                [_base logUnkownMessage:url];
            }
            decisionHandler(WKNavigationActionPolicyCancel);
        }
        
        else if ([[url scheme] isEqualToString:urlScheme]) {
            [self handleQuery:url];
            decisionHandler(WKNavigationActionPolicyCancel);
        }
        else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
            [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
        } else {
            decisionHandler(WKNavigationActionPolicyAllow);
        }
    }
    

    这里的urlScheme就是根据html中的waBridge来定义的

    2. 封装播放控制器

    我这里主要是实现了几个简单的常用功能:开始播放,暂停,停止播放,跳转到指定时长进行播放等,下面就贴出代码,对各个功能进行实现:

    #######2.1开始播放视频

    /**
     play video with videoId
     **/
    - (BOOL)playVideoWithVideoId:(NSString *)videoId {
    
        NSDictionary *dic = @{@"autoplay":@1,@"controls":@2,@"playsinline":@1,@"origin":YouTubeOrigin};
        return  [self playVideoWithVideoId:videoId playerVars:dic];
    }
    
    /**
     play video with videoId and playerVars
     **/
    - (BOOL)playVideoWithVideoId:(NSString *)videoId playerVars:(NSDictionary *)playerVars {
        if (!playerVars) {
            playerVars = @{};
        }
        NSDictionary *playerParams = @{ @"videoId" : videoId, @"playerVars" : playerVars };
        return [self playVideoWithParams:playerParams];
    }
    
    /*
     play video with playerVars
     **/
    - (BOOL)playVideoWithParams:(NSDictionary *)additionalPlayerParams {
        NSDictionary *playerCallbacks = @{
                                          @"onReady" : @"onPlayerReady",
                                          @"onStateChange" : @"onPlayerStateChange",
                                          @"onError" : @"onPlayerError"
                                          };
        NSMutableDictionary *playerParams = [[NSMutableDictionary alloc] init];
        if (additionalPlayerParams) {
            [playerParams addEntriesFromDictionary:additionalPlayerParams];
        }
        if (![playerParams objectForKey:@"height"]) {
            [playerParams setValue:@"100%" forKey:@"height"];
        }
        if (![playerParams objectForKey:@"width"]) {
            [playerParams setValue:@"100%" forKey:@"width"];
        }
        
        [playerParams setValue:playerCallbacks forKey:@"events"];
        
        if ([playerParams objectForKey:@"playerVars"]) {
            NSMutableDictionary *playerVars = [[NSMutableDictionary alloc] init];
            [playerVars addEntriesFromDictionary:[playerParams objectForKey:@"playerVars"]];
            
            if (![playerVars objectForKey:@"origin"]) {
                self.originalURL = [NSURL URLWithString:YouTubeOrigin];
            } else {
                self.originalURL = [NSURL URLWithString: [playerVars objectForKey:@"origin"]];
            }
        } else {
            // This must not be empty so we can render a '{}' in the output JSON
            [playerParams setValue:[[NSDictionary alloc] init] forKey:@"playerVars"];
        }
        NSString *html = [[NSBundle mainBundle] pathForResource:@"youtube" ofType:@"html"];
        NSString *htmlStr = [NSString stringWithContentsOfFile:html encoding:NSUTF8StringEncoding error:nil];
        NSError *jsonRenderingError = nil;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:playerParams
                                                           options:NSJSONWritingPrettyPrinted
                                                             error:&jsonRenderingError];
        NSString *playerVarsJsonString =
        [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
        
        NSString *embedHTML = [NSString stringWithFormat:htmlStr, playerVarsJsonString];
            self.webView.userInteractionEnabled = YES;
            [self.webView loadHTMLString:embedHTML baseURL: self.originalURL];
        
        return YES;
    
    }
    

    ########2.2暂停播放

    /**
     pause play video
     **/
    - (void)pauseVideo {
    
        if (self.webView.isLoading) {
            [self.webView stopLoading];
        }
        [self.webView evaluateJavaScript:@"player.pauseVideo();" completionHandler:nil];
        
    }
    

    ########2.3停止播放

    /**
     stop play video
     **/
    - (void)stopVideo {
    
        if (self.webView.isLoading) {
            [self.webView stopLoading];
        }
        [self.webView evaluateJavaScript:@"player.stopVideo();" completionHandler:nil];
        [self closeInterfacePlayer];
    }
    
    

    ########2.4跳转到指定时间播放

    /**
     play video from seconds
     **/
    - (void)seekToSecondsPlayVideo:(float)seconds allowSeekAhead:(BOOL)allowSeekAhead {
    
        NSString *command = [NSString stringWithFormat:@"player.seekTo(%@,%@);",[NSNumber numberWithFloat:seconds],allowSeekAhead ? @"true":@"false"];
    
        [self.webView evaluateJavaScript:command completionHandler:nil];
    }
    

    ########2.5暂停之后再点击开始播放

    /**
     start play video
     **/
    - (void)startVideo {
    
        [self openInterfacePlayer];
        [self.webView evaluateJavaScript:@"player.playVideo();" completionHandler:nil];
    }
    

    ########2.6获取当前播放时长

    /*
     get video play time
     **/
    - (void)currentTime:(void (^)(float, NSError *))complentionHandler {
    
        [self.webView evaluateJavaScript:@"player.getCurrentTime();" completionHandler:^(id returnValue, NSError * _Nullable error) {
            float currentTime = [returnValue floatValue];
            complentionHandler(currentTime,error);
        }];
    }
    
    

    在外界只需要导入YouTuBePlayerView头文件,直接调用其中的方法即可:

    最后附上一张效果图

    Simulator Screen Shot 2017年5月15日 12.51.56.png

    在GitHub上有相关的源码,欢迎有需要的同胞前去下载,有问题请指出来,大家共同学习进步,谢谢,最后附上源码下载链接:

    YouTube视频播放源码

    相关文章

      网友评论

      • isletn:你好, 请问你 Demo 中视频右上角两个按钮的功能问题有解决吗?历史按钮不能点, 分享按钮点了也没有功能实现。
        isletn:@顾语流年 那两个按钮是 Youtube iframe 自带的,使用 WKWebView 会影响这两个按钮的功能( UIWebView 是正常的),很蛋疼。。
        皮乐皮儿:那个功能没做,只简单播放,我demo中没有写那两个按钮吧,是播放器加载后就有的,这个估计要深入结合HTML中弄了

      本文标题:封装WKWebView播放YouTuBe视频的播放器

      本文链接:https://www.haomeiwen.com/subject/hsqhxxtx.html