Merge H264Decoder -> VideoCodec.

This commit is contained in:
shogo4405 2022-09-24 02:20:45 +09:00
parent 65b8aca8ee
commit 04d23d9953
15 changed files with 257 additions and 345 deletions

View File

@ -138,7 +138,6 @@
29AF3FCF1D7C744C00E41212 /* NetStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AF3FCE1D7C744C00E41212 /* NetStream.swift */; };
29AF3FD01D7C745200E41212 /* NetStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AF3FCE1D7C744C00E41212 /* NetStream.swift */; };
29B8765B1CD70A7900FC07DA /* AudioCodec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876571CD70A7900FC07DA /* AudioCodec.swift */; };
29B8765C1CD70A7900FC07DA /* H264Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876581CD70A7900FC07DA /* H264Decoder.swift */; };
29B8765D1CD70A7900FC07DA /* VideoCodec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876591CD70A7900FC07DA /* VideoCodec.swift */; };
29B876691CD70AB300FC07DA /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876631CD70AB300FC07DA /* Constants.swift */; };
29B8766D1CD70AB300FC07DA /* DataConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876671CD70AB300FC07DA /* DataConvertible.swift */; };
@ -175,7 +174,6 @@
29B876BD1CD70B3900FC07DA /* CRC32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876B91CD70B3900FC07DA /* CRC32.swift */; };
29B876BE1CD70B3900FC07DA /* EventDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876BA1CD70B3900FC07DA /* EventDispatcher.swift */; };
29B876EC1CD70D5900FC07DA /* AudioCodec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876571CD70A7900FC07DA /* AudioCodec.swift */; };
29B876ED1CD70D5900FC07DA /* H264Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876581CD70A7900FC07DA /* H264Decoder.swift */; };
29B876EE1CD70D5900FC07DA /* VideoCodec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876591CD70A7900FC07DA /* VideoCodec.swift */; };
29B876F01CD70D5900FC07DA /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876631CD70AB300FC07DA /* Constants.swift */; };
29B876F41CD70D5900FC07DA /* DataConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876671CD70AB300FC07DA /* DataConvertible.swift */; };
@ -253,7 +251,6 @@
29EB3DEB1ED055B0001CAE8B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D3D5051ED053C000DD4AA6 /* ViewController.swift */; };
29EB3DED1ED055B4001CAE8B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 29D3D5001ED053C000DD4AA6 /* Assets.xcassets */; };
29EB3DEE1ED05763001CAE8B /* AudioCodec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876571CD70A7900FC07DA /* AudioCodec.swift */; };
29EB3DEF1ED05766001CAE8B /* H264Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876581CD70A7900FC07DA /* H264Decoder.swift */; };
29EB3DF01ED05768001CAE8B /* VideoCodec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876591CD70A7900FC07DA /* VideoCodec.swift */; };
29EB3DF11ED0576C001CAE8B /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876631CD70AB300FC07DA /* Constants.swift */; };
29EB3DF21ED05770001CAE8B /* DataConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876671CD70AB300FC07DA /* DataConvertible.swift */; };
@ -344,15 +341,15 @@
BC4914A628DDD367009E2DF6 /* VTSessionOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914A528DDD367009E2DF6 /* VTSessionOption.swift */; };
BC4914A728DDD367009E2DF6 /* VTSessionOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914A528DDD367009E2DF6 /* VTSessionOption.swift */; };
BC4914A828DDD367009E2DF6 /* VTSessionOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914A528DDD367009E2DF6 /* VTSessionOption.swift */; };
BC4914AA28DDD966009E2DF6 /* VTSessionHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914A928DDD966009E2DF6 /* VTSessionHolder.swift */; };
BC4914AB28DDD966009E2DF6 /* VTSessionHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914A928DDD966009E2DF6 /* VTSessionHolder.swift */; };
BC4914AC28DDD966009E2DF6 /* VTSessionHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914A928DDD966009E2DF6 /* VTSessionHolder.swift */; };
BC4914AE28DDF445009E2DF6 /* VTDecompressionSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914AD28DDF445009E2DF6 /* VTDecompressionSession+Extension.swift */; };
BC4914AF28DDF445009E2DF6 /* VTDecompressionSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914AD28DDF445009E2DF6 /* VTDecompressionSession+Extension.swift */; };
BC4914B028DDF445009E2DF6 /* VTDecompressionSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914AD28DDF445009E2DF6 /* VTDecompressionSession+Extension.swift */; };
BC4914B228DDFE31009E2DF6 /* VTSessionOptionKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914B128DDFE31009E2DF6 /* VTSessionOptionKey.swift */; };
BC4914B328DDFE31009E2DF6 /* VTSessionOptionKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914B128DDFE31009E2DF6 /* VTSessionOptionKey.swift */; };
BC4914B428DDFE31009E2DF6 /* VTSessionOptionKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914B128DDFE31009E2DF6 /* VTSessionOptionKey.swift */; };
BC4914B628DEC2FE009E2DF6 /* VTSessionMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914B528DEC2FE009E2DF6 /* VTSessionMode.swift */; };
BC4914B728DEC2FE009E2DF6 /* VTSessionMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914B528DEC2FE009E2DF6 /* VTSessionMode.swift */; };
BC4914B828DEC2FE009E2DF6 /* VTSessionMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4914B528DEC2FE009E2DF6 /* VTSessionMode.swift */; };
BC4C9EAC23F00F3A004A14F2 /* Preference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 291468161E581C7D00E619BA /* Preference.swift */; };
BC4C9EAF23F2E736004A14F2 /* AudioStreamBasicDescription+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4C9EAE23F2E736004A14F2 /* AudioStreamBasicDescription+Extension.swift */; };
BC4C9EB023F2E736004A14F2 /* AudioStreamBasicDescription+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC4C9EAE23F2E736004A14F2 /* AudioStreamBasicDescription+Extension.swift */; };
@ -763,7 +760,6 @@
29AF3FCE1D7C744C00E41212 /* NetStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetStream.swift; sourceTree = "<group>"; };
29B8761B1CD701F900FC07DA /* HaishinKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HaishinKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
29B876571CD70A7900FC07DA /* AudioCodec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioCodec.swift; sourceTree = "<group>"; };
29B876581CD70A7900FC07DA /* H264Decoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = H264Decoder.swift; sourceTree = "<group>"; };
29B876591CD70A7900FC07DA /* VideoCodec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoCodec.swift; sourceTree = "<group>"; };
29B876631CD70AB300FC07DA /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
29B876671CD70AB300FC07DA /* DataConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataConvertible.swift; sourceTree = "<group>"; };
@ -844,9 +840,9 @@
BC44A1A823D31E92002D4297 /* AudioCodecBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioCodecBuffer.swift; sourceTree = "<group>"; wrapsLines = 1; };
BC4914A128DDD33D009E2DF6 /* VTSessionConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VTSessionConvertible.swift; sourceTree = "<group>"; };
BC4914A528DDD367009E2DF6 /* VTSessionOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VTSessionOption.swift; sourceTree = "<group>"; };
BC4914A928DDD966009E2DF6 /* VTSessionHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VTSessionHolder.swift; sourceTree = "<group>"; };
BC4914AD28DDF445009E2DF6 /* VTDecompressionSession+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VTDecompressionSession+Extension.swift"; sourceTree = "<group>"; };
BC4914B128DDFE31009E2DF6 /* VTSessionOptionKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VTSessionOptionKey.swift; sourceTree = "<group>"; };
BC4914B528DEC2FE009E2DF6 /* VTSessionMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VTSessionMode.swift; sourceTree = "<group>"; };
BC4C9EAE23F2E736004A14F2 /* AudioStreamBasicDescription+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AudioStreamBasicDescription+Extension.swift"; sourceTree = "<group>"; };
BC558267240BB40E00011AC0 /* RTMPStreamInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTMPStreamInfo.swift; sourceTree = "<group>"; };
BC566F6D25D2ECC500573C4C /* HLSService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HLSService.swift; sourceTree = "<group>"; };
@ -1019,10 +1015,9 @@
29B876571CD70A7900FC07DA /* AudioCodec.swift */,
BC44A1A823D31E92002D4297 /* AudioCodecBuffer.swift */,
297E69112324E38800D418AB /* AudioCodecFormat.swift */,
29B876581CD70A7900FC07DA /* H264Decoder.swift */,
29B876591CD70A7900FC07DA /* VideoCodec.swift */,
BC4914A128DDD33D009E2DF6 /* VTSessionConvertible.swift */,
BC4914A928DDD966009E2DF6 /* VTSessionHolder.swift */,
BC4914B528DEC2FE009E2DF6 /* VTSessionMode.swift */,
BC4914A528DDD367009E2DF6 /* VTSessionOption.swift */,
BC4914B128DDFE31009E2DF6 /* VTSessionOptionKey.swift */,
);
@ -2080,7 +2075,6 @@
296242621D8DB86500C451A3 /* TSWriter.swift in Sources */,
BC9CFA9323BDE8B700917EEF /* NetStreamDrawable.swift in Sources */,
29B8769B1CD70B1100FC07DA /* MIME.swift in Sources */,
BC4914AA28DDD966009E2DF6 /* VTSessionHolder.swift in Sources */,
BCC1A727264FA1C100661156 /* ProfileLevelIndicationIndexDescriptor.swift in Sources */,
29B8769C1CD70B1100FC07DA /* NetClient.swift in Sources */,
BC94E530264146540094C169 /* MP4ReaderConvertible.swift in Sources */,
@ -2162,6 +2156,7 @@
29B8769D1CD70B1100FC07DA /* NetService.swift in Sources */,
29B8769E1CD70B1100FC07DA /* NetSocket.swift in Sources */,
2958911A1EEB8E3F00CE51E1 /* FLVAudioCodec.swift in Sources */,
BC4914B628DEC2FE009E2DF6 /* VTSessionMode.swift in Sources */,
295891261EEB8EF300CE51E1 /* FLVAACPacket.swift in Sources */,
29B876791CD70ACE00FC07DA /* HTTPStream.swift in Sources */,
BCA97C15263D93DB0027213C /* MP4VisualSampleEntry.swift in Sources */,
@ -2183,7 +2178,6 @@
BCB976D126107B1200C9A649 /* TSAdaptationExtensionField.swift in Sources */,
BCA97C05263C61940027213C /* MP4MediaHeaderBox.swift in Sources */,
29B876771CD70ACE00FC07DA /* HTTPResponse.swift in Sources */,
29B8765C1CD70A7900FC07DA /* H264Decoder.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -2231,7 +2225,6 @@
29B876EC1CD70D5900FC07DA /* AudioCodec.swift in Sources */,
BCA97C0B263D80F40027213C /* MP4SampleEntry.swift in Sources */,
BCC1A6D826446B2D00661156 /* MP4SoundMediaHeaderBox.swift in Sources */,
29B876ED1CD70D5900FC07DA /* H264Decoder.swift in Sources */,
BCB977402621812800C9A649 /* AVCFormatStream.swift in Sources */,
29B876EE1CD70D5900FC07DA /* VideoCodec.swift in Sources */,
BCA97BF1263C31020027213C /* MP4SampleDescriptionBox.swift in Sources */,
@ -2291,6 +2284,7 @@
BCC1A6C62643F41600661156 /* MP4MovieFragmentBox.Builder.swift in Sources */,
296242641D8DBA9000C451A3 /* TSWriter.swift in Sources */,
2958912F1EEB8F4100CE51E1 /* FLVSoundType.swift in Sources */,
BC4914B728DEC2FE009E2DF6 /* VTSessionMode.swift in Sources */,
BC94E4FF263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift in Sources */,
29B877031CD70D5A00FC07DA /* AVAudioIOUnit.swift in Sources */,
BCC1A6C02643F41600661156 /* MP4TrackFragmentBox.Builder.swift in Sources */,
@ -2368,7 +2362,6 @@
29EA87DA1E79A00E0043A5F8 /* ExpressibleByIntegerLiteral+Extension.swift in Sources */,
29D0E3681DD4CE3700863B3B /* AnyUtil.swift in Sources */,
29B8771C1CD70D5A00FC07DA /* CRC32.swift in Sources */,
BC4914AB28DDD966009E2DF6 /* VTSessionHolder.swift in Sources */,
2958912B1EEB8F1D00CE51E1 /* FLVSoundSize.swift in Sources */,
BC94E50B263FEBB60094C169 /* MP4TrackRunBox.swift in Sources */,
BC4914B328DDFE31009E2DF6 /* VTSessionOptionKey.swift in Sources */,
@ -2476,7 +2469,6 @@
BCA97BF6263C390E0027213C /* CustomXmlStringConvertible.swift in Sources */,
BCC1A6BB2643F41600661156 /* MP4File.Builder.swift in Sources */,
BC94E52E264146530094C169 /* MP4ReaderConvertible.swift in Sources */,
BC4914AC28DDD966009E2DF6 /* VTSessionHolder.swift in Sources */,
BCC1A72D264FAC1800661156 /* ElementaryStreamSpecificData.swift in Sources */,
BCB976E126107B5600C9A649 /* TSAdaptationField.swift in Sources */,
BC4914B028DDF445009E2DF6 /* VTDecompressionSession+Extension.swift in Sources */,
@ -2492,7 +2484,6 @@
29EB3E151ED0588C001CAE8B /* VideoEffect.swift in Sources */,
29EB3E061ED05865001CAE8B /* MP4Reader.swift in Sources */,
29EB3E041ED05860001CAE8B /* AVCConfigurationRecord.swift in Sources */,
29EB3DEF1ED05766001CAE8B /* H264Decoder.swift in Sources */,
29EB3DF71ED05797001CAE8B /* URL+Extension.swift in Sources */,
29DF20682312A436004057C3 /* RTMPSocketCompatible.swift in Sources */,
29EB3E0B1ED05871001CAE8B /* TSReader.swift in Sources */,
@ -2550,6 +2541,7 @@
29EB3E181ED05896001CAE8B /* NetService.swift in Sources */,
295891281EEB8EF300CE51E1 /* FLVAACPacket.swift in Sources */,
BCC1A7152647F28F00661156 /* SLConfigDescriptor.swift in Sources */,
BC4914B828DEC2FE009E2DF6 /* VTSessionMode.swift in Sources */,
BCA97B88263AC0F30027213C /* MP4BoxConvertible.swift in Sources */,
2958911C1EEB8E3F00CE51E1 /* FLVAudioCodec.swift in Sources */,
BC94E500263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift in Sources */,

