English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

توضيح تعقيد تحضير مشغل الفيديو في iOS

مقدمة

如果仅仅是播放视频两者的使用都非常简单,但是相比MediaPlayer,AVPlayer对于视频播放的可控制性更强一些,可以通过自定义的一些控件来实现视频的播放暂停等等。因此这里使用AVPlayer的视频播放。

视频播放器布局

首先使用xib创建CLAVPlayerView继承UIView用来承载播放器,这样我们在外部使用的时候,直接在控制器View或者Cell上添加CLAVPlayerView即可,至于播放器播放或者暂停等操作交给CLAVPlayerView来管理。下面来看一下CLAVPlayerView的结构。


CLAVPlayerView的结构

CLAVPlayerView的布局很简单,重点在于约束的添加和控件层次关系,添加约束只要自己挨个细心添加就没有问题,需要注意控件的层次关系,从上图中可以看出四个控件是分先后顺序平行添加在CLAVPlayerView上的,要注意他们的层次关系,避免相互遮挡。

视频播放器实现

布局完成之后,就是实现播放器功能,我们把播放器功能大致分为四部分来完成

一. 通过播放按钮实现视频播放。

首先CLAVPlayerView加载时需要将播放器layer添加到imageView的layer上,此时蒙版和底部工具条一定都是隐藏的,点击中间播放按钮,视频开始播放并隐藏播放按钮。因此我们需要在CLAVPlayerView的awakeFromNib方法中,在加载CLAVPlayerView时对其做一些处理。

1、初始化AVPlayer和AVPlayerLayer,并将AVPlayerLayer添加到imageView的layer上,在layoutSubviews中设置playerLayer的frame

// 初始化player 和playerLayer
self.player = [[AVPlayer alloc]init];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
// imageView上添加playerLayer
[self.imageView.layer addSublayer:self.playerLayer];
-(void)layoutSubviews
{
 [super layoutSubviews];
 self.playerLayer.frame = self.imageView.bounds;
}

2. إنشاء AVPlayerItem بناءً على URL لعرض الفيديو

NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
self.playerItem = [AVPlayerItem playerItemWithURL:url];

3. إعداد نقطة البداية ونقطة النهاية الصغيرة والكبيرة لـ Slider

// إعداد Slider
[self.progressSlider setThumbImage:[UIImage imageNamed:@"thumbImage"] forState:UIControlStateNormal];
[self.progressSlider setMaximumTrackImage:[UIImage imageNamed:@"MaximumTrackImage"] forState:UIControlStateNormal];
[self.progressSlider setMinimumTrackImage:[UIImage imageNamed:@"MinimumTrackImage"] forState:UIControlStateNormal];

4. إضافة إشارة اللمس tap لـ imageView، عند النقر على imageView يتم عرض الأداة

// إضافة إشارة اللمس لـ imageView
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
[self.imageView addGestureRecognizer:tap];

الاحتياطات:إذا كنت تستخدم xib لإضافة إشارة اللمس لـ imageView، فيجب الحصول على firstObject من العودة من loadNibNamed، لأنه هو الذي يحتوي على View من xib، إذا كنت تحصل على lastObject، فإنه يحتوي على إشارة اللمس tap، وسيتم إظهار خطأ بأن objekt tap لا يحتوي على طريقة View.

5、其他控件显示以及状态的设置

// 隐藏遮盖版
self.coverView.hidden = YES;
// 设置工具栏状态
self.toolView.alpha = 0;
self.isShowToolView = NO;
// 设置工具栏播放按钮状态
self.playOrPauseBtn.selected = NO;

这盖板只有播放完毕之后显现,点击重播之后又隐藏,因此使用hidden直接隐藏即可,而工具栏需要重复显示,并且我们为了能让工具栏的显示有动画效果,这里通过设置toolView的alpha来显示或隐藏工具栏,并通过isShowToolView来记录toolView的显示或隐藏。

6、中间播放按钮的点击

- (IBAction)playOrPauseBigBtnClick:(UIButton *)sender {
 // 隐藏中间播放按钮,工具栏播放按钮为选中状态
 sender.hidden = YES;
 self.playOrPauseBtn.selected = YES;
 // 替换播放内容
 [self.player replaceCurrentItemWithPlayerItem:self.playerItem];
 [self.player play];
 [self addProgressTimer];
}

此时,当我们点击中间播放按钮播放器就可以播放视频了。

二. 工具条的显示与隐藏

在播放状态时,当点击imageView,就会弹出底部工具条,可以查看当前播放的时间,视频总时间或进行暂停视频、全屏播放等操作。如果没有操作,工具栏会在5秒之后自动隐藏。而当未播放状态时,点击imageView和中间播放按钮效果一样,开始播放视频。

