AV Foundation 视频播放中的可视拖拽进度条

news2024/11/18 11:21:46

引言

在视频播放软件中,通过拖拽进度条来调整播放进度几乎已成为不可或缺的功能。这一功能使用户能够精确指定视频播放的时间点。近年来,视频播放器在原有的拖拽进度条基础上进行了更加人性化的性能提升,引入了可视化拖拽条。这一创新为用户提供了更直观的表示方式,使用户能够一目了然地了解视频当前的位置和期望调整到的位置。通过可视拖拽条,用户能够实现更快速的导航,无需再像以往一样依赖于对播放时间的估计。这种改进为用户提供了更加便捷和直观的操作体验。

那么它是如何实现的呢,接下来我们以播放中常见的两种展现形式来介绍一下它的实现方式。

实现原理

介绍可视拖拽条的实现原理涉及到两个比较重要的类:

AVAsset

AVAsset在整个AV Foundation框架中都是一个十分核心的类,是一个抽象的不可变类,它定义了媒体资混合呈现方式,将媒体资源的静态属性模块化成一个整体。使开发者在处理它的时候面对的就只有资源这么一个概念,而不需要考虑它是什么类型的视频或者音频资源。

AVAssetImageGenerator

AVAssetImageGenerator是AV Foundation框架中的一个工具类,它专门用来从一个AVAsset资源中提取图片。

AVAssetImageGenerator定义了两个从视频资源中读取图片的方法:

1.获取指定时间的画面资源图片。

- (nullable CGImageRef)copyCGImageAtTime:(CMTime)requestedTime actualTime:(nullable CMTime *)actualTime error:(NSError * _Nullable * _Nullable)outError;

2.获取指定时间段的一组画面资源图片。

- (void)generateCGImagesAsynchronouslyForTimes:(NSArray<NSValue *> *)requestedTimes completionHandler:(AVAssetImageGeneratorCompletionHandler)handler;

AVAssetImageGenerator既可以生成本地图片,也可以生成持续下载的资源。不过它不能从HTTP Live Stream生成图片。

代码实现

那么接下来我们就用上面两个方法来实现一下现在主流播放器的两种可视进度的功能。

一.在普通进度条的拖拽点上显示当前拖拽进度点的画面。

播放器的实现在这里面就不过多介绍了,我们将重点集中在播放器的进度条以及拖拽进度条显示当前进度的预览画面上。

1.主要组件

播放器的控制按钮我们全在THOverlayView类中来实现。

slider:是一个简易的可拖拽的视频进度条,由UISlider实现。

floatImageView:就是我们要实现的预览画面,由UIImageView实现。

2.添加组件

添加UISlider,和场景的播放器一样我们将slider添加到了底部的UIToolbar上面。

- (void)addToolBar{
    self.toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height - 44, [UIScreen mainScreen].bounds.size.width, 44)];
    [self addSubview:self.toolBar];

......    
    self.slider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width - 290, 0)];
    [self.slider addTarget:self action:@selector(showPopupUI) forControlEvents:UIControlEventValueChanged];
    [self.slider addTarget:self action:@selector(hidePopupUI) forControlEvents:UIControlEventTouchUpInside];
    [self.slider addTarget:self action:@selector(unhidePopupUI) forControlEvents:UIControlEventTouchDown];
tonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
.....    
    self.toolBar.items = @[spaceItem0,playItem,spaceItem1,currentItem,sliderItem,durationItem,spaceItem2,subtitlesItem,spaceItem3];
}

添加预览画面floatImageView,添加后先设置为隐藏状态,并且也没有设置它的位置及大小信息,因为我们会根据拖拽的进度条来设置floatImageView的位置。

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setupView];
    }
    return self;
}

- (void)setupView{
....
    [self addSubview:self.floatImageView];
}


- (UIImageView *)floatImageView {
    if (!_floatImageView) {
        _floatImageView = [[UIImageView alloc] init];
        _floatImageView.layer.masksToBounds = YES;
        _floatImageView.layer.cornerRadius = 8.0;
        _floatImageView.layer.borderWidth = 1.0;
        _floatImageView.layer.borderColor = [UIColor whiteColor].CGColor;
        _floatImageView.hidden = YES;
    }
    return _floatImageView;
}
3.显示预览

上面的slider组件有三个方法

unhidePopupUI:当手指按下slider时调用。

在此方法中,对于播放器我们应该做暂停的操作,因为接下来我们就要改变播放进度了。

而对于预览画面我们也可以让它显示出来。

//MARK:按下搓擦条
- (void)unhidePopupUI{
    //配置预览视图
    [self configFloatImageView];
    //通知播放器 暂停
    [self.delegate scrubbingDidStart];
}

showPopupUI:对应当slider的值发生改变时调用。

//MARK:搓擦条值改变
- (void)showPopupUI{

    //获取预览画面
    [self configFloatImageView];

}

hidePopupUI:当手指从slider上移除后调用。