View File

@ -1,219 +0,0 @@
import AVFoundation
import CoreFoundation
import CoreVideo
import VideoToolbox
#if os(iOS)
import UIKit
#endif
protocol VideoDecoderDelegate: AnyObject {
func sampleOutput(video sampleBuffer: CMSampleBuffer)
}
// MARK: -
final class H264Decoder {
static let defaultDecodeFlags: VTDecodeFrameFlags = [
._EnableAsynchronousDecompression,
._EnableTemporalProcessing
]
static let defaultMinimumGroupOfPictures: Int = 12
static let defaultAttributes: [NSString: AnyObject] = [
kCVPixelBufferPixelFormatTypeKey: NSNumber(value: kCVPixelFormatType_32BGRA),
kCVPixelBufferIOSurfacePropertiesKey: [:] as AnyObject,
kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue
]
var formatDescription: CMFormatDescription? {
didSet {
if let atoms: [String: AnyObject] = formatDescription?.`extension`(by: "SampleDescriptionExtensionAtoms"), let avcC: Data = atoms["avcC"] as? Data {
let config = AVCConfigurationRecord(data: avcC)
isBaseline = config.AVCProfileIndication == 66
}
invalidateSession = true
}
}
var isRunning: Atomic<Bool> = .init(false)
weak var delegate: VideoDecoderDelegate?
var lockQueue = DispatchQueue(label: "com.haishinkit.HaishinKit.H264Decoder.lock")
var needsSync: Atomic<Bool> = .init(true)
var isBaseline = true
private var buffers: [CMSampleBuffer] = []
private var attributes: [NSString: AnyObject] {
H264Decoder.defaultAttributes
}
private var minimumGroupOfPictures: Int = H264Decoder.defaultMinimumGroupOfPictures
private(set) var status: OSStatus = noErr {
didSet {
if status != noErr {
logger.warn("\(self.status)")
}
}
}
private var invalidateSession = true
private var callback: VTDecompressionOutputCallback = {(decompressionOutputRefCon: UnsafeMutableRawPointer?, _: UnsafeMutableRawPointer?, status: OSStatus, infoFlags: VTDecodeInfoFlags, imageBuffer: CVBuffer?, presentationTimeStamp: CMTime, duration: CMTime) in
let decoder: H264Decoder = Unmanaged<H264Decoder>.fromOpaque(decompressionOutputRefCon!).takeUnretainedValue()
decoder.didOutputForSession(status, infoFlags: infoFlags, imageBuffer: imageBuffer, presentationTimeStamp: presentationTimeStamp, duration: duration)
}
private var _session: VTDecompressionSession?
private var session: VTDecompressionSession! {
get {
if _session == nil {
guard let formatDescription: CMFormatDescription = formatDescription else {
return nil
}
var record = VTDecompressionOutputCallbackRecord(
decompressionOutputCallback: callback,
decompressionOutputRefCon: Unmanaged.passUnretained(self).toOpaque()
)
guard VTDecompressionSessionCreate(
allocator: kCFAllocatorDefault,
formatDescription: formatDescription,
decoderSpecification: nil,
imageBufferAttributes: attributes as CFDictionary?,
outputCallback: &record,
decompressionSessionOut: &_session ) == noErr else {
return nil
}
invalidateSession = false
}
return _session!
}
set {
if let session: VTDecompressionSession = _session {
VTDecompressionSessionInvalidate(session)
}
_session = newValue
}
}
func decodeSampleBuffer(_ sampleBuffer: CMSampleBuffer) -> OSStatus {
if invalidateSession {
session = nil
needsSync.mutate { $0 = true }
}
if !sampleBuffer.isNotSync {
needsSync.mutate { $0 = false }
}
guard let session: VTDecompressionSession = session, !needsSync.value else {
return kVTInvalidSessionErr
}
var flagsOut: VTDecodeInfoFlags = []
return VTDecompressionSessionDecodeFrame(
session,
sampleBuffer: sampleBuffer,
flags: H264Decoder.defaultDecodeFlags,
frameRefcon: nil,
infoFlagsOut: &flagsOut
)
}
func didOutputForSession(_ status: OSStatus, infoFlags: VTDecodeInfoFlags, imageBuffer: CVImageBuffer?, presentationTimeStamp: CMTime, duration: CMTime) {
guard let imageBuffer: CVImageBuffer = imageBuffer, status == noErr else {
return
}
var timingInfo = CMSampleTimingInfo(
duration: duration,
presentationTimeStamp: presentationTimeStamp,
decodeTimeStamp: CMTime.invalid
)
var videoFormatDescription: CMVideoFormatDescription?
self.status = CMVideoFormatDescriptionCreateForImageBuffer(
allocator: kCFAllocatorDefault,
imageBuffer: imageBuffer,
formatDescriptionOut: &videoFormatDescription
)
var sampleBuffer: CMSampleBuffer?
self.status = CMSampleBufferCreateForImageBuffer(
allocator: kCFAllocatorDefault,
imageBuffer: imageBuffer,
dataReady: true,
makeDataReadyCallback: nil,
refcon: nil,
formatDescription: videoFormatDescription!,
sampleTiming: &timingInfo,
sampleBufferOut: &sampleBuffer
)
guard let buffer: CMSampleBuffer = sampleBuffer else {
return
}
if isBaseline {
delegate?.sampleOutput(video: buffer)
} else {
buffers.append(buffer)
buffers.sort {
$0.presentationTimeStamp < $1.presentationTimeStamp
}
if minimumGroupOfPictures <= buffers.count {
delegate?.sampleOutput(video: buffers.removeFirst())
}
}
}
#if os(iOS)
@objc
private func applicationWillEnterForeground(_ notification: Notification) {
invalidateSession = true
}
@objc
private func didAudioSessionInterruption(_ notification: Notification) {
guard
let userInfo: [AnyHashable: Any] = notification.userInfo,
let value: NSNumber = userInfo[AVAudioSessionInterruptionTypeKey] as? NSNumber,
let type = AVAudioSession.InterruptionType(rawValue: value.uintValue) else {
return
}
switch type {
case .ended:
invalidateSession = true
default:
break
}
}
#endif
}
extension H264Decoder: Running {
// MARK: Running
func startRunning() {
lockQueue.async {
self.isRunning.mutate { $0 = true }
#if os(iOS)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.didAudioSessionInterruption),
name: AVAudioSession.interruptionNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.applicationWillEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
#endif
}
}
func stopRunning() {
lockQueue.async {
self.session = nil
self.needsSync.mutate { $0 = true }
self.invalidateSession = true
self.buffers.removeAll()
self.formatDescription = nil
#if os(iOS)
NotificationCenter.default.removeObserver(self)
#endif
self.isRunning.mutate { $0 = false }
}
}
}

