diff --git a/README.md b/README.md index 6994031e..25f39fca 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Qplayer2是一款跨平台的播放器SDK,除了基础的播放器能力外, Platform | Build Status -------- | ------------ Android | https://github.com/pili-engineering/QPlayer2-Android - IOS | Last Version: 1.2.2 + IOS | Last Version: 1.2.3 Windows | 敬请期待 Mac | 敬请期待 ### qplayer2-core 功能列表 @@ -43,7 +43,7 @@ Qplayer2是一款跨平台的播放器SDK,除了基础的播放器能力外, ##### 引入依赖 ```groovy -pod 'qplayer2-core', '1.2.2' +pod 'qplayer2-core', '1.2.3' ``` diff --git a/ReleaseNote/ReleaseNote-1.2.3.md b/ReleaseNote/ReleaseNote-1.2.3.md new file mode 100644 index 00000000..6df1763a --- /dev/null +++ b/ReleaseNote/ReleaseNote-1.2.3.md @@ -0,0 +1,20 @@ +# 1.2.3 ReleaseNote + +#### 能力 + +- 新增 author_id 鉴权方式 +- 添加双视频切换以及其初始化配置 render ratio 和播放速度。 + +#### 优化 + +- 缩短了立即切换清晰度的耗时, + + +#### 修复问题 + +- 修复在后台创建播放器崩溃 +- 修复填充模式为 FULL_SCREEN 时,渲染控件为正方形时不生效 +- 修复部分 mp4 转 m3u8 的视频播放花屏的问题 + + + diff --git a/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj b/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj index c5e1dd9b..2b66fa4a 100644 --- a/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj +++ b/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj @@ -71,6 +71,7 @@ 50B1F4C3289CC62D0074E02D /* pl_rotate@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 50B1F47F289CC62D0074E02D /* pl_rotate@3x.png */; }; 50B1F4C5289CC62D0074E02D /* QNHomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B1F483289CC62D0074E02D /* QNHomeViewController.m */; }; 616D0BA391829A738EA77F6B /* libPods-qplayer2demo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C0C2D1DB69E96781D80C1A2 /* libPods-qplayer2demo.a */; }; + 977927F029B71BF1006E7931 /* QNDoublePlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 977927EF29B71BF1006E7931 /* QNDoublePlayerViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -178,6 +179,8 @@ 50B1F481289CC62D0074E02D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50B1F483289CC62D0074E02D /* QNHomeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QNHomeViewController.m; sourceTree = ""; }; 7C0C2D1DB69E96781D80C1A2 /* libPods-qplayer2demo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-qplayer2demo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 977927EE29B71BE5006E7931 /* QNDoublePlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QNDoublePlayerViewController.h; sourceTree = ""; }; + 977927EF29B71BF1006E7931 /* QNDoublePlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QNDoublePlayerViewController.m; sourceTree = ""; }; B2BDA3706EA1C6372228C6C3 /* Pods-qplayer2demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-qplayer2demo.debug.xcconfig"; path = "Target Support Files/Pods-qplayer2demo/Pods-qplayer2demo.debug.xcconfig"; sourceTree = ""; }; CA90B80F289A7AF60087543B /* qplayer2demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = qplayer2demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -226,9 +229,11 @@ 50B1F457289CC62D0074E02D /* QNInfoHeaderView.h */, 50B1F43F289CC62D0074E02D /* QNInfoHeaderView.m */, 50B1F41D289CC62D0074E02D /* QNPlayerViewController.h */, + 977927EE29B71BE5006E7931 /* QNDoublePlayerViewController.h */, 50B1F447289CC62D0074E02D /* QNPlayerViewController.m */, 50B1F436289CC62D0074E02D /* QNPublicHeader.h */, 50B1F455289CC62D0074E02D /* QNURLListTableViewCell.h */, + 977927EF29B71BF1006E7931 /* QNDoublePlayerViewController.m */, 50B1F437289CC62D0074E02D /* QNURLListTableViewCell.m */, 50B1F45B289CC62D0074E02D /* Resource */, 50B1F458289CC62D0074E02D /* ScanCode */, @@ -557,6 +562,7 @@ 50B1F48D289CC62D0074E02D /* QNPlayerConfigViewController.m in Sources */, 5076C9612906ADC3007763E7 /* QNPlayerShortVideoMaskView.m in Sources */, 50B1F491289CC62D0074E02D /* QNToastView.m in Sources */, + 977927F029B71BF1006E7931 /* QNDoublePlayerViewController.m in Sources */, 50B1F49C289CC62D0074E02D /* QNButtonView.m in Sources */, 50B1F48B289CC62D0074E02D /* QNSpeedPlayerView.m in Sources */, 50B1F493289CC62D0074E02D /* QNInfoHeaderView.m in Sources */, @@ -728,7 +734,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2.2; + MARKETING_VERSION = 1.2.3; PODS_PODFILE_DIR_PATH = "${SRCROOT}/."; PODS_ROOT = "${SRCROOT}/Pods"; PRODUCT_BUNDLE_IDENTIFIER = com.qiniu.qplayer2demo; @@ -770,7 +776,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2.2; + MARKETING_VERSION = 1.2.3; PODS_PODFILE_DIR_PATH = "${SRCROOT}/."; PODS_ROOT = "${SRCROOT}/Pods"; PRODUCT_BUNDLE_IDENTIFIER = com.qiniu.qplayer2demo; diff --git a/qplayer2demo/qplayer2demo/QNDoublePlayerViewController.h b/qplayer2demo/qplayer2demo/QNDoublePlayerViewController.h new file mode 100644 index 00000000..6941b3d4 --- /dev/null +++ b/qplayer2demo/qplayer2demo/QNDoublePlayerViewController.h @@ -0,0 +1,19 @@ +// +// UIViewController+QNDoublePlayerViewController.h +// qplayer2demo +// +// Created by 李林峰 on 2023/2/14. +// + +#import +#import "QNClassModel.h" + + +@interface QNDoublePlayerViewController: UIViewController + +//configArray +@property (nonatomic, strong) NSArray *playerConfigArray; + +@end + + diff --git a/qplayer2demo/qplayer2demo/QNDoublePlayerViewController.m b/qplayer2demo/qplayer2demo/QNDoublePlayerViewController.m new file mode 100644 index 00000000..749f47ad --- /dev/null +++ b/qplayer2demo/qplayer2demo/QNDoublePlayerViewController.m @@ -0,0 +1,376 @@ +// +// QNDoublePlayerViewController.m +// qplayer2demo +// +// Created by 李林峰 on 2023/2/14. +// + +#import "QNDoublePlayerViewController.h" +#import "QNPlayerShortVideoMaskView.h" +#import "QNToastView.h" + +#import "QDataHandle.h" + +static BOOL playerSwitchFlag = true; +static NSString *status[] = { + @"Unknow", + @"Preparing", + @"Ready", + @"Open", + @"Caching", + @"Playing", + @"Paused", + @"Stopped", + @"Error", + @"Reconnecting", + @"Completed" +}; + +@interface QNDoublePlayerViewController() +< +QIPlayerStateChangeListener, +QIPlayerProgressListener, +QIPlayerRenderListener, +QIMediaItemStateChangeListener, +QIMediaItemCommandNotAllowListener +> + +@property (nonatomic, strong) QPlayerContext *player; +@property (nonatomic, strong) QPlayerContext * player_other; +@property (nonatomic, strong) RenderView *myRenderView; +@property (nonatomic, strong) RenderView *otherRenderView; + +@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView; +@property (nonatomic, assign) CGFloat topSpace; +@property (nonatomic, strong) QNToastView *toastView; + +//交换player +@property (nonatomic, strong) QMediaModelBuilder *modleBuilder_main; +@property (nonatomic, strong) QMediaModelBuilder *modelBuilder_other; +//@property (nonatomic, assign, getter=true) Boolean *PlayerSwitchFlag; + + + +@end + +@implementation QNDoublePlayerViewController + +-(void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self.navigationController setNavigationBarHidden:NO animated:NO]; + +} + +-(void)viewDidLoad { + [super viewDidLoad]; + [self configView]; + [self setupPlayer: _playerConfigArray]; + [self setupPlayer_other: _playerConfigArray]; + + [self makeSwitch]; +} + + +- (void)dealloc { + NSLog(@"dealloc"); +} + +- (void)viewWillDisAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self.player.controlHandler stop]; + + _toastView = nil; + [self.player.controlHandler playerRelease]; + self.myRenderView = nil; + self.otherRenderView = nil; + self.player = nil; + self.player_other = nil; +} + +#pragma mark - configPlayerANDconfigDefaults +- (void)configView { + if (@available(iOS 13.0, *)) { + UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc]init]; + [appearance configureWithOpaqueBackground]; + appearance.backgroundColor =PL_SEGMENT_BG_COLOR; + [self.navigationController.navigationBar setScrollEdgeAppearance:appearance]; + [self.navigationController.navigationBar setStandardAppearance:appearance]; + + } else { + self.navigationController.navigationBar.barTintColor = PL_SEGMENT_BG_COLOR; + } + + UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(5, 6, 34, 34)]; + UIImage *image = [UIImage imageNamed:@"pl_back"]; + if ([UIDevice currentDevice].systemVersion.floatValue >= 11.0f) { + image = [self originImage:image scaleToSize:CGSizeMake(34, 34)]; + } + [backButton setImage:image forState:UIControlStateNormal]; + [backButton addTarget:self action:@selector(getBack) forControlEvents:UIControlEventTouchDown]; + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:backButton]; + + //configDefaults + _playerConfigArray = [QDataHandle shareInstance].playerConfigArray; + +} + +// +#pragma mark setup +- (void)setupPlayer:(NSArray*)models { + if (PL_HAS_NOTCH) { + _topSpace = 88; + } else { + _topSpace = 64; + } + NSMutableArray *configs = [NSMutableArray array]; + + if (models) { + configs = [models mutableCopy]; + } else { + NSUserDefaults *userdafault = [NSUserDefaults standardUserDefaults]; + NSArray *dataArray = [userdafault objectForKey:@"PLPlayer_settings"]; + if (dataArray.count != 0 ) { + for (NSData *data in dataArray) { + QNClassModel *classModel = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + [configs addObject:classModel]; + } + } + } + NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; + + _myRenderView = [[RenderView alloc]initWithFrame:CGRectMake(0, _topSpace, PLAYER_PORTRAIT_WIDTH, PL_SCREEN_HEIGHT)]; + + [self.view addSubview:_myRenderView]; + + QPlayerContext *player = [[QPlayerContext alloc]initPlayerAPPVersion:@"" localStorageDir:documentsDir logLevel:LOG_VERBOSE]; + + self.player = player; + [self.myRenderView attachPlayerContext:self.player]; + + _modleBuilder_main = [[QMediaModelBuilder alloc] initWithIsLive:NO]; + [_modleBuilder_main addStreamElementWithUserType:@"" + urlType:QURL_TYPE_QAUDIO_AND_VIDEO //资源的类型,这里的url对应的资源是音视频 + url:@"http://demo-videos.qnsdk.com/shortvideo/nike.mp4" //播放地址 + quality:1080 //清晰度数值标记为1080 + isSelected:YES + backupUrl:@"" //备用地址 + referer:@"" //http/https 协议的地址 支持该属性 + renderType:QPLAYER_RENDER_TYPE_PLANE]; + for (QNClassModel* model in configs) { + for (PLConfigureModel* configModel in model.classValue) { + if ([model.classKey isEqualToString:@"PLPlayerOption"]) { + [self configurePlayerWithConfigureModel:configModel classModel:model isMain:true]; + } + } + } + QMediaModel *model = [_modleBuilder_main build]; + [self.player.controlHandler playMediaModel:model startPos:0]; + + + +} +- (void)setupPlayer_other:(NSArray*)models { + if (PL_HAS_NOTCH) { + _topSpace = 88; + } else { + _topSpace = 64; + } + NSMutableArray *configs = [NSMutableArray array]; + + if (models) { + configs = [models mutableCopy]; + } else { + NSUserDefaults *userdafault = [NSUserDefaults standardUserDefaults]; + NSArray *dataArray = [userdafault objectForKey:@"PLPlayer_settings"]; + if (dataArray.count != 0 ) { + for (NSData *data in dataArray) { + QNClassModel *classModel = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + [configs addObject:classModel]; + } + } + } + + NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; + _otherRenderView = [[RenderView alloc]initWithFrame:CGRectMake(0, PL_SCREEN_HEIGHT-350, PL_SCREEN_WIDTH, PL_SCREEN_WIDTH)]; + [self.view addSubview:_otherRenderView]; + QPlayerContext *player_other = [[QPlayerContext alloc]initPlayerAPPVersion:@"" localStorageDir:documentsDir logLevel:LOG_VERBOSE]; + self.player_other = player_other; + [self.otherRenderView attachPlayerContext:self.player_other]; + _modelBuilder_other = [[QMediaModelBuilder alloc] initWithIsLive:NO]; + [_modelBuilder_other addStreamElementWithUserType:@"" + urlType:QURL_TYPE_QAUDIO_AND_VIDEO + url:@"http://demo-videos.qnsdk.com/shortvideo/桃花.mp4" + quality:1080 + isSelected:YES + backupUrl:@"" + referer:@"" + renderType:QPLAYER_RENDER_TYPE_PLANE]; + for (QNClassModel* model in configs) { + for (PLConfigureModel* configModel in model.classValue) { + if ([model.classKey isEqualToString:@"PLPlayerOption"]) { + [self configurePlayerWithConfigureModel:configModel classModel:model isMain:false];; + } + } + } + QMediaModel *model = [_modelBuilder_other build]; + [self.player_other.controlHandler playMediaModel:model startPos:0]; + +} +- (void)getBack { + [self.player.controlHandler stop]; + [self.navigationController popViewControllerAnimated:YES]; +} + +- (UIImage *)originImage:(UIImage *)image scaleToSize:(CGSize)size{ + UIGraphicsBeginImageContext(size); + [image drawInRect:CGRectMake(0, 0, size.width, size.height)]; + UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return scaledImage; +} + + +#pragma mark -function of switch + +- (void)makeSwitch { + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(swithActionPlayer)]; + UITapGestureRecognizer *tapGesture_other = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(swithActionPlayer_other)]; + tapGesture.numberOfTapsRequired = 1; + tapGesture.cancelsTouchesInView = NO; + tapGesture_other.numberOfTapsRequired = 1; + tapGesture_other.cancelsTouchesInView = NO; + [_otherRenderView addGestureRecognizer:tapGesture]; + [_myRenderView addGestureRecognizer:tapGesture_other]; +} +//交换rendview +- (void)switchAction { + BOOL isPlaying_player = (_player.controlHandler.currentPlayerState == QPLAYER_STATE_PLAYING); + if(isPlaying_player) { + [_player.controlHandler pauseRender]; + [_player_other.controlHandler pauseRender]; + } + _myRenderView.frame = CGRectMake(0, _topSpace, PLAYER_PORTRAIT_WIDTH, PL_SCREEN_HEIGHT); + _otherRenderView.frame = CGRectMake(0, _topSpace, PLAYER_PORTRAIT_WIDTH, PL_SCREEN_HEIGHT); + + [_player_other.controlHandler resumeRender]; + [_player.controlHandler resumeRender]; + +} + +//交换player +- (void)swithActionPlayer { + BOOL isPlaying_player = (_player.controlHandler.currentPlayerState == QPLAYER_STATE_PLAYING); + BOOL isPlaying_player_other = (_player_other.controlHandler.currentPlayerState == QPLAYER_STATE_PLAYING); + + BOOL bothPlaying = (isPlaying_player && isPlaying_player_other); + BOOL onlyPlayerplaying = (isPlaying_player && !isPlaying_player_other); + BOOL onlyPlayerOtherplaying = (!isPlaying_player && isPlaying_player_other); + + if(bothPlaying) { + if(playerSwitchFlag) { + [self.otherRenderView attachPlayerContext:self.player]; + [self.myRenderView attachPlayerContext:self.player_other]; + playerSwitchFlag = false; + } else { + [self.otherRenderView attachPlayerContext:self.player_other]; + [self.myRenderView attachPlayerContext:self.player]; + playerSwitchFlag = true; + } + } else if(onlyPlayerplaying) { + [_player.controlHandler resumeRender]; + } else if(onlyPlayerOtherplaying) { + [_player_other.controlHandler resumeRender]; + } else { + [self switchAction]; + } +} +- (void)swithActionPlayer_other { + BOOL isPlaying_player = (_player.controlHandler.currentPlayerState == QPLAYER_STATE_PLAYING); + BOOL isPlaying_player_other = (_player_other.controlHandler.currentPlayerState == QPLAYER_STATE_PLAYING); + + BOOL bothPlaying = (isPlaying_player && isPlaying_player_other); + BOOL onlyPlayerplaying = (isPlaying_player && !isPlaying_player_other); + BOOL onlyPlayerOtherplaying = (!isPlaying_player && isPlaying_player_other); + + if(bothPlaying) { + if(playerSwitchFlag) { + [self.otherRenderView attachPlayerContext:self.player]; + [self.myRenderView attachPlayerContext:self.player_other]; + playerSwitchFlag = false; + } else { + [self.otherRenderView attachPlayerContext:self.player_other]; + [self.myRenderView attachPlayerContext:self.player]; + playerSwitchFlag = true; + } + } else if(onlyPlayerplaying) { + [_player.controlHandler resumeRender]; + } else if(onlyPlayerOtherplaying) { + [_player_other.controlHandler resumeRender]; + } else { + [self switchAction]; + } +} + +#pragma mark - PLPlayerSettingsVcDelegate + + +- (void)configurePlayerWithConfigureModel:(PLConfigureModel *)configureModel classModel:(QNClassModel *)classModel isMain:(BOOL)isMain { + NSInteger index = [configureModel.selectedNum integerValue]; + + if ([classModel.classKey isEqualToString:@"PLPlayerOption"]) { + if ([configureModel.configuraKey containsString:@"播放速度"]) { + if(isMain) { + [self.player.controlHandler setSpeed:[configureModel.configuraValue[index] floatValue]]; + } else { + [self.player_other.controlHandler setSpeed:[configureModel.configuraValue[index] floatValue]]; + } + + } + + if ([configureModel.configuraKey containsString:@"播放起始"]){ + + } else if ([configureModel.configuraKey containsString:@"Decoder"]) { + [self.player.controlHandler setDecoderType:(QPlayerDecoder)index]; + + + } else if ([configureModel.configuraKey containsString:@"Seek"]) { + [self.player.controlHandler setSeekMode:index]; + + } else if ([configureModel.configuraKey containsString:@"Start Action"]) { + [self.player.controlHandler setStartAction:(QPlayerStart)index]; + + } else if ([configureModel.configuraKey containsString:@"Render ratio"]) { + if(isMain) { + [self.player.renderHandler setRenderRatio:(QPlayerRenderRatio)(index + 1)]; + } else { + [self.player_other.renderHandler setRenderRatio:(QPlayerRenderRatio)(index + 1)]; + } + + } else if ([configureModel.configuraKey containsString:@"色盲模式"]) { + [self.player.renderHandler setBlindType:(QPlayerBlind)index]; + } + else if ([configureModel.configuraKey containsString:@"SEI"]) { + if (index == 0) { + + [self.player.controlHandler setSEIEnable:YES]; + }else{ + [self.player.controlHandler setSEIEnable:NO]; + } + } + else if ([configureModel.configuraKey containsString:@"鉴权"]) { + if (index == 0) { + [self.player.controlHandler forceAuthenticationFromNetwork]; + } + } + else if ([configureModel.configuraKey containsString:@"后台播放"]){ + if (index == 0) { + [self.player.controlHandler setBackgroundPlayEnable:YES]; + } + else{ + [self.player.controlHandler setBackgroundPlayEnable:NO]; + } + } + } +} + +@end diff --git a/qplayer2demo/qplayer2demo/QNHomeViewController.m b/qplayer2demo/qplayer2demo/QNHomeViewController.m index ab02c41f..5a5a6013 100644 --- a/qplayer2demo/qplayer2demo/QNHomeViewController.m +++ b/qplayer2demo/qplayer2demo/QNHomeViewController.m @@ -10,6 +10,7 @@ #import "QNPlayerViewController.h" #import "PLCellPlayerViewController.h" #import "QNPlayerConfigViewController.h" +#import "QNDoublePlayerViewController.h" #define kLogoSizeWidth (PL_SCREEN_WIDTH - 100) @@ -79,6 +80,15 @@ - (void)layoutMainView { demoVersionLabel.font = [UIFont systemFontOfSize:10.0]; [self.view addSubview:demoVersionLabel]; + // 两个player切换 + UIButton *doublePlayerButton = [[UIButton alloc] initWithFrame:CGRectMake(70, (PL_SCREEN_HEIGHT - kLogoSizeHeight - 116)/4 + kLogoSizeHeight + 50 + 210, PL_SCREEN_WIDTH - 140, 34)]; + doublePlayerButton.backgroundColor = PL_BUTTON_BACKGROUNDCOLOR; + doublePlayerButton.layer.cornerRadius = 3; + doublePlayerButton.tag = 20; + [doublePlayerButton addTarget:self action:@selector(enterDoublePlayerAction:) forControlEvents:UIControlEventTouchDown]; + [doublePlayerButton setTitle:@"两个player切换" forState:UIControlStateNormal]; + doublePlayerButton.titleLabel.font = PL_FONT_MEDIUM(14); + [self.view addSubview:doublePlayerButton]; UILabel *frameworkVersionLabel = [[UILabel alloc]initWithFrame:CGRectMake(PL_SCREEN_WIDTH-150, PL_SCREEN_HEIGHT-70 , 150, 30)]; frameworkVersionLabel.textColor = [UIColor blackColor]; @@ -113,6 +123,11 @@ - (void)enterItemPlayerAction:(UIButton *)button { [self.navigationController pushViewController:itemPlayerViewController animated:YES]; } +- (void)enterDoublePlayerAction:(UIButton *)button { + QNDoublePlayerViewController *doublePlayerViewController = [[QNDoublePlayerViewController alloc] init]; + [self.navigationController pushViewController:doublePlayerViewController animated:YES]; +} + - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.