protocolized MP4Boxes

This commit is contained in:
shogo4405 2021-04-29 21:31:26 +09:00
parent 36c35833a2
commit 9defc2d67b
55 changed files with 1674 additions and 944 deletions

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>HaishinKit.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>

View File

@ -11,11 +11,9 @@
2901A4EE1D437170002BBD23 /* DisplayLinkedQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2901A4ED1D437170002BBD23 /* DisplayLinkedQueue.swift */; };
2901A4EF1D437662002BBD23 /* DisplayLinkedQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2901A4ED1D437170002BBD23 /* DisplayLinkedQueue.swift */; };
290686031DFDB7A7008EB7ED /* RTMPConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290686021DFDB7A6008EB7ED /* RTMPConnectionTests.swift */; };
290686051DFDC19B008EB7ED /* SampleVideo_360x240_5mb-base.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 290686041DFDC19B008EB7ED /* SampleVideo_360x240_5mb-base.mp4 */; };
290EA8901DFB616000053022 /* Foundation+ExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EA88E1DFB616000053022 /* Foundation+ExtensionTests.swift */; };
290EA8911DFB616000053022 /* SwiftCore+ExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EA88F1DFB616000053022 /* SwiftCore+ExtensionTests.swift */; };
290EA8931DFB617800053022 /* HTTPRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EA8921DFB617800053022 /* HTTPRequestTests.swift */; };
290EA8981DFB619600053022 /* MP4SamplerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EA8941DFB619600053022 /* MP4SamplerTests.swift */; };
290EA8991DFB619600053022 /* PacketizedElementaryStreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EA8951DFB619600053022 /* PacketizedElementaryStreamTests.swift */; };
290EA89A1DFB619600053022 /* ProgramSpecificTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EA8961DFB619600053022 /* ProgramSpecificTests.swift */; };
290EA89B1DFB619600053022 /* TSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290EA8971DFB619600053022 /* TSTests.swift */; };
@ -43,14 +41,9 @@
2923A1F81D6300650019FBCD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296543641D62FEB700734698 /* AppDelegate.swift */; };
29245AEE1D32347E00AFFB9A /* VideoGravityUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29245AEC1D3233EB00AFFB9A /* VideoGravityUtil.swift */; };
29245AEF1D32348400AFFB9A /* VideoGravityUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29245AEC1D3233EB00AFFB9A /* VideoGravityUtil.swift */; };
2926A9EC1DE6B71E0074E3D2 /* MachUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2926A9EB1DE6B71D0074E3D2 /* MachUtil.swift */; };
2926A9EF1DE6B83F0074E3D2 /* MachUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2926A9EB1DE6B71D0074E3D2 /* MachUtil.swift */; };
2926A9F21DE6F08E0074E3D2 /* TimerDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2926A9F01DE6EAEB0074E3D2 /* TimerDriver.swift */; };
2926A9F31DE6F08F0074E3D2 /* TimerDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2926A9F01DE6EAEB0074E3D2 /* TimerDriver.swift */; };
292AC17C1CF4C871004F5730 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2942424C1CF4C01300D65DCB /* MD5.swift */; };
292D8A331D8B293300DBECE2 /* MP4Sampler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292D8A321D8B293300DBECE2 /* MP4Sampler.swift */; };
292D8A341D8B294900DBECE2 /* MP4Sampler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292D8A321D8B293300DBECE2 /* MP4Sampler.swift */; };
292D8A351D8B294E00DBECE2 /* MP4Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29798E511CE5DF1900F5CBD0 /* MP4Reader.swift */; };
292D8A331D8B293300DBECE2 /* MP4Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292D8A321D8B293300DBECE2 /* MP4Reader.swift */; };
292D8A341D8B294900DBECE2 /* MP4Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292D8A321D8B293300DBECE2 /* MP4Reader.swift */; };
292F6DB11EEBB2040097EDBE /* AVFoundation+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292F6DB01EEBB2040097EDBE /* AVFoundation+Extension.swift */; };
2930D0411E12D35400DA2DC5 /* SampleHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2930D03F1E12D17C00DA2DC5 /* SampleHandler.swift */; };
29373DB6205524D700099860 /* HaishinKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29D3D4DC1ED0509900DD4AA6 /* HaishinKit.framework */; };
@ -139,7 +132,6 @@
2976A4821D4902CE00B53EF2 /* IOComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2976A4801D49025B00B53EF2 /* IOComponent.swift */; };
2976A4861D4903C300B53EF2 /* DeviceUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2976A4851D4903C300B53EF2 /* DeviceUtil.swift */; };
2976A4871D49045700B53EF2 /* DeviceUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2976A4851D4903C300B53EF2 /* DeviceUtil.swift */; };
29798E521CE5DF1A00F5CBD0 /* MP4Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29798E511CE5DF1900F5CBD0 /* MP4Reader.swift */; };
29798E671CE610F500F5CBD0 /* HaishinKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B8761B1CD701F900FC07DA /* HaishinKit.framework */; };
29798E751CE614FE00F5CBD0 /* SampleVideo_360x240_5mb in Resources */ = {isa = PBXBuildFile; fileRef = 29B876D71CD70CE700FC07DA /* SampleVideo_360x240_5mb */; };
29798E761CE614FE00F5CBD0 /* SampleVideo_360x240_5mb.m3u8 in Resources */ = {isa = PBXBuildFile; fileRef = 29B876D81CD70CE700FC07DA /* SampleVideo_360x240_5mb.m3u8 */; };
@ -291,8 +283,7 @@
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 */; };
29EB3E051ED05862001CAE8B /* MP4Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29798E511CE5DF1900F5CBD0 /* MP4Reader.swift */; };
29EB3E061ED05865001CAE8B /* MP4Sampler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292D8A321D8B293300DBECE2 /* MP4Sampler.swift */; };
29EB3E061ED05865001CAE8B /* MP4Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292D8A321D8B293300DBECE2 /* MP4Reader.swift */; };
29EB3E071ED05867001CAE8B /* NALUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B8767F1CD70AE800FC07DA /* NALUnit.swift */; };
29EB3E081ED05869001CAE8B /* PacketizedElementaryStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876801CD70AE800FC07DA /* PacketizedElementaryStream.swift */; };
29EB3E091ED0586B001CAE8B /* ProgramSpecific.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876811CD70AE800FC07DA /* ProgramSpecific.swift */; };
@ -330,9 +321,7 @@
29EB3E341ED05A30001CAE8B /* CRC32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876B91CD70B3900FC07DA /* CRC32.swift */; };
29EB3E351ED05A33001CAE8B /* DeviceUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2976A4851D4903C300B53EF2 /* DeviceUtil.swift */; };
29EB3E361ED05A35001CAE8B /* EventDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B876BA1CD70B3900FC07DA /* EventDispatcher.swift */; };
29EB3E371ED05A38001CAE8B /* MachUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2926A9EB1DE6B71D0074E3D2 /* MachUtil.swift */; };
29EB3E381ED05A41001CAE8B /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2942424C1CF4C01300D65DCB /* MD5.swift */; };
29EB3E3A1ED05A45001CAE8B /* TimerDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2926A9F01DE6EAEB0074E3D2 /* TimerDriver.swift */; };
29EB3E3B1ED05A48001CAE8B /* VideoGravityUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29245AEC1D3233EB00AFFB9A /* VideoGravityUtil.swift */; };
29EF03781CD79A5400473D99 /* HaishinKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 299F7E3B1CD71A97001E7272 /* HaishinKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
29F6F4851DFB83E200920A3A /* RTMPHandshake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29F6F4841DFB83E200920A3A /* RTMPHandshake.swift */; };
@ -391,9 +380,89 @@
BC83A4752403D83B006BDE06 /* VTCompressionSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC83A4722403D83B006BDE06 /* VTCompressionSession+Extension.swift */; };
BC8E32E42532F3700087DF49 /* GLHKView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C263171D00804A0098D4EF /* GLHKView.swift */; };
BC8E32E92532F3710087DF49 /* GLHKView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C263171D00804A0098D4EF /* GLHKView.swift */; };
BC94E4FE263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E4FD263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift */; };
BC94E4FF263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E4FD263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift */; };
BC94E500263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E4FD263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift */; };
BC94E502263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E501263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift */; };
BC94E503263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E501263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift */; };
BC94E504263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E501263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift */; };
BC94E506263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E505263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift */; };
BC94E507263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E505263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift */; };
BC94E508263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E505263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift */; };
BC94E50A263FEBB60094C169 /* MP4TrackRunBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E509263FEBB60094C169 /* MP4TrackRunBox.swift */; };
BC94E50B263FEBB60094C169 /* MP4TrackRunBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E509263FEBB60094C169 /* MP4TrackRunBox.swift */; };
BC94E50C263FEBB60094C169 /* MP4TrackRunBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E509263FEBB60094C169 /* MP4TrackRunBox.swift */; };
BC94E52E264146530094C169 /* MP4ReaderConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E52C264146120094C169 /* MP4ReaderConvertible.swift */; };
BC94E52F264146540094C169 /* MP4ReaderConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E52C264146120094C169 /* MP4ReaderConvertible.swift */; };
BC94E530264146540094C169 /* MP4ReaderConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC94E52C264146120094C169 /* MP4ReaderConvertible.swift */; };
BC94E53A264192B00094C169 /* MP4FileHandleTests2.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0C10263ED28500ADFB80 /* MP4FileHandleTests2.swift */; };
BC94E53B2641984F0094C169 /* MP4FileHandleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BC7263AF8F70027213C /* MP4FileHandleTests.swift */; };
BC9CFA9323BDE8B700917EEF /* NetStreamRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9CFA9223BDE8B700917EEF /* NetStreamRenderer.swift */; };
BC9CFA9423BDE8B700917EEF /* NetStreamRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9CFA9223BDE8B700917EEF /* NetStreamRenderer.swift */; };
BC9CFA9523BDE8B700917EEF /* NetStreamRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9CFA9223BDE8B700917EEF /* NetStreamRenderer.swift */; };
BCA97B86263AC0F30027213C /* MP4BoxConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B85263AC0F30027213C /* MP4BoxConvertible.swift */; };
BCA97B87263AC0F30027213C /* MP4BoxConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B85263AC0F30027213C /* MP4BoxConvertible.swift */; };
BCA97B88263AC0F30027213C /* MP4BoxConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B85263AC0F30027213C /* MP4BoxConvertible.swift */; };
BCA97B8A263AC1830027213C /* MP4Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B89263AC1830027213C /* MP4Box.swift */; };
BCA97B8B263AC1830027213C /* MP4Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B89263AC1830027213C /* MP4Box.swift */; };
BCA97B8C263AC1830027213C /* MP4Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B89263AC1830027213C /* MP4Box.swift */; };
BCA97BE2263C095B0027213C /* MP4TimeToSampleBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BA2263AC86D0027213C /* MP4TimeToSampleBox.swift */; };
BCA97BE3263C095C0027213C /* MP4TimeToSampleBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BA2263AC86D0027213C /* MP4TimeToSampleBox.swift */; };
BCA97BE4263C095C0027213C /* MP4TimeToSampleBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BA2263AC86D0027213C /* MP4TimeToSampleBox.swift */; };
BCA97BE5263C0B8A0027213C /* MP4FileTypeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BDE263BC7770027213C /* MP4FileTypeBox.swift */; };
BCA97BE6263C0B8A0027213C /* MP4FileTypeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BDE263BC7770027213C /* MP4FileTypeBox.swift */; };
BCA97BE7263C0B8B0027213C /* MP4FileTypeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BDE263BC7770027213C /* MP4FileTypeBox.swift */; };
BCA97BE8263C27070027213C /* MP4SampleSizeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BA6263AC9880027213C /* MP4SampleSizeBox.swift */; };
BCA97BE9263C27080027213C /* MP4SampleSizeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BA6263AC9880027213C /* MP4SampleSizeBox.swift */; };
BCA97BEA263C27080027213C /* MP4SampleSizeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BA6263AC9880027213C /* MP4SampleSizeBox.swift */; };
BCA97BF0263C31020027213C /* MP4SampleDescriptionBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BBE263AD1920027213C /* MP4SampleDescriptionBox.swift */; };
BCA97BF1263C31020027213C /* MP4SampleDescriptionBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BBE263AD1920027213C /* MP4SampleDescriptionBox.swift */; };
BCA97BF2263C31020027213C /* MP4SampleDescriptionBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BBE263AD1920027213C /* MP4SampleDescriptionBox.swift */; };
BCA97BF4263C390E0027213C /* CustomXmlStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BF3263C390E0027213C /* CustomXmlStringConvertible.swift */; };
BCA97BF5263C390E0027213C /* CustomXmlStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BF3263C390E0027213C /* CustomXmlStringConvertible.swift */; };
BCA97BF6263C390E0027213C /* CustomXmlStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BF3263C390E0027213C /* CustomXmlStringConvertible.swift */; };
BCA97BF7263C4B8E0027213C /* MP4ChunkOffsetBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B91263AC5FB0027213C /* MP4ChunkOffsetBox.swift */; };
BCA97BF8263C4B8F0027213C /* MP4ChunkOffsetBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B91263AC5FB0027213C /* MP4ChunkOffsetBox.swift */; };
BCA97BF9263C4B8F0027213C /* MP4ChunkOffsetBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B91263AC5FB0027213C /* MP4ChunkOffsetBox.swift */; };
BCA97BFA263C4F980027213C /* MP4EditListBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B9A263AC7540027213C /* MP4EditListBox.swift */; };
BCA97BFB263C4F980027213C /* MP4EditListBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B9A263AC7540027213C /* MP4EditListBox.swift */; };
BCA97BFC263C4F990027213C /* MP4EditListBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B9A263AC7540027213C /* MP4EditListBox.swift */; };
BCA97BFD263C54550027213C /* MP4SampleToChunkBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B9E263AC7D60027213C /* MP4SampleToChunkBox.swift */; };
BCA97BFE263C54560027213C /* MP4SampleToChunkBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B9E263AC7D60027213C /* MP4SampleToChunkBox.swift */; };
BCA97BFF263C54560027213C /* MP4SampleToChunkBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B9E263AC7D60027213C /* MP4SampleToChunkBox.swift */; };
BCA97C00263C599C0027213C /* MP4SyncSampleBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B96263AC6980027213C /* MP4SyncSampleBox.swift */; };
BCA97C01263C599C0027213C /* MP4SyncSampleBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B96263AC6980027213C /* MP4SyncSampleBox.swift */; };
BCA97C02263C599D0027213C /* MP4SyncSampleBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B96263AC6980027213C /* MP4SyncSampleBox.swift */; };
BCA97C03263C61930027213C /* MP4MediaHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B8D263AC49B0027213C /* MP4MediaHeaderBox.swift */; };
BCA97C04263C61940027213C /* MP4MediaHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B8D263AC49B0027213C /* MP4MediaHeaderBox.swift */; };
BCA97C05263C61940027213C /* MP4MediaHeaderBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97B8D263AC49B0027213C /* MP4MediaHeaderBox.swift */; };
BCA97C0A263D80F40027213C /* MP4SampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97C09263D80F40027213C /* MP4SampleEntry.swift */; };
BCA97C0B263D80F40027213C /* MP4SampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97C09263D80F40027213C /* MP4SampleEntry.swift */; };
BCA97C0C263D80F40027213C /* MP4SampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97C09263D80F40027213C /* MP4SampleEntry.swift */; };
BCA97C12263D8C850027213C /* MP4FullBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97C11263D8C850027213C /* MP4FullBox.swift */; };
BCA97C13263D8C850027213C /* MP4FullBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97C11263D8C850027213C /* MP4FullBox.swift */; };
BCA97C14263D8C850027213C /* MP4FullBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97C11263D8C850027213C /* MP4FullBox.swift */; };
BCA97C15263D93DB0027213C /* MP4VisualSampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BC2263AD2280027213C /* MP4VisualSampleEntry.swift */; };
BCA97C16263D93DC0027213C /* MP4VisualSampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BC2263AD2280027213C /* MP4VisualSampleEntry.swift */; };
BCA97C17263D93DC0027213C /* MP4VisualSampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BC2263AD2280027213C /* MP4VisualSampleEntry.swift */; };
BCA97C18263DA5060027213C /* MP4AudioSampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BBA263AD0520027213C /* MP4AudioSampleEntry.swift */; };
BCA97C19263DA5070027213C /* MP4AudioSampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BBA263AD0520027213C /* MP4AudioSampleEntry.swift */; };
BCA97C1A263DA5070027213C /* MP4AudioSampleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BBA263AD0520027213C /* MP4AudioSampleEntry.swift */; };
BCA97C1B263DAD070027213C /* MP4ElementaryStreamDescriptorBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BB6263ACFB30027213C /* MP4ElementaryStreamDescriptorBox.swift */; };
BCA97C1C263DAD070027213C /* MP4ElementaryStreamDescriptorBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BB6263ACFB30027213C /* MP4ElementaryStreamDescriptorBox.swift */; };
BCA97C1D263DAD080027213C /* MP4ElementaryStreamDescriptorBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA97BB6263ACFB30027213C /* MP4ElementaryStreamDescriptorBox.swift */; };
BCAD0C00263E968400ADFB80 /* MP4FileReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0BFF263E968400ADFB80 /* MP4FileReader.swift */; };
BCAD0C01263E968400ADFB80 /* MP4FileReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0BFF263E968400ADFB80 /* MP4FileReader.swift */; };
BCAD0C02263E968400ADFB80 /* MP4FileReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0BFF263E968400ADFB80 /* MP4FileReader.swift */; };
BCAD0C18263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v.m3u8 in Resources */ = {isa = PBXBuildFile; fileRef = BCAD0C16263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v.m3u8 */; };
BCAD0C19263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v in Resources */ = {isa = PBXBuildFile; fileRef = BCAD0C17263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v */; };
BCAD0C1B263EE1D000ADFB80 /* MP4Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0C1A263EE1D000ADFB80 /* MP4Util.swift */; };
BCAD0C1C263EE1D000ADFB80 /* MP4Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0C1A263EE1D000ADFB80 /* MP4Util.swift */; };
BCAD0C1D263EE1D000ADFB80 /* MP4Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0C1A263EE1D000ADFB80 /* MP4Util.swift */; };
BCAD0C1F263EE53A00ADFB80 /* MP4UtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0C1E263EE53A00ADFB80 /* MP4UtilTests.swift */; };
BCAD0C21263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0C20263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift */; };
BCAD0C22263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0C20263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift */; };
BCAD0C23263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCAD0C20263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift */; };
BCB976D126107B1200C9A649 /* TSAdaptationExtensionField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB976D026107B1200C9A649 /* TSAdaptationExtensionField.swift */; };
BCB976D226107B1200C9A649 /* TSAdaptationExtensionField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB976D026107B1200C9A649 /* TSAdaptationExtensionField.swift */; };
BCB976D326107B1200C9A649 /* TSAdaptationExtensionField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB976D026107B1200C9A649 /* TSAdaptationExtensionField.swift */; };
@ -512,11 +581,9 @@
035AFA032263868E009DD0BB /* RTMPStreamTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RTMPStreamTests.swift; sourceTree = "<group>"; };
2901A4ED1D437170002BBD23 /* DisplayLinkedQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayLinkedQueue.swift; sourceTree = "<group>"; };
290686021DFDB7A6008EB7ED /* RTMPConnectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RTMPConnectionTests.swift; sourceTree = "<group>"; };
290686041DFDC19B008EB7ED /* SampleVideo_360x240_5mb-base.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SampleVideo_360x240_5mb-base.mp4"; sourceTree = "<group>"; };
290EA88E1DFB616000053022 /* Foundation+ExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Foundation+ExtensionTests.swift"; sourceTree = "<group>"; };
290EA88F1DFB616000053022 /* SwiftCore+ExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftCore+ExtensionTests.swift"; sourceTree = "<group>"; };
290EA8921DFB617800053022 /* HTTPRequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPRequestTests.swift; sourceTree = "<group>"; };
290EA8941DFB619600053022 /* MP4SamplerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MP4SamplerTests.swift; sourceTree = "<group>"; };
290EA8951DFB619600053022 /* PacketizedElementaryStreamTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketizedElementaryStreamTests.swift; sourceTree = "<group>"; };
290EA8961DFB619600053022 /* ProgramSpecificTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgramSpecificTests.swift; sourceTree = "<group>"; };
290EA8971DFB619600053022 /* TSTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TSTests.swift; sourceTree = "<group>"; };
@ -538,10 +605,8 @@
291F4E361CF206E200F59C51 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = "<group>"; };
29205CBD1E461F4E009D3FFF /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
29245AEC1D3233EB00AFFB9A /* VideoGravityUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoGravityUtil.swift; sourceTree = "<group>"; };
2926A9EB1DE6B71D0074E3D2 /* MachUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachUtil.swift; sourceTree = "<group>"; };
2926A9F01DE6EAEB0074E3D2 /* TimerDriver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimerDriver.swift; sourceTree = "<group>"; };
2927A2991E7ED2D70044AF91 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
292D8A321D8B293300DBECE2 /* MP4Sampler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MP4Sampler.swift; sourceTree = "<group>"; };
292D8A321D8B293300DBECE2 /* MP4Reader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MP4Reader.swift; sourceTree = "<group>"; };
292F6DB01EEBB2040097EDBE /* AVFoundation+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVFoundation+Extension.swift"; sourceTree = "<group>"; };
2930D03F1E12D17C00DA2DC5 /* SampleHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleHandler.swift; sourceTree = "<group>"; };
293B42E82340B4840086F973 /* RTMPObjectEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTMPObjectEncoding.swift; sourceTree = "<group>"; };
@ -593,7 +658,6 @@
2976A47D1D48C5C700B53EF2 /* AVRecorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AVRecorder.swift; sourceTree = "<group>"; };
2976A4801D49025B00B53EF2 /* IOComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IOComponent.swift; sourceTree = "<group>"; };
2976A4851D4903C300B53EF2 /* DeviceUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceUtil.swift; sourceTree = "<group>"; };
29798E511CE5DF1900F5CBD0 /* MP4Reader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MP4Reader.swift; sourceTree = "<group>"; };
29798E591CE60E5300F5CBD0 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
29798E5D1CE60E5300F5CBD0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
297E69112324E38800D418AB /* AudioCodec.Destination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioCodec.Destination.swift; sourceTree = "<group>"; };
@ -694,7 +758,37 @@
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>"; };
BC83A4722403D83B006BDE06 /* VTCompressionSession+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VTCompressionSession+Extension.swift"; sourceTree = "<group>"; };
BC94E4FD263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4MovieFragmentHeaderBox.swift; sourceTree = "<group>"; };
BC94E501263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4TrackFragmentHeaderBox.swift; sourceTree = "<group>"; };
BC94E505263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4TrackFragmentBaseMediaDecodeTimeBox.swift; sourceTree = "<group>"; };
BC94E509263FEBB60094C169 /* MP4TrackRunBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4TrackRunBox.swift; sourceTree = "<group>"; };
BC94E52C264146120094C169 /* MP4ReaderConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4ReaderConvertible.swift; sourceTree = "<group>"; };
BC9CFA9223BDE8B700917EEF /* NetStreamRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetStreamRenderer.swift; sourceTree = "<group>"; };
BCA97B85263AC0F30027213C /* MP4BoxConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4BoxConvertible.swift; sourceTree = "<group>"; };
BCA97B89263AC1830027213C /* MP4Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4Box.swift; sourceTree = "<group>"; };
BCA97B8D263AC49B0027213C /* MP4MediaHeaderBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4MediaHeaderBox.swift; sourceTree = "<group>"; };
BCA97B91263AC5FB0027213C /* MP4ChunkOffsetBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4ChunkOffsetBox.swift; sourceTree = "<group>"; };
BCA97B96263AC6980027213C /* MP4SyncSampleBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4SyncSampleBox.swift; sourceTree = "<group>"; };
BCA97B9A263AC7540027213C /* MP4EditListBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4EditListBox.swift; sourceTree = "<group>"; };
BCA97B9E263AC7D60027213C /* MP4SampleToChunkBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4SampleToChunkBox.swift; sourceTree = "<group>"; };
BCA97BA2263AC86D0027213C /* MP4TimeToSampleBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4TimeToSampleBox.swift; sourceTree = "<group>"; };
BCA97BA6263AC9880027213C /* MP4SampleSizeBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4SampleSizeBox.swift; sourceTree = "<group>"; };
BCA97BB6263ACFB30027213C /* MP4ElementaryStreamDescriptorBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4ElementaryStreamDescriptorBox.swift; sourceTree = "<group>"; };
BCA97BBA263AD0520027213C /* MP4AudioSampleEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4AudioSampleEntry.swift; sourceTree = "<group>"; };
BCA97BBE263AD1920027213C /* MP4SampleDescriptionBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4SampleDescriptionBox.swift; sourceTree = "<group>"; };
BCA97BC2263AD2280027213C /* MP4VisualSampleEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4VisualSampleEntry.swift; sourceTree = "<group>"; };
BCA97BC7263AF8F70027213C /* MP4FileHandleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MP4FileHandleTests.swift; path = Tests/MP4FileHandleTests.swift; sourceTree = SOURCE_ROOT; };
BCA97BDE263BC7770027213C /* MP4FileTypeBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4FileTypeBox.swift; sourceTree = "<group>"; };
BCA97BF3263C390E0027213C /* CustomXmlStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomXmlStringConvertible.swift; sourceTree = "<group>"; };
BCA97C09263D80F40027213C /* MP4SampleEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4SampleEntry.swift; sourceTree = "<group>"; };
BCA97C11263D8C850027213C /* MP4FullBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MP4FullBox.swift; sourceTree = "<group>"; };
BCAD0BFF263E968400ADFB80 /* MP4FileReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4FileReader.swift; sourceTree = "<group>"; };
BCAD0C10263ED28500ADFB80 /* MP4FileHandleTests2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4FileHandleTests2.swift; sourceTree = "<group>"; };
BCAD0C16263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v.m3u8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SampleVideo_360x240_5mb@m4v.m3u8"; sourceTree = "<group>"; };
BCAD0C17263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "SampleVideo_360x240_5mb@m4v"; sourceTree = "<group>"; };
BCAD0C1A263EE1D000ADFB80 /* MP4Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4Util.swift; sourceTree = "<group>"; };
BCAD0C1E263EE53A00ADFB80 /* MP4UtilTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4UtilTests.swift; sourceTree = "<group>"; };
BCAD0C20263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP4SegmentIndexBox.swift; sourceTree = "<group>"; };
BCB976D026107B1200C9A649 /* TSAdaptationExtensionField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAdaptationExtensionField.swift; sourceTree = "<group>"; };
BCB976DE26107B5600C9A649 /* TSAdaptationField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAdaptationField.swift; sourceTree = "<group>"; };
BCB9773E2621812800C9A649 /* AVCFormatStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVCFormatStream.swift; sourceTree = "<group>"; };
@ -809,16 +903,15 @@
294B2D3123785E3800CE7BDC /* CircularBuffer.swift */,
29B876631CD70AB300FC07DA /* Constants.swift */,
29B876B91CD70B3900FC07DA /* CRC32.swift */,
BCA97BF3263C390E0027213C /* CustomXmlStringConvertible.swift */,
29B876671CD70AB300FC07DA /* DataConvertible.swift */,
2976A4851D4903C300B53EF2 /* DeviceUtil.swift */,
2901A4ED1D437170002BBD23 /* DisplayLinkedQueue.swift */,
29B876BA1CD70B3900FC07DA /* EventDispatcher.swift */,
BC4DAEB02479851D005EFD57 /* FilenameUtil.swift */,
2926A9EB1DE6B71D0074E3D2 /* MachUtil.swift */,
2942424C1CF4C01300D65DCB /* MD5.swift */,
2942A4F721A9418A004E1BEE /* Running.swift */,
2943ED52232FCA7C00ED6301 /* Setting.swift */,
2926A9F01DE6EAEB0074E3D2 /* TimerDriver.swift */,
29245AEC1D3233EB00AFFB9A /* VideoGravityUtil.swift */,
);
path = Util;
@ -842,7 +935,6 @@
isa = PBXGroup;
children = (
2917CB652104CA2800F6823A /* AudioSpecificConfigTests.swift */,
290EA8941DFB619600053022 /* MP4SamplerTests.swift */,
290EA8951DFB619600053022 /* PacketizedElementaryStreamTests.swift */,
290EA8961DFB619600053022 /* ProgramSpecificTests.swift */,
290EA8971DFB619600053022 /* TSTests.swift */,
@ -869,10 +961,11 @@
isa = PBXGroup;
children = (
29B876D71CD70CE700FC07DA /* SampleVideo_360x240_5mb */,
290686041DFDC19B008EB7ED /* SampleVideo_360x240_5mb-base.mp4 */,
294637A91EC8A79F008EEC71 /* SampleVideo_360x240_5mb.flv */,
29B876D81CD70CE700FC07DA /* SampleVideo_360x240_5mb.m3u8 */,
29B876D91CD70CE700FC07DA /* SampleVideo_360x240_5mb.mp4 */,
BCAD0C17263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v */,
BCAD0C16263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v.m3u8 */,
);
path = Asset;
sourceTree = "<group>";
@ -936,6 +1029,7 @@
2960CD3E1CC0C7C900B4E877 /* HTTP */,
295FEFA91C38236900271E90 /* ISO */,
29BDE0BD1C65BC2400D6A768 /* Media */,
BCA97B84263AC0A80027213C /* MP4 */,
297C16881CC5382600117ADF /* Net */,
BC20DF2A25037454007BC608 /* PiP */,
29C0E0591C2EB00A009DD8E8 /* RTMP */,
@ -976,8 +1070,6 @@
29B8767D1CD70AE800FC07DA /* AudioSpecificConfig.swift */,
29B8767E1CD70AE800FC07DA /* AVCConfigurationRecord.swift */,
BCB9773E2621812800C9A649 /* AVCFormatStream.swift */,
29798E511CE5DF1900F5CBD0 /* MP4Reader.swift */,
292D8A321D8B293300DBECE2 /* MP4Sampler.swift */,
29B8767F1CD70AE800FC07DA /* NALUnit.swift */,
29B876801CD70AE800FC07DA /* PacketizedElementaryStream.swift */,
29B876811CD70AE800FC07DA /* ProgramSpecific.swift */,
@ -1067,6 +1159,7 @@
291C2AD21CE9FF48006F042B /* Core */,
291C2AD31CE9FF68006F042B /* HTTP */,
291C2ACF1CE9FF2B006F042B /* ISO */,
BCA97BC6263AF3E20027213C /* MP4 */,
BCC9E9072636FF5300948774 /* Net */,
291C2ACE1CE9FF25006F042B /* RTMP */,
291C2AD01CE9FF33006F042B /* Util */,
@ -1223,6 +1316,48 @@
path = PiP;
sourceTree = "<group>";
};
BCA97B84263AC0A80027213C /* MP4 */ = {
isa = PBXGroup;
children = (
BCA97BBA263AD0520027213C /* MP4AudioSampleEntry.swift */,
BCA97B89263AC1830027213C /* MP4Box.swift */,
BCA97B85263AC0F30027213C /* MP4BoxConvertible.swift */,
BCA97B91263AC5FB0027213C /* MP4ChunkOffsetBox.swift */,
BCA97B9A263AC7540027213C /* MP4EditListBox.swift */,
BCA97BB6263ACFB30027213C /* MP4ElementaryStreamDescriptorBox.swift */,
BCAD0BFF263E968400ADFB80 /* MP4FileReader.swift */,
BCA97BDE263BC7770027213C /* MP4FileTypeBox.swift */,
BCA97C11263D8C850027213C /* MP4FullBox.swift */,
BCA97B8D263AC49B0027213C /* MP4MediaHeaderBox.swift */,
BC94E4FD263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift */,
292D8A321D8B293300DBECE2 /* MP4Reader.swift */,
BC94E52C264146120094C169 /* MP4ReaderConvertible.swift */,
BCA97BBE263AD1920027213C /* MP4SampleDescriptionBox.swift */,
BCA97C09263D80F40027213C /* MP4SampleEntry.swift */,
BCA97BA6263AC9880027213C /* MP4SampleSizeBox.swift */,
BCA97B9E263AC7D60027213C /* MP4SampleToChunkBox.swift */,
BCAD0C20263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift */,
BCA97B96263AC6980027213C /* MP4SyncSampleBox.swift */,
BCA97BA2263AC86D0027213C /* MP4TimeToSampleBox.swift */,
BC94E505263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift */,
BC94E501263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift */,
BC94E509263FEBB60094C169 /* MP4TrackRunBox.swift */,
BCAD0C1A263EE1D000ADFB80 /* MP4Util.swift */,
BCA97BC2263AD2280027213C /* MP4VisualSampleEntry.swift */,
);
path = MP4;
sourceTree = "<group>";
};
BCA97BC6263AF3E20027213C /* MP4 */ = {
isa = PBXGroup;
children = (
BCA97BC7263AF8F70027213C /* MP4FileHandleTests.swift */,
BCAD0C10263ED28500ADFB80 /* MP4FileHandleTests2.swift */,
BCAD0C1E263EE53A00ADFB80 /* MP4UtilTests.swift */,
);
path = MP4;
sourceTree = "<group>";
};
BCC9E9072636FF5300948774 /* Net */ = {
isa = PBXGroup;
children = (
@ -1539,11 +1674,12 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BCAD0C19263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v in Resources */,
294637AA1EC8A79F008EEC71 /* SampleVideo_360x240_5mb.flv in Resources */,
29798E751CE614FE00F5CBD0 /* SampleVideo_360x240_5mb in Resources */,
29798E761CE614FE00F5CBD0 /* SampleVideo_360x240_5mb.m3u8 in Resources */,
290686051DFDC19B008EB7ED /* SampleVideo_360x240_5mb-base.mp4 in Resources */,
29798E771CE614FE00F5CBD0 /* SampleVideo_360x240_5mb.mp4 in Resources */,
BCAD0C18263ED67F00ADFB80 /* SampleVideo_360x240_5mb@m4v.m3u8 in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1636,13 +1772,17 @@
buildActionMask = 2147483647;
files = (
295891161EEB8DFC00CE51E1 /* FLVTagType.swift in Sources */,
BCA97C0A263D80F40027213C /* MP4SampleEntry.swift in Sources */,
29B876B11CD70B2800FC07DA /* RTMPMessage.swift in Sources */,
2941746B22D069B300A2944F /* AudioEffect.swift in Sources */,
BCB9773F2621812800C9A649 /* AVCFormatStream.swift in Sources */,
295891011EEB7A8B00CE51E1 /* ScalingMode.swift in Sources */,
BCA97BF0263C31020027213C /* MP4SampleDescriptionBox.swift in Sources */,
BC83A4732403D83B006BDE06 /* VTCompressionSession+Extension.swift in Sources */,
2943ED53232FCA7C00ED6301 /* Setting.swift in Sources */,
BC3FA38C2413AEDA009C83D3 /* AVFoundation+Extension.swift in Sources */,
BCAD0C00263E968400ADFB80 /* MP4FileReader.swift in Sources */,
BCA97B8A263AC1830027213C /* MP4Box.swift in Sources */,
2915EC4D1D85BB8C00621092 /* RTMPTSocket.swift in Sources */,
BCFB355C24FAB29B00DC5108 /* HKPictureInPicureController.swift in Sources */,
2958910A1EEB8D1800CE51E1 /* FLVReader.swift in Sources */,
@ -1663,10 +1803,11 @@
29AF3FCF1D7C744C00E41212 /* NetStream.swift in Sources */,
294B2D3223785E3800CE7BDC /* CircularBuffer.swift in Sources */,
2958910E1EEB8D3C00CE51E1 /* FLVVideoCodec.swift in Sources */,
BCA97BFC263C4F990027213C /* MP4EditListBox.swift in Sources */,
BCA97C02263C599D0027213C /* MP4SyncSampleBox.swift in Sources */,
299B13271D3B751400A1E8F5 /* HKView.swift in Sources */,
BC44A1A923D31E92002D4297 /* AudioCodec.AudioBuffer.swift in Sources */,
BC20DF38250377A3007BC608 /* ScreenCaptureSession.swift in Sources */,
2926A9EC1DE6B71E0074E3D2 /* MachUtil.swift in Sources */,
29B876AF1CD70B2800FC07DA /* RTMPChunk.swift in Sources */,
29D3D4CF1ED04C4C00DD4AA6 /* VideoIOComponent+Extension.swift in Sources */,
29B876841CD70AE800FC07DA /* AVCConfigurationRecord.swift in Sources */,
@ -1674,6 +1815,7 @@
BC9CFA9323BDE8B700917EEF /* NetStreamRenderer.swift in Sources */,
29B8769B1CD70B1100FC07DA /* MIME.swift in Sources */,
29B8769C1CD70B1100FC07DA /* NetClient.swift in Sources */,
BC94E530264146540094C169 /* MP4ReaderConvertible.swift in Sources */,
29B876871CD70AE800FC07DA /* ProgramSpecific.swift in Sources */,
BC4C9EAF23F2E736004A14F2 /* AudioStreamBasicDescription+Extension.swift in Sources */,
BC558268240BB40E00011AC0 /* RTMPStreamInfo.swift in Sources */,
@ -1684,10 +1826,11 @@
29B876B61CD70B2800FC07DA /* RTMPStream.swift in Sources */,
BC566F6E25D2ECC500573C4C /* HLSService.swift in Sources */,
29EA87EA1E79A3B70043A5F8 /* CMBlockBuffer+Extension.swift in Sources */,
2926A9F31DE6F08F0074E3D2 /* TimerDriver.swift in Sources */,
BCA97C12263D8C850027213C /* MP4FullBox.swift in Sources */,
2976A4811D49025B00B53EF2 /* IOComponent.swift in Sources */,
293B42E92340B4840086F973 /* RTMPObjectEncoding.swift in Sources */,
2976A47E1D48C5C700B53EF2 /* AVRecorder.swift in Sources */,
BC94E4FE263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift in Sources */,
29B876B21CD70B2800FC07DA /* RTMPMuxer.swift in Sources */,
2958912E1EEB8F4100CE51E1 /* FLVSoundType.swift in Sources */,
BC0D236D26331BAB001DDA0C /* NetSocket.CircularBuffer.swift in Sources */,
@ -1695,10 +1838,14 @@
29B876851CD70AE800FC07DA /* NALUnit.swift in Sources */,
29EA87ED1E79A3E30043A5F8 /* CVPixelBuffer+Extension.swift in Sources */,
2958912A1EEB8F1D00CE51E1 /* FLVSoundSize.swift in Sources */,
BCA97BF4263C390E0027213C /* CustomXmlStringConvertible.swift in Sources */,
29EA87DC1E79A0460043A5F8 /* Data+Extension.swift in Sources */,
BCA97C1B263DAD070027213C /* MP4ElementaryStreamDescriptorBox.swift in Sources */,
BC94E502263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift in Sources */,
29DF20622312A3DD004057C3 /* RTMPNWSocket.swift in Sources */,
29B876BD1CD70B3900FC07DA /* CRC32.swift in Sources */,
29EA87E61E79A2780043A5F8 /* CMAudioFormatDescription+Extension.swift in Sources */,
BC94E506263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift in Sources */,
29B876B51CD70B2800FC07DA /* RTMPSocket.swift in Sources */,
29B876AB1CD70B2800FC07DA /* AMF0Serializer.swift in Sources */,
29B8765B1CD70A7900FC07DA /* AudioCodec.swift in Sources */,
@ -1706,10 +1853,16 @@
297E69122324E38800D418AB /* AudioCodec.Destination.swift in Sources */,
2942A4F821A9418A004E1BEE /* Running.swift in Sources */,
29F6F4851DFB83E200920A3A /* RTMPHandshake.swift in Sources */,
BCA97BF9263C4B8F0027213C /* MP4ChunkOffsetBox.swift in Sources */,
BCA97BFD263C54550027213C /* MP4SampleToChunkBox.swift in Sources */,
29EA87DF1E79A0810043A5F8 /* CMSampleBuffer+Extension.swift in Sources */,
29EA87E21E79A1E90043A5F8 /* CMVideoFormatDescription+Extension.swift in Sources */,
29D3D4D11ED04D1200DD4AA6 /* NetStream+Extension.swift in Sources */,
BCA97BE4263C095C0027213C /* MP4TimeToSampleBox.swift in Sources */,
BCAD0C21263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift in Sources */,
BCAD0C1B263EE1D000ADFB80 /* MP4Util.swift in Sources */,
29EA87D81E79A0090043A5F8 /* URL+Extension.swift in Sources */,
BCA97B86263AC0F30027213C /* MP4BoxConvertible.swift in Sources */,
29B876BC1CD70B3900FC07DA /* ByteArray.swift in Sources */,
29B876831CD70AE800FC07DA /* AudioSpecificConfig.swift in Sources */,
295891121EEB8D7200CE51E1 /* FLVFrameType.swift in Sources */,
@ -1721,14 +1874,17 @@
29C263181D00804A0098D4EF /* GLHKView.swift in Sources */,
BCFB355E24FAB2D200DC5108 /* HKPictureInPicureControllerImpl.swift in Sources */,
29B876881CD70AE800FC07DA /* TSPacket.swift in Sources */,
BCA97BE8263C27070027213C /* MP4SampleSizeBox.swift in Sources */,
BCA97C18263DA5060027213C /* MP4AudioSampleEntry.swift in Sources */,
29B876BE1CD70B3900FC07DA /* EventDispatcher.swift in Sources */,
29B8769D1CD70B1100FC07DA /* NetService.swift in Sources */,
29B8769E1CD70B1100FC07DA /* NetSocket.swift in Sources */,
2958911A1EEB8E3F00CE51E1 /* FLVAudioCodec.swift in Sources */,
295891261EEB8EF300CE51E1 /* FLVAACPacket.swift in Sources */,
29B876791CD70ACE00FC07DA /* HTTPStream.swift in Sources */,
292D8A331D8B293300DBECE2 /* MP4Sampler.swift in Sources */,
29798E521CE5DF1A00F5CBD0 /* MP4Reader.swift in Sources */,
BCA97C15263D93DB0027213C /* MP4VisualSampleEntry.swift in Sources */,
292D8A331D8B293300DBECE2 /* MP4Reader.swift in Sources */,
BCA97BE5263C0B8A0027213C /* MP4FileTypeBox.swift in Sources */,
295747911E37AC1000EF056E /* RTMPStreamDelegate.swift in Sources */,
29B876AC1CD70B2800FC07DA /* AMF3Serializer.swift in Sources */,
2916196C1E7F0768009FB344 /* CMFormatDescription+Extension.swift in Sources */,
@ -1741,7 +1897,9 @@
29245AEF1D32348400AFFB9A /* VideoGravityUtil.swift in Sources */,
29FD1B5022FF13190095A0BE /* VTSessionPropertyKey.swift in Sources */,
29B876901CD70AFE00FC07DA /* AudioIOComponent.swift in Sources */,
BC94E50A263FEBB60094C169 /* MP4TrackRunBox.swift in Sources */,
BCB976D126107B1200C9A649 /* TSAdaptationExtensionField.swift in Sources */,
BCA97C05263C61940027213C /* MP4MediaHeaderBox.swift in Sources */,
29B876771CD70ACE00FC07DA /* HTTPResponse.swift in Sources */,
29B8765C1CD70A7900FC07DA /* H264Decoder.swift in Sources */,
);
@ -1757,13 +1915,15 @@
290EA8A91DFB61E700053022 /* ByteArrayTests.swift in Sources */,
295018221FFA1C9D00358E10 /* SinWaveUtil.swift in Sources */,
294637A81EC89BC9008EEC71 /* Config.swift in Sources */,
290EA8981DFB619600053022 /* MP4SamplerTests.swift in Sources */,
295018201FFA1BD700358E10 /* AudioConverterTests.swift in Sources */,
BC94E53B2641984F0094C169 /* MP4FileHandleTests.swift in Sources */,
290EA8AC1DFB61E700053022 /* MD5Tests.swift in Sources */,
290EA8A01DFB61B100053022 /* ASClassTests.swift in Sources */,
BC94E53A264192B00094C169 /* MP4FileHandleTests2.swift in Sources */,
2917CB662104CA2800F6823A /* AudioSpecificConfigTests.swift in Sources */,
290EA8AB1DFB61E700053022 /* EventDispatcherTests.swift in Sources */,
290EA8901DFB616000053022 /* Foundation+ExtensionTests.swift in Sources */,
BCAD0C1F263EE53A00ADFB80 /* MP4UtilTests.swift in Sources */,
290EA8991DFB619600053022 /* PacketizedElementaryStreamTests.swift in Sources */,
29F97F242336A4FA00A4C317 /* SettingTests.swift in Sources */,
290EA8911DFB616000053022 /* SwiftCore+ExtensionTests.swift in Sources */,
@ -1786,13 +1946,17 @@
buildActionMask = 2147483647;
files = (
29B876EC1CD70D5900FC07DA /* AudioCodec.swift in Sources */,
BCA97C0B263D80F40027213C /* MP4SampleEntry.swift in Sources */,
29B876ED1CD70D5900FC07DA /* H264Decoder.swift in Sources */,
BC8E32E42532F3700087DF49 /* GLHKView.swift in Sources */,
BCB977402621812800C9A649 /* AVCFormatStream.swift in Sources */,
29B876EE1CD70D5900FC07DA /* H264Encoder.swift in Sources */,
BCA97BF1263C31020027213C /* MP4SampleDescriptionBox.swift in Sources */,
29EA87EB1E79A3B70043A5F8 /* CMBlockBuffer+Extension.swift in Sources */,
29B876F01CD70D5900FC07DA /* Constants.swift in Sources */,
29EA87D91E79A0090043A5F8 /* URL+Extension.swift in Sources */,
BCAD0C01263E968400ADFB80 /* MP4FileReader.swift in Sources */,
BCA97B8B263AC1830027213C /* MP4Box.swift in Sources */,
2943ED54232FCA7C00ED6301 /* Setting.swift in Sources */,
292AC17C1CF4C871004F5730 /* MD5.swift in Sources */,
2958910B1EEB8D1800CE51E1 /* FLVReader.swift in Sources */,
@ -1813,43 +1977,50 @@
29EA87D61E799F6A0043A5F8 /* Mirror+Extension.swift in Sources */,
BC7A23F525171C8F0089F77C /* MTHKView.swift in Sources */,
297E69132324E38800D418AB /* AudioCodec.Destination.swift in Sources */,
BCA97BFB263C4F980027213C /* MP4EditListBox.swift in Sources */,
BCA97C01263C599C0027213C /* MP4SyncSampleBox.swift in Sources */,
29B876F81CD70D5900FC07DA /* HTTPService.swift in Sources */,
29B876F91CD70D5900FC07DA /* HTTPStream.swift in Sources */,
296543631D62FE9000734698 /* HKView-macOS.swift in Sources */,
29B876FA1CD70D5900FC07DA /* M3U.swift in Sources */,
292D8A341D8B294900DBECE2 /* MP4Sampler.swift in Sources */,
292D8A341D8B294900DBECE2 /* MP4Reader.swift in Sources */,
29B876FD1CD70D5A00FC07DA /* AudioSpecificConfig.swift in Sources */,
2958911F1EEB8E9600CE51E1 /* FLVSoundRate.swift in Sources */,
2941746C22D069B300A2944F /* AudioEffect.swift in Sources */,
BC9CFA9423BDE8B700917EEF /* NetStreamRenderer.swift in Sources */,
BC20DF31250374A7007BC608 /* HKPictureInPicureController.swift in Sources */,
296242631D8DBA8C00C451A3 /* TSReader.swift in Sources */,
BC94E52F264146540094C169 /* MP4ReaderConvertible.swift in Sources */,
29B876FE1CD70D5A00FC07DA /* AVCConfigurationRecord.swift in Sources */,
295891171EEB8DFC00CE51E1 /* FLVTagType.swift in Sources */,
294852571D852499002DE492 /* RTMPTSocket.swift in Sources */,
BC83A4742403D83B006BDE06 /* VTCompressionSession+Extension.swift in Sources */,
29245AEE1D32347E00AFFB9A /* VideoGravityUtil.swift in Sources */,
292D8A351D8B294E00DBECE2 /* MP4Reader.swift in Sources */,
29B876FF1CD70D5A00FC07DA /* NALUnit.swift in Sources */,
29B877001CD70D5A00FC07DA /* PacketizedElementaryStream.swift in Sources */,
BC566F6F25D2ECC500573C4C /* HLSService.swift in Sources */,
295891131EEB8D7200CE51E1 /* FLVFrameType.swift in Sources */,
29B877011CD70D5A00FC07DA /* ProgramSpecific.swift in Sources */,
BCA97C13263D8C850027213C /* MP4FullBox.swift in Sources */,
295891271EEB8EF300CE51E1 /* FLVAACPacket.swift in Sources */,
29B877021CD70D5A00FC07DA /* TSPacket.swift in Sources */,
296242641D8DBA9000C451A3 /* TSWriter.swift in Sources */,
2958912F1EEB8F4100CE51E1 /* FLVSoundType.swift in Sources */,
BC94E4FF263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift in Sources */,
29B877031CD70D5A00FC07DA /* AudioIOComponent.swift in Sources */,
BC0D236E26331BAB001DDA0C /* NetSocket.CircularBuffer.swift in Sources */,
29B877051CD70D5A00FC07DA /* AVMixer.swift in Sources */,
294B2D3323785E3800CE7BDC /* CircularBuffer.swift in Sources */,
2992D1541ED04A2C008D9DC1 /* VideoIOComponent+Extension-macOS.swift in Sources */,
2926A9EF1DE6B83F0074E3D2 /* MachUtil.swift in Sources */,
2976A47F1D48FD6900B53EF2 /* AVRecorder.swift in Sources */,
BCA97BF5263C390E0027213C /* CustomXmlStringConvertible.swift in Sources */,
BC44A1AA23D31E92002D4297 /* AudioCodec.AudioBuffer.swift in Sources */,
BCA97C1C263DAD070027213C /* MP4ElementaryStreamDescriptorBox.swift in Sources */,
BC94E503263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift in Sources */,
29B877071CD70D5A00FC07DA /* SoundTransform.swift in Sources */,
29B877081CD70D5A00FC07DA /* VideoIOComponent.swift in Sources */,
294CC9B422D9BEC000F9DD5C /* DisplayLink-macOS.swift in Sources */,
BC94E507263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift in Sources */,
BC20DF2D25037498007BC608 /* HKPictureInPicureControllerPosition.swift in Sources */,
29B877091CD70D5A00FC07DA /* VideoEffect.swift in Sources */,
29B8770A1CD70D5A00FC07DA /* MIME.swift in Sources */,
@ -1857,9 +2028,15 @@
29EA87EE1E79A3E30043A5F8 /* CVPixelBuffer+Extension.swift in Sources */,
29B8770C1CD70D5A00FC07DA /* NetService.swift in Sources */,
2958911B1EEB8E3F00CE51E1 /* FLVAudioCodec.swift in Sources */,
BCA97BF8263C4B8F0027213C /* MP4ChunkOffsetBox.swift in Sources */,
BCA97BFE263C54560027213C /* MP4SampleToChunkBox.swift in Sources */,
296543611D62FE7100734698 /* GLHKView-macOS.swift in Sources */,
293B42EA2340B4840086F973 /* RTMPObjectEncoding.swift in Sources */,
29DC17B421D0CC0600E26CED /* Atomic.swift in Sources */,
BCA97BE3263C095C0027213C /* MP4TimeToSampleBox.swift in Sources */,
BCAD0C22263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift in Sources */,
BCAD0C1C263EE1D000ADFB80 /* MP4Util.swift in Sources */,
BCA97B87263AC0F30027213C /* MP4BoxConvertible.swift in Sources */,
BC558269240BB40E00011AC0 /* RTMPStreamInfo.swift in Sources */,
29EA87E01E79A0810043A5F8 /* CMSampleBuffer+Extension.swift in Sources */,
29B8770D1CD70D5A00FC07DA /* NetSocket.swift in Sources */,
@ -1872,13 +2049,17 @@
29B877121CD70D5A00FC07DA /* RTMPChunk.swift in Sources */,
29AF3FD01D7C745200E41212 /* NetStream.swift in Sources */,
29F6F4861DFB862400920A3A /* RTMPHandshake.swift in Sources */,
BCA97BE9263C27080027213C /* MP4SampleSizeBox.swift in Sources */,
BCA97C19263DA5070027213C /* MP4AudioSampleEntry.swift in Sources */,
29B877131CD70D5A00FC07DA /* RTMPConnection.swift in Sources */,
2958910F1EEB8D3C00CE51E1 /* FLVVideoCodec.swift in Sources */,
295891021EEB7AFC00CE51E1 /* ScalingMode.swift in Sources */,
29B877141CD70D5A00FC07DA /* RTMPMessage.swift in Sources */,
29B877151CD70D5A00FC07DA /* RTMPMuxer.swift in Sources */,
29EA87E31E79A1E90043A5F8 /* CMVideoFormatDescription+Extension.swift in Sources */,
BCA97C16263D93DC0027213C /* MP4VisualSampleEntry.swift in Sources */,
29B877171CD70D5A00FC07DA /* RTMPSharedObject.swift in Sources */,
BCA97BE6263C0B8A0027213C /* MP4FileTypeBox.swift in Sources */,
BC3FA38B2413AEDA009C83D3 /* AVFoundation+Extension.swift in Sources */,
29B877181CD70D5A00FC07DA /* RTMPSocket.swift in Sources */,
29EA87DD1E79A0460043A5F8 /* Data+Extension.swift in Sources */,
@ -1889,9 +2070,10 @@
29EA87DA1E79A00E0043A5F8 /* ExpressibleByIntegerLiteral+Extension.swift in Sources */,
29D0E3681DD4CE3700863B3B /* AnyUtil.swift in Sources */,
29B8771C1CD70D5A00FC07DA /* CRC32.swift in Sources */,
2926A9F21DE6F08E0074E3D2 /* TimerDriver.swift in Sources */,
2958912B1EEB8F1D00CE51E1 /* FLVSoundSize.swift in Sources */,
BC94E50B263FEBB60094C169 /* MP4TrackRunBox.swift in Sources */,
BCB976D226107B1200C9A649 /* TSAdaptationExtensionField.swift in Sources */,
BCA97C04263C61940027213C /* MP4MediaHeaderBox.swift in Sources */,
29B8771D1CD70D5A00FC07DA /* EventDispatcher.swift in Sources */,
2901A4EF1D437662002BBD23 /* DisplayLinkedQueue.swift in Sources */,
);
@ -1929,7 +2111,9 @@
files = (
BC55826A240BB40E00011AC0 /* RTMPStreamInfo.swift in Sources */,
294B2D3423785E3800CE7BDC /* CircularBuffer.swift in Sources */,
BC94E504263FE8400094C169 /* MP4TrackFragmentHeaderBox.swift in Sources */,
BC20DF32250374A8007BC608 /* HKPictureInPicureController.swift in Sources */,
BCA97BE7263C0B8B0027213C /* MP4FileTypeBox.swift in Sources */,
29EB3E1F1ED059F7001CAE8B /* RTMPChunk.swift in Sources */,
29EB3DFA1ED057A1001CAE8B /* CMVideoFormatDescription+Extension.swift in Sources */,
29EB3E071ED05867001CAE8B /* NALUnit.swift in Sources */,
@ -1940,8 +2124,11 @@
29EB3E321ED05A2C001CAE8B /* ByteArray.swift in Sources */,
29EB3E191ED05898001CAE8B /* NetSocket.swift in Sources */,
29EB3E101ED0587F001CAE8B /* AVRecorder.swift in Sources */,
BCA97C17263D93DC0027213C /* MP4VisualSampleEntry.swift in Sources */,
2958910C1EEB8D1800CE51E1 /* FLVReader.swift in Sources */,
29EB3E0F1ED0587C001CAE8B /* AVMixer.swift in Sources */,
BC94E50C263FEBB60094C169 /* MP4TrackRunBox.swift in Sources */,
BC94E508263FEA7F0094C169 /* MP4TrackFragmentBaseMediaDecodeTimeBox.swift in Sources */,
BC7A23F625171C8F0089F77C /* MTHKView.swift in Sources */,
29EB3E1A1ED0589B001CAE8B /* NetStream.swift in Sources */,
29EB3E211ED059FB001CAE8B /* RTMPHandshake.swift in Sources */,
@ -1950,20 +2137,23 @@
29EB3E351ED05A33001CAE8B /* DeviceUtil.swift in Sources */,
29DC17B521D0CC0600E26CED /* Atomic.swift in Sources */,
BC8E32E92532F3710087DF49 /* GLHKView.swift in Sources */,
BCA97BFF263C54560027213C /* MP4SampleToChunkBox.swift in Sources */,
BCA97B8C263AC1830027213C /* MP4Box.swift in Sources */,
BC44A1AB23D31E92002D4297 /* AudioCodec.AudioBuffer.swift in Sources */,
29EB3E261ED05A07001CAE8B /* RTMPStream.swift in Sources */,
BCA97BE2263C095B0027213C /* MP4TimeToSampleBox.swift in Sources */,
29DF20642312A3DD004057C3 /* RTMPNWSocket.swift in Sources */,
29EB3E111ED05881001CAE8B /* IOComponent.swift in Sources */,
29EB3E3A1ED05A45001CAE8B /* TimerDriver.swift in Sources */,
BCA97BF2263C31020027213C /* MP4SampleDescriptionBox.swift in Sources */,
29EB3E131ED05887001CAE8B /* SoundTransform.swift in Sources */,
29EB3DF81ED05799001CAE8B /* Mirror+Extension.swift in Sources */,
BCA97C1A263DA5070027213C /* MP4AudioSampleEntry.swift in Sources */,
BC83A4752403D83B006BDE06 /* VTCompressionSession+Extension.swift in Sources */,
BC9CFA9523BDE8B700917EEF /* NetStreamRenderer.swift in Sources */,
29EB3E031ED0585D001CAE8B /* AudioSpecificConfig.swift in Sources */,
29EB3E3B1ED05A48001CAE8B /* VideoGravityUtil.swift in Sources */,
29EB3E141ED05889001CAE8B /* VideoIOComponent.swift in Sources */,
29EB3E221ED059FD001CAE8B /* RTMPMessage.swift in Sources */,
29EB3E371ED05A38001CAE8B /* MachUtil.swift in Sources */,
29EB3E001ED05854001CAE8B /* HTTPService.swift in Sources */,
29EB3DFF1ED05852001CAE8B /* HTTPResponse.swift in Sources */,
29EB3E021ED05858001CAE8B /* M3U.swift in Sources */,
@ -1972,11 +2162,15 @@
295891101EEB8D3C00CE51E1 /* FLVVideoCodec.swift in Sources */,
295891031EEB7AFC00CE51E1 /* ScalingMode.swift in Sources */,
29EB3DFD1ED05847001CAE8B /* CVPixelBuffer+Extension.swift in Sources */,
BCA97BF6263C390E0027213C /* CustomXmlStringConvertible.swift in Sources */,
BC94E52E264146530094C169 /* MP4ReaderConvertible.swift in Sources */,
BC20DF2E25037498007BC608 /* HKPictureInPicureControllerPosition.swift in Sources */,
BCB976E126107B5600C9A649 /* TSAdaptationField.swift in Sources */,
BCAD0C02263E968400ADFB80 /* MP4FileReader.swift in Sources */,
BCA97C00263C599C0027213C /* MP4SyncSampleBox.swift in Sources */,
2941746D22D069B300A2944F /* AudioEffect.swift in Sources */,
29EB3E151ED0588C001CAE8B /* VideoEffect.swift in Sources */,
29EB3E061ED05865001CAE8B /* MP4Sampler.swift in Sources */,
29EB3E061ED05865001CAE8B /* MP4Reader.swift in Sources */,
29EB3E041ED05860001CAE8B /* AVCConfigurationRecord.swift in Sources */,
29EB3DEF1ED05766001CAE8B /* H264Decoder.swift in Sources */,
29EB3DF71ED05797001CAE8B /* URL+Extension.swift in Sources */,
@ -1984,22 +2178,28 @@
29EB3E0B1ED05871001CAE8B /* TSReader.swift in Sources */,
297E69142324E38800D418AB /* AudioCodec.Destination.swift in Sources */,
29EB3DF51ED05779001CAE8B /* CMFormatDescription+Extension.swift in Sources */,
BCA97C0C263D80F40027213C /* MP4SampleEntry.swift in Sources */,
29EB3E381ED05A41001CAE8B /* MD5.swift in Sources */,
29EB3E271ED05A09001CAE8B /* RTMPStreamDelegate.swift in Sources */,
295891141EEB8D7200CE51E1 /* FLVFrameType.swift in Sources */,
BC20DF302503749D007BC608 /* HKPictureInPicureControllerImpl.swift in Sources */,
29EB3E1B1ED0589F001CAE8B /* AMF0Serializer.swift in Sources */,
BCA97C03263C61930027213C /* MP4MediaHeaderBox.swift in Sources */,
29EB3E0C1ED05874001CAE8B /* TSWriter.swift in Sources */,
295891181EEB8DFC00CE51E1 /* FLVTagType.swift in Sources */,
BCA97BFA263C4F980027213C /* MP4EditListBox.swift in Sources */,
29EB3DF91ED0579C001CAE8B /* ExpressibleByIntegerLiteral+Extension.swift in Sources */,
29EB3E201ED059F9001CAE8B /* RTMPConnection.swift in Sources */,
29EB3E331ED05A2E001CAE8B /* DisplayLinkedQueue.swift in Sources */,
BCA97BEA263C27080027213C /* MP4SampleSizeBox.swift in Sources */,
29EB3E1D1ED058A5001CAE8B /* ASClass.swift in Sources */,
29EB3E251ED05A04001CAE8B /* RTMPSocket.swift in Sources */,
29EB3E311ED05A29001CAE8B /* AnyUtil.swift in Sources */,
29EB3E1C1ED058A2001CAE8B /* AMF3Serializer.swift in Sources */,
29EB3DEE1ED05763001CAE8B /* AudioCodec.swift in Sources */,
29EB3E241ED05A02001CAE8B /* RTMPSharedObject.swift in Sources */,
BCA97C14263D8C850027213C /* MP4FullBox.swift in Sources */,
BCA97BF7263C4B8E0027213C /* MP4ChunkOffsetBox.swift in Sources */,
29EB3E231ED059FF001CAE8B /* RTMPMuxer.swift in Sources */,
29EB3DF11ED0576C001CAE8B /* Constants.swift in Sources */,
292F6DB11EEBB2040097EDBE /* AVFoundation+Extension.swift in Sources */,
@ -2007,8 +2207,10 @@
29EB3E171ED05893001CAE8B /* NetClient.swift in Sources */,
BC0D236F26331BAB001DDA0C /* NetSocket.CircularBuffer.swift in Sources */,
BCB976D326107B1200C9A649 /* TSAdaptationExtensionField.swift in Sources */,
BCA97C1D263DAD080027213C /* MP4ElementaryStreamDescriptorBox.swift in Sources */,
BCB977412621812800C9A649 /* AVCFormatStream.swift in Sources */,
BC4C9EB123F2E736004A14F2 /* AudioStreamBasicDescription+Extension.swift in Sources */,
BCAD0C23263EFCEF00ADFB80 /* MP4SegmentIndexBox.swift in Sources */,
2958912C1EEB8F1D00CE51E1 /* FLVSoundSize.swift in Sources */,
BC4DAEB32479851D005EFD57 /* FilenameUtil.swift in Sources */,
295891241EEB8EC500CE51E1 /* FLVAVCPacketType.swift in Sources */,
@ -2024,10 +2226,12 @@
29EB3E341ED05A30001CAE8B /* CRC32.swift in Sources */,
BC566F7025D2ECC500573C4C /* HLSService.swift in Sources */,
29FD1B5222FF13190095A0BE /* VTSessionPropertyKey.swift in Sources */,
BCAD0C1D263EE1D000ADFB80 /* MP4Util.swift in Sources */,
29EB3E181ED05896001CAE8B /* NetService.swift in Sources */,
295891281EEB8EF300CE51E1 /* FLVAACPacket.swift in Sources */,
29EB3E051ED05862001CAE8B /* MP4Reader.swift in Sources */,
BCA97B88263AC0F30027213C /* MP4BoxConvertible.swift in Sources */,
2958911C1EEB8E3F00CE51E1 /* FLVAudioCodec.swift in Sources */,
BC94E500263FE6B80094C169 /* MP4MovieFragmentHeaderBox.swift in Sources */,
29EB3DF61ED0577C001CAE8B /* CMSampleBuffer+Extension.swift in Sources */,
BC20DF36250374EA007BC608 /* HKPictureInPicureController+Extension.swift in Sources */,
29EB3E281ED05A0C001CAE8B /* RTMPTSocket.swift in Sources */,

View File

@ -26,6 +26,7 @@ let package = Package(
"HTTP",
"ISO",
"Media",
"MP4",
"Net",
"PiP",
"RTMP",

View File

@ -1,723 +0,0 @@
import AVFoundation
class MP4Box {
static func create(_ data: Data) throws -> MP4Box {
let buffer = ByteArray(data: data)
let size: UInt32 = try buffer.readUInt32()
let type: String = try buffer.readUTF8Bytes(4)
buffer.clear()
switch type {
case "moov", "trak", "mdia", "minf", "stbl", "edts":
return MP4ContainerBox(size: size, type: type)
case "mp4v", "s263", "avc1":
return MP4VisualSampleEntryBox(size: size, type: type)
case "mvhd", "mdhd":
return MP4MediaHeaderBox(size: size, type: type)
case "mp4a":
return MP4AudioSampleEntryBox(size: size, type: type)
case "esds":
return MP4ElementaryStreamDescriptorBox(size: size, type: type)
case "stts":
return MP4TimeToSampleBox(size: size, type: type)
case "stss":
return MP4SyncSampleBox(size: size, type: type)
case "stsd":
return MP4SampleDescriptionBox(size: size, type: type)
case "stco":
return MP4ChunkOffsetBox(size: size, type: type)
case "stsc":
return MP4SampleToChunkBox(size: size, type: type)
case "stsz":
return MP4SampleSizeBox(size: size, type: type)
case "elst":
return MP4EditListBox(size: size, type: type)
default:
return MP4Box(size: size, type: type)
}
}
var leafNode: Bool {
false
}
fileprivate(set) var type: String = "undf"
fileprivate(set) var size: UInt32 = 0
fileprivate(set) var offset: UInt64 = 0
fileprivate(set) var parent: MP4Box?
init() {
}
init(size: UInt32, type: String) {
self.size = size
self.type = type
}
func load(_ fileHandle: FileHandle) throws -> UInt32 {
if size == 0 {
size = UInt32(fileHandle.seekToEndOfFile() - offset)
return size
}
fileHandle.seek(toFileOffset: offset + UInt64(size))
return size
}
func getBoxes(byName: String) -> [MP4Box] {
[]
}
func clear() {
parent = nil
}
func create(_ data: Data, offset: UInt32) throws -> MP4Box {
let box: MP4Box = try MP4Box.create(data)
box.parent = self
box.offset = self.offset + UInt64(offset)
return box
}
}
extension MP4Box: CustomDebugStringConvertible {
// MARK: CustomDebugStringConvertible
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
}
// MARK: -
class MP4ContainerBox: MP4Box {
fileprivate var children: [MP4Box] = []
override var leafNode: Bool {
false
}
override func load(_ file: FileHandle) throws -> UInt32 {
children.removeAll(keepingCapacity: false)
var offset: UInt32 = parent == nil ? 0 : 8
file.seek(toFileOffset: self.offset + UInt64(offset))
while size != offset {
let child: MP4Box = try create(file.readData(ofLength: 8), offset: offset)
offset += try child.load(file)
children.append(child)
}
return offset
}
override func getBoxes(byName: String) -> [MP4Box] {
var list: [MP4Box] = []
for child in children {
if byName == child.type || byName == "*" {
list.append(child)
}
if !child.leafNode {
list += child.getBoxes(byName: byName)
}
}
return list
}
override func clear() {
for child in children {
child.clear()
}
children.removeAll(keepingCapacity: false)
parent = nil
}
}
// MARK: -
final class MP4MediaHeaderBox: MP4Box {
var version: UInt8 = 0
var creationTime: UInt32 = 0
var modificationTime: UInt32 = 0
var timeScale: UInt32 = 0
var duration: UInt32 = 0
var language: UInt16 = 0
var quality: UInt16 = 0
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(size) - 8))
version = try buffer.readUInt8()
buffer.position += 3
creationTime = try buffer.readUInt32()
modificationTime = try buffer.readUInt32()
timeScale = try buffer.readUInt32()
duration = try buffer.readUInt32()
language = try buffer.readUInt16()
quality = try buffer.readUInt16()
buffer.clear()
return size
}
}
// MARK: -
final class MP4ChunkOffsetBox: MP4Box {
var entries: [UInt32] = []
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(size) - 8))
buffer.position += 4
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
entries.append(try buffer.readUInt32())
}
buffer.clear()
return size
}
}
// MARK: -
final class MP4SyncSampleBox: MP4Box {
var entries: [UInt32] = []
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
entries.removeAll(keepingCapacity: false)
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(size) - 8))
buffer.position += 4
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
entries.append(try buffer.readUInt32())
}
return size
}
}
// MARK: -
final class MP4TimeToSampleBox: MP4Box {
struct Entry: CustomDebugStringConvertible {
var sampleCount: UInt32 = 0
var sampleDuration: UInt32 = 0
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
init(sampleCount: UInt32, sampleDuration: UInt32) {
self.sampleCount = sampleCount
self.sampleDuration = sampleDuration
}
}
var entries: [Entry] = []
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
entries.removeAll(keepingCapacity: false)
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(size) - 8))
buffer.position += 4
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
entries.append(Entry(
sampleCount: try buffer.readUInt32(),
sampleDuration: try buffer.readUInt32()
))
}
return size
}
}
// MARK: -
final class MP4SampleSizeBox: MP4Box {
var entries: [UInt32] = []
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
entries.removeAll(keepingCapacity: false)
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(self.size) - 8))
buffer.position += 4
let sampleSize: UInt32 = try buffer.readUInt32()
if sampleSize != 0 {
entries.append(sampleSize)
return size
}
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
entries.append(try buffer.readUInt32())
}
buffer.clear()
return size
}
}
// MARK: -
final class MP4ElementaryStreamDescriptorBox: MP4ContainerBox {
var audioDecorderSpecificConfig = Data()
var tag: UInt8 = 0
var tagSize: UInt8 = 0
var id: UInt16 = 0
var streamDependenceFlag: UInt8 = 0
var urlFlag: UInt8 = 0
var ocrStreamFlag: UInt8 = 0
var streamPriority: UInt8 = 0
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
var tagSize: UInt8 = 0
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(self.size) - 8))
buffer.position += 4
tag = try buffer.readUInt8()
self.tagSize = try buffer.readUInt8()
if self.tagSize == 0x80 {
buffer.position += 2
self.tagSize = try buffer.readUInt8()
}
id = try buffer.readUInt16()
let data: UInt8 = try buffer.readUInt8()
streamDependenceFlag = data >> 7
urlFlag = (data >> 6) & 0x1
ocrStreamFlag = (data >> 5) & 0x1
streamPriority = data & 0x1f
if streamDependenceFlag == 1 {
let _: UInt16 = try buffer.readUInt16()
}
// Decorder Config Descriptor
let _: UInt8 = try buffer.readUInt8()
tagSize = try buffer.readUInt8()
if tagSize == 0x80 {
buffer.position += 2
tagSize = try buffer.readUInt8()
}
buffer.position += 13
// Audio Decorder Spec Info
let _: UInt8 = try buffer.readUInt8()
tagSize = try buffer.readUInt8()
if tagSize == 0x80 {
buffer.position += 2
tagSize = try buffer.readUInt8()
}
audioDecorderSpecificConfig = try buffer.readBytes(Int(tagSize))
return size
}
}
// MARK: -
final class MP4AudioSampleEntryBox: MP4ContainerBox {
var version: UInt16 = 0
var channelCount: UInt16 = 0
var sampleSize: UInt16 = 0
var compressionId: UInt16 = 0
var packetSize: UInt16 = 0
var sampleRate: UInt32 = 0
var samplesPerPacket: UInt32 = 0
var bytesPerPacket: UInt32 = 0
var bytesPerFrame: UInt32 = 0
var bytesPerSample: UInt32 = 0
var soundVersion2Data: [UInt8] = []
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(size) - 8))
buffer.position += 8
version = try buffer.readUInt16()
buffer.position += 6
channelCount = try buffer.readUInt16()
sampleSize = try buffer.readUInt16()
compressionId = try buffer.readUInt16()
packetSize = try buffer.readUInt16()
sampleRate = try buffer.readUInt32()
if type != "mlpa" {
sampleRate = sampleRate >> 16
}
if 0 < version {
samplesPerPacket = try buffer.readUInt32()
bytesPerPacket = try buffer.readUInt32()
bytesPerFrame = try buffer.readUInt32()
bytesPerSample = try buffer.readUInt32()
}
if version == 2 {
soundVersion2Data += try buffer.readBytes(20)
}
var offset = UInt32(buffer.position) + 8
fileHandle.seek(toFileOffset: self.offset + UInt64(offset))
let esds: MP4Box = try create(fileHandle.readData(ofLength: 8), offset: offset)
offset += try esds.load(fileHandle)
children.append(esds)
// skip
fileHandle.seek(toFileOffset: self.offset + UInt64(size))
return size
}
}
// MARK: -
final class MP4VisualSampleEntryBox: MP4ContainerBox {
static var dataSize: Int = 78
var width: UInt16 = 0
var height: UInt16 = 0
var hSolution: UInt32 = 0
var vSolution: UInt32 = 0
var frameCount: UInt16 = 1
var compressorname: String = ""
var depth: UInt16 = 16
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
let buffer = ByteArray(data: fileHandle.readData(ofLength: MP4VisualSampleEntryBox.dataSize))
buffer.position += 24
width = try buffer.readUInt16()
height = try buffer.readUInt16()
hSolution = try buffer.readUInt32()
vSolution = try buffer.readUInt32()
buffer.position += 4
frameCount = try buffer.readUInt16()
compressorname = try buffer.readUTF8Bytes(32)
depth = try buffer.readUInt16()
_ = try buffer.readUInt16()
buffer.clear()
var offset = UInt32(MP4VisualSampleEntryBox.dataSize + 8)
while size > offset {
let child: MP4Box = try create(fileHandle.readData(ofLength: 8), offset: offset)
offset += try child.load(fileHandle)
children.append(child)
}
return offset
}
}
// MARK: -
final class MP4SampleDescriptionBox: MP4ContainerBox {
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
children.removeAll(keepingCapacity: false)
let buffer = ByteArray(data: fileHandle.readData(ofLength: 8))
buffer.position = 4
var offset: UInt32 = 16
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
let child: MP4Box = try create(fileHandle.readData(ofLength: 8), offset: offset)
offset += try child.load(fileHandle)
children.append(child)
}
return offset
}
}
// MARK: -
final class MP4SampleToChunkBox: MP4Box {
struct Entry: CustomDebugStringConvertible {
var firstChunk: UInt32 = 0
var samplesPerChunk: UInt32 = 0
var sampleDescriptionIndex: UInt32 = 0
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
init(firstChunk: UInt32, samplesPerChunk: UInt32, sampleDescriptionIndex: UInt32) {
self.firstChunk = firstChunk
self.samplesPerChunk = samplesPerChunk
self.sampleDescriptionIndex = sampleDescriptionIndex
}
}
var entries: [Entry] = []
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(size) - 8))
buffer.position += 4
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
entries.append(Entry(
firstChunk: try buffer.readUInt32(),
samplesPerChunk: try buffer.readUInt32(),
sampleDescriptionIndex: try buffer.readUInt32()
))
}
buffer.clear()
return size
}
}
// MARK: -
final class MP4EditListBox: MP4Box {
struct Entry: CustomDebugStringConvertible {
var segmentDuration: UInt32 = 0
var mediaTime: UInt32 = 0
var mediaRate: UInt32 = 0
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
init(segmentDuration: UInt32, mediaTime: UInt32, mediaRate: UInt32) {
self.segmentDuration = segmentDuration
self.mediaTime = mediaTime
self.mediaRate = mediaRate
}
}
var version: UInt32 = 0
var entries: [Entry] = []
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
let buffer = ByteArray(data: fileHandle.readData(ofLength: Int(size) - 8))
version = try buffer.readUInt32()
entries.removeAll(keepingCapacity: false)
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
entries.append(Entry(
segmentDuration: try buffer.readUInt32(),
mediaTime: try buffer.readUInt32(),
mediaRate: try buffer.readUInt32()
))
}
return size
}
}
// MARK: -
final class MP4Reader: MP4ContainerBox {
private(set) var url: URL
var isEmpty: Bool {
getBoxes(byName: "mdhd").isEmpty
}
private var fileHandle: FileHandle?
init(url: URL) {
do {
self.url = url
super.init()
fileHandle = try FileHandle(forReadingFrom: url)
} catch let error as NSError {
logger.error("\(error)")
}
}
func seek(toFileOffset: UInt64) {
fileHandle!.seek(toFileOffset: toFileOffset)
}
func readData(ofLength: Int) -> Data {
fileHandle!.readData(ofLength: ofLength)
}
func readData(ofBox: MP4Box) -> Data {
guard let fileHandle: FileHandle = fileHandle else {
return Data()
}
let currentOffsetInFile: UInt64 = fileHandle.offsetInFile
fileHandle.seek(toFileOffset: ofBox.offset + 8)
let data: Data = fileHandle.readData(ofLength: Int(ofBox.size) - 8)
fileHandle.seek(toFileOffset: currentOffsetInFile)
return data
}
func load() throws -> UInt32 {
guard let fileHandle: FileHandle = self.fileHandle else {
return 0
}
return try load(fileHandle)
}
func close() {
fileHandle?.closeFile()
}
override func load(_ fileHandle: FileHandle) throws -> UInt32 {
let size: UInt64 = fileHandle.seekToEndOfFile()
fileHandle.seek(toFileOffset: 0)
self.size = UInt32(size)
return try super.load(fileHandle)
}
}
// MARK: -
final class MP4TrakReader {
static let defaultBufferTime: Double = 500
var trak: MP4Box
var bufferTime: Double = MP4TrakReader.defaultBufferTime
weak var delegate: MP4SamplerDelegate?
private var id: Int = 0
private var handle: FileHandle?
private lazy var timerDriver: TimerDriver = {
var timerDriver = TimerDriver()
timerDriver.delegate = self
return timerDriver
}()
private var currentOffset: UInt64 {
UInt64(offset[cursor])
}
private var currentIsKeyframe: Bool {
keyframe[cursor] != nil
}
private var currentDuration: Double {
Double(totalTimeToSample) * 1000 / Double(timeScale)
}
private var currentTimeToSample: Double {
Double(timeToSample[cursor]) * 1000 / Double(timeScale)
}
private var currentSampleSize: Int {
Int((sampleSize.count == 1) ? sampleSize[0] : sampleSize[cursor])
}
private var cursor: Int = 0
private var offset: [UInt32] = []
private var keyframe: [Int: Bool] = [:]
private var timeScale: UInt32 = 0
private var sampleSize: [UInt32] = []
private var timeToSample: [UInt32] = []
private var totalTimeToSample: UInt32 = 0
init(id: Int, trak: MP4Box) {
self.id = id
self.trak = trak
let mdhd: MP4Box? = trak.getBoxes(byName: "mdhd").first
if let mdhd: MP4MediaHeaderBox = mdhd as? MP4MediaHeaderBox {
timeScale = mdhd.timeScale
}
let stss: MP4Box? = trak.getBoxes(byName: "stss").first
if let stss: MP4SyncSampleBox = stss as? MP4SyncSampleBox {
let keyframes: [UInt32] = stss.entries
for i in 0..<keyframes.count {
keyframe[Int(keyframes[i]) - 1] = true
}
}
let stts: MP4Box? = trak.getBoxes(byName: "stts").first
if let stts: MP4TimeToSampleBox = stts as? MP4TimeToSampleBox {
let timeToSample: [MP4TimeToSampleBox.Entry] = stts.entries
for i in 0..<timeToSample.count {
let entry: MP4TimeToSampleBox.Entry = timeToSample[i]
for _ in 0..<entry.sampleCount {
self.timeToSample.append(entry.sampleDuration)
}
}
}
let stsz: MP4Box? = trak.getBoxes(byName: "stsz").first
if let stsz: MP4SampleSizeBox = stsz as? MP4SampleSizeBox {
sampleSize = stsz.entries
}
let stco: MP4Box = trak.getBoxes(byName: "stco").first!
let stsc: MP4Box = trak.getBoxes(byName: "stsc").first!
let offsets: [UInt32] = (stco as! MP4ChunkOffsetBox).entries
let sampleToChunk: [MP4SampleToChunkBox.Entry] = (stsc as! MP4SampleToChunkBox).entries
var index: Int = 0
let count: Int = sampleToChunk.count
for i in 0..<count {
let m: Int = (i + 1 < count) ? Int(sampleToChunk[i + 1].firstChunk) - 1 : offsets.count
for j in (Int(sampleToChunk[i].firstChunk) - 1)..<m {
var offset: UInt32 = offsets[j]
for _ in 0..<sampleToChunk[i].samplesPerChunk {
self.offset.append(offset)
offset += sampleSize[index]
index += 1
}
}
}
totalTimeToSample = timeToSample[cursor]
}
func execute(_ reader: MP4Reader) {
do {
handle = try FileHandle(forReadingFrom: reader.url)
if let avcC: MP4Box = trak.getBoxes(byName: "avcC").first {
delegate?.didSet(config: reader.readData(ofBox: avcC), withID: id, type: .video)
}
if let esds: MP4ElementaryStreamDescriptorBox = trak.getBoxes(byName: "esds").first as? MP4ElementaryStreamDescriptorBox {
delegate?.didSet(config: Data(esds.audioDecorderSpecificConfig), withID: id, type: .audio)
}
timerDriver.interval = MachUtil.nanosToAbs(UInt64(currentTimeToSample * 1000 * 1000))
while currentDuration <= bufferTime {
tick(timerDriver)
}
timerDriver.startRunning()
} catch {
logger.warn("file open error : \(reader.url)")
}
}
private func hasNext() -> Bool {
cursor + 1 < offset.count
}
private func next() {
defer {
cursor += 1
}
totalTimeToSample += timeToSample[cursor]
}
}
extension MP4TrakReader: TimerDriverDelegate {
// MARK: TimerDriverDelegate
func tick(_ driver: TimerDriver) {
guard let handle: FileHandle = handle else {
driver.stopRunning()
return
}
driver.interval = MachUtil.nanosToAbs(UInt64(currentTimeToSample * 1000 * 1000))
handle.seek(toFileOffset: currentOffset)
autoreleasepool {
delegate?.output(
data: handle.readData(ofLength: currentSampleSize),
withID: id,
currentTime: currentTimeToSample,
keyframe: currentIsKeyframe
)
}
if hasNext() {
next()
} else {
driver.stopRunning()
}
}
}
extension MP4TrakReader: CustomDebugStringConvertible {
// MARK: CustomDebugStringConvertible
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
}

