【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的使用