ios swift画中画技术尝试
继上篇:iOS swift 后台运行应用尝试失败-CSDN博客
为什么想到画中画,起初是看到后台模式里有一个picture in picture,去了解了后发现这个就是小窗口视频播放,方便用户执行多任务。看小窗口视频的同时,可以作其他的事情。
画中画功能在20世纪80年代开始在电视机中应用,使得用户可以在一个屏幕上同时观看两个频道的内容。
这个技术在安卓里已经非常普遍了。各种视频内容网站都有类似功能。
而苹果支持画中画是在ios14已经开始支持。目前在使用的大多数机型,比如iphone 8p,升级系统后可到ios16.7,都能支持画中画技术。
后台任务苹果管理太严格,不好搞,那么使用画中画这种技术,直接做成多任务,这样也是另一条可以尝试的路径。
1、标准PIP使用
首先,标准写法是采用AVPlayer,输入url需要是MP4等视频文件,如果是加密后的网址,无法播放,比如b站的网址。
创建AVPlayer,使用AVPlayerLayer来初始化AVPictureInPictrueController。
// 创建 AVPlayer 对象let videoURL = URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!player = AVPlayer(url: videoURL)// 创建 AVPlayerLayer 并添加到视图层上playerLayer = AVPlayerLayer(player: player)playerLayer.frame = view.boundsview.layer.addSublayer(playerLayer)// 设置画中画控制器pipController = AVPictureInPictureController(playerLayer: playerLayer)pipController.delegate = selfplayer.play() // 直接播放
按home键退出,就会自动启动小窗口继续播放视频。
这里有一个坑,需要初始化音频,否则播放mp4无声音,且画中画也不会触发。
do {// 设置AVAudioSession为后台模式try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)try AVAudioSession.sharedInstance().setActive(true)} catch {print("无法设置AVAudioSession: \(error)")}
视频画中画,iphone 8p
2、摄像头预览
摄像头捕捉并预览,这个算法也很容易找到,使用AVCaptureSession
func setupCamera(forgroundFlag: Bool, view: UIView) {farView = viewflag = forgroundFlagcaptureSession = AVCaptureSession()captureSession?.beginConfiguration()captureSession?.sessionPreset = .highguard let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) else {print("No front camera available")return}guard let input = try? AVCaptureDeviceInput(device: captureDevice) else {print("Unable to access front camera")return}captureSession!.addInput(input)videoOutput = AVCaptureVideoDataOutput()videoOutput?.automaticallyConfiguresOutputBufferDimensions = truevideoOutput!.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))captureSession!.addOutput(videoOutput!)if (forgroundFlag) {videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)guard let preLayer = videoPreviewLayer else { return }preLayer.frame = view.frameview.layer.addSublayer(preLayer)// 设置 UILabel 的属性label = UILabel()label!.text = "Hello, Swift!" // 设置文本内容label!.textColor = UIColor.systemBlue // 设置文本颜色label!.font = UIFont.systemFont(ofSize: 20) // 设置字体和大小label!.textAlignment = .left // 设置文本对齐方式label!.numberOfLines = 0 // 设置行数,0表示自动换行// 设置 UILabel 的位置和大小label!.frame = CGRect(x: 8, y: 20, width: 200, height: 30)}captureSession?.commitConfiguration()}
AVCaptureSession输出的是AVCaptureVideoPreviewLayer,这个layer无法直接用来初始化AVPictureInPictrueController。
3、摄像头输出PIP
swift代码在标准案例上实现都很简洁,但要自定义实现一些功能时,就会发现材料很难找。
比如怎样把摄像头预览与PIP结合。
深度搜索AI给出了一个结果,看上去好像可以,实践下来编辑都不能通过。但是它给出了一个提示,就是AVSampleBufferDisplayLayer。
这里吐槽下AI,最喜欢把不可用的东西包装成很好看的样子,在技术搜索方面,某些时候还不如原始的搜索引擎来得方便。
搜索AVCaptureVideoPreviewLayer转为AVSampleBufferDisplayLayer,也有方法,但是算法看上去稍显复杂。
官方文档Adopting Picture in Picture in a Custom Player | Apple Developer Documentation里也提到了可以使用AVSampleBufferDisplayLayer来初始化PIPController。
网上另一篇文章,说在捕获处理接口里,还有一个视频数据
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)
CMSampleBuffer可以直接转为AVSampleBufferDisplayLayer,使用enqueu接口,如下:
func setupSampleBufferDisplayLayer() {sampleBufferDisplayLayer = AVSampleBufferDisplayLayer()sampleBufferDisplayLayer.frame = view.boundssampleBufferDisplayLayer.videoGravity = .resizeAspectview.layer.addSublayer(sampleBufferDisplayLayer)}func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
。。。。。。 if (sampleBufferDisplayLayer.status == AVQueuedSampleBufferRenderingStatus.failed) {sampleBufferDisplayLayer.flush() // 异常处理}sampleBufferDisplayLayer.enqueue(sampleBuffer)
。。。。。。
这样整个流程就能打通了。
通过AVSampleBufferDisplayLayer,可以用任何视频流来初始化AVPictureInPictureController。
private func setupPiPController() {guard AVPictureInPictureController.isPictureInPictureSupported() else {print("Picture in Picture is not supported on this device")return}print("support pip!!!")if pipController == nil {pipController = AVPictureInPictureController(contentSource: .init(sampleBufferDisplayLayer: sampleBufferDisplayLayer, playbackDelegate: self))}if let pipController = pipController {pipController.delegate = selfif pipController.isPictureInPicturePossible {pipController.startPictureInPicture()}}}
4、摄像头多任务需要硬件支持
前面生成的工程,在iphone 8p上测试效果如下,进入画中画模式时,在1s以内视频捕获就停止了。
摄像头画中画,iphone 8p
原因在于苹果对摄像头硬件管理非常严格,摄像头开小窗口,那么用户就可能用摄像头打开另一个任务,意味着摄像头需要支持多任务。
参考官方说明:



增加代码检查如下:
func setupCamera(forgroundFlag: Bool, view: UIView) {
。。。。。。 guard let tempcap = captureSession else { return }if (tempcap.isMultitaskingCameraAccessSupported) {print("camera supp multitask")// Enable use of the camera in multitasking modes.captureSession?.isMultitaskingCameraAccessEnabled = true} else {print("camera not supp multitask")}captureSession?.commitConfiguration()}
从调试打印来看,iphone 8p的摄像头不支持多任务。
根据AI查询结果,可能需要iphone 12以上的机型才能支持摄像头多任务。

对于AI返回结果比较存疑,因为多次返回的结果可能会不一样。比如iphone XR,有的说支持,有的说不支持。
参考另外的官方文章,视频通话、直播画中画都是一样的。
Adopting Picture in Picture for video calls | Apple Developer Documentation
因为本人手上只有iphone 8p,没有其他新机型,所以后面的调试验证没法继续下去了。在没有订单推动情况下,也不会投入了。
如果有相关项目需求的,可以找我咨询合作。
相关文章:
ios swift画中画技术尝试
继上篇:iOS swift 后台运行应用尝试失败-CSDN博客 为什么想到画中画,起初是看到后台模式里有一个picture in picture,去了解了后发现这个就是小窗口视频播放,方便用户执行多任务。看小窗口视频的同时,可以作其他的事情…...
Prometheus 中的 Exporter
在 Prometheus 生态系统中,Exporter 扮演着至关重要的角色,它们负责从不同的服务或系统中收集和暴露度量数据。本文将详细介绍 Exporter 的概念、类型以及如何有效使用它们将 Prometheus 集成到各种系统中进行监控。 什么是 Exporter? Exporter 是一段软件,它从应用程序或…...
玄奘的启示
今天没事,又看了一遍央视拍的《玄奘大师》(程池、齐秦配乐版)伪纪录片,很有感触。 古罗马哲学家塞内加说“人最可怕的事情莫过于死前只留下活过的岁数。” 他在《论生命之短暂》中这样写道:“生命并非短促࿰…...
车载以太网---数据链路层
在上一章节中,我们讲解了数据链路层与物理层的接口MIIM,在本章中我们主要介绍车载网络中的数据链路层。 目录 数据链路层与网络层的区别 数据链路层:负责“同一链路”或“局域网/子网”内的可靠传输 传输范围: 主要功能: 通路…...
文本复制兼容方案最佳实现落地。
文章目录 一、navigator.clipboard.writeText二、方案落地总结 一、navigator.clipboard.writeText navigator.clipboard.writeText 是一个Web API,它允许网页脚本将文本数据写入用户的系统剪贴板。这个API是异步的,并且设计用于提高安全性和用户体验&a…...
ArkTS高性能编程实践
文章目录 概述声明与表达式函数数组异常 概述 本文主要提供应用性能敏感场景下的高性能编程的相关建议,助力开发者开发出高性能的应用。高性能编程实践,是在开发过程中逐步总结出来的一些高性能的写法和建议,在业务功能实现过程中࿰…...
阿里新发的大模型Qwen2.5-max如何?
阿里新发布的大模型Qwen2.5-Max是一款性能卓越、技术先进的大型语言模型,其在多个方面展现了突出的表现。以下是基于我搜索到的资料对Qwen2.5-Max的详细评价: 技术特点 超大规模预训练数据:Qwen2.5-Max采用了超过20万亿tokens的超大规模预训…...
吴晓波 历代经济变革得失@简明“中国经济史” - 读书笔记
目录 《历代经济变革得失》读书笔记一、核心观点二、主要内容(一)导论(二)春秋战国时期(三)汉代(四)北宋(五)明清时期(六)近现代&…...
SQL GROUP BY 详解
SQL GROUP BY 详解 引言 在数据库查询中,GROUP BY 子句是一个非常有用的工具,它允许我们对查询结果进行分组,并基于这些分组进行聚合计算。本文将详细介绍 GROUP BY 的用法、注意事项以及在实际应用中的场景。 什么是 GROUP BY? GROUP BY 子句用于对查询结果进行分组。…...
走向基于大语言模型的新一代推荐系统:综述与展望
HightLight 论文题目:Towards Next-Generation LLM-based Recommender Systems: A Survey and Beyond作者机构:吉林大学、香港理工大学、悉尼科技大学、Meta AI论文地址: https://arxiv.org/abs/2410.1974 基于大语言模型的下一代推荐系统&…...
6 Flink 状态管理
6 Flink 状态管理 1. State-Keyed State2. State-Operator State3. Broadcast State 我们前面写的 wordcount 的例子,没有包含状态管理。如果一个task在处理过程中挂掉了,那么它在内存中的状态都会丢失,所有的数据都需要重新计算。从容错和消…...
第1章 量子暗网中的血色黎明
月球暗面的危机与阴谋 量子隧穿效应催生的幽蓝电弧,于环形山表面肆意跳跃,仿若无数奋力挣扎的机械蠕虫,将月球暗面的死寂打破,徒增几分诡异。艾丽伫立在被遗弃的“广寒宫”量子基站顶端,机械义眼之中,倒映着…...
爬虫基础(六)代理简述
目录 一、什么是代理 二、基本原理 三、代理分类 一、什么是代理 爬虫一般是自动化的,当我们自动运行时 爬虫自动抓取数据,但一会就出现了错误: 如,您的访问频率过高! 这是因为网站的反爬措施,如果频…...
前端 Vue 性能提升策略
一、引言 前端性能优化是确保 Web 应用快速响应和流畅用户体验的关键。对于使用 Vue.js 构建的应用,性能优化不仅涉及通用的前端技术,还包括针对 Vue 特性的特定优化措施。本文将从多个方面探讨如何全面提升前端和 Vue 应用的性能。 二、前端性能优化基础 1. 减少初始加载…...
MCU内部ADC模块误差如何校准
本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时,也能帮助其他需要参考的朋友。如有谬误,欢迎大家进行指正。 一、ADC误差校准引言 MCU 片内 ADC 模块的误差总包括了 5 个静态参数 (静态失调,增益误差,微分非线性…...
Spring MVC消息转换器
在Spring MVC框架中,extendMessageConverters 通常与消息转换器(Message Converters)相关。消息转换器是Spring MVC用于将HTTP请求和响应主体(body)转换为Java对象和字符串的组件。它们在处理不同的媒体类型࿰…...
手写防抖函数、手写节流函数
文章目录 1 手写防抖函数2 手写节流函数 1 手写防抖函数 函数防抖是指在事件被触发n秒后再执行回调,如果在这n秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。 function debou…...
【Rust自学】15.4. Drop trait:告别手动清理,释放即安全
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 15.4.1. Drop trait的意义 类型如果实现了Drop trait,就可以让程序员自定义当值离开作用域时发生的操作。例如文件、网络资源…...
【Block总结】CPCA,通道优先卷积注意力|即插即用
论文信息 标题: Channel Prior Convolutional Attention for Medical Image Segmentation 论文链接: arxiv.org 代码链接: GitHub 创新点 本文提出了一种新的通道优先卷积注意力(CPCA)机制,旨在解决医学图像分割中存在的低对比度和显著…...
信息学奥赛一本通 1607:【 例 2】任务安排 2 | 洛谷 P10979 任务安排 2
【题目链接】 ybt 1607:【 例 2】任务安排 2 洛谷 P10979 任务安排 2 注:ybt1607中n最大达到 1 0 4 10^4 104,洛谷P10979中n最大达到 3 ∗ 1 0 5 3*10^5 3∗105,本题解统一认为n最大达到 3 ∗ 1 0 5 3*10^5 3∗105。 【题目考点…...
AI(计算机视觉)自学路线
本文仅用来记录一下自学路线方便日后复习,如果对你自学有帮助的话也很开心o(* ̄▽ ̄*)ブ B站吴恩达机器学习->B站小土堆pytorch基础学习->opencv相关知识(Halcon或者opencv库)->四类神经网络(这里跟…...
OFDM系统仿真
1️⃣ OFDM的原理 1.1 介绍 OFDM是一种多载波调制技术,将输入数据分配到多个子载波上,每个子载波上可以独立使用 QAM、PSK 等传统调制技术进行调制。这些子载波之间互相正交,从而可以有效利用频谱并减少干扰。 1.2 OFDM的核心 多载波调制…...
torch numpy seed使用方法
1 import numpy as np np.random.seed(500) np.random.rand(5)array([0.69367953, 0.06171699, 0.6666116 , 0.55920894, 0.08511062])import torch torch.manual_seed(500) torch.rand(5)为了能够复现数据,我们可以使用seed 来控制生成的随机数。设置seed数据来设…...
【Go语言圣经】第四节:复合数据类型
第四章:复合数据类型 本节主要讨论四种类型——数组、slice、map和结构体。 数组和结构体都是有固定内存大小的数据结构。相比之下,slice 和 map 则是动态的数据结构,它们可以根据需要动态增长。 4.1 数组 数组是一个定长的由特定类型元素…...
【Vite + Vue + Ts 项目三个 tsconfig 文件】
Vite Vue Ts 项目三个 tsconfig 文件 为什么 Vite Vue Ts 项目会有三个 tsconfig 文件?首先我们先了解什么是 tsconfig.json ? 为什么 Vite Vue Ts 项目会有三个 tsconfig 文件? 在使用 Vite 创建 vue-ts 模板的项目时,会发现除了 ts…...
论文和代码解读:RF-Inversion 图像/视频编辑技术
Diffusion Models专栏文章汇总:入门与实战 前言:Rectified Flow的反演和DDIM这些不太一样,上一篇博客中介绍了腾讯提出的一种方法《基于Rectified Flow FLUX的图像编辑方法 RF-Solver》,主要就是用泰勒展开和一阶导数近似来分解反演公式。这篇博客介绍谷歌提出的方法RF-Inv…...
完美还是完成?把握好度,辨证看待
完美还是完成? 如果说之前这个答案有争议,那么现在,答案毋庸置疑 ■为什么完美大于完成 ●时间成本: 做事不仅要考虑结果,还要考虑时间和精力,要说十年磨一剑的确质量更好,但是现实没有那么多…...
Many Whelps! Handle It! (10 player) Many Whelps! Handle It! (25 player)
http://db.nfuwow.com/80/?achievement4403 http://db.nfuwow.com/80/?achievement4406 最少扣你50DKP! 第二阶段 当奥妮克希亚升空后,在10秒内引出50只奥妮克希亚雏龙,随后击败奥妮克希亚。 World of Warcraft [CLASSIC][80猎人][Grandel][最少扣你5…...
3.4 Go函数作用域(标识符)
只有精准分析每个标识符的作用域范围,才能编写出优质、健壮的代码,避免逻辑错误的发生。 作用域标识符 简单来说,作用域指的是标识符可以起作用的范围,即其可见范围。将标识符的可见性限制在一定范围内,这个范围就是…...
【React】PureComponent 和 Component 的区别
前言 在 React 中,PureComponent 和 Component 都是用于创建组件的基类,但它们有一个主要的区别:PureComponent 会给类组件默认加一个shouldComponentUpdate周期函数。在此周期函数中,它对props 和 state (新老的属性/状态)会做一…...