View File

@ -1,87 +0,0 @@
import AVFoundation
protocol MP4SamplerDelegate: AnyObject {
func didOpen(_ reader: MP4Reader)
func didSet(config: Data, withID: Int, type: AVMediaType)
func output(data: Data, withID: Int, currentTime: Double, keyframe: Bool)
}
// MARK: -
public class MP4Sampler {
public typealias Handler = () -> Void
weak var delegate: MP4SamplerDelegate?
private var files: [URL] = []
private var handlers: [URL: Handler?] = [:]
private let lockQueue = DispatchQueue(label: "com.haishinkit.HaishinKit.MP4Sampler.lock")
private let loopQueue = DispatchQueue(label: "com.haishinkit.HaishinKit.MP4Sampler.loop")
private let operations = OperationQueue()
public private(set) var isRunning: Atomic<Bool> = .init(false)
func appendFile(_ file: URL, completionHandler: Handler? = nil) {
lockQueue.async {
self.handlers[file] = completionHandler
self.files.append(file)
}
}
private func execute(url: URL) {
let reader = MP4Reader(url: url)
do {
_ = try reader.load()
} catch {
logger.warn("")
return
}
delegate?.didOpen(reader)
let traks: [MP4Box] = reader.getBoxes(byName: "trak")
for i in 0..<traks.count {
let trakReader = MP4TrakReader(id: i, trak: traks[i])
trakReader.delegate = delegate
operations.addOperation {
trakReader.execute(reader)
}
}
operations.waitUntilAllOperationsAreFinished()
reader.close()
}
private func run() {
if files.isEmpty {
return
}
let url: URL = files.first!
let handler: Handler? = handlers[url]!
files.remove(at: 0)
handlers[url] = nil
execute(url: url)
handler?()
}
}
extension MP4Sampler: Running {
// MARK: Running
public func startRunning() {
loopQueue.async {
self.isRunning.mutate { $0 = true }
while self.isRunning.value {
self.lockQueue.sync {
self.run()
if self.files.isEmpty {
sleep(1)
}
}
}
}
}
public func stopRunning() {
lockQueue.async {
self.isRunning.mutate { $0 = false }
}
}
}

