protocolized MP4Boxes
This commit is contained in:
parent
36c35833a2
commit
9defc2d67b
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -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>
|
|
@ -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 */,
|
||||
|
|
|
@ -26,6 +26,7 @@ let package = Package(
|
|||
"HTTP",
|
||||
"ISO",
|
||||
"Media",
|
||||
"MP4",
|
||||
"Net",
|
||||
"PiP",
|
||||
"RTMP",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
protocol MP4FullBox: MP4BoxConvertible {
|
||||
var version: UInt8 { get }
|
||||
var flags: UInt32 { get }
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import Foundation
|
||||
|
||||
protocol MP4SampleEntry: MP4BoxConvertible {
|
||||
var dataReferenceIndex: UInt16 { get }
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import Foundation
|
||||
|
||||
protocol CustomXmlStringConvertible {
|
||||
var xmlString: String { get }
|
||||
}
|
Binary file not shown.
|
@ -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.
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue