供给空间体会的视频内容

依据HLS交给2D视频内容

VisionOS  音视频播放

视频

在视频方面,对源视频进行编码,将其辑成适宜的长度,并调整比特率进行颜色校对。在这儿,用户可选择怎么装备和运用HEVC(高效视频编码的简称)等视频编码器。该渠道支撑高达4K分辨率的内容播映,显现器的刷新率是90Hz,而关于每秒24fps的视频,能够自动运用特别的96Hz方法。支撑标准和高动态规模。

音频

在视频的对应音频方面,确认并制造所需的源音频流的数量(数量取决于首要人物对话和其他音频)。在考虑到HLS传输的情况下将这些音源进行编码。(HLS开发者页面有关于准备音频的文档链接。)

字幕

字幕包括字幕和隐藏式字幕,以包括不同的言语和人物。这儿的字幕是指视频中用于供给不同言语翻译的白话文本转录,或用于确认设置的时刻和地点。隐藏式字幕类似于一般字幕,它不仅能供给对话转录还能提示声响作用和相关音频的细节描述,便于听障人士检查,即SDH(Subtitles for Deaf and Hard-of-hearing)人群。

与视频和音频编码类似,用户能够制造HLS支撑的字幕文件和格局,最常见的是WebVTT。

包装/打包有了源视频、音频和字幕在手,接下来便是打包,这是将源媒体文件转化为各种类型的片段以进行牢靠传输的进程。能够用苹果公司的HLS东西来完成,可在前期的HLS流媒体页面上找到,也能够运用其他内容渠道的东西。

在2D的视频内容根底上交给3D视频内容

VisionOS  音视频播放

经过上图能够看到,3D的内容交互是依据当时2D内容传输流程根底之上的,HLS为碎片化的MP4守时元数据(metadata)增加了新的支撑。

现在来重点重视2D内容和3D立体内容的差异。首先了解一下3D视频,3D视频是一种经过在时刻轴上摆放一系列帧序列来出现三维场景的视频方法。每个帧都包括了在不同视角或位置捕捉到的图画,以产生具有深度感的视觉作用。这些帧序列能够经过不同的办法进行捕捉,例如运用双目摄像机、深度传感器或计算机生成图形

3D视频中的“3D”可与“立体”互换运用,这儿的立体视觉可为左眼供给一张图画,为右眼供给另一个非常相似的图画,视角略有不同。左右图画之间的这些差异,称为视差,运用户在观看这类视频时能感受到三维的深度。

VisionOS  音视频播放

接下来视频介绍了3D视频帧制造的一些参阅办法。经过对一切立体帧运用单个视频轨迹,保留了传统的2D视频轨迹的制造。左边和右边的图画或视图,关于任何显现时刻都是在一个单一的紧缩帧。理想情况下,取得苹果芯片支撑后,应该能够被非3D感知播映所解码,答应在2D作业流程中预览视频。为了供给立体帧,咱们引入了多视图HEVC(也称为MV-HEVC),它是 HEVC 的扩展。“MV”是多视图的。每帧携带多个视图,每帧都有一对紧缩的左图画和右图画

在MV-HEVC中,将根本的HEVC 2D视图存储在每个紧缩的帧中。编码确认左右图画之间的差异或增量。这种技术被称为2D Plus Delta,意味着2D解码器能够找到并运用根底2D视图,例如左眼 。但 3D 解码器能够计算出另一个视图,将两个视图出现给对应的眼睛

因为视差的不同,视频场景中的一个物体或许被以为比另一个更近或更远。能够界说立体深度内容的三个首要区域:

VisionOS  音视频播放

  • 一是没有视差的屏幕平面
  • 二是负视差,将导致物体在屏幕平面前被感知
  • 三是正视差,将导致物体在屏幕平面后被感知

关于3D字幕,官方的说法是不要求运用新的字幕格局或改动现有的格局,而是供给一种办法来描述每个视频帧的视差,有些区域显然离观看者较近,有些区域则离观看者较远。他们将其称为“视差概括(parallax contour)”并将其作为元数据记录在元数据轨迹中,与视频轨迹的帧同步。

