Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

visionOS向け空間ビデオプレイヤーを実装してみた① ~空間ビデオのメタデータについて~

1. はじめに

みなさん、こんにちは!
株式会社ヤプリでiOSエンジニアをしています白数 (@cychow_app) です。

最近はClaude CodeやCodexといったAI Agent周りのキャッチアップに日々追われていますが、それと並行してvisionOSについても日々追っています。
弊社ではApple Vision Proを3台保有しており、空間コンピューティングの可能性についても日々模索しています。

これまでの取り組みに関しては、下記の記事をご参照いただければと思います。

tech.yappli.io

tech.yappli.io

私自身もApple Vision Proが日本で販売開始してすぐのタイミングで、表参道のApple Storeにお邪魔させていただき、空間コンピューティングの世界を体験させていただきました。
その時からvisionOSに魅力を感じ、一度実機で開発してみたいと思っていたところ、社内でApple Vision Proの実機があるということで、その実機をお借りし技術検証を行っています。

visionOSには他のOSでは体験できない多くの魅力的な機能がありますが、今回は「空間ビデオ (Spatial Video)」に焦点を当て、空間ビデオとはどういった機能なのか、空間ビデオのファイル内部ではどのようなデータを持っているのかについてご紹介できればと思います。

2. 空間ビデオとメタデータ

2.1 空間ビデオとは

visionOS内部にも多くの特徴的な機能がありますが、その中でも私が印象的に感じたのが空間ビデオという機能でした。

空間ビデオとは、通常のスマートフォンで閲覧できるビデオとは異なり、平面の情報に加えて、奥行き情報を含み、主にApple Vision Pro上で立体的なビデオを閲覧できる機能となっています。

2.2 ファイル形式とメタデータについて

空間ビデオ自体はApple Vision Proはもちろん、以下の端末でも撮影可能となっています。(2026/03/24時点)

【空間ビデオが撮影可能な端末一覧】
・Apple Vision Pro
・iPhone 15 Pro
・iPhone 15 Pro Max
・iPhone 16
・iPhone 16 Plus
・iPhone 16 Pro
・iPhone 16 Pro Max
・iPhone 17
・iPhone Air
・iPhone 17 Pro
・iPhone 17 Pro Max

撮影したファイルは.MOV形式として保存されますが、 メタデータ内部では空間ビデオの情報が含まれています。 AVURLAsset を用いた以下のコードで、空間ビデオの.MOVファイルのメタデータを確認することができます。

let url = URL(fileURLWithPath: "/Users/cychow/{空間ビデオを配置している場所のPath}")
let asset = AVURLAsset(url: url)

// メタデータを取得
print("\n【メタデータ】")
for format in asset.availableMetadataFormats {
    let metadata = asset.metadata(forFormat: format)
        
    for item in metadata {
        if let key = item.commonKey?.rawValue ?? item.key as? String,
            let value = item.value {
            print("\(key): \(value)")
        }
    }
}

特定の空間ビデオに対して、上記のスクリプトを実行した結果が以下となります。

$ swift check_metadata.swift 

【メタデータ】
com.apple.quicktime.location.accuracy.horizontal: 100.066798
com.apple.quicktime.full-frame-rate-playback-intent: 1
com.apple.quicktime.spatial.format-version: 1.0
com.apple.quicktime.spatial.aggressors-seen: 0
location: +34.7058+135.4891+004.310/
make: Apple
model: iPhone 17 Pro
software: 26.4
creationDate: 2026-03-22T12:37:25+0900

上記メタデータの一部を確認してみると、

com.apple.quicktime.spatial.format-version: 1.0
com.apple.quicktime.spatial.aggressors-seen: 0

と出力されていることがわかります。

まず com.apple.quicktime.spatial.format-version は、空間ビデオ用メタデータのフォーマットバージョンを示しており、「Apple の spatial video metadata のどの世代の形式で書かれているか」を表しているようです。

一方、com.apple.quicktime.spatial.aggressors-seen に関しては「視聴快適性に影響する要素の検出有無を示す内部フラグ」を意味しているかと思われます。
(断定していないのは、説明資料が見当たらなかったため、推察となっているからです。上記フラグに関して、詳しい方がいらっしゃいましたら、XのDMなどで教えていただけると嬉しいです...)

少なからず.MOVファイルのメタデータを確認した時に上記のキーが含まれている場合は空間ビデオに対応しているファイルとなります。

上記の情報だけでなく、空間ビデオのトラック情報からも内部のメタデータについて確認することができます。

