文字っぽいの。

文字を書いています。写真も混ざります。

CMSampleBuffer を Resize する

はじめに

CMSampleBuffer をResizeしてサイズを小さく(大きく)したいことがあると思います。試していませんが、途中で CIImage になっているので、回転、変形、加工などCIFilterで行う処理を適用できると思います。

環境

  • Xcode11.1
  • Swift 5.1

手順まとめ

  1. CMSampleTimingInfoを保存しておく
  2. CMSampleBufferをCIImageに変換する
  3. CIImageをResizeする
  4. ResizeしたCIImageをCVPixelBufferに変換する
  5. CVPixelBufferをCMSampleBufferに変換する

手順詳細

1.CMSampleTimingInfoを保存しておく

import ReplayKit

final class Sample {
    let timingInfo: CMSampleTimingInfo

    init(sampleBuffer: CMSampleBuffer) {
        let presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
        let duration = CMSampleBufferGetDuration(sampleBuffer)
        let decodeTimeStamp = CMSampleBufferGetDecodeTimeStamp(sampleBuffer)
        timingInfo = CMSampleTimingInfo(duration: duration, presentationTimeStamp: presentationTimeStamp, decodeTimeStamp: decodeTimeStamp)
    }
}

2.CMSampleBufferをCIImageに変換する

private func generateCIImage(from sampleBuffer: CMSampleBuffer) -> CIImage? {
    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
        return nil
    }
    
    let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
    return ciImage
}

3.CIImageをResizeする

private func resizeCIImage(from ciImage: CIImage, scale: CGFloat) -> CIImage? {
    guard let filter = CIFilter(name: "CILanczosScaleTransform") else {
        return nil
    }

    filter.setDefaults()
    filter.setValue(ciImage, forKey: kCIInputImageKey)
    filter.setValue(scale, forKey: kCIInputScaleKey)
    return filter.outputImage
}

【追記】

こちらの処理は、CoreImage.CIFilterBuiltins - cockscomblog?の記事を参考にすると、もう少しスッキリ書けそうです。

4.ResizeしたCIImageをCVPixelBufferに変換する

private func generateCVPixelBuffer(from ciImage: CIImage) -> CVPixelBuffer? {
    let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary
    var pixelBuffer: CVPixelBuffer!
    let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(ciImage.extent.size.width), Int(ciImage.extent.size.height), kCVPixelFormatType_32BGRA, attrs, &pixelBuffer)
    guard status == kCVReturnSuccess else {
        print("CVPixelBufferCreateに失敗")
        return nil
    }

    let ciContext = CIContext()
    ciContext.render(ciImage, to: pixelBuffer, bounds: ciImage.extent, colorSpace: CGColorSpaceCreateDeviceRGB())
    CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
    
    return pixelBuffer
}

5.CVPixelBufferをCMSampleBufferに変換する

この際に、保持しておいた CMSampleTimingInfo を利用します。

private func generateCMSampleBuffer(from cvPixelBuffer: CVPixelBuffer) -> CMSampleBuffer? {
    var sampleBuffer: CMSampleBuffer?
    var timimgInfo: CMSampleTimingInfo = timingInfo
    var videoInfo: CMVideoFormatDescription!
    CMVideoFormatDescriptionCreateForImageBuffer(allocator: nil, imageBuffer: cvPixelBuffer, formatDescriptionOut: &videoInfo)
    CMSampleBufferCreateForImageBuffer(allocator: kCFAllocatorDefault,
                                       imageBuffer: cvPixelBuffer,
                                       dataReady: true,
                                       makeDataReadyCallback: nil,
                                       refcon: nil,
                                       formatDescription: videoInfo,
                                       sampleTiming: &timimgInfo,
                                       sampleBufferOut: &sampleBuffer)

    return sampleBuffer
}

完成

これでResize処理をしたCMSampleBufferを生成することができます。

参考資料