1、添加定时器,5秒钟之后隐藏底部工具条,并提供移除定时器的方法。

/** toolView显示时开始计时,5s后隐藏toolView */
-(void)addShowTime
{
 self.showTime = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(upDateToolView) userInfo:nil repeats:NO];
 [[NSRunLoop mainRunLoop]addTimer:self.showTime forMode:NSRunLoopCommonModes];
}
/** إخفاء toolView */
-(void)upDateToolView
{
 self.isShowToolView = !self.isShowToolView;
 [UIView animateWithDuration:0.5 animations:^{
  self.toolView.alpha = 0;
 };
}
/** إزالة الموقت */
-(void)removeShowTime
{
 [self.showTime invalidate];
 self.showTime = nil;
}

2- تنفيذ طريقة اللمس لimageView، يتم تقسيمها إلى عدة حالات، عند عدم تشغيل الفيديو، لا يتم عرض الشريط الأدنى للأدوات عند النقر على imageView، بل يشبه النقر على زر التشغيل في المنتصف، ويبدأ تشغيل الفيديو، أثناء تشغيل الفيديو، يتم عرض الشريط الأدنى للأدوات عند النقر على imageView، وإذا تم النقر على زر التوقف في الشريط الأدنى للأدوات، يتم التشغيل والتوقف، فإن الشريط الأدنى للأدوات لن يختفي، ويبدأ تشغيل الفيديو مرة أخرى، ويختفي الشريط الأدنى للأدوات في غضون 5 ثوانٍ

/** طريقة اللمس لimageView */
-(void)tapAction:(UITapGestureRecognizer *)tap
{
 // عند عدم اللعب، النقر على imageView يشبه النقر على زر التشغيل في المنتصف، ويبدأ تشغيل الفيديو
 if (self.player.status == AVPlayerStatusUnknown) {
  [self playOrPauseBigBtnClick:self.playOrPauseBigBtn];
  return;
 }
 // تسجيل حالة العرض أو إخفاء الشريط الأدنى للأدوات
 self.isShowToolView = !self.isShowToolView;
 // إذا كنت بحاجة إلى عرض الشريط الأدنى للأدوات، يتم إضافة حركة العرض
 if (self.isShowToolView){
  [UIView animateWithDuration:0.5 animations:^{
   self.toolView.alpha = 1;
  };
  // عند تشغيل زر التشغيل في الشريط الأدنى للأدوات، يتم إضافة الموقت، وإخفاء الشريط الأدنى للأدوات بعد 5 ثوانٍ
  if (self.playOrPauseBtn.selected) {
   [self addShowTime];
  }
 // إذا كنت بحاجة إلى إخفاء الشريط الأدنى للأدوات، قم بإزالة الموقت وإخفاء الشريط الأدنى للأدوات
 }else{
  [self removeShowTime];
  [UIView animateWithDuration:0.5 animations:^{
   self.toolView.alpha = 0;
  };
 }
}

3- يجب معالجة النقر على زر التشغيل/ال暂停 في قائمة الأدوات، عند التشغيل على حاله التوقف، يتم ضبط قيمة alpha للشريط الأدنى للأدوات على 1، وإزالة الموقت، وعند بدء تشغيل الفيديو مرة أخرى، يتم إضافة الموقت مرة أخرى للبدء في حساب الوقت، بعد 5 ثوانٍ يختفي الشريط الأدنى للأدوات. سيتم تقديم الكود بشكل مفصل في تزامن وقت اللعب، Slider مع لعب الفيديو.

ثالثًا. تزامن وقت اللعب، Slider مع لعب الفيديو

الشريط أدنى للأدوات يجب أن يتم تزامن وقت اللعب، وقت الفيديو الكلي، وتمرير Slider مع وقت لعب الفيديو.

1- إضافة timer لتشغيل الفيديو وSlider، وتكرار التحديث كل ثانية لlabel الوقت وSlider

 /** إضافة timer slider */
 -(void)addProgressTimer
 {
  self.progressTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateProgressInfo) userInfo:nil repeats:YES];
  [[NSRunLoop mainRunLoop]addTimer:self.progressTimer forMode:NSRunLoopCommonModes];
 }
 /** إزالة timer slider */
 -(void)removeProgressTimer
 {
  [self.progressTimer invalidate];
  self.progressTimer = nil;
 }
 /** تحديث slider وtimeLabel */
 - (void)updateProgressInfo
 {
 NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentTime);
  NSTimeInterval durationTime = CMTimeGetSeconds(self.player.currentItem.duration);
  self.timeLabel.text = [self timeToStringWithTimeInterval:currentTime];
  self.allTimeLabel.text = [self timeToStringWithTimeInterval:durationTime];
  self.progressSlider.value = CMTimeGetSeconds(self.player.currentTime) / CMTimeGetSeconds(self.player.currentItem.duration);
  if (self.progressSlider.value == 1) {
   [self removeProgressTimer];
   self.coverView.hidden = NO;
  } 
 }