// 空間ビデオを配置している場所のPathは適切なものを設定してください。
let url = URL(fileURLWithPath: "/Users/{ユーザー名}/Desktop/sample.MOV")
let asset = AVURLAsset(url: url)

// ビデオトラックの詳細
print("【ビデオトラック】")
for track in asset.tracks(withMediaType: .video) {
    print("Format Descriptions:")
    
    for formatDescription in track.formatDescriptions {
        let desc = formatDescription as! CMFormatDescription
        print(" Media Type: \(CMFormatDescriptionGetMediaType(desc))")
        print(" Media SubType: \(CMFormatDescriptionGetMediaSubType(desc))")
            
        // 拡張情報
        if let extensions = CMFormatDescriptionGetExtensions(desc) as? [String: Any] {
            print(" Extensions:")
            for (key, value) in extensions {
                print("  \(key): \(value)")
            }
         }
    }
}

上記のスクリプトを実行することで、さらに詳細にメタデータを出力することができます。 上記スクリプトの実行結果が以下です。

$ swift check_tracks.swift 

【ビデオトラック】
Format Descriptions:
  Media Type: 1986618469
  Media SubType: 1752589105
  Extensions:
    HasLeftStereoEyeView: 1
    HorizontalDisparityAdjustment: 200
    CVImageBufferColorPrimaries: ITU_R_709_2
    Version: 0
    SpatialQuality: 512
    RevisionLevel: 0
    SampleDescriptionExtensionAtoms: {
      hvcC = {length = 157, bytes = 0x01016000 0000b000 00000000 7bf000fc ... 01b00404 0a802080 };
      lhvC = {length = 37, bytes = 0x01f000fc cb02a100 01000b42 090e8224 ... 4802d930 536a0140 };
    }
    VerbatimSampleDescription: {length = 431, bytes = 0x000001af 68766331 00000000 00000001 ... 0000f7a8 00000000 }
    BitsPerComponent: 8
    Depth: 24
    HorizontalFieldOfView: 63400
    CVImageBufferTransferFunction: ITU_R_709_2
    CVImageBufferChromaLocationBottomField: Left
    HasRightStereoEyeView: 1
    CVImageBufferYCbCrMatrix: ITU_R_709_2
    TemporalQuality: 512
    CVImageBufferChromaLocationTopField: Left
    FormatName: HEVC
    StereoCameraBaseline: 19255
    CVFieldCount: 1
    FullRangeVideo: 0
    ProjectionKind: Rectilinear

それぞれのキーについて、チャッピーさんに聞いてみたところ、以下のようにまとめてくれました。