View File

@ -0,0 +1,52 @@
import Foundation
struct MP4AudioSampleEntry: MP4SampleEntry {
static let channelCount: UInt16 = 2
static let sampleSize: UInt16 = 16
// MARK: MP4SampleEntry
var size: UInt32 = 0
var type: String = ""
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
var dataReferenceIndex: UInt16 = 0
// MARK: MP4AudioSampleEntry
var channelCount: UInt16 = Self.channelCount
var sampleSize: UInt16 = Self.sampleSize
var sampleRate: UInt32 = 0
}
extension MP4AudioSampleEntry: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
type = try buffer.readUTF8Bytes(4)
buffer.position += 16
channelCount = try buffer.readUInt16()
sampleSize = try buffer.readUInt16()
buffer.position += 4
sampleRate = try buffer.readUInt32() >> 16
children.removeAll()
while 0 < buffer.bytesAvailable {
let size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
buffer.position -= 8
var entry = MP4Box()
entry.data = try buffer.readBytes(Int(size))
children.append(entry)
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let mp4a = MP4Box.Name<MP4AudioSampleEntry>(rawValue: "mp4a")
static let mlpa = MP4Box.Name<MP4AudioSampleEntry>(rawValue: "mlpa")
}

84
Sources/MP4/MP4Box.swift Normal file
View File

