IOS development – AVPlayer network package, local video

IOS development - AVPlayer network package, local video

: when it comes to video players, I believe we can think of the basic AVPlayer, the use of AVPlayer simple lines of code can be achieved on the local and network video playback. If you want to achieve a slightly more complex functions, such as increasing the progress bar, full screen button, if these are written in the ViewController it will make ViewController seem more jumbled code. Based on this, a small package in use of AVPlayer, realized the time display, playback progress continued broadcasting, buffer progress bar, progress bar drag forward and backward, a plurality of video sequence playback, full screen playback function.

Principle: access to AVPlayerItem loadedTimeRanges and status two properties of the implementation schedule and monitor buffer playback state; to create model save video information is stored in the array to achieve the order of play; the player title and package to reduce the amount of code toolbar customization in view, and use the value of the callback.

First look at the renderings

IOS development - AVPlayer network package, local video

we start the following package:
first, create a video storage information model (we can modify according to their needs) as follows:

#import < Foundation/Foundation.h> typedef; NS_ENUM (NSInteger, RHVideoPlayStyle) {RHVideoPlayStyleLocal = 0 / / RHVideoPlayStyleNetwork / / play local video, online video playback RHVideoPlayStyleNetworkSD, SD video broadcast network / / RHVideoPlayStyleNetworkHD / / HD video broadcast network: NSObject RHVideoModel @interface}; @property (nonatomic, copy, readonly) NSString * @property (nonatomic, videoId; copy, readonly) NSString * title; @property (nonatomic, strong, readonly) NSURL * URL; @property (nonatomic, assign) RHVideoPlayStyle style @property (nonatomic, assign); NSTimeInterval currentTime; @param videoId / * * create local video model ID @param Title @param VI video title DeoPath @param currentTime is currently playing the file path @return local video playback time model - (instancetype) initWithVideoId: * (NSString *) videoId title: (NSString * title) videoPath: (NSString *) videoPath currentTime: (NSTimeInterval) currentTime; / * * create network video model @param videoId ID @param Title @param URL video title @param currentTime video address current playback time of @return network video model - (instancetype) initWithVideoId: * (NSString *) videoId title: (NSString * title) url: (NSString *) URL currentTime: (NSTimeInterval) currentTime; / * * create network video model @param videoId ID @param Title @param sdUrl video title @param hdUrl HD SD. @param currentTime address current playback time of @return network video model - (instancetype) initWithVideoId: * (NSString *) videoId title: (NSString * title) sdUrl: (NSString * sdUrl) hdUrl: (NSString *) hdUrl currentTime: (NSTimeInterval) currentTime; @end
#import "RHVideoModel.h" @interface (RHVideoModel) @property (nonatomic, copy) NSString * sdUrl; @property (nonatomic, copy) NSString * hdUrl; @end @implementation RHVideoModel (instancetype) initWithVideoId: (NSString * videoId) title: (NSString * title) videoPath: (NSString *) videoPath currentTime: (NSTimeInterval currentTime) {self = [super init]; if (self) {_videoId = [videoId = copy]; _title [title copy]; _currentTime = currentTime; _url = [[NSURL fileURLWithPath:videoPath] copy]; _style = RHVideoPlayStyleLocal;} return self;} - (instancetype) initWithVideoId: (NSString * videoId) title: (NSString * title) url: (NSString *) URL currentTime: (NSTimeInterval) {self = currentTime [super init]; if (self) {_videoId = [videoId = copy]; _title [title copy]; _currentTime = currentTime; _url = [[NSURL URLWithString:url] copy]; _style = RHVideoPlayStyleNetwork;} return self;} - (instancetype) initWithVideoId: (NSString * videoId) title: (NSString * title) sdUrl: (NSString * sdUrl) hdUrl: (NSString *) hdUrl currentTime: (NSTimeInterval currentTime) {self = [super init]; if (self) {_videoId = [videoId = copy]; _title [title copy]; _currentTime = currentTime; _sdUrl = [sdUrl copy]; _hdUrl [hdUrl = copy]; = RHVideoPlayStyleNetworkHD;} return self;} - (void) setStyle: (RHVideoPlayStyle style) {_ Style = style; if (_style = = RHVideoPlayStyleNetworkSD) {_url = [[NSURL URLWithString:_sdUrl] copy]; NSLog (@ "% @", _sdUrl);} else if (_style = = RHVideoPlayStyleNetworkHD) {_url = [[NSURL URLWithString:_hdUrl] copy]; NSLog (@ "% @", _hdUrl);}} @end

All of the methods of this model have been commented, no longer do this in detail.

Then give you a full screen, click on the screen when I was in, from the current ViewController pop up a new ViewController and will play view from ViewController before it was removed and added to the new top ViewController, change the view frame, the new ViewController is a cross screen state can achieve full screen effect. Take a look at the full screen ViewController only need to create a class to be inherited in UIViewController, in the.M rewrite the two methods are as follows:

#import "RHFullViewController.h" @interface RHFullViewController (@end @implementation RHFullViewController) - (UIInterfaceOrientationMask) supportedInterfaceOrientations {return UIInterfaceOrientationMaskLandscape;} - (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation return YES {@end};

Because the AVPlayer display effect is in AVPlayerLayer above, so write a RHPlayerLayerView to add AVPlayerLayer and AVPlayerLayer to frame with RHPlayerLayerView frame change to change, so you only need the RHPlayerLayerView frame to modify. As follows:

#import < UIKit/UIKit.h> #import < AVFoundation/AVFoundation.h> @interface RHPlayerLayerView; UIView (void) addPlayerLayer: (AVPlayerLayer *) playerLayer; @end
#import "RHPlayerLayerView.h" @interface (RHPlayerLayerView) @property (nonatomic, strong) AVPlayerLayer * playerLayer; @end @implementation RHPlayerLayerView (void) addPlayerLayer: (AVPlayerLayer * playerLayer) {_playerLayer = playerLayer; playerLayer.backgroundColor = [UIColor blackColor].CGColor; _playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; _playerLayer.contentsScale = [UIScreen mainScreen].scale; [self.layer addSublayer:_playerLayer];} - (void) layoutSublayersOfLayer: (CALayer * layer) [super {layoutSublayersOfLayer:layer]; _playerLayer.frame = self.bounds;} @end

For the player on the top of the title and control bar and the failure to display the page in the package is not much to say here, the main use of the proxy callback to pass the value of the player control.

Next, we focus on the AVPlayer package:
first created RHPlayerView inherited from UIView, the definition of the method in RHPlayerView.h is as follows:

#import < UIKit/UIKit.h> #import; < AVFoundation/AVFoundation.h> #import "RHVideoModel.h" @protocol RHPlayerViewDelegate; @interface RHPlayerView: UIView @property (nonatomic, weak) id< RHPlayerViewDelegate> delegate; / * * object method create the object @param frame @param controller the @return controller constraint object * / - (instancetype) initWithFrame: (CGRect) frame currentVC: (UIViewController * controller) @param videoModels model; video store video set to play video / * * list and to play the current videoId array @param to play video / ID - (void) setVideoModels: (NSArray< RHVideoModel *> videoModels playVideoId: (* *) NSString videoId); / * * set cover pictures @param imageUr L cover picture URL * / - (void) setCoverImage: (NSString * imageUrl); / * * click on the directory to play video or video on videoId @param ID ID * / - (void) playVideoWithVideoId: (NSString * videoId); / * * * / - suspended (void) pause; / * * * / stop - (void) stop; @end @protocol RHPlayerViewDelegate < NSObject> / / if you can play - (BOOL) playerViewShouldPlay; / / @optional - (void) playerView: playback end (RHPlayerView *) playView didPlayEndVideo: (RHVideoModel *) videoModel index: index (NSInteger); / / to play - (void) playerView: (RHPlayerView * playView) didPlayVideo: (RHVideoModel *) videoModel index: (NSInteger) index; / / broadcast - (void) (RHPlayerView * playerView:) playView didPlayVideo: (RHVideoModel * videoModel playTime: (NSTimeInte) Rval) playTime; @end

All the way to add a note, I believe we can be clear at a glance, this small addition agent to the view, so you can control the player and get the play progress and play the video information in real time in ViewController.

Next we see in RHPlayerView.m, due to the addition of more functions, so here’s some more code, hope you can be patient, including titleView, toolView, failedView are the control bar at the top player customized title bar, and failed to play below the display view, you can temporarily ignore this of these, the specific code is as follows:

#import "RHPlayerView.h" #import "RHFullViewController.h" #import "RHPlayerTitleView.h" #import "RHPlayerToolView.h" #import "RHPlayerFailedView.h" #import "RHPlayerLayerView.h" @interface (RHPlayerView) < RHPlayerToolViewDelegate, RHPlayerTitleViewDelegate, RHPlayerFailedViewDelegate> @property (nonatomic, strong) AVPlayer * player; @property (nonatomic, strong) AVPlayerItem * playerItem; @property (nonatomic, strong) AVPlayerLayer * playerLayer; @property (nonatomic, strong) RHFullViewController * fullVC; @property (nonatomic, weak) UIViewController * currentVC; @property (nonatomic, strong) RHPlayerTitleView * titleView; @property (nonatomic, strong) RHPlayerToolView * toolView; @property (nonatomic, strong) RHPlayerFailedView * failedView; @property (nonatomic, strong) RHPlayerLayerView * layerView; @property (nonatomic, strong) UIActivityIndicatorView * activity; @property (nonatomic, strong) UIImageView * coverImageView; @property (nonatomic, strong) CADisplayLink * link (nonatomic, assign); @property NSTimeInterval lastTime; @property (nonatomic, strong) @property (NSTimer * toolViewShowTimer; nonatomic assign, NSTimeInterval toolViewShowTime); / / if @property displays the current control (nonatomic, assign) BOOL isShowToolView; / / if the first player @property (nonatomic, assign) BOOL isFirstPlay @property; / / if a replay (nonatomic, assign) BOOL isReplay; @property (nonatomic, strong) NSArray * videoArr; @property (nonatomic, strong) RHVideoModel * vid EoModel; @property (nonatomic) CGRect playerFrame @end @implementation RHPlayerView #pragma; Mark - public / / initialization method - (instancetype) initWithFrame: (CGRect) frame currentVC: (UIViewController * controller) {self = [super initWithFrame:frame]; if (self) {self.clipsToBounds = YES; self.backgroundColor = [UIColor blackColor]; self.currentVC = controller; _isShowToolView = YES; _isFirstPlay = YES; _isReplay = NO; _playerFrame = frame; [self addSubviews]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (videoPlayEnd) name:AVPlayerItemDidPlayToEndTimeNotification object:nil] return self;}}; / / set the cover picture (V OID) setCoverImage: (NSString *) imageUrl {_coverImageView.hidden = NO; [_coverImageView sd_setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage "imageNamed:@";} / / set "to play the video list and to play video - (void) setVideoModels: (NSArray< RHVideoModel *> videoModels playVideoId: (* *) NSString videoId) {self.videoArr = [NSArray (videoId.length arrayWithArray:videoModels]; if > 0) {for (RHVideoModel * model in self.videoArr) {if ([model.videoId isEqualToString:videoId]) {NSInteger index [self.videoArr = indexOfObject:model]; self.videoModel = self.videoArr[index]; break;}} {else} Self.videoModel = self.videoArr.firstObject;} _titleView.title = self.videoModel.title; _isFirstPlay = YES;} / / click the directory to play video ID - (void) playVideoWithVideoId: (NSString * videoId) {if ([self.delegate! RespondsToSelector:@ selector (playerViewShouldPlay)] {return}); [self.delegate playerViewShouldPlay]; for (RHVideoModel * model in self.videoArr if ([model.videoId isEqualToString:videoId]) {index = [self.videoArr NSInteger) {indexOfObject:model]; self.videoModel = self.videoArr[index]; _titleView.title = break;}} self.videoModel.title; if (_isFirstPlay) {_coverImageView.hidden = YES; [self se TPlayer]; [self addToolViewTimer]; _isFirstPlay = NO;} else {[self.player pause]; [self replaceCurrentPlayerItemWithVideoModel:self.videoModel]; [self addToolViewTimer];}} / / pause (void) pause {[self.player pause]; = YES; _toolView.playSwitch.selected = NO; [self removeToolViewTimer];} / / stop - (void) stop pause] [ {[self.player; invalidate]; _toolView.playSwitch.selected = NO; [self removeToolViewTimer] #pragma mark add subviews;} - and make constraints - (void) addSubviews layerView [self addSubview:self.layerView] {/ / play / / [self / / addSubview:self.activity]; chrysanthemum; failed to load [se LF addSubview:self.failedView]; / / cover picture [self addSubview:self.coverImageView]; / / [self / / addSubview:self.toolView] lower toolbar; upper part of the title bar of the [self addSubview:self.titleView]; / / add constraint [self makeConstraintsForUI];} - {(void) makeConstraintsForUI [_layerView mas_makeConstraints:^ (MASConstraintMaker *make) { (@0); make.left.mas_equalTo (@0); make.right.mas_equalTo (@0); make.bottom.mas_equalTo (@0);}] [_toolView; mas_makeConstraints:^ (MASConstraintMaker *make) {make.bottom.mas_equalTo (@0); make.left.mas_equalTo (@0); make.right.mas_equalTo (@0); make.height.mas_equalTo (@44);}]; [_titleView mas_makeConstraints:^ (MASConstraintMaker *make) { (@0); make.left.mas_equalTo (@0); make.right.mas_equalTo (@0); make.height.mas_equalTo (@44);}]; [_activity mas_makeConstraints:^ (MASConstraintMaker *make) {make.size.mas_equalTo (CGSizeMake (30, 30)); make.centerX.mas_equalTo (self.mas_centerX); make.centerY.mas_equalTo (self.mas_centerY);}]; [_failedView mas_makeConstraints:^ (MASConstraintMaker *make) { (@0); make.left.mas_equalTo (@0); make.right.mas_equalTo (@0); make.bottom.mas_equalTo (@0);}]; [_coverImageView mas_makeConstraints:^ (MASConstraintMaker *make) {make .top.mas_equalTo (@0); make.left.mas_equalTo (@0); make.right.mas_equalTo (@0); make.bottom.mas_equalTo (@0);}];} - {[self.superview (void) layoutSubviews bringSubviewToFront:self] #pragma mark notification;} / / video broadcast completion notification (void) videoPlayEnd - {NSLog (@ "play"); _toolView.playSwitch.selected = NO; [UIView animateWithDuration:0.25 animations:^{[_toolView mas_updateConstraints:^ (MASConstraintMaker *make) {make.bottom.mas_equalTo (@ 0);}]; [_titleView mas_updateConstraints:^ (MASConstraintMaker *make) { (@0);}]; [self layoutIfNeeded];} completion:^ {_isSh (BOOL finished) }]; owToolView = YES; self.videoModel.currentTime = 0; NSInteger = index [self.videoArr indexOfObject:self.videoModel]; if (self.delegate & & [self.delegate; respondsToSelector:@selector (playerView:didPlayEndVideo:index:)] [self.delegate playerView:self didPlayEndVideo:self.videoModel) {index:index]}; if (index! = self.videoArr.count - 1) {[self.player pause]; self.videoModel = self.videoArr[index + 1]; _titleView.title = self.videoModel.title; [self replaceCurrentPlayerItemWithVideoModel:self.videoModel]; [self addToolViewTimer];} else {_isReplay = YES; [self.player = pause]; YES; [self removeToolVi EwTimer]; _coverImageView.hidden = NO; _toolView.slider.sliderPercent = 0; _toolView.slider.enabled = NO; [_activity stopAnimating];}} - #pragma mark monitor video buffer and load state observer and state monitor / register buffer (void) - addObserverWithPlayerItem: (AVPlayerItem * playerItem) {if (playerItem) {[playerItem addObserver:self forKeyPath:@ "loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; [playerItem addObserver:self forKeyPath:@ "status options:NSKeyValueObservingOptionNew context:nil]";}} / / remove Observer - (void) removeObserverWithPlayerItem: (AVPlayerItem * playerItem) {if (playerItem) {[playerItem removeObserver:self forKeyPath:@ Lo " AdedTimeRanges "[playerItem removeObserver:self forKeyPath:@"; "status";}} / / monitor change method - (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (ID) object change: (NSDictionary< NSKeyValueChangeKey, id> *) change context: (void * context) {AVPlayerItem = (AVPlayerItem * playerItem * if ([keyPath) object; isEqualToString:@ "loadedTimeRanges"]) {NSTimeInterval loadedTime = [self availableDurationWithplayerItem:playerItem]; NSTimeInterval totalTime = CMTimeGetSeconds (playerItem.duration); if (! _toolView.slider.isSliding) {_toolView.slider.progressPercent = loadedTime/totalTime;}} (else if [keyPath isEqualToString:@ "status"] {if (playerItem.) Status = = AVPlayerItemStatusReadyToPlay) {NSLog (@ "playerItem is ready"); [self.player play]; = NO; CMTime = seekTime CMTimeMake (self.videoModel.currentTime, 1); [self.player seekToTime:seekTime completionHandler:^ (BOOL finished) {if (finished) {NSTimeInterval current = CMTimeGetSeconds (self.player.currentTime); _toolView.currentTimeLabel.text [self = convertTimeToString:current];}}]; _toolView.slider.enabled = YES; _toolView.playSwitch.enabled = YES; _toolView.playSwitch.selected = YES;} else{NSLog (@ load break); Self.failedView.hidden = NO;}}} - #pragma mark private / / set the player - (void) setPlayer (self.videoModel) {if {if (self.videoModel.url) {if ([self! CheckNetwork]) {return}; AVPlayerItem item = [AVPlayerItem * playerItemWithURL:self.videoModel.url]; self.playerItem = item; [self addObserverWithPlayerItem:self.playerItem]; if (self.player) {[self.player replaceCurr