View File

@ -7,6 +7,7 @@ protocol VTSessionConvertible {
func setOptions(_ options: Set<VTSessionOption>) -> OSStatus
func copySupportedPropertyDictionary() -> [AnyHashable: Any]
func inputBuffer(_ imageBuffer: CVImageBuffer, presentationTimeStamp: CMTime, duration: CMTime, outputHandler: @escaping VTCompressionOutputHandler)
func inputBuffer(_ sampleBuffer: CMSampleBuffer, outputHandler: @escaping VTDecompressionOutputHandler)
func invalidate()
}

View File

@ -1,42 +0,0 @@
import Foundation
import VideoToolbox
struct VTSessionHolder {
private(set) var isInvalidateSession = false
private(set) var session: VTSessionConvertible?
mutating func makeSession(_ videoCodec: VideoCodec) -> OSStatus {
session?.invalidate()
session = nil
var session: VTCompressionSession?
var status = VTCompressionSessionCreate(
allocator: kCFAllocatorDefault,
width: videoCodec.width,
height: videoCodec.height,
codecType: kCMVideoCodecType_H264,
encoderSpecification: nil,
imageBufferAttributes: nil,
compressedDataAllocator: nil,
outputCallback: nil,
refcon: nil,
compressionSessionOut: &session
)
guard status == noErr, let session else {
videoCodec.delegate?.videoCodec(videoCodec, errorOccurred: .failedToCreate(status: status))
return status
}
status = session.setOptions(videoCodec.options())
status = session.prepareToEncodeFrame()
guard status == noErr else {
videoCodec.delegate?.videoCodec(videoCodec, errorOccurred: .failedToPrepare(status: status))
return status
}
self.session = session
isInvalidateSession = false
return noErr
}
mutating func invalidateSession() {
isInvalidateSession = true
}
}

