iOS でアルファチャンネル付きで動画を作成する
- AVAssetWriter を使うと、iOS で透過情報付きの動画を作成する事が可能です
- iOS の写真アプリは透過付きの動画を再生可能です
- ツールバーの表示/非表示で背景色が変わったり、プルダウンすることで後ろの画像が見えるので、動画の背景が透過されている事がわかります
iOSでアルファチャンネル付きの動画を作成して保存できた✌️
— ふじき (@fzkqi) June 7, 2021
MTLTexture にレンダリングして hevc に保存してます pic.twitter.com/hWrjQBGxJ6
サンプルアプリ
- リポジトリはこちらです
-
Examples/NativeExamples/NativeExamples.xcodeproj
のCrossMetal iOS
ターゲットです - Xcode の Metal の Game アプリをベースにしています
透過情報付きで保存するポイント
-
AVAssetWriterInput
の生成時の settings で、hevcWithAlpha
を指定します
let videoConfigs: [String: Any] = [AVVideoCodecKey : AVVideoCodecType.hevcWithAlpha,
AVVideoWidthKey : 1920,
AVVideoHeightKey :1080]
let videoAssetWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoConfigs)
- 書き込む CMSampleBuffer の CVPixelBuffer のフォーマットとして
kCVPixelFormatType_32BGRA
を選択します
let options = [kCVPixelBufferIOSurfacePropertiesKey: [:]] as [String : Any]
let status = CVPixelBufferCreate(nil, width, height,
kCVPixelFormatType_32BGRA,
options as CFDictionary,
&pixelBuffer)
- 透過情報付きの画像データを生成し、AVAssetWriterInput に書き込むことで、アルファチャンネル付きの動画を取得可能です
実装
録画開始
- ファイルタイプは mov を指定しました
- hevcWithAlpha が保存できるコンテナであれば良いと思います
let assetWriter = try! AVAssetWriter(outputURL: url, fileType: AVFileType.mov)
let videoConfigs: [String: Any] = [AVVideoCodecKey : AVVideoCodecType.hevcWithAlpha,
AVVideoWidthKey : 1920,
AVVideoHeightKey : 1080]
let videoAssetWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoConfigs)
videoAssetWriterInput.expectsMediaDataInRealTime = true
assetWriter.add(videoAssetWriterInput)
- 元の画像を Metal でレンダリングしているので、MTLTexture と描画時刻を受け取ります
- MTLTexture -> CIImage -> CVPixelBuffer -> CMSampleBuffer の順番に変換します
func make(mtlTexture: MTLTexture, time: CMTime) -> CMSampleBuffer? {
let ci = CIImage(mtlTexture: mtlTexture, options: nil)!
CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
context.render(ci, to: pixelBuffer)
var res: CMSampleBuffer? = nil
var sampleTiming = CMSampleTimingInfo()
sampleTiming.presentationTimeStamp = time
let _ = CMSampleBufferCreateForImageBuffer(allocator: kCFAllocatorDefault,
imageBuffer: pixelBuffer,
dataReady: true,
makeDataReadyCallback: nil,
refcon: nil,
formatDescription: formatDescription,
sampleTiming: &sampleTiming,
sampleBufferOut: &res)
CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
return res
}
- 作成した CMSampleBuffer を videoAssetWriterInput に書き込みます
videoAssetWriterInput.append(sample)
録画終了
- finishWriting することで mov ファイルが作成されています
assetWriter.finishWriting(completionHandler: { })
保存
- 指定した url に動画が保存されているので、必要に応じて保存します
- ALAssetsLibrary は deprecated なので Photos framework を使った方が良さそうです
ALAssetsLibrary()
.writeVideoAtPath(toSavedPhotosAlbum: url) { (url: URL?, error: Error?) in
print("url: \(String(describing: url)), error: \(String(describing: error))")
}
おわりに
- アルファチャンネル付きの動画を保存可能になると、汎用性が高そうです
- 例えば、LiDAR のデプスをアルファチャンネルに入れて保存するなど活用できそうです
- Unity でも R8G8B8A8_UNorm など、alpha チャンネルがあるテクスチャを渡すことで、透過情報付きの動作を作成する事が可能です