SwiftUI 视频播放器 iOS 14+,macOS 11+,tvOS 14+

如果您认为继续开发此软件包是有价值的,请给仓库点赞。这将帮助我了解哪个软件包值得投入更多精力。

这是一个纯粹的软件包,没有任何第三方库。我的主要重点是性能。特别是如果您需要在背景中添加视频作为设计元素,在这种情况下,您会希望使用一个轻量级的组件,而不需要很多不必要的功能。希望它能对您有所帮助

SwiftUI 应用示例 点击链接

既然我们已经有了 Apple 的 VideoPlayer,为什么还需要这个?!

Apple 的 VideoPlayer 为 SwiftUI 中的视频播放提供了快速设置。但是,它不允许您隐藏或自定义默认的视频控件 UI,从而限制了其在自定义场景中的使用。或者,您需要使用 AVPlayerViewController,它包含大量功能,仅仅是为了禁用控件。相比之下,此解决方案充当模块化框架,允许您仅集成所需的功能,同时保持核心组件的轻量级。它提供了对播放的完全控制,包括添加自定义 UI 元素的能力,使其非常适合背景视频、工具提示、视频提示和其他自定义场景。此外,它还支持高级功能,例如字幕、无缝循环和实时滤镜应用。它还允许向视频流添加矢量图形,访问帧数据以进行自定义过滤,以及将 ML 或 AI 算法应用于视频处理,从而实现广泛的增强和修改等等。

此软件包使用声明式方法来声明视频组件的参数,这些参数基于构建块。此实现可能会让您深入了解 SwiftUI 在底层是如何工作的。您也可以通过常用方式传递参数。如果要分析软件包的性能,请在真机上进行。

  ExtVideoPlayer{
       VideoSettings{
           SourceName("swipe") 
       }
   } 

   ExtVideoPlayer(fileName: 'swipe')  

播放器动态哲学

播放器的功能围绕双向 ⇆ 交互模型设计

文档(API)

The concept

规格

功能类别 功能名称 描述
通用 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 环绕声和空间音频轨道。
字幕和隐藏式字幕 支持多种字幕和隐藏式字幕格式。
事件处理 批量事件处理 批量收集和处理事件以避免泛滥。
播放状态事件 playingpausedseekduration(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)
   }

CornerRadius effect video player swift

顺便说一句

也许这足以满足您的需求

测试

该软件包包含涵盖关键功能的单元测试。虽然不是详尽的,但这些测试有助于确保核心组件按预期工作。UI 测试正在进行中,并在示例应用程序中开发。run_tests.sh 是一个示例脚本,它通过将测试命令封装到单个可执行文件中来自动化测试,从而简化执行过程。您可以配置脚本以运行与您的项目相关的特定测试环境。

关于 YouTube 等视频来源的免责声明

请注意,使用来自 URL 的视频需要确保您有权使用和流式传输这些视频。由于其服务条款的限制,无法直接使用托管在 YouTube 等平台上的视频。

API

属性/方法 类型 描述
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 使用此结构激活允许通过命令添加基于矢量的叠加层的设置。如果未通过设置传递,则任何 addVectorremoveAllVectors 命令都将无效。 未启用
PictureInPicture 启用画中画 (PiP) 支持。如果未传递,则任何类似 startPiPstopPiP 的命令都无效。请查看示例应用 Video11.swift。它在模拟器上不起作用。您只能在真机上观察到此功能。 未启用
Events([.durationAny, .itemStatusChangedAny]) 如果在 settings: VideoSettings 中未传递 Events(),则事件机制将被禁用。您可以精确指定要接收哪些事件(例如,.itemStatusChangedAny),或者只需传递 Events() 即可接收所有可用事件。添加此设置是为了提高性能,因为事件是通过 @State 更改发出的,这会触发视图更新。如果您不需要观察任何事件,禁用它们可以显着提高性能。请查看示例应用 Video8.swift 中的实现 未启用

关于设置的其他说明

命令

处理命令

   @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 从视频流中移除所有矢量图形层的命令。

关于矢量图形命令的其他说明

音频和语言命令

命令 描述
audioTrack(String) 根据语言代码选择特定音轨的命令。languageCode 参数指定所需音轨的语言(例如,“en”表示英语)。
subtitles(String?) 此命令将字幕设置为指定的语言或将其关闭。提供语言代码(例如,"en" 表示英语)以显示该语言的字幕,或传递 nil 以完全禁用字幕。注意:这仅在视频文件具有嵌入式字幕轨道时适用。

关于 subtitles 命令的其他说明