//MARK:手指离开搓擦条
- (void)hidePopupUI{

    //隐藏预览视图
    self.floatImageView.hidden = YES;
    // 通知播放器 修改进度
    [self.delegate scrubbedToTime:self.slider.value];
    // 通知播放器 修改进度完成
    [self.delegate scrubbingDidEnd];
}

配置预览画面主要有两个需要注意的事项,配置视图的frame以及预览的内容,我们来看一下他们的实现。

- (void)configFloatImageView{
    self.floatImageView.frame = [self floatFrame];
    self.floatImageView.image = [self floatImage];
}

- (CGRect)floatFrame{
    self.floatImageView.hidden = false;
    CGRect sliderFrame = [self.slider convertRect:self.slider.bounds toView:self];
    CGFloat width = 90.0;
    CGFloat height = 60.0;
    CGFloat x = sliderFrame.origin.x + sliderFrame.size.width * (self.slider.value/self.slider.maximumValue) - width * 0.5;
    CGRect floatFrame = CGRectMake(x, CGRectGetMinY(sliderFrame) - 15.0 - height, width, height);
    return floatFrame;
}

- (UIImage *)floatImage{
    return [self.delegate generateImageWithTime:self.slider.value];
}

计算frame我们通过进度条的位置以及进度来计算出预览视图的位置。

而获取预览视图的功能,我们通过代理来实现。(获取到的CGImageRef需要及时释放)。

/// 获取时间点预览画面
- (UIImage *)generateImageWithTime:(NSTimeInterval)time {
    if (self.imageGenerator == nil) {
        self.imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:self.asset];
        self.imageGenerator.maximumSize = CGSizeMake(200.0f, 0.0f);         
    }
    CMTime ctime = CMTimeMake(time, 1);
    CMTime actualTime = kCMTimeZero;
    NSError * error;
    CGImageRef cgImage = [self.imageGenerator copyCGImageAtTime:ctime actualTime:&actualTime error:&error];
    UIImage * image = [[UIImage alloc] initWithCGImage:cgImage];
    CGImageRelease(cgImage);
    return image;
}

这样我们就实现了显示当前进度预览画面的全部功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1417731.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年CSDN年终总结:长风破浪会有时,风物长宜放眼量

目录 0 回首20231 打造垂类专栏2 个人技术成长3 首发SCI期刊4 生活中的美好5 新年新flag 0 回首2023 这是去年flag的完成情况&#xff0c;很惊喜地发现全部顺利完成了。 CSDN坚持垂类写作&#xff0c;完结机器学习和ROS机器人专栏&#xff0c;开启深度学习新篇章 粉丝数希望突…

TS学习笔记十二:项目配置

本节介绍ts项目配置相关内容&#xff0c;包括项目配置文件tsconfig.json的说明及编译选项的内容介绍。 讲解视频 TS学习笔记二十五&#xff1a;TS项目配置 B站视频 TS学习笔记二十五&#xff1a;TS项目配置 西瓜视频 https://www.ixigua.com/7327847796814709288 一、tsconf…

GUN/Linux时间同步服务之ntp配置管理

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任&#xff0c;图解仅供参考&#xff0c;请悉知&#xff01;相关配置操作是在一个全新的演示环境下进行的&#xff0c;演示环境中没有任何有价值的数据&#xff0c;但这并不代表摆在你面前的环境也是如此。生产环境…

超声波清洗机可以洗哪些东西?性价比比较高的超声波清洗机推荐

超声波清洗机现在作为一个相对来说比较方便快捷的清洁工具&#xff0c;其应用范围也是非常广泛的。无论是生活中的小物件&#xff0c;像眼镜还是耳钉这些&#xff0c;还是工业生产中的大型设备&#xff0c;超声波清洗机都能发挥出其独特的清洗效果&#xff0c;能够非常的省事的…

Docker容器引擎(4)

目录 一.搭建本地私有仓库 运行 registry 容器&#xff1a; Docker容器的重启策略如下&#xff1a; 为镜像打标签&#xff1a; 上传到私有仓库&#xff1a; 列出私有仓库的所有镜像&#xff1a; 列出私有仓库的 centos 镜像有哪些tag&#xff1a; 二.Docker--harbor私有…

【LeetCode: Z 字形变换 + 模拟】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

(28)Linux 信号保存 信号处理 不可重入函数

首先介绍几个新的概念&#xff1a; 信号递达(Delivery)&#xff1a;实际执行信号的处理动作。信号未决(Pending)&#xff1a;信号从产生到递达之间的状态。信号阻塞(Block)&#xff1a;被阻塞的信号产生时将保持在未决状态&#xff0c;直达解除对该信号的阻塞&#xff0c;才执…

Mac本上快速搭建redis服务指南

文章目录 前言1. 查看可用版本2.安装指定版本的redis3.添加redis到PATH3.1 按照执行brew install命令后输出的提示信息执行如下命令将redis添加到PATH3.2 执行命令要添加的redis环境信息生效: 4. 增加密码4.1 在文件中找到requirepass所在位置4.2 去掉注释并将requirepass值替换…