@ -0,0 +1,84 @@
import Foundation
struct MP4Box: MP4BoxConvertible {
static let containers: Set<String> = [
"cmov",
"ctts",
"edts",
"esds",
"iods",
"junk",
"mdia",
"minf",
"moov",
"pict",
"pnot",
"rmda",
"rmra",
"skip",
"stbl",
"trak",
"uuid",
"wide",
"moof",
"traf"
]
class Names {
}
final class Name<T: MP4BoxConvertible>: Names, Hashable, RawRepresentable {
let rawValue: String
typealias RawValue = String
init(rawValue: String) {
self.rawValue = rawValue
}
}
var size: UInt32 = 0
var type: String = ""
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
private var _data = Data()
}
extension MP4Box: DataConvertible {
var data: Data {
get {
_data
}
set {
do {
_data = newValue
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
type = try buffer.readUTF8Bytes(4)
if Self.containers.contains(type) {
children.removeAll()
while 0 < buffer.bytesAvailable {
let size = try buffer.readInt32()
_ = try buffer.readBytes(4)
buffer.position -= 8
var child = MP4Box()
child.data = try buffer.readBytes(Int(size))
children.append(child)
}
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let trak = MP4Box.Name<MP4Box>(rawValue: "trak")
}
extension MP4Box: CustomDebugStringConvertible {
// MARK: CustomDebugStringConvertible
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
}

View File

@ -0,0 +1,43 @@
import Foundation
protocol MP4BoxConvertible: DataConvertible, CustomXmlStringConvertible {
var size: UInt32 { get }
var type: String { get }
var offset: UInt64 { get set }
var children: [MP4BoxConvertible] { get }
init()
func getBoxes<T>(by name: MP4Box.Name<T>) -> [T]
}
extension MP4BoxConvertible {
var xmlString: String {
guard !children.isEmpty else {
return "<\(type) size=\"\(size)\" offset=\"\(offset)\" />"
}
var tags: [String] = []
for child in children {
tags.append(child.xmlString)
}
return "<\(type) size=\"\(size)\" offset=\"\(offset)\">\(tags.joined())</\(type)>"
}
func getBoxes<T>(by name: MP4Box.Name<T>) -> [T] {
var list: [T] = []
for child in children {
if name.rawValue == child.type {
if let box = child as? T {
list.append(box)
} else {
var box = T()
box.data = child.data
list.append(box)
}
}
if !child.children.isEmpty {
list += child.getBoxes(by: name)
}
}
return list
}
}

View File

@ -0,0 +1,38 @@
import Foundation
struct MP4ChunkOffsetBox: MP4BoxConvertible {
// MARK: MP4BoxConvertible
var size: UInt32 = 0
let type: String = "stco"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
// MARK: MP4ChunkOffsetBox
var entries: [UInt32] = []
}
extension MP4ChunkOffsetBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
buffer.position += 4
let numberOfEntries = try buffer.readUInt32()
entries.removeAll()
for _ in 0..<numberOfEntries {
entries.append(try buffer.readUInt32())
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let stco = MP4Box.Name<MP4ChunkOffsetBox>(rawValue: "stco")
}

View File

@ -0,0 +1,52 @@
import Foundation
struct MP4EditListBox: MP4BoxConvertible {
struct Entry: CustomDebugStringConvertible {
let segmentDuration: UInt32
let mediaTime: UInt32
let mediaRate: UInt32
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
}
// MARK: MP4BoxConvertible
var size: UInt32 = 0
let type: String = "elst"
var offset: UInt64 = 0
// MARK: MP4EditListBox
var version: UInt32 = 0
var entries: [Entry] = []
var children: [MP4BoxConvertible] = []
}
extension MP4EditListBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
version = try buffer.readUInt32()
let numberOfEntries = try buffer.readUInt32()
entries.removeAll()
for _ in 0..<numberOfEntries {
entries.append(Entry(
segmentDuration: try buffer.readUInt32(),
mediaTime: try buffer.readUInt32(),
mediaRate: try buffer.readUInt32()
))
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let elst = MP4Box.Name<MP4EditListBox>(rawValue: "elst")
}

View File

@ -0,0 +1,70 @@
import Foundation
struct MP4ElementaryStreamDescriptorBox: MP4BoxConvertible {
// MARK: MP4ContainerBoxConvertible
var size: UInt32 = 0
let type: String = "esds"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
// MARK: MP4ElementaryStreamDescriptorBox
var audioDecorderSpecificConfig = Data()
var tag: UInt8 = 0
var tagSize: UInt8 = 0
var id: UInt16 = 0
var streamDependenceFlag: UInt8 = 0
var urlFlag: UInt8 = 0
var ocrStreamFlag: UInt8 = 0
var streamPriority: UInt8 = 0
}
extension MP4ElementaryStreamDescriptorBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
tag = try buffer.readUInt8()
self.tagSize = try buffer.readUInt8()
if self.tagSize == 0x80 {
buffer.position += 2
self.tagSize = try buffer.readUInt8()
}
id = try buffer.readUInt16()
let data: UInt8 = try buffer.readUInt8()
streamDependenceFlag = data >> 7
urlFlag = (data >> 6) & 0x1
ocrStreamFlag = (data >> 5) & 0x1
streamPriority = data & 0x1f
if streamDependenceFlag == 1 {
let _: UInt16 = try buffer.readUInt16()
}
// Decorder Config Descriptor
let _: UInt8 = try buffer.readUInt8()
tagSize = try buffer.readUInt8()
if tagSize == 0x80 {
buffer.position += 2
tagSize = try buffer.readUInt8()
}
buffer.position += 13
// Audio Decorder Spec Info
let _: UInt8 = try buffer.readUInt8()
tagSize = try buffer.readUInt8()
if tagSize == 0x80 {
buffer.position += 2
tagSize = try buffer.readUInt8()
}
audioDecorderSpecificConfig = try buffer.readBytes(Int(tagSize))
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let esds = MP4Box.Name<MP4ElementaryStreamDescriptorBox>(rawValue: "esds")
}

View File

@ -0,0 +1,53 @@
import Foundation
final class MP4FileReader: MP4ReaderConvertible {
var fileType: MP4FileTypeBox {
root.getBoxes(by: .ftyp).first ?? MP4FileTypeBox()
}
var tracks: [MP4TrackReader] = []
private var root = MP4Box()
private let fileHandle: FileHandle
init(forReadingFrom url: URL) throws {
fileHandle = try FileHandle(forReadingFrom: url)
}
func execute() -> Self {
do {
var currentOffset = root.offset
let length = fileHandle.seekToEndOfFile()
root.children.removeAll()
repeat {
fileHandle.seek(toFileOffset: currentOffset)
let buffer = ByteArray(data: fileHandle.readData(ofLength: 8))
let size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
fileHandle.seek(toFileOffset: currentOffset)
var child = MP4Box()
child.data = fileHandle.readData(ofLength: Int(size))
root.children.append(child)
currentOffset += UInt64(size)
} while currentOffset < length
} catch {
logger.error(error)
}
return self
}
func getBoxes<T: MP4BoxConvertible>(by name: MP4Box.Name<T>) -> [T] {
return root.getBoxes(by: name)
}
}
extension MP4FileReader: CustomDebugStringConvertible {
var debugDescription: String {
return root.debugDescription
}
}
extension MP4FileReader: CustomXmlStringConvertible {
var xmlString: String {
return root.xmlString
}
}

View File

@ -0,0 +1,41 @@
import Foundation
struct MP4FileTypeBox: MP4BoxConvertible {
// MARK: MP4BoxConvertible
var size: UInt32 = 0
var type: String = ""
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
// MARK: MP4MediaHeaderBox
var majorBrand: UInt32 = 0
var minorVersion: UInt32 = 0
var compatibleBrands: [UInt32] = []
}
extension MP4FileTypeBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
type = try buffer.readUTF8Bytes(4)
majorBrand = try buffer.readUInt32()
minorVersion = try buffer.readUInt32()
compatibleBrands.removeAll()
while 0 < buffer.bytesAvailable {
compatibleBrands.append(try buffer.readUInt32())
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let styp = MP4Box.Name<MP4FileTypeBox>(rawValue: "styp")
static let ftyp = MP4Box.Name<MP4FileTypeBox>(rawValue: "ftyp")
}

View File

@ -0,0 +1,6 @@
import Foundation
protocol MP4FullBox: MP4BoxConvertible {
var version: UInt8 { get }
var flags: UInt32 { get }
}

View File

@ -0,0 +1,47 @@
import Foundation
struct MP4MediaHeaderBox: MP4BoxConvertible {
// MARK: MP4BoxConvertible
var size: UInt32 = 0
var type: String = ""
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
// MARK: MP4MediaHeaderBox
var version: UInt8 = 0
var creationTime: UInt32 = 0
var modificationTime: UInt32 = 0
var timeScale: UInt32 = 0
var duration: UInt32 = 0
var language: UInt16 = 0
var quality: UInt16 = 0
}
extension MP4MediaHeaderBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
type = try buffer.readUTF8Bytes(4)
version = try buffer.readUInt8()
buffer.position += 3
creationTime = try buffer.readUInt32()
modificationTime = try buffer.readUInt32()
timeScale = try buffer.readUInt32()
duration = try buffer.readUInt32()
language = try buffer.readUInt16()
quality = try buffer.readUInt16()
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let mvhd = MP4Box.Name<MP4MediaHeaderBox>(rawValue: "mvhd")
static let mdhd = MP4Box.Name<MP4MediaHeaderBox>(rawValue: "mdhd")
}

View File

@ -0,0 +1,37 @@
import Foundation
struct MP4MovieFragmentHeaderBox: MP4FullBox {
static let version: UInt8 = 0
static let flags: UInt32 = 0
// MARK: MP4FullBox
var size: UInt32 = 0
let type: String = "mfhd"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
let version: UInt8 = Self.version
let flags: UInt32 = Self.flags
// MARK: MP4MovieFragmentHeaderBox
var sequenceNumber: UInt32 = 0
}
extension MP4MovieFragmentHeaderBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
buffer.position += 8
sequenceNumber = try buffer.readUInt32()
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let mfhd = MP4Box.Name<MP4MovieFragmentHeaderBox>(rawValue: "mfhd")
}

View File

@ -0,0 +1,32 @@
import AVFoundation
final class MP4Reader: MP4ReaderConvertible {
let fileType: MP4FileTypeBox
let tracks: [MP4TrackReader]
init(fileType: MP4FileTypeBox, tracks: [MP4TrackReader]) {
self.fileType = fileType
self.tracks = tracks
}
}
final class MP4TrackReader {
struct MP4SampleIterator: IteratorProtocol {
typealias Element = UInt8
private var cursor: Int = 0
private let reader: MP4TrackReader
init(reader: MP4TrackReader) {
self.reader = reader
}
mutating func next() -> Element? {
return nil
}
}
func makeIterator() -> MP4SampleIterator {
return MP4SampleIterator(reader: self)
}
}

View File

@ -0,0 +1,14 @@
import Foundation
protocol MP4ReaderConvertible: AnyObject {
var fileType: MP4FileTypeBox { get }
var tracks: [MP4TrackReader] { get }
func execute() -> Self
}
extension MP4ReaderConvertible {
func execute() -> Self {
return self
}
}

View File

@ -0,0 +1,61 @@
import Foundation
struct MP4SampleDescriptionBox: MP4FullBox {
static let audio: Set<String> = ["mp4a"]
static let video: Set<String> = ["mp4v", "s263", "avc1"]
static func makeEntry(by type: String) -> MP4SampleEntry? {
switch true {
case video.contains(type):
return MP4VisualSampleEntry()
case audio.contains(type):
return MP4AudioSampleEntry()
default:
return nil
}
}
static let flags: UInt32 = 0
// MARK: MP4FullBox
var size: UInt32 = 0
let type: String = "stsd"
var offset: UInt64 = 0
var version: UInt8 = 0
let flags: UInt32 = Self.flags
// MARK:
var children: [MP4BoxConvertible] = []
}
extension MP4SampleDescriptionBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
version = try buffer.readUInt8()
let numberOfEntries = try buffer.readUInt32()
children.removeAll()
for _ in 0..<numberOfEntries {
let size = try buffer.readUInt32()
let type = try buffer.readUTF8Bytes(4)
buffer.position -= 8
var entry = Self.makeEntry(by: type)
entry?.data = try buffer.readBytes(Int(size))
if let entry = entry {
children.append(entry)
}
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let stsd = MP4Box.Name<MP4SampleDescriptionBox>(rawValue: "stsd")
}

View File

@ -0,0 +1,5 @@
import Foundation
protocol MP4SampleEntry: MP4BoxConvertible {
var dataReferenceIndex: UInt16 { get }
}

View File

@ -0,0 +1,43 @@
import Foundation
struct MP4SampleSizeBox: MP4BoxConvertible {
// MARK: MP4BoxConvertible
var size: UInt32 = 0
let type: String = "stsz"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
// MARK: MP4SampleSizeBox
var entries: [UInt32] = []
}
extension MP4SampleSizeBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
buffer.position += 4
entries.removeAll()
let sampleSize = try buffer.readUInt32()
if sampleSize == 0 {
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
entries.append(try buffer.readUInt32())
}
} else {
entries.append(sampleSize)
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let stsz = MP4Box.Name<MP4SampleSizeBox>(rawValue: "stsz")
}

