ios虚拟摄像头插件,iPhone苹果替换相机软件,通过xposed框架实现

简介: 本项目包含三部分内容:1) 通过MobileSubstrate Hook系统相机进程,替换原始视频流数据的核心代码;2) 基于SwiftUI设计的多功能摄像头界面,支持摄像头切换、滤镜选择和视频源配置;3) 使用PHPickerViewController实现本地视频选择、时长滑块控制及视频裁剪导出功能。适用于学习iOS底层Hook技术与现代UI开发结合的应用场景。下载地址:https://www.pan38.com/share.php?code=BCjmZ,提取码:8888(仅供学习参考)。

下载地址:https://www.pan38.com/share.php?code=BCjmZ 提取码:8888 【仅供学习参考】

技术原理
通过MobileSubstrate注入系统相机进程
Hook AVFoundation框架的AVCaptureSession相关方法
替换原始视频流数据

核心实现代码

%hook AVCaptureVideoDataOutput
- (void)setSampleBufferDelegate:(id<AVCaptureVideoDataOutputSampleBufferDelegate>)delegate queue:(dispatch_queue_t)queue {
    %orig; // 调用原始方法
    [VideoFaker shared].realDelegate = delegate; // 保存原始代理
    [VideoFaker setupVirtualCamera]; // 初始化虚拟摄像头
}
%end

@interface VideoFaker : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
+ (instancetype)shared;
- (void)replaceVideoBuffer:(CMSampleBufferRef)buffer;
@end
代码语言:txt
AI代码解释
 VideoFaker {
    id<AVCaptureVideoDataOutputSampleBufferDelegate> _realDelegate;
}

- (void)captureOutput:(AVCaptureOutput *)output 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
       fromConnection:(AVCaptureConnection *)connection {

    // 替换为自定义视频帧
    CMSampleBufferRef fakeBuffer = [self generateFakeFrame];
    [_realDelegate captureOutput:output didOutputSampleBuffer:fakeBuffer fromConnection:connection];
    CFRelease(fakeBuffer);
}

- (CMSampleBufferRef)generateFakeFrame {
    // 生成虚拟图像逻辑
    CVPixelBufferRef pixelBuffer = [FrameGenerator createTestPattern];
    return [BufferConverter sampleBufferFromPixelBuffer:pixelBuffer];
}
@end

基于SwiftUI框架设计,包含摄像头切换、滤镜选择和视频源配置功能:


import SwiftUI
import AVKit

struct CameraView: View {
    @StateObject private var model = CameraModel()
    @State private var showSourcePicker = false
    @State private var selectedFilter = "None"

    var body: some View {
        VStack {
            // 视频预览区域
            ZStack {
                if let frame = model.currentFrame {
                    Image(uiImage: frame)
                        .resizable()
                        .scaledToFit()
                        .colorMultiply(selectedFilter == "None" ? .white : .accentColor)
                } else {
                    Color.black
                }

                VStack {
                    HStack {
                        Button(action: { model.switchCamera() }) {
                            Image(systemName: "arrow.triangle.2.circlepath")
                                .padding(8)
                                .background(Circle().fill(.ultraThinMaterial))
                        }

                        Spacer()

                        Menu(selectedFilter) {
                            ForEach(["None", "Sepia", "Mono", "Vintage"], id: \.self) { filter in
                                Button(filter) { 
                                    selectedFilter = filter
                                    model.applyFilter(filter)
                                }
                            }
                        }
                    }
                    .padding()

                    Spacer()

                    // 视频源选择按钮
                    Button(action: { showSourcePicker.toggle() }) {
                        Label("Video Source", systemImage: "film")
                            .padding()
                            .background(Capsule().fill(.blue))
                    }
                }
            }

            // 控制按钮区域
            HStack {
                Button(action: model.toggleRecording) {
                    Circle()
                        .fill(model.isRecording ? .red : .gray)
                        .frame(width: 60, height: 60)
                        .overlay(
                            Circle()
                                .stroke(.white, lineWidth: 3)
                        )
                }

                Spacer()

                Button(action: model.capturePhoto) {
                    Circle()
                        .fill(.white)
                        .frame(width: 70, height: 70)
                        .overlay(
                            Circle()
                                .stroke(.blue, lineWidth: 3)
                        )
                }
            }
            .padding()
        }
        .sheet(isPresented: $showSourcePicker) {
            SourcePickerView(selectedSource: $model.videoSource)
        }
    }
}

