blob: 3fa6ad3ca97a6d21410e087d6fb653abe75f4988 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#import "WXVideoComponent.h"
#import "WXHandlerFactory.h"
#import "WXURLRewriteProtocol.h"
#import "WXSDKEngine.h"
#import "WXImgLoaderProtocol.h"
#import <AVFoundation/AVPlayer.h>
#import <AVKit/AVPlayerViewController.h>
#import <AVFoundation/AVPlayerItem.h>
@interface WXPlayer : NSObject
@end
@implementation WXPlayer
@end
@interface WXVideoView()
@property (nonatomic, strong) UIViewController *playerViewController;
@property (nonatomic, strong) AVPlayerItem *playerItem;
@property (nonatomic, strong) WXSDKInstance *weexSDKInstance;
@property (nonatomic, strong) UIImageView *posterImageView;
@property (nonatomic, strong) id<WXImageOperationProtocol> imageOperation;
@property (nonatomic, assign) BOOL playerDidPlayed;
@end
@implementation WXVideoView
- (id)init
{
if (self = [super init]) {
_playerViewController = [AVPlayerViewController new];
[self addSubview:_playerViewController.view];
}
return self;
}
- (void)dealloc
{
_weexSDKInstance = nil;
AVPlayerViewController *AVVC = (AVPlayerViewController*)_playerViewController;
[AVVC.player removeObserver:self forKeyPath:@"rate"];
[_playerItem removeObserver:self forKeyPath:@"status"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object: _playerItem];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *,id> *)change
context:(void *)context
{
if ([keyPath isEqualToString:@"rate"]) {
float rate = [change[NSKeyValueChangeNewKey] floatValue];
if (rate == 0.0) {
if (_playbackStateChanged)
_playbackStateChanged(WXPlaybackStatePaused);
} else if (rate == 1.0) {
if (_playbackStateChanged)
_playbackStateChanged(WXPlaybackStatePlaying);
} else if (rate == -1.0) {
// Reverse playback
}
} else if ([keyPath isEqualToString:@"status"]) {
NSInteger status = [change[NSKeyValueChangeNewKey] integerValue];
if (status == AVPlayerStatusFailed) {
if (_playbackStateChanged)
_playbackStateChanged(WXPlaybackStateFailed);
}
}
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
CGRect videoFrame = frame;
videoFrame.origin.x = 0;
videoFrame.origin.y = 0;
[_playerViewController.view setFrame:videoFrame];
}
- (void)setURL:(NSURL *)URL
{
NSString *newURL = [URL.absoluteString copy];
WX_REWRITE_URL(URL.absoluteString, WXResourceTypeVideo, self.weexSDKInstance)
if (!newURL) {
return;
}
NSURL *videoNewURL = [NSURL URLWithString:newURL];
AVPlayerViewController *AVVC = (AVPlayerViewController*)_playerViewController;
if (AVVC.player && _playerItem) {
[_playerItem removeObserver:self forKeyPath:@"status"];
[AVVC.player removeObserver:self forKeyPath:@"rate"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object: _playerItem];
}
_playerItem = [[AVPlayerItem alloc] initWithURL:videoNewURL];
AVPlayer *player = [AVPlayer playerWithPlayerItem: _playerItem];
AVVC.player = player;
[player addObserver:self
forKeyPath:@"rate"
options:NSKeyValueObservingOptionNew
context:NULL];
[_playerItem addObserver:self
forKeyPath:@"status"
options:NSKeyValueObservingOptionNew
context:NULL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinish) name:AVPlayerItemDidPlayToEndTimeNotification object: _playerItem];
}
- (void)setPosterURL:(NSURL *)posterURL
{
if (!posterURL) {
return;
}
[self cancelImage];
__weak typeof(self) weakSelf = self;
weakSelf.imageOperation = [[self imageLoader] downloadImageWithURL:posterURL.absoluteString imageFrame:self.posterImageView.frame
userInfo:@{@"instanceId":self.weexSDKInstance.instanceId}
completed:^(UIImage *image, NSError *error, BOOL finished)
{
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(self) strongSelf = weakSelf;
if (!error) {
strongSelf.posterImageView.image = image;
strongSelf.posterImageView.hidden = strongSelf.playerDidPlayed;
}
});
}];
}
- (void)setControlShow:(BOOL)showControl
{
AVPlayerViewController *AVVC = (AVPlayerViewController*)_playerViewController;
AVVC.showsPlaybackControls = showControl;
}
- (void)playFinish
{
if (_playbackStateChanged)
_playbackStateChanged(WXPlaybackStatePlayFinish);
AVPlayerViewController *AVVC = (AVPlayerViewController*)_playerViewController;
[[AVVC player] seekToTime:CMTimeMultiply([AVVC player].currentTime, 0)];
}
- (void)play
{
_posterImageView.hidden = YES;
AVPlayerViewController *AVVC = (AVPlayerViewController*)_playerViewController;
[[AVVC player] play];
}
- (void)pause
{
AVPlayerViewController *AVVC = (AVPlayerViewController*)_playerViewController;
[[AVVC player] pause];
}
- (void)posterTapHandler {
if (self.posterClickHandle) {
self.posterClickHandle();
}
}
- (UIImageView *)posterImageView {
if (!_posterImageView) {
_posterImageView = [[UIImageView alloc] initWithFrame:self.bounds];
_posterImageView.userInteractionEnabled = YES;
[_posterImageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(posterTapHandler)]];
_posterImageView.hidden = YES;
[self addSubview:_posterImageView];
[self bringSubviewToFront:_posterImageView];
}
return _posterImageView;
}
- (id<WXImgLoaderProtocol>)imageLoader
{
static id<WXImgLoaderProtocol> imageLoader;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
imageLoader = [WXHandlerFactory handlerForProtocol:@protocol(WXImgLoaderProtocol)];
});
return imageLoader;
}
- (void)cancelImage
{
[_imageOperation cancel];
_imageOperation = nil;
}
@end
@interface WXVideoComponent()
@property (nonatomic, weak) WXVideoView *videoView;
@property (nonatomic, strong) NSURL *videoURL;
@property (nonatomic, strong) NSURL *posterURL;
@property (nonatomic) BOOL autoPlay;
@property (nonatomic) BOOL playStatus;
@property (nonatomic) BOOL showControl;
@end
@implementation WXVideoComponent
- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance {
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
if (attributes[@"src"]) {
_videoURL = [NSURL URLWithString: attributes[@"src"]];
}
if (attributes[@"autoPlay"]) {
_autoPlay = [attributes[@"autoPlay"] boolValue];
}
if ([attributes[@"playStatus"] compare:@"play" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
_playStatus = true;
}
if ([attributes[@"playStatus"] compare:@"pause" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
_playStatus = false;
}
if (attributes[@"poster"]) {
_posterURL = [NSURL URLWithString: attributes[@"poster"]];
}
if (attributes[@"controls"]) {
_showControl = ![attributes[@"controls"] isEqualToString:@"nocontrols"];
}
}
return self;
}
- (UIView *)loadView
{
WXVideoView* videoView = [[WXVideoView alloc] init];
videoView.weexSDKInstance = self.weexInstance;
return videoView;
}
- (void)viewDidLoad
{
_videoView = (WXVideoView *)self.view;
_videoView.layer.mask = [self drawBorderRadiusMaskLayer:_videoView.bounds];
[_videoView setURL:_videoURL];
[_videoView setPosterURL:_posterURL];
[_videoView setControlShow:_showControl];
__weak __typeof__(self) weakSelf = self;
_videoView.posterClickHandle = ^{
[weakSelf.videoView play];
};
_videoView.playbackStateChanged = ^(WXPlaybackState state) {
NSString *eventType = nil;
switch (state) {
case WXPlaybackStatePlaying:
eventType = @"start";
break;
case WXPlaybackStatePaused:
eventType = @"pause";
break;
case WXPlaybackStatePlayFinish:
eventType = @"finish";
break;
case WXPlaybackStateFailed:
eventType = @"fail";
break;
default:
NSCAssert(NO, @"");
break;
}
[weakSelf fireEvent:eventType params:nil];
};
if (_playStatus) {
[_videoView play];
} else {
[_videoView pause];
}
if (_autoPlay) {
[_videoView play];
}
}
- (void)updateAttributes:(NSDictionary *)attributes
{
if (attributes[@"src"]) {
_videoURL = [NSURL URLWithString: attributes[@"src"]];
[_videoView setURL:_videoURL];
}
if (attributes[@"autoPlay"]) {
_autoPlay = [attributes[@"autoPlay"] boolValue];
[_videoView play];
}
if ([attributes[@"playStatus"] compare:@"play" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
_playStatus = true;
[_videoView play];
}
if ([attributes[@"playStatus"] compare:@"pause" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
_playStatus = false;
[_videoView pause];
}
if (attributes[@"poster"]) {
_posterURL = [NSURL URLWithString: attributes[@"poster"]];
[_videoView setPosterURL:_posterURL];
}
if (attributes[@"controls"]) {
_showControl = ![attributes[@"controls"] isEqualToString:@"nocontrols"];
[_videoView setControlShow:_showControl];
}
}
@end