View File

@ -0,0 +1,51 @@
import Foundation
struct MP4SampleToChunkBox: MP4BoxConvertible {
struct Entry: CustomDebugStringConvertible {
let firstChunk: UInt32
let samplesPerChunk: UInt32
let sampleDescriptionIndex: UInt32
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
}
// MARK: MP4BoxConvertible
var size: UInt32 = 0
let type: String = "stsc"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
// MARK: MP4SampleToChunkBox
var entries: [Entry] = []
}
extension MP4SampleToChunkBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
buffer.position += 4
let numberOfEntries: UInt32 = try buffer.readUInt32()
entries.removeAll()
for _ in 0..<numberOfEntries {
entries.append(Entry(
firstChunk: try buffer.readUInt32(),
samplesPerChunk: try buffer.readUInt32(),
sampleDescriptionIndex: try buffer.readUInt32()
))
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let stsc = MP4Box.Name<MP4SampleToChunkBox>(rawValue: "stsc")
}

View File

@ -0,0 +1,74 @@
import Foundation
struct MP4SegmentIndexBox: MP4FullBox {
static let flags: UInt32 = 0
struct Reference {
var type = false
var size: UInt32 = 0
var subsegmentDuration: UInt32 = 0
var startsWithSap = false
var sapType: UInt8 = 0
var sapDeltaTime: UInt32 = 0
}
// MARK: MP4FullBox
var size: UInt32 = 0
let type: String = "sidx"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
var version: UInt8 = 0
let flags: UInt32 = Self.flags
// MARK: MP4SegmentIndexBox
var referenceID: UInt32 = 0
var timescale: UInt32 = 0
var earliestPresentationTime: UInt64 = 0
var firstOffset: UInt64 = 0
var references: [Reference] = []
}
extension MP4SegmentIndexBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
version = try buffer.readUInt8()
buffer.position += 3
referenceID = try buffer.readUInt32()
timescale = try buffer.readUInt32()
if version == 0 {
earliestPresentationTime = UInt64(try buffer.readUInt32())
firstOffset = UInt64(try buffer.readUInt32())
} else {
earliestPresentationTime = try buffer.readUInt64()
firstOffset = try buffer.readUInt64()
}
buffer.position += 2
let referenceCount: UInt16 = try buffer.readUInt16()
references.removeAll()
for _ in 0..<referenceCount {
let first = try buffer.readUInt32()
let second = try buffer.readUInt32()
let third = try buffer.readUInt32()
references.append(Reference(
type: (first >> 31) == 1,
size: first & 0x7FFFFFFF,
subsegmentDuration: second,
startsWithSap: (third >> 31) == 1,
sapType: UInt8(third & 0x70000000 >> 28),
sapDeltaTime: third & 0xFFFFFFF
))
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let sidx = MP4Box.Name<MP4SegmentIndexBox>(rawValue: "sidx")
}