如何免费从 Android 恢复丢失/删除的数据

随着手机的广泛使用&#xff0c;手机中可以存储的数据越来越多&#xff0c;这给我们带来了便利&#xff0c;但也在一定程度上给我们带来了一些麻烦。 通常&#xff0c;手机中存储着我们的亲朋好友的联系方式、商务消息、私人聊天、音频文件、搞笑视频、生活照片等个人信息。不…

2.【Vue3】Vue 基本使用——局部使用Vue

1. 快速入门 现在需要将 “hello vue3” 这样一个字符串渲染到页面上进行展示。 这个需求并不陌生&#xff0c;可以使用原生 JS 代码完成&#xff1a; let msg"hello vue3"; document.getElementById("元素的id属性值").innerHTMLmsg;接下来学习如何使用…

SpringBoot常见错误

SpringBoot常见错误 1、SpringBoot启动时报错 错误: 找不到或无法加载主类 com.xxx.xxx.Application springboot启动时报错错误&#xff1a;找不到或无法加载主类 com.xxx.xxx.Application。 解决方法就是打开idea的控制台&#xff0c;输入以下三行命令&#xff1a; mvn cl…

使用核模型高斯过程(KMGPs)进行数据建模

核模型高斯过程(KMGPs)作为一种复杂的工具可以处理各种数据集的复杂性。他通过核函数来扩展高斯过程的传统概念。本文将深入探讨kmgp的理论基础、实际应用以及它们所面临的挑战。 核模型高斯过程是机器学习和统计学中对传统高斯过程的一种扩展。要理解kmgp&#xff0c;首先掌握…

智能小程序页面配置、运行机制及路由

页面介绍 Page 代表应用的一个页面&#xff0c;负责页面展示和交互。每个页面对应一个子目录&#xff0c;一般有多少个页面&#xff0c;就有多少个子目录。它也是一个构造函数&#xff0c;用来指定页面的初始数据、生命周期回调、事件处理函数等。 每个小程序页面一般包含以下…

FMEA:总监和架构师都在用的高可用架构分析方法

FMEA&#xff1a;总监和架构师都在用的高可用架构分析方法 记得之前准备春晚项目的时候&#xff0c;团队成员在一起过架构&#xff0c;老板最常问的问题是“这个组件挂了怎么办?有什么影响&#xff1f;”&#xff0c;我当时还在心里默默嘀咕&#xff1a;这咋都这么容易挂呢&a…

有趣的css - 好看的呼吸灯效果

整体效果 这个效果主要用 css3 的 animation 属性来实现的。 这个效果可以用作在网站的整体 Loading&#xff0c;也可以放在网站首屏当一个 banner 的背景也是非常棒的&#xff01; 代码部分 html 部分代码&#xff1a; <div class"app"><span class&quo…

什么是git,怎样下载安装?

简介&#xff1a; 应用场景&#xff1a; 应用场景&#xff1a;团队企业开发 作用&#xff1a; 安装&#xff1a; 网址&#xff1a;Git - Downloads cmd 安装&#xff1a;winget install --id Git.Git -e --source winget

Linux之中文字体安装

一、需求说明 客户系统部署要求使用指定字体&#xff0c;该字体在linux服务器上没有&#xff0c;导致系统处理相关上传文档的时候显示乱码&#xff0c;所以我们需要在linux服务器上安装指定字体。博主实验环境&#xff1a; 操作系统&#xff1a;centos7.6安装window环境下的宋…

Nginx实现反向代理负载均衡实验

实验环境&#xff1a; VM REdhat虚拟机&#xff08;192.168.87.5&#xff09;一台、VM Redhat虚拟机&#xff08;192.168.87.3&#xff09;一台、阿里云服务器&#xff08;47.93.79.92&#xff09;一台 实验要求&#xff1a;通过windows浏览器访问192.168.87.5&#xff08;虚…

MIT_线性代数笔记:线性代数常用概念及术语总结

目录 1.系数矩阵2.高斯消元法3.置换矩阵 Permutation4.逆矩阵 Inverse5.高斯-若尔当消元法6.矩阵的 LU 分解7.三角矩阵8.正定矩阵 1.系数矩阵 线性代数的基本问题就是解 n 元一次方程组。例如&#xff1a;二元一次方程组 2 x − y 0 − x 2 y 3 \begin{align*} & 2x -…

DeepSORT算法实现车辆和行人跟踪计数和是否道路违规检测(代码+教程)

DeepSORT算法是一种用于目标跟踪的算法&#xff0c;它可以对车辆和行人进行跟踪计数&#xff0c;并且可以检测是否存在道路违规行为。该算法采用深度学习技术来提取特征&#xff0c;并使用卡尔曼滤波器来估计物体的速度和位置。 DeepSORT算法通过首先使用目标检测算法来识别出…