经过上面的介绍能够看到:HLS 交给的作业原理与 3D 财物相同。交给 3D 财物与交给 2D 财物根本相同,但您能够采纳一些措施来优化体会。准备源资源。将 MV-HEVC 用于3D 视频,并包括新的视差概括元数据。而关于音频和字幕能够在 2D 和 3D 中运用相同的流。运用更新的包装来生成相关片段和播映列表。财物坚持不变

VisionOS  音视频播放

关于怎么创立MV-HEVC格局的视频文件或许怎么拍照这种格局的视频,以上session并未说到,能够重视官方论坛。

打造超卓的空间播映体会

VisionOS  音视频播放

在VisionOS 中,AVFoundation 得到了增强,能够支撑运用其共同功用的新媒体格局,例如 3D 视频。同时能够运用 RealityKit 进行高性能和高质量烘托,从而能够无缝合成视频融入您周围的国际,这样音频也会对您周围的国际做出反应。AVKit 的 AVPlayerViewController已得到扩展,能够运用 RealityKit 的强壮功用和渠道的共同功用来创立高度精美的体会。这包括您期望的一切播映控件,而且还具有许多共同的功用。接下来我将运用demo为咱们一一展现这个session里边的具体内容

在VisionOS中运用 AVPlayerViewController来进行视频的播映,就像在 iOS 或 tvOS 上运用相同

在内嵌播映器中播映视频

内联播映器便是将AVPlayerViewController放在另一个视图的内部时,会显现内联控件,例如暂停、越过和查找。在应用程序中显现标准播映控件可供给了解的 UI,该 UI 可自动调整其外观以适应每个渠道,

VisionOS  音视频播放

这儿运用体系的AVPlayerViewController简略完成了一个内联播映,其间左边是预览播映,右边是该视频的相关信息。

现在翻开VisionOS的模拟器,在Demo应用程序发动之前,只要房间可见。当应用程序发动时,一个大屏幕出现在前面,房间变暗,营造出良好的氛围。移动时,屏幕坚持在原位,而且音频坚持固定在屏幕上。要显现播映控件,请检查屏幕并点击。控件浮动在视频前面。能够经过看着屏幕并再次点击来使它们消失。

咱们也能够抓住屏幕下方的窗口栏以从头定位它。抓住屏幕的一角来调整巨细。请注意,跟着屏幕巨细的调整,它会平滑地出现动画并与视频的宽高比相匹配。经过旋转数码表冠调理音量。或许运用 Digital Crown 翻开一个环境(在模拟器上能够点击右上角的scene)。你有必要亲自体会一下。现在,让咱们更仔细地检查播映控件以了解它们供给的功用。首先是播映器界面。右上角是音量控制,可进行快速调理或静音。左下角是了解的播映/暂停和后退/前进按钮。底部中间是跳转到电影中不同时刻的洗涤器。右下角是这个按钮,有更多选项。以下是调整播映速度的选项。当电影包括多个音轨或字幕轨迹时,请运用这些选项来选择言语用于音轨,或启用首选言语的字幕。最后一个选项是调光作用。我喜爱在漆黑中看电影,以便真正会集注意力,这些都是内置的功用。当在内嵌视图中播映时还有出现全屏打开的按钮,点击后默许全屏播映。

AVPlayerViewController在内嵌时仅支撑显现 2D 内容。需求全屏显现播映器才能播映 3D 视频。

在全屏播映器中播映视频

经过将播映器设置为应用程序的根视图或运用fullScreenCover来出现播映器,以全屏方法出现播映器。在全屏方法下,播映器出现出愈加内容前向的规划,默许会调暗环境以供给更适宜的观看作用。

import Foundation
import SwiftUI
import AVFoundation
import AVKit
struct TestPlayView: UIViewControllerRepresentable {
    private var player: AVPlayer {
        return AVPlayer(url: Bundle.main.url(forResource: "test1", withExtension: "mp4")!)
    }
    func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {
        playerController.modalPresentationStyle = .fullScreen
        playerController.player = player
        playerController.player?.play()
    }
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        return AVPlayerViewController()
    }
}