View File

@ -0,0 +1,38 @@
import Foundation
struct MP4SyncSampleBox: MP4BoxConvertible {
// MARK: MP4BoxConvertible
var size: UInt32 = 0
let type: String = "stss"
var offset: UInt64 = 0
// MARK: MP4SyncSampleBox
var entries: [UInt32] = []
var children: [MP4BoxConvertible] = []
}
extension MP4SyncSampleBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
buffer.position += 4
let numberOfEntries: UInt32 = try buffer.readUInt32()
entries.removeAll()
for _ in 0..<numberOfEntries {
entries.append(try buffer.readUInt32())
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let stss = MP4Box.Name<MP4SyncSampleBox>(rawValue: "stss")
}

View File

@ -0,0 +1,48 @@
import Foundation
struct MP4TimeToSampleBox: MP4BoxConvertible {
struct Entry: CustomDebugStringConvertible {
let sampleCount: UInt32
let sampleDuration: UInt32
var debugDescription: String {
Mirror(reflecting: self).debugDescription
}
}
// MARK: MP4BoxConvertible
var size: UInt32 = 0
let type: String = "stts"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
// MARK: MP4TimeToSampleBox
var entries: [Entry] = []
}
extension MP4TimeToSampleBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
entries.removeAll()
let numberOfEntries: UInt32 = try buffer.readUInt32()
for _ in 0..<numberOfEntries {
entries.append(Entry(
sampleCount: try buffer.readUInt32(),
sampleDuration: try buffer.readUInt32()
))
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let stts = MP4Box.Name<MP4TimeToSampleBox>(rawValue: "stts")
}

