feat: Support 10-bit Video HDR (#1827)
* feat: Select 10-bit YUV HDR format if HDR is enabled * fix: Remove video EDR setting in favor of new 10-bit video HDR * Format Swift
This commit is contained in:
parent
cf4882b152
commit
9809075507
@ -115,6 +115,7 @@ enum FormatError {
|
||||
case invalidFps(fps: Int)
|
||||
case invalidHdr
|
||||
case invalidFormat
|
||||
case incompatiblePixelFormatWithHDR
|
||||
|
||||
var code: String {
|
||||
switch self {
|
||||
@ -124,6 +125,8 @@ enum FormatError {
|
||||
return "invalid-fps"
|
||||
case .invalidHdr:
|
||||
return "invalid-hdr"
|
||||
case .incompatiblePixelFormatWithHDR:
|
||||
return "incompatible-pixel-format-with-hdr-setting"
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +137,9 @@ enum FormatError {
|
||||
case let .invalidFps(fps):
|
||||
return "The given format cannot run at \(fps) FPS! Make sure your FPS is lower than `format.maxFps` but higher than `format.minFps`."
|
||||
case .invalidHdr:
|
||||
return "The currently selected format does not support HDR capture! Make sure you select a format which includes `supportsPhotoHDR`!"
|
||||
return "The currently selected format does not support HDR capture! Make sure you select a format which includes `supportsPhotoHDR`/`supportsVideoHDR`!"
|
||||
case .incompatiblePixelFormatWithHDR:
|
||||
return "The currently selected pixelFormat is not compatible with HDR! HDR only works with the `yuv` pixelFormat."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,34 +117,10 @@ extension CameraView {
|
||||
videoOutput!.setSampleBufferDelegate(self, queue: CameraQueues.videoQueue)
|
||||
videoOutput!.alwaysDiscardsLateVideoFrames = false
|
||||
|
||||
if let pixelFormat = pixelFormat as? String {
|
||||
let supportedPixelFormats = videoOutput!.availableVideoPixelFormatTypes
|
||||
let defaultFormat = supportedPixelFormats.first! // first value is always the most efficient format
|
||||
var pixelFormatType: OSType = defaultFormat
|
||||
switch pixelFormat {
|
||||
case "yuv":
|
||||
if supportedPixelFormats.contains(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
|
||||
pixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
|
||||
} else if supportedPixelFormats.contains(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
|
||||
pixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
||||
} else {
|
||||
invokeOnError(.device(.pixelFormatNotSupported))
|
||||
}
|
||||
case "rgb":
|
||||
if supportedPixelFormats.contains(kCVPixelFormatType_32BGRA) {
|
||||
pixelFormatType = kCVPixelFormatType_32BGRA
|
||||
} else {
|
||||
invokeOnError(.device(.pixelFormatNotSupported))
|
||||
}
|
||||
case "native":
|
||||
pixelFormatType = defaultFormat
|
||||
default:
|
||||
invokeOnError(.parameter(.invalid(unionName: "pixelFormat", receivedValue: pixelFormat)))
|
||||
}
|
||||
videoOutput!.videoSettings = [
|
||||
String(kCVPixelBufferPixelFormatTypeKey): pixelFormatType,
|
||||
]
|
||||
}
|
||||
let pixelFormatType = getPixelFormat(videoOutput: videoOutput!)
|
||||
videoOutput!.videoSettings = [
|
||||
String(kCVPixelBufferPixelFormatTypeKey): pixelFormatType,
|
||||
]
|
||||
captureSession.addOutput(videoOutput!)
|
||||
}
|
||||
|
||||
@ -157,6 +133,64 @@ extension CameraView {
|
||||
ReactLogger.log(level: .info, message: "Session successfully configured!")
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the pixel format that should be used for the AVCaptureVideoDataOutput.
|
||||
If HDR is enabled, this will return YUV 4:2:0 10-bit.
|
||||
If HDR is disabled, this will return whatever the user specified as a pixelFormat, or the most efficient format as a fallback.
|
||||
*/
|
||||
private func getPixelFormat(videoOutput: AVCaptureVideoDataOutput) -> OSType {
|
||||
let supportedPixelFormats = videoOutput.availableVideoPixelFormatTypes
|
||||
// as per documentation, the first value is always the most efficient format
|
||||
let defaultFormat = supportedPixelFormats.first!
|
||||
|
||||
// If the user enabled HDR, we can only use the YUV 4:2:0 10-bit pixel format.
|
||||
if hdr == true {
|
||||
guard pixelFormat == nil || pixelFormat == "yuv" else {
|
||||
invokeOnError(.format(.incompatiblePixelFormatWithHDR))
|
||||
return defaultFormat
|
||||
}
|
||||
guard supportedPixelFormats.contains(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange) else {
|
||||
invokeOnError(.format(.invalidHdr))
|
||||
return defaultFormat
|
||||
}
|
||||
// YUV 4:2:0 10-bit
|
||||
return kCVPixelFormatType_420YpCbCr10BiPlanarFullRange
|
||||
}
|
||||
|
||||
// If the user didn't specify a custom pixelFormat, just return the default one.
|
||||
guard let pixelFormat = pixelFormat else {
|
||||
return defaultFormat
|
||||
}
|
||||
|
||||
// If we don't use HDR, we can use any other custom pixel format.
|
||||
switch pixelFormat {
|
||||
case "yuv":
|
||||
if supportedPixelFormats.contains(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
|
||||
// YUV 4:2:0 8-bit (full video colors)
|
||||
return kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
|
||||
} else if supportedPixelFormats.contains(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
|
||||
// YUV 4:2:0 8-bit (limited video colors)
|
||||
return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
||||
} else {
|
||||
invokeOnError(.device(.pixelFormatNotSupported))
|
||||
return defaultFormat
|
||||
}
|
||||
case "rgb":
|
||||
if supportedPixelFormats.contains(kCVPixelFormatType_32BGRA) {
|
||||
// RGBA 8-bit
|
||||
return kCVPixelFormatType_32BGRA
|
||||
} else {
|
||||
invokeOnError(.device(.pixelFormatNotSupported))
|
||||
return defaultFormat
|
||||
}
|
||||
case "native":
|
||||
return defaultFormat
|
||||
default:
|
||||
invokeOnError(.parameter(.invalid(unionName: "pixelFormat", receivedValue: pixelFormat as String)))
|
||||
return defaultFormat
|
||||
}
|
||||
}
|
||||
|
||||
// pragma MARK: Configure Device
|
||||
|
||||
/**
|
||||
@ -188,17 +222,6 @@ extension CameraView {
|
||||
device.activeVideoMinFrameDuration = CMTime.invalid
|
||||
device.activeVideoMaxFrameDuration = CMTime.invalid
|
||||
}
|
||||
if hdr != nil {
|
||||
if hdr == true && !device.activeFormat.isVideoHDRSupported {
|
||||
invokeOnError(.format(.invalidHdr))
|
||||
return
|
||||
}
|
||||
if !device.automaticallyAdjustsVideoHDREnabled {
|
||||
if device.isVideoHDREnabled != hdr!.boolValue {
|
||||
device.isVideoHDREnabled = hdr!.boolValue
|
||||
}
|
||||
}
|
||||
}
|
||||
if lowLightBoost != nil {
|
||||
if lowLightBoost == true && !device.isLowLightBoostSupported {
|
||||
invokeOnError(.device(.lowLightBoostNotSupported))
|
||||
|
@ -26,9 +26,9 @@ private let propsThatRequireReconfiguration = ["cameraId",
|
||||
"photo",
|
||||
"video",
|
||||
"enableFrameProcessor",
|
||||
"hdr",
|
||||
"pixelFormat"]
|
||||
private let propsThatRequireDeviceReconfiguration = ["fps",
|
||||
"hdr",
|
||||
"lowLightBoost"]
|
||||
|
||||
// MARK: - CameraView
|
||||
|
@ -50,7 +50,7 @@ extension AVCaptureDevice.Format {
|
||||
"minISO": minISO,
|
||||
"fieldOfView": videoFieldOfView,
|
||||
"maxZoom": videoMaxZoomFactor,
|
||||
"supportsVideoHDR": isVideoHDRSupported,
|
||||
"supportsVideoHDR": availablePixelFormats.contains(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange),
|
||||
"supportsPhotoHDR": false,
|
||||
"minFps": minFrameRate,
|
||||
"maxFps": maxFrameRate,
|
||||
|
@ -18,6 +18,7 @@ export type DeviceError =
|
||||
export type FormatError =
|
||||
| 'format/invalid-fps'
|
||||
| 'format/invalid-hdr'
|
||||
| 'format/incompatible-pixel-format-with-hdr-setting'
|
||||
| 'format/invalid-low-light-boost'
|
||||
| 'format/invalid-format'
|
||||
| 'format/invalid-color-space';
|
||||
|
Loading…
Reference in New Issue
Block a user