接着只需求在SwiftUI中展现该控制器即可。

struct ContentView: View {
    var body: some View {
        PlayerView()
    }
}

装备空间音频体会

咱们的APP除了需求装备应用程序进行媒体播映中的过程之外,还能够针对全屏播映,优化一下听觉上的作用,装备一下空间音频。它是差异于立体音频的。

func changePresentation(_ presentation: Presentation) {
        self.presentation = presentation
        do {
 let experience: AVAudioSessionSpatialExperience
 switch presentation {
 case .inline:
 // 关于内联播映器视图,它将体会设置为一个小型的前置声场,其间音频来自人对前方的感知。
experience = .headTracked(soundStageSize: .small, anchoringStrategy: .front)
 case .fullScreen:
 // 全屏显现视频时,它会指定自动设置,让体系优化体会以最适合视频演示。
experience = .headTracked(soundStageSize: .automatic, anchoringStrategy: .automatic)
}
 try  AVAudioSession .sharedInstance().setIntendedSpatialExperience(experience)
} catch {
logger.error( "Unable to set the intended spatial experience. (error.localizedDescription) " )
}
    }

拖动进度条时显现缩略图(Trick-Play)

这儿有关于缩略图的一份阐明developer.apple.com/documentati…

这儿全屏播映的url换成苹果供给的测验url能够看到,在拖动进度条的时候能够显现缩略图

HLS测验链接:

  • playgrounds-cdn.apple.com/assets/beac…
  • playgrounds-cdn.apple.com/assets/lake…
  • playgrounds-cdn.apple.com/assets/lake…

VisionOS  音视频播放

插页式事件处理

有时需求在视频媒体中刺进Logo、回忆或广告到时刻线中。插页式广告支撑可完成此功用。当出现插页式广告时,控件将自动经过时刻线中的指示器来反映它们。这些插页式广告能够运用 AVPlayerInterstital EventController以编程方法进行装备,也能够在 HLS 流中进行描述。

VisionOS  音视频播放

这个功用在iOS15就推出来了,现在在VisionOS里也支撑了。

增加附加的UI

有一些视频播映应用程序常用的附加 UI 选项上下文操作答应您增加“越过简介”或“播映下一集”等按钮。它们能够有标题和可选图画。自界说信息视图控制器可用于显现有关内容的元数据或主张相关内容。这些 API 的作业方法与其他 Apple 渠道相同。

VisionOS  音视频播放

这儿介绍了三种功用,默许的附加UI选项,自界说信息视图,依据上下文增加按钮

接下来我用代码一一简略完成:

  1. 默许的附加UI选项

当播映的媒体信息供给嵌入或外部元数据时,播映器 UI 会显现“信息”选项卡。该选项卡的视图显现元数据详细信息,而且或许会沿其后缘显现最多两个控件

先创立AVMetadataItem

func createMetadataItems(for video: Video) -> [AVMetadataItem] {
        let mapping: [AVMetadataIdentifier: Any] = [
            .commonIdentifierTitle: video.title,
            .commonIdentifierArtwork: video.imageData,
            .commonIdentifierDescription: video.description,
            .commonIdentifierCreationDate: video.info.releaseDate,
            .iTunesMetadataContentRating: video.info.contentRating,
            .quickTimeMetadataGenre: video.info.genres
        ]
        return mapping.compactMap { createMetadataItem(for: $0, value: $1) }
    }
    private func createMetadataItem(
        for identifier: AVMetadataIdentifier,
        value: Any
    ) -> AVMetadataItem {
        let item = AVMutableMetadataItem()
        item.identifier = identifier
        item.value = value as? NSCopying & NSObjectProtocol
        // 指定未界说言语
        item.extendedLanguageTag = "und"
        return item.copy() as! AVMetadataItem
    }

然后指定当时播映的AVPlayerItem的额定元数据为上面创立的demo信息即可

struct FullScreenPlayer: UIViewControllerRepresentable {
    @Environment(PlayerModel.self) private var model
    @Environment(VideoLibrary.self) private var video
    func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {
        playerController.modalPresentationStyle = .fullScreen
playerController.player ? .currentItem ? .externalMetadata = model.createMetadataItems(for: video.videos.first ! )
    }
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        return model.makePlayerViewController()
    }
}

