ios swift画中画技术尝试

继上篇:iOS swift 后台运行应用尝试失败-CSDN博客

为什么想到画中画,起初是看到后台模式里有一个picture in picture,去了解了后发现这个就是小窗口视频播放,方便用户执行多任务。看小窗口视频的同时,可以作其他的事情。

画中画功能在20世纪80年代开始在电视机中应用,使得用户可以在一个屏幕上同时观看两个频道的内容。‌

这个技术在安卓里已经非常普遍了。各种视频内容网站都有类似功能。

而苹果支持画中画是在ios14已经开始支持。目前在使用的大多数机型,比如iphone 8p,升级系统后可到ios16.7,都能支持画中画技术。

后台任务苹果管理太严格,不好搞,那么使用画中画这种技术,直接做成多任务,这样也是另一条可以尝试的路径。

1、标准PIP使用

首先,标准写法是采用AVPlayer,输入url需要是MP4等视频文件,如果是加密后的网址,无法播放,比如b站的网址。

创建AVPlayer,使用AVPlayerLayer来初始化AVPictureInPictrueController。

Swift 复制代码
        // 创建 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.bounds
        view.layer.addSublayer(playerLayer)
        
        // 设置画中画控制器
        pipController = AVPictureInPictureController(playerLayer: playerLayer)
        pipController.delegate = self

        player.play()   // 直接播放

按home键退出,就会自动启动小窗口继续播放视频。

这里有一个坑,需要初始化音频,否则播放mp4无声音,且画中画也不会触发。

Swift 复制代码
    do {
        // 设置AVAudioSession为后台模式
        try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
        try AVAudioSession.sharedInstance().setActive(true)
    } catch {
        print("无法设置AVAudioSession: \(error)")
    }

视频画中画,iphone 8p_哔哩哔哩_bilibili

2、摄像头预览

摄像头捕捉并预览,这个算法也很容易找到,使用AVCaptureSession

Swift 复制代码
func setupCamera(forgroundFlag: Bool, view: UIView) {
        farView = view
        flag = forgroundFlag
        captureSession = AVCaptureSession()
        captureSession?.beginConfiguration()
        captureSession?.sessionPreset = .high
        
        guard 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 = true
        videoOutput!.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
        captureSession!.addOutput(videoOutput!)
        
        if (forgroundFlag) {
            videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
            guard let preLayer = videoPreviewLayer else { return }
            preLayer.frame = view.frame
            view.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接口,如下:

Swift 复制代码
    func setupSampleBufferDisplayLayer() {
        sampleBufferDisplayLayer = AVSampleBufferDisplayLayer()
        sampleBufferDisplayLayer.frame = view.bounds
        sampleBufferDisplayLayer.videoGravity = .resizeAspect
        view.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。

Swift 复制代码
    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 = self
            if pipController.isPictureInPicturePossible {
                pipController.startPictureInPicture()
            }
        }
    }

4、摄像头多任务需要硬件支持

前面生成的工程,在iphone 8p上测试效果如下,进入画中画模式时,在1s以内视频捕获就停止了。

摄像头画中画,iphone 8p_哔哩哔哩_bilibili

原因在于苹果对摄像头硬件管理非常严格,摄像头开小窗口,那么用户就可能用摄像头打开另一个任务,意味着摄像头需要支持多任务。

参考官方说明:

增加代码检查如下:

Swift 复制代码
    
    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,没有其他新机型,所以后面的调试验证没法继续下去了。在没有订单推动情况下,也不会投入了。

如果有相关项目需求的,可以找我咨询合作。

相关推荐
大熊猫侯佩11 小时前
Swift 6.2 列传(第十三篇):香香公主的“倾城之恋”与优先级飞升
swift·编程语言·apple
专业开发者16 小时前
调试 iOS 蓝牙应用的新方法
物联网·macos·ios·cocoa
1024小神19 小时前
Swift配置WKwebview加载网站或静态资源后,开启调试在电脑上debug
swift
tangbin58308521 小时前
iOS Swift 可选值(Optional)详解
前端·ios
卷心菜加农炮1 天前
基于Python的FastAPI后端开发框架如何使用PyInstaller 进行打包与部署
ios
北极象2 天前
千问大模型接入示例
ios·iphone·qwen
ipad协议开发2 天前
企业微信 iPad 协议应用机器人开发
ios·企业微信·ipad
kkoral2 天前
基于MS-Swift 为 Qwen3-0.6B-Base 模型搭建可直接调用的 API 服务
python·conda·fastapi·swift
QuantumLeap丶3 天前
《Flutter全栈开发实战指南:从零到高级》- 26 -持续集成与部署
android·flutter·ios
2501_915918413 天前
TCP 抓包分析在复杂网络问题中的作用,从连接和数据流层面理解系统异常行为
网络·网络协议·tcp/ip·ios·小程序·uni-app·iphone