الوقت الحالي للعب والوقت الإجمالي المكتسب نوعه CMTime، يجب تحويله إلى NSTimeInterval وتحويل الثواني إلى دقائق ووقت، وإخراج طريقة التحويل

/** تحويل وقت التشغيل والوقت الإجمالي */
-(NSString *)timeToStringWithTimeInterval:(NSTimeInterval)interval;
{
 NSInteger Min = interval / 60;
 NSInteger Sec = (NSInteger)interval % 60;
 NSString *intervalString = [NSString stringWithFormat:@"%02ld:%02ld",Min,Sec];
 return intervalString;
}

2、When the middle play button is clicked to start playing, add a timer to synchronize the play time and Slider. When clicking the pause button on the toolbar during playback, the video needs to be paused, and the timer needs to be removed. When starting to play again, add the timer and start playing.

/** Click event of the pause button on toolView */
- (IBAction)playOrPauseBtnClick:(UIButton *)sender {
 // Play state button selected is YES, pause state selected is NO.
 sender.selected = !sender.selected;
 if (!sender.selected) {
  self.toolView.alpha = 1;
  [self removeShowTime];
  [self.player pause];
  [self removeProgressTimer];
 }else{
  [self addShowTime];
  [self.player play];
  [self addProgressTimer];
 }
}

3、Slider's Drag Jump Video Play

حسب تمرير Slider يمكن أن يتم إطلاق الفيديو بحسب موضع التمرير، يجب مراقبة ثلاث مراحل: الضغط، التمرير (تغيير البيانات)، والإفراج. عند الضغط، يتم إزالة التimer، عند التمرير يتم حساب الوقت الحالي لإطلاق الفيديو وعرضه على الرسم البياني، وعند الإفراج يتم حساب الوقت الحالي وإطلاق الفيديو عند ذلك الوقت.

/** slider拖动和点击事件 */
- (IBAction)touchDownSlider:(UISlider *)sender {
 // 按下去 移除监听器
 [self removeProgressTimer];
 [self removeShowTime];
}
- (IBAction)valueChangedSlider:(UISlider *)sender {
 // 计算slider拖动的点对应的播放时间
 NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * sender.value;
 self.timeLabel.text = [self timeToStringWithTimeInterval:currentTime];
}
- (IBAction)touchUpInside:(UISlider *)sender {
 [self addProgressTimer];
 // حساب الوقت الحالي للعب المناسب للسحب في Slider
 NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * sender.value;
 // seekToTime:انتقال إلى الوقت الحالي للعب
 [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
 [self addShowTime];
}

الجزء الرابع: تنفيذ زر العرض مرة أخرى وزر العرض الكامل

1、في كل مرة يتم فيها استدعاء طريقة تحديث Slider من قبل闹ق كل ثانية، يتم التحقق مما إذا كان الفيديو يلعب حتى النهاية، ويتم عرض View القبالة، وتنفيذ زر العرض مرة أخرى، وهو في الواقع تعيين value Slider إلى 0 وإعادة استدعاء طريقة النقر على Slider عند انطلاق النقر، وإعادة تعيين الوقت الحالي للعب إلى 0، وإعادة إخفاء View القبالة، وإعادة استدعاء زر البداية المتوسط للبدء في العرض.

/** النقر على زر العرض مرة أخرى */
- (IBAction)repeatBtnClick:(UIButton *)sender {
 self.progressSlider.value = 0;
 [self touchUpInside:self.progressSlider];
 self.coverView.hidden = YES;
 [self playOrPauseBigBtnClick:self.playOrPauseBigBtn];
}

2、تنفيذ عرض الشاشة الكاملة

تتطلب عرض الشاشة الكاملة أن يقدم Moda مدير عرض الشاشة الكاملة للعب الشاشة الكاملة، يتم إنشاء مدير عرض الشاشة الكاملة CLFullViewController، وإتاحة الدوران في الاتجاهين الأيمن والأيسر، يتم تقديم مدير CLFullViewController، وإضافة CLAVPlayerView إلى View مدير CLFullViewController وتعيين الإطار، عند الخروج من الشاشة الكاملة، يتم إزالة CLFullViewController وإعادة تعيين إطار CLAVPlayerView إلى القيمة الأصلية.
CLFullViewController حيث يتم ضبط التحول والاتجاه

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
 return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
 return YES;
}

حادثة الضغط على زر العرض الكامل