キー名 詳細説明
Media Type 1986618469 は FourCharCode で vide を表しており、このトラックが映像トラックであることを示します。
Media SubType 1752589105 は FourCharCode で hvc1 を表しており、映像コーデックが HEVC であることを示します。Apple の HEVC Stereo Video Profile でも codec type は hvc1 が使われます。
HasLeftStereoEyeView 左目用の映像ビューがこのトラックに含まれていることを示すフラグです。1 の場合、左目映像が存在します。
HasRightStereoEyeView 右目用の映像ビューがこのトラックに含まれていることを示すフラグです。1 の場合、右目映像が存在します。空間ビデオでは左右両方が入っているため、通常は左目・右目の両方が 1 になります。
HorizontalDisparityAdjustment 左右画像の相対的な水平方向シフト量を示し、ゼロ視差面をどこに置くかを調整する値です。-10000 ... 10000-1.0 ... 1.0 に対応します。今回の 200 は正規化すると 0.02 で、片眼画像の幅に対して約 2% 分のシフト量に相当します。
StereoCameraBaseline 左右のレンズ中心間距離を表します。単位はマイクロメートルです。今回の 1925519.255 mm を意味します。
HorizontalFieldOfView 水平方向の画角です。単位は 1/1000 度です。今回の 6340063.4 度 を意味します。
ProjectionKind 映像をどの投影モデルで解釈すべきかを示します。Rectilinear は一般的な平面投影で、Apple の説明では「概ね 90 度未満で樽型歪みのない投影」です。iPhone の空間ビデオではこの値になるのが自然です。
CVImageBufferColorPrimaries 色度座標系の基準となる原色を示します。ITU_R_709_2 は Rec.709 を意味し、HD 映像で広く使われる色域です。
CVImageBufferTransferFunction 輝度信号のエンコード特性を示します。ITU_R_709_2 は Rec.709 系の伝達特性を意味し、ガンマ特性の解釈に使われます。
CVImageBufferYCbCrMatrix YCbCr から RGB へ変換する際の色変換行列を示します。ITU_R_709_2 は Rec.709 用の変換行列です。
CVImageBufferChromaLocationTopField クロマサンプルの位置情報です。Left は、色差成分が輝度成分の左列に水平位置合わせされていることを示します。Apple のヘッダでは progressive 画像では TopField 側の値が使われます。
CVImageBufferChromaLocationBottomField インターレース系フォーマット向けのクロマ位置情報です。今回の値は Left ですが、CVFieldCount: 1 のようにプログレッシブ映像では実質的には TopField 側の解釈が主になります。
CVFieldCount フィールド数です。1 はプログレッシブ映像、2 はインターレース映像を意味します。今回の空間ビデオは 1 なのでプログレッシブです。
FullRangeVideo 画素値がフルレンジかビデオレンジかを示します。0 はフルレンジではなく、YCbCr ベースの圧縮映像で一般的な video-range であることを意味します。
BitsPerComponent 各色成分のビット深度です。今回の 8 は 8-bit の映像であることを示します。もし 10-bit HDR 系の映像なら 10 になることが想定されます。
Depth QuickTime 由来の古い深度フィールドで、概ね色深度を表します。今回の 24 は一般的な 24-bit カラーとして読めます。
FormatName 人が読みやすい形式名です。今回の HEVC は、このトラックが HEVC 形式であることを示しています。
Version サンプル記述子のバージョン番号です。QuickTime / CoreMedia 由来の管理用フィールドで、通常の空間ビデオでは 0 のことが多いです。
RevisionLevel サンプル記述子のリビジョン番号です。これも QuickTime 由来の管理用フィールドで、通常は 0 です。
SpatialQuality QuickTime 由来の空間品質指標です。Apple の SDK では CFNumber として定義されているものの、現行の HEVC 空間ビデオにおける厳密な意味までは公開ドキュメント上で詳しく説明されていません。実運用では参考値に近い位置づけと考えるのが安全です。
TemporalQuality QuickTime 由来の時間方向の品質指標です。こちらも Apple の SDK 上は型定義のみで、512 のような具体値の意味までは明示されていません。SpatialQuality と同様、互換性維持のために残っているレガシーな品質情報として見るのが無難です。
SampleDescriptionExtensionAtoms Sample Description 内にある拡張 atom のうち、CoreMedia が個別のキーへ変換しなかったものを格納する辞書です。空間ビデオでは hvcClhvC が特に重要です。
SampleDescriptionExtensionAtoms.hvcC HEVC Decoder Configuration Record です。Apple の HEVC Stereo Video Profile では、hvcC が primary stereo view、つまりベースレイヤー側の設定を保持します。HEVC のデコーダ初期化に必要な VPS / SPS / PPS などが入ります。
SampleDescriptionExtensionAtoms.lhvC L-HEVC Decoder Configuration Record です。Apple の HEVC Stereo Video Profile では、lhvC が secondary stereo view、つまりもう片方の目のレイヤー設定を保持します。空間ビデオが左右 2 視点を 1 トラック内に持てるのは、この hvcClhvC の組み合わせがあるためです。
VerbatimSampleDescription 元の Sample Description 全体をバイナリのまま保持したものです。CoreMedia が CMFormatDescription に変換しても、元のサンプル記述子を完全に再現できるよう保持されています。Apple のヘッダでも、これが残っている状態で複製した CMFormatDescription を編集すると変更が無視される可能性があるため、編集時は注意が必要とされています。

(本当にチャッピーさん流石です...)

3. まとめ

今回は、visionOS向けの空間ビデオプレイヤーを実装するにあたり、空間ビデオのメタデータとトラック情報を中心に調査しました。
一見すると空間ビデオは通常の .MOV ファイルとして保存されていますが、内部を確認すると、左右の視点情報や視差調整、カメラ間距離、画角、投影方式など、空間ビデオとして再生するための情報が数多く含まれていることが分かりました。

特に、トラック情報の中に HasLeftStereoEyeView / HasRightStereoEyeViewHorizontalDisparityAdjustmentStereoCameraBaselineHorizontalFieldOfViewProjectionKind、そして hvcC / lhvC といった値が含まれていることから、単に「2 眼の動画が入っている」のではなく、再生側が立体的な見え方を適切に解釈するための情報まで含めて構成されていることが分かります。
空間ビデオを扱う実装では、プレイヤーの見た目や UI だけでなく、このようなトラック内部の構造を理解しておくことが重要だと感じました。

今回の調査によって、空間ビデオがどのような情報を持っているのかを一通り把握できたため、次はこれらの情報を踏まえながら、実際に visionOS 上でどのように再生し、どのような体験としてアプリに組み込めるのかを検証していきたいと思います。