Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
UIView to CMSampleBuffer (UIViewをCMSampleBufferに変換するExtension)
import UIKit
import CoreMedia
extension UIView {
func toCMSampleBuffer() -> CMSampleBuffer? {
let scale: CGFloat = UIScreen.main.scale
let size: CGSize = .init(width: bounds.width * scale, height: bounds.height * scale)
guard let pixelBuffer = makeCVPicelBuffer(scale: scale, size: size) else { return nil }
defer {
CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
}
CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
guard let context = makeCGContext(scale: scale, size: size, pixelBuffer: pixelBuffer) else { return nil }
layer.render(in: context)
guard let formatDescription = makeCMFormatDescription(pixelBuffer: pixelBuffer) else { return nil }
do {
return try CMSampleBuffer(
imageBuffer: pixelBuffer,
formatDescription: formatDescription,
sampleTiming: getCMSampleTimingInfo()
)
} catch {
assertionFailure("Failed to create CMSampleBuffer: \(error)")
return nil
}
}
private func makeCVPicelBuffer(scale: CGFloat, size: CGSize) -> CVPixelBuffer? {
var pixelBuffer: CVPixelBuffer?
let createPixelBufferResult: CVReturn = CVPixelBufferCreate(
kCFAllocatorDefault,
Int(size.width),
Int(size.height),
kCVPixelFormatType_32ARGB,
[
kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue!,
kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue!,
kCVPixelBufferIOSurfacePropertiesKey: [:] as CFDictionary,
] as CFDictionary,
&pixelBuffer
)
if createPixelBufferResult != kCVReturnSuccess {
assertionFailure("Failed to create CVPixelBuffer: \(createPixelBufferResult)")
return nil
}
return pixelBuffer
}
private func makeCGContext(scale: CGFloat, size: CGSize, pixelBuffer: CVPixelBuffer) -> CGContext? {
guard let context: CGContext = .init(
data: CVPixelBufferGetBaseAddress(pixelBuffer),
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: 8,
bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer),
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
) else { return nil }
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: scale, y: -scale)
return context
}
private func makeCMFormatDescription(pixelBuffer: CVPixelBuffer) -> CMFormatDescription? {
var formatDescription: CMFormatDescription?
let createImageBufferResult: CVReturn = CMVideoFormatDescriptionCreateForImageBuffer(
allocator: kCFAllocatorDefault,
imageBuffer: pixelBuffer,
formatDescriptionOut: &formatDescription
)
if createImageBufferResult != kCVReturnSuccess {
assertionFailure("Failed to create CMFormatDescription: \(createImageBufferResult)")
return nil
}
return formatDescription
}
private func getCMSampleTimingInfo() -> CMSampleTimingInfo {
let currentTime: CMTime = .init(
seconds: CACurrentMediaTime(),
preferredTimescale: 60
)
let timingInfo: CMSampleTimingInfo = .init(
duration: .init(seconds: 1, preferredTimescale: 60),
presentationTimeStamp: currentTime,
decodeTimeStamp: currentTime
)
return timingInfo
}
}
@fukemy
Copy link

fukemy commented Dec 26, 2022

hi, thanks for usefull gist. Did you tried to apply webrtc call with PIP mode?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment