From d692c9abe6a886aa4913f4162118b0e490cf0a1f Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Tue, 26 Dec 2023 14:21:49 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E6=96=B0=E5=A2=9Ereleasenode=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit fd50682285fb4aa8d423c708243681cc85ebcd7d) --- README.md | 4 ++-- ReleaseNote/ReleaseNote-1.3.4.md | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 ReleaseNote/ReleaseNote-1.3.4.md diff --git a/README.md b/README.md index ac67df60..bc323bc5 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.3.3 + IOS | Last Version: 1.3.4 Windows | 敬请期待 Mac | 敬请期待 ### qplayer2-core 功能列表 @@ -45,7 +45,7 @@ Qplayer2是一款跨平台的播放器SDK,除了基础的播放器能力外, ##### 引入依赖 ```groovy -pod 'qplayer2-core', '1.3.3' +pod 'qplayer2-core', '1.3.4' ``` diff --git a/ReleaseNote/ReleaseNote-1.3.4.md b/ReleaseNote/ReleaseNote-1.3.4.md new file mode 100644 index 00000000..dbf8129d --- /dev/null +++ b/ReleaseNote/ReleaseNote-1.3.4.md @@ -0,0 +1,17 @@ +# 1.3.4 ReleaseNote + +- #### 能力 + + - 新增解码失败回调 :QIPlayerVideoDecodeListener + + `-(**void**)onDecodeFailed:(QPlayerContext *)context retry:(**BOOL**)retry` + + +- #### 优化 + + - 优化硬解降级方案。 + + +- #### 修复问题 + + - 修复冷启动情况下播放第一个视频为 rtmp 时,首帧卡死,音频只能播放2-3秒,且无法恢复的问题 From 555967d063aa04923062d4e256bfd2eaaea6f2dd Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Tue, 2 Jan 2024 10:50:22 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20releaseNote=20?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 56e23ae1ca02f13cb218fe2def72e68106d54c95) --- ReleaseNote/ReleaseNote-1.3.4.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ReleaseNote/ReleaseNote-1.3.4.md b/ReleaseNote/ReleaseNote-1.3.4.md index dbf8129d..1af18c88 100644 --- a/ReleaseNote/ReleaseNote-1.3.4.md +++ b/ReleaseNote/ReleaseNote-1.3.4.md @@ -6,12 +6,10 @@ `-(**void**)onDecodeFailed:(QPlayerContext *)context retry:(**BOOL**)retry` - - #### 优化 - 优化硬解降级方案。 - - #### 修复问题 - - 修复冷启动情况下播放第一个视频为 rtmp 时,首帧卡死,音频只能播放2-3秒,且无法恢复的问题 + - 修复冷启动情况下播放第一个视频为首帧非零且很大的 rtmp 时,首帧卡死,音频只能播放2-3秒,且无法恢复的问题 From 1a602c9816a51fbbd21196a3935660fed0be6700 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Wed, 3 Jan 2024 11:50:51 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E9=9B=86=E6=88=90=E6=8E=A8=E6=B5=81sdk?= =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=E6=95=B0=E6=8D=AE=E4=B8=8A=E6=8A=9B?= =?UTF-8?q?=E5=B9=B6=E6=8E=A8=E6=B5=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcschemes/qplayer2demo.xcscheme | 2 +- .../LongVideo/QNPlayerViewController.m | 142 +++++++++++++++++- .../qplayer2demo/Public/QNPlayerMaskView.h | 2 + .../qplayer2demo/Public/QNPlayerMaskView.m | 2 +- qplayer2demo/qplayer2demo/QNAppDelegate.m | 3 +- qplayer2demo/qplayer2demo/QNPublicHeader.h | 1 + qplayer2demo/qplayer2demo/toast/QNToastView.m | 8 +- 7 files changed, 145 insertions(+), 15 deletions(-) diff --git a/qplayer2demo/qplayer2demo.xcodeproj/xcshareddata/xcschemes/qplayer2demo.xcscheme b/qplayer2demo/qplayer2demo.xcodeproj/xcshareddata/xcschemes/qplayer2demo.xcscheme index e406cc7c..da26dc39 100644 --- a/qplayer2demo/qplayer2demo.xcodeproj/xcshareddata/xcschemes/qplayer2demo.xcscheme +++ b/qplayer2demo/qplayer2demo.xcodeproj/xcshareddata/xcschemes/qplayer2demo.xcscheme @@ -43,7 +43,7 @@ /** 播放器蒙版视图 **/ @@ -91,12 +93,19 @@ @interface QNPlayerViewController () @property (nonatomic, strong) NSString *mSEIString; @property (nonatomic, assign) BOOL mIsStartPush; +@property (nonatomic, strong) PLStreamingSession *mSession; + @end @implementation QNPlayerViewController - (void)dealloc { - + if (self.mSession.isRunning) { + [self.mSession stop]; + self.mSession.delegate = nil; + self.mSession = nil; + } + NSLog(@"QNPlayerViewController dealloc"); } @@ -244,7 +253,8 @@ - (void)viewDidLoad { [self addPlayerMaskView]; [self layoutUrlListTableView]; - + [self setPLStream]; + self.mToastView = [[QNToastView alloc]initWithFrame:CGRectMake(0, PL_SCREEN_HEIGHT-300, 200, 300)]; [self.view addSubview:self.mToastView]; [self playerContextAllCallBack]; @@ -252,6 +262,66 @@ - (void)viewDidLoad { } +#pragma mark - 初始化 PLStreaming +-(void)setPLStream{ + //默认配置 + PLVideoStreamingConfiguration *videoStreamingConfiguration = [PLVideoStreamingConfiguration defaultConfiguration]; + PLAudioStreamingConfiguration *audioStreamingConfiguration = [PLAudioStreamingConfiguration defaultConfiguration]; + videoStreamingConfiguration.videoSize =CGSizeMake(1080, 720); + self.mSession = [[PLStreamingSession alloc] initWithVideoStreamingConfiguration:videoStreamingConfiguration audioStreamingConfiguration:audioStreamingConfiguration stream:nil]; + +} +-(void)pushStreamButtonClick:(BOOL)isSelected{ + self.mIsStartPush = isSelected; + if(isSelected){ + __weak typeof(self) weakSelf = self; + NSURL *url = [NSURL URLWithString:PL_PUSH_STREAMING_URL]; + [self.mSession startWithPushURL:url feedback:^(PLStreamStartStateFeedback feedback) { + [weakSelf streamStateAlert:feedback]; + + [self.mPlayerView.controlHandler addPlayerVideoDataListener:self]; + [self.mPlayerView.controlHandler addPlayerAudioDataListener:self]; + }]; + }else{ + [self.mSession stop]; + [self.mPlayerView.controlHandler removePlayerAudioDataListener:self]; + [self.mPlayerView.controlHandler removePlayerVideoDataListener:self]; + } +} +- (void)streamStateAlert:(PLStreamStartStateFeedback)feedback { + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + switch (feedback) { + case PLStreamStartStateSuccess: + NSLog(@"成功开始推流!"); + [weakSelf.mToastView addText:@"成功开始推流!"]; + break; + case PLStreamStartStateSessionUnknownError: + NSLog(@"发生未知错误无法启动!"); + [weakSelf.mToastView addText:@"发生未知错误无法启动!"]; + break; + case PLStreamStartStateSessionStillRunning: + NSLog(@"已经在运行中,无需重复启动!"); + [weakSelf.mToastView addText:@"已经在运行中,无需重复启动!"]; + break; + case PLStreamStartStateStreamURLUnauthorized: + NSLog(@"当前的 StreamURL 没有被授权!"); + [weakSelf.mToastView addText:@"当前的 StreamURL 没有被授权!"]; + break; + case PLStreamStartStateSessionConnectStreamError: + NSLog(@"建立 socket 连接错误!"); + [weakSelf.mToastView addText:@"建立 socket 连接错误!"]; + break; + case PLStreamStartStateSessionPushURLInvalid: + NSLog(@"当前传入的 pushURL 无效!"); + [weakSelf.mToastView addText:@"当前传入的 pushURL 无效!"]; + break; + default: + break; + } + }); +} + #pragma mark - 初始化 PLPlayer @@ -335,7 +405,6 @@ - (void)setUpPlayer:(NSArray*)models { [self.mPlayerView.controlHandler forceAuthenticationFromNetwork]; QMediaModel *model = self.mPlayerModels.firstObject; - [self.mPlayerView.controlHandler playMediaModel:model startPos:[[QDataHandle shareInstance] getConfiguraPostion]]; for (QNClassModel* model in configs) { for (PLConfigureModel* configModel in model.classValue) { if ([model.classKey isEqualToString:@"PLPlayerOption"]) { @@ -344,6 +413,7 @@ - (void)setUpPlayer:(NSArray*)models { } } + [self.mPlayerView.controlHandler playMediaModel:model startPos:[[QDataHandle shareInstance] getConfiguraPostion]]; } @@ -443,8 +513,67 @@ -(void)onDecodeFailed:(QPlayerContext *)context retry:(BOOL)retry{ [self.mToastView addText:[NSString stringWithFormat:@"解码失败 : retry false"]]; } } +-(void)onVideoDecodeByType:(QPlayerContext *)context Type:(QPlayerDecoderType)type{ + NSString* text = @"使用的解码类型:"; + switch (type) { + case QPLAYER_DECODER_TYPE_NONE: + text = [NSString stringWithFormat:@"%@ none",text]; + break; + case QPLAYER_DECODER_TYPE_SOFTWARE: + text = [NSString stringWithFormat:@"%@ 软解",text]; + break; + case QPLAYER_DECODER_TYPE_HARDWARE: + text = [NSString stringWithFormat:@"%@ 硬解",text]; + break; + default: + text = [NSString stringWithFormat:@"%@ none",text]; + break; + } + [self.mToastView addText:text]; +} +-(void)onVideoData:(QPlayerContext *)context width:(int)width height:(int)height videoType:(QVideoType)videoType buffer:(CVPixelBufferRef)buffer{ + if(self.mIsStartPush &&videoType == QVIDEO_TYPE_RGBA){ + } + if(self.mIsStartPush && videoType==QVIDEO_TYPE_YUV_420P){ + [self.mSession pushPixelBuffer:buffer completion:^(BOOL success) { + if (success) { + NSLog(@"push stream success"); + }else{ + NSLog(@"push stream false"); + } + }]; + } +} +-(void)onAudioData:(QPlayerContext *)context sampleRate:(int)sampleRate format:(QSampleFormat)format channelNum:(int)channelNum channelLayout:(QChannelLayout)channelLayout data:(NSData *)data{ + if (self.mIsStartPush) { + + AudioStreamBasicDescription audioFormatDesc; + audioFormatDesc.mSampleRate = sampleRate; + audioFormatDesc.mFormatID = kAudioFormatLinearPCM; + audioFormatDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; + audioFormatDesc.mFramesPerPacket = 1; + audioFormatDesc.mChannelsPerFrame = channelNum; + audioFormatDesc.mBitsPerChannel = 16; + audioFormatDesc.mBytesPerFrame = (audioFormatDesc.mBitsPerChannel/8) * audioFormatDesc.mChannelsPerFrame; + audioFormatDesc.mBytesPerPacket = audioFormatDesc.mBytesPerFrame * audioFormatDesc.mFramesPerPacket; + audioFormatDesc.mReserved = 0; + + AudioBuffer audioBuffer; + + audioBuffer.mNumberChannels = channelNum; + audioBuffer.mDataByteSize = (UInt32)[data length]; + + audioBuffer.mData = malloc( audioBuffer.mDataByteSize ); + + [data getBytes:audioBuffer.mData length:audioBuffer.mDataByteSize]; + [self.mSession pushAudioBuffer:&audioBuffer asbd:&audioFormatDesc completion:^(BOOL success) { + + }]; + } + +} -(void)onStateChange:(QPlayerContext *)context state:(QPlayerState)state{ if (state == QPLAYER_STATE_PREPARE) { @@ -692,7 +821,6 @@ - (void)forceOrientationLandscape:(BOOL)isLandscape { } [appDelegate application:[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:self.view.window]; - UIDeviceOrientation ori = [UIDevice currentDevice].orientation; self.mIsFlip = appDelegate.mIsFlip; if(@available(iOS 16.0,*)){ [UIViewController attemptRotationToDeviceOrientation]; @@ -792,8 +920,6 @@ - (void)configurePlayerWithConfigureModel:(PLConfigureModel *)configureModel cla } else if ([configureModel.mConfiguraKey containsString:@"Decoder"]) { [self.mPlayerView.controlHandler setDecoderType:(QPlayerDecoder)index]; - - } else if ([configureModel.mConfiguraKey containsString:@"Seek"]) { [self.mPlayerView.controlHandler setSeekMode:index]; diff --git a/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.h b/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.h index 8bc066d7..6afe6fd9 100644 --- a/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.h +++ b/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.h @@ -94,4 +94,6 @@ 返回解码类型 */ -(QPlayerDecoder)getDecoderType; + +- (void)pushStreamButtonClick:(BOOL)isSelected; @end diff --git a/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.m b/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.m index f01676cb..6327abc6 100644 --- a/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.m +++ b/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.m @@ -166,7 +166,7 @@ - (id)initWithFrame:(CGRect)frame player:(QPlayerView *)player isLiving:(BOOL)is [self.mPushStreamButton setImage:[UIImage imageNamed:@"pl_stopStream"] forState:UIControlStateSelected]; self.mPushStreamButton.tintColor = [UIColor whiteColor]; self.mPushStreamButton.hidden = YES; -// [self addSubview:self.pushStreamButton]; + [self addSubview:self.mPushStreamButton]; [self createGesture]; diff --git a/qplayer2demo/qplayer2demo/QNAppDelegate.m b/qplayer2demo/qplayer2demo/QNAppDelegate.m index f6f76c36..ebbca278 100644 --- a/qplayer2demo/qplayer2demo/QNAppDelegate.m +++ b/qplayer2demo/qplayer2demo/QNAppDelegate.m @@ -21,7 +21,8 @@ @implementation QNAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Bugly startWithAppId:@"f562ca3299"]; - + [PLStreamingEnv initEnv]; + QNHomeViewController *mainVC = [[QNHomeViewController alloc] init]; UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:mainVC]; diff --git a/qplayer2demo/qplayer2demo/QNPublicHeader.h b/qplayer2demo/qplayer2demo/QNPublicHeader.h index 5c84daf9..0da0f295 100644 --- a/qplayer2demo/qplayer2demo/QNPublicHeader.h +++ b/qplayer2demo/qplayer2demo/QNPublicHeader.h @@ -67,6 +67,7 @@ else if (@available(iOS 11.0, *)) {\ #import #import /** 第三方 **/ +#import // 轻量级布局框架 #import diff --git a/qplayer2demo/qplayer2demo/toast/QNToastView.m b/qplayer2demo/qplayer2demo/toast/QNToastView.m index 6825eccf..fa131ca6 100644 --- a/qplayer2demo/qplayer2demo/toast/QNToastView.m +++ b/qplayer2demo/qplayer2demo/toast/QNToastView.m @@ -32,17 +32,17 @@ -(void)addText:(NSString *)str{ -(void)addDecoderType:(QPlayerDecoder)type{ switch (type) { case QPLAYER_DECODER_SETTING_AUTO: - [self addView:@"解码方式:自动"]; + [self addView:@"设置的解码类型:自动"]; break; case QPLAYER_DECODER_SETTING_HARDWARE_PRIORITY: - [self addView:@"解码方式:硬解"]; + [self addView:@"设置的解码类型:硬解优先"]; break; case QPLAYER_DECODER_SETTING_SOFT_PRIORITY: - [self addView:@"解码方式:软解"]; + [self addView:@"设置的解码类型:软解优先"]; break; default: - [self addView:@"解码方式:NULL"]; + [self addView:@"设置的解码类型:NULL"]; break; } } From f70e4e4ae03a8c6d48a996dfb23bb2210eaeff3f Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Wed, 3 Jan 2024 11:51:28 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=8E=89=E5=AF=B9=20mi?= =?UTF-8?q?ku=20=E7=9A=84=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 540d9f3525d4be60e7020aae529fa56e909e5c9f) --- .../qplayer2demo/ShortVideo/QNCellPlayerViewController.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qplayer2demo/qplayer2demo/ShortVideo/QNCellPlayerViewController.m b/qplayer2demo/qplayer2demo/ShortVideo/QNCellPlayerViewController.m index 68437939..20d098b3 100644 --- a/qplayer2demo/qplayer2demo/ShortVideo/QNCellPlayerViewController.m +++ b/qplayer2demo/qplayer2demo/ShortVideo/QNCellPlayerViewController.m @@ -135,10 +135,11 @@ - (void)viewDidLoad { //获取 streamElements 字段数据 for (NSDictionary *elDic in dic[@"streamElements"]) { NSString * urlstr = [ [NSString stringWithFormat:@"%@",[elDic valueForKey:@"url"]] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; - NSURL * url = [[[QNMikuClientManager sharedInstance] getMikuClient] makeProxyURL:urlstr]; + //注释掉即不使用 miku +// NSURL * url = [[[QNMikuClientManager sharedInstance] getMikuClient] makeProxyURL:urlstr]; [modleBuilder addStreamElementWithUserType:[NSString stringWithFormat:@"%@",[elDic valueForKey:@"userType"]] urlType: [NSString stringWithFormat:@"%@",[elDic valueForKey:@"urlType"]].intValue - url: [url absoluteString] + url: urlstr quality: [NSString stringWithFormat:@"%@",[elDic valueForKey:@"quality"]].intValue isSelected:[NSString stringWithFormat:@"%@",[elDic valueForKey:@"isSelected"]].intValue == 0?NO : YES backupUrl: [NSString stringWithFormat:@"%@",[elDic valueForKey:@"backupUrl"]] From 44f5cfa484dc9b08856984ea8ac635e30898b8e1 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Wed, 10 Jan 2024 16:52:17 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E4=B8=8A=E6=8A=9B=E8=AE=BE=E7=BD=AE=E5=AD=97=E6=AE=B5=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=95=B0=E6=8D=AE=E4=B8=8A=E6=8A=9Bnsdata?= =?UTF-8?q?=E8=BD=ACbuffer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qplayer2demo.xcodeproj/project.pbxproj | 40 ++++- qplayer2demo/qplayer2demo/Info.plist | 2 + .../NSDataToCVPixelBufferRefHelper.h | 18 +++ .../NSDataToCVPixelBufferRefHelper.m | 96 ++++++++++++ .../LongVideo/QNPlayerViewController.m | 145 ++++++++++++++---- .../qplayer2demo/PlayerSettings/QDataHandle.m | 6 +- .../qplayer2demo/Public/QNPlayerMaskView.m | 14 +- .../Public/view/QNChangePlayerView.h | 5 +- .../Public/view/QNPlayerSettingsView.m | 18 ++- 9 files changed, 301 insertions(+), 43 deletions(-) create mode 100644 qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.h create mode 100644 qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.m diff --git a/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj b/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj index ada878d0..eeb640a0 100644 --- a/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj +++ b/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj @@ -71,6 +71,9 @@ 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 */; }; + BA0FD0B42B43B822009C9390 /* qplayer2_core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA0FD0B32B43B822009C9390 /* qplayer2_core.framework */; }; + BA0FD0B52B43B822009C9390 /* qplayer2_core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BA0FD0B32B43B822009C9390 /* qplayer2_core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BA0FD0BB2B480048009C9390 /* NSDataToCVPixelBufferRefHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BA0FD0BA2B480048009C9390 /* NSDataToCVPixelBufferRefHelper.m */; }; BA3E5B032A8CB85F00032327 /* MikuDelivery.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA3E5B012A8CB85800032327 /* MikuDelivery.framework */; }; BA3E5B042A8CB85F00032327 /* MikuDelivery.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BA3E5B012A8CB85800032327 /* MikuDelivery.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BA67ABB02A6FE23000BE06FA /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA67ABAF2A6FE22F00BE06FA /* CoreAudio.framework */; }; @@ -81,6 +84,8 @@ BA78EDAA2A89FAFE001DE624 /* QNShortVideoPlayerViewCache.m in Sources */ = {isa = PBXBuildFile; fileRef = BA78EDA92A89FAFE001DE624 /* QNShortVideoPlayerViewCache.m */; }; BA78EDB02A8A205A001DE624 /* QNMikuClientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BA78EDAF2A8A205A001DE624 /* QNMikuClientManager.m */; }; BA8977B62A6E52EB007C1F84 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA8977B52A6E52EB007C1F84 /* CoreMedia.framework */; }; + BAD4CFDE2AD642B900D7F9FC /* qplayer2demoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = BAD4CFDD2AD642B900D7F9FC /* qplayer2demoUITests.m */; }; + BAD4CFE02AD642B900D7F9FC /* qplayer2demoUITestsLaunchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BAD4CFDF2AD642B900D7F9FC /* qplayer2demoUITestsLaunchTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -101,6 +106,7 @@ dstSubfolderSpec = 10; files = ( BA3E5B042A8CB85F00032327 /* MikuDelivery.framework in Embed Frameworks */, + BA0FD0B52B43B822009C9390 /* qplayer2_core.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -200,8 +206,10 @@ 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 = ""; }; + BA0FD0B32B43B822009C9390 /* qplayer2_core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = qplayer2_core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BA0FD0B92B480048009C9390 /* NSDataToCVPixelBufferRefHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSDataToCVPixelBufferRefHelper.h; sourceTree = ""; }; + BA0FD0BA2B480048009C9390 /* NSDataToCVPixelBufferRefHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSDataToCVPixelBufferRefHelper.m; sourceTree = ""; }; BA1855D32A5C08BC009BD367 /* qplayer2_core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = qplayer2_core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BA322DA62B2AAEAD009DAA1F /* qplayer2_core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = qplayer2_core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BA3E5B012A8CB85800032327 /* MikuDelivery.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MikuDelivery.framework; sourceTree = ""; }; BA40090E2AC565DF00059A15 /* qplayer2_core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = qplayer2_core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BA44EB8E2A936A2700AE54A8 /* qplayer2_core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = qplayer2_core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -225,6 +233,8 @@ BAAD19762A65450000DB96FF /* qplayer2_core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = qplayer2_core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BAC57C682AB84F7C006F2F8D /* qplayer2_core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = qplayer2_core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BAD4CFDB2AD642B900D7F9FC /* qplayer2demoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = qplayer2demoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BAD4CFDD2AD642B900D7F9FC /* qplayer2demoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = qplayer2demoUITests.m; sourceTree = ""; }; + BAD4CFDF2AD642B900D7F9FC /* qplayer2demoUITestsLaunchTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = qplayer2demoUITestsLaunchTests.m; sourceTree = ""; }; BAD5A6FE2AAEE93000EC706A /* qplayer2_core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = qplayer2_core.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CA90B80F289A7AF60087543B /* qplayer2demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = qplayer2demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -243,6 +253,7 @@ files = ( 50B1F3D5289BBF7A0074E02D /* Foundation.framework in Frameworks */, BA3E5B032A8CB85F00032327 /* MikuDelivery.framework in Frameworks */, + BA0FD0B42B43B822009C9390 /* qplayer2_core.framework in Frameworks */, BA67ABB02A6FE23000BE06FA /* CoreAudio.framework in Frameworks */, 50387155292CA3410000C4DD /* CoreMotion.framework in Frameworks */, 616D0BA391829A738EA77F6B /* libPods-qplayer2demo.a in Frameworks */, @@ -435,6 +446,8 @@ 50B1F447289CC62D0074E02D /* QNPlayerViewController.m */, 50B1F455289CC62D0074E02D /* QNURLListTableViewCell.h */, 50B1F437289CC62D0074E02D /* QNURLListTableViewCell.m */, + BA0FD0B92B480048009C9390 /* NSDataToCVPixelBufferRefHelper.h */, + BA0FD0BA2B480048009C9390 /* NSDataToCVPixelBufferRefHelper.m */, ); path = LongVideo; sourceTree = ""; @@ -463,10 +476,20 @@ path = MikuDelivery; sourceTree = ""; }; + BAD4CFDC2AD642B900D7F9FC /* qplayer2demoUITests */ = { + isa = PBXGroup; + children = ( + BAD4CFDD2AD642B900D7F9FC /* qplayer2demoUITests.m */, + BAD4CFDF2AD642B900D7F9FC /* qplayer2demoUITestsLaunchTests.m */, + ); + path = qplayer2demoUITests; + sourceTree = ""; + }; CA90B806289A7AF60087543B = { isa = PBXGroup; children = ( 50B1F41C289CC62D0074E02D /* qplayer2demo */, + BAD4CFDC2AD642B900D7F9FC /* qplayer2demoUITests */, CA90B810289A7AF60087543B /* Products */, CA90BF8B289B846C0087543B /* Frameworks */, 36EE3C23CAAEDEE976CDECB3 /* Pods */, @@ -485,7 +508,7 @@ CA90BF8B289B846C0087543B /* Frameworks */ = { isa = PBXGroup; children = ( - BA322DA62B2AAEAD009DAA1F /* qplayer2_core.framework */, + BA0FD0B32B43B822009C9390 /* qplayer2_core.framework */, BA4823782AD3A7AB009F43AA /* qplayer2_core.framework */, BA40090E2AC565DF00059A15 /* qplayer2_core.framework */, BAC57C682AB84F7C006F2F8D /* qplayer2_core.framework */, @@ -536,7 +559,7 @@ CA90B80C289A7AF60087543B /* Frameworks */, CA90B80D289A7AF60087543B /* Resources */, BA576B1A2A8B1537008BD482 /* Embed Frameworks */, - 5D10B73D1234589B8D8AA9DA /* [CP] Embed Pods Frameworks */, + 6861C2044A02A2E0D4C87A38 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -641,7 +664,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 5D10B73D1234589B8D8AA9DA /* [CP] Embed Pods Frameworks */ = { + 6861C2044A02A2E0D4C87A38 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -687,6 +710,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + BAD4CFE02AD642B900D7F9FC /* qplayer2demoUITestsLaunchTests.m in Sources */, + BAD4CFDE2AD642B900D7F9FC /* qplayer2demoUITests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -698,6 +723,7 @@ 50B1F48F289CC62D0074E02D /* QNURLListTableViewCell.m in Sources */, 5076C95E2906AD7C007763E7 /* QNCellPlayerTableViewCell.m in Sources */, BA78EDB02A8A205A001DE624 /* QNMikuClientManager.m in Sources */, + BA0FD0BB2B480048009C9390 /* NSDataToCVPixelBufferRefHelper.m in Sources */, 50B1F487289CC62D0074E02D /* QNConfigSegTableViewCell.m in Sources */, 50B1F4A0289CC62D0074E02D /* QNScanViewController.m in Sources */, BA78EDA42A8632B4001DE624 /* QNPlayItemManager.m in Sources */, @@ -938,7 +964,6 @@ "$(inherited)", "\"${PODS_ROOT}/Headers/Public\"", "\"${PODS_ROOT}/Headers/Public/Masonry\"", - "\"$(SRCROOT)/../../qplayer2-core/dependency/qplayer-ffmpeg\"", "\"$(SRCROOT)/qplayer2demo\"", "\"$(SRCROOT)/qplayer2demo/MikuDelivery.framework\"", ); @@ -958,7 +983,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.4; + MARKETING_VERSION = 1.4.0; PODS_PODFILE_DIR_PATH = "${SRCROOT}/."; PODS_ROOT = "${SRCROOT}/Pods"; PRODUCT_BUNDLE_IDENTIFIER = com.qiniu.qplayer2demo; @@ -1000,7 +1025,6 @@ "$(inherited)", "\"${PODS_ROOT}/Headers/Public\"", "\"${PODS_ROOT}/Headers/Public/Masonry\"", - "\"$(SRCROOT)/../../qplayer2-core/dependency/qplayer-ffmpeg\"", "\"$(SRCROOT)/qplayer2demo\"", "\"$(SRCROOT)/qplayer2demo/MikuDelivery.framework\"", ); @@ -1020,7 +1044,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.4; + MARKETING_VERSION = 1.4.0; PODS_PODFILE_DIR_PATH = "${SRCROOT}/."; PODS_ROOT = "${SRCROOT}/Pods"; PRODUCT_BUNDLE_IDENTIFIER = com.qiniu.qplayer2demo; diff --git a/qplayer2demo/qplayer2demo/Info.plist b/qplayer2demo/qplayer2demo/Info.plist index aee69383..a99b4219 100644 --- a/qplayer2demo/qplayer2demo/Info.plist +++ b/qplayer2demo/qplayer2demo/Info.plist @@ -2,6 +2,8 @@ + NSPhotoLibraryAddUsageDescription + NSLocationAlwaysAndWhenInUseUsageDescription 是否允许使用网络 NSLocalNetworkUsageDescription diff --git a/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.h b/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.h new file mode 100644 index 00000000..c23dfe9a --- /dev/null +++ b/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.h @@ -0,0 +1,18 @@ +// +// NSDataToCVPixelBufferRefHelper.h +// qplayer2demo +// +// Created by Dynasty Dream on 2024/1/5. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDataToCVPixelBufferRefHelper : NSObject + ++(CVPixelBufferRef) NSDataToCVPixelBufferRef:(NSData *)pixeldata height:(int)height width:(int)width type:(QVideoType)type; ++(void)ClearDataFile; +@end + +NS_ASSUME_NONNULL_END diff --git a/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.m b/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.m new file mode 100644 index 00000000..c48777b6 --- /dev/null +++ b/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.m @@ -0,0 +1,96 @@ +// +// NSDataToCVPixelBufferRefHelper.m +// qplayer2demo +// +// Created by Dynasty Dream on 2024/1/5. +// + +#import "NSDataToCVPixelBufferRefHelper.h" +#define RGBA_FILE_NAME @"rgba_data.bin" +@implementation NSDataToCVPixelBufferRefHelper + ++(CVPixelBufferRef) NSDataToCVPixelBufferRef:(NSData *)pixeldata height:(int)height width:(int)width type:(QVideoType)type{ + // 创建CVPixelBufferRef + uint32_t size = (uint32_t)pixeldata.length; + uint8_t* pdata = (uint8_t*)pixeldata.bytes; + CVPixelBufferRef pixelBuffer = NULL; + CVReturn status; + if(type == QVIDEO_TYPE_YUV_420P){ + //420p + status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_420YpCbCr8Planar, NULL, &pixelBuffer); + + if (status != kCVReturnSuccess) { + NSLog(@"Unable to create pixel buffer"); + }else{ + + // 锁定pixel buffer的基地址 + CVPixelBufferLockBaseAddress(pixelBuffer, 0); + + // 获取pixel buffer的Y和UV平面基地址 + uint8_t *baseAddressY = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); + uint8_t *baseAddressU = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); + uint8_t *baseAddressV = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2); + memcpy(baseAddressY, pdata, width * height); + memcpy(baseAddressU, pdata + width * height, width * height / 4); + + memcpy(baseAddressV, pdata + (width * height)*5/4, width * height / 4); + + // 解锁pixel buffer的基地址 + CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); + + } + }else if (type == QVIDEO_TYPE_NV12){ + //nv12 + status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBuffer); + // 锁定pixel buffer的基地址 + CVPixelBufferLockBaseAddress(pixelBuffer, 0); + // 获取 pixel buffer 的 Y 平面和 UV 平面基地址 + uint8_t *baseAddressY = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); + uint8_t *baseAddressUV = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); + + // 将图像数据复制到 Y 平面 + memcpy(baseAddressY, pdata, width * height); + + // 将图像数据复制到 UV 平面(NV12 格式) + size_t uvPlaneSize = width * height / 2; + uint8_t *sourceUV = pdata + width * height; + uint8_t *destinationUV = baseAddressUV; + + for (size_t i = 0; i < uvPlaneSize; i++) { + *destinationUV++ = *sourceUV++; + } + // 解锁pixel buffer的基地址 + CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); + + } + else if (type == QVIDEO_TYPE_RGBA){ + //rgba + // 创建 CVPixelBuffer 的属性字典 + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths firstObject]; + NSString *filePath = [documentsDirectory stringByAppendingPathComponent:RGBA_FILE_NAME]; + BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath]; + if (!fileExists) { + [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]; + } + NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; + [fileHandle seekToEndOfFile]; + [fileHandle writeData:pixeldata]; + [fileHandle closeFile]; + return nil; + + }else{ + return nil; + } + return pixelBuffer; +} ++(void)ClearDataFile{ + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths firstObject]; + NSString *filePath = [documentsDirectory stringByAppendingPathComponent:RGBA_FILE_NAME]; + BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath]; + if(fileExists){ + [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; + } +} +@end diff --git a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m index 5df1661d..f7ac76e2 100644 --- a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m +++ b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m @@ -23,8 +23,9 @@ #import #import #import - - +#import "NSDataToCVPixelBufferRefHelper.h" +#import +#import #define PL_PLAYER_VIDEO_ROOT_FOLDER @"PLPlayerFloder" #define GET_PL_PLAYER_VIDEO_FOLDER(folderName) [PL_PLAYER_VIDEO_ROOT_FOLDER stringByAppendingPathComponent:folderName] #define PL_PLAYER_VIDEO_REVERSER GET_PL_PLAYER_VIDEO_FOLDER(@"PLPlayerCacheFile") @@ -225,30 +226,12 @@ - (void)viewDidLoad { self.mSubtitleLabel.lineBreakMode = NSLineBreakByWordWrapping; [self.mPlayerView addSubview:self.mSubtitleLabel]; - NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.mSubtitleLabel - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationLessThanOrEqual - toItem:self.mPlayerView - attribute:NSLayoutAttributeWidth - multiplier:1.0 - constant:0.0]; + NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.mSubtitleLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.mPlayerView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]; + + NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self.mSubtitleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.mPlayerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]; + + NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.mSubtitleLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.mPlayerView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-70.0]; - NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:self.mSubtitleLabel - attribute:NSLayoutAttributeCenterX - relatedBy:NSLayoutRelationEqual - toItem:self.mPlayerView - attribute:NSLayoutAttributeCenterX - multiplier:1.0 - constant:0.0]; - - NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.mSubtitleLabel - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.mPlayerView - attribute:NSLayoutAttributeBottom - multiplier:1.0 - constant:-70.0]; - [NSLayoutConstraint activateConstraints:@[widthConstraint, centerXConstraint, bottomConstraint]]; [self addPlayerMaskView]; @@ -281,6 +264,7 @@ -(void)pushStreamButtonClick:(BOOL)isSelected{ [self.mPlayerView.controlHandler addPlayerVideoDataListener:self]; [self.mPlayerView.controlHandler addPlayerAudioDataListener:self]; + [NSDataToCVPixelBufferRefHelper ClearDataFile]; }]; }else{ [self.mSession stop]; @@ -414,7 +398,8 @@ - (void)setUpPlayer:(NSArray*)models { } [self.mPlayerView.controlHandler playMediaModel:model startPos:[[QDataHandle shareInstance] getConfiguraPostion]]; - + +// [self.mPlayerView.controlHandler setVideoDataType:QVIDEO_TYPE_NV12]; } #pragma mark - PlayerListenerDelegate @@ -531,18 +516,115 @@ -(void)onVideoDecodeByType:(QPlayerContext *)context Type:(QPlayerDecoderType)ty } [self.mToastView addText:text]; } --(void)onVideoData:(QPlayerContext *)context width:(int)width height:(int)height videoType:(QVideoType)videoType buffer:(CVPixelBufferRef)buffer{ +//n 用于限制文件写入次数的,此处仅写入100帧。发布前要删除该内容,不限制会导致文件过大 发生crash +int n = 0; +-(void)onVideoData:(QPlayerContext *)context width:(int)width height:(int)height videoType:(QVideoType)videoType buffer:(NSData *)buffer{ if(self.mIsStartPush &&videoType == QVIDEO_TYPE_RGBA){ - + if(n>100){ + return; + } + n++; + CVPixelBufferRef piexel = [NSDataToCVPixelBufferRefHelper NSDataToCVPixelBufferRef:buffer height:height width:width type:videoType]; + if(piexel != nil){ + CFRelease(piexel); + } } - if(self.mIsStartPush && videoType==QVIDEO_TYPE_YUV_420P){ - [self.mSession pushPixelBuffer:buffer completion:^(BOOL success) { + if(self.mIsStartPush && videoType == QVIDEO_TYPE_NV12){ + CVPixelBufferRef piexel = [NSDataToCVPixelBufferRefHelper NSDataToCVPixelBufferRef:buffer height:height width:width type:videoType]; + [self.mSession pushPixelBuffer:piexel completion:^(BOOL success) { if (success) { NSLog(@"push stream success"); }else{ NSLog(@"push stream false"); } }]; + if(piexel != nil){ + CFRelease(piexel); + } + } + if(self.mIsStartPush && videoType==QVIDEO_TYPE_YUV_420P){ + CVPixelBufferRef piexel = [NSDataToCVPixelBufferRefHelper NSDataToCVPixelBufferRef:buffer height:height width:width type:videoType]; + CVPixelBufferLockBaseAddress(piexel, 0); + + // Y 分量 + uint8_t *yData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 0); + size_t yBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 0); + + // U 分量 + uint8_t *uData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 1); + size_t uBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 1); + + // V 分量 + uint8_t *vData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 2); + size_t vBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 2); + + // 图像宽度和高度 + size_t width = CVPixelBufferGetWidth(piexel); + size_t height = CVPixelBufferGetHeight(piexel); + + // 创建 YUV 数据的 RGB 数据 + size_t rgbBytesPerRow = width * 4; // RGBA 格式 + uint8_t *rgbData = (uint8_t *)malloc(rgbBytesPerRow * height); + memset(rgbData, 0, rgbBytesPerRow * height); + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + uint8_t y = yData[i * yBytesPerRow + j]; + uint8_t u = uData[(i / 2) * uBytesPerRow + (j / 2)]; + uint8_t v = vData[(i / 2) * vBytesPerRow + (j / 2)]; + + int32_t r = (int32_t)(y + 1.4075 * (v - 128)); + int32_t g = (int32_t)(y - 0.3455 * (u - 128) - 0.7169 * (v - 128)); + int32_t b = (int32_t)(y + 1.779 * (u - 128)); + + r = MIN(MAX(0, r), 255); + g = MIN(MAX(0, g), 255); + b = MIN(MAX(0, b), 255); + + rgbData[i * rgbBytesPerRow + j * 4] = (uint8_t)r; + rgbData[i * rgbBytesPerRow + j * 4 + 1] = (uint8_t)g; + rgbData[i * rgbBytesPerRow + j * 4 + 2] = (uint8_t)b; + rgbData[i * rgbBytesPerRow + j * 4 + 3] = 255; // 不透明度设置为255 + } + } + + // 创建 RGB 数据的 CGDataProviderRef + CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbData, rgbBytesPerRow * height, NULL); + + // 创建 RGB 的位图上下文 + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast; + CGImageRef rgbImageRef = CGImageCreate(width, height, 8, 32, rgbBytesPerRow, colorSpace, bitmapInfo, dataProvider, NULL, NO, kCGRenderingIntentDefault); + + // 创建 UIImage + UIImage *image = [UIImage imageWithCGImage:rgbImageRef]; + + // 释放资源 + CVPixelBufferUnlockBaseAddress(piexel, 0); + CGColorSpaceRelease(colorSpace); + CGDataProviderRelease(dataProvider); + CGImageRelease(rgbImageRef); + free(rgbData); + + // 将 UIImage 保存到相册 + [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ + [PHAssetChangeRequest creationRequestForAssetFromImage:image]; + } completionHandler:^(BOOL success, NSError *error) { + if (success) { + NSLog(@"Image saved to photo library"); + } else { + NSLog(@"Error saving image to photo library: %@", error); + } + }]; +// [self.mSession pushPixelBuffer:piexel completion:^(BOOL success) { +// if (success) { +// NSLog(@"push stream success"); +// }else{ +// NSLog(@"push stream false"); +// } +// }]; + if(piexel != nil){ + CFRelease(piexel); + } } } -(void)onAudioData:(QPlayerContext *)context sampleRate:(int)sampleRate format:(QSampleFormat)format channelNum:(int)channelNum channelLayout:(QChannelLayout)channelLayout data:(NSData *)data{ @@ -956,6 +1038,9 @@ - (void)configurePlayerWithConfigureModel:(PLConfigureModel *)configureModel cla else if ([configureModel.mConfiguraKey containsString:@"清晰度切换"]){ self.mImmediatelyType =(int)index; } + else if ([configureModel.mConfiguraKey containsString:@"video 回调数据类型"]){ + [self.mPlayerView.controlHandler setVideoDataType:(QVideoType)(index+1)]; + } else if ([configureModel.mConfiguraKey containsString:@"字幕"]){ [self.mPlayerView.controlHandler setSubtitleEnable:index==0?NO:YES]; if(index == 1 ){ diff --git a/qplayer2demo/qplayer2demo/PlayerSettings/QDataHandle.m b/qplayer2demo/qplayer2demo/PlayerSettings/QDataHandle.m index dfdc58b7..5dc3865a 100644 --- a/qplayer2demo/qplayer2demo/PlayerSettings/QDataHandle.m +++ b/qplayer2demo/qplayer2demo/PlayerSettings/QDataHandle.m @@ -34,7 +34,7 @@ - (void)showPlayerConfiguration { NSUserDefaults *userdafault = [NSUserDefaults standardUserDefaults]; NSArray *dataArray = [userdafault objectForKey:@"PLPlayer_settings"]; - NSMutableArray *piliOptionNameArray = [NSMutableArray arrayWithArray:@[@"播放起始 (ms)",@"Decoder", @"Seek",@"Start Action",@"Render ratio",@"播放速度",@"色盲模式",@"鉴权",@"SEI",@"后台播放",@"清晰度切换",@"字幕"]]; + NSMutableArray *piliOptionNameArray = [NSMutableArray arrayWithArray:@[@"播放起始 (ms)",@"Decoder", @"Seek",@"Start Action",@"Render ratio",@"播放速度",@"色盲模式",@"鉴权",@"SEI",@"后台播放",@"清晰度切换",@"字幕",@"video 回调数据类型"]]; if (dataArray.count != 0 ) { NSMutableArray *array = [NSMutableArray array]; for (NSData *data in dataArray) { @@ -97,7 +97,9 @@ -(NSDictionary *)getDefult:(NSString *)key{ }else if([key isEqual:@"清晰度切换"]){ return @{@"清晰度切换":@[@"立即切换",@"无缝切换",@"直播立即点播无缝"], @"default":@2}; }else if([key isEqual:@"字幕"]){ - return @{@"字幕":@[@"关闭",@"中文",@"英文"], @"default":@0}; + return @{@"字幕":@[@"关闭",@"中文",@"英文"], @"default":@0}; + }else if([key isEqual:@"video 回调数据类型"]){ + return @{@"video 回调数据类型":@[@"YUV420p",@"NV12"], @"default":@0}; }else{ NSLog(@"读取PLPlayerOption数据出错"); return nil; diff --git a/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.m b/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.m index 6327abc6..ba87adee 100644 --- a/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.m +++ b/qplayer2demo/qplayer2demo/Public/QNPlayerMaskView.m @@ -236,7 +236,6 @@ - (id)initWithFrame:(CGRect)frame player:(QPlayerView *)player isLiving:(BOOL)is [[QDataHandle shareInstance] setSelConfiguraKey:@"后台播放" selIndex:1]; } } - else if (800 <= type && type <= 802){ if(weakSelf.mDelegate != nil && [weakSelf.mDelegate respondsToSelector:@selector(setImmediately:)]){ [weakSelf.mDelegate setImmediately:(int)(type-800)]; @@ -257,6 +256,16 @@ - (id)initWithFrame:(CGRect)frame player:(QPlayerView *)player isLiving:(BOOL)is [[QDataHandle shareInstance] setSelConfiguraKey:@"字幕" selIndex:(int)(type-900)]; + }else if (1000 <= type && type <= 1002){ + if(weakSelf.mDelegate != nil && [weakSelf.mDelegate respondsToSelector:@selector(setImmediately:)]){ + if(type == 1000){ + [weakSelf.mPlayer.controlHandler setVideoDataType:QVIDEO_TYPE_YUV_420P]; + }else if(type == 1001){ + [weakSelf.mPlayer.controlHandler setVideoDataType:QVIDEO_TYPE_NV12]; + } + } + [[QDataHandle shareInstance] setSelConfiguraKey:@"video 回调数据类型" selIndex:(int)(type-1000)]; + } if (startPosition) { @@ -389,6 +398,9 @@ - (void)configurePlayerWithConfigureModel:(PLConfigureModel *)configureModel cla }else if ([configureModel.mConfiguraKey containsString:@"字幕"]) { [_mSettingView setChangeDefault:(ChangeButtonType)(index+900)]; + }else if ([configureModel.mConfiguraKey containsString:@"video 回调数据类型"]) { + [_mSettingView setChangeDefault:(ChangeButtonType)(index+1000)]; + } } diff --git a/qplayer2demo/qplayer2demo/Public/view/QNChangePlayerView.h b/qplayer2demo/qplayer2demo/Public/view/QNChangePlayerView.h index 5cabd02e..81c5a6e2 100644 --- a/qplayer2demo/qplayer2demo/Public/view/QNChangePlayerView.h +++ b/qplayer2demo/qplayer2demo/Public/view/QNChangePlayerView.h @@ -42,7 +42,10 @@ typedef NS_ENUM(NSInteger, ChangeButtonType){ BUTTON_TYPE_SUBTITLE_CLOSE = 900, //关闭字幕 BUTTON_TYPE_SUBTITLE_CHINESE, //中文字幕 - BUTTON_TYPE_SUBTITLE_ENGLISH //英文字幕 + BUTTON_TYPE_SUBTITLE_ENGLISH, //英文字幕 + + BUTTON_TYPE_VIDEO_DATA_YUV420P = 1000, //YUV420p + BUTTON_TYPE_VIDEO_DATA_NV12 //NV12 }; NS_ASSUME_NONNULL_BEGIN diff --git a/qplayer2demo/qplayer2demo/Public/view/QNPlayerSettingsView.m b/qplayer2demo/qplayer2demo/Public/view/QNPlayerSettingsView.m index 71237201..bf98e42e 100644 --- a/qplayer2demo/qplayer2demo/Public/view/QNPlayerSettingsView.m +++ b/qplayer2demo/qplayer2demo/Public/view/QNPlayerSettingsView.m @@ -21,6 +21,7 @@ @implementation QNPlayerSettingsView{ QNChangePlayerView *mBackgroundPlayPlayerView; QNChangePlayerView *mImmediatelyPlayerView; QNChangePlayerView *mSubtitlePlayerView; + QNChangePlayerView *mVideoDataTypePlayerView; void (^changePlayerViewCallback)(ChangeButtonType type , NSString * startPosition,BOOL selected); void (^speedViewCallback)(SpeedUIButtonType type); } @@ -121,6 +122,10 @@ -(void)setChangeDefault:(ChangeButtonType)type{ case BUTTON_TYPE_SUBTITLE_ENGLISH: [mSubtitlePlayerView setDefault:type]; break; + case BUTTON_TYPE_VIDEO_DATA_NV12: + case BUTTON_TYPE_VIDEO_DATA_YUV420P: + [mVideoDataTypePlayerView setDefault:type]; + break; default: NSLog(@"设置出错"); break; @@ -150,7 +155,7 @@ -(void)SpeedButtonClick:(UIButton*)btn{ } -(void)addScrollView:(CGRect)frame{ // self = [[UIScrollView alloc]initWithFrame:frame]; - self.contentSize = CGSizeMake(frame.size.width, 1170); + self.contentSize = CGSizeMake(frame.size.width, 1290); // self.backgroundColor = [UIColor clearColor]; self.userInteractionEnabled = YES; self.scrollEnabled = YES; @@ -215,6 +220,9 @@ -(void)addScrollView:(CGRect)frame{ [self addSubtitleView:CGRectMake(0, 1065, 350, 90)]; + [self addLine:CGRectMake(5, 1163, self.frame.size.width, 2)]; + + [self addVideoDataType:CGRectMake(0, 1165, 350, 90)]; } -(void)addImmediately:(CGRect)frame{ @@ -348,6 +356,14 @@ -(void)addSubtitleView:(CGRect)frame{ [mSubtitlePlayerView setDefault:BUTTON_TYPE_SUBTITLE_CLOSE]; [self addSubview:mSubtitlePlayerView]; } +-(void)addVideoDataType:(CGRect)frame{ + mVideoDataTypePlayerView = [[QNChangePlayerView alloc]initWithFrame:frame backgroudColor:[UIColor clearColor]]; + [mVideoDataTypePlayerView setTitleLabelText:@"video 回调数据类型" frame:CGRectMake(10, 10, 120, 30) textColor:[UIColor whiteColor]]; + [mVideoDataTypePlayerView addButtonText:@"YUV420p" frame:CGRectMake(10, 50, 100, 20) type:BUTTON_TYPE_VIDEO_DATA_YUV420P target:self selector:@selector(changePlayerViewClick:) selectorTag:@selector(changePlayerViewClickTag:)]; + [mVideoDataTypePlayerView addButtonText:@"NV12" frame:CGRectMake(165, 50, 100, 20) type:BUTTON_TYPE_VIDEO_DATA_NV12 target:self selector:@selector(changePlayerViewClick:) selectorTag:@selector(changePlayerViewClickTag:)]; + [mVideoDataTypePlayerView setDefault:BUTTON_TYPE_VIDEO_DATA_YUV420P]; + [self addSubview:mVideoDataTypePlayerView]; +} -(void)addRender:(CGRect)frame{ mStretchPlayerView = [[QNChangePlayerView alloc]initWithFrame:frame backgroudColor:[UIColor clearColor]]; From 089d5986e00f19940a112914305a042611a01f91 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Wed, 28 Feb 2024 17:37:17 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8E=A8=E6=B5=81?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E5=AE=BD=E9=AB=98=E5=92=8C=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E5=AE=BD=E9=AB=98=E4=B8=8D=E5=90=8C=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 97d8ff69e6c391e82aa9a47b1ddd942423a361c0) --- .../LongVideo/QNPlayerViewController.m | 190 ++++++++++-------- 1 file changed, 109 insertions(+), 81 deletions(-) diff --git a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m index f7ac76e2..dbe70133 100644 --- a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m +++ b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m @@ -94,6 +94,8 @@ @interface QNPlayerViewController () @property (nonatomic, strong) NSString *mSEIString; @property (nonatomic, assign) BOOL mIsStartPush; +@property (nonatomic, assign) int mVideoHeight; +@property (nonatomic, assign) int mVideoWidth; @property (nonatomic, strong) PLStreamingSession *mSession; @end @@ -115,6 +117,8 @@ - (void)viewWillAppear:(BOOL)animated { QNAppDelegate *appDelegate = (QNAppDelegate *)[UIApplication sharedApplication].delegate; self.mIsStartPush = false; self.mScanClick = NO; + self.mVideoWidth = 0; + self.mVideoHeight = 0; if (appDelegate.mIsFlip) { [self.navigationController setNavigationBarHidden:YES animated:NO]; } else{ @@ -250,7 +254,7 @@ -(void)setPLStream{ //默认配置 PLVideoStreamingConfiguration *videoStreamingConfiguration = [PLVideoStreamingConfiguration defaultConfiguration]; PLAudioStreamingConfiguration *audioStreamingConfiguration = [PLAudioStreamingConfiguration defaultConfiguration]; - videoStreamingConfiguration.videoSize =CGSizeMake(1080, 720); + videoStreamingConfiguration.videoSize =CGSizeMake(1920/2, 1080/2); self.mSession = [[PLStreamingSession alloc] initWithVideoStreamingConfiguration:videoStreamingConfiguration audioStreamingConfiguration:audioStreamingConfiguration stream:nil]; } @@ -278,6 +282,7 @@ - (void)streamStateAlert:(PLStreamStartStateFeedback)feedback { switch (feedback) { case PLStreamStartStateSuccess: NSLog(@"成功开始推流!"); + self.mIsStartPush = true; [weakSelf.mToastView addText:@"成功开始推流!"]; break; case PLStreamStartStateSessionUnknownError: @@ -517,13 +522,36 @@ -(void)onVideoDecodeByType:(QPlayerContext *)context Type:(QPlayerDecoderType)ty [self.mToastView addText:text]; } //n 用于限制文件写入次数的,此处仅写入100帧。发布前要删除该内容,不限制会导致文件过大 发生crash -int n = 0; -(void)onVideoData:(QPlayerContext *)context width:(int)width height:(int)height videoType:(QVideoType)videoType buffer:(NSData *)buffer{ - if(self.mIsStartPush &&videoType == QVIDEO_TYPE_RGBA){ - if(n>100){ - return; + + if (self.mIsStartPush && self.mVideoWidth != 0 && self.mVideoHeight != 0 && (self.mVideoHeight != height || self.mVideoWidth != width)) { + if (self.mSession != nil) { + [self.mSession stop]; + [self.mSession destroy]; + self.mSession = nil; + } + self.mVideoHeight = height; + self.mVideoWidth = width; + //推流端视频编码参数要求最大值为 width:1280 height:720 故超过该值的等比例缩小 + while (self.mVideoWidth > 1280||self.mVideoHeight>720) { + self.mVideoWidth = self.mVideoWidth/2; + self.mVideoHeight = self.mVideoHeight/2; } - n++; + self.mIsStartPush = false; + PLVideoStreamingConfiguration *videoStreamingConfiguration = [PLVideoStreamingConfiguration defaultConfiguration]; + videoStreamingConfiguration.videoSize =CGSizeMake(self.mVideoWidth, self.mVideoHeight); + PLAudioStreamingConfiguration *audioStreamingConfiguration = [PLAudioStreamingConfiguration defaultConfiguration]; + self.mSession = [[PLStreamingSession alloc] initWithVideoStreamingConfiguration:videoStreamingConfiguration audioStreamingConfiguration:audioStreamingConfiguration stream:nil]; + __weak typeof(self) weakSelf = self; + NSURL *url = [NSURL URLWithString:PL_PUSH_STREAMING_URL]; + [self.mSession startWithPushURL:url feedback:^(PLStreamStartStateFeedback feedback) { + [weakSelf streamStateAlert:feedback]; + [NSDataToCVPixelBufferRefHelper ClearDataFile]; + }]; + } + self.mVideoHeight = height; + self.mVideoWidth = width; + if(self.mIsStartPush &&videoType == QVIDEO_TYPE_RGBA){ CVPixelBufferRef piexel = [NSDataToCVPixelBufferRefHelper NSDataToCVPixelBufferRef:buffer height:height width:width type:videoType]; if(piexel != nil){ CFRelease(piexel); @@ -544,84 +572,84 @@ -(void)onVideoData:(QPlayerContext *)context width:(int)width height:(int)height } if(self.mIsStartPush && videoType==QVIDEO_TYPE_YUV_420P){ CVPixelBufferRef piexel = [NSDataToCVPixelBufferRefHelper NSDataToCVPixelBufferRef:buffer height:height width:width type:videoType]; - CVPixelBufferLockBaseAddress(piexel, 0); - - // Y 分量 - uint8_t *yData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 0); - size_t yBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 0); - - // U 分量 - uint8_t *uData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 1); - size_t uBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 1); - - // V 分量 - uint8_t *vData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 2); - size_t vBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 2); - - // 图像宽度和高度 - size_t width = CVPixelBufferGetWidth(piexel); - size_t height = CVPixelBufferGetHeight(piexel); - - // 创建 YUV 数据的 RGB 数据 - size_t rgbBytesPerRow = width * 4; // RGBA 格式 - uint8_t *rgbData = (uint8_t *)malloc(rgbBytesPerRow * height); - memset(rgbData, 0, rgbBytesPerRow * height); - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++) { - uint8_t y = yData[i * yBytesPerRow + j]; - uint8_t u = uData[(i / 2) * uBytesPerRow + (j / 2)]; - uint8_t v = vData[(i / 2) * vBytesPerRow + (j / 2)]; - - int32_t r = (int32_t)(y + 1.4075 * (v - 128)); - int32_t g = (int32_t)(y - 0.3455 * (u - 128) - 0.7169 * (v - 128)); - int32_t b = (int32_t)(y + 1.779 * (u - 128)); - - r = MIN(MAX(0, r), 255); - g = MIN(MAX(0, g), 255); - b = MIN(MAX(0, b), 255); - - rgbData[i * rgbBytesPerRow + j * 4] = (uint8_t)r; - rgbData[i * rgbBytesPerRow + j * 4 + 1] = (uint8_t)g; - rgbData[i * rgbBytesPerRow + j * 4 + 2] = (uint8_t)b; - rgbData[i * rgbBytesPerRow + j * 4 + 3] = 255; // 不透明度设置为255 - } - } - - // 创建 RGB 数据的 CGDataProviderRef - CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbData, rgbBytesPerRow * height, NULL); - - // 创建 RGB 的位图上下文 - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast; - CGImageRef rgbImageRef = CGImageCreate(width, height, 8, 32, rgbBytesPerRow, colorSpace, bitmapInfo, dataProvider, NULL, NO, kCGRenderingIntentDefault); - - // 创建 UIImage - UIImage *image = [UIImage imageWithCGImage:rgbImageRef]; - - // 释放资源 - CVPixelBufferUnlockBaseAddress(piexel, 0); - CGColorSpaceRelease(colorSpace); - CGDataProviderRelease(dataProvider); - CGImageRelease(rgbImageRef); - free(rgbData); - - // 将 UIImage 保存到相册 - [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ - [PHAssetChangeRequest creationRequestForAssetFromImage:image]; - } completionHandler:^(BOOL success, NSError *error) { - if (success) { - NSLog(@"Image saved to photo library"); - } else { - NSLog(@"Error saving image to photo library: %@", error); - } - }]; -// [self.mSession pushPixelBuffer:piexel completion:^(BOOL success) { +// CVPixelBufferLockBaseAddress(piexel, 0); +// +// // Y 分量 +// uint8_t *yData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 0); +// size_t yBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 0); +// +// // U 分量 +// uint8_t *uData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 1); +// size_t uBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 1); +// +// // V 分量 +// uint8_t *vData = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(piexel, 2); +// size_t vBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(piexel, 2); +// +// // 图像宽度和高度 +// size_t width = CVPixelBufferGetWidth(piexel); +// size_t height = CVPixelBufferGetHeight(piexel); +// +// // 创建 YUV 数据的 RGB 数据 +// size_t rgbBytesPerRow = width * 4; // RGBA 格式 +// uint8_t *rgbData = (uint8_t *)malloc(rgbBytesPerRow * height); +// memset(rgbData, 0, rgbBytesPerRow * height); +// for (int i = 0; i < height; i++) { +// for (int j = 0; j < width; j++) { +// uint8_t y = yData[i * yBytesPerRow + j]; +// uint8_t u = uData[(i / 2) * uBytesPerRow + (j / 2)]; +// uint8_t v = vData[(i / 2) * vBytesPerRow + (j / 2)]; +// +// int32_t r = (int32_t)(y + 1.4075 * (v - 128)); +// int32_t g = (int32_t)(y - 0.3455 * (u - 128) - 0.7169 * (v - 128)); +// int32_t b = (int32_t)(y + 1.779 * (u - 128)); +// +// r = MIN(MAX(0, r), 255); +// g = MIN(MAX(0, g), 255); +// b = MIN(MAX(0, b), 255); +// +// rgbData[i * rgbBytesPerRow + j * 4] = (uint8_t)r; +// rgbData[i * rgbBytesPerRow + j * 4 + 1] = (uint8_t)g; +// rgbData[i * rgbBytesPerRow + j * 4 + 2] = (uint8_t)b; +// rgbData[i * rgbBytesPerRow + j * 4 + 3] = 255; // 不透明度设置为255 +// } +// } +// +// // 创建 RGB 数据的 CGDataProviderRef +// CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbData, rgbBytesPerRow * height, NULL); +// +// // 创建 RGB 的位图上下文 +// CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); +// CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast; +// CGImageRef rgbImageRef = CGImageCreate(width, height, 8, 32, rgbBytesPerRow, colorSpace, bitmapInfo, dataProvider, NULL, NO, kCGRenderingIntentDefault); +// +// // 创建 UIImage +// UIImage *image = [UIImage imageWithCGImage:rgbImageRef]; +// +// // 释放资源 +// CVPixelBufferUnlockBaseAddress(piexel, 0); +// CGColorSpaceRelease(colorSpace); +// CGDataProviderRelease(dataProvider); +// CGImageRelease(rgbImageRef); +// free(rgbData); +// +// // 将 UIImage 保存到相册 +// [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ +// [PHAssetChangeRequest creationRequestForAssetFromImage:image]; +// } completionHandler:^(BOOL success, NSError *error) { // if (success) { -// NSLog(@"push stream success"); -// }else{ -// NSLog(@"push stream false"); +// NSLog(@"Image saved to photo library"); +// } else { +// NSLog(@"Error saving image to photo library: %@", error); // } // }]; + [self.mSession pushPixelBuffer:piexel completion:^(BOOL success) { + if (success) { + NSLog(@"push stream success"); + }else{ + NSLog(@"push stream false"); + } + }]; if(piexel != nil){ CFRelease(piexel); } From 161bf25da6a660e6ae64320c8524076b98229032 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Wed, 28 Feb 2024 17:40:09 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E5=80=BC=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 15be0e9b92aaf98506cfaeff44a303ba14ef278a) --- .../qplayer2demo/LongVideo/QNPlayerViewController.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m index dbe70133..008f3a72 100644 --- a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m +++ b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m @@ -117,8 +117,6 @@ - (void)viewWillAppear:(BOOL)animated { QNAppDelegate *appDelegate = (QNAppDelegate *)[UIApplication sharedApplication].delegate; self.mIsStartPush = false; self.mScanClick = NO; - self.mVideoWidth = 0; - self.mVideoHeight = 0; if (appDelegate.mIsFlip) { [self.navigationController setNavigationBarHidden:YES animated:NO]; } else{ @@ -254,7 +252,9 @@ -(void)setPLStream{ //默认配置 PLVideoStreamingConfiguration *videoStreamingConfiguration = [PLVideoStreamingConfiguration defaultConfiguration]; PLAudioStreamingConfiguration *audioStreamingConfiguration = [PLAudioStreamingConfiguration defaultConfiguration]; - videoStreamingConfiguration.videoSize =CGSizeMake(1920/2, 1080/2); + self.mVideoHeight = 1080/2; + self.mVideoWidth = 1920/2; + videoStreamingConfiguration.videoSize =CGSizeMake(self.mVideoWidth, self.mVideoHeight); self.mSession = [[PLStreamingSession alloc] initWithVideoStreamingConfiguration:videoStreamingConfiguration audioStreamingConfiguration:audioStreamingConfiguration stream:nil]; } From e63c7a9486cfb04c9173af7dcd6beb1c676170d7 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Fri, 1 Mar 2024 18:28:52 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E4=B8=8A=E6=8A=9B=E8=BD=ACrtc=E6=8E=A8=E6=B5=81=E8=8A=B1?= =?UTF-8?q?=E5=B1=8F=E5=92=8C=E8=A7=86=E9=A2=91=E8=A3=81=E5=89=AA=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit ee88597fcc1261a379acb7d83ea4cd89809754ea) --- .../NSDataToCVPixelBufferRefHelper.m | 126 +++++++++++++----- .../LongVideo/QNPlayerViewController.m | 22 ++- 2 files changed, 110 insertions(+), 38 deletions(-) diff --git a/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.m b/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.m index c48777b6..d1dbe43f 100644 --- a/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.m +++ b/qplayer2demo/qplayer2demo/LongVideo/NSDataToCVPixelBufferRefHelper.m @@ -15,51 +15,115 @@ +(CVPixelBufferRef) NSDataToCVPixelBufferRef:(NSData *)pixeldata height:(int)hei uint8_t* pdata = (uint8_t*)pixeldata.bytes; CVPixelBufferRef pixelBuffer = NULL; CVReturn status; + + int inner_width = 0; + int inner_height = 0; + if (width*1.0 / height*1.0 != 16.0/9 || width*1.0 / height*1.0 != 1.0 || width*1.0 / height*1.0 != 4.0/3 || width%32 != 0 || height%32 != 0 ) { + inner_width = 1920; + inner_height = 1080; + }else{ + inner_width = width; + inner_height = height; + } if(type == QVIDEO_TYPE_YUV_420P){ //420p - status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_420YpCbCr8Planar, NULL, &pixelBuffer); + status = CVPixelBufferCreate(kCFAllocatorDefault, inner_width, inner_height, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBuffer); + if (status != kCVReturnSuccess) { NSLog(@"Unable to create pixel buffer"); }else{ - - // 锁定pixel buffer的基地址 + CVPixelBufferLockBaseAddress(pixelBuffer, 0); - - // 获取pixel buffer的Y和UV平面基地址 - uint8_t *baseAddressY = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); - uint8_t *baseAddressU = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); - uint8_t *baseAddressV = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2); - memcpy(baseAddressY, pdata, width * height); - memcpy(baseAddressU, pdata + width * height, width * height / 4); - - memcpy(baseAddressV, pdata + (width * height)*5/4, width * height / 4); - - // 解锁pixel buffer的基地址 + unsigned char *yDestPlane = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); + for (int i = 0, k = 0; i < inner_height; i++) { + for (int j = 0; j< inner_width; j++) { + if (inner_width > width && j >= width) { + if (i >= height) { + yDestPlane[k++] = pdata[height*width]; + continue; + } + yDestPlane[k++] = pdata[i*width + width]; + continue; + } + if (inner_height > height && i >= height) { + + yDestPlane[k++] = pdata[height * width]; + continue; + } + yDestPlane[k++] = pdata[i*width + j]; + } + } + unsigned char *uvDestPlane = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); + for (int i = 0, k = 0; i < inner_height / 2; i++) { + for (int j = 0; j< inner_width / 2; j ++) { + if (inner_width > width && j >= width/2) { + if (i > height/2) { + + uvDestPlane[k++] = pdata[height *width/4 + width * height]; + uvDestPlane[k++] = pdata[height *width/4 + width * height * 5 / 4]; + }else{ + uvDestPlane[k++] = pdata[i *width/2 + width/2 + width * height]; + uvDestPlane[k++] = pdata[i *width/2 + width/2 + width * height * 5 / 4]; + + } + continue; + } + if (inner_height > height && i >= height/2) { + + uvDestPlane[k++] = pdata[height *width/4 + width * height]; + uvDestPlane[k++] = pdata[height *width/4 + width * height * 5 / 4]; + continue; + } + uvDestPlane[k++] = pdata[i *width/2 + j + width * height]; + uvDestPlane[k++] = pdata[i *width/2 + j + width * height * 5 / 4]; + } + } CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); } }else if (type == QVIDEO_TYPE_NV12){ //nv12 - status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBuffer); - // 锁定pixel buffer的基地址 + status = CVPixelBufferCreate(kCFAllocatorDefault, inner_width, inner_height, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBuffer); CVPixelBufferLockBaseAddress(pixelBuffer, 0); - // 获取 pixel buffer 的 Y 平面和 UV 平面基地址 - uint8_t *baseAddressY = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); - uint8_t *baseAddressUV = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); - - // 将图像数据复制到 Y 平面 - memcpy(baseAddressY, pdata, width * height); - - // 将图像数据复制到 UV 平面(NV12 格式) - size_t uvPlaneSize = width * height / 2; - uint8_t *sourceUV = pdata + width * height; - uint8_t *destinationUV = baseAddressUV; - - for (size_t i = 0; i < uvPlaneSize; i++) { - *destinationUV++ = *sourceUV++; + unsigned char *yDestPlane = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); + for (int i = 0, k = 0; i < inner_height; i++) { + for (int j = 0; j< inner_width; j++) { + if (inner_width > width && j >= width) { + if (i >= height) { + yDestPlane[k++] = pdata[height*width]; + continue; + } + yDestPlane[k++] = pdata[i*width + width]; + continue; + } + if (inner_height > height && i >= height) { + + yDestPlane[k++] = pdata[height * width]; + continue; + } + yDestPlane[k++] = pdata[i*width + j]; + } + } + unsigned char *uvDestPlane = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); + for (int i = 0, k = 0; i < inner_height/2; i++) { + for (int j = 0; j< inner_width; j ++) { + if (inner_width > width && j >= width) { + if (i > height/2) { + uvDestPlane[k++] = pdata[height *width/2 + width * height]; + }else{ + uvDestPlane[k++] = pdata[i *width + width + width * height]; + } + continue; + } + if (inner_height > height && i >= height/2) { + + uvDestPlane[k++] = pdata[height *width/2 + width * height]; + continue; + } + uvDestPlane[k++] = pdata[i *width + j + width * height]; + } } - // 解锁pixel buffer的基地址 CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); } diff --git a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m index 008f3a72..450656f4 100644 --- a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m +++ b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m @@ -521,18 +521,25 @@ -(void)onVideoDecodeByType:(QPlayerContext *)context Type:(QPlayerDecoderType)ty } [self.mToastView addText:text]; } -//n 用于限制文件写入次数的,此处仅写入100帧。发布前要删除该内容,不限制会导致文件过大 发生crash -(void)onVideoData:(QPlayerContext *)context width:(int)width height:(int)height videoType:(QVideoType)videoType buffer:(NSData *)buffer{ - - if (self.mIsStartPush && self.mVideoWidth != 0 && self.mVideoHeight != 0 && (self.mVideoHeight != height || self.mVideoWidth != width)) { + int inner_width = 0; + int inner_height = 0; + if (width*1.0 / height*1.0 != 16.0/9 || width*1.0 / height*1.0 != 1.0 || width*1.0 / height*1.0 != 4.0/3 || width%32 != 0 || height%32 != 0 ) { + inner_width = 1920; + inner_height = 1080; + }else{ + inner_width = width; + inner_height = height; + } + if (self.mIsStartPush && self.mVideoWidth != 0 && self.mVideoHeight != 0 && (self.mVideoHeight != inner_height || self.mVideoWidth != inner_width)) { if (self.mSession != nil) { [self.mSession stop]; [self.mSession destroy]; self.mSession = nil; } - self.mVideoHeight = height; - self.mVideoWidth = width; //推流端视频编码参数要求最大值为 width:1280 height:720 故超过该值的等比例缩小 + self.mVideoHeight = inner_height; + self.mVideoWidth = inner_width; while (self.mVideoWidth > 1280||self.mVideoHeight>720) { self.mVideoWidth = self.mVideoWidth/2; self.mVideoHeight = self.mVideoHeight/2; @@ -548,9 +555,10 @@ -(void)onVideoData:(QPlayerContext *)context width:(int)width height:(int)height [weakSelf streamStateAlert:feedback]; [NSDataToCVPixelBufferRefHelper ClearDataFile]; }]; + } - self.mVideoHeight = height; - self.mVideoWidth = width; + self.mVideoHeight = inner_height; + self.mVideoWidth = inner_width; if(self.mIsStartPush &&videoType == QVIDEO_TYPE_RGBA){ CVPixelBufferRef piexel = [NSDataToCVPixelBufferRefHelper NSDataToCVPixelBufferRef:buffer height:height width:width type:videoType]; if(piexel != nil){ From e5d64bf2879c4e8ef982d9e91abcb15fe7258da5 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Mon, 4 Mar 2024 10:26:11 +0800 Subject: [PATCH 09/13] =?UTF-8?q?=E5=A4=87=E6=B3=A8=E6=8B=89=E6=B5=81?= =?UTF-8?q?=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 4236ef53a9cb0c49f8fad52b482b4a5269b29a98) --- qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m | 1 + 1 file changed, 1 insertion(+) diff --git a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m index 450656f4..1a311c9c 100644 --- a/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m +++ b/qplayer2demo/qplayer2demo/LongVideo/QNPlayerViewController.m @@ -30,6 +30,7 @@ #define GET_PL_PLAYER_VIDEO_FOLDER(folderName) [PL_PLAYER_VIDEO_ROOT_FOLDER stringByAppendingPathComponent:folderName] #define PL_PLAYER_VIDEO_REVERSER GET_PL_PLAYER_VIDEO_FOLDER(@"PLPlayerCacheFile") #define PL_PUSH_STREAMING_URL @"rtmp://pili-publish.qnsdk.com/sdk-live/1234" +// 拉流地址 rtmp://pili-rtmp.qnsdk.com/sdk-live/1234 @interface QNPlayerViewController () < UITableViewDelegate, From a962ba37f42535e5d48fc384c5974a6ad0b706b5 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Wed, 6 Mar 2024 15:07:27 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme=E5=92=8CreleaseN?= =?UTF-8?q?ode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit a397e5a2cb8e8dc2ecf46d840bdc77652597f12f) --- README.md | 10 +++++++--- ReleaseNote/ReleaseNote-1.4.0.md | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 ReleaseNote/ReleaseNote-1.4.0.md diff --git a/README.md b/README.md index bc323bc5..06fbffb9 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,11 @@ Qplayer2是一款跨平台的播放器SDK,除了基础的播放器能力外, Platform | Build Status -------- | ------------ Android | https://github.com/pili-engineering/QPlayer2-Android - IOS | Last Version: 1.3.4 - Windows | 敬请期待 + IOS | Last Version: 1.4.0 + Windows | https://github.com/pili-engineering/QPlayer2-Windows + HarmonyOS NEXT | 敬请期待 Mac | 敬请期待 + Linux | 敬请期待 ### qplayer2-core 功能列表 | 能力 | 亮点 | 备注 | @@ -35,6 +37,8 @@ Qplayer2是一款跨平台的播放器SDK,除了基础的播放器能力外, | 静音播放 | 针对视频的静音,不影响系统声音 | | | 字幕 | 支持srt字幕文件加载并根据时间回调当前时间的文案给上层 | | | DRM | 支持HLS 私有加密/通用加密 2种方式,支持 MP4 CENC-AES-CTR 加密 | | +| 音视频数据上抛 | 适用于业务层需要获取当前播放的音视频数据的场景(比如推流等) | | +| 本地重建时间轴 | 以真实的流逝时间为准,避免某些视频时间轴错乱导致播放过程中异常 | | @@ -45,7 +49,7 @@ Qplayer2是一款跨平台的播放器SDK,除了基础的播放器能力外, ##### 引入依赖 ```groovy -pod 'qplayer2-core', '1.3.4' +pod 'qplayer2-core', '1.4.0' ``` diff --git a/ReleaseNote/ReleaseNote-1.4.0.md b/ReleaseNote/ReleaseNote-1.4.0.md new file mode 100644 index 00000000..ab288f21 --- /dev/null +++ b/ReleaseNote/ReleaseNote-1.4.0.md @@ -0,0 +1,15 @@ +# 1.4.0 ReleaseNote + +- #### 能力 + + - 新增数据上抛 +- 支持播放器端重建直播时间轴 + - 支持 Windows 平台 + +- #### 修复问题 + + - 修复播放完成后,无法截图的问题 + + - 修复多次 seek ,然后暂停播放后截图非常慢的问题 + + From 07208d2e2d0ac3d35b86060b580f9a28fed490b7 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Thu, 7 Mar 2024 14:23:38 +0800 Subject: [PATCH 11/13] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7=E6=94=B9?= =?UTF-8?q?=E4=B8=BA1.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qplayer2-core.podspec | 2 +- qplayer2demo/Podfile | 4 ++-- qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/qplayer2-core.podspec b/qplayer2-core.podspec index 0e07fcc9..4fdeae65 100644 --- a/qplayer2-core.podspec +++ b/qplayer2-core.podspec @@ -11,7 +11,7 @@ Pod::Spec.new do |spec| spec.name = "qplayer2-core" - spec.version = "1.3.4" + spec.version = "1.4.0" spec.summary = "Pili iOS video player SDK, RTMP, HLS video streaming supported." diff --git a/qplayer2demo/Podfile b/qplayer2demo/Podfile index 55887175..2c1f5aaf 100644 --- a/qplayer2demo/Podfile +++ b/qplayer2demo/Podfile @@ -5,9 +5,9 @@ target 'qplayer2demo' do - #pod 'PLMediaStreamingKit/iphoneos' + pod 'PLMediaStreamingKit/iphoneos' pod 'Bugly' pod 'Masonry', '1.0.1' #pod 'qplayer2-core', :path => '../' - pod 'qplayer2-core', '1.3.4' + #pod 'qplayer2-core', '1.4.0' end diff --git a/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj b/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj index eeb640a0..553a051a 100644 --- a/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj +++ b/qplayer2demo/qplayer2demo.xcodeproj/project.pbxproj @@ -71,8 +71,6 @@ 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 */; }; - BA0FD0B42B43B822009C9390 /* qplayer2_core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA0FD0B32B43B822009C9390 /* qplayer2_core.framework */; }; - BA0FD0B52B43B822009C9390 /* qplayer2_core.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BA0FD0B32B43B822009C9390 /* qplayer2_core.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BA0FD0BB2B480048009C9390 /* NSDataToCVPixelBufferRefHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BA0FD0BA2B480048009C9390 /* NSDataToCVPixelBufferRefHelper.m */; }; BA3E5B032A8CB85F00032327 /* MikuDelivery.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA3E5B012A8CB85800032327 /* MikuDelivery.framework */; }; BA3E5B042A8CB85F00032327 /* MikuDelivery.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BA3E5B012A8CB85800032327 /* MikuDelivery.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -106,7 +104,6 @@ dstSubfolderSpec = 10; files = ( BA3E5B042A8CB85F00032327 /* MikuDelivery.framework in Embed Frameworks */, - BA0FD0B52B43B822009C9390 /* qplayer2_core.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -253,7 +250,6 @@ files = ( 50B1F3D5289BBF7A0074E02D /* Foundation.framework in Frameworks */, BA3E5B032A8CB85F00032327 /* MikuDelivery.framework in Frameworks */, - BA0FD0B42B43B822009C9390 /* qplayer2_core.framework in Frameworks */, BA67ABB02A6FE23000BE06FA /* CoreAudio.framework in Frameworks */, 50387155292CA3410000C4DD /* CoreMotion.framework in Frameworks */, 616D0BA391829A738EA77F6B /* libPods-qplayer2demo.a in Frameworks */, From d7417590b5e66844d186c573b14c5253147b5373 Mon Sep 17 00:00:00 2001 From: Mengwangchao Date: Thu, 7 Mar 2024 17:03:35 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3=20?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E8=BF=9C=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../html/Classes/QMediaItemContext.html | 2 +- .../Classes/QMediaItemControlHandler.html | 54 +-- .../html/Classes/QMediaModel.html | 45 +- .../html/Classes/QMediaModelBuilder.html | 232 +--------- .../html/Classes/QPlayerContext.html | 2 +- .../html/Classes/QPlayerControlHandler.html | 411 +----------------- .../html/Classes/QPlayerRenderHandler.html | 2 +- .../html/Classes/QPlayerView.html | 2 +- .../html/Classes/QStreamElement.html | 47 +- .../html/Constants/QLogLevel.html | 2 +- .../html/Constants/QMediaItemNotify.html | 2 +- .../html/Constants/QMediaItemState.html | 2 +- .../QPlayerAuthenticationErrorType.html | 2 +- .../html/Constants/QPlayerBlind.html | 2 +- .../html/Constants/QPlayerDecoder.html | 2 +- .../html/Constants/QPlayerDecoderType.html | 2 +- .../html/Constants/QPlayerOpenError.html | 2 +- .../html/Constants/QPlayerRenderRatio.html | 2 +- .../html/Constants/QPlayerRenderType.html | 2 +- .../html/Constants/QPlayerSeek.html | 2 +- .../html/Constants/QPlayerShootVideoType.html | 2 +- .../html/Constants/QPlayerStart.html | 2 +- .../html/Constants/QPlayerState.html | 2 +- .../html/Constants/QPlayerURLType.html | 2 +- .../QIMediaItemCommandNotAllowListener.html | 2 +- .../QIMediaItemStateChangeListener.html | 2 +- .../html/Protocols/QIPlayerAudioListener.html | 2 +- .../QIPlayerAuthenticationListener.html | 2 +- .../Protocols/QIPlayerBiteRateListener.html | 2 +- .../Protocols/QIPlayerBufferingListener.html | 2 +- .../QIPlayerCommandNotAllowListener.html | 2 +- .../Protocols/QIPlayerDownloadListener.html | 2 +- .../html/Protocols/QIPlayerFPSListener.html | 2 +- .../Protocols/QIPlayerFormatListener.html | 2 +- .../QIPlayerMediaNetworkListener.html | 2 +- .../Protocols/QIPlayerProgressListener.html | 2 +- .../Protocols/QIPlayerQualityListener.html | 2 +- .../Protocols/QIPlayerRenderListener.html | 2 +- .../Protocols/QIPlayerSEIDataListener.html | 2 +- .../html/Protocols/QIPlayerSeekListener.html | 2 +- .../Protocols/QIPlayerShootVideoListener.html | 2 +- .../html/Protocols/QIPlayerSpeedListener.html | 2 +- .../QIPlayerStateChangeListener.html | 2 +- .../QIPlayerVideoDecodeListener.html | 2 +- .../QIPlayerVideoFrameSizeChangeListener.html | 2 +- document/qplayer2-core/html/hierarchy.html | 18 +- document/qplayer2-core/html/index.html | 20 +- qplayer2demo/Podfile | 2 +- 48 files changed, 56 insertions(+), 853 deletions(-) diff --git a/document/qplayer2-core/html/Classes/QMediaItemContext.html b/document/qplayer2-core/html/Classes/QMediaItemContext.html index bb7d458d..06336815 100644 --- a/document/qplayer2-core/html/Classes/QMediaItemContext.html +++ b/document/qplayer2-core/html/Classes/QMediaItemContext.html @@ -337,7 +337,7 @@

Declared In