You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在iOS应用中捕获Flask实现的树莓派摄像头直播流?

嘿,看起来你已经搞定了树莓派端的直播流,要在iOS App里实现观看的话,有几个实用的方案,我给你详细说说:

方案1:用WKWebView直接复用现有网页(最省事)

这是最快实现的方式——既然你已经有了可以正常访问的网页端,直接在iOS App里用WKWebView加载这个网页就行,完全不需要修改后端代码,也不用自己处理流解析。

示例代码:

import UIKit
import WebKit

class StreamWebViewController: UIViewController, WKUIDelegate {
    private var webView: WKWebView!

    override func loadView() {
        let webConfig = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfig)
        webView.uiDelegate = self
        view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        guard let streamURL = URL(string: "http://192.168.0.241:7070") else { return }
        webView.load(URLRequest(url: streamURL))
    }
}

这个方案的优势是零学习成本,直接复用现有资源,适合快速验证需求。

方案2:原生解析multipart/x-mixed-replace流

如果不想用WebView,想自己控制帧渲染逻辑,可以直接请求你的/video_feed接口,手动解析这个多部分JPEG流,把每帧转成UIImage显示。

核心思路是:用URLSession获取流数据,按--frame边界分割出每个JPEG帧,再转成图片更新UI(注意必须在主线程更新UI)。

示例代码(核心部分):

import UIKit

class NativeStreamViewController: UIViewController {
    @IBOutlet weak var streamImageView: UIImageView!
    private var urlSession: URLSession?
    private var dataTask: URLSessionDataTask?
    private var accumulatedData = Data()

    override func viewDidLoad() {
        super.viewDidLoad()
        startStreaming()
    }

    private func startStreaming() {
        guard let streamURL = URL(string: "http://192.168.0.241:7070/video_feed") else { return }
        
        let config = URLSessionConfiguration.default
        urlSession = URLSession(configuration: config, delegate: self, delegateQueue: nil)
        dataTask = urlSession?.dataTask(with: streamURL)
        dataTask?.resume()
    }
}

extension NativeStreamViewController: URLSessionDataDelegate {
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        accumulatedData.append(data)
        let boundary = "--frame\r\n".data(using: .utf8)!
        
        // 分割所有完整的帧
        while let boundaryIndex = accumulatedData.range(of: boundary)?.lowerBound {
            let frameData = accumulatedData[..<boundaryIndex]
            accumulatedData.removeSubrange(...boundaryIndex)
            
            // 提取JPEG数据(跳过Content-Type头)
            if let headerEnd = frameData.range(of: "\r\n\r\n".data(using: .utf8)!)?.upperBound {
                let jpegData = frameData[headerEnd...]
                DispatchQueue.main.async {
                    self.streamImageView.image = UIImage(data: Data(jpegData))
                }
            }
        }
    }
}

注意:实际使用时需要处理更复杂的边界情况(比如不完整的帧数据),但这个示例已经能实现基础的流渲染。

方案3:转成iOS友好的HLS流(进阶优化)

如果想要更好的播放体验(比如缓存、帧率自适应、播放控制),可以把后端的流转成HLS格式——iOS的AVPlayer原生支持HLS,体验比前面两种方案更流畅。

步骤1:后端用FFmpeg转流

在树莓派上安装FFmpeg,然后执行命令把摄像头流转成HLS:

ffmpeg -f v4l2 -i /dev/video0 -c:v libx264 -preset ultrafast -f hls -hls_time 2 -hls_list_size 5 /path/to/your/flask/static/stream.m3u8

这个命令会生成stream.m3u8和对应的分片文件,放在Flask的static目录下。

步骤2:iOS端用AVPlayer播放

import UIKit
import AVKit

class HLSPlayerViewController: UIViewController {
    private var player: AVPlayer!
    private var playerLayer: AVPlayerLayer!

    override func viewDidLoad() {
        super.viewDidLoad()
        guard let hlsURL = URL(string: "http://192.168.0.241:7070/static/stream.m3u8") else { return }
        
        player = AVPlayer(url: hlsURL)
        playerLayer = AVPlayerLayer(player: player)
        playerLayer.frame = view.bounds
        view.layer.addSublayer(playerLayer)
        
        player.play()
    }
}

这个方案的优势是播放体验更稳定,还能轻松添加暂停、进度条等控制功能,但需要额外配置后端的转流工具。

内容的提问来源于stack exchange,提问作者gawron103

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 10:47:46