【OC】AVPlayerLayer的学习
文章目录
- 前言
- 关于AVPlayer
- 概念及作用
- 具体方法及使用
- 关于AVPlayerLayer
- 基本概念及作用
- 具体用法
- AVPlayer与AVPlayerLayer结合实现视频播放应用
- 使用本地视频
- 使用网络请求申请视频数据
- 总结
前言
在编写类视频软件项目时,涉及到视频播放的问题,我们需要给已有的或者用网络申请拉下来的视频一个承载器,来更好的在客户端展示和播放视频。这里就要用到AVPlayer和APlayerLayer,本篇博客会简单介绍一下AVPlayer和APlayerLayer的用法以及两者之间的关系,最后会有一个具体应用的小demo。
关于AVPlayer
概念及作用
AVPlayer 是 AVFoundation 框架中的核心类,是播放控制核心,主要用于管理和控制音频或视频的播放。它抽象了播放逻辑(如时间控制、速率调整、多源切换等),但不直接处理视频渲染。
主要用途有播放控制、数据源绑定、状态监听等。
播放控制:即管理播放、暂停、跳转、速率调整(如 0.5x 慢放、2x 快进)。
数据源绑定:通过 AVPlayerItem 关联媒体数据(本地文件、网络流媒体)。
状态监听:提供播放状态、缓冲进度、错误信息等回调(如通过 KVO 或通知)。
高级功能:支持多角度视频、画中画(PiP)、后台播放等。
具体方法及使用
1.创建及初始化(这里先以本地视频为例子)
//用于获取本地视频文件的路径 URL
NSURL *videoURL = [[NSBundle mainBundle] URLForResource:@"video_test" withExtension:@"mp4"];
//参数一:视频文件的名称(不包含扩展名)
//参数二:视频文件的扩展名(如 .mp4, .mov 等)//创建一个 AVPlayerItem 对象,作为 AVPlayer 的数据源
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:videoURL];//初始化 AVPlayer 实例,绑定 AVPlayerItem 数据源
self.player = [AVPlayer playerWithPlayerItem:playerItem];
注意:在使用获取视频URL得方法时,要确保这个本地视频必须存在于项目的 Bundle 资源目录中(在 Xcode 中勾选 “Target Membership”)。

2.播放控制
[self.player play]; // 播放
[self.player pause]; // 暂停
[self.player seekToTime:kCMTimeZero]; // 跳转到开头
3.速率控制
self.player.rate = 1.0; // 正常速度(0.0~2.0,1.0为正常)
4.状态监听
// 监听播放状态(通过通知中心)
//当 AVPlayerItem 播放到结尾时,触发 AVPlayerItemDidPlayToEndTimeNotification 通知,调用 playerItemDidReachend 方法
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachend) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
//参数一:观察者对象(当前类实例),需实现 playerItemDidReachend 方法
//参数二:回调方法,播放完成时触发,需在类中实现此方法
//参数三:系统定义的通知名称,表示播放项已播放到结尾
//参数四:被监听的 AVPlayerItem 对象。仅当该对象触发通知时,才会调用回调方法
涉及到的关键属性:

关于AVPlayerLayer
基本概念及作用
AVPlayerLayer 是 CALayer 的子类,继承自 Core Animation 框架,专门用于将 AVPlayer 的视频内容渲染到屏幕上。我们刚刚在介绍AVPlayer时,提到AVPlayer并不会对视频进行渲染处理,AVPlayer 需要通过 AVPlayerLayer 渲染视频,而AVPlayerLayer 必须绑定一个 AVPlayer 实例才能工作。AVPlayer 负责播放逻辑(如控制时间、速率),AVPlayerLayer 负责视频显示(如图层效果、缩放模式)。
对视频的渲染处理是指将原始视频数据(如未经处理的视频流、图像序列或3D场景数据)转换为最终可视化图像或视频的过程。这一过程涉及多个技术环节,目的是优化视觉效果、适配播放设备或满足特定需求(如实时性、格式兼容性等)。
其主要用途有视频渲染、图层控制、硬件加速等。
视频渲染:将 AVPlayer 的解码后的视频帧绘制到图层。
图层控制:支持缩放模式(videoGravity)、旋转、裁剪、叠加其他图层(如水印)。
硬件加速:高效处理高分辨率视频,利用 GPU 加速渲染。
具体用法
1.创建与绑定
// 创建 AVPlayerLayer 并绑定到 AVPlayer
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.frame = CGRectMake(0, 100, self.view.bounds.size.width, 300);
[self.view.layer addSublayer:self.playerLayer];
2.缩放模式
// 等比例缩放(默认)
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;// 等比例填充(可能裁剪)
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;// 拉伸填充(可能变形)
self.playerLayer.videoGravity = AVLayerVideoGravityResize;
3.动态调整尺寸(这个功能笔者只是简单了解,具体实现与应用还有待完善)
// 监听屏幕旋转,调整图层尺寸
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];- (void)orientationChanged {self.playerLayer.frame = self.view.bounds;
}
其中涉及到的关键属性:

