Last active
July 7, 2023 09:42
-
-
Save tsuzukihashi/9b6302f023c19192b685d1e689025968 to your computer and use it in GitHub Desktop.
Data → CMBlockBuffer → CMSampleBuffer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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