当增加以上信息后,能够看到体系自动支撑了这样的一个Additional UI。

接着咱们在该界面追加另一个按钮:

func makeUIViewController(context: Context) -> AVPlayerViewController {
    let controller = model.makePlayerViewController()
    // 给体系默许info界面增加一个按钮
 let infoCircle =  UIImage (systemName: "info.circle" )
 let showMoreInfo =  UIAction (title: "展现更多信息" , image: infoCircle) { action in
 print ( "展现更多信息" )
}
    controller.infoViewActions.append(showMoreInfo)
    return controller
}

能够看到,这儿在信息选项卡的界面追加了另一个控件。

VisionOS  音视频播放

  1. 自界说信息视图

VisionOS播映器UI还能够在用户界面中显现多个内容选项卡以显现支撑信息或相关内容。默许情况下,当财物包括嵌入元数据或在播映器项目上设置外部元数据时,播映器会显现“信息”选项卡,如上面的媒体信息展现。

接下来咱们来自界说一个选项卡来显现支撑内容。

func makeUIViewController(context: Context) -> AVPlayerViewController {
    let controller = model.makePlayerViewController()
    // 给体系默许info界面增加一个按钮
let infoCircle = UIImage(systemName: "info.circle")
    let showMoreInfo = UIAction(title: "展现更多信息", image: infoCircle) { action in
        print("展现更多信息")
    }
    controller.infoViewActions.append(showMoreInfo)
    // 新增一个播映下一首的按钮UI先关页面
 if  let upNextViewController {
controller.customInfoViewControllers = [upNextViewController]
}
    return controller
}
func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {
        playerController.modalPresentationStyle = .fullScreen
        playerController.player?.currentItem?.externalMetadata = model.createMetadataItems(for: video.videos.first!)
        Task { @MainActor  in
 if  let upNextViewController {
playerController.customInfoViewControllers = [upNextViewController]
}
}
    }
extension  FullScreenPlayer {
 var upNextViewController: UIViewController ? {
 let view =  UpNextView ()
 let controller =  UIHostingController (rootView: view)
controller.title =  "PlayList"
controller.preferredContentSize =  CGSize (width: 500 , height: 150 )
 return controller
}
} 
  1. 依据上下文增加按钮

咱们也能够运用visionOS播映器UI来依据上下文出现控件,咱们能够在内容中依据播映时刻规模规模来展现或许铲除这个按钮。此类控件的常见用途是在电影或电视节目的标题序列期间显现的越过按钮。人们能够点击按钮绕过介绍并快速跳至首要内容。

播映器将它们显现在屏幕的底部跟随侧。以下代码示例显现了一个操作的简略完成

func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {
        playerController.modalPresentationStyle = .fullScreen
        playerController.player?.currentItem?.externalMetadata = model.createMetadataItems(for: video.videos.first!)
        Task { @MainActor  in
if let upNextViewController {
                playerController.customInfoViewControllers = [upNextViewController]
            }
 if  let upNextAction {
playerController.contextualActions = [skipAdAction]
} else {
playerController.contextualActions = []
}
}
    }
extension  FullScreenPlayer {
var upNextViewController: UIViewController? {
        let view = UpNextView()
        let controller = UIHostingController(rootView: view)
        controller.title = "Next Video"
        controller.preferredContentSize = CGSize(width: 500, height: 150)
        return controller
    }
 var skipAdAction: UIAction ? {
 return  UIAction (title: "越过广告" ) { _  in
 print ( "越过广告, seek到正文内容" )
}
}
} 

当咱们设置这个特点值时,播映器会当即显现控件。假如要仅在内容的相关部分中出现它们,能够经过增加对播映时长的监听。来判别是否需求展现这个选项卡。假如是,该示例将越过广告进入正文播映;不然,它将经过将其设置为空数组来铲除该选项卡。

private func addTimeObserver() {
    // Observe the player's timing every second.
    let interval = CMTime(value: 1, timescale: 1)
    let fifteenSeconds = CMTime (value: 15, timescale: 1)
    timeObserver = avPlayer.addPeriodicTimeObserver(forInterval: interval,
                                                    queue: .main) { [weak self] time in
        guard let self else { return }
        let duration = avPlayer.currentItem?.duration ?? .zero
        // Show the Skip Intro button during the first 15 seconds of the content.
        showSkipIntroAction = time <= fifteenSeconds
    }
}
func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {
        playerController.modalPresentationStyle = .fullScreen
        playerController.player?.currentItem?.externalMetadata = model.createMetadataItems(for: video.videos.first!)
        Task { @MainActor  in
if let upNextViewController {
                playerController.customInfoViewControllers = [upNextViewController]
            }
if let upNextAction , showSkipIntroAction {
playerController.contextualActions = [skipAdAction]
            } else {
                playerController.contextualActions = []
            }
}
    }

当然这个跳转监听逻辑我没有去真的完成……

出现沉溺式空间

接下来,咱们来介绍成沉溺式控件,咱们的APP能够经过名为“沉溺式空间”的功用将观看者带到另一个当地。当咱们在APP中创立沉溺式空间时,咱们能够自行决定该空间的外观。当进入沉溺式空间播映视频时,屏幕将自动移动到该空间并固定在可预测的尺寸和位置以确保每次都有超卓的视角。同时那些操作控件将与屏幕别离,(比方上面介绍的信息选项卡,操作按钮等),使它们更容易交互。

介绍这部分,肯定得先知道怎么创立一个沉溺式空间developer.apple.com/documentati…

依据介绍,要创立彻底沉溺式的体会,首先要翻开ImmersiveSpace并将其款式设置为full。沉溺式空间是一种SwiftUl场景,能够让你将内容放置在人周围的任何当地。将完好款式应用于场景会告知体系隐藏直通视频,只显现应用程序的内容。在你的应用目标的body特点中声明沉溺式空间,或许在你管理SwiftUl场景的任何当地。

@main
struct MovePlayingAppApp: App {
    @State private var player = PlayerModel()
    @State private var library = VideoLibrary()
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(player)
                .environment(library)
        }
        // 这儿咱们为指定类型的出现数据创立一个沉溺式空间
        ImmersiveSpace (for: Destination . self ) { $content  in
 if  let content {
}
}
 // 设置沉溺式空间的风格,这儿有3种风格可供设置
 // mixed风格将您的内容与直通融合在一起。这使您能够将虚拟目标放置在人的周围环境中。
 // full款式仅显现您的内容,并封闭直通。这使您能够彻底控制视觉体会,就像当您想要将人们传送到一个新国际时相同。
 // progressive款式彻底替代了部分显现的直通。您能够运用这种风格让人们立足于现实国际,同时展现另一个国际的观点。
.immersionStyle(selection: .constant(.full), in: .progressive, .mixed, .full)
    }
}

接下来是在这个无限空间中展现你的内容,可所以2D的,也可所以3D的。一般咱们运用RealityKit 来出现3D内容

ImmersiveSpace(for: Destination.self) { $content in
            if let content {
 DestinationView (content)
            }
        }
