Skip to content

Commit

Permalink
Genericize VideoCodec.
Browse files Browse the repository at this point in the history
  • Loading branch information
shogo4405 committed Oct 11, 2023
1 parent d382fdb commit 2346c57
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 55 deletions.
2 changes: 1 addition & 1 deletion Sources/Codec/VTSessionMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ enum VTSessionMode {
case compression
case decompression

func makeSession(_ videoCodec: VideoCodec) -> (any VTSessionConvertible)? {
func makeSession<T>(_ videoCodec: VideoCodec<T>) -> (any VTSessionConvertible)? {
switch self {
case .compression:
var session: VTCompressionSession?
Expand Down
32 changes: 15 additions & 17 deletions Sources/Codec/VideoCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,24 @@ import UIKit
*/
protocol VideoCodecDelegate: AnyObject {
/// Tells the receiver to set a formatDescription.
func videoCodec(_ codec: VideoCodec, didOutput formatDescription: CMFormatDescription?)
func videoCodec(_ codec: VideoCodec<Self>, didOutput formatDescription: CMFormatDescription?)
/// Tells the receiver to output an encoded or decoded sampleBuffer.
func videoCodec(_ codec: VideoCodec, didOutput sampleBuffer: CMSampleBuffer)
func videoCodec(_ codec: VideoCodec<Self>, didOutput sampleBuffer: CMSampleBuffer)
/// Tells the receiver to occured an error.
func videoCodec(_ codec: VideoCodec, errorOccurred error: IOMixerVideoError)
func videoCodec(_ codec: VideoCodec<Self>, errorOccurred error: IOMixerVideoError)
}

private let kVideoCodec_defaultFrameInterval: Double = 0.0
private let kVideoCodec_defaultAttributes: [NSString: AnyObject]? = [
kCVPixelBufferIOSurfacePropertiesKey: NSDictionary(),
kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue
]

// MARK: -
/**
* The VideoCodec class provides methods for encode or decode for video.
*/
final class VideoCodec {
private static let defaultFrameInterval: Double = 0.0

/// The videoCodec's attributes value.
static var defaultAttributes: [NSString: AnyObject]? = [
kCVPixelBufferIOSurfacePropertiesKey: NSDictionary(),
kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue
]

final class VideoCodec<T: VideoCodecDelegate> {
let lockQueue: DispatchQueue

/// Specifies the settings for a VideoCodec.
Expand All @@ -48,21 +46,21 @@ final class VideoCodec {
private(set) var isRunning: Atomic<Bool> = .init(false)
var needsSync: Atomic<Bool> = .init(true)
var attributes: [NSString: AnyObject]? {
guard VideoCodec.defaultAttributes != nil else {
guard kVideoCodec_defaultAttributes != nil else {
return nil
}
var attributes: [NSString: AnyObject] = [:]
for (key, value) in VideoCodec.defaultAttributes ?? [:] {
for (key, value) in kVideoCodec_defaultAttributes ?? [:] {
attributes[key] = value
}
attributes[kCVPixelBufferWidthKey] = NSNumber(value: settings.videoSize.width)
attributes[kCVPixelBufferHeightKey] = NSNumber(value: settings.videoSize.height)
return attributes
}
var passthrough = true
var frameInterval = VideoCodec.defaultFrameInterval
var frameInterval = kVideoCodec_defaultFrameInterval
var expectedFrameRate = IOMixer.defaultFrameRate
weak var delegate: (any VideoCodecDelegate)?
weak var delegate: T?
private var startedAt: CMTime = .zero
private(set) var inputFormat: CMFormatDescription? {
didSet {
Expand Down Expand Up @@ -172,7 +170,7 @@ final class VideoCodec {
guard startedAt <= presentationTimeStamp else {
return true
}
guard Self.defaultFrameInterval < frameInterval else {
guard kVideoCodec_defaultFrameInterval < frameInterval else {
return false
}
return presentationTimeStamp.seconds - self.presentationTimeStamp.seconds <= frameInterval
Expand Down
4 changes: 2 additions & 2 deletions Sources/Codec/VideoCodecSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public struct VideoCodecSettings: Codable {
)
}

func apply(_ codec: VideoCodec, rhs: VideoCodecSettings) {
func apply<T>(_ codec: VideoCodec<T>, rhs: VideoCodecSettings) {
if bitRate != rhs.bitRate {
let option = VTSessionOption(key: bitRateMode.key, value: NSNumber(value: bitRate))
if let status = codec.session?.setOption(option), status != noErr {
Expand All @@ -156,7 +156,7 @@ public struct VideoCodecSettings: Codable {
}
}

func options(_ codec: VideoCodec) -> Set<VTSessionOption> {
func options<T>(_ codec: VideoCodec<T>) -> Set<VTSessionOption> {
let isBaseline = profileLevel.contains("Baseline")
var options = Set<VTSessionOption>([
.init(key: .realTime, value: kCFBooleanTrue),
Expand Down
36 changes: 19 additions & 17 deletions Sources/Media/IOMixer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,18 @@ public final class IOMixer {
return IOMixer.audioEngineHolder.retain()
}()

private var isMultiCamSupported: Bool {
#if os(iOS) || os(tvOS)
if #available(iOS 13.0, *) {
return session is AVCaptureMultiCamSession
} else {
return false
}
#else
return false
#endif
}

deinit {
#if os(iOS) || os(macOS) || os(tvOS)
if #available(tvOS 17.0, *) {
Expand Down Expand Up @@ -268,13 +280,13 @@ public final class IOMixer {
extension IOMixer: IOUnitEncoding {
/// Starts encoding for video and audio data.
public func startEncoding() {
videoIO.startEncoding()
videoIO.startRunning()
audioIO.startEncoding()
}

/// Stop encoding.
public func stopEncoding() {
videoIO.stopEncoding()
videoIO.startRunning()
audioIO.stopEncoding()
}
}
Expand All @@ -283,15 +295,15 @@ extension IOMixer: IOUnitDecoding {
/// Starts decoding for video and audio data.
public func startDecoding() {
audioIO.startDecoding()
videoIO.startDecoding()
videoIO.startRunning()
mediaLink.startRunning()
}

/// Stop decoding.
public func stopDecoding() {
mediaLink.stopRunning()
audioIO.stopDecoding()
videoIO.stopDecoding()
videoIO.startRunning()
}
}

Expand Down Expand Up @@ -369,16 +381,6 @@ extension IOMixer: Running {
let error = AVError(_nsError: errorValue)
switch error.code {
case .unsupportedDeviceActiveFormat:
#if os(iOS) || os(tvOS)
let isMultiCamSupported: Bool
if #available(iOS 13.0, *) {
isMultiCamSupported = session is AVCaptureMultiCamSession
} else {
isMultiCamSupported = false
}
#else
let isMultiCamSupported = true
#endif
guard let device = error.device, let format = device.videoFormat(
width: sessionPreset.width ?? Int32(videoIO.settings.videoSize.width),
height: sessionPreset.height ?? Int32(videoIO.settings.videoSize.height),
Expand Down Expand Up @@ -443,11 +445,11 @@ extension IOMixer: Running {

extension IOMixer: VideoCodecDelegate {
// MARK: VideoCodecDelegate
func videoCodec(_ codec: VideoCodec, didOutput formatDescription: CMFormatDescription?) {
func videoCodec(_ codec: VideoCodec<IOMixer>, didOutput formatDescription: CMFormatDescription?) {
muxer?.videoFormat = formatDescription
}

func videoCodec(_ codec: VideoCodec, didOutput sampleBuffer: CMSampleBuffer) {
func videoCodec(_ codec: VideoCodec<IOMixer>, didOutput sampleBuffer: CMSampleBuffer) {
switch sampleBuffer.formatDescription?._mediaSubType {
case kCVPixelFormatType_1Monochrome,
kCVPixelFormatType_2Indexed,
Expand Down Expand Up @@ -502,7 +504,7 @@ extension IOMixer: VideoCodecDelegate {
}
}

func videoCodec(_ codec: VideoCodec, errorOccurred error: IOMixerVideoError) {
func videoCodec(_ codec: VideoCodec<IOMixer>, errorOccurred error: IOMixerVideoError) {
delegate?.mixer(self, videoErrorOccurred: error)
}
}
Expand Down
32 changes: 14 additions & 18 deletions Sources/Media/IOVideoUnit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ final class IOVideoUnit: NSObject, IOUnit {
#endif

var context: CIContext = .init()
var isRunning: Atomic<Bool> = .init(false)

#if os(iOS) || os(macOS)
var videoOrientation: AVCaptureVideoOrientation = .portrait {
Expand Down Expand Up @@ -113,7 +114,7 @@ final class IOVideoUnit: NSObject, IOUnit {
return videoMixer
}()
private lazy var codec: VideoCodec = {
var codec = VideoCodec(lockQueue: lockQueue)
var codec = VideoCodec<IOMixer>(lockQueue: lockQueue)
codec.delegate = mixer
return codec
}()
Expand Down Expand Up @@ -282,27 +283,22 @@ final class IOVideoUnit: NSObject, IOUnit {
}
}

extension IOVideoUnit: IOUnitEncoding {
// MARK: IOUnitEncoding
func startEncoding() {
codec.startRunning()
}

func stopEncoding() {
codec.stopRunning()
}
}

extension IOVideoUnit: IOUnitDecoding {
// MARK: IOUnitDecoding
func startDecoding() {
codec.delegate = mixer
extension IOVideoUnit: Running {
// MARK: Running
func startRunning() {
guard !isRunning.value else {
return
}
codec.startRunning()
isRunning.mutate { $0 = false }
}

func stopDecoding() {
func stopRunning() {
guard isRunning.value else {
return
}
codec.stopRunning()
drawable?.enqueue(nil)
isRunning.mutate { $0 = true }
}
}

Expand Down

0 comments on commit 2346c57

Please sign in to comment.