AVPlayer与AVPlayerLayer结合实现视频播放应用
使用本地视频
//VideoPlayerView.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>NS_ASSUME_NONNULL_BEGIN@interface VideoPlayerView : UIView
@property(nonatomic, strong)AVPlayerLayer *playerLayer;
@endNS_ASSUME_NONNULL_END//VideoPlayerView.m
#import "VideoPlayerView.h"@implementation VideoPlayerView+(Class)layerClass {return [AVPlayerLayer class];
}
- (AVPlayerLayer *)playerLayer {return (AVPlayerLayer *)self.layer;
}@end
//ViewController.m
#import "ViewController.h"
#import "VideoPlayerView.h"
#import "AVFoundation/AVFoundation.h"@interface ViewController ()@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) AVPlayerLayer *playerLayer;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//1.创建视频URLNSURL *videoURL = [[NSBundle mainBundle] URLForResource:@"video_test" withExtension:@"mp4"];if (videoURL) {NSLog(@"视频文件 URL: %@", videoURL);} else {NSLog(@"未找到视频文件");}//2.创建AVPlayerself.player = [AVPlayer playerWithURL:videoURL];//3.创建VideoPlayerViewVideoPlayerView *playerView = [[VideoPlayerView alloc] initWithFrame:CGRectMake(0, 200, self.view.bounds.size.width, 300)];playerView.playerLayer.player = self.player;//4.设置videoGravity(视频填充模式)playerView.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;//5.将VideoOlayerView添加到视频上[self.view addSubview:playerView];//6.开始播放[self.player play];//7.监听播放结束[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachend) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
}- (void)playerItemDidReachend {//播放结束后执行的操作NSLog(@"播放结束🎵");//如果需要循环播放,可以在这里重新设置播放时间[self.player seekToTime:kCMTimeZero];[self.player play];
}- (void)dealloc {//移除监听[[NSNotificationCenter defaultCenter] removeObserver:self];
}@end
效果图(可以看到视频播放结束后会自动重播):