View File

@ -0,0 +1,59 @@
import Foundation
import VideoToolbox
enum VTSessionMode {
case compression
case decompression
func makeSession(_ videoCodec: VideoCodec) -> VTSessionConvertible? {
switch self {
case .compression:
var session: VTCompressionSession?
var status = VTCompressionSessionCreate(
allocator: kCFAllocatorDefault,
width: videoCodec.width,
height: videoCodec.height,
codecType: kCMVideoCodecType_H264,
encoderSpecification: nil,
imageBufferAttributes: videoCodec.attributes as CFDictionary?,
compressedDataAllocator: nil,
outputCallback: nil,
refcon: nil,
compressionSessionOut: &session
)
guard status == noErr, let session else {
videoCodec.delegate?.videoCodec(videoCodec, errorOccurred: .failedToCreate(status: status))
return nil
}
status = session.setOptions(videoCodec.options())
status = session.prepareToEncodeFrames()
guard status == noErr else {
videoCodec.delegate?.videoCodec(videoCodec, errorOccurred: .failedToPrepare(status: status))
return nil
}
return session
case .decompression:
guard let formatDescription = videoCodec.formatDescription else {
videoCodec.delegate?.videoCodec(videoCodec, errorOccurred: .failedToCreate(status: kVTParameterErr))
return nil
}
var attributes = videoCodec.attributes
attributes?.removeValue(forKey: kCVPixelBufferWidthKey)
attributes?.removeValue(forKey: kCVPixelBufferHeightKey)
var session: VTDecompressionSession?
let status = VTDecompressionSessionCreate(
allocator: kCFAllocatorDefault,
formatDescription: formatDescription,
decoderSpecification: nil,
imageBufferAttributes: attributes as CFDictionary?,
outputCallback: nil,
decompressionSessionOut: &session
)
guard status == noErr else {
videoCodec.delegate?.videoCodec(videoCodec, errorOccurred: .failedToCreate(status: status))
return nil
}
return session
}
}
}

