下面给你一份超清晰、可直接上手的 AVPlayer 播放“在线视频 + 本地文件(含数据库路径管理)”的完整方案,包括常见踩坑点、最佳实践、示例代码。
✅ 一、播放在线视频(URL)
AVPlayer 播放在线视频最简单,只需要给它一个远程 URL。
示例代码:
import AVFoundation
import AVKit
func playOnlineVideo(urlString: String, on vc: UIViewController) {
guard let url = URL(string: urlString) else { return }
let player = AVPlayer(url: url)
let playerVC = AVPlayerViewController()
playerVC.player = player
vc.present(playerVC, animated: true) {
player.play()
}
}
注意点:
- 需要 HTTPS 或 ATS 放开。
- 避免 URL 被缓存,可以加随机参数
?t=timestamp。 - 若需要预加载,可用
AVPlayerItem+ KVO。
✅ 二、播放本地视频(文件路径)
你的视频若存到沙盒,如 Documents / tmp / Caches,可以直接用 File URL 播放。
示例:
func playLocalVideo(filePath: String, on vc: UIViewController) {
let url = URL(fileURLWithPath: filePath)
let player = AVPlayer(url: url)
let playerVC = AVPlayerViewController()
playerVC.player = player
vc.present(playerVC, animated: true) {
player.play()
}
}
沙盒文件路径示例:
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let filePath = path + "/myVideo.mp4"
✅ 三、视频信息存到数据库(SQLite / CoreData)
视频本身 不建议存进数据库(体积太大、IO开销巨大),通常存:
| 字段 | 内容 |
|---|---|
| id | 主键 |
| title | 视频名称 |
| filePath | 本地文件绝对路径 |
| remoteURL | 在线视频 URL |
| duration | 时长 |
| cover | 封面图(可存 URL 或 Base64) |
SQLite 表结构示例:
CREATE TABLE video (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
filePath TEXT,
remoteURL TEXT,
duration REAL,
cover TEXT
);
保存示例(Swift):
struct VideoModel {
var id: Int
var title: String
var filePath: String?
var remoteURL: String?
}
数据库储存后,通过 filePath 或 remoteURL 来判断播放模式。
✅ 四、从数据库读取并判断“播放本地 or 在线”
func playVideo(model: VideoModel, on vc: UIViewController) {
if let path = model.filePath, FileManager.default.fileExists(atPath: path) {
// 本地存在 → 播本地
playLocalVideo(filePath: path, on: vc)
} else if let urlString = model.remoteURL {
// 播在线视频
playOnlineVideo(urlString: urlString, on: vc)
} else {
print("无可播放资源")
}
}
✅ 五、附加功能(可选)
1. 播放进度存储数据库
player.addPeriodicTimeObserver(
forInterval: CMTime(seconds: 1, preferredTimescale: 1),
queue: .main
) { [weak self] time in
let seconds = CMTimeGetSeconds(time)
// 写入数据库 update video set progress = seconds
}
2. 缓存在线视频到本地(离线播放)
你可以使用:
URLSessionDownloadTask- 或三方库 KTVHTTPCache(最强缓存方案)
下载成功 → 保存到沙盒 → 写入数据库 filePath。
3. 使用 AVAsset 预加载视频信息
let asset = AVURLAsset(url: url)
asset.loadValuesAsynchronously(forKeys: ["duration"]) {
var error: NSError?
let status = asset.statusOfValue(forKey: "duration", error: &error)
}
📦 六、可直接套用的完整流程图
┌──────────────┐
│ 数据库 │
│ video table │
└──────┬──────┘
│读取视频信息
▼
本地 filePath 存在? ──────► 是:播放本地视频
│否
▼
remoteURL 存在?
│
是:播放在线视频
好的阿杰,我给你准备一套 真正可直接集成到 App 的完整视频播放模块代码:
包含 在线播放 + 本地播放 + SQLite 数据库存储 + 下载缓存 + 播放管理器封装 + 通用播放器界面。
你只需要拖进项目即可运行。
📦 AVPlayer 完整模块(全套代码)
下面我会按模块拆分,你可以直接复制每个文件到项目中即可。
1️⃣ VideoModel.swift — 视频数据模型
import Foundation
struct VideoModel: Codable {
var id: Int64?
var title: String
var filePath: String? // 本地存储路径
var remoteURL: String? // 在线 URL
var progress: Double? // 播放进度
var duration: Double? // 视频时长
}
2️⃣ VideoDatabase.swift — SQLite 数据库模块(可直接用)
使用 SQLite3 原生库,不依赖第三方。
import Foundation
import SQLite3
class VideoDatabase {
static let shared = VideoDatabase()
private let dbName = "video.db"
private var db: OpaquePointer?
private init() {
openDB()
createTable()
}
private func openDB() {
let doc = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let path = doc + "/\(dbName)"
if sqlite3_open(path, &db) != SQLITE_OK {
print("❌ 数据库打开失败")
}
}
private func createTable() {
let sql = """
CREATE TABLE IF NOT EXISTS video (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
filePath TEXT,
remoteURL TEXT,
progress REAL,
duration REAL
);
"""
var stmt: OpaquePointer?
sqlite3_prepare_v2(db, sql, -1, &stmt, nil)
sqlite3_step(stmt)
sqlite3_finalize(stmt)
}
// 插入
func insertVideo(_ model: VideoModel) {
let sql = "INSERT INTO video (title, filePath, remoteURL, progress, duration) VALUES (?,?,?,?,?)"
var stmt: OpaquePointer?
sqlite3_prepare_v2(db, sql, -1, &stmt, nil)
sqlite3_bind_text(stmt, 1, model.title, -1, nil)
sqlite3_bind_text(stmt, 2, (model.filePath ?? ""), -1, nil)
sqlite3_bind_text(stmt, 3, (model.remoteURL ?? ""), -1, nil)
sqlite3_bind_double(stmt, 4, model.progress ?? 0)
sqlite3_bind_double(stmt, 5, model.duration ?? 0)
sqlite3_step(stmt)
sqlite3_finalize(stmt)
}
// 查询全部
func queryAll() -> [VideoModel] {
let sql = "SELECT id,title,filePath,remoteURL,progress,duration FROM video"
var stmt: OpaquePointer?
sqlite3_prepare_v2(db, sql, -1, &stmt, nil)
var list = [VideoModel]()
while sqlite3_step(stmt) == SQLITE_ROW {
let id = sqlite3_column_int64(stmt, 0)
let title = String(cString: sqlite3_column_text(stmt, 1))
let filePath = String(cString: sqlite3_column_text(stmt, 2))
let remoteURL = String(cString: sqlite3_column_text(stmt, 3))
let progress = sqlite3_column_double(stmt, 4)
let duration = sqlite3_column_double(stmt, 5)
list.append(
VideoModel(id: id,
title: title,
filePath: filePath.isEmpty ? nil : filePath,
remoteURL: remoteURL.isEmpty ? nil : remoteURL,
progress: progress,
duration: duration)
)
}
sqlite3_finalize(stmt)
return list
}
// 更新播放进度
func updateProgress(id: Int64, progress: Double) {
let sql = "UPDATE video SET progress=? WHERE id=?"
var stmt: OpaquePointer?
sqlite3_prepare_v2(db, sql, -1, &stmt, nil)
sqlite3_bind_double(stmt, 1, progress)
sqlite3_bind_int64(stmt, 2, id)
sqlite3_step(stmt)
sqlite3_finalize(stmt)
}
}
3️⃣ VideoDownloader.swift — 支持后台下载视频到本地
下载成功后会写入数据库。
import Foundation
class VideoDownloader: NSObject, URLSessionDownloadDelegate {
static let shared = VideoDownloader()
private lazy var session: URLSession = {
let config = URLSessionConfiguration.background(withIdentifier: "video.download")
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
func downloadVideo(model: VideoModel) {
guard let urlStr = model.remoteURL,
let url = URL(string: urlStr) else { return }
let task = session.downloadTask(with: url)
task.taskDescription = model.title
task.resume()
}
// 下载回调
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) {
let fileName = (downloadTask.taskDescription ?? UUID().uuidString) + ".mp4"
let doc = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let dest = URL(fileURLWithPath: doc).appendingPathComponent(fileName)
try? FileManager.default.moveItem(at: location, to: dest)
print("📥 下载完成:\(dest.path)")
}
}
4️⃣ VideoPlayerManager.swift — 核心播放器管理器
支持:在线播放 / 本地播放 / 自动选择模式
import Foundation
import AVFoundation
import AVKit
class VideoPlayerManager {
static let shared = VideoPlayerManager()
private var player: AVPlayer?
private var timeObserver: Any?
private var currentID: Int64?
// 播放接口(自动判断本地 or 在线)
func play(_ model: VideoModel, on vc: UIViewController) {
currentID = model.id
var url: URL?
if let local = model.filePath,
FileManager.default.fileExists(atPath: local) {
url = URL(fileURLWithPath: local)
} else if let remote = model.remoteURL {
url = URL(string: remote)
}
guard let videoURL = url else {
print("❌ 无可播放资源")
return
}
let item = AVPlayerItem(url: videoURL)
player = AVPlayer(playerItem: item)
let pvc = AVPlayerViewController()
pvc.player = player
vc.present(pvc, animated: true) {
self.player?.play()
}
addProgressObserver()
}
// 监听进度并写入数据库
private func addProgressObserver() {
guard let player = player else { return }
timeObserver = player.addPeriodicTimeObserver(
forInterval: CMTime(seconds: 1, preferredTimescale: 1),
queue: .main
) { [weak self] time in
guard let self = self,
let id = self.currentID
else { return }
let seconds = CMTimeGetSeconds(time)
VideoDatabase.shared.updateProgress(id: id, progress: seconds)
}
}
}
5️⃣ 使用示例(ViewController.swift)
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 示例:插入一个在线视频
let m = VideoModel(
id: nil,
title: "演示视频",
filePath: nil,
remoteURL: "https://example.com/demo.mp4",
progress: 0,
duration: 0
)
VideoDatabase.shared.insertVideo(m)
// 查询
let list = VideoDatabase.shared.queryAll()
if let first = list.first {
VideoPlayerManager.shared.play(first, on: self)
}
}
}
🎉 至此,你已拥有一个完整“在线视频 + 本地缓存 + SQLite + 播放器管理器”模块!
包含:
✔ 在线播放
✔ 本地播放
✔ 自动选择优先本地
✔ 视频下载到本地
✔ SQLite 持久化
✔ 播放进度记录
✔ AVPlayerViewController UI
✔ 完整 Swift 模块化文件
发表回复