【OC】AVPlayerLayer的学习

news2025/4/22 2:17:03

文章目录

  • 前言
  • 关于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;
@end

NS_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.创建视频URL
    NSURL *videoURL = [[NSBundle mainBundle] URLForResource:@"video_test" withExtension:@"mp4"];
 
    if (videoURL) {
        NSLog(@"视频文件 URL: %@", videoURL);
    } else {
        NSLog(@"未找到视频文件");
    }

    //2.创建AVPlayer
    self.player = [AVPlayer playerWithURL:videoURL];
    //3.创建VideoPlayerView
    VideoPlayerView *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.
    
    // 创建 WKWebView
    self.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 {
    // 创建 AFHTTPSessionManager
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer]; // 设置响应为原始数据
    
    // 发送 GET 请求
    [manager GET:shortURLString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // 获取重定向后的 URL
        NSHTTPURLResponse *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 {
    // 创建 AFHTTPSessionManager
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer]; // 设置响应为原始数据
    
    // 发送 GET 请求
    [manager GET:pageURLString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // 解析 HTML
        NSString *htmlString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
        if (htmlString) {
            // 使用正则表达式提取视频 URL
            NSString *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(), ^{
        // 创建 AVPlayer
        self.player = [AVPlayer playerWithURL:videoURL];
        
        // 创建 AVPlayerLayer
        self.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的使用

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

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

相关文章

C++笔记-list

list即是我们之前学的链表&#xff0c;这篇主要还是讲解list的底层实现&#xff0c;前面会讲一些list区别于前面string和vector的一些接口以及它们的注意事项。 一.list的基本使用 和之前的string&#xff0c;vector一样&#xff0c;有很多之前见过的一些接口&#xff0c;经过…

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 介绍 是一个可扩展、功能丰富且用户友好的…

极狐GitLab 登录限制如何设置?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 登录限制 (BASIC SELF) 您可以使用登录限制自定义 Web 界面以及基于 HTTP(S) 的 Git 的身份验证限制。 设置 要访问登录限…

嵌入式exfat-nofuse文件系统移植和使用

exfat-nofuse 是一款专为linux ARM平台设计的开源项目,它提供了一个非FUSE机制的内核级驱动,用于在Linux系统上无缝地读写exFAT和VFAT文件系统。此项目由Dorimanx维护,采用C语言编写&#xff0c;兼容GPL-2.0许可证。它避开了FUSE&#xff08;用户空间文件系统&#xff09;的使用…

再来一篇,Linux中的软件管理

Linux中软件包的类型 在Linux系统中&#xff0c;软件包有多种不同的格式和类型&#xff0c;主要包括以下几种&#xff1a; DEB (Debian软件包)&#xff08;此软件包不适用于RHEL8 系统&#xff09;&#xff1a; 适用于 Debian 及其衍生版本&#xff08;如Ubuntu等&…

SimBody安装

SimBody安装 Simbody 是一个用于创建生物力学和机械系统仿真的多体动力学库。 SimBody安装 Windows安装&#xff1a; 下载地址&#xff1a;GitHub - simbody/simbody: High-performance C multibody dynamics/physics library for simulating articulated biomechanical and…

thinkphp:部署完整项目到本地phpstudy

一、准备工作 首先准备一个thinkphp的项目文件&#xff1b;准备mysql数据库 二、小皮初步搭建 1、建立网站 在小皮界面&#xff0c;网站->创建网站->输入域名&#xff0c;选择PHP版本等 注&#xff1a;确保端口未被占用 2、将项目文件放入根目录 网站->管理->…

大模型相关面试问题原理及举例

大模型相关面试问题原理及举例 目录 大模型相关面试问题原理及举例Transformer相关面试问题原理及举例大模型模型结构相关面试问题原理及举例注意力机制相关面试问题原理及举例大模型与传统模型区别 原理:大模型靠海量参数和复杂结构,能学习更复杂模式。传统模型参数少、结构…

Redis List 的详细介绍

Redis List 的详细介绍 以下是 Redis List 的详细介绍&#xff0c;从基础命令、内部编码和使用场景三个维度展开&#xff1a; 一、基础命令 Redis List 支持双向操作&#xff08;头尾插入/删除&#xff09;&#xff0c;适用于队列、栈等场景&#xff0c;以下是核心命令分类&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 瑞吉外卖系统开发&#xff08;五&#xff09; 删除分类 分类列表中每条分类信息右侧提供了一个“删除”按钮&#xff0c;当需要将已经存在的分类信息删除时&#xff0c;可以通过单击“删除”按钮实现。 请求路径为/category&#xff0c;携带参数id&#xf…

【Web部署问题】在Tomcat中部署web项目出现http状态-404 -未找到详细解决方案

部署完tomcat记得在选中要运行的工件。 如果没有工件&#xff0c;或者工件有缺失东西&#xff0c;去这里配置工件&#xff0c;

Linux——Shell编程之正则表达式与文本处理器(笔记)

目录 基础正则表达式 1:基础正则表达式示例 &#xff08;4&#xff09;查找任意一个字符“.”与重新字符“*” &#xff08;5&#xff09;查找连续字符范围“{ }” 文本处理器 一、sed工具 二、awk工具 &#xff08;1&#xff09;按行输出文本 &#xff08;2&#xff0…

05-DevOps-Jenkins自动拉取构建代码

新建Gitlab仓库 先在Gitab上创建一个代码仓库&#xff0c;选择创建空白项目 安装说明进行填写&#xff0c;然后点击创建项目 创建好的仓库是空的&#xff0c;什么都没有 新建一个springboot项目&#xff0c;用于代码上传使用。 只是为了测试代码上传功能&#xff0c;所以代码…

SRS transcode支持 h264_nvenc 硬件解码方案

文章目录 SRS transcode支持 h264_nvenc 硬件解码方案1、修改文件2、重新编译3、使用 SRS transcode支持 h264_nvenc 硬件解码方案 SRS 是开源的流媒体服务&#xff0c;但在使用 GPU 服务器时&#xff0c;想要通过硬件加速&#xff0c;目前官方是不支持的&#xff0c;所以简单…

阿里云服务器搭建开源版禅道

一&#xff0c;下载地址&#xff1a;禅道11.5版本发布&#xff0c;主要完善细节&#xff0c;修复bug&#xff0c;新增动态过滤机制 - 禅道下载 - 禅道项目管理软件 下载地址二&#xff1a; 禅道21.6.stable 实现旧编辑器撰写的文档无感升级至新版编辑器 - 禅道下载 - 禅道项目…

怎么用面向对象和状态机架构,设计一个通用的按键检测功能?

说起按键检测&#xff0c;在座的各位&#xff0c;哪个没被它折磨过&#xff1f; 我刚入门时&#xff0c;为了实现一个简单的按键功能&#xff0c;硬生生写了几十行代码&#xff0c;各种 if...else 嵌套&#xff0c;逻辑绕得我自己都头晕。 更可气的是&#xff0c;辛辛苦苦写完…

Java基础系列-LinkedList源码解析

文章目录 简介LinkedList 插入和删除元素的时间复杂度&#xff1f;LinkedList 为什么不能实现 RandomAccess 接口&#xff1f; LinkedList 源码分析Node 定义初始化获取元素插入元素删除元素遍历链表 简介 LinkedList 是一个基于双向链表实现的集合类&#xff0c;经常被拿来和…

qwen 14B模型配置文件,层名称weight_map. 28GB

qwen 14B模型配置文件,层名称weight_map. 28GB 目录 qwen 14B模型配置文件,层名称weight_map. 28GBmetadata(元数据)weight_map(权重映射)lm_head.weightmodel.layersmlp.{proj_type}.weightpost_attention_layernormself_attn.{proj_type}.{bias_or_weight}model.norm.w…