Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import AVFoundation
import CoreGraphics

// The colour every recording is decoded as: BT.709 primaries, transfer and matrix, studio
// range. The compositor assumes it for every file, and the Windows (#929) and Linux (#932)
// helpers write it. Left to the defaults, ScreenCaptureKit hands over the display's own colour
// space (P3 on most Macs) and VideoToolbox picks the matrix and the tags
// (getopenscreen/openscreen#943).
//
// Range has no key here: it comes from the pixel format, and the stream captures `420v`, the
// studio-range one.

/// The matrix ScreenCaptureKit converts to YCbCr with. It has to be the one the file is tagged
/// with in `videoColorProperties`, which `VideoColorTests` pins.
public let captureYCbCrMatrix: CFString = CGDisplayStream.yCbCrMatrix_ITU_R_709_2

/// `AVVideoColorPropertiesKey` for the H.264 writer input.
public let videoColorProperties: [String: String] = [
AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2,
AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2,
AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_709_2,
]
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,15 @@ final class ScreenCaptureRecorder: NSObject, SCStreamOutput, SCStreamDelegate {
configuration.minimumFrameInterval = CMTime(value: 1, timescale: CMTimeScale(max(1, request.video.fps)))
configuration.queueDepth = 6
configuration.showsCursor = !request.video.hideSystemCursor
configuration.pixelFormat = kCVPixelFormatType_32BGRA
// Studio-range BT.709 YCbCr from ScreenCaptureKit itself, which the encoder takes as is
// and the compositor decodes (#943). BGRA left the matrix to VideoToolbox. Nothing here
// reads the pixels: a frame goes through `retimedSampleBuffer` (timing only) to
// `append`, and `isCompleteFrame` reads attachments. 1.5 bytes a pixel instead of 4,
// times `queueDepth`.
configuration.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
configuration.colorMatrix = captureYCbCrMatrix
// Unset, the buffers carry the display's colour space: P3 on most Macs.
configuration.colorSpaceName = CGColorSpace.sRGB
configuration.sampleRate = 48_000
configuration.channelCount = 2
configuration.excludesCurrentProcessAudio = true
Expand Down Expand Up @@ -754,6 +762,8 @@ final class ScreenCaptureRecorder: NSObject, SCStreamOutput, SCStreamDelegate {
AVVideoCodecKey: AVVideoCodecType.h264,
AVVideoWidthKey: outputWidth,
AVVideoHeightKey: outputHeight,
// BT.709 tags, the colour the stream is captured in (#943).
AVVideoColorPropertiesKey: videoColorProperties,
AVVideoCompressionPropertiesKey: [
// From the size this stream really got. The renderer sends none (#924).
AVVideoAverageBitRateKey: request.video.bitrate
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import AVFoundation
import XCTest

import OpenScreenCaptureCore

/// The file has to say what its samples are: a matrix that converts with one standard and tags
/// another is the shifted-hue defect #943 exists to remove.
final class VideoColorTests: XCTestCase {
func testCaptureMatrixIsTheOneTheFileIsTaggedWith() {
XCTAssertEqual(captureYCbCrMatrix as String, videoColorProperties[AVVideoYCbCrMatrixKey])
}

func testFileIsTaggedBT709() {
XCTAssertEqual(videoColorProperties[AVVideoColorPrimariesKey], "ITU_R_709_2")
XCTAssertEqual(videoColorProperties[AVVideoTransferFunctionKey], "ITU_R_709_2")
XCTAssertEqual(videoColorProperties[AVVideoYCbCrMatrixKey], "ITU_R_709_2")
}
}
Loading