View File

@ -1,13 +1,16 @@
import Foundation
public struct VTSessionOption: Hashable {
public struct VTSessionOption {
let key: VTSessionOptionKey
let value: AnyObject
}
extension VTSessionOption: Hashable {
// MARK: Hashable
public static func == (lhs: VTSessionOption, rhs: VTSessionOption) -> Bool {
return lhs.key.CFString == rhs.key.CFString
}
let key: VTSessionOptionKey
let value: AnyObject
public func hash(into hasher: inout Hasher) {
return hasher.combine(key.CFString)
}

View File

@ -32,7 +32,7 @@ struct VTSessionOptionKey {
#endif
static let multiPassStorage = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_MultiPassStorage)
static let forceKeyFrame = VTSessionOptionKey(CFString: kVTEncodeFrameOptionKey_ForceKeyFrame)
static let fpixelTransferProperties = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_PixelTransferProperties)
static let pixelTransferProperties = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_PixelTransferProperties)
static let averageBitRate = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_AverageBitRate)
static let dataRateLimits = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_DataRateLimits)
static let moreFramesAfterEnd = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_MoreFramesAfterEnd)
@ -41,7 +41,7 @@ struct VTSessionOptionKey {
static let realTime = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_RealTime)
static let maxH264SliceBytes = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_MaxH264SliceBytes)
static let maxFrameDelayCount = VTSessionOptionKey(CFString: kVTCompressionPropertyKey_MaxFrameDelayCount)
static let encoderUsage = VTSessionOptionKey(CFString: "EncoderUsage" as CFString)
static let encoderID = VTSessionOptionKey(CFString: kVTVideoEncoderSpecification_EncoderID)
let CFString: CFString
}

View File

