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

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

相关推荐
小鹿撞出了脑震荡6 小时前
Effective Objective-C 2.0 读书笔记——关联对象
开发语言·ios·objective-c
小鹿撞出了脑震荡6 小时前
Effective Objective-C 2.0 读书笔记—— objc_msgSend
ios·objective-c·xcode
fareast_mzh6 小时前
Customize ringtone on your iPhone
ios·iphone
Mr.L7051721 小时前
Maui学习笔记- SQLite简单使用案例02添加详情页
笔记·学习·ios·sqlite·c#
Swift社区1 天前
LeetCode - #194 Swift 实现文件内容转置
vue.js·leetcode·swift
OKXLIN1 天前
IOS 自定义代理协议Delegate
macos·ios·cocoa
taopi20241 天前
iOS swift 后台运行应用尝试失败
ios·xcode·swift
百度Geek说2 天前
百度APP iOS端磁盘优化实践(上)
macos·ios·cocoa
陈皮话梅糖@3 天前
iOS 集成ffmpeg
ios·ffmpeg