View File

@ -0,0 +1,41 @@
import Foundation
struct MP4TrackFragmentBaseMediaDecodeTimeBox: MP4BoxConvertible {
static let flags: UInt32 = 0
// MARK: MP4FullBox
var size: UInt32 = 0
let type: String = "tfdt"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
var version: UInt8 = 0
let flags: UInt32 = Self.flags
// MARK: MP4TrackFragmentBaseMediaDecodeTimeBox
var baseMediaDecodeTime: UInt64 = 0
}
extension MP4TrackFragmentBaseMediaDecodeTimeBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
version = try buffer.readUInt8()
if version == 0 {
baseMediaDecodeTime = UInt64(try buffer.readUInt32())
} else {
baseMediaDecodeTime = try buffer.readUInt64()
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let tfdt = MP4Box.Name<MP4TrackFragmentBaseMediaDecodeTimeBox>(rawValue: "tfdt")
}

View File

@ -0,0 +1,72 @@
import Foundation
/// ISO/IEC 14496-12 5th 8.8.7.2
struct MP4TrackFragmentHeaderBox: MP4FullBox {
static let version: UInt8 = 0
enum Field: UInt32 {
case baseDataOffset = 0x000001
case sampleDescriptionIndex = 0x000002
case defaultSampleDuration = 0x000008
case defaultSampleSize = 0x000010
case defaultSampleFlags = 0x000020
case durationIsEmpty = 0x010000
case defaultBaseIsMoof = 0x020000
}
// MARK: MP4FullBox
var size: UInt32 = 0
let type: String = "tfhd"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
let version: UInt8 = Self.version
var flags: UInt32 = 0
// MARK: MP4TrackFragmentHeaderBox
var trackId: UInt32 = 0
var baseDataOffset: UInt64 = 0
var sampleDescriptionIndex: UInt32 = 0
var defaultSampleDuration: UInt32 = 0
var defaultSampleSize: UInt32 = 0
var defaultSampleFlags: UInt32 = 0
private func contains(_ value: Field) -> Bool {
return (flags & value.rawValue) != 0
}
}
extension MP4TrackFragmentHeaderBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
buffer.position += 1
flags = UInt32(try buffer.readUInt24())
trackId = try buffer.readUInt32()
if contains(.baseDataOffset) {
baseDataOffset = try buffer.readUInt64()
}
if contains(.sampleDescriptionIndex) {
sampleDescriptionIndex = try buffer.readUInt32()
}
if contains(.defaultSampleDuration) {
defaultSampleDuration = try buffer.readUInt32()
}
if contains(.defaultSampleSize) {
defaultSampleSize = try buffer.readUInt32()
}
if contains(.defaultSampleFlags) {
defaultSampleFlags = try buffer.readUInt32()
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let tfhd = MP4Box.Name<MP4TrackFragmentHeaderBox>(rawValue: "tfhd")
}

View File

@ -0,0 +1,73 @@
import Foundation
/// ISO/IEC 14496-12 5th 8.8.8.2
struct MP4TrackRunBox: MP4FullBox {
struct Sample {
var duration: UInt32?
var size: UInt32?
var flags: UInt32?
var compositionTimeOffset: Int32?
}
enum Field: UInt32 {
case dataOffset = 0x000001
case firstSampleFlags = 0x000004
case sampleDuration = 0x000100
case sampleSize = 0x000200
case sampleFlags = 0x000400
case sampleCompositionTimeOffset = 0x000800
}
// MARK: MP4FullBox
var size: UInt32 = 0
let type: String = "trun"
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
var version: UInt8 = 0
var flags: UInt32 = 0
// MARK: MP4TrackRunBox
var dataOffset: Int32 = 0
var firstSampleFlags: UInt32 = 0
var samples: [Sample] = []
private func contains(_ value: Field) -> Bool {
return (flags & value.rawValue) != 0
}
}
extension MP4TrackRunBox: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
_ = try buffer.readBytes(4)
version = try buffer.readUInt8()
flags = try buffer.readUInt24()
let sampleCount: UInt32 = try buffer.readUInt32()
if contains(.dataOffset) {
dataOffset = try buffer.readInt32()
}
if contains(.firstSampleFlags) {
firstSampleFlags = try buffer.readUInt32()
}
samples.removeAll()
for _ in 0..<sampleCount {
samples.append(Sample(
duration: contains(.sampleDuration) ? try buffer.readUInt32() : nil,
size: contains(.sampleSize) ? try buffer.readUInt32() : nil,
flags: contains(.sampleFlags) ? try buffer.readUInt32() : nil,
compositionTimeOffset: contains(.sampleCompositionTimeOffset) ? try buffer.readInt32() : nil
))
}
} catch {
logger.error(error)
}
}
}
}
extension MP4Box.Names {
static let trun = MP4Box.Name<MP4TrackRunBox>(rawValue: "trun")
}

17
Sources/MP4/MP4Util.swift Normal file
View File

@ -0,0 +1,17 @@
import Foundation
struct MP4Util {
static func string(_ value: UInt32) -> String? {
return String(data: value.bigEndian.data, encoding: .ascii)
}
static func uint32(_ value: String) -> UInt32 {
var loop = 0
var result: UInt32 = 0
for scalar in value.unicodeScalars {
result |= scalar.value << (8 * loop)
loop += 1
}
return result.bigEndian
}
}

View File

@ -0,0 +1,56 @@
import Foundation
struct MP4VisualSampleEntry: MP4SampleEntry {
static let hSolution: UInt32 = 0x00480000
static let vSolution: UInt32 = 0x00480000
static let depth: UInt16 = 0x0018
// MARK: MP4SampleEntry
var size: UInt32 = 0
var type: String = ""
var offset: UInt64 = 0
var children: [MP4BoxConvertible] = []
var dataReferenceIndex: UInt16 = 0
// MARK: MP4VisualSampleEntryBox
var width: UInt16 = 0
var height: UInt16 = 0
var hSolution: UInt32 = Self.hSolution
var vSolution: UInt32 = Self.vSolution
var frameCount: UInt16 = 1
var compressorname: String = ""
var depth: UInt16 = Self.depth
}
extension MP4VisualSampleEntry: DataConvertible {
var data: Data {
get {
Data()
}
set {
do {
let buffer = ByteArray(data: newValue)
size = try buffer.readUInt32()
type = try buffer.readUTF8Bytes(4)
buffer.position += 24
width = try buffer.readUInt16()
height = try buffer.readUInt16()
hSolution = try buffer.readUInt32()
vSolution = try buffer.readUInt32()
buffer.position += 4
frameCount = try buffer.readUInt16()
compressorname = try buffer.readUTF8Bytes(32)
depth = try buffer.readUInt16()
_ = try buffer.readUInt16()
while 0 < buffer.bytesAvailable {
let size = try buffer.readUInt32()
_ = try buffer.readUTF8Bytes(4)
buffer.position -= 8
var entry = MP4Box()
entry.data = try buffer.readBytes(Int(size))
children.append(entry)
}
} catch {
logger.error(error)
}
}
}
}