此功能旨在用于视频文件已包含嵌入在其元数据中的多个字幕轨道(即,可读媒体轨道)的用例。换句话说,容器格式(例如 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 命令时,您可以动态地在视频流上添加新的矢量图形层(例如徽标或动画矢量)。这对于通过叠加层(例如品牌元素、动画图形)增强用户体验特别有用。

添加矢量层:

重要的生命周期注意事项:将矢量图形集成到 SwiftUI 视图中,尤其是在 onAppear 等生命周期事件期间,需要仔细考虑底层系统行为。当在视图出现时添加矢量时,开发人员可能会遇到构建器接收到帧和边界值为零的问题。这种差异源于 SwiftUI 视图的生命周期与 UIView 或 NSView 的生命周期(取决于平台)之间的内在不匹配。SwiftUI 将其大部分视图布局和渲染推迟到视图生命周期的后期阶段。为了缓解这些问题,可以在 onAppear 期间引入 небольшая задержка. 我稍后会尝试在初始配置中添加此命令,以涵盖您需要在视频流的早期阶段添加矢量层的情况。

关于亮度和对比度的其他说明

如何使用该软件包

1. 创建 LoopPlayerView

    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

该软件包现在支持使用远程视频 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
        )
    }
}

软件包的实用想法

您可以将关于某些功能的视频提示引入应用程序,例如如何将位置添加到收藏夹。将循环视频提示放入背景或作为弹出窗口打开。

The concept

The concept

具有自适应质量的 HLS

自适应质量切换的工作原理

  1. 多比特率

    • 视频以多个质量级别(例如,240p、360p、720p、1080p)编码,每个级别具有不同的比特率。
  2. Manifest 文件

    • 服务器提供 Manifest 文件
      • 在 HLS 中:一个 .m3u8 文件,其中包含指向每个质量级别的视频片段的链接。
  3. 片段

    • 视频被分成短片段,通常为 2-10 秒长。
  4. 动态切换

    • 客户端(例如,AVQueuePlayer)根据当前的互联网速度动态调整播放质量
      • 以最合适的质量开始播放。
      • 随着连接速度的变化,在播放期间切换到更高或更低的质量。

为什么这是最佳选择

AVQueuePlayer 开箱即用的功能

在此软件包的核心中,我使用了 AVQueuePlayer。以下是 AVQueuePlayer 自动启用的支持功能,无需传递任何额外的参数

功能 描述
硬件加速器 AVQueuePlayer 默认在可用时使用硬件加速。
4k/HDR/HDR10/HDR10+/杜比视界 这些高清和高动态范围格式由 AVQueuePlayer 原生支持。
多声道音频/杜比全景声/空间音频 AVQueuePlayer 原生支持高级音频格式。
文本字幕/图像字幕/隐藏式字幕 视频文件中包含的字幕和隐藏式字幕轨道会自动检测和呈现。
根据网络自动切换到多比特率流 当从支持自适应比特率流的源流式传输时,自适应比特率流由 AVQueuePlayer 自动处理。
外部播放控制支持 支持通过外部配件(如耳机和蓝牙设备)进行播放控制。
AirPlay 支持 原生支持通过 AirPlay 将音频和视频流式传输到兼容设备,无需额外设置。
后台音频播放 当前提是设置了适当的音频会话类别时,应用在后台时仍继续音频播放。
画中画 (PiP) 支持 在兼容设备上启用画中画模式,无需额外设置。
HLS (HTTP Live Streaming) 支持 原生支持 HLS 内容的流式传输,用于直播和点播播放。
FairPlay DRM 支持 可以播放受 FairPlay DRM 保护的内容。
正在播放信息中心集成 自动更新正在播放信息中心,以显示锁定屏幕和控制中心的当前播放信息。
远程控制事件处理 支持处理来自外部配件和系统控件的远程控制事件。
自定义播放速率 允许设置自定义播放速率,用于慢动作或快进播放,无需额外配置。
项目之间的无缝过渡 在队列中的媒体项目之间提供平滑过渡,确保连续播放,没有间隙。
自动音频会话管理 管理音频会话,以适当处理中断(如电话呼叫)和路线更改。
字幕和隐藏式字幕样式 支持用户偏好设置,用于设置字幕和隐藏式字幕的样式,包括字体大小、颜色和背景。
音频焦点和音量衰减 通过在必要时暂停或降低音量来处理音频焦点,例如当导航提示播放时。
元数据处理 读取和显示嵌入在媒体文件中的元数据,例如歌曲标题、艺术家和艺术作品。
缓冲和缓存 高效管理流式传输内容的缓冲,以减少播放中断。
错误处理和恢复 提供内置机制来处理播放错误,并尝试恢复,而不会导致应用程序崩溃。
辅助功能 支持 VoiceOver 和其他辅助功能,使所有用户都可以访问媒体内容。