Merge pull request #1195 from shogo4405/feature/rtmp-h265

Support RTMP with HEVC Enhancing RTMP, FLV.
This commit is contained in:
shogo4405 2023-05-06 02:19:10 +09:00 committed by GitHub
commit d72ae256da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 467 additions and 114 deletions

View File

@ -126,7 +126,7 @@
29B876791CD70ACE00FC07DA /* HTTPStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876721CD70ACE00FC07DA /* HTTPStream.swift */; };
29B8767A1CD70ACE00FC07DA /* M3U.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876731CD70ACE00FC07DA /* M3U.swift */; };
29B876831CD70AE800FC07DA /* AudioSpecificConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767D1CD70AE800FC07DA /* AudioSpecificConfig.swift */; };
29B876841CD70AE800FC07DA /* AVCConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767E1CD70AE800FC07DA /* AVCConfigurationRecord.swift */; };
29B876841CD70AE800FC07DA /* AVCDecoderConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767E1CD70AE800FC07DA /* AVCDecoderConfigurationRecord.swift */; };
29B876861CD70AE800FC07DA /* PacketizedElementaryStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876801CD70AE800FC07DA /* PacketizedElementaryStream.swift */; };
29B876871CD70AE800FC07DA /* TSProgram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876811CD70AE800FC07DA /* TSProgram.swift */; };
29B876881CD70AE800FC07DA /* TSPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876821CD70AE800FC07DA /* TSPacket.swift */; };
@ -161,7 +161,7 @@
29B876F91CD70D5900FC07DA /* HTTPStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876721CD70ACE00FC07DA /* HTTPStream.swift */; };
29B876FA1CD70D5900FC07DA /* M3U.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876731CD70ACE00FC07DA /* M3U.swift */; };
29B876FD1CD70D5A00FC07DA /* AudioSpecificConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767D1CD70AE800FC07DA /* AudioSpecificConfig.swift */; };
29B876FE1CD70D5A00FC07DA /* AVCConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767E1CD70AE800FC07DA /* AVCConfigurationRecord.swift */; };
29B876FE1CD70D5A00FC07DA /* AVCDecoderConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767E1CD70AE800FC07DA /* AVCDecoderConfigurationRecord.swift */; };
29B877001CD70D5A00FC07DA /* PacketizedElementaryStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876801CD70AE800FC07DA /* PacketizedElementaryStream.swift */; };
29B877011CD70D5A00FC07DA /* TSProgram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876811CD70AE800FC07DA /* TSProgram.swift */; };
29B877021CD70D5A00FC07DA /* TSPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876821CD70AE800FC07DA /* TSPacket.swift */; };
@ -244,7 +244,7 @@
29EB3E011ED05856001CAE8B /* HTTPStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876721CD70ACE00FC07DA /* HTTPStream.swift */; };
29EB3E021ED05858001CAE8B /* M3U.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876731CD70ACE00FC07DA /* M3U.swift */; };
29EB3E031ED0585D001CAE8B /* AudioSpecificConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767D1CD70AE800FC07DA /* AudioSpecificConfig.swift */; };
29EB3E041ED05860001CAE8B /* AVCConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767E1CD70AE800FC07DA /* AVCConfigurationRecord.swift */; };
29EB3E041ED05860001CAE8B /* AVCDecoderConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767E1CD70AE800FC07DA /* AVCDecoderConfigurationRecord.swift */; };
29EB3E081ED05869001CAE8B /* PacketizedElementaryStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876801CD70AE800FC07DA /* PacketizedElementaryStream.swift */; };
29EB3E091ED0586B001CAE8B /* TSProgram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876811CD70AE800FC07DA /* TSProgram.swift */; };
29EB3E0A1ED0586F001CAE8B /* TSPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876821CD70AE800FC07DA /* TSPacket.swift */; };
@ -312,9 +312,23 @@
BC1DC4A429F4F74F00E928ED /* AVCaptureSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC4A329F4F74F00E928ED /* AVCaptureSession+Extension.swift */; };
BC1DC4A529F4F74F00E928ED /* AVCaptureSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC4A329F4F74F00E928ED /* AVCaptureSession+Extension.swift */; };
BC1DC4A629F4F74F00E928ED /* AVCaptureSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC4A329F4F74F00E928ED /* AVCaptureSession+Extension.swift */; };
BC1DC4FB2A02868900E928ED /* FLVVideoFourCC.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC4FA2A02868900E928ED /* FLVVideoFourCC.swift */; };
BC1DC4FC2A02868900E928ED /* FLVVideoFourCC.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC4FA2A02868900E928ED /* FLVVideoFourCC.swift */; };
BC1DC4FD2A02868900E928ED /* FLVVideoFourCC.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC4FA2A02868900E928ED /* FLVVideoFourCC.swift */; };
BC1DC5042A02894D00E928ED /* FLVVideoFourCCTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5032A02894D00E928ED /* FLVVideoFourCCTests.swift */; };
BC1DC5062A02963600E928ED /* FLVTagType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5052A02963600E928ED /* FLVTagType.swift */; };
BC1DC5072A02963600E928ED /* FLVTagType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5052A02963600E928ED /* FLVTagType.swift */; };
BC1DC5082A02963600E928ED /* FLVTagType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5052A02963600E928ED /* FLVTagType.swift */; };
BC1DC50A2A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5092A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift */; };
BC1DC50B2A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5092A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift */; };
BC1DC50C2A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5092A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift */; };
BC1DC50E2A039E1900E928ED /* FLVVideoPacketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC50D2A039E1900E928ED /* FLVVideoPacketType.swift */; };
BC1DC50F2A039E1900E928ED /* FLVVideoPacketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC50D2A039E1900E928ED /* FLVVideoPacketType.swift */; };
BC1DC5102A039E1900E928ED /* FLVVideoPacketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC50D2A039E1900E928ED /* FLVVideoPacketType.swift */; };
BC1DC5122A04E46E00E928ED /* HEVCDecoderConfigurationRecordTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5112A04E46E00E928ED /* HEVCDecoderConfigurationRecordTests.swift */; };
BC1DC5142A05428800E928ED /* HEVCNALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5132A05428800E928ED /* HEVCNALUnit.swift */; };
BC1DC5152A05428800E928ED /* HEVCNALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5132A05428800E928ED /* HEVCNALUnit.swift */; };
BC1DC5162A05428800E928ED /* HEVCNALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1DC5132A05428800E928ED /* HEVCNALUnit.swift */; };
BC20DF38250377A3007BC608 /* IOUIScreenCaptureUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 299B131C1D35272D00A1E8F5 /* IOUIScreenCaptureUnit.swift */; };
BC3004CE296B0A1700119932 /* Shape.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC3004CD296B0A1700119932 /* Shape.swift */; };
BC3004CF296B0A1700119932 /* Shape.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC3004CD296B0A1700119932 /* Shape.swift */; };
@ -433,9 +447,9 @@
BCC1A72D264FAC1800661156 /* ESSpecificData.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCC1A72A264FAC1800661156 /* ESSpecificData.swift */; };
BCC9E9092636FF7400948774 /* DataBufferTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCC9E9082636FF7400948774 /* DataBufferTests.swift */; };
BCCBCE9529A7C9C90095B51C /* AVCFormatStreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9429A7C9C90095B51C /* AVCFormatStreamTests.swift */; };
BCCBCE9729A90D880095B51C /* NALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9629A90D880095B51C /* NALUnit.swift */; };
BCCBCE9829A90D880095B51C /* NALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9629A90D880095B51C /* NALUnit.swift */; };
BCCBCE9929A90D880095B51C /* NALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9629A90D880095B51C /* NALUnit.swift */; };
BCCBCE9729A90D880095B51C /* AVCNALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9629A90D880095B51C /* AVCNALUnit.swift */; };
BCCBCE9829A90D880095B51C /* AVCNALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9629A90D880095B51C /* AVCNALUnit.swift */; };
BCCBCE9929A90D880095B51C /* AVCNALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9629A90D880095B51C /* AVCNALUnit.swift */; };
BCCBCE9B29A9D96A0095B51C /* NALUnitReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9A29A9D96A0095B51C /* NALUnitReaderTests.swift */; };
BCCBCEA029ADF55A0095B51C /* AudioCodecBufferTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCCBCE9F29ADF55A0095B51C /* AudioCodecBufferTests.swift */; };
BCD1DC3A260627C300A1C593 /* Logboard.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC34DFD125EBB12C005F975A /* Logboard.xcframework */; };
@ -660,7 +674,7 @@
29B876721CD70ACE00FC07DA /* HTTPStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPStream.swift; sourceTree = "<group>"; };
29B876731CD70ACE00FC07DA /* M3U.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = M3U.swift; sourceTree = "<group>"; };
29B8767D1CD70AE800FC07DA /* AudioSpecificConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioSpecificConfig.swift; sourceTree = "<group>"; };
29B8767E1CD70AE800FC07DA /* AVCConfigurationRecord.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AVCConfigurationRecord.swift; sourceTree = "<group>"; };
29B8767E1CD70AE800FC07DA /* AVCDecoderConfigurationRecord.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AVCDecoderConfigurationRecord.swift; sourceTree = "<group>"; };
29B876801CD70AE800FC07DA /* PacketizedElementaryStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketizedElementaryStream.swift; sourceTree = "<group>"; };
29B876811CD70AE800FC07DA /* TSProgram.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TSProgram.swift; sourceTree = "<group>"; };
29B876821CD70AE800FC07DA /* TSPacket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TSPacket.swift; sourceTree = "<group>"; };
@ -728,7 +742,13 @@
BC110252292DD6E900D48035 /* vImage_Buffer+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "vImage_Buffer+Extension.swift"; sourceTree = "<group>"; };
BC110256292E661E00D48035 /* MultiCamCaptureSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiCamCaptureSettings.swift; sourceTree = "<group>"; };
BC1DC4A329F4F74F00E928ED /* AVCaptureSession+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureSession+Extension.swift"; sourceTree = "<group>"; };
BC1DC4FA2A02868900E928ED /* FLVVideoFourCC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FLVVideoFourCC.swift; sourceTree = "<group>"; };
BC1DC5032A02894D00E928ED /* FLVVideoFourCCTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FLVVideoFourCCTests.swift; sourceTree = "<group>"; };
BC1DC5052A02963600E928ED /* FLVTagType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FLVTagType.swift; sourceTree = "<group>"; };
BC1DC5092A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HEVCDecoderConfigurationRecord.swift; sourceTree = "<group>"; };
BC1DC50D2A039E1900E928ED /* FLVVideoPacketType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FLVVideoPacketType.swift; sourceTree = "<group>"; };
BC1DC5112A04E46E00E928ED /* HEVCDecoderConfigurationRecordTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HEVCDecoderConfigurationRecordTests.swift; sourceTree = "<group>"; };
BC1DC5132A05428800E928ED /* HEVCNALUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HEVCNALUnit.swift; sourceTree = "<group>"; };
BC3004CD296B0A1700119932 /* Shape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shape.swift; sourceTree = "<group>"; };
BC3004D3296BFFF600119932 /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = "<group>"; };
BC3004F0296C0C7400119932 /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = "<group>"; };
@ -777,7 +797,7 @@
BCC1A72A264FAC1800661156 /* ESSpecificData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ESSpecificData.swift; sourceTree = "<group>"; };
BCC9E9082636FF7400948774 /* DataBufferTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBufferTests.swift; sourceTree = "<group>"; };
BCCBCE9429A7C9C90095B51C /* AVCFormatStreamTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVCFormatStreamTests.swift; sourceTree = "<group>"; };
BCCBCE9629A90D880095B51C /* NALUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NALUnit.swift; sourceTree = "<group>"; };
BCCBCE9629A90D880095B51C /* AVCNALUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVCNALUnit.swift; sourceTree = "<group>"; };
BCCBCE9A29A9D96A0095B51C /* NALUnitReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NALUnitReaderTests.swift; sourceTree = "<group>"; };
BCCBCE9F29ADF55A0095B51C /* AudioCodecBufferTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioCodecBufferTests.swift; sourceTree = "<group>"; };
BCD63AB226FDF1250084842D /* Example iOS+SwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Example iOS+SwiftUI.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -937,6 +957,7 @@
2917CB652104CA2800F6823A /* AudioSpecificConfigTests.swift */,
BCCBCE9429A7C9C90095B51C /* AVCFormatStreamTests.swift */,
BC7C56C629A7701F00C41A9B /* ESSpecificDataTests.swift */,
BC1DC5112A04E46E00E928ED /* HEVCDecoderConfigurationRecordTests.swift */,
BCCBCE9A29A9D96A0095B51C /* NALUnitReaderTests.swift */,
290EA8951DFB619600053022 /* PacketizedElementaryStreamTests.swift */,
290EA8971DFB619600053022 /* TSPacketTests.swift */,
@ -1063,6 +1084,8 @@
2958912D1EEB8F4100CE51E1 /* FLVSoundType.swift */,
BC1DC5052A02963600E928ED /* FLVTagType.swift */,
2958910D1EEB8D3C00CE51E1 /* FLVVideoCodec.swift */,
BC1DC4FA2A02868900E928ED /* FLVVideoFourCC.swift */,
BC1DC50D2A039E1900E928ED /* FLVVideoPacketType.swift */,
);
path = FLV;
sourceTree = "<group>";
@ -1149,6 +1172,7 @@
295018191FFA196800358E10 /* Codec */,
294637A71EC89BC9008EEC71 /* Config.swift */,
291C2AD21CE9FF48006F042B /* Core */,
BC1DC5022A02893600E928ED /* FLV */,
291C2AD31CE9FF68006F042B /* HTTP */,
29798E5D1CE60E5300F5CBD0 /* Info.plist */,
BC0BF4F329866FB700D72CB4 /* Media */,
@ -1335,6 +1359,14 @@
path = Media;
sourceTree = "<group>";
};
BC1DC5022A02893600E928ED /* FLV */ = {
isa = PBXGroup;
children = (
BC1DC5032A02894D00E928ED /* FLVVideoFourCCTests.swift */,
);
path = FLV;
sourceTree = "<group>";
};
BC3004FA296C3FC400119932 /* Extension */ = {
isa = PBXGroup;
children = (
@ -1350,11 +1382,13 @@
children = (
BC7C56CC29A786AE00C41A9B /* ADTS.swift */,
29B8767D1CD70AE800FC07DA /* AudioSpecificConfig.swift */,
29B8767E1CD70AE800FC07DA /* AVCConfigurationRecord.swift */,
29B8767E1CD70AE800FC07DA /* AVCDecoderConfigurationRecord.swift */,
BCB9773E2621812800C9A649 /* AVCFormatStream.swift */,
BCCBCE9629A90D880095B51C /* AVCNALUnit.swift */,
29B876B91CD70B3900FC07DA /* CRC32.swift */,
BCC1A72A264FAC1800661156 /* ESSpecificData.swift */,
BCCBCE9629A90D880095B51C /* NALUnit.swift */,
BC1DC5092A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift */,
BC1DC5132A05428800E928ED /* HEVCNALUnit.swift */,
29B876801CD70AE800FC07DA /* PacketizedElementaryStream.swift */,
BCB976DE26107B5600C9A649 /* TSField.swift */,
29B876821CD70AE800FC07DA /* TSPacket.swift */,
@ -1881,15 +1915,17 @@
29AF3FCF1D7C744C00E41212 /* NetStream.swift in Sources */,
294B2D3223785E3800CE7BDC /* RingBuffer.swift in Sources */,
2958910E1EEB8D3C00CE51E1 /* FLVVideoCodec.swift in Sources */,
BC1DC5142A05428800E928ED /* HEVCNALUnit.swift in Sources */,
BC6FC9222961B3D800A746EE /* vImage_CGImageFormat+Extension.swift in Sources */,
299B13271D3B751400A1E8F5 /* HKView.swift in Sources */,
BC44A1A923D31E92002D4297 /* AudioCodecRingBuffer.swift in Sources */,
BC20DF38250377A3007BC608 /* IOUIScreenCaptureUnit.swift in Sources */,
29B876AF1CD70B2800FC07DA /* RTMPChunk.swift in Sources */,
29B876841CD70AE800FC07DA /* AVCConfigurationRecord.swift in Sources */,
29B876841CD70AE800FC07DA /* AVCDecoderConfigurationRecord.swift in Sources */,
296242621D8DB86500C451A3 /* TSWriter.swift in Sources */,
BC9CFA9323BDE8B700917EEF /* NetStreamDrawable.swift in Sources */,
29B8769B1CD70B1100FC07DA /* MIME.swift in Sources */,
BC1DC50E2A039E1900E928ED /* FLVVideoPacketType.swift in Sources */,
29B8769C1CD70B1100FC07DA /* NetClient.swift in Sources */,
29B876871CD70AE800FC07DA /* TSProgram.swift in Sources */,
BC4C9EAF23F2E736004A14F2 /* AudioStreamBasicDescription+Extension.swift in Sources */,
@ -1904,6 +1940,7 @@
BC3004CE296B0A1700119932 /* Shape.swift in Sources */,
BC34FA0B286CB90A00EFAF27 /* PiPHKView.swift in Sources */,
293B42E92340B4840086F973 /* RTMPObjectEncoding.swift in Sources */,
BC1DC4FB2A02868900E928ED /* FLVVideoFourCC.swift in Sources */,
2976A47E1D48C5C700B53EF2 /* IORecorder.swift in Sources */,
BC110257292E661E00D48035 /* MultiCamCaptureSettings.swift in Sources */,
29B876B21CD70B2800FC07DA /* RTMPMuxer.swift in Sources */,
@ -1914,7 +1951,7 @@
2958912A1EEB8F1D00CE51E1 /* FLVSoundSize.swift in Sources */,
29EA87DC1E79A0460043A5F8 /* Data+Extension.swift in Sources */,
29DF20622312A3DD004057C3 /* RTMPNWSocket.swift in Sources */,
BCCBCE9729A90D880095B51C /* NALUnit.swift in Sources */,
BCCBCE9729A90D880095B51C /* AVCNALUnit.swift in Sources */,
29B876BD1CD70B3900FC07DA /* CRC32.swift in Sources */,
BCA2252C293CC5B600DD7CB2 /* IOScreenCaptureUnit.swift in Sources */,
BC4914A628DDD367009E2DF6 /* VTSessionOption.swift in Sources */,
@ -1955,6 +1992,7 @@
BC4914B628DEC2FE009E2DF6 /* VTSessionMode.swift in Sources */,
295891261EEB8EF300CE51E1 /* FLVAACPacket.swift in Sources */,
29B876791CD70ACE00FC07DA /* HTTPStream.swift in Sources */,
BC1DC50A2A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift in Sources */,
BC6FC91E29609A6800A746EE /* ShapeFactory.swift in Sources */,
BC32E88829C9971100051507 /* InstanceHolder.swift in Sources */,
BC7C56B7299E579F00C41A9B /* AudioCodecSettings.swift in Sources */,
@ -1998,7 +2036,9 @@
290EA89A1DFB619600053022 /* TSProgramTests.swift in Sources */,
BCCBCEA029ADF55A0095B51C /* AudioCodecBufferTests.swift in Sources */,
BC0BF4F72986CE8700D72CB4 /* VideoCodecTests.swift in Sources */,
BC1DC5042A02894D00E928ED /* FLVVideoFourCCTests.swift in Sources */,
290EA8931DFB617800053022 /* HTTPRequestTests.swift in Sources */,
BC1DC5122A04E46E00E928ED /* HEVCDecoderConfigurationRecordTests.swift in Sources */,
2976077F20A89FBB00DCF24F /* RTMPMessageTests.swift in Sources */,
BC7C56C729A7701F00C41A9B /* ESSpecificDataTests.swift in Sources */,
BCCBCE9B29A9D96A0095B51C /* NALUnitReaderTests.swift in Sources */,
@ -2040,6 +2080,7 @@
296543631D62FE9000734698 /* HKView-macOS.swift in Sources */,
BC11024B2925147300D48035 /* IOCaptureUnit.swift in Sources */,
29B876FA1CD70D5900FC07DA /* M3U.swift in Sources */,
BC1DC4FC2A02868900E928ED /* FLVVideoFourCC.swift in Sources */,
29B876FD1CD70D5A00FC07DA /* AudioSpecificConfig.swift in Sources */,
BC32E88929C9971100051507 /* InstanceHolder.swift in Sources */,
BC7C56C0299FC38D00C41A9B /* VideoSize.swift in Sources */,
@ -2047,10 +2088,11 @@
2941746C22D069B300A2944F /* AudioEffect.swift in Sources */,
BC9CFA9423BDE8B700917EEF /* NetStreamDrawable.swift in Sources */,
296242631D8DBA8C00C451A3 /* TSReader.swift in Sources */,
29B876FE1CD70D5A00FC07DA /* AVCConfigurationRecord.swift in Sources */,
29B876FE1CD70D5A00FC07DA /* AVCDecoderConfigurationRecord.swift in Sources */,
294852571D852499002DE492 /* RTMPTSocket.swift in Sources */,
BC83A4742403D83B006BDE06 /* VTCompressionSession+Extension.swift in Sources */,
BCC1A72C264FAC1800661156 /* ESSpecificData.swift in Sources */,
BC1DC50B2A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift in Sources */,
BC1DC4A529F4F74F00E928ED /* AVCaptureSession+Extension.swift in Sources */,
BC110258292E661E00D48035 /* MultiCamCaptureSettings.swift in Sources */,
29B877001CD70D5A00FC07DA /* PacketizedElementaryStream.swift in Sources */,
@ -2080,10 +2122,12 @@
29B8770B1CD70D5A00FC07DA /* NetClient.swift in Sources */,
29EA87EE1E79A3E30043A5F8 /* CVPixelBuffer+Extension.swift in Sources */,
29B8770C1CD70D5A00FC07DA /* NetService.swift in Sources */,
BC1DC5152A05428800E928ED /* HEVCNALUnit.swift in Sources */,
2958911B1EEB8E3F00CE51E1 /* FLVAudioCodec.swift in Sources */,
BC4914A328DDD33D009E2DF6 /* VTSessionConvertible.swift in Sources */,
293B42EA2340B4840086F973 /* RTMPObjectEncoding.swift in Sources */,
29DC17B421D0CC0600E26CED /* Atomic.swift in Sources */,
BC1DC50F2A039E1900E928ED /* FLVVideoPacketType.swift in Sources */,
BC959EF0296EE4190067BA97 /* ImageTransform.swift in Sources */,
BC9F9C7926F8C16600B01ED0 /* Choreographer.swift in Sources */,
BC558269240BB40E00011AC0 /* RTMPStreamInfo.swift in Sources */,
@ -2113,7 +2157,7 @@
BCB976E026107B5600C9A649 /* TSField.swift in Sources */,
29B877191CD70D5A00FC07DA /* RTMPStream.swift in Sources */,
29B8771B1CD70D5A00FC07DA /* ByteArray.swift in Sources */,
BCCBCE9829A90D880095B51C /* NALUnit.swift in Sources */,
BCCBCE9829A90D880095B51C /* AVCNALUnit.swift in Sources */,
295891231EEB8EC500CE51E1 /* FLVAVCPacketType.swift in Sources */,
29EA87DA1E79A00E0043A5F8 /* ExpressibleByIntegerLiteral+Extension.swift in Sources */,
BC7C56B8299E579F00C41A9B /* AudioCodecSettings.swift in Sources */,
@ -2190,7 +2234,7 @@
BC7C56B9299E579F00C41A9B /* AudioCodecSettings.swift in Sources */,
29EB3DF01ED05768001CAE8B /* VideoCodec.swift in Sources */,
29EB3E351ED05A33001CAE8B /* DeviceUtil.swift in Sources */,
BCCBCE9929A90D880095B51C /* NALUnit.swift in Sources */,
BCCBCE9929A90D880095B51C /* AVCNALUnit.swift in Sources */,
29DC17B521D0CC0600E26CED /* Atomic.swift in Sources */,
BC7C56CF29A786AE00C41A9B /* ADTS.swift in Sources */,
BC44A1AB23D31E92002D4297 /* AudioCodecRingBuffer.swift in Sources */,
@ -2212,6 +2256,7 @@
29EB3E0D1ED05877001CAE8B /* IOAudioUnit.swift in Sources */,
2942A4FA21A9418A004E1BEE /* Running.swift in Sources */,
295891101EEB8D3C00CE51E1 /* FLVVideoCodec.swift in Sources */,
BC1DC5102A039E1900E928ED /* FLVVideoPacketType.swift in Sources */,
29EB3DFD1ED05847001CAE8B /* CVPixelBuffer+Extension.swift in Sources */,
BCC1A72D264FAC1800661156 /* ESSpecificData.swift in Sources */,
BCB976E126107B5600C9A649 /* TSField.swift in Sources */,
@ -2222,7 +2267,7 @@
BC4914A428DDD33D009E2DF6 /* VTSessionConvertible.swift in Sources */,
2941746D22D069B300A2944F /* AudioEffect.swift in Sources */,
29EB3E151ED0588C001CAE8B /* VideoEffect.swift in Sources */,
29EB3E041ED05860001CAE8B /* AVCConfigurationRecord.swift in Sources */,
29EB3E041ED05860001CAE8B /* AVCDecoderConfigurationRecord.swift in Sources */,
29EB3DF71ED05797001CAE8B /* URL+Extension.swift in Sources */,
29DF20682312A436004057C3 /* RTMPSocketCompatible.swift in Sources */,
29EB3E0B1ED05871001CAE8B /* TSReader.swift in Sources */,
@ -2238,6 +2283,7 @@
BC4914B428DDFE31009E2DF6 /* VTSessionOptionKey.swift in Sources */,
29EB3DF91ED0579C001CAE8B /* ExpressibleByIntegerLiteral+Extension.swift in Sources */,
29EB3E201ED059F9001CAE8B /* RTMPConnection.swift in Sources */,
BC1DC5162A05428800E928ED /* HEVCNALUnit.swift in Sources */,
29EB3E331ED05A2E001CAE8B /* MediaLink.swift in Sources */,
29EB3E1D1ED058A5001CAE8B /* AMFFoundation.swift in Sources */,
29EB3E251ED05A04001CAE8B /* RTMPSocket.swift in Sources */,
@ -2248,6 +2294,7 @@
29EB3E231ED059FF001CAE8B /* RTMPMuxer.swift in Sources */,
29EB3DF11ED0576C001CAE8B /* Constants.swift in Sources */,
29EB3E011ED05856001CAE8B /* HTTPStream.swift in Sources */,
BC1DC50C2A039B4400E928ED /* HEVCDecoderConfigurationRecord.swift in Sources */,
29EB3E171ED05893001CAE8B /* NetClient.swift in Sources */,
BC1DC4A629F4F74F00E928ED /* AVCaptureSession+Extension.swift in Sources */,
BC0D236F26331BAB001DDA0C /* DataBuffer.swift in Sources */,
@ -2272,6 +2319,7 @@
BC566F7025D2ECC500573C4C /* HLSService.swift in Sources */,
29EB3E181ED05896001CAE8B /* NetService.swift in Sources */,
295891281EEB8EF300CE51E1 /* FLVAACPacket.swift in Sources */,
BC1DC4FD2A02868900E928ED /* FLVVideoFourCC.swift in Sources */,
BC4914B828DEC2FE009E2DF6 /* VTSessionMode.swift in Sources */,
2958911C1EEB8E3F00CE51E1 /* FLVAudioCodec.swift in Sources */,
29EB3DF61ED0577C001CAE8B /* CMSampleBuffer+Extension.swift in Sources */,

View File

@ -1,10 +1,12 @@
# HaishinKit for iOS, macOS, tvOS, and [Android](https://github.com/shogo4405/HaishinKit.kt).
[![GitHub Stars](https://img.shields.io/github/stars/shogo4405/HaishinKit.swift?style=social)](https://github.com/shogo4405/HaishinKit.swift/stargazers)
[![Release](https://img.shields.io/github/v/release/shogo4405/HaishinKit.swift)](https://github.com/shogo4405/HaishinKit.swift/releases/latest)
[![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fshogo4405%2FHaishinKit.swift%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/shogo4405/HaishinKit.swift)
[![Swift Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fshogo4405%2FHaishinKit.swift%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/shogo4405/HaishinKit.swift)
[![GitHub license](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://raw.githubusercontent.com/shogo4405/HaishinKit.swift/master/LICENSE.md)
* Camera and Microphone streaming library via RTMP, HLS for iOS, macOS, tvOS.
* README.md contains unreleased content, which can be tested on the main branch.
* [API Documentation](https://shogo4405.github.io/HaishinKit.swift/documentation/haishinkit)
<p align="center">
@ -33,10 +35,16 @@ Enterprise Grade APIs for Feeds & Chat. <a href="https://getstream.io/tutorials/
<a href="https://streamlabs.com/" target="_blank"><img src="https://user-images.githubusercontent.com/810189/206836172-9c360977-ab6b-4eff-860b-82d0e7b06318.png" width="350px" alt="Streamlabs" /></a>
</p>
## 🌏 Related projects
Project name |Notes |License
----------------|------------|--------------
[HaishinKit for Android.](https://github.com/shogo4405/HaishinKit.kt)|Camera and Microphone streaming library via RTMP for Android.|[BSD 3-Clause "New" or "Revised" License](https://github.com/shogo4405/HaishinKit.kt/blob/master/LICENSE.md)
[HaishinKit for Flutter.](https://github.com/shogo4405/HaishinKit.dart)|Camera and Microphone streaming library via RTMP for Flutter.|[BSD 3-Clause "New" or "Revised" License](https://github.com/shogo4405/HaishinKit.dart/blob/master/LICENSE.md)
## 🎨 Features
### RTMP
- [x] Authentication
- [x] Publish and Recording (H264/AAC)
- [x] Publish and Recording
- [x] _Playback (Beta)_
- [x] Adaptive bitrate streaming
- [x] Handling (see also [#1153](/../../issues/1153))
@ -49,6 +57,12 @@ Enterprise Grade APIs for Feeds & Chat. <a href="https://getstream.io/tutorials/
- [x] _Tunneled (RTMPT over SSL/TLS) (Technical Preview)_
- [x] _RTMPT (Technical Preview)_
- [x] ReplayKit Live as a Broadcast Upload Extension
- [x] Supported codec
- Audio
- [x] AAC
- Video
- [x] H264/AVC
- [x] H265/HEVC ([Server-side support is required.](https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf))
### HLS
- [x] HTTPService
@ -75,11 +89,11 @@ if #available(iOS 13.0, *) {
|Features|[HKView](https://shogo4405.github.io/HaishinKit.swift/Classes/HKView.html)|[PiPHKView](https://shogo4405.github.io/HaishinKit.swift/Classes/PiPHKView.html)|[MTHKView](https://shogo4405.github.io/HaishinKit.swift/Classes/MTHKView.html)|
|-|:---:|:---:|:---:|
|Engine|AVCaptureVideoPreviewLayer|AVSampleBufferDisplayLayer|Metal|
|Publish|◯|◯|◯|
|Playback|×|◯|◯|
|VisualEffect|×|◯|◯|
|PictureInPicture|×|◯|×|
|MultiCamera|×|◯|◯|
|Publish|✔|✔|✔|
|Playback|<br />|✔|✔|
|VisualEffect|<br />|✔|✔|
|PictureInPicture|<br />|✔|<br />|
|MultiCamera|<br />|✔|✔|
### Others
- [x] [Support multitasking camera access.](https://developer.apple.com/documentation/avfoundation/capture_setup/accessing_the_camera_while_multitasking)
@ -96,8 +110,6 @@ if #available(iOS 13.0, *) {
|:----:|:----:|:----:|:----:|:----:|:----:|
|1.5.0+|11.0+|10.13+|10.2+|14.3+|5.7+|
|1.4.0+|11.0+|10.13+|10.2+|14.0+|5.7+|
|1.3.0+|11.0+|10.13+|10.2+|14.0+|5.7+|
|1.2.0+|9.0+|10.11+|10.2+|13.0+|5.5+|
## 🐾 Examples
Examples project are available for iOS with UIKit, iOS with SwiftUI, macOS and tvOS.

View File

@ -13,7 +13,7 @@ enum VTSessionMode {
allocator: kCFAllocatorDefault,
width: videoCodec.settings.videoSize.width,
height: videoCodec.settings.videoSize.height,
codecType: kCMVideoCodecType_H264,
codecType: videoCodec.settings.format.codecType,
encoderSpecification: nil,
imageBufferAttributes: videoCodec.attributes as CFDictionary?,
compressedDataAllocator: nil,

View File

@ -27,14 +27,6 @@ public protocol VideoCodecDelegate: AnyObject {
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.
*/
@ -77,7 +69,7 @@ public class VideoCodec {
return
}
if let atoms: [String: AnyObject] = formatDescription?.`extension`(by: "SampleDescriptionExtensionAtoms"), let avcC: Data = atoms["avcC"] as? Data {
let config = AVCConfigurationRecord(data: avcC)
let config = AVCDecoderConfigurationRecord(data: avcC)
isBaseline = config.avcProfileIndication == 66
}
delegate?.videoCodec(self, didOutput: formatDescription)
@ -148,7 +140,7 @@ public class VideoCodec {
var timingInfo = CMSampleTimingInfo(
duration: duration,
presentationTimeStamp: presentationTimeStamp,
decodeTimeStamp: .invalid
decodeTimeStamp: sampleBuffer.decodeTimeStamp
)
var videoFormatDescription: CMVideoFormatDescription?
var status = CMVideoFormatDescriptionCreateForImageBuffer(
@ -175,9 +167,7 @@ public class VideoCodec {
delegate?.videoCodec(self, errorOccurred: .failedToFlame(status: status))
return
}
if isBaseline {
delegate?.videoCodec(self, didOutput: buffer)
} else {
if buffer.decodeTimeStamp.isValid {
buffers.append(buffer)
buffers.sort {
$0.presentationTimeStamp < $1.presentationTimeStamp
@ -185,6 +175,8 @@ public class VideoCodec {
if minimumGroupOfPictures <= buffers.count {
delegate?.videoCodec(self, didOutput: buffers.removeFirst())
}
} else {
delegate?.videoCodec(self, didOutput: buffer)
}
}
}

View File

@ -43,6 +43,36 @@ public struct VideoCodecSettings: Codable {
case trim = "Trim"
}
/// The type of the VideoCodec supports format.
enum Format: Codable {
case h264
case hevc
#if os(macOS)
var encoderID: NSString {
switch self {
case .h264:
#if arch(arm64)
return NSString(string: "com.apple.videotoolbox.videoencoder.ave.avc")
#else
return NSString(string: "com.apple.videotoolbox.videoencoder.h264.gva")
#endif
case .hevc:
return NSString(string: "com.apple.videotoolbox.videoencoder.ave.hevc")
}
}
#endif
var codecType: UInt32 {
switch self {
case .h264:
return kCMVideoCodecType_H264
case .hevc:
return kCMVideoCodecType_HEVC
}
}
}
/// Specifies the video size of encoding video.
public var videoSize: VideoSize
/// Specifies the bitrate.
@ -57,10 +87,19 @@ public struct VideoCodecSettings: Codable {
/// Specifies the bitRateMode.
public var bitRateMode: BitRateMode
/// Specifies the H264 profileLevel.
public var profileLevel: String
/// Specifies the HardwareEncoder is enabled(TRUE), or not(FALSE) for macOS.
public var profileLevel: String {
didSet {
if profileLevel.contains("HEVC") {
format = .hevc
} else {
format = .h264
}
}
}
/// Specifies the HardwareEncoder is enabled(TRUE), or not(FALSE) for macOS.
public var isHardwareEncoderEnabled = true
var format: Format = .h264
var expectedFrameRate: Float64 = IOMixer.defaultFrameRate
/// Creates a new VideoCodecSettings instance.
@ -82,6 +121,9 @@ public struct VideoCodecSettings: Codable {
self.bitRateMode = bitRateMode
self.allowFrameReordering = allowFrameReordering
self.isHardwareEncoderEnabled = isHardwareEncoderEnabled
if profileLevel.contains("HEVC") {
self.format = .hevc
}
}
func invalidateSession(_ rhs: VideoCodecSettings) -> Bool {
@ -120,7 +162,7 @@ public struct VideoCodecSettings: Codable {
])
#if os(macOS)
if isHardwareEncoderEnabled {
options.insert(.init(key: .encoderID, value: VideoCodec.encoderName))
options.insert(.init(key: .encoderID, value: format.encoderID))
options.insert(.init(key: .enableHardwareAcceleratedVideoEncoder, value: kCFBooleanTrue))
options.insert(.init(key: .requireHardwareAcceleratedVideoEncoder, value: kCFBooleanTrue))
}

View File

@ -0,0 +1,18 @@
import Foundation
enum FLVVideoFourCC: UInt32 {
case av1 = 0x61763031 // { 'a', 'v', '0', '1' }
case vp9 = 0x76703039 // { 'v', 'p', '0', '9' }
case hevc = 0x68766331 // { 'h', 'v', 'c', '1' }
var isSupported: Bool {
switch self {
case .av1:
return false
case .vp9:
return false
case .hevc:
return true
}
}
}

View File

@ -0,0 +1,10 @@
import Foundation
enum FLVVideoPacketType: UInt8 {
case sequenceStart = 0
case codedFrames = 1
case sequenceEnd = 2
case codedFramesX = 3
case metadata = 4
case mpeg2TSSequenceStart = 5
}

View File

@ -1,16 +1,20 @@
import AVFoundation
import VideoToolbox
protocol DecoderConfigurationRecord {
func makeFormatDescription(_ formatDescriptionOut: UnsafeMutablePointer<CMFormatDescription?>) -> OSStatus
}
// MARK: -
/*
- seealso: ISO/IEC 14496-15 2010
*/
struct AVCConfigurationRecord {
struct AVCDecoderConfigurationRecord: DecoderConfigurationRecord {
static func getData(_ formatDescription: CMFormatDescription?) -> Data? {
guard let formatDescription = formatDescription else {
guard let formatDescription else {
return nil
}
if let atoms: NSDictionary = CMFormatDescriptionGetExtension(formatDescription, extensionKey: "SampleDescriptionExtensionAtoms" as CFString) as? NSDictionary {
if let atoms = CMFormatDescriptionGetExtension(formatDescription, extensionKey: "SampleDescriptionExtensionAtoms" as CFString) as? NSDictionary {
return atoms["avcC"] as? Data
}
return nil
@ -75,7 +79,7 @@ struct AVCConfigurationRecord {
}
}
extension AVCConfigurationRecord: DataConvertible {
extension AVCDecoderConfigurationRecord: DataConvertible {
// MARK: DataConvertible
var data: Data {
get {
@ -108,7 +112,7 @@ extension AVCConfigurationRecord: DataConvertible {
avcLevelIndication = try buffer.readUInt8()
lengthSizeMinusOneWithReserved = try buffer.readUInt8()
numOfSequenceParameterSetsWithReserved = try buffer.readUInt8()
let numOfSequenceParameterSets: UInt8 = numOfSequenceParameterSetsWithReserved & ~AVCConfigurationRecord.reserveNumOfSequenceParameterSets
let numOfSequenceParameterSets: UInt8 = numOfSequenceParameterSetsWithReserved & ~AVCDecoderConfigurationRecord.reserveNumOfSequenceParameterSets
for _ in 0..<numOfSequenceParameterSets {
let length = Int(try buffer.readUInt16())
sequenceParameterSets.append(try buffer.readBytes(length).bytes)
@ -125,7 +129,7 @@ extension AVCConfigurationRecord: DataConvertible {
}
}
extension AVCConfigurationRecord: CustomDebugStringConvertible {
extension AVCDecoderConfigurationRecord: CustomDebugStringConvertible {
// MARK: CustomDebugStringConvertible
var debugDescription: String {
Mirror(reflecting: self).debugDescription

View File

@ -1,7 +1,7 @@
import CoreMedia
import Foundation
enum NALUnitType: UInt8, Equatable {
enum AVCNALUnitType: UInt8, Equatable {
case unspec = 0
case slice = 1 // P frame
case dpa = 2
@ -18,9 +18,9 @@ enum NALUnitType: UInt8, Equatable {
}
// MARK: -
struct NALUnit: Equatable {
struct AVCNALUnit: Equatable {
let refIdc: UInt8
let type: NALUnitType
let type: AVCNALUnitType
let payload: Data
init(_ data: Data) {
@ -29,7 +29,7 @@ struct NALUnit: Equatable {
init(_ data: Data, length: Int) {
self.refIdc = data[0] >> 5
self.type = NALUnitType(rawValue: data[0] & 0x1f) ?? .unspec
self.type = AVCNALUnitType(rawValue: data[0] & 0x1f) ?? .unspec
self.payload = data.subdata(in: 1..<length)
}
@ -41,14 +41,14 @@ struct NALUnit: Equatable {
}
}
class NALUnitReader {
class AVCNALUnitReader {
static let defaultStartCodeLength: Int = 4
static let defaultNALUnitHeaderLength: Int32 = 4
var nalUnitHeaderLength: Int32 = NALUnitReader.defaultNALUnitHeaderLength
var nalUnitHeaderLength: Int32 = AVCNALUnitReader.defaultNALUnitHeaderLength
func read(_ data: Data) -> [NALUnit] {
var units: [NALUnit] = []
func read(_ data: Data) -> [AVCNALUnit] {
var units: [AVCNALUnit] = []
var lastIndexOf = data.count - 1
for i in (2..<data.count).reversed() {
guard data[i] == 1 && data[i - 1] == 0 && data[i - 2] == 0 else {

View File

@ -0,0 +1,134 @@
import AVFoundation
import Foundation
/// ISO/IEC 14496-15 8.3.3.1.2
struct HEVCDecoderConfigurationRecord: DecoderConfigurationRecord {
static func getData(_ formatDescription: CMFormatDescription?) -> Data? {
guard let formatDescription else {
return nil
}
if let atoms = CMFormatDescriptionGetExtension(formatDescription, extensionKey: "SampleDescriptionExtensionAtoms" as CFString) as? NSDictionary {
return atoms["hvcC"] as? Data
}
return nil
}
var configurationVersion: UInt8 = 1
var generalProfileSpace: UInt8 = 0
var generalTierFlag = false
var generalProfileIdc: UInt8 = 0
var generalProfileCompatibilityFlags: UInt32 = 0
var generalConstraintIndicatorFlags: UInt64 = 0
var generalLevelIdc: UInt8 = 0
var minSpatialSegmentationIdc: UInt16 = 0
var parallelismType: UInt8 = 0
var chromaFormat: UInt8 = 0
var bitDepthLumaMinus8: UInt8 = 0
var bitDepthChromaMinus8: UInt8 = 0
var avgFrameRate: UInt16 = 0
var constantFrameRate: UInt8 = 0
var numTemporalLayers: UInt8 = 0
var temporalIdNested: UInt8 = 0
var lengthSizeMinusOne: UInt8 = 0
var numberOfArrays: UInt8 = 0
var array: [HEVCNALUnitType: [Data]] = [:]
init() {
}
init(data: Data) {
self.data = data
}
func makeFormatDescription(_ formatDescriptionOut: UnsafeMutablePointer<CMFormatDescription?>) -> OSStatus {
guard let vps = array[.vps], let sps = array[.sps], let pps = array[.pps] else {
return kCMFormatDescriptionBridgeError_InvalidParameter
}
return vps[0].withUnsafeBytes { (vpsBuffer: UnsafeRawBufferPointer) -> OSStatus in
guard let vpsBaseAddress = vpsBuffer.baseAddress else {
return kCMFormatDescriptionBridgeError_InvalidParameter
}
return sps[0].withUnsafeBytes { (spsBuffer: UnsafeRawBufferPointer) -> OSStatus in
guard let spsBaseAddress = spsBuffer.baseAddress else {
return kCMFormatDescriptionBridgeError_InvalidParameter
}
return pps[0].withUnsafeBytes { (ppsBuffer: UnsafeRawBufferPointer) -> OSStatus in
guard let ppsBaseAddress = ppsBuffer.baseAddress else {
return kCMFormatDescriptionBridgeError_InvalidParameter
}
let pointers: [UnsafePointer<UInt8>] = [
vpsBaseAddress.assumingMemoryBound(to: UInt8.self),
spsBaseAddress.assumingMemoryBound(to: UInt8.self),
ppsBaseAddress.assumingMemoryBound(to: UInt8.self)
]
let sizes: [Int] = [vpsBuffer.count, spsBuffer.count, ppsBuffer.count]
let nalUnitHeaderLength: Int32 = 4
return CMVideoFormatDescriptionCreateFromHEVCParameterSets(
allocator: kCFAllocatorDefault,
parameterSetCount: pointers.count,
parameterSetPointers: pointers,
parameterSetSizes: sizes,
nalUnitHeaderLength: nalUnitHeaderLength,
extensions: nil,
formatDescriptionOut: formatDescriptionOut
)
}
}
}
}
}
extension HEVCDecoderConfigurationRecord: DataConvertible {
// MARK: DataConvertible
var data: Data {
get {
let buffer = ByteArray()
.writeUInt8(configurationVersion)
return buffer.data
}
set {
let buffer = ByteArray(data: newValue)
do {
configurationVersion = try buffer.readUInt8()
let a = try buffer.readUInt8()
generalProfileSpace = a >> 6
generalTierFlag = a & 0x20 > 0
generalProfileIdc = a & 0x1F
generalProfileCompatibilityFlags = try buffer.readUInt32()
generalConstraintIndicatorFlags = UInt64(try buffer.readUInt32()) << 16 | UInt64(try buffer.readUInt16())
generalLevelIdc = try buffer.readUInt8()
minSpatialSegmentationIdc = try buffer.readUInt16() & 0xFFF
parallelismType = try buffer.readUInt8() & 0x3
chromaFormat = try buffer.readUInt8() & 0x3
bitDepthLumaMinus8 = try buffer.readUInt8() & 0x7
bitDepthChromaMinus8 = try buffer.readUInt8() & 0x7
avgFrameRate = try buffer.readUInt16()
let b = try buffer.readUInt8()
constantFrameRate = b >> 6
numTemporalLayers = b & 0x38 >> 3
temporalIdNested = b & 0x6 >> 1
lengthSizeMinusOne = b & 0x3
numberOfArrays = try buffer.readUInt8()
for _ in 0..<numberOfArrays {
let a = try buffer.readUInt8()
let nalUnitType = HEVCNALUnitType(rawValue: a & 0b00111111) ?? .unspec
array[nalUnitType] = []
let numNalus = try buffer.readUInt16()
for _ in 0..<numNalus {
let length = try buffer.readUInt16()
array[nalUnitType]?.append(try buffer.readBytes(Int(length)))
}
}
} catch {
logger.error("\(buffer)")
}
}
}
}
extension HEVCDecoderConfigurationRecord: CustomDebugStringConvertible {
// MARK: CustomDebugStringConvertible
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
}

View File

@ -0,0 +1,8 @@
import Foundation
enum HEVCNALUnitType: UInt8 {
case unspec = 0
case vps = 32
case sps = 33
case pps = 34
}

View File

@ -155,7 +155,7 @@ struct PacketizedElementaryStream: PESPacketHeader {
if let config: AudioSpecificConfig = config as? AudioSpecificConfig {
return PacketizedElementaryStream(bytes: bytes, count: count, presentationTimeStamp: presentationTimeStamp, decodeTimeStamp: decodeTimeStamp, timestamp: timestamp, config: config)
}
if let config: AVCConfigurationRecord = config as? AVCConfigurationRecord {
if let config: AVCDecoderConfigurationRecord = config as? AVCDecoderConfigurationRecord {
return PacketizedElementaryStream(bytes: bytes, count: count, presentationTimeStamp: presentationTimeStamp, decodeTimeStamp: decodeTimeStamp, timestamp: timestamp, config: randomAccessIndicator ? config : nil)
}
return nil
@ -231,11 +231,11 @@ struct PacketizedElementaryStream: PESPacketHeader {
}
}
init?(bytes: UnsafePointer<UInt8>?, count: UInt32, presentationTimeStamp: CMTime, decodeTimeStamp: CMTime, timestamp: CMTime, config: AVCConfigurationRecord?) {
init?(bytes: UnsafePointer<UInt8>?, count: UInt32, presentationTimeStamp: CMTime, decodeTimeStamp: CMTime, timestamp: CMTime, config: AVCDecoderConfigurationRecord?) {
guard let bytes = bytes else {
return nil
}
if let config: AVCConfigurationRecord = config {
if let config: AVCDecoderConfigurationRecord = config {
data.append(contentsOf: [0x00, 0x00, 0x00, 0x01, 0x09, 0x10])
data.append(contentsOf: [0x00, 0x00, 0x00, 0x01])
data.append(contentsOf: config.sequenceParameterSets[0])

View File

@ -38,7 +38,7 @@ public class TSReader {
}
}
}
private var nalUnitReader = NALUnitReader()
private var nalUnitReader = AVCNALUnitReader()
private var programs: [UInt16: UInt16] = [:]
private var esSpecData: [UInt16: ESSpecificData] = [:]
private var formatDescriptions: [UInt16: CMFormatDescription] = [:]

View File

@ -45,7 +45,7 @@ public class TSWriter: Running {
writeProgramIfNeeded()
}
}
private var videoConfig: AVCConfigurationRecord? {
private var videoConfig: AVCDecoderConfigurationRecord? {
didSet {
writeProgramIfNeeded()
}
@ -244,7 +244,7 @@ extension TSWriter: VideoCodecDelegate {
public func videoCodec(_ codec: VideoCodec, didOutput formatDescription: CMFormatDescription?) {
guard
let formatDescription,
let avcC = AVCConfigurationRecord.getData(formatDescription) else {
let avcC = AVCDecoderConfigurationRecord.getData(formatDescription) else {
return
}
var data = ESSpecificData()
@ -252,7 +252,7 @@ extension TSWriter: VideoCodecDelegate {
data.elementaryPID = TSWriter.defaultVideoPID
PMT.elementaryStreamSpecificData.append(data)
videoContinuityCounter = 0
videoConfig = AVCConfigurationRecord(data: avcC)
videoConfig = AVCDecoderConfigurationRecord(data: avcC)
}
public func videoCodec(_ codec: VideoCodec, didOutput sampleBuffer: CMSampleBuffer) {

View File

@ -624,9 +624,6 @@ final class RTMPAudioMessage: RTMPMessage {
7.1.5. Video Message (9)
*/
final class RTMPVideoMessage: RTMPMessage {
private(set) var codec: FLVVideoCodec = .unknown
private(set) var status: OSStatus = noErr
init() {
super.init(type: .video)
}
@ -646,28 +643,46 @@ final class RTMPVideoMessage: RTMPMessage {
guard FLVTagType.video.headerSize <= payload.count else {
return
}
switch payload[1] {
case FLVAVCPacketType.seq.rawValue:
status = makeFormatDescription(stream)
stream.mixer.mediaLink.hasVideo = true
stream.dispatch(.rtmpStatus, bubbles: false, data: RTMPStream.Code.videoDimensionChange.data(""))
case FLVAVCPacketType.nal.rawValue:
if let sampleBuffer = makeSampleBuffer(stream, type: type) {
sampleBuffer.isNotSync = !(payload[0] >> 4 == FLVFrameType.key.rawValue)
stream.mixer.mediaLink.enqueueVideo(sampleBuffer)
if (payload[0] & 0b10000000) == 0 {
switch payload[0] >> 4 & 0b00001111 {
case FLVVideoCodec.avc.rawValue:
switch payload[1] {
case FLVAVCPacketType.seq.rawValue:
makeFormatDescription(stream, format: .h264)
case FLVAVCPacketType.nal.rawValue:
if let sampleBuffer = makeSampleBuffer(stream, type: type, offset: 0) {
stream.mixer.mediaLink.enqueueVideo(sampleBuffer)
}
default:
break
}
default:
break
}
if stream.mixer.mediaLink.isPaused && stream.mixer.audioIO.codec.inSourceFormat == nil {
stream.mixer.mediaLink.isPaused = false
} else {
// IsExHeader for Enhancing RTMP, FLV
switch true {
// hvc1
case payload[1] == 0x68 && payload[2] == 0x76 && payload[3] == 0x63 && payload[4] == 0x31:
switch payload[0] & 0b00001111 {
case FLVVideoPacketType.sequenceStart.rawValue:
makeFormatDescription(stream, format: .hevc)
case FLVVideoPacketType.codedFrames.rawValue:
if let sampleBuffer = makeSampleBuffer(stream, type: type, offset: 3) {
stream.mixer.mediaLink.enqueueVideo(sampleBuffer)
}
default:
break
}
default:
break
}
default:
break
}
}
private func makeSampleBuffer(_ stream: RTMPStream, type: RTMPChunkType) -> CMSampleBuffer? {
let isBaseline = stream.mixer.videoIO.codec.isBaseline
private func makeSampleBuffer(_ stream: RTMPStream, type: RTMPChunkType, offset: Int = 0) -> CMSampleBuffer? {
// compositionTime -> SI24
var compositionTime = isBaseline ? 0 : Int32(data: [0] + payload[2..<5]).bigEndian
var compositionTime = Int32(data: [0] + payload[2 + offset..<5 + offset]).bigEndian
compositionTime <<= 8
compositionTime /= 256
@ -686,10 +701,10 @@ final class RTMPVideoMessage: RTMPMessage {
var timing = CMSampleTimingInfo(
duration: CMTimeMake(value: duration, timescale: 1000),
presentationTimeStamp: CMTimeMake(value: Int64(stream.videoTimestamp) + Int64(compositionTime), timescale: 1000),
decodeTimeStamp: .invalid
decodeTimeStamp: compositionTime == 0 ? .invalid : CMTimeMake(value: Int64(stream.videoTimestamp), timescale: 1000)
)
let blockBuffer = payload.makeBlockBuffer(advancedBy: FLVTagType.video.headerSize)
let blockBuffer = payload.makeBlockBuffer(advancedBy: FLVTagType.video.headerSize + offset)
var sampleBuffer: CMSampleBuffer?
var sampleSize = blockBuffer?.dataLength ?? 0
guard CMSampleBufferCreate(
@ -707,14 +722,26 @@ final class RTMPVideoMessage: RTMPMessage {
sampleBufferOut: &sampleBuffer) == noErr else {
return nil
}
sampleBuffer?.isNotSync = !(payload[0] >> 4 & 0b0111 == FLVFrameType.key.rawValue)
return sampleBuffer
}
private func makeFormatDescription(_ stream: RTMPStream) -> OSStatus {
var config = AVCConfigurationRecord()
config.data = payload.subdata(in: FLVTagType.video.headerSize..<payload.count)
return config.makeFormatDescription(&stream.mixer.videoIO.formatDescription)
private func makeFormatDescription(_ stream: RTMPStream, format: VideoCodecSettings.Format) {
var status = noErr
switch format {
case .h264:
var config = AVCDecoderConfigurationRecord()
config.data = payload.subdata(in: FLVTagType.video.headerSize..<payload.count)
status = config.makeFormatDescription(&stream.mixer.videoIO.formatDescription)
case .hevc:
var config = HEVCDecoderConfigurationRecord()
config.data = payload.subdata(in: FLVTagType.video.headerSize..<payload.count)
status = config.makeFormatDescription(&stream.mixer.videoIO.formatDescription)
}
if status == noErr {
stream.mixer.mediaLink.hasVideo = true
stream.dispatch(.rtmpStatus, bubbles: false, data: RTMPStream.Code.videoDimensionChange.data(""))
}
}
}

View File

@ -14,12 +14,13 @@ final class RTMPMuxer {
static let aac: UInt8 = FLVAudioCodec.aac.rawValue << 4 | FLVSoundRate.kHz44.rawValue << 2 | FLVSoundSize.snd16bit.rawValue << 1 | FLVSoundType.stereo.rawValue
weak var delegate: RTMPMuxerDelegate?
private var audioTimeStamp = CMTime.zero
private var videoTimeStamp = CMTime.zero
private var audioTimeStamp: CMTime = .zero
private var videoTimeStamp: CMTime = .zero
private let compositiionTimeOffset = CMTime.init(value: 1, timescale: 10)
func dispose() {
audioTimeStamp = CMTime.zero
videoTimeStamp = CMTime.zero
audioTimeStamp = .zero
videoTimeStamp = .zero
}
}
@ -55,38 +56,58 @@ extension RTMPMuxer: VideoCodecDelegate {
}
func videoCodec(_ codec: VideoCodec, didOutput formatDescription: CMFormatDescription?) {
guard
let formatDescription = formatDescription,
let avcC = AVCConfigurationRecord.getData(formatDescription) else {
guard let formatDescription else {
return
}
var buffer = Data([FLVFrameType.key.rawValue << 4 | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.seq.rawValue, 0, 0, 0])
buffer.append(avcC)
delegate?.muxer(self, didOutputVideo: buffer, withTimestamp: 0)
switch codec.settings.format {
case .h264:
guard let avcC = AVCDecoderConfigurationRecord.getData(formatDescription) else {
return
}
var buffer = Data([FLVFrameType.key.rawValue << 4 | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.seq.rawValue, 0, 0, 0])
buffer.append(avcC)
delegate?.muxer(self, didOutputVideo: buffer, withTimestamp: 0)
case .hevc:
guard let hvcC = HEVCDecoderConfigurationRecord.getData(formatDescription) else {
return
}
var buffer = Data([0b10000000 | FLVFrameType.key.rawValue << 4 | FLVVideoPacketType.sequenceStart.rawValue, 0x68, 0x76, 0x63, 0x31])
buffer.append(hvcC)
delegate?.muxer(self, didOutputVideo: buffer, withTimestamp: 0)
}
}
func videoCodec(_ codec: VideoCodec, didOutput sampleBuffer: CMSampleBuffer) {
let keyframe = !sampleBuffer.isNotSync
var compositionTime: Int32 = 0
let presentationTimeStamp = sampleBuffer.presentationTimeStamp
var decodeTimeStamp = sampleBuffer.decodeTimeStamp
if decodeTimeStamp == CMTime.invalid {
decodeTimeStamp = presentationTimeStamp
} else {
compositionTime = (videoTimeStamp == .zero) ? 0 : Int32((sampleBuffer.presentationTimeStamp.seconds - videoTimeStamp.seconds) * 1000)
}
let delta = (videoTimeStamp == .zero ? 0 : decodeTimeStamp.seconds - videoTimeStamp.seconds) * 1000
let decodeTimeStamp = sampleBuffer.decodeTimeStamp.isValid ? sampleBuffer.decodeTimeStamp : sampleBuffer.presentationTimeStamp
let compositionTime = getCompositionTime(sampleBuffer)
let delta = (videoTimeStamp == .zero ? .zero : decodeTimeStamp - videoTimeStamp).seconds * 1000
guard let data = sampleBuffer.dataBuffer?.data, 0 <= delta else {
return
}
var buffer = Data([((keyframe ? FLVFrameType.key.rawValue : FLVFrameType.inter.rawValue) << 4) | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.nal.rawValue])
buffer.append(contentsOf: compositionTime.bigEndian.data[1..<4])
buffer.append(data)
delegate?.muxer(self, didOutputVideo: buffer, withTimestamp: delta)
switch codec.settings.format {
case .h264:
var buffer = Data([((keyframe ? FLVFrameType.key.rawValue : FLVFrameType.inter.rawValue) << 4) | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.nal.rawValue])
buffer.append(contentsOf: compositionTime.bigEndian.data[1..<4])
buffer.append(data)
delegate?.muxer(self, didOutputVideo: buffer, withTimestamp: delta)
case .hevc:
var buffer = Data([0b10000000 | ((keyframe ? FLVFrameType.key.rawValue : FLVFrameType.inter.rawValue) << 4) | FLVVideoPacketType.codedFrames.rawValue, 0x68, 0x76, 0x63, 0x31])
buffer.append(contentsOf: compositionTime.bigEndian.data[1..<4])
buffer.append(data)
delegate?.muxer(self, didOutputVideo: buffer, withTimestamp: delta)
}
videoTimeStamp = decodeTimeStamp
}
func videoCodecWillDropFame(_ codec: VideoCodec) -> Bool {
return delegate?.muxerWillDropFrame(self) ?? false
}
private func getCompositionTime(_ sampleBuffer: CMSampleBuffer) -> Int32 {
guard sampleBuffer.decodeTimeStamp.isValid, sampleBuffer.decodeTimeStamp != sampleBuffer.presentationTimeStamp else {
return 0
}
return Int32((sampleBuffer.presentationTimeStamp - videoTimeStamp + compositiionTimeOffset).seconds * 1000)
}
}

View File

@ -0,0 +1,22 @@
import Foundation
import XCTest
import AVFoundation
@testable import HaishinKit
final class FLVVideoFourCCTests: XCTestCase {
func testMain() {
XCTAssertEqual("av01", str4(n: Int(FLVVideoFourCC.av1.rawValue)))
XCTAssertEqual("hvc1", str4(n: Int(FLVVideoFourCC.hevc.rawValue)))
XCTAssertEqual("vp09", str4(n: Int(FLVVideoFourCC.vp9.rawValue)))
}
func str4(n: Int) -> String {
var result = String(UnicodeScalar((n >> 24) & 255)?.description ?? "")
result.append(UnicodeScalar((n >> 16) & 255)?.description ?? "")
result.append(UnicodeScalar((n >> 8) & 255)?.description ?? "")
result.append(UnicodeScalar(n & 255)?.description ?? "")
return result
}
}

View File

@ -0,0 +1,15 @@
import Foundation
import AVFoundation
import XCTest
@testable import HaishinKit
final class HEVCDecoderConfigurationRecordTests: XCTestCase {
func testMain() {
let data = Data([1, 1, 96, 0, 0, 0, 176, 0, 0, 0, 0, 0, 93, 240, 0, 252, 253, 248, 248, 0, 0, 15, 3, 32, 0, 1, 0, 24, 64, 1, 12, 1, 255, 255, 1, 96, 0, 0, 3, 0, 176, 0, 0, 3, 0, 0, 3, 0, 93, 21, 192, 144, 33, 0, 1, 0, 36, 66, 1, 1, 1, 96, 0, 0, 3, 0, 176, 0, 0, 3, 0, 0, 3, 0, 93, 160, 2, 40, 128, 39, 28, 178, 226, 5, 123, 145, 101, 83, 80, 16, 16, 16, 8, 34, 0, 1, 0, 7, 68, 1, 192, 44, 188, 20, 201])
let hevc = HEVCDecoderConfigurationRecord(data: data)
var formatDescription: CMFormatDescription?
_ = hevc.makeFormatDescription(&formatDescription)
XCTAssertNotNil(formatDescription)
}
}

View File

@ -7,7 +7,7 @@ import XCTest
final class NALUnitReaderTests: XCTestCase {
func testMain() {
let data = Data([0, 0, 0, 1, 9, 240, 0, 0, 0, 1, 103, 77, 64, 13, 218, 5, 7, 236, 4, 64, 0, 0, 3, 0, 64, 0, 0, 7, 131, 197, 10, 168, 0, 0, 0, 1, 104, 239, 60, 128, 0, 0, 0, 1, 101, 136, 130, 1, 15, 250, 120, 30, 255, 244, 55, 157, 215, 115, 255, 239, 112, 39, 83, 211, 17, 103, 152, 229, 241, 131, 49, 7, 123, 10, 145, 184, 0, 0, 3, 3, 133, 122, 49, 20, 214, 115, 51, 202, 59, 43, 204, 79, 27, 229, 101, 135, 60, 234, 243, 78, 210, 98, 30, 252, 36, 38, 20, 202, 41, 121, 70, 45, 15, 54, 125, 153, 199, 236, 90, 142, 247, 27, 202, 17, 205, 77, 133, 21, 189, 212, 159, 87, 222, 100, 53, 75, 211, 139, 219, 83, 89, 59, 199, 242, 182, 18, 245, 72, 70, 50, 230, 58, 82, 122, 179, 121, 243, 232, 107, 206, 157, 13, 151, 218, 93, 118, 157, 216, 67, 142, 2, 95, 69, 134, 167, 106, 101, 67, 112, 72, 120, 144, 105, 148, 234, 94, 74, 154, 149, 190, 13, 10, 88, 148, 169, 56, 46, 152, 176, 173, 110, 22, 215, 35, 18, 203, 125, 158, 16, 25, 228, 163, 26, 63, 30, 3, 96, 123, 237, 109, 12, 174, 216, 184, 25, 33, 123, 175, 69, 154, 240, 37, 168, 99, 38, 144, 221, 227, 119, 206, 215, 149, 111, 250, 180, 134, 78, 85, 50, 129, 178, 93, 255, 227, 144, 100, 156, 113, 113, 235, 47, 242, 68, 236, 109, 135, 87, 84, 178, 184, 163, 161, 170, 184, 84, 68, 113, 213, 73, 180, 25, 1, 77, 13, 222, 138, 69, 24, 104, 255, 218, 76, 224, 26, 122, 0, 231, 230, 203, 211, 172, 224, 26, 184, 69, 180, 123, 221, 8, 182, 241, 202, 193, 169, 120, 208, 135, 31, 82, 168, 125, 93, 207, 207, 109, 14, 243, 179, 97, 102, 58, 243, 14, 152, 13, 231, 30, 221, 177, 9, 72, 68, 212, 196, 71, 223, 142, 0, 248, 116, 139, 133, 210, 142, 83, 112, 87, 53, 138, 103, 202, 169, 112, 27, 7, 213, 152, 144, 207, 141, 84, 183, 121, 30, 128, 64, 95, 28, 10, 88, 116, 188, 83, 127, 181, 57, 47, 5, 19, 62, 132, 173, 201, 203, 170, 68, 224, 135, 134, 58, 206, 71, 77, 98, 77, 150, 225, 111, 103, 65, 84, 29, 176, 97, 72, 182, 151, 220, 153, 39, 247, 78, 136, 9, 166, 140, 221, 243, 68, 139, 229, 236, 189, 181, 124, 7, 35, 230, 139, 247, 223, 16, 78, 15, 189, 12, 144, 241, 169, 170, 166, 232, 17, 221, 212, 71, 69, 95, 122, 9, 36, 153, 246, 136, 111, 36, 50, 56, 118, 181, 240, 100, 5, 137, 252, 23, 244, 131, 41, 190, 128, 198, 134, 232, 40, 242, 214, 82, 69, 9, 168, 59, 179, 254, 220, 234, 16, 1, 170, 182, 214, 131, 169, 124, 91, 19, 65, 162, 179, 8, 98, 204, 219, 240, 6, 79, 49, 67, 120, 31, 236, 103, 167, 108, 213, 69, 193, 226, 66, 66, 242, 52, 18, 161, 42, 164, 133, 191, 82, 156, 2, 204, 75, 254, 217, 111, 215, 140, 157, 195, 195, 112, 120, 165, 163, 136, 125, 92, 195, 182, 99, 106, 220])
let reader = NALUnitReader()
let reader = AVCNALUnitReader()
let units = reader.read(data)
let sps = units.first(where: { $0.type == .sps })
XCTAssertEqual(sps?.data.bytes, [103, 77, 64, 13, 218, 5, 7, 236, 4, 64, 0, 0, 3, 0, 64, 0, 0, 7, 131, 197, 10, 168])
@ -16,7 +16,7 @@ final class NALUnitReaderTests: XCTestCase {
func testSlice_startCode3() {
let data = Data([0, 0, 1, 65, 226, 8, 13, 224, 179, 253, 15, 80, 87, 254, 170, 10, 255, 213, 65, 95, 250, 168, 43, 255, 85, 5, 127, 234, 160, 175, 253, 84, 21, 255, 170, 130, 10, 197, 255, 170, 134, 250, 37, 66, 31, 232, 170, 28, 59, 199, 255, 170, 135, 42, 122, 250, 11, 143, 255, 85, 5, 127, 234, 160, 175, 253, 84, 43, 208, 134, 33, 111, 244, 37, 66, 221, 88, 190, 147, 21, 200, 236, 47, 210, 127, 166, 196, 119, 250, 168, 43, 255, 85, 5, 127, 234, 160, 175, 253, 84, 21, 255, 170, 130, 191, 245, 80, 87, 254, 170, 10, 255, 213, 65, 95, 250, 168, 43, 248])
let reader = NALUnitReader()
let reader = AVCNALUnitReader()
let units = reader.read(data)
XCTAssertEqual([65, 226, 8, 13], units.first?.data.bytes[0..<4])
XCTAssertTrue(units.contains(where: { $0.type == .slice }))
@ -24,7 +24,7 @@ final class NALUnitReaderTests: XCTestCase {
func testSPSPPS_startCode3() {
let data = Data([0, 0, 1, 39, 66, 0, 30, 171, 64, 88, 25, 242, 203, 53, 1, 1, 1, 2, 0, 0, 1, 40, 206, 60, 128, 0, 0, 1, 39, 66, 0, 30, 171, 64, 88, 25, 242, 203, 53, 1, 1, 1, 2, 0, 0, 1, 40, 206, 60, 128, 0, 0, 1, 101, 184, 32, 3, 255, 255, 254, 30, 30, 40, 0, 8, 162, 251, 239, 190, 251, 239, 190, 251, 239, 190, 251, 239, 190, 251, 239, 190, 251, 239, 190, 251, 239, 190, 251, 239, 190, 251, 239, 190, 251, 239, 190, 251, 239, 190, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 175, 255, 227, 240, 65, 192, 1, 16, 101, 17, 204, 73, 101, 165, 194, 60, 154, 126, 49, 8, 164, 150, 125, 247, 223, 125, 247, 223, 125, 247, 223, 125, 247, 223, 127, 255, 138, 252, 16, 120, 0, 55, 178, 27, 153, 226, 14, 166, 169, 75, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 193, 63, 255, 134, 166, 0, 0, 128, 200, 0, 8, 17, 0, 1, 96, 242, 96, 19, 129, 190, 129, 218, 96, 235, 136, 1, 198, 4, 169, 254, 196, 184, 65, 20, 5, 128, 176, 30, 2, 0, 160, 2, 32, 166, 47, 196, 70, 248, 176, 128, 1, 5, 23, 44, 129, 0, 128, 61, 204, 4, 163, 208, 131, 132, 72, 82, 216, 66, 185, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 224, 31, 255, 96, 174, 0, 9, 168, 2, 235, 144, 14, 120, 97, 73, 182, 124, 161, 0, 0, 32, 32, 0, 2, 12, 64, 0, 32, 65, 65, 134, 48, 150, 0, 2, 1, 128, 6, 2, 0, 19, 128, 18, 51, 13, 97, 202, 112, 8, 110, 242, 1, 128, 6, 185, 128, 130, 164, 24, 117, 238, 64, 27, 154, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 235, 174, 186, 233, 235, 174, 186, 235, 175, 255, 255, 130, 239, 0, 7, 24, 27, 56, 90, 108, 195, 52, 5, 156, 12, 212, 90, 62, 1, 23, 128, 8, 35, 201, 76, 218, 71, 9, 31, 159, 253, 221, 199, 65, 220, 0, 24, 152, 156, 8, 30, 102, 87, 136, 225, 17, 170, 242, 109, 0, 88, 97, 19, 133, 35, 14, 179, 217, 217, 188, 212, 50, 138, 112, 86, 161, 151, 215, 56, 73, 126, 108, 130, 28, 45, 172, 222, 5, 7, 27, 252, 215, 255, 121, 11, 86, 3, 2, 86, 156, 239, 209, 189, 183, 117, 226, 29, 21, 192, 236, 87, 16, 153, 48, 100, 226, 90, 243, 160, 0, 100, 129, 102, 20, 1, 17, 64, 38, 251, 224, 128, 1, 0, 25, 192, 240, 160, 62, 54, 11, 90, 25, 108, 164, 6, 82, 223, 19, 200, 0, 4, 0, 136, 131, 97, 133, 147, 194, 128, 35, 135, 110, 31, 255, 234, 131, 191, 85, 162, 0, 1, 0, 61, 111, 224, 82, 52, 69, 101, 187, 238, 215, 255, 131, 0, 79, 136, 142, 24, 190, 138, 230, 192, 9, 218, 71, 116])
let reader = NALUnitReader()
let reader = AVCNALUnitReader()
XCTAssertNotNil(reader.makeFormatDescription(data))
}
}