Last active
November 27, 2023 00:13
-
-
Save tsuzukihashi/97e379a42e32cc0647aa7a4770d2d9a6 to your computer and use it in GitHub Desktop.
UIView to CMSampleBuffer (UIViewをCMSampleBufferに変換するExtension)
This file contains 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 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 | |
} | |
} |
This was very helpful - domo arigato!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hi, thanks for usefull gist. Did you tried to apply webrtc call with PIP mode?