騰訊云點播VOD 點擊鏈接
點播(Video on Demand)匯聚騰訊強大視頻處理能力,提供一站式視頻點播服務(wù)。從靈活上傳到快速轉(zhuǎn)碼,從便捷發(fā)布到自定義播放器開發(fā),為客戶提供專業(yè)可靠的完整視頻服務(wù)。
效果
播放器gif.gif
快速集成
前言
騰訊云點播VOD SDK支持iOS 7.0以上系統(tǒng)
-
新建工程MyVideoPlayer
將SKD拖入到工程中
SDK下載地址
SDK.png 添加系統(tǒng)依賴庫
TARGETS -> Build Phases -> Link Binary with Libraries
VideoToolbox.framework
SystemConfiguration.framework
CoreTelephony.framework
AVFoundation.framework
CoreMedia.framework
CoreGraphics.framework
libstdc++.tbd
libz.tbd
libiconv.tbd
libresolv.tbd
-
關(guān)閉工程Bitcode選項
bitcode.png 導(dǎo)入頭文件編譯運行
#import "TXRTMPSDK/TXLivePush.h"
NSLog(@"SDK Version = %@", [[TXLivePush getSDKVersion] componentsJoinedByString:@"."]);
沒有報錯表示SDK集成完成
- 播放器
創(chuàng)建播放器對象
@interface KLPlayVideoView : UIView
搭建UI
播放UI.png
KLPlayVideoView.h
@interface KLPlayVideoView : UIView
/*視頻url地址 */
//@property (copy, nonatomic) NSString *vodUrl;
/* 包含在哪一個控制器中 用于全屏 */
@property (nonatomic, weak) UIViewController *contrainerViewController;
/* 暫停播放 */
- (void)pausePlayer;
@end
KLPlayVideoView.m
@interface KLPlayVideoView ()<TXLivePlayListener>
{
TXLivePlayer * _txLivePlayer;//播放器
BOOL _startSeek;
float _sliderValue;
long long _trackingTouchTS;
BOOL _videoPause;//是否暫停
TX_Enum_PlayType _playType;
BOOL _appIsInterrupt;
BOOL _play_switch;
}
@property (weak, nonatomic) IBOutlet UIView *videoContentView;
@property (weak, nonatomic) IBOutlet UIView *toolView;
@property (weak, nonatomic) IBOutlet UIView *topToolView;
@property (weak, nonatomic) IBOutlet UIButton *playButton;
@property (weak, nonatomic) IBOutlet UILabel *currentTimeLabel;
@property (weak, nonatomic) IBOutlet UISlider *progressSlider;
@property (weak, nonatomic) IBOutlet UILabel *totalTimeLabel;
@property (weak, nonatomic) IBOutlet UIButton *fullButton;
@property (weak, nonatomic) IBOutlet UIImageView *logingImageView;
@property (weak, nonatomic) IBOutlet UIButton *titleButton;
/* 全屏控制器 */
@property (nonatomic, strong) KLFullViewController *fullVc;
/* 展示工具條 */
@property (assign, nonatomic) BOOL isShowToolView;
@end
@implementation KLPlayVideoView
全屏試圖控制器,用戶橫屏播放視頻
- (KLFullViewController *)fullVc
{
if (_fullVc == nil) {
_fullVc = [[KLFullViewController alloc] init];
}
return _fullVc;
}
對象釋放的時候移除通知觀察者
- (void)dealloc
{
[[NSNotificationCenter defaultCenter]removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
NSLog(@"-------palyVideoView --- deallo");
self.fullVc = nil;
[_txLivePlayer stopPlay];
_txLivePlayer = nil;
[UIApplication sharedApplication].idleTimerDisabled = NO;//自動鎖屏
}
添加通知、設(shè)置進度條、初始化播放器對象
- (void)awakeFromNib
{
[super awakeFromNib];
self.isShowToolView = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAudioSessionEvent:) name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackGround:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
self.logingImageView.hidden = YES;
_videoPause = NO;
[self addGestureRecognizer:[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)]];
_progressSlider.value = 0;
_progressSlider.continuous = NO;
// 設(shè)置進度條的內(nèi)容
[self.progressSlider setThumbImage:[UIImage imageNamed:@"thumbImage_live"] forState:UIControlStateNormal];
[self.progressSlider setMaximumTrackImage:[UIImage imageNamed:@"MaximumTrackImage_live"] forState:UIControlStateNormal];
[self.progressSlider setMinimumTrackImage:[UIImage imageNamed:@"MinimumTrackImage_live"] forState:UIControlStateNormal];
[_progressSlider addTarget:self action:@selector(onSeek:) forControlEvents:(UIControlEventValueChanged)];
[_progressSlider addTarget:self action:@selector(onSeekBegin:) forControlEvents:(UIControlEventTouchDown)];
[_progressSlider addTarget:self action:@selector(onDrag:) forControlEvents:UIControlEventTouchDragInside];
_trackingTouchTS = 0;
_txLivePlayer = [[TXLivePlayer alloc] init];//初始化
[_txLivePlayer setLogLevel:LOGLEVEL_INFO];
_txLivePlayer.delegate = self; //如果您需要處理播放的事件
[_txLivePlayer setupVideoWidget:self.videoContentView.bounds containView:self.videoContentView insertIndex:0];
}
//外部接口,暫停播放
- (void)pausePlayer
{
if ([_txLivePlayer isPlaying]) {
[self playButtonClick:self.playButton];
}
}
點擊背景切換工具條顯示和隱藏
- (void)tapAction:(UITapGestureRecognizer *)tap
{
[self showToolView:self.isShowToolView];
}
- (void)showToolView:(BOOL)isShow
{
[UIView animateWithDuration:1.0 animations:^{
self.toolView.alpha = !self.isShowToolView;
self.topToolView.alpha = !self.isShowToolView;
self.isShowToolView = !self.isShowToolView;
}];
}
TXLivePlayListener 代理方法
#pragma mark - TXLivePlayListener
/*
*監(jiān)聽播放的進度 代理方法必須實現(xiàn)
*/
-(void)onPlayEvent:(int)EvtID withParam:(NSDictionary*)param
{
NSDictionary* dict = param;
dispatch_async(dispatch_get_main_queue(), ^{
if (EvtID == PLAY_EVT_PLAY_BEGIN)//開始播放
{
//結(jié)束菊花動畫
[self.logingImageView.layer resumeAnimate];
self.logingImageView.hidden = YES;
[self showToolView:self.isShowToolView];
}
else if(EvtID == PLAY_EVT_PLAY_PROGRESS && !_startSeek)
{
//處理播放進度
float progress = [dict[EVT_PLAY_PROGRESS] floatValue];
_currentTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d",
(int)progress/60,(int)progress%60];
[_progressSlider setValue:progress];
//設(shè)置播放時間
float duration = [dict[EVT_PLAY_DURATION] floatValue];
if (duration > 0 && _progressSlider.maximumValue != duration)
{
[_progressSlider setMaximumValue:duration];
_totalTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d",
(int)duration/60,(int)duration%60];
}
return ;
}
else if (EvtID == PLAY_ERR_NET_DISCONNECT || EvtID == PLAY_EVT_PLAY_END)
{
// 播放結(jié)束的事件
[self stopRtmp];
self.playButton.selected = NO;
_play_switch = NO;
_videoPause = NO;
_currentTimeLabel.text = [NSString stringWithFormat:@"00:00"];
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];//自動鎖屏
[_progressSlider setValue:0];
[self fullButtonClick: self.fullButton];
}
else if (EvtID == PLAY_EVT_PLAY_LOADING)
{
[self starAnimation];
}
});
}
//選擇實現(xiàn)
-(void) onNetStatus:(NSDictionary*) param
{
}
視頻網(wǎng)絡(luò)加載中開始動畫和結(jié)束動畫
#pragma mark - private
- (void)starAnimation
{
//1.創(chuàng)建基本動畫
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
//2.設(shè)置動畫的屬性
animation.fromValue = @(0);
animation.toValue = @(M_PI *2);
animation.duration = 3;
animation.repeatCount = NSIntegerMax;
//添加到圖層上
[self.logingImageView.layer addAnimation:animation forKey:nil];
self.logingImageView.hidden = NO;
}
//菊花停止動畫
- (void)stopRtmp
{
[self.logingImageView.layer resumeAnimate];
self.logingImageView.hidden = YES;
if(_txLivePlayer != nil)
{
// _txLivePlayer.delegate = nil;
[_txLivePlayer stopPlay];
// [_txLivePlayer removeVideoWidget];
}
}
//為了避免播放出現(xiàn)異常情況, 播放之前必須檢查地址
-(BOOL)checkPlayUrl:(NSString*)playUrl
{
if (!([playUrl hasPrefix:@"http:"] || [playUrl hasPrefix:@"https:"] || [playUrl hasPrefix:@"rtmp:"] )) {
[KLBaseViewController showHUDWithMsg:@"播放地址不合法,目前僅支持rtmp,flv,hls,mp4播放方式!" duration:1.5];
return NO;
}
if ([playUrl hasPrefix:@"https:"] || [playUrl hasPrefix:@"http:"])
{
if ([playUrl rangeOfString:@".flv"].length > 0)
{
_playType = PLAY_TYPE_VOD_FLV;
} else if ([playUrl rangeOfString:@".m3u8"].length > 0)
{
_playType= PLAY_TYPE_VOD_HLS;
} else if ([playUrl rangeOfString:@".mp4"].length > 0)
{
_playType= PLAY_TYPE_VOD_MP4;
} else
{
[KLBaseViewController showHUDWithMsg:@"播放地址不合法,點播目前僅支持flv,hls,mp4播放方式!" duration:1.5];
return NO;
}
}
else
{
[KLBaseViewController showHUDWithMsg:@"播放地址不合法,點播目前僅支持flv,hls,mp4播放方式!" duration:1.5];
return NO;
}
return YES;
}
播放以及全屏按鈕點擊方法
- (IBAction)playButtonClick:(UIButton *)sender
{
if (self.isShowToolView) {
if ([self checkPlayUrl:self.video.videoPath])
{
if (self.playButton.selected)//暫停
{
self.playButton.selected = NO;
[_txLivePlayer pause];
_videoPause = YES;
_play_switch = NO;
[UIApplication sharedApplication].idleTimerDisabled = NO;//自動鎖屏
}else//播放
{
if (_videoPause == YES)//繼續(xù)播放
{
[_txLivePlayer resume];
_videoPause = NO;
_play_switch = YES;
[UIApplication sharedApplication].idleTimerDisabled = YES;//不自動鎖屏
}else//重新播放
{
int result = [_txLivePlayer startPlay:self.video.videoPath type:_playType];
if (result == -1)
{
[KLBaseViewController showHUDWithMsg:@"非騰訊云鏈接,若要放開限制請聯(lián)系騰訊云商務(wù)團隊" duration:1.5];
}
if( result != 0)
{
[KLBaseViewController showHUDWithMsg:@"播放器啟動失敗" duration:1.5];
}
[UIApplication sharedApplication].idleTimerDisabled = YES;//不自動鎖屏
}
self.playButton.selected = YES;
_play_switch = YES;
}
}
}
}
//全屏or取消全屏按鈕點擊
- (IBAction)fullButtonClick:(UIButton *)sender
{
sender.selected = !sender.selected;
[self videoplayViewSwitchOrientation:sender.selected];
}
//返回
- (IBAction)titleButtonClick:(UIButton *)sender {
self.fullButton.selected = !self.fullButton.selected;
[self videoplayViewSwitchOrientation:self.fullButton.selected];
}
//全屏和取消全屏動畫
- (void)videoplayViewSwitchOrientation:(BOOL)isFull
{
if (isFull) {
[self.contrainerViewController presentViewController:self.fullVc animated:NO completion:^{
[self.fullVc.view addSubview:self];
self.center = self.fullVc.view.center;
self.titleButton.hidden = NO;
self.topToolView.hidden = NO;
[UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{
self.frame = self.fullVc.view.bounds;
} completion:nil];
}];
} else {
[self.fullVc dismissViewControllerAnimated:NO completion:^{
[self.contrainerViewController.view addSubview:self];
[UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{
self.frame = CGRectMake(0, 20, self.contrainerViewController.view.bounds.size.width, self.contrainerViewController.view.bounds.size.width * 9 / 16);
self.titleButton.hidden = YES;
self.topToolView.hidden = YES;
} completion:nil];
}];
}
}
進度條的拖動事件
#pragma -- UISlider - play seek
-(void)onSeek:(UISlider *)slider
{
[_txLivePlayer seek:_sliderValue];
_trackingTouchTS = [[NSDate date]timeIntervalSince1970]*1000;
_startSeek = NO;
NSLog(@"vod seek drag end");
}
-(void)onSeekBegin:(UISlider *)slider{
_startSeek = YES;
NSLog(@"vod seek drag begin");
}
-(void)onDrag:(UISlider *)slider
{
float progress = slider.value;
int intProgress = progress + 0.5;
_currentTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d",(int)(intProgress / 60), (int)(intProgress % 60)];
_sliderValue = slider.value;
}
//系統(tǒng)事件處理
//在低系統(tǒng)(如7.1.2)可能收不到這個回調(diào),請在onAppDidEnterBackGround和onAppWillEnterForeground里面處理打斷邏輯
- (void) onAudioSessionEvent: (NSNotification *) notification
{
NSDictionary *info = notification.userInfo;
AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan) {
if (_play_switch == YES && _appIsInterrupt == NO) {
if (_playType == PLAY_TYPE_VOD_FLV || _playType == PLAY_TYPE_VOD_HLS || _playType == PLAY_TYPE_VOD_MP4) {
if (!_videoPause) {
[_txLivePlayer pause];
}
}
_appIsInterrupt = YES;
}
}else{
AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
if (options == AVAudioSessionInterruptionOptionShouldResume) {
if (_play_switch == YES && _appIsInterrupt == YES) {
if (_playType == PLAY_TYPE_VOD_FLV || _playType == PLAY_TYPE_VOD_HLS || _playType == PLAY_TYPE_VOD_MP4) {
if (!_videoPause) {
[_txLivePlayer resume];
}
}
_appIsInterrupt = NO;
}
}
}
}
- (void)onAppDidEnterBackGround:(UIApplication*)app {
if (_play_switch == YES && _appIsInterrupt == NO) {
if (_playType == PLAY_TYPE_VOD_FLV || _playType == PLAY_TYPE_VOD_HLS || _playType == PLAY_TYPE_VOD_MP4) {
if (!_videoPause) {
[_txLivePlayer pause];
}
}
_appIsInterrupt = YES;
}
}
- (void)onAppWillEnterForeground:(UIApplication*)app {
if (_play_switch == YES && _appIsInterrupt == YES) {
if (_playType == PLAY_TYPE_VOD_FLV || _playType == PLAY_TYPE_VOD_HLS || _playType == PLAY_TYPE_VOD_MP4) {
if (!_videoPause) {
[_txLivePlayer resume];
}
}
_appIsInterrupt = NO;
}
}
補充工程全屏設(shè)置
可以參考簡友文章
http://www.lxweimin.com/p/4a51a2edf270-
工程設(shè)置
系統(tǒng)設(shè)置.png FullViewController.m
@interface FullView : UIView//替代FullViewController的View
@end
@implementation FullView
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
self.autoresizesSubviews = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.backgroundColor = [UIColor blackColor];
}
return self;
}
@end
@implementation KLFullViewController
- (void)loadView
{
FullView *fullView = [[FullView alloc] init];
self.view = fullView;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
// 如果需要橫屏的時候,一定要重寫這個方法并返回NO
- (BOOL)prefersStatusBarHidden
{
return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
// 畫面一開始加載時就是豎向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeRight;
}
@end
本文只做學(xué)習(xí)用,大神忽噴。