使用网络请求申请视频数据
#import "ViewController.h"
#import <AFNetworking/AFNetworking.h>
#import "AVFoundation/AVFoundation.h"
#import <WebKit/WebKit.h>@interface ViewController ()<WKNavigationDelegate>
@property (nonatomic, strong) WKWebView *webView;@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) AVPlayerLayer *playerLayer;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.// 创建 WKWebViewself.webView = [[WKWebView alloc] initWithFrame:self.view.bounds];self.webView.navigationDelegate = self;[self.view addSubview:self.webView];// 加载抖音短链接NSURL *shortURL = [NSURL URLWithString:@"https://v.douyin.com/L4FJNR3/"];NSURLRequest *request = [NSURLRequest requestWithURL:shortURL];[self.webView loadRequest:request];
}- (void)parseDouyinShortURL:(NSString *)shortURLString {// 创建 AFHTTPSessionManagerAFHTTPSessionManager *manager = [AFHTTPSessionManager manager];manager.responseSerializer = [AFHTTPResponseSerializer serializer]; // 设置响应为原始数据// 发送 GET 请求[manager GET:shortURLString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {// 获取重定向后的 URLNSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;if (httpResponse.statusCode == 302) {NSString *redirectURLString = httpResponse.allHeaderFields[@"Location"];if (redirectURLString) {NSLog(@"重定向 URL: %@", redirectURLString);[self parseDouyinPageWithURL:redirectURLString];} else {NSLog(@"未找到重定向 URL");}} else {NSLog(@"未找到重定向 URL");}} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {NSLog(@"解析短链接失败: %@", error.localizedDescription);}];
}- (void)parseDouyinPageWithURL:(NSString *)pageURLString {// 创建 AFHTTPSessionManagerAFHTTPSessionManager *manager = [AFHTTPSessionManager manager];manager.responseSerializer = [AFHTTPResponseSerializer serializer]; // 设置响应为原始数据// 发送 GET 请求[manager GET:pageURLString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {// 解析 HTMLNSString *htmlString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];if (htmlString) {// 使用正则表达式提取视频 URLNSString *pattern = @"\"playAddr\":\"(.*?)\"";NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];NSTextCheckingResult *result = [regex firstMatchInString:htmlString options:0 range:NSMakeRange(0, htmlString.length)];if (result) {NSString *videoURLString = [htmlString substringWithRange:[result rangeAtIndex:1]];videoURLString = [videoURLString stringByReplacingOccurrencesOfString:@"\\" withString:@""];NSLog(@"提取的视频 URL: %@", videoURLString);// 播放视频[self playVideoWithURL:[NSURL URLWithString:videoURLString]];} else {NSLog(@"未找到视频 URL");}} else {NSLog(@"HTML 解析失败");}} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {NSLog(@"加载页面失败: %@", error.localizedDescription);}];
}- (void)playVideoWithURL:(NSURL *)videoURL {dispatch_async(dispatch_get_main_queue(), ^{// 创建 AVPlayerself.player = [AVPlayer playerWithURL:videoURL];// 创建 AVPlayerLayerself.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];self.playerLayer.frame = CGRectMake(0, 0, self.view.bounds.size.width, 300);self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;// 将 AVPlayerLayer 添加到视图上[self.view.layer addSublayer:self.playerLayer];// 开始播放[self.player play];//监听播放结束[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachend) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];});
}- (void)playerItemDidReachend {//播放结束后执行的操作NSLog(@"播放结束🎵");//如果需要循环播放,可以在这里重新设置播放时间[self.player seekToTime:kCMTimeZero];[self.player play];
}- (void)dealloc {//移除监听[[NSNotificationCenter defaultCenter] removeObserver:self];
}@end
效果图:

总结
AVPlayer 和 AVPlayerLayer 是 AVFoundation 框架中用于音视频播放的两个核心类。
参数间的协作关系:
videoURL → playerItem:AVPlayerItem 通过 videoURL 加载媒体数据。
playerItem → player:AVPlayer 通过 playerItem 获取媒体数据并控制播放。
player → AVPlayerLayer:AVPlayerLayer 需绑定 AVPlayer 实例才能渲染视频。

推荐文章:iOS AVPlayer的使用
相关文章:
【OC】AVPlayerLayer的学习
文章目录 前言关于AVPlayer概念及作用具体方法及使用 关于AVPlayerLayer基本概念及作用具体用法 AVPlayer与AVPlayerLayer结合实现视频播放应用使用本地视频使用网络请求申请视频数据 总结 前言 在编写类视频软件项目时,涉及到视频播放的问题,我们需要给…...
C++笔记-list
list即是我们之前学的链表,这篇主要还是讲解list的底层实现,前面会讲一些list区别于前面string和vector的一些接口以及它们的注意事项。 一.list的基本使用 和之前的string,vector一样,有很多之前见过的一些接口,经过…...
k8s报错kubelet.go:2461] “Error getting node“ err=“node \“k8s-master\“ not found“
问题 首先最初问题: [rootk8s-master ~]# kubectl get pods -owide --all-namespaces The connection to the server 192.168.2.129:6443 was refused - did you specify the right host or port?检查kubelet状态 查看kubelet status报找不到master节点 [rootk8…...
open webui 介绍 是一个可扩展、功能丰富且用户友好的本地部署 AI 平台,支持完全离线运行。
AI MCP 系列 AgentGPT-01-入门介绍 Browser-use 是连接你的AI代理与浏览器的最简单方式 AI MCP(大模型上下文)-01-入门介绍 AI MCP(大模型上下文)-02-awesome-mcp-servers 精选的 MCP 服务器 AI MCP(大模型上下文)-03-open webui 介绍 是一个可扩展、功能丰富且用户友好的…...
使用cursor进行原型图设计
1.下载cursor 2.模式设置: 模型使用claude-3.7-sonnet的think模式 3.引导词模板: 我想要开发一个中高考英语口语考试的模拟考试系统,我需要将上面的这个应用输出成高保真的原型图设计。请考虑以下的规范: 用户体验࿱…...
极狐GitLab 登录限制如何设置?
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 登录限制 (BASIC SELF) 您可以使用登录限制自定义 Web 界面以及基于 HTTP(S) 的 Git 的身份验证限制。 设置 要访问登录限…...
设计模式之工厂模式(factory pattern):在商品对象创建系统中的应用
目录 一、设计思路 1. 简单工厂模式 2. 工厂方法模式 3. 抽象工厂模式 二、UML类图(PlantUML格式) 1.简单工厂模式 2.工厂方法模式 3.抽象工厂模式 三、实现过程与结果 1. 简单工厂模式 2. 工厂方法模式 3. 抽象工厂模式 四、总结 在面向对…...
Spring Boot 自定义定时任务组件深度解析:Quartz 集成与设计模式实战
一、组件设计目标 解决痛点: 简化 Quartz 原生 API 的复杂性统一任务调度管理(增删改查、日志、重试)与 Spring Boot 生态无缝整合 二、实现步骤详解 1. 组件初始化配置 1.1 初始化 Quartz 表结构 下载 SQL 脚本 🔗 官方表…...
嵌入式exfat-nofuse文件系统移植和使用
exfat-nofuse 是一款专为linux ARM平台设计的开源项目,它提供了一个非FUSE机制的内核级驱动,用于在Linux系统上无缝地读写exFAT和VFAT文件系统。此项目由Dorimanx维护,采用C语言编写,兼容GPL-2.0许可证。它避开了FUSE(用户空间文件系统)的使用…...
再来一篇,Linux中的软件管理
Linux中软件包的类型 在Linux系统中,软件包有多种不同的格式和类型,主要包括以下几种: DEB (Debian软件包)(此软件包不适用于RHEL8 系统): 适用于 Debian 及其衍生版本(如Ubuntu等&…...
【重学Android】02.Java环境配置的一些分享
背景说明 其实只是学习Android的话,只要下载好Android Studio开发工具,是自带JDK环境的,所以不需要再额外去进行配置,我之所以还要进行单独配置,是因为我其他的工具需要Java的环境,而且我目前用的是JDK 12…...
SimBody安装
SimBody安装 Simbody 是一个用于创建生物力学和机械系统仿真的多体动力学库。 SimBody安装 Windows安装: 下载地址:GitHub - simbody/simbody: High-performance C multibody dynamics/physics library for simulating articulated biomechanical and…...
CUDA编程中影响性能的小细节总结
一、内存访问优化 合并内存访问:确保相邻线程访问连续内存地址(全局内存对齐访问)。优先使用共享内存(Shared Memory)减少全局内存访问。避免共享内存的Bank Conflict(例如,使用padding或调整访…...
thinkphp:部署完整项目到本地phpstudy
一、准备工作 首先准备一个thinkphp的项目文件;准备mysql数据库 二、小皮初步搭建 1、建立网站 在小皮界面,网站->创建网站->输入域名,选择PHP版本等 注:确保端口未被占用 2、将项目文件放入根目录 网站->管理->…...
Linux网络编程——基于ET模式下的Reactor
一、前言 上篇文章中我们已经讲解了多路转接剩下的两个接口:poll和epoll,并且知道了epoll的两种工作模式分别是 LT模式和ET模式,下来我们就实现的是一个简洁版的 Reactor,即半同步半异步I/O,在linux网络中,…...
大模型相关面试问题原理及举例
大模型相关面试问题原理及举例 目录 大模型相关面试问题原理及举例Transformer相关面试问题原理及举例大模型模型结构相关面试问题原理及举例注意力机制相关面试问题原理及举例大模型与传统模型区别 原理:大模型靠海量参数和复杂结构,能学习更复杂模式。传统模型参数少、结构…...
Redis List 的详细介绍
Redis List 的详细介绍 以下是 Redis List 的详细介绍,从基础命令、内部编码和使用场景三个维度展开: 一、基础命令 Redis List 支持双向操作(头尾插入/删除),适用于队列、栈等场景,以下是核心命令分类&a…...
使用virtualbox的HostOnly建立共享网络-实现虚拟机上网
目录 环境描述解决方案具体步骤1.新建一个virtual host-only ethernet adapter2.设置windows的wifi信号网络共享3.确认winows宿主网络信息3.1.wifi适配器的信息3.2.虚拟网卡的信息3.3.确认virtualbox中虚拟网卡的ip地址 4.虚拟机网卡设置5.虚拟机网络设置5.1.本地连接设置5.2.u…...
springboot+vue3+mysql+websocket实现的即时通讯软件
项目演示 即时通讯软件项目演示 业务架构 技术栈 后端 选用编程语言 Javaweb框架SpringBootdb MySQL 持久存储nosql 缓存 Redis全双工通信框架 WebSocket 前端 前端框架Vue3TypescriptUI样式 Css、ElementPlus网页路由 vue-router全双工通信框架Websocket 功能完成情况 已实…...
基于 Spring Boot 瑞吉外卖系统开发(五)
基于 Spring Boot 瑞吉外卖系统开发(五) 删除分类 分类列表中每条分类信息右侧提供了一个“删除”按钮,当需要将已经存在的分类信息删除时,可以通过单击“删除”按钮实现。 请求路径为/category,携带参数id…...
AES (高级加密标准)
原理详解 AES是一种对称加密算法,使用相同的密钥进行加密和解密。它采用替代-置换网络(SPN)结构,主要步骤包括: 密钥扩展:从初始密钥派生多轮密钥 初始轮:AddRoundKey(轮密钥加) 主轮ÿ…...
详解Windows(一)——系统盘下目录及文件详解
引言 你是否曾经好奇过电脑里那些神秘的文件夹都是干什么用的?为什么有些文件是.exe而有些是.dll?不同的图片格式.jpg和.png到底有什么区别?如果你对这些问题感到困惑,这篇文章就是为你准备的。今天,我们将以通俗易懂…...
基于Spring MVC的客户端真实IP获取方案解析
文章目录 基于Spring MVC的客户端真实IP获取方案解析概述核心方法解析代码实现工作流程 IP获取优先级策略IP有效性验证异常处理与日志使用场景注意事项扩展建议 基于Spring MVC的客户端真实IP获取方案解析 概述 在Web应用开发中,准确获取客户端真实IP地址是常见的…...
【Web部署问题】在Tomcat中部署web项目出现http状态-404 -未找到详细解决方案
部署完tomcat记得在选中要运行的工件。 如果没有工件,或者工件有缺失东西,去这里配置工件,...
Linux——Shell编程之正则表达式与文本处理器(笔记)
目录 基础正则表达式 1:基础正则表达式示例 (4)查找任意一个字符“.”与重新字符“*” (5)查找连续字符范围“{ }” 文本处理器 一、sed工具 二、awk工具 (1)按行输出文本 (2࿰…...
Linux 进程控制(自用)
非阻塞调用waitpid 这样父进程就不会阻塞,此时循环使用我们可以让父进程执行其他任务而不是阻塞等待 进程程序替换 进程PCB加载到内存中的代码和数据 替换就是完全替换当前进程的代码段、数据段、堆和栈,保存当前的PCB 代码指的是二进制代码不是源码&a…...
05-DevOps-Jenkins自动拉取构建代码
新建Gitlab仓库 先在Gitab上创建一个代码仓库,选择创建空白项目 安装说明进行填写,然后点击创建项目 创建好的仓库是空的,什么都没有 新建一个springboot项目,用于代码上传使用。 只是为了测试代码上传功能,所以代码…...
【Spring学习】
Spring学习 简介 Spring 是一个开源的 Java 企业级开发框架,最核心的特点是: IOC(控制反转)AOP(面向切面编程) 它有完整的生态: 🚀 Spring Boot:用于快速构建服务&a…...
网络基础与 HTTP 协议
一、网络基础 (一)TCP/IP 协议族 TCP/IP 协议族是互联网通信的核心协议,它包含了多个层次的协议,共同协作实现网络通信。 1. IP 协议 IP(Internet Protocol)协议位于网络层,主要负责将数据包…...
SRS transcode支持 h264_nvenc 硬件解码方案
文章目录 SRS transcode支持 h264_nvenc 硬件解码方案1、修改文件2、重新编译3、使用 SRS transcode支持 h264_nvenc 硬件解码方案 SRS 是开源的流媒体服务,但在使用 GPU 服务器时,想要通过硬件加速,目前官方是不支持的,所以简单…...
