这是一个纯粹的软件包,没有任何第三方库。我的主要重点是性能。特别是如果您需要在背景中添加视频作为设计元素,在这种情况下,您会希望使用一个轻量级的组件,而不需要很多不必要的功能。希望它能对您有所帮助。
Apple 的 VideoPlayer 为 SwiftUI 中的视频播放提供了快速设置。但是,它不允许您隐藏或自定义默认的视频控件 UI,从而限制了其在自定义场景中的使用。或者,您需要使用 AVPlayerViewController,它包含大量功能,仅仅是为了禁用控件。相比之下,此解决方案充当模块化框架,允许您仅集成所需的功能,同时保持核心组件的轻量级。它提供了对播放的完全控制,包括添加自定义 UI 元素的能力,使其非常适合背景视频、工具提示、视频提示和其他自定义场景。此外,它还支持高级功能,例如字幕、无缝循环和实时滤镜应用。它还允许向视频流添加矢量图形,访问帧数据以进行自定义过滤,以及将 ML 或 AI 算法应用于视频处理,从而实现广泛的增强和修改等等。
此软件包使用声明式方法来声明视频组件的参数,这些参数基于构建块。此实现可能会让您深入了解 SwiftUI 在底层是如何工作的。您也可以通过常用方式传递参数。如果要分析软件包的性能,请在真机上进行。
ExtVideoPlayer{
VideoSettings{
SourceName("swipe")
}
}
或
ExtVideoPlayer(fileName: 'swipe')
播放器的功能围绕双向 ⇆ 交互模型设计
命令和设置:通过这些,您可以指示播放器做什么以及如何做。设置定义环境和初始状态,而命令提供实时控制。就目前而言,您可以方便地逐个传递命令;也许稍后我会添加对批量命令的支持
事件反馈:通过事件处理,播放器与应用程序进行通信,告知应用程序可能需要注意的内部更改。由于媒体播放器的性质,尤其是在具有动态内容或用户交互的环境中,事件流可能会变得泛滥。为了有效地管理这一点并防止应用程序被大量传入事件淹没,系统每秒收集这些事件,并将它们作为批量返回。在 settings
中传递 Events()
以启用事件机制。
功能类别 | 功能名称 | 描述 |
---|---|---|
通用 | SwiftUI 声明式语法 | 使用声明式语法轻松集成。 |
平台兼容性 | 支持 iOS 14+,macOS 11+,tvOS 14+。 | |
Swift 兼容性 | 与 Swift 5 对齐,并为 Swift 6 做好准备 | |
循环播放 | 视频结束时自动重新开始。 | |
本地和远程视频 URL | 支持从本地文件或远程 URL 播放。 | |
自适应 HLS 流媒体 | 处理具有动态质量调整的 HLS 流媒体。 | |
错误处理 | 可自定义的错误消息和可视化显示。 | |
字幕支持 | 添加外部 .vtt 文件或使用嵌入式字幕轨道。 |
|
自定义叠加层 | 在视频上添加矢量图形和自定义叠加层。 | |
画中画 (PiP) | iOS 和 iPadOS 上支持画中画 (PiP) | |
播放命令 | 空闲命令 | 初始化时不执行特定的播放操作。 |
播放/暂停 | 控制播放状态。 | |
Seek 命令 | 移动到特定的视频时间戳。 | |
静音/取消静音 | 切换音频播放。 | |
音量控制 | 调整音量级别。 | |
播放速度 | 动态修改播放速度。 | |
循环/取消循环 | 切换循环行为。 | |
应用滤镜 | 向视频流添加 Core Image 滤镜。 | |
移除滤镜 | 清除所有已应用的滤镜。 | |
添加矢量图形 | 将自定义矢量图形叠加到视频上。 | |
设置 | SourceName | 定义视频源(本地或远程)。 |
文件扩展名 | 视频文件的默认扩展名(例如,.mp4 )。 |
|
Gravity | 设置内容调整大小的行为(例如,.resizeAspect )。 |
|
时间发布 | 控制播放时间报告间隔。 | |
AutoPlay | 切换加载时自动播放。 | |
默认静音 | 初始化播放时不发出声音。 | |
字幕集成 | 配置来自嵌入式轨道或外部文件的字幕。 | |
视觉功能 | 圆角 | 使用 SwiftUI 的 .mask 修饰符应用圆角。 |
叠加图形 | 在视频上添加矢量图形以获得自定义效果。 | |
亮度调整 | 动态控制亮度级别。 | |
对比度调整 | 实时修改视频对比度。 | |
播放功能 | 自适应 HLS 流媒体 | 基于网络速度的动态质量调整。 |
无缝项目过渡 | 视频项目之间的平滑过渡。 | |
多声道音频 | 播放杜比全景声、5.1 环绕声和空间音频轨道。 | |
字幕和隐藏式字幕 | 支持多种字幕和隐藏式字幕格式。 | |
事件处理 | 批量事件处理 | 批量收集和处理事件以避免泛滥。 |
播放状态事件 | playing 、paused 、seek 、duration(CMTime) 等。 |
|
当前项目状态 | 检测当前项目何时更改或被移除。 | |
音量更改事件 | 监听音量级别的更改。 | |
测试与开发 | 单元测试 | 包括核心功能的单元测试。 |
UI 测试 | 示例应用中集成了 UI 测试。 | |
示例脚本 | 用于简化测试执行的自动化测试脚本。 | |
媒体支持 | 文件类型 | .mp4 、.mov 、.m4v 、.3gp 、.mkv (有限支持)。 |
编解码器 | H.264、H.265 (HEVC)、MPEG-4、AAC、MP3。 | |
流媒体协议 | 支持 HLS (.m3u8 ) 自适应流媒体。 |
您可以通过蒙版修饰符轻松实现此效果
ExtVideoPlayer(
settings : $settings,
command: $playbackCommand,
VideoSettings{
SourceName("swipe")
}
)
.mask{
RoundedRectangle(cornerRadius: 25)
}
该软件包包含涵盖关键功能的单元测试。虽然不是详尽的,但这些测试有助于确保核心组件按预期工作。UI 测试正在进行中,并在示例应用程序中开发。run_tests.sh 是一个示例脚本,它通过将测试命令封装到单个可执行文件中来自动化测试,从而简化执行过程。您可以配置脚本以运行与您的项目相关的特定测试环境。
请注意,使用来自 URL 的视频需要确保您有权使用和流式传输这些视频。由于其服务条款的限制,无法直接使用托管在 YouTube 等平台上的视频。
属性/方法 | 类型 | 描述 |
---|---|---|
settings |
Binding<VideoSettings> |
视频播放器设置的绑定,用于配置播放器行为的各个方面。 |
command |
Binding<PlaybackCommand> |
用于控制播放操作(例如播放、暂停或 seek)的绑定。 |
init(fileName:ext:gravity:timePublishing command:) |
构造函数 | 使用特定的视频参数(例如文件名、扩展名、gravity、时间发布和一个播放命令绑定)初始化播放器。 |
init(settings: () -> VideoSettings, command:) |
构造函数 | 以声明方式使用设置块和播放命令绑定初始化播放器。 |
init(settings: Binding<VideoSettings>, command:) |
构造函数 | 使用视频设置和播放命令的绑定初始化播放器。 |
名称 | 描述 | 默认值 |
---|---|---|
SourceName | 直接 URL 字符串 如果名称表示有效的 URL (HTTP 等)。 本地文件 URL 如果名称是有效的本地文件路径 (file:// 方案)。 Bundle 资源 它尝试使用 Bundle.main.url(forResource:withExtension:) 在主 bundle 中查找文件 |
- |
Ext | 视频的文件扩展名,用于从本地资源加载时。当提供 URL 且 URL 以视频文件扩展名结尾时,此项是可选的。 | "mp4" |
Gravity | 视频内容应如何调整大小以适应播放器的边界。 | .resizeAspect |
Loop | 视频到达结尾时是否应自动重新开始。如果未明确传递,视频将不会循环。 | false |
Mute | 指示视频是否应在没有声音的情况下播放。 | false |
NotAutoPlay | 指示视频在初始化后是否不应播放。请注意,如果您使用 command 作为播放器的控制流,则启动命令应为 .idle |
false |
TimePublishing | 指定播放器发布当前播放时间的间隔。 | 未启用 |
Subtitles | 要与视频合并的 WebVTT (.vtt) 字幕文件的 URL 或本地文件名。使用软件包中当前使用的 AVMutableComposition 方法,您无法直接更改字幕的位置或大小。AVFoundation 内置的“文本”轨道处理只是以默认样式呈现它们,而不允许其他布局选项。请查看示例应用 Video8.swift 中的实现 | 未启用 |
EnableVector | 使用此结构激活允许通过命令添加基于矢量的叠加层的设置。如果未通过设置传递,则任何 addVector 或 removeAllVectors 命令都将无效。 |
未启用 |
PictureInPicture | 启用画中画 (PiP) 支持。如果未传递,则任何类似 startPiP 或 stopPiP 的命令都无效。请查看示例应用 Video11.swift。它在模拟器上不起作用。您只能在真机上观察到此功能。 |
未启用 |
Events([.durationAny, .itemStatusChangedAny]) | 如果在 settings: VideoSettings 中未传递 Events() ,则事件机制将被禁用。您可以精确指定要接收哪些事件(例如,.itemStatusChangedAny),或者只需传递 Events() 即可接收所有可用事件。添加此设置是为了提高性能,因为事件是通过 @State 更改发出的,这会触发视图更新。如果您不需要观察任何事件,禁用它们可以显着提高性能。请查看示例应用 Video8.swift 中的实现 |
未启用 |
时间发布: 如果在初始化期间传递此参数,播放器将根据输入设置发布时间。您可以仅传递 TimePublishing
而不带任何值,以使用 1 秒的默认间隔,或者您可以传递特定的 CMTime
值来设置自定义间隔。| 1 秒(CMTime,1 秒,首选时间刻度为 600)如果未提供 TimePublishing
,播放器将不会发出时间事件,这可以在不需要时间信息时提高性能。
SourceName: 如果提供了有效的 URL(http 或 https),视频将从 URL 流式传输。如果不是 URL,系统将检查本地 bundle 中是否存在具有给定名称的视频。提供的本地名称可以包含扩展名,也可以不包含。系统首先检查本地名称是否包含扩展名。如果本地名称包含扩展名,则提取此扩展名并将其用作默认扩展名。如果本地名称不包含扩展名,系统将分配默认扩展名 .mp4 默认文件扩展名可以通过 Ext 参数设置。
Loop: 视频到达结尾时是否应自动重新开始。如果未明确传递,视频将不会循环。
@State public var playbackCommand: PlaybackCommand = .idle
@State
更新在 SwiftUI 中是异步和批处理的。当您分配
playbackCommand = .play
playbackCommand = .pause
SwiftUI 仅在同一运行循环周期中注册最后一次分配 (.pause
),而忽略 .play
。为了确保在 .pause 之前应用 .play,您可以使用 Task
将第二次更新安排在下一个运行循环迭代中
.play → .pause
playbackCommand = .play
Task {
playbackCommand = .pause
}
.play → .pause → .play
playbackCommand = .play
Task {
playbackCommand = .pause
Task { playbackCommand = .play } // This runs AFTER `.pause`
}
在 SwiftUI 应用程序中使用视频播放器控件时,重要的是要了解命令处理的工作方式。具体来说,连续发出两个相同的命令将导致第二个命令被忽略。这是由于底层实现阻止冗余命令执行,以优化性能和用户体验(就 UI 更新而言)。
例如,如果您尝试连续两次暂停视频播放器,则第二个暂停命令将无效,因为播放器已处于暂停状态。同样,如果视频已经在播放,发送两个连续的播放命令也不会重新触发播放。
在某些情况下,您需要重新发出一个可能看起来是冗余的命令,但在特定条件下是必要的,您必须在两个相似的命令之间插入一个 idle
命令。idle
命令重置播放器的命令状态,允许后续命令被视为新操作进行处理。
.play → .idle → .play
playbackCommand = .play
Task {
playbackCommand = .idle
Task { playbackCommand = .play } // This runs AFTER `.idle`
}
命令 | 描述 |
---|---|
idle |
启动时不执行任何操作。初始化期间传递的任何命令都将被执行。如果您希望在不基于设置值执行任何操作的情况下启动,只需将命令设置为 .idle |
play |
播放视频的命令。 |
pause |
暂停视频的命令。 |
seek(to: Double, play: Bool) |
Seek 到视频中特定时间的命令。time 参数指定目标位置,以秒为单位。如果 time 为负数,播放将跳转到视频的开头。如果 time 超过视频的持续时间,播放将移动到视频的结尾。对于视频持续时间内的有效值,播放将精确移动到指定时间。play 参数确定在 seek 后是否应自动恢复播放,默认值为 true。 |
begin |
将视频定位到开头的命令。 |
end |
将视频定位到结尾的命令。 |
mute |
静音视频的命令。默认情况下,播放器处于静音状态。 |
unmute |
取消静音视频的命令。 |
volume(Float) |
调整视频播放音量的命令。volume 参数是一个 Float 值,介于 0.0(静音)和 1.0(最大音量)之间。如果传递的值超出此范围,它将被限制为最接近的有效值(0.0 或 1.0)。 |
playbackSpeed(Float) |
调整视频播放速度的命令。speed 参数是一个 Float 值,表示播放速度(例如,1.0 表示正常速度,0.5 表示半速,2.0 表示双倍速度)。如果传递负值,它将被限制为 0.0。 |
loop |
启用视频播放循环的命令。默认情况下,循环已启用,因此如果循环已激活,则此命令将无效。 |
unloop |
禁用视频播放循环的命令。此命令仅在视频当前正在循环时才生效。 |
startPiP |
启动视频播放的画中画 (PiP) 模式的命令。如果 PiP 功能已激活,则此命令将不会产生额外的效果。不要忘记在设置中添加 PictureInPicture() 以启用 PiP 功能。 |
stopPiP |
终止画中画 (PiP) 模式的命令,将视频播放返回到其内联视图。如果 PiP 未激活,则此命令将无效。不要忘记在设置中添加 PictureInPicture() 以启用 PiP 功能。 |
命令 | 描述 |
---|---|
brightness(Float) |
调整视频播放亮度的命令。brightness 参数是一个 Float 值,通常范围从 -1.0(最暗)到 1.0(最亮)。超出此范围的值将被限制为最接近的有效值。 |
contrast(Float) |
调整视频播放对比度的命令。contrast 参数是一个 Float 值,通常范围从 0.0(无对比度)到 4.0(高对比度)。超出此范围的值将被限制为最接近的有效值。 |
filter(CIFilter, clear: Bool) |
将特定的 Core Image 滤镜应用于视频。如果 clear 为 true,则在应用新滤镜之前,将移除堆栈上的任何现有滤镜;否则,新滤镜将添加到现有堆栈中。 |
removeAllFilters |
移除视频播放中所有已应用滤镜的命令。 |
命令 | 描述 |
---|---|
addVector(ShapeLayerBuilderProtocol, clear: Bool) |
在视频流上添加矢量图形层的命令。builder 参数是符合 ShapeLayerBuilderProtocol 的实例。clear 参数指定在添加新矢量层之前是否清除现有矢量层。 |
removeAllVectors |
从视频流中移除所有矢量图形层的命令。 |
boundsChanged(CGRect)
)。当叠加层或自定义矢量层需要动态适应视频播放器尺寸或其他布局调整的变化时,此方法特别有用。为了有效地处理频繁的 boundsChanged 事件并提高性能,您可以使用节流函数来限制更新发生的频率。命令 | 描述 |
---|---|
audioTrack(String) |
根据语言代码选择特定音轨的命令。languageCode 参数指定所需音轨的语言(例如,“en”表示英语)。 |
subtitles(String?) |
此命令将字幕设置为指定的语言或将其关闭。提供语言代码(例如,"en" 表示英语)以显示该语言的字幕,或传递 nil 以完全禁用字幕。注意:这仅在视频文件具有嵌入式字幕轨道时适用。 |
此功能旨在用于视频文件已包含嵌入在其元数据中的多个字幕轨道(即,可读媒体轨道)的用例。换句话说,容器格式(例如 MP4、MOV 或 QuickTime)包含一个或多个字幕或隐藏式字幕轨道,可以在运行时选择。通过调用此函数并提供语言代码(例如,“en”、“fr”、“de”),您可以指示组件在资产的媒体选择组中查找相应的字幕轨道。如果找到匹配项,它将激活该字幕轨道;否则,将不会显示字幕。传递 nil 会完全禁用字幕。当您想要在多种嵌入式字幕语言之间切换或关闭它们而无需依赖外部字幕文件(如 SRT 或 WebVTT)时,此方法非常方便。
添加字幕的另一种选择是使用设置(请查看上面),您可以在其中将字幕作为单独的源文件(例如,SRT 或 WebVTT)提供。在这种情况下,字幕是动态加载和管理的,与视频并行,而无需将它们嵌入到视频文件中。这两种方法——使用嵌入式字幕轨道或通过设置添加字幕作为外部文件——都不会合并和本地保存带有字幕的结果视频。相反,字幕在播放期间动态呈现。
配置带有英文字幕的 HLS 播放列表
这是一个配置了英文字幕的 HLS 播放列表示例。字幕被定义为使用 WebVTT 或类似格式的单独轨道,并在主播放列表中引用。此设置允许在视频播放期间无缝呈现字幕,与视频流同步。
#EXTM3U
#EXT-X-MEDIA:TYPE=SUBTITLES,
GROUP-ID="subs",
NAME="English Subtitles",
LANGUAGE="en",
AUTOSELECT=YES,
DEFAULT=YES,
URI="subtitles_en.m3u8"
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=3000000,
RESOLUTION=1280x720,
SUBTITLES="subs"
video_main.m3u8
如果在 settings: VideoSettings
中未传递 Events()
,则事件机制将被禁用。您可以精确指定要接收哪些事件(例如,.itemStatusChangedAny),或者只需传递 .all 即可接收所有可用事件。添加此设置是为了提高性能,因为事件是通过 @State 更改发出的,这会触发视图更新。如果您不需要观察任何事件,禁用它们可以显着提高性能。
事件 | 描述 |
---|---|
seek(Bool, currentTime: Double) |
表示播放器内的 seek 结束操作。第一个参数 (Bool ) 指示 seek 是否成功,第二个参数 (currentTime ) 提供播放器正在 seek 的时间(以秒为单位)。 |
paused |
指示播放器的播放当前已暂停。当播放器被用户手动暂停或通过 pause() 等方法以编程方式暂停时,会发生此状态。播放器在此状态下不播放任何内容。 |
waitingToPlayAtSpecifiedRate |
指示播放器当前正在等待以指定的速率播放。此状态通常在播放器缓冲或等待足够的数据以继续播放时发生。如果播放速率由于外部因素(例如网络条件或系统资源限制)而暂时降至零,也可能发生这种情况。 |
playing |
指示播放器正在主动播放内容。当播放器当前以指定的播放速率播放视频或音频内容时,会发生此状态。这是媒体被渲染给用户的活动状态。 |
currentItemChanged |
当播放器的 currentItem 更新为新的 AVPlayerItem 时触发。此事件指示当前正在播放的媒体项目发生了更改。 |
currentItemRemoved |
当播放器的 currentItem 设置为 nil 时发生,指示当前媒体项目已从播放器中移除。 |
error(VPErrors) |
表示播放器内发生错误。该事件提供一个 VPErrors 枚举值,指示遇到的特定错误类型。 |
volumeChanged |
当播放器的音量级别被调整时发生。此事件提供新的音量级别,范围从 0.0(静音)到 1.0(最大音量)。 |
boundsChanged(CGRect) |
当主图层的边界更改时触发,允许开发人员重新计算和更新 CompositeLayer 中的所有矢量层。 |
startedPiP |
当画中画 (PiP) 模式开始时触发的事件。 |
stoppedPiP |
当画中画 (PiP) 模式停止时触发的事件。 |
itemStatusChanged(AVPlayerItem.Status) |
指示 AVPlayerItem 的状态已更改。可能的Status:.unknown 、.readyToPlay 、.failed 。 |
duration(CMTime) |
在 AVPlayerItem 准备好播放时提供其持续时间。持续时间以 CMTime 给出。 |
PlayerEventFilter
- 此枚举提供了一种结构化的方式来过滤 PlayerEvent
case。我决定引入一个与 PlayerEvent
对应的附加结构。所有带参数的 case 都被复制为 eventNameAny,以匹配该事件的任何变体,而不管其关联的值如何。如果您需要匹配特定事件参数的特定事件,请告诉我,我会添加它们。
ExtVideoPlayer{
VideoSettings{
SourceName("swipe")
Events([.durationAny, .itemStatusChangedAny]) // *Events([PlayerEventFilter])*
}
}
.onPlayerEventChange { events in
// Here come only events [.durationAny, .itemStatusChangedAny]: any duration and any item status change events.
}
过滤器 | 描述 |
---|---|
seekAny |
匹配任何 .seek(...) case,无论 seek 是否成功 (Bool ) 或目标 seek 时间 (currentTime: Double )。 |
paused |
完全匹配 .paused 事件,指示播放已被用户或以编程方式暂停。 |
waitingToPlayAtSpecifiedRate |
完全匹配 .waitingToPlayAtSpecifiedRate 事件,当播放器缓冲或等待足够的数据时发生。 |
playing |
完全匹配 .playing 事件,指示播放器正在主动播放媒体。 |
currentItemChangedAny |
匹配任何 .currentItemChanged(...) case,当播放器的 currentItem 更新为新的媒体项目时触发。 |
currentItemRemoved |
完全匹配 .currentItemRemoved 事件,当播放器的 currentItem 设置为 nil 时发生。 |
errorAny |
匹配任何 .error(...) case,表示播放器内的错误,带有 VPErrors 枚举指示特定问题。 |
volumeChangedAny |
匹配任何 .volumeChanged(...) case,当播放器的音量级别被调整时触发。 |
boundsChangedAny |
匹配任何 .boundsChanged(...) case,当主图层的边界更改时触发。 |
startedPiP |
完全匹配 .startedPiP 事件,当画中画 (PiP) 模式开始时触发。 |
stoppedPiP |
完全匹配 .stoppedPiP 事件,当画中画 (PiP) 模式停止时触发。 |
itemStatusChangedAny |
匹配任何 .itemStatusChanged(...) case,指示 AVPlayerItem 的状态已更改(例如,.unknown 、.readyToPlay 、.failed )。 |
durationAny |
匹配任何 .duration(...) case,它提供媒体项目在准备好播放时的持续时间。 |
当 URL 在语法上有效但资源实际上不存在时(例如,404 响应或无法访问的服务器),AVPlayerItem.status 可能会无限期地保持 .unknown 状态。它可能永远不会转换为 .failed,并且如果播放从未开始,则 .AVPlayerItemFailedToPlayToEndTime 通知将不会触发。
解决方法和最佳实践 使用 HEAD 预先检查 URL
如果您想确保 URL 在传递给组件 (AVPlayerItem) 之前有效,请使用例如通过 URLSession 的简单 HEAD 请求来检查有效的 2xx 响应。
func checkURLExists(_ url: URL) async throws -> Bool {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
let (_, response) = try await URLSession.shared.data(for: request)
if let httpResponse = response as? HTTPURLResponse {
return (200...299).contains(httpResponse.statusCode)
}
return false
}
当您使用 addVector
命令时,您可以动态地在视频流上添加新的矢量图形层(例如徽标或动画矢量)。这对于通过叠加层(例如品牌元素、动画图形)增强用户体验特别有用。
添加矢量层:
addVector
命令接受 ShapeLayerBuilderProtocol
实例。此协议定义了基于给定几何图形(frame、bounds)构建 CAShapeLayer
的必要方法。clear
参数确定在添加新矢量层之前是否应移除现有矢量层。如果设置为 true
,则所有现有矢量层都将被清除,并且仅显示新层。重要的生命周期注意事项:将矢量图形集成到 SwiftUI 视图中,尤其是在 onAppear 等生命周期事件期间,需要仔细考虑底层系统行为。当在视图出现时添加矢量时,开发人员可能会遇到构建器接收到帧和边界值为零的问题。这种差异源于 SwiftUI 视图的生命周期与 UIView 或 NSView 的生命周期(取决于平台)之间的内在不匹配。SwiftUI 将其大部分视图布局和渲染推迟到视图生命周期的后期阶段。为了缓解这些问题,可以在 onAppear 期间引入 небольшая задержка. 我稍后会尝试在初始配置中添加此命令,以涵盖您需要在视频流的早期阶段添加矢量层的情况。
ExtVideoPlayer(fileName: 'swipe')
或以声明方式
ExtVideoPlayer{
VideoSettings{
SourceName("swipe")
Subtitles("subtitles_eng")
Ext("mp8") // Set default extension here If not provided then mp4 is default
Gravity(.resizeAspectFill)
TimePublishing()
Events([.durationAny, .itemStatusChangedAny])
}
}
.onPlayerTimeChange { newTime in
// Current video playback time
}
.onPlayerEventChange { events in
// Player events
}
ExtVideoPlayer{
VideoSettings{
SourceName('https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8')
}
}
现在唯一需要的设置是 SourceName。
软件包中使用的 AVFoundation 框架支持广泛的视频格式和编解码器,包括基于文件的媒体和流媒体协议。以下是根据 Apple 文档组织的网格中列出的支持的视频类型、编解码器和流媒体协议。抱歉,没有检查所有编解码器和文件。
支持的文件类型 | 支持的编解码器 | 支持的流媒体协议 |
---|---|---|
3GP | H.264 | HTTP Live Streaming (HLS) |
.3gp , .3g2 |
H.265 (HEVC) | .m3u8 |
MKV(有限支持) | MPEG-4 Part 2 | |
.mkv |
AAC(音频) | |
MP4 | MP3(音频) | |
.mp4 |
||
MOV | ||
.mov |
||
M4V | ||
.m4v |
该软件包现在支持使用远程视频 URL,允许您直接从 Web 资源流式传输视频。这是对现有功能的扩展,现有功能主要侧重于本地视频文件。以下是如何在设置中指定远程 URL
ExtVideoPlayer{
VideoSettings{
SourceName('https://example.com/video')
Gravity(.resizeAspectFill) // Video content fit
}
}
视频源 | 可以使用 | 评论 |
---|---|---|
YouTube | 否 | 违反 YouTube 的政策,因为它不允许在其平台外部直接进行视频流式传输。 |
直接 MP4 URL | 是 | 如果直接可访问的 MP4 URL 托管在允许 CORS 的服务器上,则可以使用它们。 |
HLS 流 | 是 | 支持 HLS 流,可用于直播目的。 |
该软件包现在支持播放命令,允许您控制视频播放操作,例如播放、暂停和 Seek 到特定时间。
struct VideoView: View {
@State private var playbackCommand: PlaybackCommand = .play
var body: some View {
ExtVideoPlayer(
{
VideoSettings {
SourceName("swipe")
}
},
command: $playbackCommand
)
}
}
您可以将关于某些功能的视频提示引入应用程序,例如如何将位置添加到收藏夹。将循环视频提示放入背景或作为弹出窗口打开。
多比特率
Manifest 文件
.m3u8
文件,其中包含指向每个质量级别的视频片段的链接。片段
动态切换
AVQueuePlayer
)根据当前的互联网速度动态调整播放质量在此软件包的核心中,我使用了 AVQueuePlayer
。以下是 AVQueuePlayer
自动启用的支持功能,无需传递任何额外的参数
功能 | 描述 |
---|---|
硬件加速器 | AVQueuePlayer 默认在可用时使用硬件加速。 |
4k/HDR/HDR10/HDR10+/杜比视界 | 这些高清和高动态范围格式由 AVQueuePlayer 原生支持。 |
多声道音频/杜比全景声/空间音频 | AVQueuePlayer 原生支持高级音频格式。 |
文本字幕/图像字幕/隐藏式字幕 | 视频文件中包含的字幕和隐藏式字幕轨道会自动检测和呈现。 |
根据网络自动切换到多比特率流 | 当从支持自适应比特率流的源流式传输时,自适应比特率流由 AVQueuePlayer 自动处理。 |
外部播放控制支持 | 支持通过外部配件(如耳机和蓝牙设备)进行播放控制。 |
AirPlay 支持 | 原生支持通过 AirPlay 将音频和视频流式传输到兼容设备,无需额外设置。 |
后台音频播放 | 当前提是设置了适当的音频会话类别时,应用在后台时仍继续音频播放。 |
画中画 (PiP) 支持 | 在兼容设备上启用画中画模式,无需额外设置。 |
HLS (HTTP Live Streaming) 支持 | 原生支持 HLS 内容的流式传输,用于直播和点播播放。 |
FairPlay DRM 支持 | 可以播放受 FairPlay DRM 保护的内容。 |
正在播放信息中心集成 | 自动更新正在播放信息中心,以显示锁定屏幕和控制中心的当前播放信息。 |
远程控制事件处理 | 支持处理来自外部配件和系统控件的远程控制事件。 |
自定义播放速率 | 允许设置自定义播放速率,用于慢动作或快进播放,无需额外配置。 |
项目之间的无缝过渡 | 在队列中的媒体项目之间提供平滑过渡,确保连续播放,没有间隙。 |
自动音频会话管理 | 管理音频会话,以适当处理中断(如电话呼叫)和路线更改。 |
字幕和隐藏式字幕样式 | 支持用户偏好设置,用于设置字幕和隐藏式字幕的样式,包括字体大小、颜色和背景。 |
音频焦点和音量衰减 | 通过在必要时暂停或降低音量来处理音频焦点,例如当导航提示播放时。 |
元数据处理 | 读取和显示嵌入在媒体文件中的元数据,例如歌曲标题、艺术家和艺术作品。 |
缓冲和缓存 | 高效管理流式传输内容的缓冲,以减少播放中断。 |
错误处理和恢复 | 提供内置机制来处理播放错误,并尝试恢复,而不会导致应用程序崩溃。 |
辅助功能 | 支持 VoiceOver 和其他辅助功能,使所有用户都可以访问媒体内容。 |