View File

@ -80,57 +80,3 @@ extension RTMPMuxer: VideoEncoderDelegate {
}
}
extension RTMPMuxer: MP4SamplerDelegate {
// MARK: MP4SampleDelegate
func didOpen(_ reader: MP4Reader) {
var metadata = ASObject()
if let avc1: MP4VisualSampleEntryBox = reader.getBoxes(byName: "avc1").first as? MP4VisualSampleEntryBox {
metadata["width"] = avc1.width
metadata["height"] = avc1.height
metadata["videocodecid"] = FLVVideoCodec.avc.rawValue
}
if let _: MP4AudioSampleEntryBox = reader.getBoxes(byName: "mp4a").first as? MP4AudioSampleEntryBox {
metadata["audiocodecid"] = FLVAudioCodec.aac.rawValue
}
delegate?.metadata(metadata)
}
func didSet(config: Data, withID: Int, type: AVMediaType) {
guard configs[withID] != config else {
return
}
configs[withID] = config
switch type {
case .video:
var buffer = Data([FLVFrameType.key.rawValue << 4 | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.seq.rawValue, 0, 0, 0])
buffer.append(config)
delegate?.sampleOutput(video: buffer, withTimestamp: 0, muxer: self)
case .audio:
if withID != 1 {
break
}
var buffer = Data([RTMPMuxer.aac, FLVAACPacketType.seq.rawValue])
buffer.append(config)
delegate?.sampleOutput(audio: buffer, withTimestamp: 0, muxer: self)
default:
break
}
}
func output(data: Data, withID: Int, currentTime: Double, keyframe: Bool) {
switch withID {
case 0:
let compositionTime: Int32 = 0
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?.sampleOutput(video: buffer, withTimestamp: currentTime, muxer: self)
case 1:
var buffer = Data([RTMPMuxer.aac, FLVAACPacketType.raw.rawValue])
buffer.append(data)
delegate?.sampleOutput(audio: buffer, withTimestamp: currentTime, muxer: self)
default:
break
}
}
}

View File

@ -258,7 +258,6 @@ open class RTMPStream: NetStream {
var audioTimestamp: Double = 0.0
var videoTimestamp: Double = 0.0
private let muxer = RTMPMuxer()
private var sampler: MP4Sampler?
private var messages: [RTMPCommandMessage] = []
private var frameCount: UInt16 = 0
private var dispatcher: IEventDispatcher!
@ -403,22 +402,6 @@ open class RTMPStream: NetStream {
}
}
open func appendFile(_ file: URL, completionHandler: MP4Sampler.Handler? = nil) {
lockQueue.async {
if self.sampler == nil {
self.sampler = MP4Sampler()
self.sampler?.delegate = self.muxer
switch self.readyState {
case .publishing:
self.sampler?.startRunning()
default:
break
}
}
self.sampler?.appendFile(file, completionHandler: completionHandler)
}
}
open func createMetaData() -> ASObject {
metadata.removeAll()
#if os(iOS) || os(macOS)
@ -480,7 +463,6 @@ open class RTMPStream: NetStream {
mixer.videoIO.encoder.delegate = nil
mixer.audioIO.encoder.stopRunning()
mixer.videoIO.encoder.stopRunning()
sampler?.stopRunning()
mixer.recorder.stopRunning()
default:
break
@ -518,7 +500,7 @@ open class RTMPStream: NetStream {
#endif
mixer.audioIO.encoder.delegate = muxer
mixer.videoIO.encoder.delegate = muxer
sampler?.delegate = muxer
// sampler?.delegate = muxer
mixer.startRunning()
videoWasSent = false
audioWasSent = false
@ -527,7 +509,6 @@ open class RTMPStream: NetStream {
send(handlerName: "@setDataFrame", arguments: "onMetaData", createMetaData())
mixer.audioIO.encoder.startRunning()
mixer.videoIO.encoder.startRunning()
sampler?.startRunning()
if howToPublish == .localRecord {
mixer.recorder.fileName = FilenameUtil.fileName(resourceName: info.resourceName)
mixer.recorder.startRunning()

View File

@ -36,6 +36,14 @@ protocol ByteArrayConvertible {
func writeInt32(_ value: Int32) -> Self
func readInt32() throws -> Int32
@discardableResult
func writeUInt64(_ value: UInt64) -> Self
func readUInt64() throws -> UInt64
@discardableResult
func writeInt64(_ value: Int64) -> Self
func readInt64() throws -> Int64
@discardableResult
func writeDouble(_ value: Double) -> Self
func readDouble() throws -> Double
@ -69,6 +77,7 @@ open class ByteArray: ByteArrayConvertible {
static let sizeOfInt24: Int = 3
static let sizeOfInt32: Int = 4
static let sizeOfFloat: Int = 4
static let sizeOfInt64: Int = 8
static let sizeOfDouble: Int = 8
public enum Error: Swift.Error {
@ -211,6 +220,31 @@ open class ByteArray: ByteArrayConvertible {
writeBytes(value.bigEndian.data)
}
@discardableResult
open func writeUInt64(_ value: UInt64) -> Self {
writeBytes(value.bigEndian.data)
}
open func readUInt64() throws -> UInt64 {
guard ByteArray.sizeOfInt64 <= bytesAvailable else {
throw ByteArray.Error.eof
}
position += ByteArray.sizeOfInt64
return UInt64(data: data[position - ByteArray.sizeOfInt64..<position]).bigEndian
}
open func writeInt64(_ value: Int64) -> Self {
writeBytes(value.bigEndian.data)
}
open func readInt64() throws -> Int64 {
guard ByteArray.sizeOfInt64 <= bytesAvailable else {
throw ByteArray.Error.eof
}
position += ByteArray.sizeOfInt64
return Int64(data: data[position - ByteArray.sizeOfInt64..<position]).bigEndian
}
open func readDouble() throws -> Double {
guard ByteArray.sizeOfDouble <= bytesAvailable else {
throw ByteArray.Error.eof

View File

@ -0,0 +1,5 @@
import Foundation
protocol CustomXmlStringConvertible {
var xmlString: String { get }
}

View File

@ -0,0 +1,30 @@
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:EVENT
#EXT-X-MAP:URI="SampleVideo_360x240_5mb@m4v/init.mp4"
#EXT-X-DISCONTINUITY
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/0.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/1.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/2.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/3.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/4.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/5.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/6.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/7.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/8.m4s
#EXTINF:6.000000,
SampleVideo_360x240_5mb@m4v/9.m4s
#EXTINF:4.866667,
SampleVideo_360x240_5mb@m4v/10.m4s
#EXT-X-ENDLIST

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,19 +0,0 @@
import Foundation
import XCTest
@testable import HaishinKit
final class MP4SamplerTests: XCTestCase {
func testMain() {
guard Config.enabledTimerTest else {
return
}
let bundle: Bundle = Bundle(for: type(of: self))
let url: URL = URL(fileURLWithPath: bundle.path(forResource: "SampleVideo_360x240_5mb", ofType: "mp4")!)
let sampler: MP4Sampler = MP4Sampler()
sampler.appendFile(url)
sampler.startRunning()
sleep(10)
sampler.stopRunning()
}
}

View File

@ -0,0 +1,64 @@
import Foundation
import XCTest
@testable import HaishinKit
final class MP4FileHandleTests2: XCTestCase {
func testMP4FileTypeBox() {
let file = makeFMP4File()
let styp = file.getBoxes(by: .styp).first
XCTAssertEqual(styp?.majorBrand, MP4Util.uint32("msdh"))
XCTAssertEqual(styp?.minorVersion, 0)
XCTAssertEqual(styp?.compatibleBrands[0], MP4Util.uint32("msdh"))
XCTAssertEqual(styp?.compatibleBrands[1], MP4Util.uint32("msix"))
let sidx = file.getBoxes(by: .sidx)
XCTAssertEqual(sidx[0].referenceID, 1)
XCTAssertEqual(sidx[0].timescale, 15360)
XCTAssertEqual(sidx[0].earliestPresentationTime, 0)
XCTAssertEqual(sidx[0].firstOffset, 52)
XCTAssertEqual(sidx[0].references[0].type, false)
XCTAssertEqual(sidx[0].references[0].size, 530176)
XCTAssertEqual(sidx[0].references[0].subsegmentDuration, 92160)
XCTAssertEqual(sidx[0].references[0].startsWithSap, true)
XCTAssertEqual(sidx[0].references[0].sapType, 0)
XCTAssertEqual(sidx[0].references[0].sapDeltaTime, 0)
XCTAssertEqual(sidx[1].referenceID, 2)
XCTAssertEqual(sidx[1].timescale, 48000)
XCTAssertEqual(sidx[1].earliestPresentationTime, 0)
XCTAssertEqual(sidx[1].firstOffset, 0)
let mfhd = file.getBoxes(by: .mfhd)
XCTAssertEqual(mfhd[0].sequenceNumber, 1)
let tfhd = file.getBoxes(by: .tfhd)
XCTAssertEqual(tfhd[0].flags, 131128)
XCTAssertEqual(tfhd[0].trackId, 1)
XCTAssertEqual(tfhd[0].baseDataOffset, 0)
XCTAssertEqual(tfhd[0].sampleDescriptionIndex, 0)
XCTAssertEqual(tfhd[0].defaultSampleDuration, 1024)
XCTAssertEqual(tfhd[0].defaultSampleSize, 22696)
XCTAssertEqual(tfhd[0].defaultSampleFlags, 16842752)
let tfdt = file.getBoxes(by: .tfdt)
XCTAssertEqual(tfdt[0].version, 1)
XCTAssertEqual(tfdt[0].baseMediaDecodeTime, 0)
XCTAssertEqual(tfdt[1].version, 1)
XCTAssertEqual(tfdt[1].baseMediaDecodeTime, 0)
let trun = file.getBoxes(by: .trun)
}
private func makeInitMP4File() -> MP4FileReader {
let bundle = Bundle(for: type(of: self))
let url = URL(fileURLWithPath: bundle.path(forResource: "SampleVideo_360x240_5mb@m4v/init", ofType: "mp4")!)
return try! MP4FileReader(forReadingFrom: url).execute()
}
private func makeFMP4File() -> MP4FileReader {
let bundle = Bundle(for: type(of: self))
let url = URL(fileURLWithPath: bundle.path(forResource: "SampleVideo_360x240_5mb@m4v/0", ofType: "m4s")!)
return try! MP4FileReader(forReadingFrom: url).execute()
}
}

View File

@ -0,0 +1,32 @@
import Foundation
import XCTest
@testable import HaishinKit
final class MP4FragmentFileReadTests: XCTestCase {
func testMP4FileTypeBox() {
let file = makeMP4File()
let ftyp: MP4FileTypeBox? = try? file.getBoxes(by: .ftyp).first
XCTAssertEqual(ftyp?.minorVersion, 512)
}
func testMP4SampleSizeBox() {
let file = makeMP4File()
let stsz: [MP4SampleSizeBox]? = try? file.getBoxes(by: .stsz)
XCTAssertEqual(stsz?.first?.entries[0], 22696)
XCTAssertEqual(stsz?.first?.entries[1], 807)
XCTAssertEqual(stsz?.first?.entries[2], 660)
XCTAssertEqual(stsz?.last?.entries[0], 1011)
XCTAssertEqual(stsz?.last?.entries[1], 1026)
XCTAssertEqual(stsz?.last?.entries[2], 1030)
}
private func makeMP4File() -> MP4FileHandle {
let bundle = Bundle(for: type(of: self))
let url = URL(fileURLWithPath: bundle.path(forResource: "SampleVideo_360x240_5mb", ofType: "mp4")!)
var file = try! MP4FileHandle(forReadingFrom: url)
try! file.load()
return file
}
}

View File

@ -0,0 +1,15 @@
import Foundation
import XCTest
@testable import HaishinKit
final class MP4UtilTests: XCTestCase {
func testString() {
XCTAssertEqual("msdh", MP4Util.string(1836278888))
}
func testUInt32() {
XCTAssertEqual(1836278888, MP4Util.uint32("msdh"))
}
}

View File

@ -0,0 +1,78 @@
import Foundation
import XCTest
@testable import HaishinKit
final class MP4FileHandleTests: XCTestCase {
func testMP4FileTypeBox() {
let file = makeMP4File()
let ftyp: MP4FileTypeBox? = file.getBoxes(by: .ftyp).first
XCTAssertEqual(ftyp?.minorVersion, 512)
}
func testMP4SampleSizeBox() {
let file = makeMP4File()
let stsz: [MP4SampleSizeBox]? = file.getBoxes(by: .stsz)
XCTAssertEqual(stsz?.first?.entries[0], 22696)
XCTAssertEqual(stsz?.first?.entries[1], 807)
XCTAssertEqual(stsz?.first?.entries[2], 660)
XCTAssertEqual(stsz?.last?.entries[0], 1011)
XCTAssertEqual(stsz?.last?.entries[1], 1026)
XCTAssertEqual(stsz?.last?.entries[2], 1030)
}
func testMP4SampleDescriptionBox() {
let file = makeMP4File()
let stsz = file.getBoxes(by: .stsd)
}
func testMP4ChunkOffsetBox() {
let file = makeMP4File()
let stco = file.getBoxes(by: .stco).first
XCTAssertEqual(stco?.entries[0], 1059)
XCTAssertEqual(stco?.entries[1], 26801)
}
func testMP4EditListBox() {
let file = makeMP4File()
let elst = file.getBoxes(by: .elst).first
XCTAssertEqual(elst?.entries[0].mediaRate, 65536)
XCTAssertEqual(elst?.entries[0].mediaTime, 0)
XCTAssertEqual(elst?.entries[0].segmentDuration, 64867)
}
func testMP4SampleToChunkBox() {
let file = makeMP4File()
let stsc = file.getBoxes(by: .stsc).first
XCTAssertEqual(stsc?.entries[0].firstChunk, 1)
XCTAssertEqual(stsc?.entries[0].samplesPerChunk, 1)
XCTAssertEqual(stsc?.entries[0].sampleDescriptionIndex, 1)
}
func testMP4SyncSampleBox() {
let file = makeMP4File()
let stss = file.getBoxes(by: .stss).first
XCTAssertEqual(stss?.entries[0], 1)
XCTAssertEqual(stss?.entries[1], 127)
XCTAssertEqual(stss?.entries[2], 196)
}
func testMP4MediaHeaderBox() {
let file = makeMP4File()
let mvhd = file.getBoxes(by: .mvhd).first
XCTAssertEqual(mvhd?.creationTime, 2082844800)
XCTAssertEqual(mvhd?.modificationTime, 3488636519)
XCTAssertEqual(mvhd?.timeScale, 1000)
let mdhd = file.getBoxes(by: .mdhd).first
XCTAssertEqual(mdhd?.creationTime, 2082844800)
XCTAssertEqual(mdhd?.modificationTime, 2082844800)
XCTAssertEqual(mdhd?.timeScale, 15360)
}
private func makeMP4File() -> MP4FileReader {
let bundle = Bundle(for: type(of: self))
let url = URL(fileURLWithPath: bundle.path(forResource: "SampleVideo_360x240_5mb", ofType: "mp4")!)
return try! MP4FileReader(forReadingFrom: url).execute()
}
}

View File

@ -10,7 +10,7 @@ final class RTMPConnectionTests: XCTestCase {
let connection: RTMPConnection = RTMPConnection()
let stream: RTMPStream = RTMPStream(connection: connection)
connection.connect("rtmp://localhost:1935/live")
stream.appendFile(url)
// stream.appendFile(url)
stream.publish("live")
sleep(10000)
}