Skip to content

Instantly share code, notes, and snippets.

@tsuzukihashi
Last active July 7, 2023 09:42
Show Gist options
  • Save tsuzukihashi/9b6302f023c19192b685d1e689025968 to your computer and use it in GitHub Desktop.
Save tsuzukihashi/9b6302f023c19192b685d1e689025968 to your computer and use it in GitHub Desktop.
Data → CMBlockBuffer → CMSampleBuffer
import AVKit
import AVFoundation
extension Data {
func toCMBlockBuffer() throws -> CMBlockBuffer {
var blockBuffer: CMBlockBuffer?
let data: NSMutableData = .init(data: self)
var source: CMBlockBufferCustomBlockSource = .init()
source.refCon = Unmanaged.passRetained(data).toOpaque()
source.FreeBlock = freeBlock
let result = CMBlockBufferCreateWithMemoryBlock(
allocator: kCFAllocatorDefault,
memoryBlock: data.mutableBytes,
blockLength: data.length,
blockAllocator: kCFAllocatorNull,
customBlockSource: &source,
offsetToData: 0,
dataLength: data.length,
flags: 0,
blockBufferOut: &blockBuffer
)
if OSStatus(result) != kCMBlockBufferNoErr {
throw CMEncodingError.cmBlockCreationFailed
}
guard let buffer = blockBuffer else {
throw CMEncodingError.cmBlockCreationFailed
}
assert(CMBlockBufferGetDataLength(buffer) == data.length)
return buffer
}
}
private func freeBlock(_ refCon: UnsafeMutableRawPointer?, doomedMemoryBlock: UnsafeMutableRawPointer, sizeInBytes: Int) -> Void {
let unmanagedData = Unmanaged<NSData>.fromOpaque(refCon!)
unmanagedData.release()
}
enum CMEncodingError: Error {
case cmBlockCreationFailed
}
import UIKit
import AVKit
import AVFoundation
extension UIImage {
var cmSampleBuffer: CMSampleBuffer? {
guard let jpegData = jpegData(compressionQuality: 1) else { return nil }
return sampleBufferFromJPEGData(jpegData)
}
private func sampleBufferFromJPEGData(_ jpegData: Data) -> CMSampleBuffer? {
guard let cgImage = cgImage else { return nil }
let rawPixelSize = CGSize(width: cgImage.width, height: cgImage.height)
var format: CMFormatDescription? = nil
let _ = CMVideoFormatDescriptionCreate(
allocator: kCFAllocatorDefault,
codecType: kCMVideoCodecType_JPEG,
width: Int32(rawPixelSize.width),
height: Int32(rawPixelSize.height),
extensions: nil,
formatDescriptionOut: &format
)
do {
let cmBlockBuffer: CMBlockBuffer = try jpegData.toCMBlockBuffer()
var size = jpegData.count
var sampleBuffer: CMSampleBuffer? = nil
let nowTime = CMTime(seconds: CACurrentMediaTime(), preferredTimescale: 60)
let _1_60_s = CMTime(value: 1, timescale: 60)
var timingInfo: CMSampleTimingInfo = CMSampleTimingInfo(
duration: _1_60_s,
presentationTimeStamp: nowTime,
decodeTimeStamp: .invalid
)
let _ = CMSampleBufferCreateReady(
allocator: kCFAllocatorDefault,
dataBuffer: cmBlockBuffer,
formatDescription: format,
sampleCount: 1,
sampleTimingEntryCount: 1,
sampleTimingArray: &timingInfo,
sampleSizeEntryCount: 1,
sampleSizeArray: &size,
sampleBufferOut: &sampleBuffer
)
if sampleBuffer != nil {
return sampleBuffer
} else {
print("### sampleBuffer is nil")
return nil
}
} catch {
print("### error ugh ", error)
return nil
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment