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上有相关的源码,欢迎有需要的同胞前去下载,有问题请指出来,大家共同学习进步,谢谢,最后附上源码下载链接:
网友评论