网易云信IM自带的Demo中,视频播放功能针对iOS 13进行适
2019-10-09 本文已影响0人
zhangyin
在iOS13中,MPMoviePlayerController已经被 DEPRECATED,系统提示:
MP_DEPRECATED("Use AVPlayerViewController in AVKit.", ios(2.0, 9.0))
MP_PROHIBITED(tvos, watchos)
@interface MPMoviePlayerController : NSObject <MPMediaPlayback>
网易云信IM附带提供的Demo中,视频播放功能貌似还没有进行过iOS13的适配,我找了一圈没有找见,也懒得再找了,反正中国的IT大厂写文档和组织文档的能力都比较差,对初学者非常不友好,继续找下去也许白白浪费更多的时间而且还没有结果。所以自己简单适配了一下,至少不会crash,并且能正常播放视频就行。
适配的代码也比较简单,本着练习表达能力的目的随便写一些说明吧:
网易IM Demo中,负责实现播放视频的类为:NTESVideoViewController,适配工作主要针对这个类进行;
下面是适配完成后的源代码,共涉及到以下文件:
- NETSVideoViewController.h
- NETSVideoViewController.m
- PlayerView.h
- PlayerView.m
NETSVideoViewController.h
下面是NETSVideoViewController.h的代码,其中有个BaseViewController是ViewController的基类,和具体的项目有关,不用太在意它。
#import <UIKit/UIKit.h>
//#import <MediaPlayer/MediaPlayer.h>
#import "BaseViewController.h"
#import <AVFoundation/AVFoundation.h>
@interface NTESVideoViewItem : NSObject
@property (nonatomic,copy) NSString *itemId;//message Id
@property (nonatomic,copy) NSString *path;
@property (nonatomic,copy) NSString *url;
@property (nonatomic,strong) NIMSession *session;
@end
@interface NTESVideoViewController : BaseViewController
- (instancetype)initWithVideoViewItem:(NTESVideoViewItem *)item;
//MPMoviePlayerController不再需要了,换成AVPlayer
//@property (nonatomic, readonly) MPMoviePlayerController *moviePlayer;
@property (nonatomic, readonly) AVPlayer *moviePlayer;
@end
NETSVideoViewController.m
这个.m文件中内容有点乱,本来想使用AVPlayerItem来实现本地视频文件的播放,但是调来调去就是搞不定,最后只好换成 [AVPlayer playerWithURL:]的方式来播放,可以正常播放;
所以,这个.m文件中,有第一些和AVPlayerItem相关的代码是没有用处的,仅用于回头再研究。
#import "NTESVideoViewController.h"
#import "UIView+Toast.h"
#import "Reachability.h"
#import "UIAlertView+NTESBlock.h"
#import "SVProgressHUD.h"
#import "NTESNavigationHandler.h"
#import "NTESMediaPreviewViewController.h"
#import "UIImage+imageWithColor.h"
#import "PlayerView.h"
@interface NTESVideoViewController () {
BOOL _played;
}
@property (nonatomic,strong) NTESVideoViewItem *item;
@property (nonatomic ,strong) PlayerView *playerView;
@property (nonatomic ,strong) AVPlayerItem *playerItem;
@end
@implementation NTESVideoViewController
@synthesize moviePlayer = _moviePlayer;
- (instancetype)initWithVideoViewItem:(NTESVideoViewItem *)item
{
self = [super initWithNibName:nil bundle:nil];
if (self)
{
_item = item;
}
return self;
}
- (void)dealloc{
[self.moviePlayer pause];
[self.playerItem removeObserver:self forKeyPath:@"status" context:nil];
[self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
[SVProgressHUD dismiss];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NIMSDK sharedSDK].resourceManager cancelTask:_item.path];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.edgesForExtendedLayout = UIRectEdgeAll;
self.navigationItem.title = @"视频短片";
if (self.item.session)
{
[self setupRightNavItem];
}
self.playerView.player = self.moviePlayer;
// 添加视频播放结束通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
if ([[NSFileManager defaultManager] fileExistsAtPath:self.item.path]) {
[self startPlay];
}else{
__weak typeof(self) wself = self;
[self downLoadVideo:^(NSError *error) {
if (!error) {
[wself startPlay];
}else{
[wself.view makeToast:@"下载失败,请检查网络"
duration:2
position:CSToastPositionCenter];
}
}];
}
//add by zhy
[self configNavigationBar];
}
- (PlayerView *)playerView {
if(!_playerView) {
_playerView = [[PlayerView alloc] initWithFrame:WIN_FRAME];
_playerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:_playerView];
}
return _playerView;
}
- (AVPlayerItem *)playerItem {
if(!_playerItem) {
////self.item.path http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4
NSURL *videoUrl = [NSURL URLWithString:self.item.path];
AVURLAsset *avset = [AVURLAsset assetWithURL:videoUrl];
_playerItem = [AVPlayerItem playerItemWithAsset:avset];
[_playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 监听status属性
[_playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];// 监听loadedTimeRanges属性
}
return _playerItem;
}
- (AVPlayer *)moviePlayer {
if(!_moviePlayer) {
_moviePlayer = [AVPlayer playerWithURL:[NSURL fileURLWithPath:self.item.path]];
// _moviePlayer = [AVPlayer playerWithPlayerItem:self.playerItem];
}
return _moviePlayer;
}
- (void)configNavigationBar {
[self removeSimulatorNavigationBarBgView];
[self translucentNavigationBar:YES];
[self setNavigationBarBgImg:nil];
[self setBackBarButton];
[self.navigationController.navigationBar setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys:COLOR_THEME, NSForegroundColorAttributeName,[UIFont boldSystemFontOfSize:16],NSFontAttributeName, nil]];
}
- (void)setupRightNavItem
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(onMore:) forControlEvents:UIControlEventTouchUpInside];
[button setImage:[UIImage imageNamed:@"icon_gallery_more_normal"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:@"icon_gallery_more_pressed"] forState:UIControlStateHighlighted];
[button sizeToFit];
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.rightBarButtonItem = buttonItem;
}
- (void)onMore:(id)sender
{
NIMMessageSearchOption *option = [[NIMMessageSearchOption alloc] init];
option.limit = 0;
option.messageTypes = @[@(NIMMessageTypeImage),@(NIMMessageTypeVideo)];
__weak typeof(self) weakSelf = self;
[[NIMSDK sharedSDK].conversationManager searchMessages:self.item.session option:option result:^(NSError * _Nullable error, NSArray<NIMMessage *> * _Nullable messages) {
if (weakSelf)
{
NSMutableArray *objects = [[NSMutableArray alloc] init];
NTESMediaPreviewObject *focusObject;
//显示的时候新的在前老的在后,逆序排列
//如果需要微信的显示顺序,则直接将这段代码去掉即可
NSArray *array = messages.reverseObjectEnumerator.allObjects;
for (NIMMessage *message in array)
{
switch (message.messageType) {
case NIMMessageTypeVideo:{
NTESMediaPreviewObject *object = [weakSelf previewObjectByVideo:message.messageObject];
[objects addObject:object];
if ([message.messageId isEqualToString:weakSelf.item.itemId])
{
focusObject = object;
}
break;
}
case NIMMessageTypeImage:{
NTESMediaPreviewObject *object = [weakSelf previewObjectByImage:message.messageObject];
[objects addObject:object];
break;
}
default:
break;
}
}
NTESMediaPreviewViewController *vc = [[NTESMediaPreviewViewController alloc] initWithPriviewObjects:objects focusObject:focusObject];
[weakSelf.navigationController pushViewController:vc animated:YES];
}
}];
}
- (NTESMediaPreviewObject *)previewObjectByVideo:(NIMVideoObject *)object
{
NTESMediaPreviewObject *previewObject = [[NTESMediaPreviewObject alloc] init];
previewObject.objectId = object.message.messageId;
previewObject.thumbPath = object.coverPath;
previewObject.thumbUrl = object.coverUrl;
previewObject.path = object.path;
previewObject.url = object.url;
previewObject.type = NTESMediaPreviewTypeVideo;
previewObject.timestamp = object.message.timestamp;
previewObject.displayName = object.displayName;
previewObject.duration = object.duration;
previewObject.imageSize = object.coverSize;
return previewObject;
}
- (NTESMediaPreviewObject *)previewObjectByImage:(NIMImageObject *)object
{
NTESMediaPreviewObject *previewObject = [[NTESMediaPreviewObject alloc] init];
previewObject.objectId = object.message.messageId;
previewObject.thumbPath = object.thumbPath;
previewObject.thumbUrl = object.thumbUrl;
previewObject.path = object.path;
previewObject.url = object.url;
previewObject.type = NTESMediaPreviewTypeImage;
previewObject.timestamp = object.message.timestamp;
previewObject.displayName = object.displayName;
previewObject.imageSize = object.size;
return previewObject;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear: animated];
if (![[self.navigationController viewControllers] containsObject: self])
{
[self topStatusUIHidden:NO];
}
}
- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self.moviePlayer pause];
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear: animated];
if(self.moviePlayer.status == AVPlayerStatusReadyToPlay) {
[self topStatusUIHidden:YES];
}else{
[self topStatusUIHidden:NO];
}
}
- (void)downLoadVideo:(void(^)(NSError *error))handler{
[SVProgressHUD show];
__weak typeof(self) wself = self;
[[NIMSDK sharedSDK].resourceManager download:self.item.url filepath:self.item.path progress:^(float progress) {
if (wself)
{
[SVProgressHUD showProgress:progress];
}
} completion:^(NSError *error) {
if (wself) {
[SVProgressHUD dismiss];
if (handler) {
handler(error);
}
}
}];
}
- (void)startPlay{
[self.playerView.player play];
CGRect bounds = self.view.bounds;
CGRect tapViewFrame = CGRectMake(0, 0, bounds.size.width, bounds.size.height);
UIView *tapView = [[UIView alloc]initWithFrame:tapViewFrame];
[tapView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
tapView.backgroundColor = [UIColor clearColor];
[self.view addSubview:tapView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(onTap:)];
[tapView addGestureRecognizer:tap];
}
- (void)topStatusUIHidden:(BOOL)isHidden
{
[[UIApplication sharedApplication] setStatusBarHidden:isHidden];
self.navigationController.navigationBar.hidden = isHidden;
NTESNavigationHandler *handler = (NTESNavigationHandler *)self.navigationController.delegate;
handler.recognizer.enabled = !isHidden;
if(isHidden) {
[self setNavigationBarBgImg:nil];
} else {
[self setNavigationBarBgImg:[UIImage imageWithColor:[UIColor whiteColor]]];
}
}
- (void)onTap: (UIGestureRecognizer *)recognizer {
if (!_played) {
[self.moviePlayer play];
[self topStatusUIHidden:YES];
} else {
[self.moviePlayer pause];
[self topStatusUIHidden:NO];
}
_played = !_played;
}
#pragma mark - kvo
// KVO方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
AVPlayerItem *playerItem = (AVPlayerItem *)object;
if ([keyPath isEqualToString:@"status"]) {
if ([playerItem status] == AVPlayerStatusReadyToPlay) {
NSLog(@"AVPlayerStatusReadyToPlay");
[self topStatusUIHidden:YES];
} else if ([playerItem status] == AVPlayerStatusFailed) {
NSLog(@"AVPlayerStatusFailed");
[self topStatusUIHidden:NO];
}
} else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
}
}
- (void)moviePlayDidEnd:(NSNotification *)notification {
NSLog(@"Play end");
[self topStatusUIHidden:NO];
}
@end
@implementation NTESVideoViewItem
@end
PlayerView.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface PlayerView : UIView
@property (nonatomic ,strong) AVPlayer *player;
@end
PlayerView.m
#import "PlayerView.h"
@implementation PlayerView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer *)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *)[self layer] setPlayer:player];
}
@end