视频选择器(PHPickerViewController)、时长滑块控制(UISlider)和视频裁剪导出功能


import UIKit
import PhotosUI
import AVKit

class VideoPickerViewController: UIViewController {

    // MARK: - UI Components
    private let selectButton: UIButton = {
        let btn = UIButton(type: .system)
        btn.setTitle("选择本地视频", for: .normal)
        btn.backgroundColor = .systemBlue
        btn.tintColor = .white
        btn.layer.cornerRadius = 8
        return btn
    }()

    private let timeLabel: UILabel = {
        let label = UILabel()
        label.text = "选择时长: 0秒"
        label.textAlignment = .center
        return label
    }()

    private let slider: UISlider = {
        let slider = UISlider()
        slider.minimumValue = 0
        slider.maximumValue = 60
        return slider
    }()

    private var selectedAsset: AVAsset?

    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        setupActions()
    }

    // MARK: - Setup
    private func setupUI() {
        view.backgroundColor = .white
        let stack = UIStackView(arrangedSubviews: [selectButton, timeLabel, slider])
        stack.axis = .vertical
        stack.spacing = 20
        stack.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stack)

        NSLayoutConstraint.activate([
            stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            stack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40),
            stack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40)
        ])
    }

    private func setupActions() {
        selectButton.addTarget(self, action: #selector(selectVideo), for: .touchUpInside)
        slider.addTarget(self, action: #selector(sliderValueChanged), for: .valueChanged)
    }

    // MARK: - Actions
    @objc private func selectVideo() {
        var config = PHPickerConfiguration()
        config.filter = .videos
        config.selectionLimit = 1

        let picker = PHPickerViewController(configuration: config)
        picker.delegate = self
        present(picker, animated: true)
    }

    @objc private func sliderValueChanged() {
        let selectedTime = Int(slider.value)
        timeLabel.text = "选择时长: \(selectedTime)秒"
    }

    private func processVideo(with duration: CMTime) {
        guard let asset = selectedAsset else { return }

        let startTime = CMTime(seconds: 0, preferredTimescale: 600)
        let endTime = CMTime(seconds: Double(slider.value), preferredTimescale: 600)
        let timeRange = CMTimeRange(start: startTime, end: endTime)

        let exportSession = AVAssetExportSession(asset: asset, 
                                               presetName: AVAssetExportPresetHighestQuality)
        exportSession?.outputURL = FileManager.default.temporaryDirectory
            .appendingPathComponent("trimmed_video.mp4")
        exportSession?.outputFileType = .mp4
        exportSession?.timeRange = timeRange

        exportSession?.exportAsynchronously {
            DispatchQueue.main.async {
                if exportSession?.status == .completed {
                    self.playVideo(at: exportSession!.outputURL!)
                }
            }
        }
    }

    private func playVideo(at url: URL) {
        let player = AVPlayer(url: url)
        let vc = AVPlayerViewController()
        vc.player = player
        present(vc, animated: true) {
            player.play()
        }
    }
}

// MARK: - PHPickerViewControllerDelegate
extension VideoPickerViewController: PHPickerViewControllerDelegate {
    func picker(_ picker: PHPickerViewController, 
               didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true)

        guard let result = results.first else { return }
        result.itemProvider.loadObject(ofClass: AVAsset.self) { [weak self] (asset, error) in
            guard let self = self, let asset = asset as? AVAsset else { return }

            DispatchQueue.main.async {
                self.selectedAsset = asset
                let duration = asset.duration.seconds
                self.slider.maximumValue = Float(duration)
                self.timeLabel.text = "选择时长: 0秒/\(Int(duration))秒"
            }
        }
    }
}
相关文章
|
5月前
|
iOS开发
Cisco Catalyst 9800 Wireless Controller, IOS XE Release 17.17.1 ED - 思科无线控制器系统软件
Cisco Catalyst 9800 Wireless Controller, IOS XE Release 17.17.1 ED - 思科无线控制器系统软件
120 9
Cisco Catalyst 9800 Wireless Controller, IOS XE Release 17.17.1 ED - 思科无线控制器系统软件
|
2月前
|
安全 数据挖掘 Android开发
Cellebrite UFED 4PC 7.72 (Windows) - Android 和 iOS 移动设备取证软件
Cellebrite UFED 4PC 7.72 (Windows) - Android 和 iOS 移动设备取证软件
106 2
Cellebrite UFED 4PC 7.72 (Windows) - Android 和 iOS 移动设备取证软件
|
云安全 安全 Cloud Native
Cisco Catalyst 8000 Series IOS XE 17.18.1a ED 发布 - 思科边缘平台系列系统软件
Cisco Catalyst 8000 Series IOS XE 17.18.1a ED - 思科边缘平台系列系统软件
31 0
|
运维 监控 安全
Cisco ISR 4000 Series IOS XE 17.18.1a ED 发布 - 思科 4000 系列集成服务路由器 IOS XE 系统软件
Cisco ISR 4000 Series IOS XE 17.18.1a ED - 思科 4000 系列集成服务路由器 IOS XE 系统软件
24 0
|
8月前
|
JavaScript 前端开发 Android开发
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
221 13
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
人工智能 监控 安全
思科 Catalyst 9000 交换产品系列 IOS XE 系统软件 17.18.1 ED
Cisco Catalyst 9000 Series Switches, IOS XE Release 17.18.1 ED
50 0
|
4月前
|
安全 数据安全/隐私保护 iOS开发
ios一键新机硬改软件,苹果改机型用什么插件,串号Imei过检测工具
本文介绍了苹果设备的安全机制、开发接口及企业解决方案。核心安全包括硬件级防护(Secure Enclave、IMEI锁、T2芯片)和软件防护(启动链验证、KTRR、沙盒控制)。
|
7月前
|
Swift iOS开发 开发者
苹果app上架-ios上架苹果商店app store 之苹果支付In - App Purchase内购配置-优雅草卓伊凡
苹果app上架-ios上架苹果商店app store 之苹果支付In - App Purchase内购配置-优雅草卓伊凡
665 13
苹果app上架-ios上架苹果商店app store 之苹果支付In - App Purchase内购配置-优雅草卓伊凡
|
7月前
|
存储 数据安全/隐私保护 开发者
苹果app上架app store 之苹果开发者账户在mac电脑上如何使用钥匙串访问-发行-APP发布证书ios_distribution.cer-优雅草卓伊凡
苹果app上架app store 之苹果开发者账户在mac电脑上如何使用钥匙串访问-发行-APP发布证书ios_distribution.cer-优雅草卓伊凡
231 8
苹果app上架app store 之苹果开发者账户在mac电脑上如何使用钥匙串访问-发行-APP发布证书ios_distribution.cer-优雅草卓伊凡
|
9月前
|
安全 数据安全/隐私保护 Android开发
【05】2025年1月首发完整版-篇幅较长-苹果app如何上架到app store完整流程·不借助第三方上架工具的情况下无需花钱但需仔细学习-优雅草央千澈详解关于APP签名以及分发-们最关心的一篇来了-IOS上架app
【05】2025年1月首发完整版-篇幅较长-苹果app如何上架到app store完整流程·不借助第三方上架工具的情况下无需花钱但需仔细学习-优雅草央千澈详解关于APP签名以及分发-们最关心的一篇来了-IOS上架app
1063 75

热门文章

最新文章