swiftUI使用VideoPlayer和AVPlayer播放视频

使用VideoPlayer包播放视频:https://github.com/wxxsw/VideoPlayer

提供一些可供测试的视频链接,不保证稳定可用哦:

https://vfx.mtime.cn/Video/2019/06/15/mp4/190615103827358781.mp4

https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4

https://vfx.mtime.cn/Video/2019/06/29/mp4/190629004821240734.mp4

https://vfx.mtime.cn/Video/2019/06/27/mp4/190627231412433967.mp4

https://vfx.mtime.cn/Video/2019/06/25/mp4/190625091024931282.mp4

https://vfx.mtime.cn/Video/2019/06/16/mp4/190616155507259516.mp4


https://vfx.mtime.cn/Video/2019/06/05/mp4/190605101703931259.mp4

安装VideoPlayer

使用spm安装包,直接在xcode项目的依赖包右键,添加包:

在右上角输入包地址:添加到项目中即可

使用VideoPlayer

在项目中导入:

import VideoPlayer

如果提示没有这个包:No such module 'VideoPlayer'

需要在项目的通用设置里面添加这个包到项目中:

选中 VideoPlayer 点击 add:

播放在线视频代码

直接使用在线视频url即可播放

    @State private var play: Bool = true

    var body: some View {
        VideoPlayer(url: URL(string: "你的视频URL链接")!, play: $play)
    }

实现的效果: 

播放本地视频

把视频添加到项目中,点击File > Add Files to 项目 > 选中要添加的视频,就可以了

然后在项目代码中添加视频:

// 本地文件
let localMp4Url = Bundle.main.url(forResource: "localMp4", withExtension: "mp4")


@State private var play: Bool = true


var body: some View {
    VideoPlayer(url: localMp4Url!, play: $play)
}

实现的效果:

视频控制

在视频上显示播放控制按钮等配置,可以通过给播放器绑定变量来实现控制

//
//  ContentView.swift
//  swiftPro
//
//  Created by song on 2024/5/21.
//

import Alamofire
import AVKit
import GIFImage
import Kingfisher
import SwiftUI
import VideoPlayer

private var videoURLs: [URL] = [
    URL(string: "https://vfx.mtime.cn/Video/2019/06/29/mp4/190629004821240734.mp4")!,
    URL(string: "https://vfx.mtime.cn/Video/2019/06/27/mp4/190627231412433967.mp4")!,
    URL(string: "https://vfx.mtime.cn/Video/2019/06/25/mp4/190625091024931282.mp4")!,
    URL(string: "https://vfx.mtime.cn/Video/2019/06/16/mp4/190616155507259516.mp4")!,
    URL(string: "https://vfx.mtime.cn/Video/2019/06/15/mp4/190615103827358781.mp4")!,
    URL(string: "https://vfx.mtime.cn/Video/2019/06/05/mp4/190605101703931259.mp4")!,
]

struct ContentView: View {
    // 本地文件
    let localMp4Url = Bundle.main.url(forResource: "localMp4", withExtension: "mp4")

    // 远程视频
    let remoteUrl = URL(string: "https://vfx.mtime.cn/Video/2019/06/15/mp4/190615103827358781.mp4")
    
    // 获取当前播放时间
    func getTimeString() -> String {
        let m = Int(time.seconds / 60)
        let s = Int(time.seconds.truncatingRemainder(dividingBy: 60))
        return String(format: "%d:%02d", arguments: [m, s])
    }
    
    // 获取视频所有的时间
    func getTotalDurationString() -> String {
        let m = Int(totalDuration / 60)
        let s = Int(totalDuration.truncatingRemainder(dividingBy: 60))
        return String(format: "%d:%02d", arguments: [m, s])
    }
    