@ -12,8 +12,10 @@ import UIKit
public protocol VideoCodecDelegate: AnyObject {
/// Tells the receiver to set a formatDescription.
func videoCodec(_ codec: VideoCodec, didSet formatDescription: CMFormatDescription?)
/// Tells the receiver to output a encoded or decoded sampleBuffer.
func videoCodec(_ codec: VideoCodec, didOutput sampleBuffer: CMSampleBuffer)
/// Tells the receiver to output an encoded sampleBuffer.
func videoCodec(_ codec: VideoCodec, didCompress sampleBuffer: CMSampleBuffer)
/// Tells the receiver to output a decodec sampleBuffer.
func videoCodec(_ codec: VideoCodec, didDecompress sampleBuffer: CMSampleBuffer)
/// Tells the receiver to occured an error.
func videoCodec(_ codec: VideoCodec, errorOccurred error: VideoCodec.Error)
}
@ -23,6 +25,16 @@ public protocol VideoCodecDelegate: AnyObject {
* The VideoCodec class provides methods for encode or decode for video.
*/
public class VideoCodec {
static let defaultMinimumGroupOfPictures: Int = 12
#if os(OSX)
#if arch(arm64)
static let encoderName = NSString(string: "com.apple.videotoolbox.videoencoder.ave.avc")
#else
static let encoderName = NSString(string: "com.apple.videotoolbox.videoencoder.h264.gva")
#endif
#endif
/**
* The VideoCodec error domain codes.
*/
@ -111,29 +123,29 @@ public class VideoCodec {
public private(set) var isRunning: Atomic<Bool> = .init(false)
var muted = false
var scalingMode: ScalingMode = VideoCodec.defaultScalingMode {
var scalingMode = VideoCodec.defaultScalingMode {
didSet {
guard scalingMode != oldValue else {
return
}
sessionHolder.invalidateSession()
invalidateSession = true
}
}
var width: Int32 = VideoCodec.defaultWidth {
var width = VideoCodec.defaultWidth {
didSet {
guard width != oldValue else {
return
}
sessionHolder.invalidateSession()
invalidateSession = true
}
}
var height: Int32 = VideoCodec.defaultHeight {
var height = VideoCodec.defaultHeight {
didSet {
guard height != oldValue else {
return
}
sessionHolder.invalidateSession()
invalidateSession = true
}
}
#if os(macOS)
@ -142,35 +154,35 @@ public class VideoCodec {
guard enabledHardwareEncoder != oldValue else {
return
}
sessionHolder.invalidateSession()
invalidateSession = true
}
}
#endif
var bitrate: UInt32 = VideoCodec.defaultBitrate {
var bitrate = VideoCodec.defaultBitrate {
didSet {
guard bitrate != oldValue else {
return
}
let option = VTSessionOption(key: .averageBitRate, value: NSNumber(value: bitrate))
if let status = sessionHolder.session?.setOption(option), status != noErr {
if let status = session?.setOption(option), status != noErr {
delegate?.videoCodec(self, errorOccurred: .failedToSetOption(status: status, option: option))
}
}
}
var profileLevel: String = kVTProfileLevel_H264_Baseline_3_1 as String {
var profileLevel = kVTProfileLevel_H264_Baseline_3_1 as String {
didSet {
guard profileLevel != oldValue else {
return
}
sessionHolder.invalidateSession()
invalidateSession = true
}
}
var maxKeyFrameIntervalDuration: Double = 2.0 {
var maxKeyFrameIntervalDuration = 2.0 {
didSet {
guard maxKeyFrameIntervalDuration != oldValue else {
return
}
sessionHolder.invalidateSession()
invalidateSession = true
}
}
// swiftlint:disable discouraged_optional_boolean
@ -179,19 +191,18 @@ public class VideoCodec {
guard allowFrameReordering != oldValue else {
return
}
sessionHolder.invalidateSession()
invalidateSession = true
}
}
var locked: UInt32 = 0
var lockQueue = DispatchQueue(label: "com.haishinkit.HaishinKit.VideoCodec.lock")
var expectedFPS: Float64 = AVMixer.defaultFPS {
var expectedFrameRate = AVMixer.defaultFPS {
didSet {
guard expectedFPS != oldValue else {
guard expectedFrameRate != oldValue else {
return
}
let option = VTSessionOption(key: .expectedFrameRate, value: NSNumber(value: expectedFPS))
if let status = sessionHolder.session?.setOption(option), status != noErr {
print(status)
let option = VTSessionOption(key: .expectedFrameRate, value: NSNumber(value: expectedFrameRate))
if let status = session?.setOption(option), status != noErr {
delegate?.videoCodec(self, errorOccurred: .failedToSetOption(status: status, option: option))
}
}
@ -201,11 +212,16 @@ public class VideoCodec {
guard !CMFormatDescriptionEqual(formatDescription, otherFormatDescription: oldValue) else {
return
}
if let atoms: [String: AnyObject] = formatDescription?.`extension`(by: "SampleDescriptionExtensionAtoms"), let avcC: Data = atoms["avcC"] as? Data {
let config = AVCConfigurationRecord(data: avcC)
isBaseline = config.AVCProfileIndication == 66
}
delegate?.videoCodec(self, didSet: formatDescription)
}
}
weak var delegate: VideoCodecDelegate?
private var attributes: [NSString: AnyObject]? {
var needsSync: Atomic<Bool> = .init(true)
var isBaseline = true
var attributes: [NSString: AnyObject]? {
guard VideoCodec.defaultAttributes != nil else {
return nil
}
@ -217,8 +233,17 @@ public class VideoCodec {
attributes[kCVPixelBufferHeightKey] = NSNumber(value: height)
return attributes
}
weak var delegate: VideoCodecDelegate?
private var lastImageBuffer: CVImageBuffer?
private var sessionHolder = VTSessionHolder()
private var session: VTSessionConvertible? {
didSet {
oldValue?.invalidate()
}
}
private var invalidateSession = true
private var buffers: [CMSampleBuffer] = []
private var minimumGroupOfPictures: Int = VideoCodec.defaultMinimumGroupOfPictures
init() {
settings.observer = self
@ -228,10 +253,10 @@ public class VideoCodec {
guard isRunning.value && locked == 0 else {
return
}
if sessionHolder.isInvalidateSession {
_ = sessionHolder.makeSession(self)
if invalidateSession {
session = VTSessionMode.compression.makeSession(self)
}
sessionHolder.session?.inputBuffer(
session?.inputBuffer(
muted ? lastImageBuffer ?? imageBuffer : imageBuffer,
presentationTimeStamp: presentationTimeStamp,
duration: duration
@ -241,32 +266,91 @@ public class VideoCodec {
return
}
self.formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer)
self.delegate?.videoCodec(self, didOutput: sampleBuffer)
self.delegate?.videoCodec(self, didCompress: sampleBuffer)
if !self.muted || self.lastImageBuffer == nil {
self.lastImageBuffer = imageBuffer
}
}
}
func inputBuffer(_ sampleBuffer: CMSampleBuffer) {
if invalidateSession {
session = VTSessionMode.decompression.makeSession(self)
needsSync.mutate { $0 = true }
}
if !sampleBuffer.isNotSync {
needsSync.mutate { $0 = false }
}
session?.inputBuffer(sampleBuffer) { [unowned self] status, _, imageBuffer, presentationTimeStamp, duration in
guard let imageBuffer = imageBuffer, status == noErr else {
self.delegate?.videoCodec(self, errorOccurred: .failedToFlame(status: status))
return
}
var timingInfo = CMSampleTimingInfo(
duration: duration,
presentationTimeStamp: presentationTimeStamp,
decodeTimeStamp: .invalid
)
var videoFormatDescription: CMVideoFormatDescription?
var status = CMVideoFormatDescriptionCreateForImageBuffer(
allocator: kCFAllocatorDefault,
imageBuffer: imageBuffer,
formatDescriptionOut: &videoFormatDescription
)
guard status == noErr else {
self.delegate?.videoCodec(self, errorOccurred: .failedToFlame(status: status))
return
}
var sampleBuffer: CMSampleBuffer?
status = CMSampleBufferCreateForImageBuffer(
allocator: kCFAllocatorDefault,
imageBuffer: imageBuffer,
dataReady: true,
makeDataReadyCallback: nil,
refcon: nil,
formatDescription: videoFormatDescription!,
sampleTiming: &timingInfo,
sampleBufferOut: &sampleBuffer
)
guard let buffer = sampleBuffer, status == noErr else {
self.delegate?.videoCodec(self, errorOccurred: .failedToFlame(status: status))
return
}
if self.isBaseline {
self.delegate?.videoCodec(self, didDecompress: buffer)
} else {
self.buffers.append(buffer)
self.buffers.sort {
$0.presentationTimeStamp < $1.presentationTimeStamp
}
if self.minimumGroupOfPictures <= buffers.count {
self.delegate?.videoCodec(self, didDecompress: buffer)
}
}
}
}
func options() -> Set<VTSessionOption> {
let isBaseline = profileLevel.contains("Baseline")
var options = Set<VTSessionOption>([
.init(key: .realTime, value: kCFBooleanTrue),
.init(key: .profileLevel, value: profileLevel as NSObject),
.init(key: .averageBitRate, value: NSNumber(value: bitrate)),
.init(key: .expectedFrameRate, value: NSNumber(value: expectedFPS)),
.init(key: .expectedFrameRate, value: NSNumber(value: expectedFrameRate)),
.init(key: .maxKeyFrameIntervalDuration, value: NSNumber(value: maxKeyFrameIntervalDuration)),
.init(key: .allowFrameReordering, value: (allowFrameReordering ?? !isBaseline) as NSObject)
])
#if os(OSX)
if enabledHardwareEncoder {
#if arch(arm64)
options.insert(.init(key: .init(CFString: kVTVideoEncoderSpecification_EncoderID), value: "com.apple.videotoolbox.videoencoder.ave.avc" as NSObject))
#else
options.insert(.init(key: .init(CFString: kVTVideoEncoderSpecification_EncoderID), value: "com.apple.videotoolbox.videoencoder.h264.gva" as NSObject))
#endif
options.insert(.init(key: .init(CFString: "EnableHardwareAcceleratedVideoEncoder" as CFString), value: kCFBooleanTrue))
options.insert(.init(key: .init(CFString: "RequireHardwareAcceleratedVideoEncoder" as CFString), value: kCFBooleanTrue))
options.insert(.init(key: .encoderID, value: VideoCodec.encoderName))
options.insert(.init(key: .enableHardwareAcceleratedVideoEncoder, value: kCFBooleanTrue))
options.insert(.init(key: .requireHardwareAcceleratedVideoEncoder, value: kCFBooleanTrue))
}
#endif
if !isBaseline {
@ -278,7 +362,7 @@ public class VideoCodec {
#if os(iOS)
@objc
private func applicationWillEnterForeground(_ notification: Notification) {
sessionHolder.invalidateSession()
invalidateSession = true
}
@objc
@ -291,7 +375,7 @@ public class VideoCodec {
}
switch type {
case .ended:
sessionHolder.invalidateSession()
invalidateSession = true
default:
break
}
@ -324,7 +408,10 @@ extension VideoCodec: Running {
public func stopRunning() {
lockQueue.async {
self.sessionHolder.invalidateSession()
self.session = nil
self.invalidateSession = true
self.needsSync.mutate { $0 = true }
self.buffers.removeAll()
self.lastImageBuffer = nil
self.formatDescription = nil
#if os(iOS)

View File

@ -1,7 +1,14 @@
import Foundation
import VideoToolbox
extension VTCompressionSession {
func prepareToEncodeFrames() -> OSStatus {
VTCompressionSessionPrepareToEncodeFrames(self)
}
}
extension VTCompressionSession: VTSessionConvertible {
// MARK: VTSessionConvertible
func inputBuffer(_ imageBuffer: CVImageBuffer, presentationTimeStamp: CMTime, duration: CMTime, outputHandler: @escaping VTCompressionOutputHandler) {
var flags: VTEncodeInfoFlags = []
VTCompressionSessionEncodeFrame(
@ -15,13 +22,10 @@ extension VTCompressionSession: VTSessionConvertible {
)
}
func inputBuffer(_ sampleBuffer: CMSampleBuffer, outputHandler: @escaping VTDecompressionOutputHandler) {
}
func invalidate() {
VTCompressionSessionInvalidate(self)
}
}
extension VTCompressionSession {
func prepareToEncodeFrame() -> OSStatus {
VTCompressionSessionPrepareToEncodeFrames(self)
}
}

View File

@ -2,9 +2,25 @@ import Foundation
import VideoToolbox
extension VTDecompressionSession: VTSessionConvertible {
static let defaultDecodeFlags: VTDecodeFrameFlags = [
._EnableAsynchronousDecompression,
._EnableTemporalProcessing
]
func inputBuffer(_ imageBuffer: CVImageBuffer, presentationTimeStamp: CMTime, duration: CMTime, outputHandler: @escaping VTCompressionOutputHandler) {
}
func inputBuffer(_ sampleBuffer: CMSampleBuffer, outputHandler: @escaping VTDecompressionOutputHandler) {
var flagsOut: VTDecodeInfoFlags = []
VTDecompressionSessionDecodeFrame(
self,
sampleBuffer: sampleBuffer,
flags: Self.defaultDecodeFlags,
infoFlagsOut: &flagsOut,
outputHandler: outputHandler
)
}
func invalidate() {
VTDecompressionSessionInvalidate(self)
}

View File

@ -256,6 +256,7 @@ extension AVMixer {
public func startDecoding(_ audioEngine: AVAudioEngine?) {
mediaLink.startRunning()
audioIO.startDecoding(audioEngine)
videoIO.codec.delegate = videoIO
videoIO.startDecoding()
}
@ -270,7 +271,7 @@ extension AVMixer {
extension AVMixer: MediaLinkDelegate {
// MARK: MediaLinkDelegate
func mediaLink(_ mediaLink: MediaLink, dequeue sampleBuffer: CMSampleBuffer) {
_ = videoIO.decoder.decodeSampleBuffer(sampleBuffer)
videoIO.codec.inputBuffer(sampleBuffer)
}
func mediaLink(_ mediaLink: MediaLink, didBufferingChanged: Bool) {

View File

@ -29,7 +29,7 @@ final class AVVideoIOUnit: NSObject, AVIOUnit {
var formatDescription: CMVideoFormatDescription? {
didSet {
decoder.formatDescription = formatDescription
codec.formatDescription = formatDescription
}
}
lazy var codec: VideoCodec = {
@ -37,11 +37,6 @@ final class AVVideoIOUnit: NSObject, AVIOUnit {
codec.lockQueue = lockQueue
return codec
}()
lazy var decoder: H264Decoder = {
var decoder = H264Decoder()
decoder.delegate = self
return decoder
}()
weak var mixer: AVMixer?
private(set) var effects: Set<VideoEffect> = []
@ -87,7 +82,7 @@ final class AVVideoIOUnit: NSObject, AVIOUnit {
}
fps = data.fps
codec.expectedFPS = data.fps
codec.expectedFrameRate = data.fps
logger.info("\(data)")
do {
@ -434,11 +429,11 @@ extension AVVideoIOUnit {
extension AVVideoIOUnit {
func startDecoding() {
decoder.startRunning()
codec.startRunning()
}
func stopDecoding() {
decoder.stopRunning()
codec.stopRunning()
drawable?.enqueue(nil)
}
}
@ -458,9 +453,18 @@ extension AVVideoIOUnit: AVCaptureVideoDataOutputSampleBufferDelegate {
}
}
extension AVVideoIOUnit: VideoDecoderDelegate {
// MARK: VideoDecoderDelegate
func sampleOutput(video sampleBuffer: CMSampleBuffer) {
extension AVVideoIOUnit: VideoCodecDelegate {
// MARK: VideoCodecDelegate
func videoCodec(_ codec: VideoCodec, didSet formatDescription: CMFormatDescription?) {
}
func videoCodec(_ codec: VideoCodec, didCompress sampleBuffer: CMSampleBuffer) {
}
func videoCodec(_ codec: VideoCodec, didDecompress sampleBuffer: CMSampleBuffer) {
drawable?.enqueue(sampleBuffer)
}
func videoCodec(_ codec: VideoCodec, errorOccurred error: VideoCodec.Error) {
}
}

View File

@ -644,7 +644,7 @@ final class RTMPVideoMessage: RTMPMessage {
}
private func enqueueSampleBuffer(_ stream: RTMPStream, type: RTMPChunkType) {
let isBaseline = stream.mixer.videoIO.decoder.isBaseline
let isBaseline = stream.mixer.videoIO.codec.isBaseline
// compositionTime -> SI24
var compositionTime = isBaseline ? 0 : Int32(data: [0] + payload[2..<5]).bigEndian
@ -678,7 +678,7 @@ final class RTMPVideoMessage: RTMPMessage {
dataLength: length,
flags: 0,
blockBufferOut: &blockBuffer) == noErr else {
stream.mixer.videoIO.decoder.needsSync.mutate { $0 = true }
stream.mixer.videoIO.codec.needsSync.mutate { $0 = true }
return
}
guard CMBlockBufferReplaceDataBytes(

View File

@ -59,7 +59,7 @@ extension RTMPMuxer: VideoCodecDelegate {
delegate?.muxer(self, didOutputVideo: buffer, withTimestamp: 0)
}
func videoCodec(_ codec: VideoCodec, didOutput sampleBuffer: CMSampleBuffer) {
func videoCodec(_ codec: VideoCodec, didCompress sampleBuffer: CMSampleBuffer) {
let keyframe: Bool = !sampleBuffer.isNotSync
var compositionTime: Int32 = 0
let presentationTimeStamp: CMTime = sampleBuffer.presentationTimeStamp
@ -80,6 +80,9 @@ extension RTMPMuxer: VideoCodecDelegate {
videoTimeStamp = decodeTimeStamp
}
func videoCodec(_ codec: VideoCodec, didDecompress sampleBuffer: CMSampleBuffer) {
}
func videoCodec(_ codec: VideoCodec, errorOccurred error: VideoCodec.Error) {
delegate?.muxer(self, videoCodecErrorOccurred: error)
}

View File

@ -254,7 +254,7 @@ extension TSWriter: VideoCodecDelegate {
videoConfig = AVCConfigurationRecord(data: avcC)
}
public func videoCodec(_ codec: VideoCodec, didOutput sampleBuffer: CMSampleBuffer) {
public func videoCodec(_ codec: VideoCodec, didCompress sampleBuffer: CMSampleBuffer) {
guard let dataBuffer = sampleBuffer.dataBuffer else {
return
}
@ -277,6 +277,9 @@ extension TSWriter: VideoCodecDelegate {
)
}
public func videoCodec(_ codec: VideoCodec, didDecompress sampleBuffer: CMSampleBuffer) {
}
public func videoCodec(_ codec: VideoCodec, errorOccurred error: VideoCodec.Error) {
}
}