腾讯云点播VOD 点击链接
点播(Video on Demand)汇聚腾讯强大视频处理能力,提供一站式视频点播服务。从灵活上传到快速转码,从便捷发布到自定义播放器开发,为客户提供专业可靠的完整视频服务。
效果
快速集成
前言
腾讯云点播VOD SDK支持iOS 7.0以上系统
-
新建工程MyVideoPlayer
将SKD拖入到工程中
SDK下载地址
添加系统依赖库
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
-
关闭工程Bitcode选项
导入头文件编译运行
#import "TXRTMPSDK/TXLivePush.h"
NSLog(@"SDK Version = %@", [[TXLivePush getSDKVersion] componentsJoinedByString:@"."]);
没有报错表示SDK集成完成
- 播放器
创建播放器对象
@interface KLPlayVideoView : UIView
搭建UI
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;//自动锁屏
}
添加通知、设置进度条、初始化播放器对象
- (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;
// 设置进度条的内容
[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
/*
*监听播放的进度 代理方法必须实现
*/
-(void)onPlayEvent:(int)EvtID withParam:(NSDictionary*)param
{
NSDictionary* dict = param;
dispatch_async(dispatch_get_main_queue(), ^{
if (EvtID == PLAY_EVT_PLAY_BEGIN)//开始播放
{
//结束菊花动画
[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];
//设置播放时间
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)
{
// 播放结束的事件
[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];
}
});
}
//选择实现
-(void) onNetStatus:(NSDictionary*) param
{
}
视频网络加载中开始动画和结束动画
#pragma mark - private
- (void)starAnimation
{
//1.创建基本动画
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
//2.设置动画的属性
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];
}
}
//为了避免播放出现异常情况, 播放之前必须检查地址
-(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)//继续播放
{
[_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:@"非腾讯云链接,若要放开限制请联系腾讯云商务团队" 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;
}
//系统事件处理
//在低系统(如7.1.2)可能收不到这个回调,请在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;
}
}
补充工程全屏设置
可以参考简友文章
http://www.jianshu.com/p/4a51a2edf270-
工程设置
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
本文只做学习用,大神忽喷。