    // 视频列表索引
    @State var index = 0
    // 播放状态
    @State private var play: Bool = true
    // 视频播放时间
    @State private var time: CMTime = .zero
    // 是否自动播放
    @State private var autoReplay: Bool = true
    // 是否开启声音(mute:沉默的,无声的)
    @State private var mute: Bool = false
    // 视频播放状态文字提示
    @State private var stateText: String = ""
    // 总共持续时间
    @State private var totalDuration: Double = 0
    // 播放速度
    @State private var speedRate: Float = 1.2

    // 使用状态来跟踪播放状态
    @State private var isPlaying = false

    var body: some View {
        VStack(content: {
            // 视频播放控制是通过绑定变量来实现的
            VideoPlayer(url: videoURLs[index % videoURLs.count], play: $play, time: $time)
                .autoReplay(autoReplay)
                .mute(mute)
                .speedRate(speedRate)
                .onBufferChanged { progress in print("onBufferChanged \(progress)") }
                .onPlayToEndTime { print("onPlayToEndTime") }
                .onReplay { print("onReplay") }
                .onStateChanged { state in
                    switch state {
                    case .loading:
                        self.stateText = "Loading..."
                    case .playing(let totalDuration):
                        self.stateText = "Playing!"
                        self.totalDuration = totalDuration
                    case .paused(let playProgress, let bufferProgress):
                        self.stateText = "Paused: play \(Int(playProgress * 100))% buffer \(Int(bufferProgress * 100))%"
                    case .error(let error):
                        self.stateText = "Error: \(error)"
                    }
                }
                .aspectRatio(1.78, contentMode: .fit)
                .cornerRadius(16)
                .shadow(color: Color.black.opacity(0.7), radius: 6, x: 0, y: 2)
                .padding()
            
            // 视频状态
            Text(stateText)
                .padding()
            
            // 视频控制:暂停/声音控制/自动重播/后退前进5秒/下一个视频
            HStack {
                Button(self.play ? "Pause" : "Play") {
                    self.play.toggle()
                }

                Divider().frame(height: 20)

                Button(self.mute ? "Sound Off" : "Sound On") {
                    self.mute.toggle()
                }

                Divider().frame(height: 20)

                Button(self.autoReplay ? "Auto Replay On" : "Auto Replay Off") {
                    self.autoReplay.toggle()
                }
            }

            HStack {
                Button("Backward 5s") {
                    self.time = CMTimeMakeWithSeconds(max(0, self.time.seconds - 5), preferredTimescale: self.time.timescale)
                }

                Divider().frame(height: 20)
                
                Text("\(getTimeString()) / \(getTotalDurationString())")

                Divider().frame(height: 20)

                Button("Forward 5s") {
                    self.time = CMTimeMakeWithSeconds(min(self.totalDuration, self.time.seconds + 5), preferredTimescale: self.time.timescale)
                }
            }

            Button("Next Video") { self.index += 1 }
            
        })
    }
}

#Preview {
    ContentView()
}

播放效果:

使用AVPlayer

导入AVPlayer:

import AVKit
import VideoPlayer

创建一个player:

注意视频链接协议要为https的,http的视频链接需要单独设置

    // 使用状态来跟踪播放状态
    @State private var isPlaying = false
    // AVPlayer 实例
    private let player = AVPlayer(url: URL(string: "https://vfx.mtime.cn/Video/2019/06/15/mp4/190615103827358781.mp4")!)



// 在ui层放入播放器
    var body: some View {
        VStack {
            // 视频播放器控件
            VideoPlayer(player: player)
                .ignoresSafeArea()
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .onAppear {
                    player.play()
                }
        }
    }

加载本地视频资源:

需要将本地视频资源加载到项目中,然后通过Bundle引入

    // 加载本地资源
    let player = AVPlayer(url: Bundle.main.url(forResource: "localMp4", withExtension: "mp4")!)

    var body: some View {
        VideoPlayer(player: player)
            .ignoresSafeArea()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .onAppear {
                player.play()
            }
    }

 

显示效果:

播放也没有问题,视频控制也没有问题,很不错了

ipad的效果:果然是看剧神器,视觉效果还是不错的

可以添加自定义控制:

