LoginSignup
1
1

More than 1 year has passed since last update.

iOSのAVPlayerでHDRが白飛びするのを直せなかったのだが解ったことをメモしておく

Posted at

タイトルの通り、iOSのAVPlayerでHDR(High Dynamic Range)を再生すると白飛びする。

WWDC2022の動画の説明を見たがHDR動画の扱い方はあるが白飛びの解消方法は解らなかった。
https://developer.apple.com/videos/play/wwdc2022/110565/

AVPlayer自体の白飛びは解消できなかったがビデオフレームにリアルタイムにアクセスしてPixelBufferを取り出してデコードすることはできた。
それでもHDRの10bitから8bitへの変換方法は不明だったがPixelFormatにkCVPixelFormatType_420YpCbCr8BiPlanarFullRangeを指定することで
ITU_R_2020からITU_R_709_2に変換できることは確認できた。

以下にコードを示す。

import SwiftUI
import AVKit

struct ContentView: View {
  
  @StateObject var playerView = PlayerView()
  
  var body: some View {
    VStack {
      if let player = playerView.videoPlayer {
        VideoPlayer(player: player).scaledToFit()
      }
      if let uiImage = playerView.uiImage {
        Image(uiImage: uiImage).resizable().aspectRatio(contentMode: .fit)
      }
    }
  }
}

class PlayerView: ObservableObject {
  
  @Published var videoPlayer: AVPlayer?
  @Published var uiImage: UIImage?
  var videoPlayerItem: AVPlayerItem?
  var statusObserver: NSKeyValueObservation?
  let videoOutput: AVPlayerItemVideoOutput
  
  lazy var displayLink: CADisplayLink = CADisplayLink(target: self, selector:#selector(displayLinkCopyPixelBuffers(link:)))
  
  init(){
    let url = Bundle.main.url(forResource: "IMG_0287", withExtension: "MOV")!
    let asset = AVURLAsset(url: url, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
    videoPlayerItem = AVPlayerItem(asset: asset)
    videoPlayer = AVPlayer(playerItem: videoPlayerItem)
    
//    let videoColorProperties = [
//      AVVideoColorPrimariesKey: AVVideoColorPrimaries_P3_D65,
//      AVVideoTransferFunctionKey: AVVideoTransferFunction_Linear,
//      AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_2020]
    let videoColorProperties = [
      AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2,
      AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2,
      AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_709_2]
    
    let outputVideoSettings = [
//      AVVideoAllowWideColorKey: true,
//      kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_64RGBAHalf,
      kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
      AVVideoColorPropertiesKey: videoColorProperties,
      kCVPixelBufferMetalCompatibilityKey as String: true
    ] as [String: Any]
    
    videoOutput = AVPlayerItemVideoOutput(outputSettings: outputVideoSettings)
    statusObserver = videoPlayerItem?.observe(\.status,
                                               changeHandler: { playerItem, change in
      if playerItem.status == .readyToPlay {
        playerItem.add(self.videoOutput)
        self.displayLink.add(to: .main, forMode: .common)
        self.videoPlayer?.play()
      }
    })
  }
  
  @objc func displayLinkCopyPixelBuffers(link: CADisplayLink){
    let currentTime = videoOutput.itemTime(forHostTime: CACurrentMediaTime())
    
    if videoOutput.hasNewPixelBuffer(forItemTime: currentTime){
      if let buffer = videoOutput.copyPixelBuffer(forItemTime: currentTime, itemTimeForDisplay: nil){
 
        let colorAttachments = CVBufferCopyAttachment(buffer, kCVImageBufferYCbCrMatrixKey, nil)
        let colorPrimaries = CVBufferCopyAttachment(buffer, kCVImageBufferColorPrimariesKey, nil)
        let colorTransFunc = CVBufferCopyAttachment(buffer, kCVImageBufferTransferFunctionKey, nil)
        
        print("colorAttachments: ", colorAttachments)
        print("colorPrimaries: ", colorPrimaries)
        print("colorTransFunc: ", colorTransFunc)
        
        let ciImage = CIImage(cvPixelBuffer: buffer)
        let context = CIContext(options: nil)
        let orient = ciImage.oriented(.right)
        let cgImage = context.createCGImage(orient, from: orient.extent)
        if let cgImage = cgImage {
          let uiImage = UIImage(cgImage: cgImage)
          self.uiImage = uiImage
        }
      }
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

結果

上がHDR白飛び、下がデコードした画像。
若干速度は遅いのでAVPlayerとしては駄目だが他の処理に画像を渡すのには使えそうだ。

ここにメモを残すことで、きっと誰かがやり方が判明した時に教えてくれるに違いない。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1