Skip to content

Instantly share code, notes, and snippets.

Last active November 27, 2023 00:13
Show Gist options
  • Save tsuzukihashi/97e379a42e32cc0647aa7a4770d2d9a6 to your computer and use it in GitHub Desktop.
Save tsuzukihashi/97e379a42e32cc0647aa7a4770d2d9a6 to your computer and use it in GitHub Desktop.
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(
kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue!,
kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue!,
kCVPixelBufferIOSurfacePropertiesKey: [:] as CFDictionary,
] as CFDictionary,
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
Copy link

fukemy commented Dec 26, 2022

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

Copy link

This was very helpful - domo arigato!

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