struct VideoPlayerView: View {
    // 使用状态来跟踪播放状态
    @State private var isPlaying = false
    // AVPlayer 实例
    private let player = AVPlayer(url: URL(string: "https://vfx.mtime.cn/Video/2019/06/15/mp4/190615103827358781.mp4")!)
 
    var body: some View {
        VStack {
            // 视频播放器控件
            AVPlayerViewController()
                .embed(player: player)
            // 添加自定义控制
            Button(action: togglePlay) {
                Text(isPlaying ? "暂停" : "播放")
            }
        }
    }
    
    // 切换播放状态的方法
    func togglePlay() {
        isPlaying ? player.pause() : player.play()
        isPlaying.toggle()
    }
}
 

extension AVPlayerViewController {
    func embed(player: AVPlayer) -> some View {
        return VideoPlayer(player: player)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .edgesIgnoringSafeArea(.all)
            .onAppear {
                player.play()
            }
            .onDisappear {
                player.pause()
            }
    }
}

效果:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/675872.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于ssh的实验室设备管理系统java项目实验室管理系统spring项目jsp项目

文章目录 实验室设备管理系统一、项目演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目源码(9.9¥带走) 实验室设备管理系统 一、项目演示 实验室设备管理系统 二、项目介绍 基于sshjsp的实验室设备管理系统 系统角色…

6-Django项目--分页模块化封装参数共存

目录 utils/page_data.py 分页模块化封装 在app当中创建一个python package 在当前包里面创建py文件 参数共存 完整代码 utils/page_data.py --包里创建py文件. # -*- coding:utf-8 -*- from django.utils.safestring import mark_safe from copy import deepcopyclass…

网线水晶头为什么要按标准线序打

网线接水晶头为什么要按照线序接? 减少串扰和增强信号质量: 双绞线的设计是为了减少信号间的串扰( Crosstalk),每一对线芯在传输过程中通过相互扭绞抵消外部电磁干扰。按照标准线序接线能够确保每一对线芯之间的信号传…

maridb10.4.30数据库数据迁移

1.新建数据存储文件夹,例如E:\maridb_data 2.修改原数据所在目录的my.ini文件,例如D:\Program Files\MariaDB 10.4\data\my.ini 3.剪切除my.ini文件外的其他所有文件到迁移目的地文件(E:\maridb_data) 结果如下: 原数据文件目录&#xff1a…

MySQL—约束—外键约束(基础)

一、引言 概念:外键用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性。 举个例子: 提示说明:(有两张表) (1)员工表:emp id:主键、姓名、年龄、…

qt+ffmpeg 实现音视频播放(四)之音视频同步

在处理音视频数据时,解码音频的数据往往会比解码视频的数据比较慢,所以我们在播放音视频时,音频和视频的数据会出现渐渐对不上的情况。尤其在播放时间越长的时候,这种对不上的现象越明显。 为了解决这一问题,人们想出…

485通讯网关

在工业自动化与智能化的浪潮中,数据的传输与交互显得尤为重要。作为这一领域的核心设备,485通讯网关凭借其卓越的性能和广泛的应用场景,成为了连接不同设备、不同协议之间数据转换和传输的桥梁。在众多485通讯网关中,HiWoo Box以其…

MySQL报ERROR 2002 (HY000)解决

今天在连接客户服务器时MySQL的时候报: ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/tmp/mysql/mysql.sock’ (2) [rootXXX ~]# mysql -uroot -p Enter password: ERROR 2002 (HY000): Can’t connect to local MySQL server through socket…

SpringBoot邮件发送的安全性如何有效保障?

SpringBoot邮件发送服务如何集成?怎么优化邮件发送? Spring Boot作为一个流行的Java开发框架,提供了便捷的邮件发送功能,使得开发者可以轻松地集成邮件发送到他们的应用程序中。AokSend将探讨如何有效地保障Spring Boot邮件发送的…

linux驱动学习(四)之module

一、内核模块 内核模块是一种可以动态加载到操作系统内核中并扩展其功能的软件。它们允许在运行的操作系统内核中增加新的功能或驱动程序,而无需重新启动计算机。 在linux系统中,驱动程序是各自独立存在的,而且驱动程序中包含一个moudle&am…

抽屉式备忘录(共25041字)

Sing Me to Sleep <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>与妖为邻的备忘录</title&g…

【UE5.1 角色练习】09-物体抬升、抛出技能 - part1

前言 在上一篇&#xff08;【UE5.1 角色练习】08-传送技能&#xff09;的基础上继续实现控制物体抬升、抛出的功能。 效果 步骤 一、准备技能动画 1. 在项目设置中新建一个操作映射&#xff0c;这里命名为“Skill_GravityControl”&#xff0c;用按键4触发 2. 通过IK重定向…

ESP使用巴法云远程OTA(VScode + Platform io)

ESP使用巴法云远程OTA&#xff08;Platform&#xff09; 什么是OTA&#xff1a; OTA&#xff08;Over-the-AirTechnology&#xff09;即空中下载技术&#xff0c;是通过移动通信的空中接口实现对移动终端设备及SIM卡数据进行远程管理的技术。OTA升级是物联网&#xff08;IOT&am…

[深度学习]yolov10+deepsort+pyqt5实现目标追踪

YOLOv10DeepSORTPyQt5实现目标追踪系统 在现代智能监控系统中&#xff0c;目标追踪技术扮演着至关重要的角色。结合YOLOv10&#xff08;一种先进的实时目标检测算法&#xff09;与DeepSORT&#xff08;一种多目标追踪算法&#xff09;&#xff0c;并通过PyQt5构建用户界面&…

想要做好海外广告投放?建议这几点先了解清楚

在全球市场竞争日益激烈的今天&#xff0c;跨境电商和出海营销早已经成为了热门选择&#xff0c;随之而来海外广告的投放也受到众多人的青睐。无论是想要提升品牌知名度&#xff0c;还是实现销售增长&#xff0c;正确的广告投放都能事半功倍。错误的选择不仅浪费资源&#xff0…

【Qt秘籍】[006]-Label实现Hello World程序-编程第一步

"Hello,World!" 中文意思是“你好&#xff0c;世界”。 因为 The C Programming Language 中使用它做为第一个演示程序&#xff0c;后来很多程序员在学习编程或进行设备调试时延续了这一习惯。 下面&#xff0c;我们也将演示利用Label显示Qt中的"Hello World!&q…

深度学习笔记:0.cuda安装,成功

B站上说&#xff1a;cs上骗子太多。文章太久&#xff0c;我深以为然。用了一天。才装好。其实很简单。 CUDA安装教程&#xff08;超详细&#xff09;-CSDN博客文章浏览阅读1w次&#xff0c;点赞5次&#xff0c;收藏56次。windows10 版本安装 CUDA &#xff0c;首先需要下载两个…

秋招突击——第四弹——Java的SSN框架快速入门——Spring(2)

文章目录 前言其他Spring加载properties 容器创建容器获取beanBeanFactory容器总结 注解注解开发对定义bean纯注解开发Bean管理Bean作用范围Bean生命周期 注解开发依赖注入第三方bean管理第三方bean管理第三方bean注入 注解开发总结 Spring整合整合mybatis整合Junit AOPAOP核心…

Mybatis实现树形结构方式

1&#xff0c;三级分类树形结构查询 /*** DDD(Domain-Driven Design): 领域驱动设计** 三级分类树形结构&#xff1b;* 支持无限层级&#xff1b;* 当前项目只有三级*/ Data public class CategoryTreeTo {private Long categoryId; //1private String categoryName;private …

外观数列 ---- 模拟

题目链接 题目: 分析: 题目的意思如下:所以我们需要引用双指针来找到连续的字符有几个, 并添加到答案中, 接着将此字符添加到答案中, 让left right , 继续向后遍历整个字符串, 重复上面的操作将答案重新赋给字符串, 继续重复上述操作, 应该重复n - 1 次, 因为n为1的时候, 直…