/** 全屏按钮点击事件 */
- (IBAction)fullViewBtnClick:(UIButton *)sender {
 sender.selected = !sender.selected;
 [self videoplayViewSwitchOrientation:sender.selected];
}
/** 弹出全屏播放器 */
- (void)videoplayViewSwitchOrientation:(BOOL)isFull
{
 if (isFull) {
  [self.contrainerViewController presentViewController:self.fullVc animated:NO completion:^{
   [self.fullVc.view addSubview:self];
   self.center = self.fullVc.view.center;
   [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{
    self.frame = self.fullVc.view.bounds;
   }; اكتمال: nil];
  };
 } else {
  [self.fullVc dismissViewControllerAnimated:NO completion:^{
   [self.contrainerViewController.view addSubview:self];
   [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{
    self.frame = CGRectMake(0, 200, self.contrainerViewController.view.bounds.size.width, self.contrainerViewController.view.bounds.size.width * 9 / 16);
   }; اكتمال: nil];
  };
 }
}

الاحتياطات:في هذه الحالة، نحتاج إلى الحصول على مدير التحكم من الخارج لإنشاء مدير العرض الكامل، لذا أضف خاصية contrainerViewController إلى CLAVPlayerView للحصول على مدير التحكم.

التجميع البسيط

في هذه اللحظة، تم تنفيذ وظائف مشغل الفيديو الأساسية، والآن نحتاج إلى النظر في كيفية التجميع لجعل الاستخدام أكثر سهولة، في الواقع، قمنا بمعظم التجميع، وكل ما علينا القيام به هو تقديم واجهة بسيطة وسهلة الاستخدام، مما يسمح للخارج بالتعبير عن نفسه بسهولة.

1- يقدم طريقة سريعة لإنشاء مشغل

+ (instancetype)videoPlayView
{
 return [[[NSBundle mainBundle]loadNibNamed:@"CLAVPlayerView" owner:nil options:nil]lastObject];
}

2- يجب أن يتم تحديد مصدر الفيديو من الخارج، لذا نقدم خاصية urlString لاستقبال مصدر الفيديو، ثم نعيد كتابة طريقة تعيينها لتشغيل الفيديو
/** طريقة تعيين مصدر الفيديو الذي سيتم عرضه */

-(void)setUrlString:(NSString *)urlString
{
 _urlString = urlString;
 NSURL *url = [NSURL URLWithString:urlString];
 self.playerItem = [AVPlayerItem playerItemWithURL:url];
}

في هذه اللحظة، من السهل جدًا استخدام مشغل الفيديو في الخارج، لا تحتاج إلى النظر في اللógica الداخلية، فقط قم بإنشاء CLAVPlayerView بسرعة، أضفه إلى View المسيطر، أعد إعدادات إطار، ثمحدد الموارد الفيديو التي يتم عرضها.

- (void)viewDidLoad {
 [super viewDidLoad];
 [self setUpVideoPlayView]; 
 self.playView.urlString = @"http://120.25.226.186:32812/resources/videos/minion_02.mp4";
}
-(void)setUpVideoPlayView
{
 self.playView = [CLAVPlayerView videoPlayView];
 self.playView.frame = CGRectMake(0, 200, self.view.frame.size.width, self.view.frame.size.width * 9 / 16);
 self.playView.contrainerViewController = self;
 [self.view addSubview:self.playView];
}

في النهاية، يبدو مشغل الفيديو تقريبًا هكذا

الخلاصة

هناك العديد من الأماكن التي تحتاج إلى تحسين، وعدد من الوظائف لم يتم تنفيذها بعد، مثل زرين احتياطيين من نوع Button، يمكن استخدامها في المستقبل لتحميل الفيديوهات وتشغيل مفتاح التحكم في الدماء الساخنة، لم يتم تنفيذ زر المشاركة بعد انتهاء العرض أيضًا. سيتم مشاركة المزيد من المعلومات بعد تنفيذها، هذا هو نهاية مقال هذا، آمل أن تكون محتويات هذا المقال قد ساعدتكم، إذا كان لديكم أي أسئلة، يمكنكم ترك تعليقات للتفاعل.

البيان: محتويات هذا المقال تم جمعها من الإنترنت، ملكية المحتويات لصاحب الحقوق، تم جمع المحتويات من قبل المستخدمين عبر الإنترنت بشكل متعاوني وتحميلها بشكل مستقل، لا يمتلك هذا الموقع حقوق الملكية، لم يتم تعديل المحتويات بشكل يدوي، ولا يتحمل هذا الموقع أي مسؤولية قانونية ذات صلة. إذا اكتشفتم محتويات تتضمن انتهاك حقوق النسخ، مرحبًا بكم في إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال '#' بـ '@') لتقديم الشكوى، وقدموا الأدلة ذات الصلة، وسيتم حذف المحتويات المزعجة فور التحقق منها.

من المحتمل أن تود