The code above, here to talk to you about the core lies in:
1, AVPlayerItem loadedTimeRanges and status based on the two properties of the monitor to achieve the acquisition and playback schedule playing buffer state. But these two listeners are not only added to the end of the interface dealloc must be removed, otherwise it will crash.
2, through the completion of the player to listen to the broadcast and save the video information model array to achieve the order of video playback.
3, through the timer to achieve the player’s title bar and control bar animation automatically pop up and put away.
4, through the AVPlayer seekToTime: (CMTime) time completionHandler: (void) (finished) (BOOL) completionHandler this method to achieve the function of continuous broadcast.

Here we take a look at how to use this custom RHPlayerView as follows:

#import "PlayViewController.h" #import "RHPlayerView.h" @interface (PlayViewController) < RHPlayerViewDelegate, UITableViewDelegate, UITableViewDataSource> @property (nonatomic, strong) RHPlayerView * player; @property (nonatomic, strong) UITableView * tableView; @property (nonatomic, strong) NSMutableArray * dataArr; @end @implementation PlayViewController (void viewDidLoad) {[super viewDidLoad]; [self loadData] [self; addSubviews];} - (void) viewWillDisappear: (BOOL) animated if ({[super viewWillDisappear:animated]; [self.navigationController.viewControllers indexOfObject:self] = = NSNotFound) {NSLog ("pop pop pop pop @ pop"); [_player stop];}} - {NSArray (void) loadData * tit LeArr = @[@ "video" and "video two" @, @ "video three"]; NSArray * urlArr = @[@ @ "", "", ""]; @ for (int i = 0; I < titleArr.count; i++) {RHVideoModel = [[RHVideoModel * model alloc] initWithVideoId:[NSString stringWithFormat:@ "%03d", I + 1] title:titleArr[i] url:urlArr[i] currentTime:0]; [self.dataArr addObject:model];} [self.player setVideoModels: self.dataArr playVideoId:@ "]; [self.tableView reloadData];} - {[self.view (void) addSubviews addSubview:self.player]; [self.view addSubview:self.tableView]; [self makeConstraintsForUI];} - {(void) makeConstraintsForUI [_tableView mas_makeCon Straints:^ (MASConstraintMaker *make) { (@ (9 * Screen_Width / 16)); make.left.mas_equalTo (@0); make.right.mas_equalTo (@0); make.bottom.mas_equalTo (@0)}]; #pragma mark;} - player view / / delegate is allowed to play - (BOOL) playerViewShouldPlay {return YES;} / / currently playing - (void) playerView: (RHPlayerView * playView) didPlayVideo: (RHVideoModel *) videoModel index: (NSInteger index) {} / / the playback end - (void) playerView: (RHPlayerView * playView) didPlayEndVideo: (RHVideoModel *) videoModel index: (NSInteger) {index} / / currently playing will be called several times to update the current playback time - (void) playerView: (RHPlayerView * playView) didPlayVideo: (RHVideoModel * videoModel) PlayTime: (NSTimeInterval playTime mark) {#pragma} - tableView delegate - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section {return _dataArr.count;} - (UITableViewCell *) tableView: (* UITableView) tableView cellForRowAtIndexPath: (NSIndexPath * indexPath) {UITableViewCell = [tableView * cell dequeueReusableCellWithIdentifier:@ "Cell_ID"]; cell.selectionStyle = UITableViewCellSelectionStyleNone if (indexPath.row; < _dataArr.count) {RHVideoModel * model = _dataArr[indexPath.row]; cell.textLabel.text = model.title;} return cell;} - (void) tableView: (UITableView * tableView) didSelectRowAtIndexPath: (NSIndexPath * indexPath) {RHVideoModel = _data * model Arr[indexPath.row]; [_player playVideoWithVideoId:model.videoId]; #pragma mark and getter} - setter - (UITableView * tableView) {if (! _tableView) {UITableView = [[UITableView * tableView alloc] init]; tableView.dataSource = self; tableView.delegate = self; [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@ "Cell_ID"]; [[UIView alloc] tableView.tableFooterView = init]; _tableView = tableView;} return _tableView; (RHPlayerView * player)} - {if (! _player) {_player = [[RHPlayerView alloc] initWithFrame:CGRectMake (0, 0, Screen_Width, 9 * Screen_Width / 16) currentVC:self]; _player.delegate = self;}} - return _player; (NSMutableArray *) dataArr {if ((_dataArr)) {_dataArr = [[NSMutableArray alloc] init];} return _dataArr;}

To the end of all packaging, we must remember that when the interface pop call the stop method, there will be no pop after the sound to continue to play (in fact, there is no release of RHPlayerView, but also has been there).

Xiao Bian will be a separate package written demo, if you feel like you want to look at the toolbar package, you can go to the GIT download, the address is as follows:

Finally, still hope to be able to help the apes who are in need, we would like to grow together, in the development of the road farther and farther! Thank you