struct DestinationView: View {
    @State private var destination: Destination
    @State private var destinationChanged = false
    init(_ destination: Destination) {
        self.destination = destination
    }
    var body: some View {
        RealityView { content in
 let rootEntity =  Entity ()
rootEntity.addSkybox(for: destination)
content.add(rootEntity)
}
        .transition(.opacity)
    }
}
extension Entity {
    func addSkybox(for destination: Destination) {
        let subscription = TextureResource.loadAsync(named: destination.imageName).sink(
            receiveCompletion: {
                switch $0 {
                case .finished: break
                case .failure(let error): assertionFailure("(error)")
                }
            },
            receiveValue: { [weak self] texture in
                guard let self = self else { return }
                var material = UnlitMaterial()
                material.color = .init(texture: .init(texture))
                // 创立实体视觉外观的资源调集。
                // 其间generateSphere是体系供给的创立一个具有指定半径的球体网格。 球体以实体原点为中心。 参数 球体的半径,单位为米。 返回:一个球体网格。 这儿设置10的三次方,也便是1000米,相当于无限空间
                // materials是模型运用的材料
                self.components.set(ModelComponent(
                    mesh: .generateSphere(radius: 1E3),
                    materials: [material]
                ))
                // 指定实体的缩放因子
                self.scale *= .init(x: -1, y: 1, z: 1)
                // 设置实体沿 x、y 和 z 轴的位置。
                self.transform.translation += SIMD3<Float>(0.0, 1.0, 0.0)
                // Rotate the sphere to show the best initial view of the space.
                updateRotation(for: destination)
            }
        )
        components.set(Entity.SubscriptionComponent(subscription: subscription))
    }
    func updateRotation(for destination: Destination) {
        // Rotate the immersive space around the Y-axis set the user's
        // initial view of the immersive scene.
        let angle = Angle.degrees(destination.rotationDegrees)
        let rotation = simd_quatf(angle: Float(angle.radians), axis: SIMD3<Float>(0, 1, 0))
        self.transform.rotation = rotation
    }
    /// A container for the subscription that comes from asynchronous texture loads.
    ///
    /// In order for async loading callbacks to work we need to store
    /// a subscription somewhere. Storing it on a component will keep
    /// the subscription alive for as long as the component is attached.
    struct SubscriptionComponent: Component {
        var subscription: AnyCancellable
    }
}

上面的DestinationView运用RealityView来展现3D内容,咱们供给了一个将供给的图片纹理映射到在人周围显现的球体内部的这样一个实体并将其作为RealityView的内容进行展现。

如下图,咱们能够看到在沉溺式空间中,咱们能够创立任意咱们想看到的内容。

VisionOS  音视频播放

当咱们翻开沉溺式空间时,体系会隐藏一切其他可见的应用程序。SwiftUI一次只答应翻开一个。在翻开另一个沉溺式空间之前,要先封闭当时翻开的任何沉溺式空间。

当咱们开端彻底沉溺式体会时,visionOS 界说了一个从人头部初始位置延伸 1.5 米的体系鸿沟。假如咱们的头部移出该区域,体系会自动中止沉溺式体会并再次翻开外部视频。此功用是协助防止人与物体磕碰。

当进入沉溺式空间时,视频的播映控件是自动和主屏幕别离的。这个也是体系的默许完成。

更多经过RealyKit来展现3D内容的教育请检查Explore materials with Reality Composer Pro

供给共享的观看体会

增强应用程序播映体会的最佳办法之一是让该体会可与其别人共享。咱们能够运用AVFoundation和GroupActivities框架来构建SharePlay体会,即便人们无法坐落同一位置,也能够将他们聚集在一起。

比方说当人们进行 FaceTime 通话并在全屏播映器中播映视频时,通话中的每个人都能够播映视频

但是这个在模拟器上不太好完成,所以这儿没有去实践并打开,后面有时机能够在此共享,这个SharePlay不一定是用来共享观看,能够做很多工作。

好了,上面共享的内容便是本次Session的中一切说到的功用点和相关的扩展。

官方论坛

  1. developer.apple.com/forums/tags…

新增标准和格局支撑

  1. developer.apple.com/av-foundati…
  2. developer.apple.com/av-foundati…
  3. developer.apple.com/av-foundati…

参阅资料

  • wwdc视频
  1. developer.apple.com/videos/play…
  2. developer.apple.com/videos/play…
  3. developer.apple.com/documentati…
  • 视频播映
  1. developer.apple.com/streaming/W…
  • 视频播映UI
  1. developer.apple.com/documentati…
  2. developer.apple.com/documentati…
  3. developer.apple.com/documentati…
  • 沉溺式空间
  1. developer.apple.com/documentati…
  2. developer.apple.com/documentati…
  • RealityKit,RealityView构建
  1. developer.apple.com/documentati…
  2. developer.apple.com/documentati…
  3. developer.apple.com/documentati…
  4. developer.apple.com/documentati…
  5. developer.apple.com/documentati…
  6. developer.apple.com/videos/play…