Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b0e88e182a | |||
| 3ebd933b6c | |||
| db0814ca4c | |||
| 3a9e0efb73 | |||
| 7bcc82a03e | |||
| 293e021cf6 | |||
| 340ab086d2 | |||
| f025f807f4 | |||
| 90b086c7a3 | |||
| cf56d72598 | |||
| b22f61c4e2 | |||
| 7d1311311c | |||
| a508cecc32 | |||
| 7657d27854 | |||
| 7c3aae8003 | |||
| 8c2596c23e | |||
| 681b1cd107 | |||
| 13094108e3 | |||
| abd55c5511 | |||
| fc52abe7bd | |||
| fa10e28281 | |||
| c624fbe78c | |||
| 19a355e697 | |||
| 7799f0f613 | |||
| ecf3b557cf | |||
| 58d83ed0dc | |||
| b167bda396 | |||
| 336a6e2432 | |||
| 8acddcda90 | |||
| cf2fc51fea | |||
| ae5f68661e | |||
| e30467dc13 | |||
| a217868694 | |||
| 3053a16cd3 | |||
| d2d7140312 | |||
| e93549619e | |||
| 0196b1be79 | |||
| 23ab30e668 | |||
| 42bd5c1f6b | |||
| 41393ffed8 | |||
| 2b236b2a35 | |||
| e59699b7fd | |||
| e9f3c66a3b | |||
| dc1dc18824 | |||
| 014e73f44c | |||
| ee7c25727b | |||
| 79f0ba930b | |||
| c5edbfba8e | |||
| 91c689a41f | |||
| ba48a45c0a | |||
| af09e79e74 | |||
| e4327de704 | |||
| 9760129763 | |||
| dc7f8d1d10 | |||
| 8f8bedb93d | |||
| 9428dc2c7f | |||
| e6c0d148ab | |||
| 0ca5c18c19 | |||
| cb98f0e70d | |||
| 06f1e40fba | |||
| dfbc4a51ff | |||
| f045bb14e4 | |||
| ebafc22eca | |||
| 6e3e92a6b9 | |||
| 26a47ffccc | |||
| e57d3e3806 | |||
| b73bfee6ac | |||
| 09398d0cb3 | |||
| f69b586d70 | |||
| cfb84d31fb | |||
| 984bcb8e15 | |||
| d7164f8b76 | |||
| fb7ad31bf0 | |||
| ec61b1f7ec | |||
| ba1280af3a | |||
| 9e99357113 | |||
| 8057a3e013 | |||
| 981b0dd2cd | |||
| d93d4b9bf5 | |||
| df6164fed1 | |||
| ba19663059 | |||
| 0967800b23 | |||
| d0ef23d887 | |||
| 807d83c263 | |||
| 28d20814f6 | |||
| 4c385f6e89 | |||
| 8708e04c0d | |||
| edcb2efaa0 | |||
| e47f3154a8 | |||
| 3842ff80bf | |||
| d3d5f6cb24 | |||
| 7062cdcf7c | |||
| 37bfd377d6 | |||
| bbe11292de | |||
| 2b51c69007 | |||
| eb0edf85d2 | |||
| 5b097680f9 | |||
| fb9ecaabad | |||
| 6a971f872f | |||
| c86a88487d | |||
| beac14592c | |||
| 24f8b42880 | |||
| 11848599b5 | |||
| d45e3770d6 | |||
| d883f023ca | |||
| 0d269dcf25 | |||
| 3b7ad944af | |||
| 3e92282448 | |||
| f2f25ea19d | |||
| b910e120ff | |||
| 3d5c88416b | |||
| 79744c0096 | |||
| 191a603f98 | |||
| fff7d4513d | |||
| 4f58fd019e | |||
| a440270bb0 | |||
| d57d987e67 | |||
| 204aaa6d09 | |||
| 3c58ff7e49 | |||
| 320307bc2a | |||
| 0e7b2475d7 | |||
| 531c92df51 | |||
| 91e72e2211 | |||
| 5cc50b6d47 | |||
| 659dfb81d0 | |||
| 4865bdc508 | |||
| e63026415a | |||
| 21335ed3f0 | |||
| ba1f7a383c | |||
| b109289bc1 | |||
| 1e56fc2941 | |||
| 14a3b2eb4b | |||
| 510d796ab1 | |||
| 48dac15d54 | |||
| 13bc9ad1eb | |||
| 1e6e42f2c6 | |||
| 519b685855 |
+22
-1
@@ -1,4 +1,25 @@
|
||||
# infer
|
||||
infer-out/
|
||||
|
||||
#CocoaPods
|
||||
# Xcode
|
||||
.DS_Store
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
*.xcworkspace
|
||||
!default.xcworkspace
|
||||
xcuserdata
|
||||
profile
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
.idea/
|
||||
|
||||
# CocoaPods
|
||||
Pods/
|
||||
Podfile.lock
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
language: objective-c
|
||||
osx_image: xcode7
|
||||
xcode_workspace: LFLiveKit.xcworkspace
|
||||
xcode_project: LFLiveKit/LFLiveKit.xcodeproj
|
||||
xcode_scheme: LFLiveKit
|
||||
|
||||
script:
|
||||
- xctool -workspace LFLiveKit.xcworkspace -scheme 'LFLiveKit' -configuration Release -sdk iphonesimulator -arch i386 build
|
||||
- xctool -project LFLiveKit/LFLiveKit.xcodeproj -scheme 'LFLiveKit' -configuration Release -sdk iphonesimulator -arch i386 build
|
||||
+8
-11
@@ -2,25 +2,22 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "LFLiveKit"
|
||||
s.version = "1.7.0"
|
||||
s.version = "2.2.4"
|
||||
s.summary = "LaiFeng ios Live. LFLiveKit."
|
||||
s.homepage = "https://github.com/chenliming777"
|
||||
s.license = { :type => "MIT", :file => "LICENSE" }
|
||||
s.author = { "chenliming" => "chenliming777@qq.com" }
|
||||
s.platform = :ios, "8.0"
|
||||
s.ios.deployment_target = "8.0"
|
||||
s.platform = :ios, "7.0"
|
||||
s.ios.deployment_target = "7.0"
|
||||
s.source = { :git => "https://github.com/LaiFengiOS/LFLiveKit.git", :tag => "#{s.version}" }
|
||||
s.source_files = "LFLiveKit/**/*.{*}"
|
||||
s.public_header_files = "LFLiveKit/**/*.h"
|
||||
s.source_files = "LFLiveKit/**/*.{h,m,mm,cpp,c}"
|
||||
#s.public_header_files = "LFLiveKit/LFLiveKit/**/*.h"
|
||||
s.public_header_files = ['LFLiveKit/LFLiveKit/*.h', 'LFLiveKit/LFLiveKit/objects/*.h', 'LFLiveKit/LFLiveKit/configuration/*.h']
|
||||
|
||||
s.frameworks = "VideoToolbox", "AudioToolbox","AVFoundation","Foundation","UIKit"
|
||||
s.library = "z"
|
||||
s.libraries = "c++", "z"
|
||||
|
||||
s.requires_arc = true
|
||||
|
||||
s.dependency "CocoaAsyncSocket", "~> 7.4.1"
|
||||
s.dependency 'LMGPUImage', '~> 0.1.9'
|
||||
s.dependency "pili-librtmp", "~> 1.0.2"
|
||||
s.dependency "YYDispatchQueuePool"
|
||||
s.ios.vendored_frameworks = 'Vendor/GPUImage.framework','Vendor/pili-librtmp.framework'
|
||||
|
||||
end
|
||||
|
||||
@@ -1,756 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
84001F8E1D0015D10026C63F /* LFLiveKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001F8D1D0015D10026C63F /* LFLiveKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84001F951D0015D10026C63F /* LFLiveKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001F8A1D0015D10026C63F /* LFLiveKit.framework */; };
|
||||
84001F9A1D0015D10026C63F /* LFLiveKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001F991D0015D10026C63F /* LFLiveKitTests.m */; };
|
||||
84001FD11D0016380026C63F /* LFAudioCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FA51D0016380026C63F /* LFAudioCapture.h */; };
|
||||
84001FD21D0016380026C63F /* LFAudioCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FA61D0016380026C63F /* LFAudioCapture.m */; };
|
||||
84001FD31D0016380026C63F /* LFVideoCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FA71D0016380026C63F /* LFVideoCapture.h */; };
|
||||
84001FD41D0016380026C63F /* LFVideoCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FA81D0016380026C63F /* LFVideoCapture.m */; };
|
||||
84001FD51D0016380026C63F /* LFAudioEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FAA1D0016380026C63F /* LFAudioEncoding.h */; };
|
||||
84001FD61D0016380026C63F /* LFHardwareAudioEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FAB1D0016380026C63F /* LFHardwareAudioEncoder.h */; };
|
||||
84001FD71D0016380026C63F /* LFHardwareAudioEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FAC1D0016380026C63F /* LFHardwareAudioEncoder.m */; };
|
||||
84001FD81D0016380026C63F /* LFHardwareVideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FAD1D0016380026C63F /* LFHardwareVideoEncoder.h */; };
|
||||
84001FD91D0016380026C63F /* LFHardwareVideoEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FAE1D0016380026C63F /* LFHardwareVideoEncoder.m */; };
|
||||
84001FDA1D0016380026C63F /* LFVideoEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FAF1D0016380026C63F /* LFVideoEncoding.h */; };
|
||||
84001FDB1D0016380026C63F /* LFLiveAudioConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FB11D0016380026C63F /* LFLiveAudioConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84001FDC1D0016380026C63F /* LFLiveAudioConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FB21D0016380026C63F /* LFLiveAudioConfiguration.m */; };
|
||||
84001FDD1D0016380026C63F /* LFLiveVideoConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FB31D0016380026C63F /* LFLiveVideoConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84001FDE1D0016380026C63F /* LFLiveVideoConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FB41D0016380026C63F /* LFLiveVideoConfiguration.m */; };
|
||||
84001FDF1D0016380026C63F /* LFGPUImageBeautyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FB61D0016380026C63F /* LFGPUImageBeautyFilter.h */; };
|
||||
84001FE01D0016380026C63F /* LFGPUImageBeautyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FB71D0016380026C63F /* LFGPUImageBeautyFilter.m */; };
|
||||
84001FE11D0016380026C63F /* LFGPUImageEmptyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FB81D0016380026C63F /* LFGPUImageEmptyFilter.h */; };
|
||||
84001FE21D0016380026C63F /* LFGPUImageEmptyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FB91D0016380026C63F /* LFGPUImageEmptyFilter.m */; };
|
||||
84001FE31D0016380026C63F /* LFLiveSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FBA1D0016380026C63F /* LFLiveSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84001FE41D0016380026C63F /* LFLiveSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FBB1D0016380026C63F /* LFLiveSession.m */; };
|
||||
84001FE51D0016380026C63F /* LFAudioFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FBD1D0016380026C63F /* LFAudioFrame.h */; };
|
||||
84001FE61D0016380026C63F /* LFAudioFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FBE1D0016380026C63F /* LFAudioFrame.m */; };
|
||||
84001FE71D0016380026C63F /* LFFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FBF1D0016380026C63F /* LFFrame.h */; };
|
||||
84001FE81D0016380026C63F /* LFFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FC01D0016380026C63F /* LFFrame.m */; };
|
||||
84001FE91D0016380026C63F /* LFLiveDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FC11D0016380026C63F /* LFLiveDebug.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84001FEA1D0016380026C63F /* LFLiveDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FC21D0016380026C63F /* LFLiveDebug.m */; };
|
||||
84001FEB1D0016380026C63F /* LFLiveStreamInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FC31D0016380026C63F /* LFLiveStreamInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84001FEC1D0016380026C63F /* LFLiveStreamInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FC41D0016380026C63F /* LFLiveStreamInfo.m */; };
|
||||
84001FED1D0016380026C63F /* LFVideoFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FC51D0016380026C63F /* LFVideoFrame.h */; };
|
||||
84001FEE1D0016380026C63F /* LFVideoFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FC61D0016380026C63F /* LFVideoFrame.m */; };
|
||||
84001FEF1D0016380026C63F /* LFStreamingBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FCA1D0016380026C63F /* LFStreamingBuffer.h */; };
|
||||
84001FF01D0016380026C63F /* LFStreamingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FCB1D0016380026C63F /* LFStreamingBuffer.m */; };
|
||||
84001FF11D0016380026C63F /* LFStreamRtmpSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FCC1D0016380026C63F /* LFStreamRtmpSocket.h */; };
|
||||
84001FF21D0016380026C63F /* LFStreamRtmpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FCD1D0016380026C63F /* LFStreamRtmpSocket.m */; };
|
||||
84001FF31D0016380026C63F /* LFStreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FCE1D0016380026C63F /* LFStreamSocket.h */; };
|
||||
84001FF41D0016380026C63F /* NSMutableArray+LFAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FCF1D0016380026C63F /* NSMutableArray+LFAdd.h */; };
|
||||
84001FF51D0016380026C63F /* NSMutableArray+LFAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FD01D0016380026C63F /* NSMutableArray+LFAdd.m */; };
|
||||
84001FF71D0017590026C63F /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FF61D0017590026C63F /* AVFoundation.framework */; };
|
||||
84001FF91D00175D0026C63F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FF81D00175D0026C63F /* Foundation.framework */; };
|
||||
84001FFB1D0017630026C63F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FFA1D0017630026C63F /* UIKit.framework */; };
|
||||
84001FFD1D0017680026C63F /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FFC1D0017680026C63F /* AudioToolbox.framework */; };
|
||||
84001FFF1D00176C0026C63F /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FFE1D00176C0026C63F /* VideoToolbox.framework */; };
|
||||
840020011D0017850026C63F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 840020001D0017850026C63F /* libz.tbd */; };
|
||||
840762C51D07BC7D000FD0BF /* LFStreamTcpSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 840762C31D07BC7D000FD0BF /* LFStreamTcpSocket.h */; };
|
||||
840762C61D07BC7D000FD0BF /* LFStreamTcpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 840762C41D07BC7D000FD0BF /* LFStreamTcpSocket.m */; };
|
||||
840762D61D07BC8B000FD0BF /* amf.c in Sources */ = {isa = PBXBuildFile; fileRef = 840762C91D07BC8B000FD0BF /* amf.c */; };
|
||||
840762D71D07BC8B000FD0BF /* amf.h in Headers */ = {isa = PBXBuildFile; fileRef = 840762CA1D07BC8B000FD0BF /* amf.h */; };
|
||||
840762D81D07BC8B000FD0BF /* avc.c in Sources */ = {isa = PBXBuildFile; fileRef = 840762CB1D07BC8B000FD0BF /* avc.c */; };
|
||||
840762D91D07BC8B000FD0BF /* avc.h in Headers */ = {isa = PBXBuildFile; fileRef = 840762CC1D07BC8B000FD0BF /* avc.h */; };
|
||||
840762DA1D07BC8B000FD0BF /* flv.c in Sources */ = {isa = PBXBuildFile; fileRef = 840762CD1D07BC8B000FD0BF /* flv.c */; };
|
||||
840762DB1D07BC8B000FD0BF /* flv.h in Headers */ = {isa = PBXBuildFile; fileRef = 840762CE1D07BC8B000FD0BF /* flv.h */; };
|
||||
840762DC1D07BC8B000FD0BF /* info.c in Sources */ = {isa = PBXBuildFile; fileRef = 840762CF1D07BC8B000FD0BF /* info.c */; };
|
||||
840762DD1D07BC8B000FD0BF /* info.h in Headers */ = {isa = PBXBuildFile; fileRef = 840762D01D07BC8B000FD0BF /* info.h */; };
|
||||
840762DE1D07BC8B000FD0BF /* types.c in Sources */ = {isa = PBXBuildFile; fileRef = 840762D11D07BC8B000FD0BF /* types.c */; };
|
||||
840762DF1D07BC8B000FD0BF /* types.h in Headers */ = {isa = PBXBuildFile; fileRef = 840762D21D07BC8B000FD0BF /* types.h */; };
|
||||
840762E01D07BC8B000FD0BF /* LFFlvPackage.h in Headers */ = {isa = PBXBuildFile; fileRef = 840762D31D07BC8B000FD0BF /* LFFlvPackage.h */; };
|
||||
840762E11D07BC8B000FD0BF /* LFFlvPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 840762D41D07BC8B000FD0BF /* LFFlvPackage.m */; };
|
||||
840762E21D07BC8B000FD0BF /* LFStreamPackage.h in Headers */ = {isa = PBXBuildFile; fileRef = 840762D51D07BC8B000FD0BF /* LFStreamPackage.h */; };
|
||||
AD7F89B4621A7EFEBEA72D49 /* libPods-LFLiveKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
84001F961D0015D10026C63F /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84001F811D0015D10026C63F /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 84001F891D0015D10026C63F;
|
||||
remoteInfo = LFLiveKit;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
84001F8A1D0015D10026C63F /* LFLiveKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LFLiveKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84001F8D1D0015D10026C63F /* LFLiveKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LFLiveKit.h; sourceTree = "<group>"; };
|
||||
84001F8F1D0015D10026C63F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
84001F941D0015D10026C63F /* LFLiveKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LFLiveKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84001F991D0015D10026C63F /* LFLiveKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LFLiveKitTests.m; sourceTree = "<group>"; };
|
||||
84001F9B1D0015D10026C63F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
84001FA51D0016380026C63F /* LFAudioCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioCapture.h; sourceTree = "<group>"; };
|
||||
84001FA61D0016380026C63F /* LFAudioCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFAudioCapture.m; sourceTree = "<group>"; };
|
||||
84001FA71D0016380026C63F /* LFVideoCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoCapture.h; sourceTree = "<group>"; };
|
||||
84001FA81D0016380026C63F /* LFVideoCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoCapture.m; sourceTree = "<group>"; };
|
||||
84001FAA1D0016380026C63F /* LFAudioEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioEncoding.h; sourceTree = "<group>"; };
|
||||
84001FAB1D0016380026C63F /* LFHardwareAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHardwareAudioEncoder.h; sourceTree = "<group>"; };
|
||||
84001FAC1D0016380026C63F /* LFHardwareAudioEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHardwareAudioEncoder.m; sourceTree = "<group>"; };
|
||||
84001FAD1D0016380026C63F /* LFHardwareVideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHardwareVideoEncoder.h; sourceTree = "<group>"; };
|
||||
84001FAE1D0016380026C63F /* LFHardwareVideoEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHardwareVideoEncoder.m; sourceTree = "<group>"; };
|
||||
84001FAF1D0016380026C63F /* LFVideoEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoEncoding.h; sourceTree = "<group>"; };
|
||||
84001FB11D0016380026C63F /* LFLiveAudioConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveAudioConfiguration.h; sourceTree = "<group>"; };
|
||||
84001FB21D0016380026C63F /* LFLiveAudioConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveAudioConfiguration.m; sourceTree = "<group>"; };
|
||||
84001FB31D0016380026C63F /* LFLiveVideoConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveVideoConfiguration.h; sourceTree = "<group>"; };
|
||||
84001FB41D0016380026C63F /* LFLiveVideoConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveVideoConfiguration.m; sourceTree = "<group>"; };
|
||||
84001FB61D0016380026C63F /* LFGPUImageBeautyFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFGPUImageBeautyFilter.h; sourceTree = "<group>"; };
|
||||
84001FB71D0016380026C63F /* LFGPUImageBeautyFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFGPUImageBeautyFilter.m; sourceTree = "<group>"; };
|
||||
84001FB81D0016380026C63F /* LFGPUImageEmptyFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFGPUImageEmptyFilter.h; sourceTree = "<group>"; };
|
||||
84001FB91D0016380026C63F /* LFGPUImageEmptyFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFGPUImageEmptyFilter.m; sourceTree = "<group>"; };
|
||||
84001FBA1D0016380026C63F /* LFLiveSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveSession.h; sourceTree = "<group>"; };
|
||||
84001FBB1D0016380026C63F /* LFLiveSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveSession.m; sourceTree = "<group>"; };
|
||||
84001FBD1D0016380026C63F /* LFAudioFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioFrame.h; sourceTree = "<group>"; };
|
||||
84001FBE1D0016380026C63F /* LFAudioFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFAudioFrame.m; sourceTree = "<group>"; };
|
||||
84001FBF1D0016380026C63F /* LFFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFFrame.h; sourceTree = "<group>"; };
|
||||
84001FC01D0016380026C63F /* LFFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFFrame.m; sourceTree = "<group>"; };
|
||||
84001FC11D0016380026C63F /* LFLiveDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveDebug.h; sourceTree = "<group>"; };
|
||||
84001FC21D0016380026C63F /* LFLiveDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveDebug.m; sourceTree = "<group>"; };
|
||||
84001FC31D0016380026C63F /* LFLiveStreamInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveStreamInfo.h; sourceTree = "<group>"; };
|
||||
84001FC41D0016380026C63F /* LFLiveStreamInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveStreamInfo.m; sourceTree = "<group>"; };
|
||||
84001FC51D0016380026C63F /* LFVideoFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoFrame.h; sourceTree = "<group>"; };
|
||||
84001FC61D0016380026C63F /* LFVideoFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoFrame.m; sourceTree = "<group>"; };
|
||||
84001FCA1D0016380026C63F /* LFStreamingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamingBuffer.h; sourceTree = "<group>"; };
|
||||
84001FCB1D0016380026C63F /* LFStreamingBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamingBuffer.m; sourceTree = "<group>"; };
|
||||
84001FCC1D0016380026C63F /* LFStreamRtmpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamRtmpSocket.h; sourceTree = "<group>"; };
|
||||
84001FCD1D0016380026C63F /* LFStreamRtmpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamRtmpSocket.m; sourceTree = "<group>"; };
|
||||
84001FCE1D0016380026C63F /* LFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamSocket.h; sourceTree = "<group>"; };
|
||||
84001FCF1D0016380026C63F /* NSMutableArray+LFAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+LFAdd.h"; sourceTree = "<group>"; };
|
||||
84001FD01D0016380026C63F /* NSMutableArray+LFAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+LFAdd.m"; sourceTree = "<group>"; };
|
||||
84001FF61D0017590026C63F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
84001FF81D00175D0026C63F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
84001FFA1D0017630026C63F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
84001FFC1D0017680026C63F /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
84001FFE1D00176C0026C63F /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
|
||||
840020001D0017850026C63F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
840762C31D07BC7D000FD0BF /* LFStreamTcpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamTcpSocket.h; sourceTree = "<group>"; };
|
||||
840762C41D07BC7D000FD0BF /* LFStreamTcpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamTcpSocket.m; sourceTree = "<group>"; };
|
||||
840762C91D07BC8B000FD0BF /* amf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amf.c; sourceTree = "<group>"; };
|
||||
840762CA1D07BC8B000FD0BF /* amf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = amf.h; sourceTree = "<group>"; };
|
||||
840762CB1D07BC8B000FD0BF /* avc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = avc.c; sourceTree = "<group>"; };
|
||||
840762CC1D07BC8B000FD0BF /* avc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = avc.h; sourceTree = "<group>"; };
|
||||
840762CD1D07BC8B000FD0BF /* flv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = flv.c; sourceTree = "<group>"; };
|
||||
840762CE1D07BC8B000FD0BF /* flv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = flv.h; sourceTree = "<group>"; };
|
||||
840762CF1D07BC8B000FD0BF /* info.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = info.c; sourceTree = "<group>"; };
|
||||
840762D01D07BC8B000FD0BF /* info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = info.h; sourceTree = "<group>"; };
|
||||
840762D11D07BC8B000FD0BF /* types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = types.c; sourceTree = "<group>"; };
|
||||
840762D21D07BC8B000FD0BF /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = "<group>"; };
|
||||
840762D31D07BC8B000FD0BF /* LFFlvPackage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFFlvPackage.h; sourceTree = "<group>"; };
|
||||
840762D41D07BC8B000FD0BF /* LFFlvPackage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFFlvPackage.m; sourceTree = "<group>"; };
|
||||
840762D51D07BC8B000FD0BF /* LFStreamPackage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamPackage.h; sourceTree = "<group>"; };
|
||||
A17586B27CD6843997425CCF /* Pods-LFLiveKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LFLiveKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
B75B965E6B94DE4CBCC82EA7 /* Pods-LFLiveKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LFLiveKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LFLiveKit.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
84001F861D0015D10026C63F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
840020011D0017850026C63F /* libz.tbd in Frameworks */,
|
||||
84001FFF1D00176C0026C63F /* VideoToolbox.framework in Frameworks */,
|
||||
84001FFD1D0017680026C63F /* AudioToolbox.framework in Frameworks */,
|
||||
84001FFB1D0017630026C63F /* UIKit.framework in Frameworks */,
|
||||
84001FF91D00175D0026C63F /* Foundation.framework in Frameworks */,
|
||||
84001FF71D0017590026C63F /* AVFoundation.framework in Frameworks */,
|
||||
AD7F89B4621A7EFEBEA72D49 /* libPods-LFLiveKit.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84001F911D0015D10026C63F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84001F951D0015D10026C63F /* LFLiveKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0C07D14560B9E91EA1B59306 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840020001D0017850026C63F /* libz.tbd */,
|
||||
84001FFE1D00176C0026C63F /* VideoToolbox.framework */,
|
||||
84001FFC1D0017680026C63F /* AudioToolbox.framework */,
|
||||
84001FFA1D0017630026C63F /* UIKit.framework */,
|
||||
84001FF81D00175D0026C63F /* Foundation.framework */,
|
||||
84001FF61D0017590026C63F /* AVFoundation.framework */,
|
||||
B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001F801D0015D10026C63F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001F8C1D0015D10026C63F /* LFLiveKit */,
|
||||
84001F981D0015D10026C63F /* LFLiveKitTests */,
|
||||
84001F8B1D0015D10026C63F /* Products */,
|
||||
EDD4B76A07A6817C79BB4E5C /* Pods */,
|
||||
0C07D14560B9E91EA1B59306 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001F8B1D0015D10026C63F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001F8A1D0015D10026C63F /* LFLiveKit.framework */,
|
||||
84001F941D0015D10026C63F /* LFLiveKitTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001F8C1D0015D10026C63F /* LFLiveKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001F8D1D0015D10026C63F /* LFLiveKit.h */,
|
||||
84001FBA1D0016380026C63F /* LFLiveSession.h */,
|
||||
84001FBB1D0016380026C63F /* LFLiveSession.m */,
|
||||
84001FBC1D0016380026C63F /* objects */,
|
||||
84001FB01D0016380026C63F /* configuration */,
|
||||
84001FA41D0016380026C63F /* capture */,
|
||||
84001FA91D0016380026C63F /* coder */,
|
||||
84001FB51D0016380026C63F /* filter */,
|
||||
840762C71D07BC8B000FD0BF /* packet */,
|
||||
84001FC91D0016380026C63F /* publish */,
|
||||
84001F8F1D0015D10026C63F /* Info.plist */,
|
||||
);
|
||||
path = LFLiveKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001F981D0015D10026C63F /* LFLiveKitTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001F991D0015D10026C63F /* LFLiveKitTests.m */,
|
||||
84001F9B1D0015D10026C63F /* Info.plist */,
|
||||
);
|
||||
path = LFLiveKitTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001FA41D0016380026C63F /* capture */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001FA51D0016380026C63F /* LFAudioCapture.h */,
|
||||
84001FA61D0016380026C63F /* LFAudioCapture.m */,
|
||||
84001FA71D0016380026C63F /* LFVideoCapture.h */,
|
||||
84001FA81D0016380026C63F /* LFVideoCapture.m */,
|
||||
);
|
||||
path = capture;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001FA91D0016380026C63F /* coder */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001FAA1D0016380026C63F /* LFAudioEncoding.h */,
|
||||
84001FAB1D0016380026C63F /* LFHardwareAudioEncoder.h */,
|
||||
84001FAC1D0016380026C63F /* LFHardwareAudioEncoder.m */,
|
||||
84001FAD1D0016380026C63F /* LFHardwareVideoEncoder.h */,
|
||||
84001FAE1D0016380026C63F /* LFHardwareVideoEncoder.m */,
|
||||
84001FAF1D0016380026C63F /* LFVideoEncoding.h */,
|
||||
);
|
||||
path = coder;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001FB01D0016380026C63F /* configuration */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001FB11D0016380026C63F /* LFLiveAudioConfiguration.h */,
|
||||
84001FB21D0016380026C63F /* LFLiveAudioConfiguration.m */,
|
||||
84001FB31D0016380026C63F /* LFLiveVideoConfiguration.h */,
|
||||
84001FB41D0016380026C63F /* LFLiveVideoConfiguration.m */,
|
||||
);
|
||||
path = configuration;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001FB51D0016380026C63F /* filter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001FB61D0016380026C63F /* LFGPUImageBeautyFilter.h */,
|
||||
84001FB71D0016380026C63F /* LFGPUImageBeautyFilter.m */,
|
||||
84001FB81D0016380026C63F /* LFGPUImageEmptyFilter.h */,
|
||||
84001FB91D0016380026C63F /* LFGPUImageEmptyFilter.m */,
|
||||
);
|
||||
path = filter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001FBC1D0016380026C63F /* objects */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001FBD1D0016380026C63F /* LFAudioFrame.h */,
|
||||
84001FBE1D0016380026C63F /* LFAudioFrame.m */,
|
||||
84001FBF1D0016380026C63F /* LFFrame.h */,
|
||||
84001FC01D0016380026C63F /* LFFrame.m */,
|
||||
84001FC11D0016380026C63F /* LFLiveDebug.h */,
|
||||
84001FC21D0016380026C63F /* LFLiveDebug.m */,
|
||||
84001FC31D0016380026C63F /* LFLiveStreamInfo.h */,
|
||||
84001FC41D0016380026C63F /* LFLiveStreamInfo.m */,
|
||||
84001FC51D0016380026C63F /* LFVideoFrame.h */,
|
||||
84001FC61D0016380026C63F /* LFVideoFrame.m */,
|
||||
);
|
||||
path = objects;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84001FC91D0016380026C63F /* publish */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84001FCE1D0016380026C63F /* LFStreamSocket.h */,
|
||||
84001FCA1D0016380026C63F /* LFStreamingBuffer.h */,
|
||||
84001FCB1D0016380026C63F /* LFStreamingBuffer.m */,
|
||||
84001FCC1D0016380026C63F /* LFStreamRtmpSocket.h */,
|
||||
84001FCD1D0016380026C63F /* LFStreamRtmpSocket.m */,
|
||||
840762C31D07BC7D000FD0BF /* LFStreamTcpSocket.h */,
|
||||
840762C41D07BC7D000FD0BF /* LFStreamTcpSocket.m */,
|
||||
84001FCF1D0016380026C63F /* NSMutableArray+LFAdd.h */,
|
||||
84001FD01D0016380026C63F /* NSMutableArray+LFAdd.m */,
|
||||
);
|
||||
name = publish;
|
||||
path = upload;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840762C71D07BC8B000FD0BF /* packet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840762C81D07BC8B000FD0BF /* flv */,
|
||||
840762D31D07BC8B000FD0BF /* LFFlvPackage.h */,
|
||||
840762D41D07BC8B000FD0BF /* LFFlvPackage.m */,
|
||||
840762D51D07BC8B000FD0BF /* LFStreamPackage.h */,
|
||||
);
|
||||
name = packet;
|
||||
path = LFLiveKit/packet;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
840762C81D07BC8B000FD0BF /* flv */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840762C91D07BC8B000FD0BF /* amf.c */,
|
||||
840762CA1D07BC8B000FD0BF /* amf.h */,
|
||||
840762CB1D07BC8B000FD0BF /* avc.c */,
|
||||
840762CC1D07BC8B000FD0BF /* avc.h */,
|
||||
840762CD1D07BC8B000FD0BF /* flv.c */,
|
||||
840762CE1D07BC8B000FD0BF /* flv.h */,
|
||||
840762CF1D07BC8B000FD0BF /* info.c */,
|
||||
840762D01D07BC8B000FD0BF /* info.h */,
|
||||
840762D11D07BC8B000FD0BF /* types.c */,
|
||||
840762D21D07BC8B000FD0BF /* types.h */,
|
||||
);
|
||||
path = flv;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EDD4B76A07A6817C79BB4E5C /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A17586B27CD6843997425CCF /* Pods-LFLiveKit.debug.xcconfig */,
|
||||
B75B965E6B94DE4CBCC82EA7 /* Pods-LFLiveKit.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
84001F871D0015D10026C63F /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
840762DD1D07BC8B000FD0BF /* info.h in Headers */,
|
||||
84001FDB1D0016380026C63F /* LFLiveAudioConfiguration.h in Headers */,
|
||||
840762E01D07BC8B000FD0BF /* LFFlvPackage.h in Headers */,
|
||||
84001FDD1D0016380026C63F /* LFLiveVideoConfiguration.h in Headers */,
|
||||
840762D71D07BC8B000FD0BF /* amf.h in Headers */,
|
||||
84001FE31D0016380026C63F /* LFLiveSession.h in Headers */,
|
||||
840762C51D07BC7D000FD0BF /* LFStreamTcpSocket.h in Headers */,
|
||||
840762DF1D07BC8B000FD0BF /* types.h in Headers */,
|
||||
84001FEB1D0016380026C63F /* LFLiveStreamInfo.h in Headers */,
|
||||
84001FE91D0016380026C63F /* LFLiveDebug.h in Headers */,
|
||||
840762E21D07BC8B000FD0BF /* LFStreamPackage.h in Headers */,
|
||||
840762DB1D07BC8B000FD0BF /* flv.h in Headers */,
|
||||
84001FF11D0016380026C63F /* LFStreamRtmpSocket.h in Headers */,
|
||||
84001FE71D0016380026C63F /* LFFrame.h in Headers */,
|
||||
84001FEF1D0016380026C63F /* LFStreamingBuffer.h in Headers */,
|
||||
84001FD61D0016380026C63F /* LFHardwareAudioEncoder.h in Headers */,
|
||||
84001FDF1D0016380026C63F /* LFGPUImageBeautyFilter.h in Headers */,
|
||||
84001FD31D0016380026C63F /* LFVideoCapture.h in Headers */,
|
||||
84001FD11D0016380026C63F /* LFAudioCapture.h in Headers */,
|
||||
84001FF41D0016380026C63F /* NSMutableArray+LFAdd.h in Headers */,
|
||||
84001FE11D0016380026C63F /* LFGPUImageEmptyFilter.h in Headers */,
|
||||
84001FDA1D0016380026C63F /* LFVideoEncoding.h in Headers */,
|
||||
84001FE51D0016380026C63F /* LFAudioFrame.h in Headers */,
|
||||
84001FED1D0016380026C63F /* LFVideoFrame.h in Headers */,
|
||||
84001FD81D0016380026C63F /* LFHardwareVideoEncoder.h in Headers */,
|
||||
84001FD51D0016380026C63F /* LFAudioEncoding.h in Headers */,
|
||||
840762D91D07BC8B000FD0BF /* avc.h in Headers */,
|
||||
84001FF31D0016380026C63F /* LFStreamSocket.h in Headers */,
|
||||
84001F8E1D0015D10026C63F /* LFLiveKit.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
84001F891D0015D10026C63F /* LFLiveKit */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84001F9E1D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKit" */;
|
||||
buildPhases = (
|
||||
8EE9401DCA9508E918B7FB68 /* 📦 Check Pods Manifest.lock */,
|
||||
84001F851D0015D10026C63F /* Sources */,
|
||||
84001F861D0015D10026C63F /* Frameworks */,
|
||||
84001F871D0015D10026C63F /* Headers */,
|
||||
84001F881D0015D10026C63F /* Resources */,
|
||||
817C22141AD3F2EB34365AA3 /* 📦 Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = LFLiveKit;
|
||||
productName = LFLiveKit;
|
||||
productReference = 84001F8A1D0015D10026C63F /* LFLiveKit.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
84001F931D0015D10026C63F /* LFLiveKitTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84001FA11D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKitTests" */;
|
||||
buildPhases = (
|
||||
84001F901D0015D10026C63F /* Sources */,
|
||||
84001F911D0015D10026C63F /* Frameworks */,
|
||||
84001F921D0015D10026C63F /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
84001F971D0015D10026C63F /* PBXTargetDependency */,
|
||||
);
|
||||
name = LFLiveKitTests;
|
||||
productName = LFLiveKitTests;
|
||||
productReference = 84001F941D0015D10026C63F /* LFLiveKitTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
84001F811D0015D10026C63F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0730;
|
||||
ORGANIZATIONNAME = admin;
|
||||
TargetAttributes = {
|
||||
84001F891D0015D10026C63F = {
|
||||
CreatedOnToolsVersion = 7.3;
|
||||
};
|
||||
84001F931D0015D10026C63F = {
|
||||
CreatedOnToolsVersion = 7.3;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 84001F841D0015D10026C63F /* Build configuration list for PBXProject "LFLiveKit" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 84001F801D0015D10026C63F;
|
||||
productRefGroup = 84001F8B1D0015D10026C63F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
84001F891D0015D10026C63F /* LFLiveKit */,
|
||||
84001F931D0015D10026C63F /* LFLiveKitTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
84001F881D0015D10026C63F /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84001F921D0015D10026C63F /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
817C22141AD3F2EB34365AA3 /* 📦 Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "📦 Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
8EE9401DCA9508E918B7FB68 /* 📦 Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "📦 Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
84001F851D0015D10026C63F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
840762DA1D07BC8B000FD0BF /* flv.c in Sources */,
|
||||
84001FE21D0016380026C63F /* LFGPUImageEmptyFilter.m in Sources */,
|
||||
84001FE41D0016380026C63F /* LFLiveSession.m in Sources */,
|
||||
84001FE61D0016380026C63F /* LFAudioFrame.m in Sources */,
|
||||
840762DE1D07BC8B000FD0BF /* types.c in Sources */,
|
||||
84001FDC1D0016380026C63F /* LFLiveAudioConfiguration.m in Sources */,
|
||||
840762D81D07BC8B000FD0BF /* avc.c in Sources */,
|
||||
84001FD41D0016380026C63F /* LFVideoCapture.m in Sources */,
|
||||
84001FE81D0016380026C63F /* LFFrame.m in Sources */,
|
||||
840762D61D07BC8B000FD0BF /* amf.c in Sources */,
|
||||
84001FF01D0016380026C63F /* LFStreamingBuffer.m in Sources */,
|
||||
84001FF51D0016380026C63F /* NSMutableArray+LFAdd.m in Sources */,
|
||||
84001FDE1D0016380026C63F /* LFLiveVideoConfiguration.m in Sources */,
|
||||
84001FD21D0016380026C63F /* LFAudioCapture.m in Sources */,
|
||||
84001FF21D0016380026C63F /* LFStreamRtmpSocket.m in Sources */,
|
||||
840762E11D07BC8B000FD0BF /* LFFlvPackage.m in Sources */,
|
||||
84001FD91D0016380026C63F /* LFHardwareVideoEncoder.m in Sources */,
|
||||
84001FEC1D0016380026C63F /* LFLiveStreamInfo.m in Sources */,
|
||||
84001FEA1D0016380026C63F /* LFLiveDebug.m in Sources */,
|
||||
840762DC1D07BC8B000FD0BF /* info.c in Sources */,
|
||||
84001FEE1D0016380026C63F /* LFVideoFrame.m in Sources */,
|
||||
84001FD71D0016380026C63F /* LFHardwareAudioEncoder.m in Sources */,
|
||||
840762C61D07BC7D000FD0BF /* LFStreamTcpSocket.m in Sources */,
|
||||
84001FE01D0016380026C63F /* LFGPUImageBeautyFilter.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84001F901D0015D10026C63F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84001F9A1D0015D10026C63F /* LFLiveKitTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
84001F971D0015D10026C63F /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 84001F891D0015D10026C63F /* LFLiveKit */;
|
||||
targetProxy = 84001F961D0015D10026C63F /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
84001F9C1D0015D10026C63F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84001F9D1D0015D10026C63F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
84001F9F1D0015D10026C63F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = A17586B27CD6843997425CCF /* Pods-LFLiveKit.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = LFLiveKit/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKit;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84001FA01D0015D10026C63F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = B75B965E6B94DE4CBCC82EA7 /* Pods-LFLiveKit.release.xcconfig */;
|
||||
buildSettings = {
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = LFLiveKit/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKit;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
84001FA21D0015D10026C63F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = LFLiveKitTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKitTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84001FA31D0015D10026C63F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = LFLiveKitTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKitTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
84001F841D0015D10026C63F /* Build configuration list for PBXProject "LFLiveKit" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84001F9C1D0015D10026C63F /* Debug */,
|
||||
84001F9D1D0015D10026C63F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
84001F9E1D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKit" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84001F9F1D0015D10026C63F /* Debug */,
|
||||
84001FA01D0015D10026C63F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
84001FA11D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKitTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84001FA21D0015D10026C63F /* Debug */,
|
||||
84001FA31D0015D10026C63F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 84001F811D0015D10026C63F /* Project object */;
|
||||
}
|
||||
Generated
BIN
Binary file not shown.
@@ -1,155 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "48F687CDE9990BD471D7883061F7E5D4"
|
||||
BuildableName = "libCocoaAsyncSocket.a"
|
||||
BlueprintName = "CocoaAsyncSocket"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6610362E50A6DE8BACCA1F2885CD9157"
|
||||
BuildableName = "libGPUImage.a"
|
||||
BlueprintName = "GPUImage"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7C2BDDF89D4243C7BCA44592D146DEED"
|
||||
BuildableName = "libPods-LFLiveKit.a"
|
||||
BlueprintName = "Pods-LFLiveKit"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84001F891D0015D10026C63F"
|
||||
BuildableName = "LFLiveKit.framework"
|
||||
BlueprintName = "LFLiveKit"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84001F931D0015D10026C63F"
|
||||
BuildableName = "LFLiveKitTests.xctest"
|
||||
BlueprintName = "LFLiveKitTests"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84001F931D0015D10026C63F"
|
||||
BuildableName = "LFLiveKitTests.xctest"
|
||||
BlueprintName = "LFLiveKitTests"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84001F891D0015D10026C63F"
|
||||
BuildableName = "LFLiveKit.framework"
|
||||
BlueprintName = "LFLiveKit"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84001F891D0015D10026C63F"
|
||||
BuildableName = "LFLiveKit.framework"
|
||||
BlueprintName = "LFLiveKit"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84001F891D0015D10026C63F"
|
||||
BuildableName = "LFLiveKit.framework"
|
||||
BlueprintName = "LFLiveKit"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,27 +0,0 @@
|
||||
<?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>LFLiveKit.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>84001F891D0015D10026C63F</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>84001F931D0015D10026C63F</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?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>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>84001F891D0015D10026C63F</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>84001F931D0015D10026C63F</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:LFLiveKit.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
BIN
Binary file not shown.
-71
@@ -1,71 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
type = "0"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "LFLiveKitTests/LFLiveKitTests.m"
|
||||
timestampString = "486890388.753673"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "18"
|
||||
endingLineNumber = "18"
|
||||
landmarkName = "-setUp"
|
||||
landmarkType = "5">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "LFLiveKitTests/LFLiveKitTests.m"
|
||||
timestampString = "486890389.93478"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "24"
|
||||
endingLineNumber = "24"
|
||||
landmarkName = "-tearDown"
|
||||
landmarkType = "5">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "LFLiveKitTests/LFLiveKitTests.m"
|
||||
timestampString = "486890391.195074"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "30"
|
||||
endingLineNumber = "30"
|
||||
landmarkName = "-testExample"
|
||||
landmarkType = "5">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "LFLiveKitTests/LFLiveKitTests.m"
|
||||
timestampString = "486890392.195974"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "37"
|
||||
endingLineNumber = "37"
|
||||
landmarkName = "-testPerformanceExample"
|
||||
landmarkType = "5">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
BIN
Binary file not shown.
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.7.0</string>
|
||||
<string>2.2.4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// LFLiveKit.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by admin on 16/5/24.
|
||||
// Copyright © 2016年 admin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFLiveSession.h"
|
||||
#import "LFLiveAudioConfiguration.h"
|
||||
#import "LFLiveVideoConfiguration.h"
|
||||
#import "LFAudioFrame.h"
|
||||
#import "LFFrame.h"
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFVideoFrame.h"
|
||||
@@ -0,0 +1,565 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
84D8B3D21D75753300752B56 /* LFAudioCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B39C1D75753300752B56 /* LFAudioCapture.h */; };
|
||||
84D8B3D31D75753300752B56 /* LFAudioCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B39D1D75753300752B56 /* LFAudioCapture.m */; };
|
||||
84D8B3D41D75753300752B56 /* LFVideoCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B39E1D75753300752B56 /* LFVideoCapture.h */; };
|
||||
84D8B3D51D75753300752B56 /* LFVideoCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B39F1D75753300752B56 /* LFVideoCapture.m */; };
|
||||
84D8B3D61D75753300752B56 /* LFAVEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3A21D75753300752B56 /* LFAVEncoder.h */; };
|
||||
84D8B3D71D75753300752B56 /* LFAVEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3A31D75753300752B56 /* LFAVEncoder.mm */; };
|
||||
84D8B3D81D75753300752B56 /* LFMP4Atom.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3A41D75753300752B56 /* LFMP4Atom.h */; };
|
||||
84D8B3D91D75753300752B56 /* LFMP4Atom.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3A51D75753300752B56 /* LFMP4Atom.m */; };
|
||||
84D8B3DA1D75753300752B56 /* LFNALUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3A61D75753300752B56 /* LFNALUnit.cpp */; };
|
||||
84D8B3DB1D75753300752B56 /* LFNALUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3A71D75753300752B56 /* LFNALUnit.h */; };
|
||||
84D8B3DC1D75753300752B56 /* LFVideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3A81D75753300752B56 /* LFVideoEncoder.h */; };
|
||||
84D8B3DD1D75753300752B56 /* LFVideoEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3A91D75753300752B56 /* LFVideoEncoder.m */; };
|
||||
84D8B3DE1D75753300752B56 /* LFAudioEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3AA1D75753300752B56 /* LFAudioEncoding.h */; };
|
||||
84D8B3DF1D75753300752B56 /* LFH264VideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3AB1D75753300752B56 /* LFH264VideoEncoder.h */; };
|
||||
84D8B3E01D75753300752B56 /* LFH264VideoEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3AC1D75753300752B56 /* LFH264VideoEncoder.mm */; };
|
||||
84D8B3E11D75753300752B56 /* LFHardwareAudioEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3AD1D75753300752B56 /* LFHardwareAudioEncoder.h */; };
|
||||
84D8B3E21D75753300752B56 /* LFHardwareAudioEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3AE1D75753300752B56 /* LFHardwareAudioEncoder.m */; };
|
||||
84D8B3E31D75753300752B56 /* LFHardwareVideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3AF1D75753300752B56 /* LFHardwareVideoEncoder.h */; };
|
||||
84D8B3E41D75753300752B56 /* LFHardwareVideoEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3B01D75753300752B56 /* LFHardwareVideoEncoder.m */; };
|
||||
84D8B3E51D75753300752B56 /* LFVideoEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3B11D75753300752B56 /* LFVideoEncoding.h */; };
|
||||
84D8B3E61D75753300752B56 /* LFLiveAudioConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3B31D75753300752B56 /* LFLiveAudioConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3E71D75753300752B56 /* LFLiveAudioConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3B41D75753300752B56 /* LFLiveAudioConfiguration.m */; };
|
||||
84D8B3E81D75753300752B56 /* LFLiveVideoConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3B51D75753300752B56 /* LFLiveVideoConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3E91D75753300752B56 /* LFLiveVideoConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3B61D75753300752B56 /* LFLiveVideoConfiguration.m */; };
|
||||
84D8B3EA1D75753300752B56 /* LFGPUImageBeautyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3B81D75753300752B56 /* LFGPUImageBeautyFilter.h */; };
|
||||
84D8B3EB1D75753300752B56 /* LFGPUImageBeautyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3B91D75753300752B56 /* LFGPUImageBeautyFilter.m */; };
|
||||
84D8B3EC1D75753300752B56 /* LFGPUImageEmptyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3BA1D75753300752B56 /* LFGPUImageEmptyFilter.h */; };
|
||||
84D8B3ED1D75753300752B56 /* LFGPUImageEmptyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3BB1D75753300752B56 /* LFGPUImageEmptyFilter.m */; };
|
||||
84D8B3EE1D75753300752B56 /* LFLiveKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3BC1D75753300752B56 /* LFLiveKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3EF1D75753300752B56 /* LFLiveSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3BD1D75753300752B56 /* LFLiveSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3F01D75753300752B56 /* LFLiveSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3BE1D75753300752B56 /* LFLiveSession.m */; };
|
||||
84D8B3F11D75753300752B56 /* LFAudioFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C01D75753300752B56 /* LFAudioFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3F21D75753300752B56 /* LFAudioFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C11D75753300752B56 /* LFAudioFrame.m */; };
|
||||
84D8B3F31D75753300752B56 /* LFFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C21D75753300752B56 /* LFFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3F41D75753300752B56 /* LFFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C31D75753300752B56 /* LFFrame.m */; };
|
||||
84D8B3F51D75753300752B56 /* LFLiveDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C41D75753300752B56 /* LFLiveDebug.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3F61D75753300752B56 /* LFLiveDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C51D75753300752B56 /* LFLiveDebug.m */; };
|
||||
84D8B3F71D75753300752B56 /* LFLiveStreamInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C61D75753300752B56 /* LFLiveStreamInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3F81D75753300752B56 /* LFLiveStreamInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C71D75753300752B56 /* LFLiveStreamInfo.m */; };
|
||||
84D8B3F91D75753300752B56 /* LFVideoFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C81D75753300752B56 /* LFVideoFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84D8B3FA1D75753300752B56 /* LFVideoFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C91D75753300752B56 /* LFVideoFrame.m */; };
|
||||
84D8B3FB1D75753300752B56 /* LFStreamingBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3CB1D75753300752B56 /* LFStreamingBuffer.h */; };
|
||||
84D8B3FC1D75753300752B56 /* LFStreamingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3CC1D75753300752B56 /* LFStreamingBuffer.m */; };
|
||||
84D8B3FD1D75753300752B56 /* LFStreamRTMPSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3CD1D75753300752B56 /* LFStreamRTMPSocket.h */; };
|
||||
84D8B3FE1D75753300752B56 /* LFStreamRtmpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3CE1D75753300752B56 /* LFStreamRtmpSocket.m */; };
|
||||
84D8B3FF1D75753300752B56 /* LFStreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3CF1D75753300752B56 /* LFStreamSocket.h */; };
|
||||
84D8B4001D75753300752B56 /* NSMutableArray+LFAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3D01D75753300752B56 /* NSMutableArray+LFAdd.h */; };
|
||||
84D8B4011D75753300752B56 /* NSMutableArray+LFAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3D11D75753300752B56 /* NSMutableArray+LFAdd.m */; };
|
||||
84D8B4BF1D757EB800752B56 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4BE1D757EB800752B56 /* VideoToolbox.framework */; };
|
||||
84D8B4C11D757EBE00752B56 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4C01D757EBE00752B56 /* AudioToolbox.framework */; };
|
||||
84D8B4C31D757EC400752B56 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4C21D757EC400752B56 /* AVFoundation.framework */; };
|
||||
84D8B4C51D757EC800752B56 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4C41D757EC800752B56 /* Foundation.framework */; };
|
||||
84D8B4C71D757ECC00752B56 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4C61D757ECC00752B56 /* UIKit.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
84D8B3901D7574D600752B56 /* LFLiveKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LFLiveKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84D8B39C1D75753300752B56 /* LFAudioCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioCapture.h; sourceTree = "<group>"; };
|
||||
84D8B39D1D75753300752B56 /* LFAudioCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFAudioCapture.m; sourceTree = "<group>"; };
|
||||
84D8B39E1D75753300752B56 /* LFVideoCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoCapture.h; sourceTree = "<group>"; };
|
||||
84D8B39F1D75753300752B56 /* LFVideoCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoCapture.m; sourceTree = "<group>"; };
|
||||
84D8B3A21D75753300752B56 /* LFAVEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAVEncoder.h; sourceTree = "<group>"; };
|
||||
84D8B3A31D75753300752B56 /* LFAVEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LFAVEncoder.mm; sourceTree = "<group>"; };
|
||||
84D8B3A41D75753300752B56 /* LFMP4Atom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFMP4Atom.h; sourceTree = "<group>"; };
|
||||
84D8B3A51D75753300752B56 /* LFMP4Atom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFMP4Atom.m; sourceTree = "<group>"; };
|
||||
84D8B3A61D75753300752B56 /* LFNALUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LFNALUnit.cpp; sourceTree = "<group>"; };
|
||||
84D8B3A71D75753300752B56 /* LFNALUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFNALUnit.h; sourceTree = "<group>"; };
|
||||
84D8B3A81D75753300752B56 /* LFVideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoEncoder.h; sourceTree = "<group>"; };
|
||||
84D8B3A91D75753300752B56 /* LFVideoEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoEncoder.m; sourceTree = "<group>"; };
|
||||
84D8B3AA1D75753300752B56 /* LFAudioEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioEncoding.h; sourceTree = "<group>"; };
|
||||
84D8B3AB1D75753300752B56 /* LFH264VideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFH264VideoEncoder.h; sourceTree = "<group>"; };
|
||||
84D8B3AC1D75753300752B56 /* LFH264VideoEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LFH264VideoEncoder.mm; sourceTree = "<group>"; };
|
||||
84D8B3AD1D75753300752B56 /* LFHardwareAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHardwareAudioEncoder.h; sourceTree = "<group>"; };
|
||||
84D8B3AE1D75753300752B56 /* LFHardwareAudioEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHardwareAudioEncoder.m; sourceTree = "<group>"; };
|
||||
84D8B3AF1D75753300752B56 /* LFHardwareVideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHardwareVideoEncoder.h; sourceTree = "<group>"; };
|
||||
84D8B3B01D75753300752B56 /* LFHardwareVideoEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHardwareVideoEncoder.m; sourceTree = "<group>"; };
|
||||
84D8B3B11D75753300752B56 /* LFVideoEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoEncoding.h; sourceTree = "<group>"; };
|
||||
84D8B3B31D75753300752B56 /* LFLiveAudioConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveAudioConfiguration.h; sourceTree = "<group>"; };
|
||||
84D8B3B41D75753300752B56 /* LFLiveAudioConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveAudioConfiguration.m; sourceTree = "<group>"; };
|
||||
84D8B3B51D75753300752B56 /* LFLiveVideoConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveVideoConfiguration.h; sourceTree = "<group>"; };
|
||||
84D8B3B61D75753300752B56 /* LFLiveVideoConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveVideoConfiguration.m; sourceTree = "<group>"; };
|
||||
84D8B3B81D75753300752B56 /* LFGPUImageBeautyFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFGPUImageBeautyFilter.h; sourceTree = "<group>"; };
|
||||
84D8B3B91D75753300752B56 /* LFGPUImageBeautyFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFGPUImageBeautyFilter.m; sourceTree = "<group>"; };
|
||||
84D8B3BA1D75753300752B56 /* LFGPUImageEmptyFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFGPUImageEmptyFilter.h; sourceTree = "<group>"; };
|
||||
84D8B3BB1D75753300752B56 /* LFGPUImageEmptyFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFGPUImageEmptyFilter.m; sourceTree = "<group>"; };
|
||||
84D8B3BC1D75753300752B56 /* LFLiveKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveKit.h; sourceTree = "<group>"; };
|
||||
84D8B3BD1D75753300752B56 /* LFLiveSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveSession.h; sourceTree = "<group>"; };
|
||||
84D8B3BE1D75753300752B56 /* LFLiveSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveSession.m; sourceTree = "<group>"; };
|
||||
84D8B3C01D75753300752B56 /* LFAudioFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioFrame.h; sourceTree = "<group>"; };
|
||||
84D8B3C11D75753300752B56 /* LFAudioFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFAudioFrame.m; sourceTree = "<group>"; };
|
||||
84D8B3C21D75753300752B56 /* LFFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFFrame.h; sourceTree = "<group>"; };
|
||||
84D8B3C31D75753300752B56 /* LFFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFFrame.m; sourceTree = "<group>"; };
|
||||
84D8B3C41D75753300752B56 /* LFLiveDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveDebug.h; sourceTree = "<group>"; };
|
||||
84D8B3C51D75753300752B56 /* LFLiveDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveDebug.m; sourceTree = "<group>"; };
|
||||
84D8B3C61D75753300752B56 /* LFLiveStreamInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveStreamInfo.h; sourceTree = "<group>"; };
|
||||
84D8B3C71D75753300752B56 /* LFLiveStreamInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveStreamInfo.m; sourceTree = "<group>"; };
|
||||
84D8B3C81D75753300752B56 /* LFVideoFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoFrame.h; sourceTree = "<group>"; };
|
||||
84D8B3C91D75753300752B56 /* LFVideoFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoFrame.m; sourceTree = "<group>"; };
|
||||
84D8B3CB1D75753300752B56 /* LFStreamingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamingBuffer.h; sourceTree = "<group>"; };
|
||||
84D8B3CC1D75753300752B56 /* LFStreamingBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamingBuffer.m; sourceTree = "<group>"; };
|
||||
84D8B3CD1D75753300752B56 /* LFStreamRTMPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamRTMPSocket.h; sourceTree = "<group>"; };
|
||||
84D8B3CE1D75753300752B56 /* LFStreamRtmpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamRtmpSocket.m; sourceTree = "<group>"; };
|
||||
84D8B3CF1D75753300752B56 /* LFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamSocket.h; sourceTree = "<group>"; };
|
||||
84D8B3D01D75753300752B56 /* NSMutableArray+LFAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+LFAdd.h"; sourceTree = "<group>"; };
|
||||
84D8B3D11D75753300752B56 /* NSMutableArray+LFAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+LFAdd.m"; sourceTree = "<group>"; };
|
||||
84D8B4BE1D757EB800752B56 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
|
||||
84D8B4C01D757EBE00752B56 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
84D8B4C21D757EC400752B56 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
84D8B4C41D757EC800752B56 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
84D8B4C61D757ECC00752B56 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
84D8B4C81D757ED100752B56 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
|
||||
84D8B4CA1D757ED600752B56 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
84D8B38C1D7574D600752B56 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84D8B4C71D757ECC00752B56 /* UIKit.framework in Frameworks */,
|
||||
84D8B4C51D757EC800752B56 /* Foundation.framework in Frameworks */,
|
||||
84D8B4C31D757EC400752B56 /* AVFoundation.framework in Frameworks */,
|
||||
84D8B4C11D757EBE00752B56 /* AudioToolbox.framework in Frameworks */,
|
||||
84D8B4BF1D757EB800752B56 /* VideoToolbox.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
84D8B3861D7574D600752B56 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B4CA1D757ED600752B56 /* libz.tbd */,
|
||||
84D8B4C81D757ED100752B56 /* libstdc++.tbd */,
|
||||
84D8B4C61D757ECC00752B56 /* UIKit.framework */,
|
||||
84D8B4C41D757EC800752B56 /* Foundation.framework */,
|
||||
84D8B4C21D757EC400752B56 /* AVFoundation.framework */,
|
||||
84D8B4C01D757EBE00752B56 /* AudioToolbox.framework */,
|
||||
84D8B4BE1D757EB800752B56 /* VideoToolbox.framework */,
|
||||
84D8B3921D7574D600752B56 /* LFLiveKit */,
|
||||
84D8B3911D7574D600752B56 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B3911D7574D600752B56 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B3901D7574D600752B56 /* LFLiveKit.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B3921D7574D600752B56 /* LFLiveKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B39B1D75753300752B56 /* capture */,
|
||||
84D8B3A01D75753300752B56 /* coder */,
|
||||
84D8B3B21D75753300752B56 /* configuration */,
|
||||
84D8B3B71D75753300752B56 /* filter */,
|
||||
84D8B3BC1D75753300752B56 /* LFLiveKit.h */,
|
||||
84D8B3BD1D75753300752B56 /* LFLiveSession.h */,
|
||||
84D8B3BE1D75753300752B56 /* LFLiveSession.m */,
|
||||
84D8B3BF1D75753300752B56 /* objects */,
|
||||
84D8B3CA1D75753300752B56 /* publish */,
|
||||
);
|
||||
path = LFLiveKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B39B1D75753300752B56 /* capture */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B39C1D75753300752B56 /* LFAudioCapture.h */,
|
||||
84D8B39D1D75753300752B56 /* LFAudioCapture.m */,
|
||||
84D8B39E1D75753300752B56 /* LFVideoCapture.h */,
|
||||
84D8B39F1D75753300752B56 /* LFVideoCapture.m */,
|
||||
);
|
||||
path = capture;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B3A01D75753300752B56 /* coder */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B3A11D75753300752B56 /* H264 */,
|
||||
84D8B3AA1D75753300752B56 /* LFAudioEncoding.h */,
|
||||
84D8B3AB1D75753300752B56 /* LFH264VideoEncoder.h */,
|
||||
84D8B3AC1D75753300752B56 /* LFH264VideoEncoder.mm */,
|
||||
84D8B3AD1D75753300752B56 /* LFHardwareAudioEncoder.h */,
|
||||
84D8B3AE1D75753300752B56 /* LFHardwareAudioEncoder.m */,
|
||||
84D8B3AF1D75753300752B56 /* LFHardwareVideoEncoder.h */,
|
||||
84D8B3B01D75753300752B56 /* LFHardwareVideoEncoder.m */,
|
||||
84D8B3B11D75753300752B56 /* LFVideoEncoding.h */,
|
||||
);
|
||||
path = coder;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B3A11D75753300752B56 /* H264 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B3A21D75753300752B56 /* LFAVEncoder.h */,
|
||||
84D8B3A31D75753300752B56 /* LFAVEncoder.mm */,
|
||||
84D8B3A41D75753300752B56 /* LFMP4Atom.h */,
|
||||
84D8B3A51D75753300752B56 /* LFMP4Atom.m */,
|
||||
84D8B3A61D75753300752B56 /* LFNALUnit.cpp */,
|
||||
84D8B3A71D75753300752B56 /* LFNALUnit.h */,
|
||||
84D8B3A81D75753300752B56 /* LFVideoEncoder.h */,
|
||||
84D8B3A91D75753300752B56 /* LFVideoEncoder.m */,
|
||||
);
|
||||
path = H264;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B3B21D75753300752B56 /* configuration */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B3B31D75753300752B56 /* LFLiveAudioConfiguration.h */,
|
||||
84D8B3B41D75753300752B56 /* LFLiveAudioConfiguration.m */,
|
||||
84D8B3B51D75753300752B56 /* LFLiveVideoConfiguration.h */,
|
||||
84D8B3B61D75753300752B56 /* LFLiveVideoConfiguration.m */,
|
||||
);
|
||||
path = configuration;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B3B71D75753300752B56 /* filter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B3B81D75753300752B56 /* LFGPUImageBeautyFilter.h */,
|
||||
84D8B3B91D75753300752B56 /* LFGPUImageBeautyFilter.m */,
|
||||
84D8B3BA1D75753300752B56 /* LFGPUImageEmptyFilter.h */,
|
||||
84D8B3BB1D75753300752B56 /* LFGPUImageEmptyFilter.m */,
|
||||
);
|
||||
path = filter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B3BF1D75753300752B56 /* objects */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B3C01D75753300752B56 /* LFAudioFrame.h */,
|
||||
84D8B3C11D75753300752B56 /* LFAudioFrame.m */,
|
||||
84D8B3C21D75753300752B56 /* LFFrame.h */,
|
||||
84D8B3C31D75753300752B56 /* LFFrame.m */,
|
||||
84D8B3C41D75753300752B56 /* LFLiveDebug.h */,
|
||||
84D8B3C51D75753300752B56 /* LFLiveDebug.m */,
|
||||
84D8B3C61D75753300752B56 /* LFLiveStreamInfo.h */,
|
||||
84D8B3C71D75753300752B56 /* LFLiveStreamInfo.m */,
|
||||
84D8B3C81D75753300752B56 /* LFVideoFrame.h */,
|
||||
84D8B3C91D75753300752B56 /* LFVideoFrame.m */,
|
||||
);
|
||||
path = objects;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84D8B3CA1D75753300752B56 /* publish */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84D8B3CB1D75753300752B56 /* LFStreamingBuffer.h */,
|
||||
84D8B3CC1D75753300752B56 /* LFStreamingBuffer.m */,
|
||||
84D8B3CD1D75753300752B56 /* LFStreamRTMPSocket.h */,
|
||||
84D8B3CE1D75753300752B56 /* LFStreamRtmpSocket.m */,
|
||||
84D8B3CF1D75753300752B56 /* LFStreamSocket.h */,
|
||||
84D8B3D01D75753300752B56 /* NSMutableArray+LFAdd.h */,
|
||||
84D8B3D11D75753300752B56 /* NSMutableArray+LFAdd.m */,
|
||||
);
|
||||
path = publish;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
84D8B38D1D7574D600752B56 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84D8B3F51D75753300752B56 /* LFLiveDebug.h in Headers */,
|
||||
84D8B3E61D75753300752B56 /* LFLiveAudioConfiguration.h in Headers */,
|
||||
84D8B3F11D75753300752B56 /* LFAudioFrame.h in Headers */,
|
||||
84D8B3F71D75753300752B56 /* LFLiveStreamInfo.h in Headers */,
|
||||
84D8B3E81D75753300752B56 /* LFLiveVideoConfiguration.h in Headers */,
|
||||
84D8B3F31D75753300752B56 /* LFFrame.h in Headers */,
|
||||
84D8B3EE1D75753300752B56 /* LFLiveKit.h in Headers */,
|
||||
84D8B3EF1D75753300752B56 /* LFLiveSession.h in Headers */,
|
||||
84D8B3F91D75753300752B56 /* LFVideoFrame.h in Headers */,
|
||||
84D8B3D21D75753300752B56 /* LFAudioCapture.h in Headers */,
|
||||
84D8B3FD1D75753300752B56 /* LFStreamRTMPSocket.h in Headers */,
|
||||
84D8B3D81D75753300752B56 /* LFMP4Atom.h in Headers */,
|
||||
84D8B3EA1D75753300752B56 /* LFGPUImageBeautyFilter.h in Headers */,
|
||||
84D8B3D61D75753300752B56 /* LFAVEncoder.h in Headers */,
|
||||
84D8B3DC1D75753300752B56 /* LFVideoEncoder.h in Headers */,
|
||||
84D8B3EC1D75753300752B56 /* LFGPUImageEmptyFilter.h in Headers */,
|
||||
84D8B3DE1D75753300752B56 /* LFAudioEncoding.h in Headers */,
|
||||
84D8B4001D75753300752B56 /* NSMutableArray+LFAdd.h in Headers */,
|
||||
84D8B3E31D75753300752B56 /* LFHardwareVideoEncoder.h in Headers */,
|
||||
84D8B3D41D75753300752B56 /* LFVideoCapture.h in Headers */,
|
||||
84D8B3FF1D75753300752B56 /* LFStreamSocket.h in Headers */,
|
||||
84D8B3DB1D75753300752B56 /* LFNALUnit.h in Headers */,
|
||||
84D8B3E11D75753300752B56 /* LFHardwareAudioEncoder.h in Headers */,
|
||||
84D8B3E51D75753300752B56 /* LFVideoEncoding.h in Headers */,
|
||||
84D8B3FB1D75753300752B56 /* LFStreamingBuffer.h in Headers */,
|
||||
84D8B3DF1D75753300752B56 /* LFH264VideoEncoder.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
84D8B38F1D7574D600752B56 /* LFLiveKit */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84D8B3981D7574D600752B56 /* Build configuration list for PBXNativeTarget "LFLiveKit" */;
|
||||
buildPhases = (
|
||||
84D8B38B1D7574D600752B56 /* Sources */,
|
||||
84D8B38C1D7574D600752B56 /* Frameworks */,
|
||||
84D8B38D1D7574D600752B56 /* Headers */,
|
||||
84D8B38E1D7574D600752B56 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = LFLiveKit;
|
||||
productName = LFLiveKit;
|
||||
productReference = 84D8B3901D7574D600752B56 /* LFLiveKit.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
84D8B3871D7574D600752B56 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0730;
|
||||
ORGANIZATIONNAME = admin;
|
||||
TargetAttributes = {
|
||||
84D8B38F1D7574D600752B56 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 84D8B38A1D7574D600752B56 /* Build configuration list for PBXProject "LFLiveKit" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 84D8B3861D7574D600752B56;
|
||||
productRefGroup = 84D8B3911D7574D600752B56 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
84D8B38F1D7574D600752B56 /* LFLiveKit */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
84D8B38E1D7574D600752B56 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
84D8B38B1D7574D600752B56 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84D8B3EB1D75753300752B56 /* LFGPUImageBeautyFilter.m in Sources */,
|
||||
84D8B3F41D75753300752B56 /* LFFrame.m in Sources */,
|
||||
84D8B3F61D75753300752B56 /* LFLiveDebug.m in Sources */,
|
||||
84D8B3D51D75753300752B56 /* LFVideoCapture.m in Sources */,
|
||||
84D8B3F21D75753300752B56 /* LFAudioFrame.m in Sources */,
|
||||
84D8B3F81D75753300752B56 /* LFLiveStreamInfo.m in Sources */,
|
||||
84D8B3DA1D75753300752B56 /* LFNALUnit.cpp in Sources */,
|
||||
84D8B3D71D75753300752B56 /* LFAVEncoder.mm in Sources */,
|
||||
84D8B3D91D75753300752B56 /* LFMP4Atom.m in Sources */,
|
||||
84D8B3E91D75753300752B56 /* LFLiveVideoConfiguration.m in Sources */,
|
||||
84D8B3DD1D75753300752B56 /* LFVideoEncoder.m in Sources */,
|
||||
84D8B3ED1D75753300752B56 /* LFGPUImageEmptyFilter.m in Sources */,
|
||||
84D8B3F01D75753300752B56 /* LFLiveSession.m in Sources */,
|
||||
84D8B3FE1D75753300752B56 /* LFStreamRtmpSocket.m in Sources */,
|
||||
84D8B3E71D75753300752B56 /* LFLiveAudioConfiguration.m in Sources */,
|
||||
84D8B3E21D75753300752B56 /* LFHardwareAudioEncoder.m in Sources */,
|
||||
84D8B3E01D75753300752B56 /* LFH264VideoEncoder.mm in Sources */,
|
||||
84D8B3FC1D75753300752B56 /* LFStreamingBuffer.m in Sources */,
|
||||
84D8B3E41D75753300752B56 /* LFHardwareVideoEncoder.m in Sources */,
|
||||
84D8B3FA1D75753300752B56 /* LFVideoFrame.m in Sources */,
|
||||
84D8B3D31D75753300752B56 /* LFAudioCapture.m in Sources */,
|
||||
84D8B4011D75753300752B56 /* NSMutableArray+LFAdd.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
84D8B3961D7574D600752B56 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84D8B3971D7574D600752B56 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
84D8B3991D7574D600752B56 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(SRCROOT)/../Vendor/GPUImage.framework/Headers\"",
|
||||
"\"$(SRCROOT)/../Vendor/pili-librtmp.framework/Headers\"",
|
||||
);
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MACH_O_TYPE = staticlib;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84D8B39A1D7574D600752B56 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(SRCROOT)/../Vendor/GPUImage.framework/Headers\"",
|
||||
"\"$(SRCROOT)/../Vendor/pili-librtmp.framework/Headers\"",
|
||||
);
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MACH_O_TYPE = staticlib;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
84D8B38A1D7574D600752B56 /* Build configuration list for PBXProject "LFLiveKit" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84D8B3961D7574D600752B56 /* Debug */,
|
||||
84D8B3971D7574D600752B56 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
84D8B3981D7574D600752B56 /* Build configuration list for PBXNativeTarget "LFLiveKit" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84D8B3991D7574D600752B56 /* Debug */,
|
||||
84D8B39A1D7574D600752B56 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 84D8B3871D7574D600752B56 /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84D8B38F1D7574D600752B56"
|
||||
BuildableName = "LFLiveKit.framework"
|
||||
BlueprintName = "LFLiveKit"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84D8B38F1D7574D600752B56"
|
||||
BuildableName = "LFLiveKit.framework"
|
||||
BlueprintName = "LFLiveKit"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "84D8B38F1D7574D600752B56"
|
||||
BuildableName = "LFLiveKit.framework"
|
||||
BlueprintName = "LFLiveKit"
|
||||
ReferencedContainer = "container:LFLiveKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// LFLiveKit.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by admin on 16/5/24.
|
||||
// Copyright © 2016年 admin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFLiveSession.h"
|
||||
#import "LFLiveAudioConfiguration.h"
|
||||
#import "LFLiveVideoConfiguration.h"
|
||||
#import "LFAudioFrame.h"
|
||||
#import "LFFrame.h"
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFVideoFrame.h"
|
||||
#import "LFLiveDebug.h"
|
||||
|
||||
//#import <LFLiveKit/LFLiveSession.h>
|
||||
//#import <LFLiveKit/LFLiveAudioConfiguration.h>
|
||||
//#import <LFLiveKit/LFLiveVideoConfiguration.h>
|
||||
//#import <LFLiveKit/LFAudioFrame.h>
|
||||
//#import <LFLiveKit/LFFrame.h>
|
||||
//#import <LFLiveKit/LFLiveStreamInfo.h>
|
||||
//#import <LFLiveKit/LFVideoFrame.h>
|
||||
//#import <LFLiveKit/LFLiveDebug.h>
|
||||
Executable
+151
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// LFLiveSession.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFAudioFrame.h"
|
||||
#import "LFVideoFrame.h"
|
||||
#import "LFLiveAudioConfiguration.h"
|
||||
#import "LFLiveVideoConfiguration.h"
|
||||
#import "LFLiveDebug.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger,LFLiveCaptureType) {
|
||||
LFLiveCaptureAudio, //< capture only audio
|
||||
LFLiveCaptureVideo, //< capture onlt video
|
||||
LFLiveInputAudio, //< only audio (External input audio)
|
||||
LFLiveInputVideo, //< only video (External input video)
|
||||
};
|
||||
|
||||
|
||||
///< 用来控制采集类型(可以内部采集也可以外部传入等各种组合,支持单音频与单视频,外部输入适用于录屏,无人机等外设介入)
|
||||
typedef NS_ENUM(NSInteger,LFLiveCaptureTypeMask) {
|
||||
LFLiveCaptureMaskAudio = (1 << LFLiveCaptureAudio), ///< only inner capture audio (no video)
|
||||
LFLiveCaptureMaskVideo = (1 << LFLiveCaptureVideo), ///< only inner capture video (no audio)
|
||||
LFLiveInputMaskAudio = (1 << LFLiveInputAudio), ///< only outer input audio (no video)
|
||||
LFLiveInputMaskVideo = (1 << LFLiveInputVideo), ///< only outer input video (no audio)
|
||||
LFLiveCaptureMaskAll = (LFLiveCaptureMaskAudio | LFLiveCaptureMaskVideo), ///< inner capture audio and video
|
||||
LFLiveInputMaskAll = (LFLiveInputMaskAudio | LFLiveInputMaskVideo), ///< outer input audio and video(method see pushVideo and pushAudio)
|
||||
LFLiveCaptureMaskAudioInputVideo = (LFLiveCaptureMaskAudio | LFLiveInputMaskVideo), ///< inner capture audio and outer input video(method pushVideo and setRunning)
|
||||
LFLiveCaptureMaskVideoInputAudio = (LFLiveCaptureMaskVideo | LFLiveInputMaskAudio), ///< inner capture video and outer input audio(method pushAudio and setRunning)
|
||||
LFLiveCaptureDefaultMask = LFLiveCaptureMaskAll ///< default is inner capture audio and video
|
||||
};
|
||||
|
||||
@class LFLiveSession;
|
||||
@protocol LFLiveSessionDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
/** live status changed will callback */
|
||||
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state;
|
||||
/** live debug info callback */
|
||||
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug *)debugInfo;
|
||||
/** callback socket errorcode */
|
||||
- (void)liveSession:(nullable LFLiveSession *)session errorCode:(LFLiveSocketErrorCode)errorCode;
|
||||
@end
|
||||
|
||||
@class LFLiveStreamInfo;
|
||||
|
||||
@interface LFLiveSession : NSObject
|
||||
|
||||
#pragma mark - Attribute
|
||||
///=============================================================================
|
||||
/// @name Attribute
|
||||
///=============================================================================
|
||||
/** The delegate of the capture. captureData callback */
|
||||
@property (nullable, nonatomic, weak) id<LFLiveSessionDelegate> delegate;
|
||||
|
||||
/** The running control start capture or stop capture*/
|
||||
@property (nonatomic, assign) BOOL running;
|
||||
|
||||
/** The preView will show OpenGL ES view*/
|
||||
@property (nonatomic, strong, null_resettable) UIView *preView;
|
||||
|
||||
/** The captureDevicePosition control camraPosition ,default front*/
|
||||
@property (nonatomic, assign) AVCaptureDevicePosition captureDevicePosition;
|
||||
|
||||
/** The beautyFace control capture shader filter empty or beautiy */
|
||||
@property (nonatomic, assign) BOOL beautyFace;
|
||||
|
||||
/** The beautyLevel control beautyFace Level. Default is 0.5, between 0.0 ~ 1.0 */
|
||||
@property (nonatomic, assign) CGFloat beautyLevel;
|
||||
|
||||
/** The brightLevel control brightness Level, Default is 0.5, between 0.0 ~ 1.0 */
|
||||
@property (nonatomic, assign) CGFloat brightLevel;
|
||||
|
||||
/** The torch control camera zoom scale default 1.0, between 1.0 ~ 3.0 */
|
||||
@property (nonatomic, assign) CGFloat zoomScale;
|
||||
|
||||
/** The torch control capture flash is on or off */
|
||||
@property (nonatomic, assign) BOOL torch;
|
||||
|
||||
/** The mirror control mirror of front camera is on or off */
|
||||
@property (nonatomic, assign) BOOL mirror;
|
||||
|
||||
/** The muted control callbackAudioData,muted will memset 0.*/
|
||||
@property (nonatomic, assign) BOOL muted;
|
||||
|
||||
/* The adaptiveBitrate control auto adjust bitrate. Default is NO */
|
||||
@property (nonatomic, assign) BOOL adaptiveBitrate;
|
||||
|
||||
/** The stream control upload and package*/
|
||||
@property (nullable, nonatomic, strong, readonly) LFLiveStreamInfo *streamInfo;
|
||||
|
||||
/** The status of the stream .*/
|
||||
@property (nonatomic, assign, readonly) LFLiveState state;
|
||||
|
||||
/** The captureType control inner or outer audio and video .*/
|
||||
@property (nonatomic, assign, readonly) LFLiveCaptureTypeMask captureType;
|
||||
|
||||
/** The showDebugInfo control streamInfo and uploadInfo(1s) *.*/
|
||||
@property (nonatomic, assign) BOOL showDebugInfo;
|
||||
|
||||
/** The reconnectInterval control reconnect timeInterval(重连间隔) *.*/
|
||||
@property (nonatomic, assign) NSUInteger reconnectInterval;
|
||||
|
||||
/** The reconnectCount control reconnect count (重连次数) *.*/
|
||||
@property (nonatomic, assign) NSUInteger reconnectCount;
|
||||
|
||||
/*** The warterMarkView control whether the watermark is displayed or not ,if set ni,will remove watermark,otherwise add.
|
||||
set alpha represent mix.Position relative to outVideoSize.
|
||||
*.*/
|
||||
@property (nonatomic, strong, nullable) UIView *warterMarkView;
|
||||
|
||||
@property (nonatomic, strong,readonly ,nullable) UIImage *currentImage;
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
/// @name Initializer
|
||||
///=============================================================================
|
||||
- (nullable instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
*/
|
||||
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration;
|
||||
|
||||
/**
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
*/
|
||||
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration captureType:(LFLiveCaptureTypeMask)captureType NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** The start stream .*/
|
||||
- (void)startLive:(nonnull LFLiveStreamInfo *)streamInfo;
|
||||
|
||||
/** The stop stream .*/
|
||||
- (void)stopLive;
|
||||
|
||||
/** support outer input yuv or rgb video(set LFLiveCaptureTypeMask) .*/
|
||||
- (void)pushVideo:(nullable CVPixelBufferRef)pixelBuffer;
|
||||
|
||||
/** support outer input pcm audio(set LFLiveCaptureTypeMask) .*/
|
||||
- (void)pushAudio:(nullable NSData*)audioData;
|
||||
|
||||
@end
|
||||
|
||||
Executable
+412
@@ -0,0 +1,412 @@
|
||||
//
|
||||
// LFLiveSession.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFLiveSession.h"
|
||||
#import "LFVideoCapture.h"
|
||||
#import "LFAudioCapture.h"
|
||||
#import "LFHardwareVideoEncoder.h"
|
||||
#import "LFHardwareAudioEncoder.h"
|
||||
#import "LFH264VideoEncoder.h"
|
||||
#import "LFStreamRTMPSocket.h"
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
#import "LFH264VideoEncoder.h"
|
||||
|
||||
|
||||
@interface LFLiveSession ()<LFAudioCaptureDelegate, LFVideoCaptureDelegate, LFAudioEncodingDelegate, LFVideoEncodingDelegate, LFStreamSocketDelegate>
|
||||
|
||||
/// 音频配置
|
||||
@property (nonatomic, strong) LFLiveAudioConfiguration *audioConfiguration;
|
||||
/// 视频配置
|
||||
@property (nonatomic, strong) LFLiveVideoConfiguration *videoConfiguration;
|
||||
/// 声音采集
|
||||
@property (nonatomic, strong) LFAudioCapture *audioCaptureSource;
|
||||
/// 视频采集
|
||||
@property (nonatomic, strong) LFVideoCapture *videoCaptureSource;
|
||||
/// 音频编码
|
||||
@property (nonatomic, strong) id<LFAudioEncoding> audioEncoder;
|
||||
/// 视频编码
|
||||
@property (nonatomic, strong) id<LFVideoEncoding> videoEncoder;
|
||||
/// 上传
|
||||
@property (nonatomic, strong) id<LFStreamSocket> socket;
|
||||
|
||||
|
||||
#pragma mark -- 内部标识
|
||||
/// 调试信息
|
||||
@property (nonatomic, strong) LFLiveDebug *debugInfo;
|
||||
/// 流信息
|
||||
@property (nonatomic, strong) LFLiveStreamInfo *streamInfo;
|
||||
/// 是否开始上传
|
||||
@property (nonatomic, assign) BOOL uploading;
|
||||
/// 当前状态
|
||||
@property (nonatomic, assign, readwrite) LFLiveState state;
|
||||
/// 当前直播type
|
||||
@property (nonatomic, assign, readwrite) LFLiveCaptureTypeMask captureType;
|
||||
/// 时间戳锁
|
||||
@property (nonatomic, strong) dispatch_semaphore_t lock;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
/** 时间戳 */
|
||||
#define NOW (CACurrentMediaTime()*1000)
|
||||
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
|
||||
|
||||
@interface LFLiveSession ()
|
||||
|
||||
/// 上传相对时间戳
|
||||
@property (nonatomic, assign) uint64_t relativeTimestamps;
|
||||
/// 音视频是否对齐
|
||||
@property (nonatomic, assign) BOOL AVAlignment;
|
||||
/// 当前是否采集到了音频
|
||||
@property (nonatomic, assign) BOOL hasCaptureAudio;
|
||||
/// 当前是否采集到了关键帧
|
||||
@property (nonatomic, assign) BOOL hasKeyFrameVideo;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFLiveSession
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration {
|
||||
return [self initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration captureType:LFLiveCaptureDefaultMask];
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration captureType:(LFLiveCaptureTypeMask)captureType{
|
||||
if((captureType & LFLiveCaptureMaskAudio || captureType & LFLiveInputMaskAudio) && !audioConfiguration) @throw [NSException exceptionWithName:@"LFLiveSession init error" reason:@"audioConfiguration is nil " userInfo:nil];
|
||||
if((captureType & LFLiveCaptureMaskVideo || captureType & LFLiveInputMaskVideo) && !videoConfiguration) @throw [NSException exceptionWithName:@"LFLiveSession init error" reason:@"videoConfiguration is nil " userInfo:nil];
|
||||
if (self = [super init]) {
|
||||
_audioConfiguration = audioConfiguration;
|
||||
_videoConfiguration = videoConfiguration;
|
||||
_adaptiveBitrate = NO;
|
||||
_captureType = captureType;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.videoCaptureSource.running = NO;
|
||||
self.audioCaptureSource.running = NO;
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (void)startLive:(LFLiveStreamInfo *)streamInfo {
|
||||
if (!streamInfo) return;
|
||||
_streamInfo = streamInfo;
|
||||
_streamInfo.videoConfiguration = _videoConfiguration;
|
||||
_streamInfo.audioConfiguration = _audioConfiguration;
|
||||
_streamInfo.needDropFrame = (self.captureType & LFLiveCaptureMaskVideo || self.captureType & LFLiveInputMaskVideo) ? YES : NO;//< 有视频执行丢帧算法
|
||||
[self.socket start];
|
||||
}
|
||||
|
||||
- (void)stopLive {
|
||||
self.uploading = NO;
|
||||
[self.socket stop];
|
||||
self.socket = nil;
|
||||
}
|
||||
|
||||
#pragma mark -- PrivateMethod
|
||||
- (void)pushSendBuffer:(LFFrame*)frame{
|
||||
if(self.relativeTimestamps == 0){
|
||||
self.relativeTimestamps = frame.timestamp;
|
||||
}
|
||||
frame.timestamp = [self uploadTimestamp:frame.timestamp];
|
||||
[self.socket sendFrame:frame];
|
||||
}
|
||||
|
||||
- (void)pushVideo:(nullable CVPixelBufferRef)pixelBuffer{
|
||||
if(self.captureType & LFLiveInputMaskVideo){
|
||||
if (self.uploading) [self.videoEncoder encodeVideoData:pixelBuffer timeStamp:NOW];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pushAudio:(nullable NSData*)audioData{
|
||||
if(self.captureType & LFLiveInputMaskAudio){
|
||||
if (self.uploading) [self.audioEncoder encodeAudioData:audioData timeStamp:NOW];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- CaptureDelegate
|
||||
- (void)captureOutput:(nullable LFAudioCapture *)capture audioData:(nullable NSData*)audioData {
|
||||
if (self.uploading) [self.audioEncoder encodeAudioData:audioData timeStamp:NOW];
|
||||
}
|
||||
|
||||
- (void)captureOutput:(nullable LFVideoCapture *)capture pixelBuffer:(nullable CVPixelBufferRef)pixelBuffer {
|
||||
if (self.uploading) [self.videoEncoder encodeVideoData:pixelBuffer timeStamp:NOW];
|
||||
}
|
||||
|
||||
#pragma mark -- EncoderDelegate
|
||||
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame *)frame {
|
||||
//<上传 时间戳对齐
|
||||
if (self.uploading){
|
||||
self.hasCaptureAudio = YES;
|
||||
if(self.AVAlignment) [self pushSendBuffer:frame];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame *)frame {
|
||||
//<上传 时间戳对齐
|
||||
if (self.uploading){
|
||||
if(frame.isKeyFrame && self.hasCaptureAudio) self.hasKeyFrameVideo = YES;
|
||||
if(self.AVAlignment) [self pushSendBuffer:frame];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- LFStreamTcpSocketDelegate
|
||||
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status {
|
||||
if (status == LFLiveStart) {
|
||||
if (!self.uploading) {
|
||||
self.AVAlignment = NO;
|
||||
self.hasCaptureAudio = NO;
|
||||
self.hasKeyFrameVideo = NO;
|
||||
self.relativeTimestamps = 0;
|
||||
self.uploading = YES;
|
||||
}
|
||||
} else if(status == LFLiveStop || status == LFLiveError){
|
||||
self.uploading = NO;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.state = status;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:liveStateDidChange:)]) {
|
||||
[self.delegate liveSession:self liveStateDidChange:status];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:errorCode:)]) {
|
||||
[self.delegate liveSession:self errorCode:errorCode];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug *)debugInfo {
|
||||
self.debugInfo = debugInfo;
|
||||
if (self.showDebugInfo) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:debugInfo:)]) {
|
||||
[self.delegate liveSession:self debugInfo:debugInfo];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status {
|
||||
if((self.captureType & LFLiveCaptureMaskVideo || self.captureType & LFLiveInputMaskVideo) && self.adaptiveBitrate){
|
||||
NSUInteger videoBitRate = [self.videoEncoder videoBitRate];
|
||||
if (status == LFLiveBuffferDecline) {
|
||||
if (videoBitRate < _videoConfiguration.videoMaxBitRate) {
|
||||
videoBitRate = videoBitRate + 50 * 1000;
|
||||
[self.videoEncoder setVideoBitRate:videoBitRate];
|
||||
NSLog(@"Increase bitrate %@", @(videoBitRate));
|
||||
}
|
||||
} else {
|
||||
if (videoBitRate > self.videoConfiguration.videoMinBitRate) {
|
||||
videoBitRate = videoBitRate - 100 * 1000;
|
||||
[self.videoEncoder setVideoBitRate:videoBitRate];
|
||||
NSLog(@"Decline bitrate %@", @(videoBitRate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- Getter Setter
|
||||
- (void)setRunning:(BOOL)running {
|
||||
if (_running == running) return;
|
||||
[self willChangeValueForKey:@"running"];
|
||||
_running = running;
|
||||
[self didChangeValueForKey:@"running"];
|
||||
self.videoCaptureSource.running = _running;
|
||||
self.audioCaptureSource.running = _running;
|
||||
}
|
||||
|
||||
- (void)setPreView:(UIView *)preView {
|
||||
[self willChangeValueForKey:@"preView"];
|
||||
[self.videoCaptureSource setPreView:preView];
|
||||
[self didChangeValueForKey:@"preView"];
|
||||
}
|
||||
|
||||
- (UIView *)preView {
|
||||
return self.videoCaptureSource.preView;
|
||||
}
|
||||
|
||||
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition {
|
||||
[self willChangeValueForKey:@"captureDevicePosition"];
|
||||
[self.videoCaptureSource setCaptureDevicePosition:captureDevicePosition];
|
||||
[self didChangeValueForKey:@"captureDevicePosition"];
|
||||
}
|
||||
|
||||
- (AVCaptureDevicePosition)captureDevicePosition {
|
||||
return self.videoCaptureSource.captureDevicePosition;
|
||||
}
|
||||
|
||||
- (void)setBeautyFace:(BOOL)beautyFace {
|
||||
[self willChangeValueForKey:@"beautyFace"];
|
||||
[self.videoCaptureSource setBeautyFace:beautyFace];
|
||||
[self didChangeValueForKey:@"beautyFace"];
|
||||
}
|
||||
|
||||
- (BOOL)beautyFace {
|
||||
return self.videoCaptureSource.beautyFace;
|
||||
}
|
||||
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel {
|
||||
[self willChangeValueForKey:@"beautyLevel"];
|
||||
[self.videoCaptureSource setBeautyLevel:beautyLevel];
|
||||
[self didChangeValueForKey:@"beautyLevel"];
|
||||
}
|
||||
|
||||
- (CGFloat)beautyLevel {
|
||||
return self.videoCaptureSource.beautyLevel;
|
||||
}
|
||||
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel {
|
||||
[self willChangeValueForKey:@"brightLevel"];
|
||||
[self.videoCaptureSource setBrightLevel:brightLevel];
|
||||
[self didChangeValueForKey:@"brightLevel"];
|
||||
}
|
||||
|
||||
- (CGFloat)brightLevel {
|
||||
return self.videoCaptureSource.brightLevel;
|
||||
}
|
||||
|
||||
- (void)setZoomScale:(CGFloat)zoomScale {
|
||||
[self willChangeValueForKey:@"zoomScale"];
|
||||
[self.videoCaptureSource setZoomScale:zoomScale];
|
||||
[self didChangeValueForKey:@"zoomScale"];
|
||||
}
|
||||
|
||||
- (CGFloat)zoomScale {
|
||||
return self.videoCaptureSource.zoomScale;
|
||||
}
|
||||
|
||||
- (void)setTorch:(BOOL)torch {
|
||||
[self willChangeValueForKey:@"torch"];
|
||||
[self.videoCaptureSource setTorch:torch];
|
||||
[self didChangeValueForKey:@"torch"];
|
||||
}
|
||||
|
||||
- (BOOL)torch {
|
||||
return self.videoCaptureSource.torch;
|
||||
}
|
||||
|
||||
- (void)setMirror:(BOOL)mirror {
|
||||
[self willChangeValueForKey:@"mirror"];
|
||||
[self.videoCaptureSource setMirror:mirror];
|
||||
[self didChangeValueForKey:@"mirror"];
|
||||
}
|
||||
|
||||
- (BOOL)mirror {
|
||||
return self.videoCaptureSource.mirror;
|
||||
}
|
||||
|
||||
- (void)setMuted:(BOOL)muted {
|
||||
[self willChangeValueForKey:@"muted"];
|
||||
[self.audioCaptureSource setMuted:muted];
|
||||
[self didChangeValueForKey:@"muted"];
|
||||
}
|
||||
|
||||
- (BOOL)muted {
|
||||
return self.audioCaptureSource.muted;
|
||||
}
|
||||
|
||||
- (void)setWarterMarkView:(UIView *)warterMarkView{
|
||||
[self.videoCaptureSource setWarterMarkView:warterMarkView];
|
||||
}
|
||||
|
||||
- (nullable UIView*)warterMarkView{
|
||||
return self.videoCaptureSource.warterMarkView;
|
||||
}
|
||||
|
||||
- (nullable UIImage *)currentImage{
|
||||
return self.videoCaptureSource.currentImage;
|
||||
}
|
||||
|
||||
- (LFAudioCapture *)audioCaptureSource {
|
||||
if (!_audioCaptureSource) {
|
||||
if(self.captureType & LFLiveCaptureMaskAudio){
|
||||
_audioCaptureSource = [[LFAudioCapture alloc] initWithAudioConfiguration:_audioConfiguration];
|
||||
_audioCaptureSource.delegate = self;
|
||||
}
|
||||
}
|
||||
return _audioCaptureSource;
|
||||
}
|
||||
|
||||
- (LFVideoCapture *)videoCaptureSource {
|
||||
if (!_videoCaptureSource) {
|
||||
if(self.captureType & LFLiveCaptureMaskVideo){
|
||||
_videoCaptureSource = [[LFVideoCapture alloc] initWithVideoConfiguration:_videoConfiguration];
|
||||
_videoCaptureSource.delegate = self;
|
||||
}
|
||||
}
|
||||
return _videoCaptureSource;
|
||||
}
|
||||
|
||||
- (id<LFAudioEncoding>)audioEncoder {
|
||||
if (!_audioEncoder) {
|
||||
_audioEncoder = [[LFHardwareAudioEncoder alloc] initWithAudioStreamConfiguration:_audioConfiguration];
|
||||
[_audioEncoder setDelegate:self];
|
||||
}
|
||||
return _audioEncoder;
|
||||
}
|
||||
|
||||
- (id<LFVideoEncoding>)videoEncoder {
|
||||
if (!_videoEncoder) {
|
||||
if([[UIDevice currentDevice].systemVersion floatValue] < 8.0){
|
||||
_videoEncoder = [[LFH264VideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
|
||||
}else{
|
||||
_videoEncoder = [[LFHardwareVideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
|
||||
}
|
||||
[_videoEncoder setDelegate:self];
|
||||
}
|
||||
return _videoEncoder;
|
||||
}
|
||||
|
||||
- (id<LFStreamSocket>)socket {
|
||||
if (!_socket) {
|
||||
_socket = [[LFStreamRTMPSocket alloc] initWithStream:self.streamInfo reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
|
||||
[_socket setDelegate:self];
|
||||
}
|
||||
return _socket;
|
||||
}
|
||||
|
||||
- (LFLiveStreamInfo *)streamInfo {
|
||||
if (!_streamInfo) {
|
||||
_streamInfo = [[LFLiveStreamInfo alloc] init];
|
||||
}
|
||||
return _streamInfo;
|
||||
}
|
||||
|
||||
- (dispatch_semaphore_t)lock{
|
||||
if(!_lock){
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
}
|
||||
return _lock;
|
||||
}
|
||||
|
||||
- (uint64_t)uploadTimestamp:(uint64_t)captureTimestamp{
|
||||
dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER);
|
||||
uint64_t currentts = 0;
|
||||
currentts = captureTimestamp - self.relativeTimestamps;
|
||||
dispatch_semaphore_signal(self.lock);
|
||||
return currentts;
|
||||
}
|
||||
|
||||
- (BOOL)AVAlignment{
|
||||
if((self.captureType & LFLiveCaptureMaskAudio || self.captureType & LFLiveInputMaskAudio) &&
|
||||
(self.captureType & LFLiveCaptureMaskVideo || self.captureType & LFLiveInputMaskVideo)
|
||||
){
|
||||
if(self.hasCaptureAudio && self.hasKeyFrameVideo) return YES;
|
||||
else return NO;
|
||||
}else{
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -17,7 +17,7 @@ extern NSString *_Nullable const LFAudioComponentFailedToCreateNotification;
|
||||
@class LFAudioCapture;
|
||||
/** LFAudioCapture callback audioData */
|
||||
@protocol LFAudioCaptureDelegate <NSObject>
|
||||
- (void)captureOutput:(nullable LFAudioCapture*)capture audioBuffer:(AudioBufferList)inBufferList;
|
||||
- (void)captureOutput:(nullable LFAudioCapture *)capture audioData:(nullable NSData*)audioData;
|
||||
@end
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@ extern NSString *_Nullable const LFAudioComponentFailedToCreateNotification;
|
||||
///=============================================================================
|
||||
|
||||
/** The delegate of the capture. captureData callback */
|
||||
@property (nullable,nonatomic, weak) id<LFAudioCaptureDelegate> delegate;
|
||||
@property (nullable, nonatomic, weak) id<LFAudioCaptureDelegate> delegate;
|
||||
|
||||
/** The muted control callbackAudioData,muted will memset 0.*/
|
||||
@property (nonatomic,assign) BOOL muted;
|
||||
@property (nonatomic, assign) BOOL muted;
|
||||
|
||||
/** The running control start capture or stop capture*/
|
||||
@property (nonatomic, assign) BOOL running;
|
||||
@@ -45,8 +45,8 @@ extern NSString *_Nullable const LFAudioComponentFailedToCreateNotification;
|
||||
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
*/
|
||||
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@@ -14,11 +14,11 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
|
||||
@interface LFAudioCapture ()
|
||||
|
||||
@property (nonatomic, assign) AudioComponentInstance componetInstance;
|
||||
@property (nonatomic, assign) AudioComponent component;
|
||||
@property (nonatomic, strong) dispatch_queue_t taskQueue;
|
||||
@property (nonatomic, assign) AudioComponentInstance componetInstance;
|
||||
@property (nonatomic, assign) AudioComponent component;
|
||||
@property (nonatomic, strong) dispatch_queue_t taskQueue;
|
||||
@property (nonatomic, assign) BOOL isRunning;
|
||||
@property (nonatomic, strong) LFLiveAudioConfiguration *configuration;
|
||||
@property (nonatomic, strong,nullable) LFLiveAudioConfiguration *configuration;
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,7 +32,7 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
self.taskQueue = dispatch_queue_create("com.youku.Laifeng.audioCapture.Queue", NULL);
|
||||
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
[session setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:nil];
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(handleRouteChange:)
|
||||
@@ -43,18 +43,9 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
name: AVAudioSessionInterruptionNotification
|
||||
object: session];
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers error:nil];
|
||||
|
||||
[session setMode:AVAudioSessionModeVideoRecording error:&error];
|
||||
|
||||
if (![session setActive:YES error:&error]) {
|
||||
[self handleAudioComponentCreationFailure];
|
||||
}
|
||||
|
||||
AudioComponentDescription acd;
|
||||
acd.componentType = kAudioUnitType_Output;
|
||||
//acd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
|
||||
acd.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
acd.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
acd.componentFlags = 0;
|
||||
@@ -96,18 +87,19 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
}
|
||||
|
||||
[session setPreferredSampleRate:_configuration.audioSampleRate error:nil];
|
||||
|
||||
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:nil];
|
||||
[session setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:nil];
|
||||
|
||||
[session setActive:YES error:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
|
||||
dispatch_sync(self.taskQueue, ^{
|
||||
if(self.componetInstance){
|
||||
if (self.componetInstance) {
|
||||
AudioOutputUnitStop(self.componetInstance);
|
||||
AudioComponentInstanceDispose(self.componetInstance);
|
||||
self.componetInstance = nil;
|
||||
@@ -117,16 +109,17 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
}
|
||||
|
||||
#pragma mark -- Setter
|
||||
- (void)setRunning:(BOOL)running{
|
||||
if(_running == running) return;
|
||||
- (void)setRunning:(BOOL)running {
|
||||
if (_running == running) return;
|
||||
_running = running;
|
||||
if(_running){
|
||||
if (_running) {
|
||||
dispatch_async(self.taskQueue, ^{
|
||||
self.isRunning = YES;
|
||||
NSLog(@"MicrophoneSource: startRunning");
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:nil];
|
||||
AudioOutputUnitStart(self.componetInstance);
|
||||
});
|
||||
}else{
|
||||
} else {
|
||||
self.isRunning = NO;
|
||||
}
|
||||
}
|
||||
@@ -141,45 +134,44 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
#pragma mark -- NSNotification
|
||||
- (void)handleRouteChange:(NSNotification *)notification {
|
||||
AVAudioSession *session = [ AVAudioSession sharedInstance ];
|
||||
NSString* seccReason = @"";
|
||||
NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
|
||||
NSString *seccReason = @"";
|
||||
NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
|
||||
// AVAudioSessionRouteDescription* prevRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey];
|
||||
switch (reason) {
|
||||
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
|
||||
seccReason = @"The route changed because no suitable route is now available for the specified category.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonWakeFromSleep:
|
||||
seccReason = @"The route changed when the device woke up from sleep.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonOverride:
|
||||
seccReason = @"The output route was overridden by the app.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonCategoryChange:
|
||||
seccReason = @"The category of the session object changed.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
|
||||
seccReason = @"The previous audio output path is no longer available.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
|
||||
seccReason = @"A preferred new audio output path is now available.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonUnknown:
|
||||
default:
|
||||
seccReason = @"The reason for the change is unknown.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
|
||||
seccReason = @"The route changed because no suitable route is now available for the specified category.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonWakeFromSleep:
|
||||
seccReason = @"The route changed when the device woke up from sleep.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonOverride:
|
||||
seccReason = @"The output route was overridden by the app.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonCategoryChange:
|
||||
seccReason = @"The category of the session object changed.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
|
||||
seccReason = @"The previous audio output path is no longer available.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
|
||||
seccReason = @"A preferred new audio output path is now available.";
|
||||
break;
|
||||
case AVAudioSessionRouteChangeReasonUnknown:
|
||||
default:
|
||||
seccReason = @"The reason for the change is unknown.";
|
||||
break;
|
||||
}
|
||||
NSLog(@"handleRouteChange reason is %@",seccReason);
|
||||
|
||||
[[AVAudioSession sharedInstance] setActive:YES error:nil];
|
||||
AVAudioSessionPortDescription *input = [[session.currentRoute.inputs count]?session.currentRoute.inputs:nil objectAtIndex:0];
|
||||
NSLog(@"handleRouteChange reason is %@", seccReason);
|
||||
|
||||
AVAudioSessionPortDescription *input = [[session.currentRoute.inputs count] ? session.currentRoute.inputs : nil objectAtIndex:0];
|
||||
if (input.portType == AVAudioSessionPortHeadsetMic) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleInterruption:(NSNotification *)notification {
|
||||
NSInteger reason = 0;
|
||||
NSString* reasonStr = @"";
|
||||
NSString *reasonStr = @"";
|
||||
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
|
||||
//Posted when an audio interruption occurs.
|
||||
reason = [[[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
|
||||
@@ -191,27 +183,28 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (reason == AVAudioSessionInterruptionTypeEnded) {
|
||||
reasonStr = @"AVAudioSessionInterruptionTypeEnded";
|
||||
NSNumber* seccondReason = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey] ;
|
||||
NSNumber *seccondReason = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];
|
||||
switch ([seccondReason integerValue]) {
|
||||
case AVAudioSessionInterruptionOptionShouldResume:
|
||||
if (self.isRunning) {
|
||||
dispatch_async(self.taskQueue, ^{
|
||||
NSLog(@"MicrophoneSource: stopRunning");
|
||||
AudioOutputUnitStart(self.componetInstance);
|
||||
});
|
||||
}
|
||||
// Indicates that the audio session is active and immediately ready to be used. Your app can resume the audio operation that was interrupted.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case AVAudioSessionInterruptionOptionShouldResume:
|
||||
if (self.isRunning) {
|
||||
dispatch_async(self.taskQueue, ^{
|
||||
NSLog(@"MicrophoneSource: stopRunning");
|
||||
AudioOutputUnitStart(self.componetInstance);
|
||||
});
|
||||
}
|
||||
// Indicates that the audio session is active and immediately ready to be used. Your app can resume the audio operation that was interrupted.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
NSLog(@"handleInterruption: %@ reason %@",[notification name], reasonStr);
|
||||
|
||||
}
|
||||
;
|
||||
NSLog(@"handleInterruption: %@ reason %@", [notification name], reasonStr);
|
||||
}
|
||||
|
||||
#pragma mark -- CallBack
|
||||
@@ -223,43 +216,43 @@ static OSStatus handleInputBuffer(void *inRefCon,
|
||||
AudioBufferList *ioData) {
|
||||
@autoreleasepool {
|
||||
LFAudioCapture *source = (__bridge LFAudioCapture *)inRefCon;
|
||||
if(!source) return -1;
|
||||
|
||||
if (!source) return -1;
|
||||
|
||||
AudioBuffer buffer;
|
||||
buffer.mData = NULL;
|
||||
buffer.mDataByteSize = 0;
|
||||
buffer.mNumberChannels = 1;
|
||||
|
||||
|
||||
AudioBufferList buffers;
|
||||
buffers.mNumberBuffers = 1;
|
||||
buffers.mBuffers[0] = buffer;
|
||||
|
||||
|
||||
OSStatus status = AudioUnitRender(source.componetInstance,
|
||||
ioActionFlags,
|
||||
inTimeStamp,
|
||||
inBusNumber,
|
||||
inNumberFrames,
|
||||
&buffers);
|
||||
|
||||
|
||||
if (!source.isRunning) {
|
||||
dispatch_sync(source.taskQueue, ^{
|
||||
NSLog(@"MicrophoneSource: stopRunning");
|
||||
AudioOutputUnitStop(source.componetInstance);
|
||||
});
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
if (source.muted) {
|
||||
for (int i = 0; i < buffers.mNumberBuffers; i++) {
|
||||
AudioBuffer ab = buffers.mBuffers[i];
|
||||
memset(ab.mData, 0, ab.mDataByteSize);
|
||||
}
|
||||
}
|
||||
|
||||
if(!status) {
|
||||
if(source.delegate && [source.delegate respondsToSelector:@selector(captureOutput:audioBuffer:)]){
|
||||
[source.delegate captureOutput:source audioBuffer:buffers];
|
||||
|
||||
if (!status) {
|
||||
if (source.delegate && [source.delegate respondsToSelector:@selector(captureOutput:audioData:)]) {
|
||||
[source.delegate captureOutput:source audioData:[NSData dataWithBytes:buffers.mBuffers[0].mData length:buffers.mBuffers[0].mDataByteSize]];
|
||||
}
|
||||
}
|
||||
return status;
|
||||
@@ -13,7 +13,7 @@
|
||||
@class LFVideoCapture;
|
||||
/** LFVideoCapture callback videoData */
|
||||
@protocol LFVideoCaptureDelegate <NSObject>
|
||||
- (void)captureOutput:(nullable LFVideoCapture*)capture pixelBuffer:(nullable CVImageBufferRef)pixelBuffer;
|
||||
- (void)captureOutput:(nullable LFVideoCapture *)capture pixelBuffer:(nullable CVPixelBufferRef)pixelBuffer;
|
||||
@end
|
||||
|
||||
@interface LFVideoCapture : NSObject
|
||||
@@ -24,13 +24,13 @@
|
||||
///=============================================================================
|
||||
|
||||
/** The delegate of the capture. captureData callback */
|
||||
@property (nullable,nonatomic, weak) id<LFVideoCaptureDelegate> delegate;
|
||||
@property (nullable, nonatomic, weak) id<LFVideoCaptureDelegate> delegate;
|
||||
|
||||
/** The running control start capture or stop capture*/
|
||||
@property (nonatomic, assign) BOOL running;
|
||||
|
||||
/** The preView will show OpenGL ES view*/
|
||||
@property (null_resettable,nonatomic, strong) UIView * preView;
|
||||
@property (null_resettable, nonatomic, strong) UIView *preView;
|
||||
|
||||
/** The captureDevicePosition control camraPosition ,default front*/
|
||||
@property (nonatomic, assign) AVCaptureDevicePosition captureDevicePosition;
|
||||
@@ -56,6 +56,10 @@
|
||||
/** The videoFrameRate control videoCapture output data count */
|
||||
@property (nonatomic, assign) NSInteger videoFrameRate;
|
||||
|
||||
/*** The warterMarkView control whether the watermark is displayed or not ,if set ni,will remove watermark,otherwise add *.*/
|
||||
@property (nonatomic, strong, nullable) UIView *warterMarkView;
|
||||
|
||||
@property (nonatomic, strong, nullable) UIImage *currentImage;
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
/// @name Initializer
|
||||
@@ -64,8 +68,8 @@
|
||||
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
*/
|
||||
- (nullable instancetype)initWithVideoConfiguration:(nullable LFLiveVideoConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
Executable
+367
@@ -0,0 +1,367 @@
|
||||
//
|
||||
// LFVideoCapture.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/1.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFVideoCapture.h"
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
#import "LFGPUImageEmptyFilter.h"
|
||||
|
||||
#if __has_include(<GPUImage/GPUImage.h>)
|
||||
#import <GPUImage/GPUImage.h>
|
||||
#elif __has_include("GPUImage/GPUImage.h")
|
||||
#import "GPUImage/GPUImage.h"
|
||||
#else
|
||||
#import "GPUImage.h"
|
||||
#endif
|
||||
|
||||
@interface LFVideoCapture ()
|
||||
|
||||
@property (nonatomic, strong) GPUImageVideoCamera *videoCamera;
|
||||
@property (nonatomic, strong) LFGPUImageBeautyFilter *beautyFilter;
|
||||
@property (nonatomic, strong) GPUImageOutput<GPUImageInput> *filter;
|
||||
@property (nonatomic, strong) GPUImageCropFilter *cropfilter;
|
||||
@property (nonatomic, strong) GPUImageOutput<GPUImageInput> *output;
|
||||
@property (nonatomic, strong) GPUImageView *gpuImageView;
|
||||
@property (nonatomic, strong) LFLiveVideoConfiguration *configuration;
|
||||
|
||||
@property (nonatomic, strong) GPUImageAlphaBlendFilter *blendFilter;
|
||||
@property (nonatomic, strong) GPUImageUIElement *uiElementInput;
|
||||
@property (nonatomic, strong) UIView *waterMarkContentView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFVideoCapture
|
||||
@synthesize torch = _torch;
|
||||
@synthesize beautyLevel = _beautyLevel;
|
||||
@synthesize brightLevel = _brightLevel;
|
||||
@synthesize zoomScale = _zoomScale;
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithVideoConfiguration:(LFLiveVideoConfiguration *)configuration {
|
||||
if (self = [super init]) {
|
||||
_configuration = configuration;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarChanged:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
|
||||
self.beautyFace = YES;
|
||||
self.beautyLevel = 0.5;
|
||||
self.brightLevel = 0.5;
|
||||
self.zoomScale = 1.0;
|
||||
self.mirror = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[UIApplication sharedApplication].idleTimerDisabled = NO;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[self.videoCamera stopCameraCapture];
|
||||
if(self.gpuImageView){
|
||||
[self.gpuImageView removeFromSuperview];
|
||||
self.gpuImageView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- Setter Getter
|
||||
|
||||
- (GPUImageVideoCamera *)videoCamera{
|
||||
if(!_videoCamera){
|
||||
_videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:_configuration.avSessionPreset cameraPosition:AVCaptureDevicePositionFront];
|
||||
UIInterfaceOrientation statusBar = [[UIApplication sharedApplication] statusBarOrientation];
|
||||
if (self.configuration.landscape) {
|
||||
if (statusBar != UIInterfaceOrientationLandscapeLeft && statusBar != UIInterfaceOrientationLandscapeRight) {
|
||||
@throw [NSException exceptionWithName:@"当前设置方向出错" reason:@"LFLiveVideoConfiguration landscape error" userInfo:nil];
|
||||
} else {
|
||||
_videoCamera.outputImageOrientation = statusBar;
|
||||
}
|
||||
} else {
|
||||
if (statusBar != UIInterfaceOrientationPortrait && statusBar != UIInterfaceOrientationPortraitUpsideDown) {
|
||||
@throw [NSException exceptionWithName:@"当前设置方向出错" reason:@"LFLiveVideoConfiguration landscape error" userInfo:nil];
|
||||
} else {
|
||||
_videoCamera.outputImageOrientation = statusBar;
|
||||
}
|
||||
}
|
||||
|
||||
_videoCamera.horizontallyMirrorFrontFacingCamera = YES;
|
||||
_videoCamera.horizontallyMirrorRearFacingCamera = NO;
|
||||
_videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
|
||||
}
|
||||
return _videoCamera;
|
||||
}
|
||||
|
||||
- (void)setRunning:(BOOL)running {
|
||||
if (_running == running) return;
|
||||
_running = running;
|
||||
|
||||
if (!_running) {
|
||||
[UIApplication sharedApplication].idleTimerDisabled = NO;
|
||||
[self.videoCamera stopCameraCapture];
|
||||
} else {
|
||||
[UIApplication sharedApplication].idleTimerDisabled = YES;
|
||||
[self reloadFilter];
|
||||
[self.videoCamera startCameraCapture];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setPreView:(UIView *)preView {
|
||||
if (self.gpuImageView.superview) [self.gpuImageView removeFromSuperview];
|
||||
[preView insertSubview:self.gpuImageView atIndex:0];
|
||||
self.gpuImageView.frame = CGRectMake(0, 0, preView.frame.size.width, preView.frame.size.height);
|
||||
}
|
||||
|
||||
- (UIView *)preView {
|
||||
return self.gpuImageView.superview;
|
||||
}
|
||||
|
||||
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition {
|
||||
[self.videoCamera rotateCamera];
|
||||
self.videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
|
||||
}
|
||||
|
||||
- (AVCaptureDevicePosition)captureDevicePosition {
|
||||
return [self.videoCamera cameraPosition];
|
||||
}
|
||||
|
||||
- (void)setVideoFrameRate:(NSInteger)videoFrameRate {
|
||||
if (videoFrameRate <= 0) return;
|
||||
if (videoFrameRate == self.videoCamera.frameRate) return;
|
||||
self.videoCamera.frameRate = (uint32_t)videoFrameRate;
|
||||
}
|
||||
|
||||
- (NSInteger)videoFrameRate {
|
||||
return self.videoCamera.frameRate;
|
||||
}
|
||||
|
||||
- (void)setTorch:(BOOL)torch {
|
||||
BOOL ret;
|
||||
if (!self.videoCamera.captureSession) return;
|
||||
AVCaptureSession *session = (AVCaptureSession *)self.videoCamera.captureSession;
|
||||
[session beginConfiguration];
|
||||
if (self.videoCamera.inputCamera) {
|
||||
if (self.videoCamera.inputCamera.torchAvailable) {
|
||||
NSError *err = nil;
|
||||
if ([self.videoCamera.inputCamera lockForConfiguration:&err]) {
|
||||
[self.videoCamera.inputCamera setTorchMode:(torch ? AVCaptureTorchModeOn : AVCaptureTorchModeOff) ];
|
||||
[self.videoCamera.inputCamera unlockForConfiguration];
|
||||
ret = (self.videoCamera.inputCamera.torchMode == AVCaptureTorchModeOn);
|
||||
} else {
|
||||
NSLog(@"Error while locking device for torch: %@", err);
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
NSLog(@"Torch not available in current camera input");
|
||||
}
|
||||
}
|
||||
[session commitConfiguration];
|
||||
_torch = ret;
|
||||
}
|
||||
|
||||
- (BOOL)torch {
|
||||
return self.videoCamera.inputCamera.torchMode;
|
||||
}
|
||||
|
||||
- (void)setMirror:(BOOL)mirror {
|
||||
_mirror = mirror;
|
||||
self.videoCamera.horizontallyMirrorFrontFacingCamera = mirror;
|
||||
}
|
||||
|
||||
- (void)setBeautyFace:(BOOL)beautyFace{
|
||||
_beautyFace = beautyFace;
|
||||
[self reloadFilter];
|
||||
}
|
||||
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel {
|
||||
_beautyLevel = beautyLevel;
|
||||
if (self.beautyFilter) {
|
||||
[self.beautyFilter setBeautyLevel:_beautyLevel];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)beautyLevel {
|
||||
return _beautyLevel;
|
||||
}
|
||||
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel {
|
||||
_brightLevel = brightLevel;
|
||||
if (self.beautyFilter) {
|
||||
[self.beautyFilter setBrightLevel:brightLevel];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)brightLevel {
|
||||
return _brightLevel;
|
||||
}
|
||||
|
||||
- (void)setZoomScale:(CGFloat)zoomScale {
|
||||
if (self.videoCamera && self.videoCamera.inputCamera) {
|
||||
AVCaptureDevice *device = (AVCaptureDevice *)self.videoCamera.inputCamera;
|
||||
if ([device lockForConfiguration:nil]) {
|
||||
device.videoZoomFactor = zoomScale;
|
||||
[device unlockForConfiguration];
|
||||
_zoomScale = zoomScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)zoomScale {
|
||||
return _zoomScale;
|
||||
}
|
||||
|
||||
- (void)setWarterMarkView:(UIView *)warterMarkView{
|
||||
if(_warterMarkView && _warterMarkView.superview){
|
||||
[_warterMarkView removeFromSuperview];
|
||||
_warterMarkView = nil;
|
||||
}
|
||||
_warterMarkView = warterMarkView;
|
||||
self.blendFilter.mix = warterMarkView.alpha;
|
||||
[self.waterMarkContentView addSubview:_warterMarkView];
|
||||
[self reloadFilter];
|
||||
}
|
||||
|
||||
- (GPUImageUIElement *)uiElementInput{
|
||||
if(!_uiElementInput){
|
||||
_uiElementInput = [[GPUImageUIElement alloc] initWithView:self.waterMarkContentView];
|
||||
}
|
||||
return _uiElementInput;
|
||||
}
|
||||
|
||||
- (GPUImageAlphaBlendFilter *)blendFilter{
|
||||
if(!_blendFilter){
|
||||
_blendFilter = [[GPUImageAlphaBlendFilter alloc] init];
|
||||
_blendFilter.mix = 1.0;
|
||||
[_blendFilter disableSecondFrameCheck];
|
||||
}
|
||||
return _blendFilter;
|
||||
}
|
||||
|
||||
- (UIView *)waterMarkContentView{
|
||||
if(!_waterMarkContentView){
|
||||
_waterMarkContentView = [UIView new];
|
||||
_waterMarkContentView.frame = CGRectMake(0, 0, self.configuration.videoSize.width, self.configuration.videoSize.height);
|
||||
_waterMarkContentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
}
|
||||
return _waterMarkContentView;
|
||||
}
|
||||
|
||||
- (GPUImageView *)gpuImageView{
|
||||
if(!_gpuImageView){
|
||||
_gpuImageView = [[GPUImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
[_gpuImageView setFillMode:kGPUImageFillModePreserveAspectRatioAndFill];
|
||||
[_gpuImageView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
|
||||
}
|
||||
return _gpuImageView;
|
||||
}
|
||||
|
||||
-(UIImage *)currentImage{
|
||||
if(_filter){
|
||||
[_filter useNextFrameForImageCapture];
|
||||
return _filter.imageFromCurrentFramebuffer;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark -- Custom Method
|
||||
- (void)processVideo:(GPUImageOutput *)output {
|
||||
__weak typeof(self) _self = self;
|
||||
@autoreleasepool {
|
||||
GPUImageFramebuffer *imageFramebuffer = output.framebufferForOutput;
|
||||
CVPixelBufferRef pixelBuffer = [imageFramebuffer pixelBuffer];
|
||||
if (pixelBuffer && _self.delegate && [_self.delegate respondsToSelector:@selector(captureOutput:pixelBuffer:)]) {
|
||||
[_self.delegate captureOutput:_self pixelBuffer:pixelBuffer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)reloadFilter{
|
||||
[self.filter removeAllTargets];
|
||||
[self.blendFilter removeAllTargets];
|
||||
[self.uiElementInput removeAllTargets];
|
||||
[self.videoCamera removeAllTargets];
|
||||
[self.output removeAllTargets];
|
||||
[self.cropfilter removeAllTargets];
|
||||
|
||||
if (self.beautyFace) {
|
||||
self.output = [[LFGPUImageEmptyFilter alloc] init];
|
||||
self.filter = [[LFGPUImageBeautyFilter alloc] init];
|
||||
self.beautyFilter = (LFGPUImageBeautyFilter*)self.filter;
|
||||
} else {
|
||||
self.output = [[LFGPUImageEmptyFilter alloc] init];
|
||||
self.filter = [[LFGPUImageEmptyFilter alloc] init];
|
||||
self.beautyFilter = nil;
|
||||
}
|
||||
|
||||
//< 480*640 比例为4:3 强制转换为16:9
|
||||
if([self.configuration.avSessionPreset isEqualToString:AVCaptureSessionPreset640x480]){
|
||||
CGRect cropRect = self.configuration.landscape ? CGRectMake(0, 0.125, 1, 0.75) : CGRectMake(0.125, 0, 0.75, 1);
|
||||
self.cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:cropRect];
|
||||
[self.videoCamera addTarget:self.cropfilter];
|
||||
[self.cropfilter addTarget:self.filter];
|
||||
}else{
|
||||
[self.videoCamera addTarget:self.filter];
|
||||
}
|
||||
|
||||
//< 添加水印
|
||||
if(self.warterMarkView){
|
||||
[self.filter addTarget:self.blendFilter];
|
||||
[self.uiElementInput addTarget:self.blendFilter];
|
||||
[self.blendFilter addTarget:self.gpuImageView];
|
||||
[self.filter addTarget:self.output];
|
||||
[self.uiElementInput update];
|
||||
}else{
|
||||
[self.filter addTarget:self.output];
|
||||
[self.output addTarget:self.gpuImageView];
|
||||
}
|
||||
|
||||
[self.filter forceProcessingAtSize:self.configuration.videoSize];
|
||||
[self.output forceProcessingAtSize:self.configuration.videoSize];
|
||||
[self.blendFilter forceProcessingAtSize:self.configuration.videoSize];
|
||||
[self.uiElementInput forceProcessingAtSize:self.configuration.videoSize];
|
||||
|
||||
//< 输出数据
|
||||
__weak typeof(self) _self = self;
|
||||
[self.output setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
|
||||
[_self processVideo:output];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark Notification
|
||||
|
||||
- (void)willEnterBackground:(NSNotification *)notification {
|
||||
[UIApplication sharedApplication].idleTimerDisabled = NO;
|
||||
[self.videoCamera pauseCameraCapture];
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
glFinish();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)willEnterForeground:(NSNotification *)notification {
|
||||
[self.videoCamera resumeCameraCapture];
|
||||
[UIApplication sharedApplication].idleTimerDisabled = YES;
|
||||
}
|
||||
|
||||
- (void)statusBarChanged:(NSNotification *)notification {
|
||||
NSLog(@"UIApplicationWillChangeStatusBarOrientationNotification. UserInfo: %@", notification.userInfo);
|
||||
UIInterfaceOrientation statusBar = [[UIApplication sharedApplication] statusBarOrientation];
|
||||
if (self.configuration.landscape) {
|
||||
if (statusBar == UIInterfaceOrientationLandscapeLeft) {
|
||||
self.videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeRight;
|
||||
} else if (statusBar == UIInterfaceOrientationLandscapeRight) {
|
||||
self.videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeLeft;
|
||||
}
|
||||
} else {
|
||||
if (statusBar == UIInterfaceOrientationPortrait) {
|
||||
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortraitUpsideDown;
|
||||
} else if (statusBar == UIInterfaceOrientationPortraitUpsideDown) {
|
||||
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// AVEncoder.h
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 14/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVAssetWriter.h>
|
||||
#import <AVFoundation/AVAssetWriterInput.h>
|
||||
#import <AVFoundation/AVMediaFormat.h>
|
||||
#import <AVFoundation/AVVideoSettings.h>
|
||||
#import "sys/stat.h"
|
||||
#import "LFVideoEncoder.h"
|
||||
#import "LFMP4Atom.h"
|
||||
|
||||
typedef int (^encoder_handler_t)(NSArray *data, CMTimeValue ptsValue);
|
||||
typedef int (^param_handler_t)(NSData *params);
|
||||
|
||||
@interface LFAVEncoder : NSObject
|
||||
|
||||
@property (atomic) NSUInteger bitrate;
|
||||
|
||||
+ (LFAVEncoder *)encoderForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate;
|
||||
|
||||
- (void)encodeWithBlock:(encoder_handler_t)block onParams:(param_handler_t)paramsHandler;
|
||||
- (void)encodeFrame:(CMSampleBufferRef)sampleBuffer;
|
||||
- (NSData *)getConfigData;
|
||||
- (void)shutdown;
|
||||
|
||||
|
||||
@property (readonly, atomic) int bitspersecond;
|
||||
|
||||
@end
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
//
|
||||
// AVEncoder.m
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 14/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import "LFAVEncoder.h"
|
||||
#import "LFNALUnit.h"
|
||||
|
||||
static void *AVEncoderContext = &AVEncoderContext;
|
||||
|
||||
static unsigned int to_host(unsigned char *p){
|
||||
return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
|
||||
}
|
||||
|
||||
#define OUTPUT_FILE_SWITCH_POINT (50 * 1024 * 1024) // 50 MB switch point
|
||||
#define MAX_FILENAME_INDEX 5 // filenames "capture1.mp4" wraps at capture5.mp4
|
||||
|
||||
|
||||
@interface LFAVEncoder ()
|
||||
|
||||
{
|
||||
// initial writer, used to obtain SPS/PPS from header
|
||||
LFVideoEncoder *_headerWriter;
|
||||
|
||||
// main encoder/writer
|
||||
LFVideoEncoder *_writer;
|
||||
|
||||
// writer output file (input to our extractor) and monitoring
|
||||
NSFileHandle *_inputFile;
|
||||
dispatch_queue_t _readQueue;
|
||||
dispatch_source_t _readSource;
|
||||
|
||||
// index of current file name
|
||||
BOOL _swapping;
|
||||
int _currentFile;
|
||||
int _height;
|
||||
int _width;
|
||||
|
||||
// param set data
|
||||
NSData *_avcC;
|
||||
int _lengthSize;
|
||||
|
||||
// location of mdat
|
||||
BOOL _foundMDAT;
|
||||
uint64_t _posMDAT;
|
||||
int _bytesToNextAtom;
|
||||
BOOL _needParams;
|
||||
|
||||
// tracking if NALU is next frame
|
||||
int _prev_nal_idc;
|
||||
int _prev_nal_type;
|
||||
// array of NSData comprising a single frame. each data is one nalu with no start code
|
||||
NSMutableArray *_pendingNALU;
|
||||
|
||||
// FIFO for frame times
|
||||
NSMutableArray *_times;
|
||||
|
||||
encoder_handler_t _outputBlock;
|
||||
param_handler_t _paramsBlock;
|
||||
|
||||
// estimate bitrate over first second
|
||||
int _bitspersecond;
|
||||
CMTimeValue _firstpts;
|
||||
}
|
||||
|
||||
@property (atomic) BOOL bitrateChanged;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFAVEncoder
|
||||
|
||||
@synthesize bitspersecond = _bitspersecond;
|
||||
|
||||
+ (LFAVEncoder *)encoderForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate {
|
||||
LFAVEncoder *enc = [LFAVEncoder alloc];
|
||||
[enc initForHeight:height andWidth:width bitrate:bitrate];
|
||||
return enc;
|
||||
}
|
||||
|
||||
- (NSString *)makeFilename {
|
||||
NSString *filename = [NSString stringWithFormat:@"capture%d.mp4", _currentFile];
|
||||
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
|
||||
return path;
|
||||
}
|
||||
|
||||
- (void)initForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate {
|
||||
_height = height;
|
||||
_width = width;
|
||||
_bitrate = bitrate;
|
||||
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"params.mp4"];
|
||||
_headerWriter = [LFVideoEncoder encoderForPath:path Height:height andWidth:width bitrate:(int)self.bitrate];
|
||||
_times = [NSMutableArray arrayWithCapacity:10];
|
||||
|
||||
// swap between 3 filenames
|
||||
_currentFile = 1;
|
||||
_writer = [LFVideoEncoder encoderForPath:[self makeFilename] Height:height andWidth:width bitrate:(int)self.bitrate];
|
||||
|
||||
[self addObserver:self forKeyPath:NSStringFromSelector(@selector(bitrate)) options:0 context:AVEncoderContext];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context {
|
||||
if (context == AVEncoderContext && [keyPath isEqualToString:NSStringFromSelector(@selector(bitrate))]) {
|
||||
self.bitrateChanged = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)encodeWithBlock:(encoder_handler_t)block onParams:(param_handler_t)paramsHandler {
|
||||
_outputBlock = block;
|
||||
_paramsBlock = paramsHandler;
|
||||
_needParams = YES;
|
||||
_pendingNALU = nil;
|
||||
_firstpts = -1;
|
||||
_bitspersecond = 0;
|
||||
}
|
||||
|
||||
- (BOOL)parseParams:(NSString *)path {
|
||||
NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
|
||||
struct stat s;
|
||||
fstat([file fileDescriptor], &s);
|
||||
LFMP4Atom *movie = [LFMP4Atom atomAt:0 size:(int)s.st_size type:(OSType)('file') inFile:file];
|
||||
LFMP4Atom *moov = [movie childOfType:(OSType)('moov') startAt:0];
|
||||
LFMP4Atom *trak = nil;
|
||||
if (moov != nil) {
|
||||
for (;; ) {
|
||||
trak = [moov nextChild];
|
||||
if (trak == nil) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (trak.type == (OSType)('trak')) {
|
||||
LFMP4Atom *tkhd = [trak childOfType:(OSType)('tkhd') startAt:0];
|
||||
NSData *verflags = [tkhd readAt:0 size:4];
|
||||
unsigned char *p = (unsigned char *)[verflags bytes];
|
||||
if (p[3] & 1) {
|
||||
break;
|
||||
} else {
|
||||
tkhd = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LFMP4Atom *stsd = nil;
|
||||
if (trak != nil) {
|
||||
LFMP4Atom *media = [trak childOfType:(OSType)('mdia') startAt:0];
|
||||
if (media != nil) {
|
||||
LFMP4Atom *minf = [media childOfType:(OSType)('minf') startAt:0];
|
||||
if (minf != nil) {
|
||||
LFMP4Atom *stbl = [minf childOfType:(OSType)('stbl') startAt:0];
|
||||
if (stbl != nil) {
|
||||
stsd = [stbl childOfType:(OSType)('stsd') startAt:0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stsd != nil) {
|
||||
LFMP4Atom *avc1 = [stsd childOfType:(OSType)('avc1') startAt:8];
|
||||
if (avc1 != nil) {
|
||||
LFMP4Atom *esd = [avc1 childOfType:(OSType)('avcC') startAt:78];
|
||||
if (esd != nil) {
|
||||
// this is the avcC record that we are looking for
|
||||
_avcC = [esd readAt:0 size:(int)esd.length];
|
||||
if (_avcC != nil) {
|
||||
// extract size of length field
|
||||
unsigned char *p = (unsigned char *)[_avcC bytes];
|
||||
_lengthSize = (p[4] & 3) + 1;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)onParamsCompletion {
|
||||
// the initial one-frame-only file has been completed
|
||||
// Extract the avcC structure and then start monitoring the
|
||||
// main file to extract video from the mdat chunk.
|
||||
if ([self parseParams:_headerWriter.path]) {
|
||||
if (_paramsBlock) {
|
||||
_paramsBlock(_avcC);
|
||||
}
|
||||
_headerWriter = nil;
|
||||
_swapping = NO;
|
||||
_inputFile = [NSFileHandle fileHandleForReadingAtPath:_writer.path];
|
||||
_readQueue = dispatch_queue_create("uk.co.gdcl.avencoder.read", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
_readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [_inputFile fileDescriptor], 0, _readQueue);
|
||||
dispatch_source_set_event_handler(_readSource, ^{
|
||||
[self onFileUpdate];
|
||||
});
|
||||
dispatch_resume(_readSource);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)encodeFrame:(CMSampleBufferRef)sampleBuffer {
|
||||
CMTime prestime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
|
||||
NSNumber *pts = [NSNumber numberWithLongLong:prestime.value];
|
||||
|
||||
@synchronized(self){
|
||||
if (_needParams) {
|
||||
// the avcC record is needed for decoding and it's not written to the file until
|
||||
// completion. We get round that by writing the first frame to two files; the first
|
||||
// file (containing only one frame) is then finished, so we can extract the avcC record.
|
||||
// Only when we've got that do we start reading from the main file.
|
||||
_needParams = NO;
|
||||
if ([_headerWriter encodeFrame:sampleBuffer]) {
|
||||
[_headerWriter finishWithCompletionHandler:^{
|
||||
[self onParamsCompletion];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@synchronized(_times){
|
||||
[_times addObject:pts];
|
||||
}
|
||||
@synchronized(self){
|
||||
// switch output files when we reach a size limit
|
||||
// to avoid runaway storage use.
|
||||
if (!_swapping) {
|
||||
struct stat st;
|
||||
fstat([_inputFile fileDescriptor], &st);
|
||||
if (st.st_size > OUTPUT_FILE_SWITCH_POINT || self.bitrateChanged) {
|
||||
self.bitrateChanged = NO;
|
||||
_swapping = YES;
|
||||
LFVideoEncoder *oldVideo = _writer;
|
||||
|
||||
// construct a new writer to the next filename
|
||||
if (++_currentFile > MAX_FILENAME_INDEX) {
|
||||
_currentFile = 1;
|
||||
}
|
||||
//NSLog(@"Swap to file %d", _currentFile);
|
||||
_writer = [LFVideoEncoder encoderForPath:[self makeFilename] Height:_height andWidth:_width bitrate:(int)self.bitrate];
|
||||
|
||||
// to do this seamlessly requires a few steps in the right order
|
||||
// first, suspend the read source
|
||||
if (_readSource) {
|
||||
dispatch_source_cancel(_readSource);
|
||||
// execute the next step as a block on the same queue, to be sure the suspend is done
|
||||
dispatch_async(_readQueue, ^{
|
||||
// finish the file, writing moov, before reading any more from the file
|
||||
// since we don't yet know where the mdat ends
|
||||
_readSource = nil;
|
||||
[oldVideo finishWithCompletionHandler:^{
|
||||
[self swapFiles:oldVideo.path];
|
||||
}];
|
||||
});
|
||||
} else {
|
||||
[self swapFiles:oldVideo.path];
|
||||
}
|
||||
}
|
||||
}
|
||||
[_writer encodeFrame:sampleBuffer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)swapFiles:(NSString *)oldPath {
|
||||
// save current position
|
||||
uint64_t pos = [_inputFile offsetInFile];
|
||||
|
||||
// re-read mdat length
|
||||
[_inputFile seekToFileOffset:_posMDAT];
|
||||
NSData *hdr = [_inputFile readDataOfLength:4];
|
||||
unsigned char *p = (unsigned char *)[hdr bytes];
|
||||
if (p) {
|
||||
int lenMDAT = to_host(p);
|
||||
|
||||
// extract nalus from saved position to mdat end
|
||||
uint64_t posEnd = _posMDAT + lenMDAT;
|
||||
uint32_t cRead = (uint32_t)(posEnd - pos);
|
||||
[_inputFile seekToFileOffset:pos];
|
||||
[self readAndDeliver:cRead];
|
||||
}
|
||||
|
||||
// close and remove file
|
||||
[_inputFile closeFile];
|
||||
_foundMDAT = false;
|
||||
_bytesToNextAtom = 0;
|
||||
[[NSFileManager defaultManager] removeItemAtPath:oldPath error:nil];
|
||||
|
||||
|
||||
// open new file and set up dispatch source
|
||||
_inputFile = [NSFileHandle fileHandleForReadingAtPath:_writer.path];
|
||||
_readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [_inputFile fileDescriptor], 0, _readQueue);
|
||||
dispatch_source_set_event_handler(_readSource, ^{
|
||||
[self onFileUpdate];
|
||||
});
|
||||
dispatch_resume(_readSource);
|
||||
_swapping = NO;
|
||||
}
|
||||
|
||||
- (void)readAndDeliver:(uint32_t)cReady {
|
||||
// Identify the individual NALUs and extract them
|
||||
while (cReady > _lengthSize) {
|
||||
NSData *lenField = [_inputFile readDataOfLength:_lengthSize];
|
||||
cReady -= _lengthSize;
|
||||
unsigned char *p = (unsigned char *)[lenField bytes];
|
||||
unsigned int lenNALU = to_host(p);
|
||||
|
||||
if (lenNALU > cReady) {
|
||||
// whole NALU not present -- seek back to start of NALU and wait for more
|
||||
[_inputFile seekToFileOffset:[_inputFile offsetInFile] - 4];
|
||||
break;
|
||||
}
|
||||
NSData *nalu = [_inputFile readDataOfLength:lenNALU];
|
||||
cReady -= lenNALU;
|
||||
|
||||
[self onNALU:nalu];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onFileUpdate {
|
||||
// called whenever there is more data to read in the main encoder output file.
|
||||
struct stat s;
|
||||
fstat([_inputFile fileDescriptor], &s);
|
||||
int cReady = (int)(s.st_size - [_inputFile offsetInFile]);
|
||||
|
||||
// locate the mdat atom if needed
|
||||
while (!_foundMDAT && (cReady > 8)) {
|
||||
if (_bytesToNextAtom == 0) {
|
||||
NSData *hdr = [_inputFile readDataOfLength:8];
|
||||
cReady -= 8;
|
||||
unsigned char *p = (unsigned char *)[hdr bytes];
|
||||
int lenAtom = to_host(p);
|
||||
unsigned int nameAtom = to_host(p+4);
|
||||
if (nameAtom == (unsigned int)('mdat')) {
|
||||
_foundMDAT = true;
|
||||
_posMDAT = [_inputFile offsetInFile] - 8;
|
||||
} else {
|
||||
_bytesToNextAtom = lenAtom - 8;
|
||||
}
|
||||
}
|
||||
if (_bytesToNextAtom > 0) {
|
||||
int cThis = cReady < _bytesToNextAtom ? cReady : _bytesToNextAtom;
|
||||
_bytesToNextAtom -= cThis;
|
||||
[_inputFile seekToFileOffset:[_inputFile offsetInFile]+cThis];
|
||||
cReady -= cThis;
|
||||
}
|
||||
}
|
||||
if (!_foundMDAT) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the mdat must be just encoded video.
|
||||
[self readAndDeliver:cReady];
|
||||
}
|
||||
|
||||
- (void)onEncodedFrame {
|
||||
CMTimeValue pts = 0;
|
||||
@synchronized(_times){
|
||||
if ([_times count] > 0) {
|
||||
NSNumber *time = _times[0];
|
||||
pts = [time longLongValue];
|
||||
[_times removeObjectAtIndex:0];
|
||||
if (_firstpts < 0) {
|
||||
_firstpts = pts;
|
||||
}
|
||||
if ((pts - _firstpts) < 1) {
|
||||
int bytes = 0;
|
||||
for (NSData *data in _pendingNALU) {
|
||||
bytes += [data length];
|
||||
}
|
||||
_bitspersecond += (bytes * 8);
|
||||
}
|
||||
} else {
|
||||
//NSLog(@"no pts for buffer");
|
||||
}
|
||||
}
|
||||
if (_outputBlock != nil) {
|
||||
_outputBlock(_pendingNALU, pts);
|
||||
}
|
||||
}
|
||||
|
||||
// combine multiple NALUs into a single frame, and in the process, convert to BSF
|
||||
// by adding 00 00 01 startcodes before each NALU.
|
||||
- (void)onNALU:(NSData *)nalu {
|
||||
unsigned char *pNal = (unsigned char *)[nalu bytes];
|
||||
int idc = pNal[0] & 0x60;
|
||||
int naltype = pNal[0] & 0x1f;
|
||||
|
||||
if (_pendingNALU) {
|
||||
LFNALUnit nal(pNal, (int)[nalu length]);
|
||||
|
||||
// we have existing data —is this the same frame?
|
||||
// typically there are a couple of NALUs per frame in iOS encoding.
|
||||
// This is not general-purpose: it assumes that arbitrary slice ordering is not allowed.
|
||||
BOOL bNew = NO;
|
||||
if ((idc != _prev_nal_idc) && ((idc * _prev_nal_idc) == 0)) {
|
||||
bNew = YES;
|
||||
} else if ((naltype != _prev_nal_type) && ((naltype == 5) || (_prev_nal_type == 5))) {
|
||||
bNew = YES;
|
||||
} else if ((naltype >= 1) && (naltype <= 5)) {
|
||||
nal.Skip(8);
|
||||
int first_mb = (int)nal.GetUE();
|
||||
if (first_mb == 0) {
|
||||
bNew = YES;
|
||||
}
|
||||
}
|
||||
if (bNew) {
|
||||
[self onEncodedFrame];
|
||||
_pendingNALU = nil;
|
||||
}
|
||||
}
|
||||
_prev_nal_type = naltype;
|
||||
_prev_nal_idc = idc;
|
||||
if (_pendingNALU == nil) {
|
||||
_pendingNALU = [NSMutableArray arrayWithCapacity:2];
|
||||
}
|
||||
[_pendingNALU addObject:nalu];
|
||||
}
|
||||
|
||||
- (NSData *)getConfigData {
|
||||
return [_avcC copy];
|
||||
}
|
||||
|
||||
- (void)shutdown {
|
||||
@synchronized(self){
|
||||
_readSource = nil;
|
||||
if (_headerWriter) {
|
||||
[_headerWriter finishWithCompletionHandler:^{
|
||||
_headerWriter = nil;
|
||||
}];
|
||||
}
|
||||
if (_writer) {
|
||||
[_writer finishWithCompletionHandler:^{
|
||||
_writer = nil;
|
||||
}];
|
||||
}
|
||||
// !! wait for these to finish before returning and delete temp files
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// LFMP4Atom.h
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 15/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface LFMP4Atom : NSObject
|
||||
|
||||
{
|
||||
NSFileHandle *_file;
|
||||
int64_t _offset;
|
||||
int64_t _length;
|
||||
OSType _type;
|
||||
int64_t _nextChild;
|
||||
}
|
||||
@property OSType type;
|
||||
@property int64_t length;
|
||||
|
||||
+ (LFMP4Atom *)atomAt:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle;
|
||||
- (BOOL)init:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle;
|
||||
- (NSData *)readAt:(int64_t)offset size:(int)length;
|
||||
- (BOOL)setChildOffset:(int64_t)offset;
|
||||
- (LFMP4Atom *)nextChild;
|
||||
- (LFMP4Atom *)childOfType:(OSType)fourcc startAt:(int64_t)offset;
|
||||
|
||||
@end
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// LFMP4Atom.m
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 15/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import "LFMP4Atom.h"
|
||||
|
||||
static unsigned int to_host(unsigned char *p){
|
||||
return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
|
||||
}
|
||||
|
||||
@implementation LFMP4Atom
|
||||
|
||||
@synthesize type = _type;
|
||||
@synthesize length = _length;
|
||||
|
||||
+ (LFMP4Atom *)atomAt:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle {
|
||||
LFMP4Atom *atom = [LFMP4Atom alloc];
|
||||
if (![atom init:offset size:length type:fourcc inFile:handle]) {
|
||||
return nil;
|
||||
}
|
||||
return atom;
|
||||
}
|
||||
|
||||
- (BOOL)init:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle {
|
||||
_file = handle;
|
||||
_offset = offset;
|
||||
_length = length;
|
||||
_type = fourcc;
|
||||
_nextChild = 0;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData *)readAt:(int64_t)offset size:(int)length {
|
||||
[_file seekToFileOffset:_offset + offset];
|
||||
return [_file readDataOfLength:length];
|
||||
}
|
||||
|
||||
- (BOOL)setChildOffset:(int64_t)offset {
|
||||
_nextChild = offset;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (LFMP4Atom *)nextChild {
|
||||
if (_nextChild <= (_length - 8)) {
|
||||
[_file seekToFileOffset:_offset + _nextChild];
|
||||
NSData *data = [_file readDataOfLength:8];
|
||||
int cHeader = 8;
|
||||
unsigned char *p = (unsigned char *)[data bytes];
|
||||
int64_t len = to_host(p);
|
||||
OSType fourcc = to_host(p + 4);
|
||||
if (len == 1) {
|
||||
// 64-bit extended length
|
||||
cHeader += 8;
|
||||
data = [_file readDataOfLength:8];
|
||||
p = (unsigned char *)[data bytes];
|
||||
len = to_host(p);
|
||||
len = (len << 32) + to_host(p + 4);
|
||||
} else if (len == 0) {
|
||||
// whole remaining parent space
|
||||
len = _length - _nextChild;
|
||||
}
|
||||
if (fourcc == (OSType)('uuid')) {
|
||||
cHeader += 16;
|
||||
}
|
||||
if ((len < 0) || ((len + _nextChild) > _length)) {
|
||||
return nil;
|
||||
}
|
||||
int64_t offset = _nextChild + cHeader;
|
||||
_nextChild += len;
|
||||
len -= cHeader;
|
||||
return [LFMP4Atom atomAt:offset+_offset size:(int)len type:fourcc inFile:_file];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (LFMP4Atom *)childOfType:(OSType)fourcc startAt:(int64_t)offset {
|
||||
[self setChildOffset:offset];
|
||||
LFMP4Atom *child = nil;
|
||||
do {
|
||||
child = [self nextChild];
|
||||
} while ((child != nil) && (child.type != fourcc));
|
||||
return child;
|
||||
}
|
||||
|
||||
@end
|
||||
Executable
+424
@@ -0,0 +1,424 @@
|
||||
|
||||
//
|
||||
// NALUnit.cpp
|
||||
//
|
||||
// Implementation of Basic parsing of H.264 NAL Units
|
||||
//
|
||||
// Geraint Davies, March 2004
|
||||
//
|
||||
// Copyright (c) GDCL 2004-2008 http://www.gdcl.co.uk/license.htm
|
||||
|
||||
|
||||
|
||||
#include "LFNALUnit.h"
|
||||
|
||||
|
||||
// --- core NAL Unit implementation ------------------------------
|
||||
|
||||
LFNALUnit::LFNALUnit()
|
||||
: m_pStart(NULL),
|
||||
m_cBytes(0){
|
||||
}
|
||||
|
||||
bool
|
||||
LFNALUnit::GetStartCode(const BYTE *& pBegin, const BYTE *& pStart, int& cRemain){
|
||||
// start code is any number of 00 followed by 00 00 01
|
||||
// We need to record the first 00 in pBegin and the first byte
|
||||
// following the startcode in pStart.
|
||||
// if no start code is found, pStart and cRemain should be unchanged.
|
||||
|
||||
const BYTE *pThis = pStart;
|
||||
int cBytes = cRemain;
|
||||
|
||||
pBegin = NULL;
|
||||
while (cBytes >= 4) {
|
||||
if (pThis[0] == 0) {
|
||||
// remember first 00
|
||||
if (pBegin == NULL) {
|
||||
pBegin = pThis;
|
||||
}
|
||||
if ((pThis[1] == 0) &&
|
||||
(pThis[2] == 1)) {
|
||||
// point to type byte of NAL unit
|
||||
pStart = pThis + 3;
|
||||
cRemain = cBytes - 3;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
pBegin = NULL;
|
||||
}
|
||||
cBytes--;
|
||||
pThis++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
LFNALUnit::Parse(const BYTE *pBuffer, int cSpace, int LengthSize, bool bEnd){
|
||||
// if we get the start code but not the whole
|
||||
// NALU, we can return false but still have the length property valid
|
||||
m_cBytes = 0;
|
||||
|
||||
ResetBitstream();
|
||||
|
||||
if (LengthSize > 0) {
|
||||
m_pStartCodeStart = pBuffer;
|
||||
|
||||
if (LengthSize > cSpace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_cBytes = 0;
|
||||
for (int i = 0; i < LengthSize; i++) {
|
||||
m_cBytes <<= 8;
|
||||
m_cBytes += *pBuffer++;
|
||||
}
|
||||
|
||||
if ((m_cBytes+LengthSize) <= cSpace) {
|
||||
m_pStart = pBuffer;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// this is not length-delimited: we must look for start codes
|
||||
const BYTE *pBegin;
|
||||
if (GetStartCode(pBegin, pBuffer, cSpace)) {
|
||||
m_pStart = pBuffer;
|
||||
m_pStartCodeStart = pBegin;
|
||||
|
||||
// either we find another startcode, or we continue to the
|
||||
// buffer end (if this is the last block of data)
|
||||
if (GetStartCode(pBegin, pBuffer, cSpace)) {
|
||||
m_cBytes = int(pBegin - m_pStart);
|
||||
return true;
|
||||
} else if (bEnd) {
|
||||
// current element extends to end of buffer
|
||||
m_cBytes = cSpace;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// bitwise access to data
|
||||
void
|
||||
LFNALUnit::ResetBitstream(){
|
||||
m_idx = 0;
|
||||
m_nBits = 0;
|
||||
m_cZeros = 0;
|
||||
}
|
||||
|
||||
void
|
||||
LFNALUnit::Skip(int nBits){
|
||||
if (nBits < m_nBits) {
|
||||
m_nBits -= nBits;
|
||||
} else {
|
||||
nBits -= m_nBits;
|
||||
while (nBits >= 8) {
|
||||
GetBYTE();
|
||||
nBits -= 8;
|
||||
}
|
||||
if (nBits) {
|
||||
m_byte = GetBYTE();
|
||||
m_nBits = 8;
|
||||
|
||||
m_nBits -= nBits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the next byte, removing emulation prevention bytes
|
||||
BYTE
|
||||
LFNALUnit::GetBYTE(){
|
||||
if (m_idx >= m_cBytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTE b = m_pStart[m_idx++];
|
||||
|
||||
// to avoid start-code emulation, a byte 0x03 is inserted
|
||||
// after any 00 00 pair. Discard that here.
|
||||
if (b == 0) {
|
||||
m_cZeros++;
|
||||
if ((m_idx < m_cBytes) && (m_cZeros == 2) && (m_pStart[m_idx] == 0x03)) {
|
||||
m_idx++;
|
||||
m_cZeros = 0;
|
||||
}
|
||||
} else {
|
||||
m_cZeros = 0;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
LFNALUnit::GetBit(){
|
||||
if (m_nBits == 0) {
|
||||
m_byte = GetBYTE();
|
||||
m_nBits = 8;
|
||||
}
|
||||
m_nBits--;
|
||||
return (m_byte >> m_nBits) & 0x1;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
LFNALUnit::GetWord(int nBits){
|
||||
unsigned long u = 0;
|
||||
while (nBits > 0) {
|
||||
u <<= 1;
|
||||
u |= GetBit();
|
||||
nBits--;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
LFNALUnit::GetUE(){
|
||||
// Exp-Golomb entropy coding: leading zeros, then a one, then
|
||||
// the data bits. The number of leading zeros is the number of
|
||||
// data bits, counting up from that number of 1s as the base.
|
||||
// That is, if you see
|
||||
// 0001010
|
||||
// You have three leading zeros, so there are three data bits (010)
|
||||
// counting up from a base of 111: thus 111 + 010 = 1001 = 9
|
||||
int cZeros = 0;
|
||||
while (GetBit() == 0) {
|
||||
cZeros++;
|
||||
}
|
||||
return GetWord(cZeros) + ((1 << cZeros)-1);
|
||||
}
|
||||
|
||||
long
|
||||
LFNALUnit::GetSE(){
|
||||
// same as UE but signed.
|
||||
// basically the unsigned numbers are used as codes to indicate signed numbers in pairs
|
||||
// in increasing value. Thus the encoded values
|
||||
// 0, 1, 2, 3, 4
|
||||
// mean
|
||||
// 0, 1, -1, 2, -2 etc
|
||||
|
||||
unsigned long UE = GetUE();
|
||||
bool bPositive = UE & 1;
|
||||
long SE = (UE + 1) >> 1;
|
||||
if (!bPositive) {
|
||||
SE = -SE;
|
||||
}
|
||||
return SE;
|
||||
}
|
||||
|
||||
// --- sequence params parsing ---------------
|
||||
LFSeqParamSet::LFSeqParamSet()
|
||||
: m_cx(0),
|
||||
m_cy(0),
|
||||
m_FrameBits(0){
|
||||
// SetRect(&m_rcFrame, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
ScalingList(int size, LFNALUnit *pnalu){
|
||||
long lastScale = 8;
|
||||
long nextScale = 8;
|
||||
for (int j = 0; j < size; j++) {
|
||||
if (nextScale != 0) {
|
||||
long delta = pnalu->GetSE();
|
||||
nextScale = (lastScale + delta + 256) %256;
|
||||
}
|
||||
int scaling_list_j = (nextScale == 0) ? (int)lastScale : (int)nextScale;
|
||||
lastScale = scaling_list_j;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LFSeqParamSet::Parse(LFNALUnit *pnalu){
|
||||
if (pnalu->Type() != LFNALUnit::NAL_Sequence_Params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// with the UE/SE type encoding, we must decode all the values
|
||||
// to get through to the ones we want
|
||||
pnalu->ResetBitstream();
|
||||
pnalu->Skip(8); // type
|
||||
m_Profile =(int) pnalu->GetWord(8);
|
||||
m_Compatibility = (BYTE)pnalu->GetWord(8);
|
||||
m_Level = (int)pnalu->GetWord(8);
|
||||
|
||||
/*int seq_param_id =*/ pnalu->GetUE();
|
||||
|
||||
if ((m_Profile == 100) || (m_Profile == 110) || (m_Profile == 122) || (m_Profile == 144)) {
|
||||
int chroma_fmt = (int)pnalu->GetUE();
|
||||
if (chroma_fmt == 3) {
|
||||
pnalu->Skip(1);
|
||||
}
|
||||
/* int bit_depth_luma_minus8 = */ pnalu->GetUE();
|
||||
/* int bit_depth_chroma_minus8 = */ pnalu->GetUE();
|
||||
pnalu->Skip(1);
|
||||
int seq_scaling_matrix_present = (int)pnalu->GetBit();
|
||||
if (seq_scaling_matrix_present) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (pnalu->GetBit()) {
|
||||
if (i < 6) {
|
||||
ScalingList(16, pnalu);
|
||||
} else {
|
||||
ScalingList(64, pnalu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int log2_frame_minus4 = (int)pnalu->GetUE();
|
||||
m_FrameBits = log2_frame_minus4 + 4;
|
||||
int POCtype = (int)pnalu->GetUE();
|
||||
if (POCtype == 0) {
|
||||
/*int log2_poc_minus4 =*/ pnalu->GetUE();
|
||||
} else if (POCtype == 1) {
|
||||
pnalu->Skip(1); // delta always zero
|
||||
/*int nsp_offset =*/ pnalu->GetSE();
|
||||
/*int nsp_top_to_bottom = */ pnalu->GetSE();
|
||||
int num_ref_in_cycle = (int)pnalu->GetUE();
|
||||
for (int i = 0; i < num_ref_in_cycle; i++) {
|
||||
/*int sf_offset =*/ pnalu->GetSE();
|
||||
}
|
||||
} else if (POCtype != 2) {
|
||||
return false;
|
||||
}
|
||||
// else for POCtype == 2, no additional data in stream
|
||||
|
||||
/*int num_ref_frames =*/ pnalu->GetUE();
|
||||
/*int gaps_allowed =*/ pnalu->GetBit();
|
||||
|
||||
int mbs_width = (int)pnalu->GetUE();
|
||||
int mbs_height = (int)pnalu->GetUE();
|
||||
m_cx = (mbs_width+1) * 16;
|
||||
m_cy = (mbs_height+1) * 16;
|
||||
|
||||
// smoke test validation of sps
|
||||
if ((m_cx > 2000) || (m_cy > 2000)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if this is false, then sizes are field sizes and need adjusting
|
||||
m_bFrameOnly = pnalu->GetBit() ? true : false;
|
||||
|
||||
if (!m_bFrameOnly) {
|
||||
pnalu->Skip(1); // adaptive frame/field
|
||||
}
|
||||
pnalu->Skip(1); // direct 8x8
|
||||
|
||||
#if 0
|
||||
SetRect(&m_rcFrame, 0, 0, 0, 0);
|
||||
bool bCrop = pnalu->GetBit() ? true : false;
|
||||
if (bCrop) {
|
||||
// get cropping rect
|
||||
// store as exclusive, pixel parameters relative to frame
|
||||
m_rcFrame.left = pnalu->GetUE() * 2;
|
||||
m_rcFrame.right = pnalu->GetUE() * 2;
|
||||
m_rcFrame.top = pnalu->GetUE() * 2;
|
||||
m_rcFrame.bottom = pnalu->GetUE() * 2;
|
||||
}
|
||||
|
||||
if (!IsRectEmpty(&m_rcFrame)) {
|
||||
m_rcFrame.right = m_cx - m_rcFrame.right;
|
||||
m_rcFrame.bottom = m_cy - m_rcFrame.bottom;
|
||||
}
|
||||
#endif
|
||||
// adjust rect from 2x2 units to pixels
|
||||
|
||||
if (!m_bFrameOnly) {
|
||||
// adjust heights from field to frame
|
||||
m_cy *= 2;
|
||||
#if 0
|
||||
m_rcFrame.top *= 2;
|
||||
m_rcFrame.bottom *= 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
// .. rest are not interesting yet
|
||||
m_nalu = *pnalu;
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- slice header --------------------
|
||||
bool
|
||||
LFSliceHeader::Parse(LFNALUnit *pnalu){
|
||||
switch (pnalu->Type()) {
|
||||
case LFNALUnit::NAL_IDR_Slice:
|
||||
case LFNALUnit::NAL_Slice:
|
||||
case LFNALUnit::NAL_PartitionA:
|
||||
// all these begin with a slice header
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// slice header has the 1-byte type, then one UE value,
|
||||
// then the frame number.
|
||||
pnalu->ResetBitstream();
|
||||
pnalu->Skip(8); // NALU type
|
||||
pnalu->GetUE(); // first mb in slice
|
||||
pnalu->GetUE(); // slice type
|
||||
pnalu->GetUE(); // pic param set id
|
||||
|
||||
m_framenum = (int)pnalu->GetWord(m_nBitsFrame);
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- SEI ----------------------
|
||||
|
||||
|
||||
LFSEIMessage::LFSEIMessage(LFNALUnit *pnalu){
|
||||
m_pnalu = pnalu;
|
||||
const BYTE *p = pnalu->Start();
|
||||
p++; // nalu type byte
|
||||
m_type = 0;
|
||||
while (*p == 0xff) {
|
||||
m_type += 255;
|
||||
p++;
|
||||
}
|
||||
m_type += *p;
|
||||
p++;
|
||||
m_length = 0;
|
||||
while (*p == 0xff) {
|
||||
m_type += 255;
|
||||
p++;
|
||||
}
|
||||
m_length += *p;
|
||||
p++;
|
||||
m_idxPayload = int(p - m_pnalu->Start());
|
||||
}
|
||||
|
||||
LFavcCHeader::LFavcCHeader(const BYTE *header, int cBytes){
|
||||
if (cBytes < 8) {
|
||||
return;
|
||||
}
|
||||
const BYTE *pEnd = header + cBytes;
|
||||
|
||||
int cSeq = header[5] & 0x1f;
|
||||
header += 6;
|
||||
for (int i = 0; i < cSeq; i++) {
|
||||
if ((header+2) > pEnd) {
|
||||
return;
|
||||
}
|
||||
int cThis = (header[0] << 8) + header[1];
|
||||
header += 2;
|
||||
if ((header+cThis) > pEnd) {
|
||||
return;
|
||||
}
|
||||
if (i == 0) {
|
||||
LFNALUnit n(header, cThis);
|
||||
m_sps = n;
|
||||
}
|
||||
header += cThis;
|
||||
}
|
||||
if ((header + 3) >= pEnd) {
|
||||
return;
|
||||
}
|
||||
int cPPS = header[0];
|
||||
if (cPPS > 0) {
|
||||
int cThis = (header[1] << 8) + header[2];
|
||||
header += 3;
|
||||
LFNALUnit n(header, cThis);
|
||||
m_pps = n;
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
|
||||
//
|
||||
// NALUnit.h
|
||||
//
|
||||
// Basic parsing of H.264 NAL Units
|
||||
//
|
||||
// Geraint Davies, March 2004
|
||||
//
|
||||
// Copyright (c) GDCL 2004-2008 http://www.gdcl.co.uk/license.htm
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned long ULONG;
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
class LFNALUnit
|
||||
{
|
||||
public:
|
||||
LFNALUnit();
|
||||
LFNALUnit(const BYTE* pStart, int len){
|
||||
m_pStart = m_pStartCodeStart = pStart;
|
||||
m_cBytes = len;
|
||||
ResetBitstream();
|
||||
}
|
||||
virtual ~LFNALUnit() {
|
||||
}
|
||||
|
||||
// assignment copies a pointer into a fixed buffer managed elsewhere. We do not copy the data
|
||||
LFNALUnit(const LFNALUnit &r){
|
||||
m_pStart = r.m_pStart;
|
||||
m_cBytes = r.m_cBytes;
|
||||
ResetBitstream();
|
||||
}
|
||||
const LFNALUnit& operator = (const LFNALUnit &r)
|
||||
{
|
||||
m_pStart = r.m_pStart;
|
||||
m_cBytes = r.m_cBytes;
|
||||
ResetBitstream();
|
||||
return *this;
|
||||
}
|
||||
|
||||
enum eNALType {
|
||||
NAL_Slice = 1,
|
||||
NAL_PartitionA = 2,
|
||||
NAL_PartitionB = 3,
|
||||
NAL_PartitionC = 4,
|
||||
NAL_IDR_Slice = 5,
|
||||
NAL_SEI = 6,
|
||||
NAL_Sequence_Params = 7,
|
||||
NAL_Picture_Params = 8,
|
||||
NAL_AUD = 9,
|
||||
};
|
||||
|
||||
// identify a NAL unit within a buffer.
|
||||
// If LengthSize is non-zero, it is the number of bytes
|
||||
// of length field we expect. Otherwise, we expect start-code
|
||||
// delimiters.
|
||||
bool Parse(const BYTE *pBuffer, int cSpace, int LengthSize, bool bEnd);
|
||||
|
||||
eNALType Type(){
|
||||
if (m_pStart == NULL) {
|
||||
return eNALType(0);
|
||||
}
|
||||
return eNALType(m_pStart[0] & 0x1F);
|
||||
}
|
||||
|
||||
int Length(){
|
||||
return m_cBytes;
|
||||
}
|
||||
|
||||
const BYTE *Start(){
|
||||
return m_pStart;
|
||||
}
|
||||
|
||||
// bitwise access to data
|
||||
void ResetBitstream();
|
||||
void Skip(int nBits);
|
||||
|
||||
unsigned long GetWord(int nBits);
|
||||
unsigned long GetUE();
|
||||
long GetSE();
|
||||
BYTE GetBYTE();
|
||||
unsigned long GetBit();
|
||||
|
||||
const BYTE *StartCodeStart() {
|
||||
return m_pStartCodeStart;
|
||||
}
|
||||
|
||||
private:
|
||||
bool GetStartCode(const BYTE *& pBegin, const BYTE *& pStart, int& cRemain);
|
||||
|
||||
private:
|
||||
const BYTE *m_pStartCodeStart;
|
||||
const BYTE *m_pStart;
|
||||
int m_cBytes;
|
||||
|
||||
// bitstream access
|
||||
int m_idx;
|
||||
int m_nBits;
|
||||
BYTE m_byte;
|
||||
int m_cZeros;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// simple parser for the Sequence parameter set things that we need
|
||||
class LFSeqParamSet
|
||||
{
|
||||
public:
|
||||
LFSeqParamSet();
|
||||
bool Parse(LFNALUnit *pnalu);
|
||||
int FrameBits(){
|
||||
return m_FrameBits;
|
||||
}
|
||||
|
||||
long EncodedWidth(){
|
||||
return m_cx;
|
||||
}
|
||||
|
||||
long EncodedHeight(){
|
||||
return m_cy;
|
||||
}
|
||||
|
||||
#if 0
|
||||
long CroppedWidth(){
|
||||
if (IsRectEmpty(&m_rcFrame)) {
|
||||
return EncodedWidth();
|
||||
}
|
||||
return m_rcFrame.right - m_rcFrame.left;
|
||||
}
|
||||
|
||||
long CroppedHeight(){
|
||||
if (IsRectEmpty(&m_rcFrame)) {
|
||||
return EncodedHeight();
|
||||
}
|
||||
return m_rcFrame.bottom - m_rcFrame.top;
|
||||
}
|
||||
|
||||
RECT *CropRect(){
|
||||
return &m_rcFrame;
|
||||
}
|
||||
|
||||
#endif
|
||||
bool Interlaced(){
|
||||
return !m_bFrameOnly;
|
||||
}
|
||||
|
||||
unsigned int Profile() {
|
||||
return m_Profile;
|
||||
}
|
||||
|
||||
unsigned int Level() {
|
||||
return m_Level;
|
||||
}
|
||||
|
||||
BYTE Compat() {
|
||||
return m_Compatibility;
|
||||
}
|
||||
|
||||
LFNALUnit *NALU() {
|
||||
return &m_nalu;
|
||||
}
|
||||
|
||||
private:
|
||||
LFNALUnit m_nalu;
|
||||
int m_FrameBits;
|
||||
long m_cx;
|
||||
long m_cy;
|
||||
// RECT m_rcFrame;
|
||||
bool m_bFrameOnly;
|
||||
|
||||
int m_Profile;
|
||||
int m_Level;
|
||||
BYTE m_Compatibility;
|
||||
};
|
||||
|
||||
// extract frame num from slice headers
|
||||
class LFSliceHeader
|
||||
{
|
||||
public:
|
||||
LFSliceHeader(int nBitsFrame)
|
||||
: m_framenum(0),
|
||||
m_nBitsFrame(nBitsFrame){
|
||||
}
|
||||
|
||||
bool Parse(LFNALUnit *pnalu);
|
||||
int FrameNum(){
|
||||
return m_framenum;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_framenum;
|
||||
int m_nBitsFrame;
|
||||
};
|
||||
|
||||
// SEI message structure
|
||||
class LFSEIMessage
|
||||
{
|
||||
public:
|
||||
LFSEIMessage(LFNALUnit* pnalu);
|
||||
int Type() {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
int Length() {
|
||||
return m_length;
|
||||
}
|
||||
|
||||
const BYTE *Payload() {
|
||||
return m_pnalu->Start() + m_idxPayload;
|
||||
}
|
||||
|
||||
private:
|
||||
LFNALUnit *m_pnalu;
|
||||
int m_type;
|
||||
int m_length;
|
||||
int m_idxPayload;
|
||||
};
|
||||
|
||||
// avcC structure from MP4
|
||||
class LFavcCHeader
|
||||
{
|
||||
public:
|
||||
LFavcCHeader(const BYTE* header, int cBytes);
|
||||
LFNALUnit *sps() {
|
||||
return &m_sps;
|
||||
}
|
||||
|
||||
LFNALUnit *pps() {
|
||||
return &m_pps;
|
||||
}
|
||||
|
||||
private:
|
||||
LFNALUnit m_sps;
|
||||
LFNALUnit m_pps;
|
||||
};
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// VideoEncoder.h
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 14/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AVFoundation/AVAssetWriter.h"
|
||||
#import "AVFoundation/AVAssetWriterInput.h"
|
||||
#import "AVFoundation/AVMediaFormat.h"
|
||||
#import "AVFoundation/AVVideoSettings.h"
|
||||
|
||||
@interface LFVideoEncoder : NSObject
|
||||
|
||||
|
||||
@property NSString *path;
|
||||
@property (nonatomic, readonly) NSUInteger bitrate;
|
||||
|
||||
+ (LFVideoEncoder *)encoderForPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate;
|
||||
|
||||
- (void)initPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate;
|
||||
- (void)finishWithCompletionHandler:(void (^)(void))handler;
|
||||
- (BOOL)encodeFrame:(CMSampleBufferRef)sampleBuffer;
|
||||
|
||||
@end
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// LFVideoEncoder.m
|
||||
// Encoder Demo
|
||||
//
|
||||
// Created by Geraint Davies on 14/01/2013.
|
||||
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
|
||||
//
|
||||
|
||||
#import "LFVideoEncoder.h"
|
||||
|
||||
@implementation LFVideoEncoder
|
||||
{
|
||||
AVAssetWriter *_writer;
|
||||
AVAssetWriterInput *_writerInput;
|
||||
NSString *_path;
|
||||
}
|
||||
|
||||
@synthesize path = _path;
|
||||
|
||||
+ (LFVideoEncoder *)encoderForPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate {
|
||||
LFVideoEncoder *enc = [LFVideoEncoder alloc];
|
||||
[enc initPath:path Height:height andWidth:width bitrate:bitrate];
|
||||
return enc;
|
||||
}
|
||||
|
||||
- (void)initPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate {
|
||||
self.path = path;
|
||||
_bitrate = bitrate;
|
||||
|
||||
[[NSFileManager defaultManager] removeItemAtPath:self.path error:nil];
|
||||
NSURL *url = [NSURL fileURLWithPath:self.path];
|
||||
|
||||
NSDictionary *settings = @{
|
||||
AVVideoCodecKey: AVVideoCodecH264,
|
||||
AVVideoWidthKey: @(width),
|
||||
AVVideoHeightKey: @(height),
|
||||
AVVideoCompressionPropertiesKey: @{
|
||||
AVVideoAverageBitRateKey: @(self.bitrate),
|
||||
AVVideoMaxKeyFrameIntervalKey: @(30 * 2),
|
||||
AVVideoProfileLevelKey: AVVideoProfileLevelH264Baseline41,
|
||||
AVVideoAllowFrameReorderingKey: @NO,
|
||||
}
|
||||
};
|
||||
_writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:settings];
|
||||
_writerInput.expectsMediaDataInRealTime = YES;
|
||||
|
||||
_writer = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeQuickTimeMovie error:nil];
|
||||
[_writer addInput:_writerInput];
|
||||
}
|
||||
|
||||
- (void)finishWithCompletionHandler:(void (^)(void))handler {
|
||||
if (_writer.status == AVAssetWriterStatusWriting) {
|
||||
[_writer finishWritingWithCompletionHandler:handler];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)encodeFrame:(CMSampleBufferRef)sampleBuffer {
|
||||
if (CMSampleBufferDataIsReady(sampleBuffer)) {
|
||||
if (_writer.status == AVAssetWriterStatusUnknown) {
|
||||
CMTime startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
|
||||
[_writer startWriting];
|
||||
[_writer startSessionAtSourceTime:startTime];
|
||||
}
|
||||
if (_writer.status == AVAssetWriterStatusFailed) {
|
||||
//NSLog(@"AVAssetWriterStatusFailed");
|
||||
return NO;
|
||||
}
|
||||
if (_writerInput.readyForMoreMediaData == YES) {
|
||||
[_writerInput appendSampleBuffer:sampleBuffer];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -15,17 +15,17 @@
|
||||
/// 编码器编码后回调
|
||||
@protocol LFAudioEncodingDelegate <NSObject>
|
||||
@required
|
||||
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame*)frame;
|
||||
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame *)frame;
|
||||
@end
|
||||
|
||||
/// 编码器抽象的接口
|
||||
@protocol LFAudioEncoding <NSObject>
|
||||
@required
|
||||
- (void)encodeAudioData:(AudioBufferList)inBufferList timeStamp:(uint64_t)timeStamp;
|
||||
- (void)encodeAudioData:(nullable NSData*)audioData timeStamp:(uint64_t)timeStamp;
|
||||
- (void)stopEncoder;
|
||||
@optional
|
||||
- (nullable instancetype)initWithAudioStreamConfiguration:(nullable LFLiveAudioConfiguration*)configuration;
|
||||
- (nullable instancetype)initWithAudioStreamConfiguration:(nullable LFLiveAudioConfiguration *)configuration;
|
||||
- (void)setDelegate:(nullable id<LFAudioEncodingDelegate>)delegate;
|
||||
- (nullable NSData*)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength;
|
||||
- (nullable NSData *)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength;
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// LFH264VideoEncoder
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by feng on 7/5/16.
|
||||
// Copyright (c) 2014 zhanqi.tv. All rights reserved.
|
||||
//
|
||||
#import "LFVideoEncoding.h"
|
||||
|
||||
@interface LFH264VideoEncoder : NSObject <LFVideoEncoding> {
|
||||
|
||||
}
|
||||
|
||||
- (void)shutdown;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// LFH264VideoEncoder
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by feng on 7/5/16.
|
||||
// Copyright (c) 2014 zhanqi.tv. All rights reserved.
|
||||
//
|
||||
|
||||
#import <CoreMedia/CoreMedia.h>
|
||||
#import <mach/mach_time.h>
|
||||
#import "LFNALUnit.h"
|
||||
#import "LFAVEncoder.h"
|
||||
#import "LFH264VideoEncoder.h"
|
||||
#import "LFVideoFrame.h"
|
||||
|
||||
@interface LFH264VideoEncoder() {
|
||||
FILE *fp;
|
||||
NSInteger frameCount;
|
||||
BOOL enabledWriteVideoFile;
|
||||
}
|
||||
@property (nonatomic, strong) LFLiveVideoConfiguration *configuration;
|
||||
@property (nonatomic, weak) id<LFVideoEncodingDelegate> h264Delegate;
|
||||
@property (nonatomic) BOOL isBackGround;
|
||||
@property (nonatomic) NSInteger currentVideoBitRate;
|
||||
@property (nonatomic, strong) dispatch_queue_t sendQueue;
|
||||
|
||||
@property (nonatomic, strong) LFAVEncoder *encoder;
|
||||
|
||||
@property (nonatomic, strong) NSData *naluStartCode;
|
||||
@property (nonatomic, strong) NSMutableData *videoSPSandPPS;
|
||||
@property (nonatomic, strong) NSMutableData *spsData;
|
||||
@property (nonatomic, strong) NSMutableData *ppsData;
|
||||
@property (nonatomic, strong) NSMutableData *sei;
|
||||
@property (nonatomic) CMTimeScale timescale;
|
||||
@property (nonatomic, strong) NSMutableArray *orphanedFrames;
|
||||
@property (nonatomic, strong) NSMutableArray *orphanedSEIFrames;
|
||||
@property (nonatomic) CMTime lastPTS;
|
||||
@end
|
||||
|
||||
@implementation LFH264VideoEncoder
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithVideoStreamConfiguration:(LFLiveVideoConfiguration *)configuration {
|
||||
if (self = [super init]) {
|
||||
NSLog(@"USE LF264VideoEncoder");
|
||||
_configuration = configuration;
|
||||
[self initCompressionSession];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initCompressionSession{
|
||||
_sendQueue = dispatch_queue_create("com.youku.laifeng.h264.sendframe", DISPATCH_QUEUE_SERIAL);
|
||||
[self initializeNALUnitStartCode];
|
||||
_lastPTS = kCMTimeInvalid;
|
||||
_timescale = 1000;
|
||||
frameCount = 0;
|
||||
#ifdef DEBUG
|
||||
enabledWriteVideoFile = NO;
|
||||
[self initForFilePath];
|
||||
#endif
|
||||
|
||||
_encoder = [LFAVEncoder encoderForHeight:(int)_configuration.videoSize.height andWidth:(int)_configuration.videoSize.width bitrate:(int)_configuration.videoBitRate];
|
||||
[_encoder encodeWithBlock:^int(NSArray* dataArray, CMTimeValue ptsValue) {
|
||||
[self incomingVideoFrames:dataArray ptsValue:ptsValue];
|
||||
return 0;
|
||||
} onParams:^int(NSData *data) {
|
||||
[self generateSPSandPPS];
|
||||
return 0;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) initializeNALUnitStartCode {
|
||||
NSUInteger naluLength = 4;
|
||||
uint8_t *nalu = (uint8_t*)malloc(naluLength * sizeof(uint8_t));
|
||||
nalu[0] = 0x00;
|
||||
nalu[1] = 0x00;
|
||||
nalu[2] = 0x00;
|
||||
nalu[3] = 0x01;
|
||||
_naluStartCode = [NSData dataWithBytesNoCopy:nalu length:naluLength freeWhenDone:YES];
|
||||
}
|
||||
|
||||
- (void) generateSPSandPPS {
|
||||
NSData* config = _encoder.getConfigData;
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
LFavcCHeader avcC((const BYTE*)[config bytes], (int)[config length]);
|
||||
LFSeqParamSet seqParams;
|
||||
seqParams.Parse(avcC.sps());
|
||||
|
||||
NSData* spsData = [NSData dataWithBytes:avcC.sps()->Start() length:avcC.sps()->Length()];
|
||||
NSData *ppsData = [NSData dataWithBytes:avcC.pps()->Start() length:avcC.pps()->Length()];
|
||||
|
||||
_spsData = [NSMutableData dataWithCapacity:avcC.sps()->Length()+_naluStartCode.length];
|
||||
_ppsData = [NSMutableData dataWithCapacity:avcC.pps()->Length()+_naluStartCode.length];
|
||||
|
||||
[_spsData appendData:_naluStartCode];
|
||||
[_spsData appendData:spsData];
|
||||
[_ppsData appendData:_naluStartCode];
|
||||
[_ppsData appendData:ppsData];
|
||||
|
||||
_videoSPSandPPS = [NSMutableData dataWithCapacity:avcC.sps()->Length() + avcC.pps()->Length() + _naluStartCode.length * 2];
|
||||
[_videoSPSandPPS appendData:_naluStartCode];
|
||||
[_videoSPSandPPS appendData:spsData];
|
||||
[_videoSPSandPPS appendData:_naluStartCode];
|
||||
[_videoSPSandPPS appendData:ppsData];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)setVideoBitRate:(NSInteger)videoBitRate{
|
||||
_currentVideoBitRate = videoBitRate;
|
||||
_encoder.bitrate = _currentVideoBitRate;
|
||||
}
|
||||
|
||||
- (NSInteger)videoBitRate{
|
||||
return _currentVideoBitRate;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<LFVideoEncodingDelegate>)delegate{
|
||||
_h264Delegate = delegate;
|
||||
}
|
||||
|
||||
- (void)encodeVideoData:(CVPixelBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp {
|
||||
|
||||
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
|
||||
CMVideoFormatDescriptionRef videoInfo = NULL;
|
||||
CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &videoInfo);
|
||||
|
||||
CMTime frameTime = CMTimeMake(timeStamp, 1000);
|
||||
CMTime duration = CMTimeMake(1, (int32_t)_configuration.videoFrameRate);
|
||||
CMSampleTimingInfo timing = {duration, frameTime, kCMTimeInvalid};
|
||||
|
||||
CMSampleBufferRef sampleBuffer = NULL;
|
||||
CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, YES, NULL, NULL, videoInfo, &timing, &sampleBuffer);
|
||||
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
|
||||
[_encoder encodeFrame:sampleBuffer];
|
||||
CFRelease(videoInfo);
|
||||
CFRelease(sampleBuffer);
|
||||
|
||||
frameCount++;
|
||||
}
|
||||
|
||||
- (void)addOrphanedFramesFromArray:(NSArray*)frames {
|
||||
for (NSData *data in frames) {
|
||||
unsigned char* pNal = (unsigned char*)[data bytes];
|
||||
int idc = pNal[0] & 0x60;
|
||||
int naltype = pNal[0] & 0x1f;
|
||||
if (idc == 0 && naltype == 6) { // SEI
|
||||
[self.orphanedSEIFrames addObject:data];
|
||||
} else {
|
||||
[self.orphanedFrames addObject:data];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)writeVideoFrames:(NSArray*)frames pts:(CMTime)pts {
|
||||
NSMutableArray *totalFrames = [NSMutableArray array];
|
||||
if (self.orphanedSEIFrames.count > 0) {
|
||||
[totalFrames addObjectsFromArray:self.orphanedSEIFrames];
|
||||
[self.orphanedSEIFrames removeAllObjects];
|
||||
}
|
||||
[totalFrames addObjectsFromArray:frames];
|
||||
|
||||
NSMutableData *aggregateFrameData = [NSMutableData data];
|
||||
//BOOL hasKeyframe = NO;
|
||||
|
||||
for (NSData *data in totalFrames) {
|
||||
unsigned char* pNal = (unsigned char*)[data bytes];
|
||||
int idc = pNal[0] & 0x60;
|
||||
int naltype = pNal[0] & 0x1f;
|
||||
NSData *videoData = nil;
|
||||
|
||||
if (idc == 0 && naltype == 6) { // SEI
|
||||
_sei = [NSMutableData dataWithData:data];
|
||||
continue;
|
||||
} else if (naltype == 5) { // IDR
|
||||
//hasKeyframe = YES;
|
||||
NSMutableData *IDRData = [NSMutableData dataWithData:_videoSPSandPPS];
|
||||
if (_sei) {
|
||||
[IDRData appendData:_naluStartCode];
|
||||
[IDRData appendData:_sei];
|
||||
_sei = nil;
|
||||
}
|
||||
[IDRData appendData:_naluStartCode];
|
||||
[IDRData appendData:data];
|
||||
videoData = IDRData;
|
||||
} else {
|
||||
NSMutableData *regularData = [NSMutableData dataWithData:_naluStartCode];
|
||||
[regularData appendData:data];
|
||||
videoData = regularData;
|
||||
}
|
||||
[aggregateFrameData appendData:videoData];
|
||||
|
||||
LFVideoFrame *videoFrame = [LFVideoFrame new];
|
||||
const char *dataBuffer = (const char *)aggregateFrameData.bytes;
|
||||
videoFrame.data = [NSMutableData dataWithBytes:dataBuffer + _naluStartCode.length length:aggregateFrameData.length - _naluStartCode.length];
|
||||
videoFrame.timestamp = pts.value;
|
||||
videoFrame.isKeyFrame = (naltype == 5);
|
||||
videoFrame.sps = _spsData;
|
||||
videoFrame.pps = _ppsData;
|
||||
|
||||
if(self.h264Delegate && [self.h264Delegate respondsToSelector:@selector(videoEncoder:videoFrame:)]){
|
||||
[self.h264Delegate videoEncoder:self videoFrame:videoFrame];
|
||||
}
|
||||
}
|
||||
|
||||
if (self->enabledWriteVideoFile) {
|
||||
fwrite(aggregateFrameData.bytes, 1, aggregateFrameData.length, self->fp);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) incomingVideoFrames:(NSArray*)frames ptsValue:(CMTimeValue)ptsValue {
|
||||
if (ptsValue == 0) {
|
||||
[self addOrphanedFramesFromArray:frames];
|
||||
return;
|
||||
}
|
||||
if (!_videoSPSandPPS) {
|
||||
[self generateSPSandPPS];
|
||||
}
|
||||
CMTime pts = CMTimeMake(ptsValue, _timescale);
|
||||
if (self.orphanedFrames.count > 0) {
|
||||
CMTime ptsDiff = CMTimeSubtract(pts, _lastPTS);
|
||||
NSUInteger orphanedFramesCount = self.orphanedFrames.count;
|
||||
// NSLog(@"lastPTS before first orphaned frame: %lld", _lastPTS.value);
|
||||
for (NSData *frame in self.orphanedFrames) {
|
||||
CMTime fakePTSDiff = CMTimeMultiplyByFloat64(ptsDiff, 1.0/(orphanedFramesCount + 1));
|
||||
CMTime fakePTS = CMTimeAdd(_lastPTS, fakePTSDiff);
|
||||
// NSLog(@"orphan frame fakePTS: %lld", fakePTS.value);
|
||||
[self writeVideoFrames:@[frame] pts:fakePTS];
|
||||
}
|
||||
// NSLog(@"pts after orphaned frame: %lld", pts.value);
|
||||
[self.orphanedFrames removeAllObjects];
|
||||
}
|
||||
|
||||
[self writeVideoFrames:frames pts:pts];
|
||||
_lastPTS = pts;
|
||||
}
|
||||
|
||||
|
||||
- (void) dealloc {
|
||||
[_encoder shutdown];
|
||||
}
|
||||
|
||||
- (void)shutdown {
|
||||
[_encoder encodeWithBlock:nil onParams:nil];
|
||||
}
|
||||
|
||||
- (void)initForFilePath {
|
||||
NSString *path = [self GetFilePathByfileName:@"IOSCamDemo.h264"];
|
||||
NSLog(@"%@", path);
|
||||
self->fp = fopen([path cStringUsingEncoding:NSUTF8StringEncoding], "wb");
|
||||
}
|
||||
|
||||
- (NSString *)GetFilePathByfileName:(NSString*)filename {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:filename];
|
||||
return writablePath;
|
||||
}
|
||||
|
||||
@end
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
//
|
||||
// LFHardwareAudioEncoder.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFHardwareAudioEncoder.h"
|
||||
|
||||
@interface LFHardwareAudioEncoder (){
|
||||
AudioConverterRef m_converter;
|
||||
char *leftBuf;
|
||||
char *aacBuf;
|
||||
NSInteger leftLength;
|
||||
FILE *fp;
|
||||
BOOL enabledWriteVideoFile;
|
||||
}
|
||||
@property (nonatomic, strong) LFLiveAudioConfiguration *configuration;
|
||||
@property (nonatomic, weak) id<LFAudioEncodingDelegate> aacDeleage;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFHardwareAudioEncoder
|
||||
|
||||
- (instancetype)initWithAudioStreamConfiguration:(nullable LFLiveAudioConfiguration *)configuration {
|
||||
if (self = [super init]) {
|
||||
NSLog(@"USE LFHardwareAudioEncoder");
|
||||
_configuration = configuration;
|
||||
|
||||
if (!leftBuf) {
|
||||
leftBuf = malloc(_configuration.bufferLength);
|
||||
}
|
||||
|
||||
if (!aacBuf) {
|
||||
aacBuf = malloc(_configuration.bufferLength);
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
enabledWriteVideoFile = NO;
|
||||
[self initForFilePath];
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (aacBuf) free(aacBuf);
|
||||
if (leftBuf) free(leftBuf);
|
||||
}
|
||||
|
||||
#pragma mark -- LFAudioEncoder
|
||||
- (void)setDelegate:(id<LFAudioEncodingDelegate>)delegate {
|
||||
_aacDeleage = delegate;
|
||||
}
|
||||
|
||||
- (void)encodeAudioData:(nullable NSData*)audioData timeStamp:(uint64_t)timeStamp {
|
||||
if (![self createAudioConvert]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(leftLength + audioData.length >= self.configuration.bufferLength){
|
||||
///< 发送
|
||||
NSInteger totalSize = leftLength + audioData.length;
|
||||
NSInteger encodeCount = totalSize/self.configuration.bufferLength;
|
||||
char *totalBuf = malloc(totalSize);
|
||||
char *p = totalBuf;
|
||||
|
||||
memset(totalBuf, (int)totalSize, 0);
|
||||
memcpy(totalBuf, leftBuf, leftLength);
|
||||
memcpy(totalBuf + leftLength, audioData.bytes, audioData.length);
|
||||
|
||||
for(NSInteger index = 0;index < encodeCount;index++){
|
||||
[self encodeBuffer:p timeStamp:timeStamp];
|
||||
p += self.configuration.bufferLength;
|
||||
}
|
||||
free(totalBuf);
|
||||
|
||||
leftLength = totalSize%self.configuration.bufferLength;
|
||||
memset(leftBuf, 0, self.configuration.bufferLength);
|
||||
memcpy(leftBuf, totalBuf + (totalSize -leftLength), leftLength);
|
||||
|
||||
}else{
|
||||
///< 积累
|
||||
memcpy(leftBuf+leftLength, audioData.bytes, audioData.length);
|
||||
leftLength = leftLength + audioData.length;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)encodeBuffer:(char*)buf timeStamp:(uint64_t)timeStamp{
|
||||
|
||||
AudioBuffer inBuffer;
|
||||
inBuffer.mNumberChannels = 1;
|
||||
inBuffer.mData = buf;
|
||||
inBuffer.mDataByteSize = (UInt32)self.configuration.bufferLength;
|
||||
|
||||
AudioBufferList buffers;
|
||||
buffers.mNumberBuffers = 1;
|
||||
buffers.mBuffers[0] = inBuffer;
|
||||
|
||||
|
||||
// 初始化一个输出缓冲列表
|
||||
AudioBufferList outBufferList;
|
||||
outBufferList.mNumberBuffers = 1;
|
||||
outBufferList.mBuffers[0].mNumberChannels = inBuffer.mNumberChannels;
|
||||
outBufferList.mBuffers[0].mDataByteSize = inBuffer.mDataByteSize; // 设置缓冲区大小
|
||||
outBufferList.mBuffers[0].mData = aacBuf; // 设置AAC缓冲区
|
||||
UInt32 outputDataPacketSize = 1;
|
||||
if (AudioConverterFillComplexBuffer(m_converter, inputDataProc, &buffers, &outputDataPacketSize, &outBufferList, NULL) != noErr) {
|
||||
return;
|
||||
}
|
||||
|
||||
LFAudioFrame *audioFrame = [LFAudioFrame new];
|
||||
audioFrame.timestamp = timeStamp;
|
||||
audioFrame.data = [NSData dataWithBytes:aacBuf length:outBufferList.mBuffers[0].mDataByteSize];
|
||||
|
||||
char exeData[2];
|
||||
exeData[0] = _configuration.asc[0];
|
||||
exeData[1] = _configuration.asc[1];
|
||||
audioFrame.audioInfo = [NSData dataWithBytes:exeData length:2];
|
||||
if (self.aacDeleage && [self.aacDeleage respondsToSelector:@selector(audioEncoder:audioFrame:)]) {
|
||||
[self.aacDeleage audioEncoder:self audioFrame:audioFrame];
|
||||
}
|
||||
|
||||
if (self->enabledWriteVideoFile) {
|
||||
NSData *adts = [self adtsData:_configuration.numberOfChannels rawDataLength:audioFrame.data.length];
|
||||
fwrite(adts.bytes, 1, adts.length, self->fp);
|
||||
fwrite(audioFrame.data.bytes, 1, audioFrame.data.length, self->fp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)stopEncoder {
|
||||
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (BOOL)createAudioConvert { //根据输入样本初始化一个编码转换器
|
||||
if (m_converter != nil) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
AudioStreamBasicDescription inputFormat = {0};
|
||||
inputFormat.mSampleRate = _configuration.audioSampleRate;
|
||||
inputFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
inputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
inputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;
|
||||
inputFormat.mFramesPerPacket = 1;
|
||||
inputFormat.mBitsPerChannel = 16;
|
||||
inputFormat.mBytesPerFrame = inputFormat.mBitsPerChannel / 8 * inputFormat.mChannelsPerFrame;
|
||||
inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
|
||||
|
||||
AudioStreamBasicDescription outputFormat; // 这里开始是输出音频格式
|
||||
memset(&outputFormat, 0, sizeof(outputFormat));
|
||||
outputFormat.mSampleRate = inputFormat.mSampleRate; // 采样率保持一致
|
||||
outputFormat.mFormatID = kAudioFormatMPEG4AAC; // AAC编码 kAudioFormatMPEG4AAC kAudioFormatMPEG4AAC_HE_V2
|
||||
outputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;;
|
||||
outputFormat.mFramesPerPacket = 1024; // AAC一帧是1024个字节
|
||||
|
||||
const OSType subtype = kAudioFormatMPEG4AAC;
|
||||
AudioClassDescription requestedCodecs[2] = {
|
||||
{
|
||||
kAudioEncoderComponentType,
|
||||
subtype,
|
||||
kAppleSoftwareAudioCodecManufacturer
|
||||
},
|
||||
{
|
||||
kAudioEncoderComponentType,
|
||||
subtype,
|
||||
kAppleHardwareAudioCodecManufacturer
|
||||
}
|
||||
};
|
||||
|
||||
OSStatus result = AudioConverterNewSpecific(&inputFormat, &outputFormat, 2, requestedCodecs, &m_converter);;
|
||||
UInt32 outputBitrate = _configuration.audioBitrate;
|
||||
UInt32 propSize = sizeof(outputBitrate);
|
||||
// UInt32 outputPacketSize = 0;
|
||||
|
||||
|
||||
if(result == noErr) {
|
||||
result = AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, propSize, &outputBitrate);
|
||||
}
|
||||
|
||||
// if(result == noErr) {
|
||||
// AudioConverterGetProperty(m_converter, kAudioConverterPropertyMaximumOutputPacketSize, &propSize, &outputPacketSize);
|
||||
// }
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -- AudioCallBack
|
||||
OSStatus inputDataProc(AudioConverterRef inConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription * *outDataPacketDescription, void *inUserData) { //<span style="font-family: Arial, Helvetica, sans-serif;">AudioConverterFillComplexBuffer 编码过程中,会要求这个函数来填充输入数据,也就是原始PCM数据</span>
|
||||
AudioBufferList bufferList = *(AudioBufferList *)inUserData;
|
||||
ioData->mBuffers[0].mNumberChannels = 1;
|
||||
ioData->mBuffers[0].mData = bufferList.mBuffers[0].mData;
|
||||
ioData->mBuffers[0].mDataByteSize = bufferList.mBuffers[0].mDataByteSize;
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -- Custom Method
|
||||
/**
|
||||
* Add ADTS header at the beginning of each and every AAC packet.
|
||||
* This is needed as MediaCodec encoder generates a packet of raw
|
||||
* AAC data.
|
||||
*
|
||||
* Note the packetLen must count in the ADTS header itself.
|
||||
* See: http://wiki.multimedia.cx/index.php?title=ADTS
|
||||
* Also: http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Channel_Configurations
|
||||
**/
|
||||
- (NSData *)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength {
|
||||
int adtsLength = 7;
|
||||
char *packet = malloc(sizeof(char) * adtsLength);
|
||||
// Variables Recycled by addADTStoPacket
|
||||
int profile = 2; //AAC LC
|
||||
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
|
||||
NSInteger freqIdx = [self sampleRateIndex:self.configuration.audioSampleRate]; //44.1KHz
|
||||
int chanCfg = (int)channel; //MPEG-4 Audio Channel Configuration. 1 Channel front-center
|
||||
NSUInteger fullLength = adtsLength + rawDataLength;
|
||||
// fill in ADTS data
|
||||
packet[0] = (char)0xFF; // 11111111 = syncword
|
||||
packet[1] = (char)0xF9; // 1111 1 00 1 = syncword MPEG-2 Layer CRC
|
||||
packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
|
||||
packet[3] = (char)(((chanCfg&3)<<6) + (fullLength>>11));
|
||||
packet[4] = (char)((fullLength&0x7FF) >> 3);
|
||||
packet[5] = (char)(((fullLength&7)<<5) + 0x1F);
|
||||
packet[6] = (char)0xFC;
|
||||
NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES];
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSInteger)sampleRateIndex:(NSInteger)frequencyInHz {
|
||||
NSInteger sampleRateIndex = 0;
|
||||
switch (frequencyInHz) {
|
||||
case 96000:
|
||||
sampleRateIndex = 0;
|
||||
break;
|
||||
case 88200:
|
||||
sampleRateIndex = 1;
|
||||
break;
|
||||
case 64000:
|
||||
sampleRateIndex = 2;
|
||||
break;
|
||||
case 48000:
|
||||
sampleRateIndex = 3;
|
||||
break;
|
||||
case 44100:
|
||||
sampleRateIndex = 4;
|
||||
break;
|
||||
case 32000:
|
||||
sampleRateIndex = 5;
|
||||
break;
|
||||
case 24000:
|
||||
sampleRateIndex = 6;
|
||||
break;
|
||||
case 22050:
|
||||
sampleRateIndex = 7;
|
||||
break;
|
||||
case 16000:
|
||||
sampleRateIndex = 8;
|
||||
break;
|
||||
case 12000:
|
||||
sampleRateIndex = 9;
|
||||
break;
|
||||
case 11025:
|
||||
sampleRateIndex = 10;
|
||||
break;
|
||||
case 8000:
|
||||
sampleRateIndex = 11;
|
||||
break;
|
||||
case 7350:
|
||||
sampleRateIndex = 12;
|
||||
break;
|
||||
default:
|
||||
sampleRateIndex = 15;
|
||||
}
|
||||
return sampleRateIndex;
|
||||
}
|
||||
|
||||
- (void)initForFilePath {
|
||||
NSString *path = [self GetFilePathByfileName:@"IOSCamDemo_HW.aac"];
|
||||
NSLog(@"%@", path);
|
||||
self->fp = fopen([path cStringUsingEncoding:NSUTF8StringEncoding], "wb");
|
||||
}
|
||||
|
||||
- (NSString *)GetFilePathByfileName:(NSString*)filename {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:filename];
|
||||
return writablePath;
|
||||
}
|
||||
|
||||
@end
|
||||
+81
-97
@@ -19,7 +19,7 @@
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) LFLiveVideoConfiguration *configuration;
|
||||
@property (nonatomic,weak) id<LFVideoEncodingDelegate> h264Delegate;
|
||||
@property (nonatomic, weak) id<LFVideoEncodingDelegate> h264Delegate;
|
||||
@property (nonatomic) BOOL isBackGround;
|
||||
@property (nonatomic) NSInteger currentVideoBitRate;
|
||||
|
||||
@@ -28,68 +28,68 @@
|
||||
@implementation LFHardwareVideoEncoder
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithVideoStreamConfiguration:(LFLiveVideoConfiguration *)configuration{
|
||||
if(self = [super init]){
|
||||
- (instancetype)initWithVideoStreamConfiguration:(LFLiveVideoConfiguration *)configuration {
|
||||
if (self = [super init]) {
|
||||
NSLog(@"USE LFHardwareVideoEncoder");
|
||||
_configuration = configuration;
|
||||
[self initCompressionSession];
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
enabledWriteVideoFile = NO;
|
||||
[self initForFilePath];
|
||||
#endif
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initCompressionSession{
|
||||
if(compressionSession){
|
||||
- (void)initCompressionSession {
|
||||
if (compressionSession) {
|
||||
VTCompressionSessionCompleteFrames(compressionSession, kCMTimeInvalid);
|
||||
|
||||
|
||||
VTCompressionSessionInvalidate(compressionSession);
|
||||
CFRelease(compressionSession);
|
||||
compressionSession = NULL;
|
||||
}
|
||||
|
||||
|
||||
OSStatus status = VTCompressionSessionCreate(NULL, _configuration.videoSize.width, _configuration.videoSize.height, kCMVideoCodecType_H264, NULL, NULL, NULL, VideoCompressonOutputCallback, (__bridge void *)self, &compressionSession);
|
||||
if(status != noErr){
|
||||
if (status != noErr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_currentVideoBitRate = _configuration.videoBitRate;
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval,(__bridge CFTypeRef)@(_configuration.videoMaxKeyframeInterval));
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration,(__bridge CFTypeRef)@(_configuration.videoMaxKeyframeInterval));
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, (__bridge CFTypeRef)@(_configuration.videoMaxKeyframeInterval));
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, (__bridge CFTypeRef)@(_configuration.videoMaxKeyframeInterval));
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate, (__bridge CFTypeRef)@(_configuration.videoFrameRate));
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AverageBitRate, (__bridge CFTypeRef)@(_configuration.videoBitRate));
|
||||
NSArray *limit = @[@(_configuration.videoBitRate * 1.5/8),@(1)];
|
||||
NSArray *limit = @[@(_configuration.videoBitRate * 1.5/8), @(1)];
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_DataRateLimits, (__bridge CFArrayRef)limit);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanFalse);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Main_AutoLevel);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanTrue);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_H264EntropyMode, kVTH264EntropyMode_CABAC);
|
||||
VTCompressionSessionPrepareToEncodeFrames(compressionSession);
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void)setVideoBitRate:(NSInteger)videoBitRate{
|
||||
if(_isBackGround) return;
|
||||
- (void)setVideoBitRate:(NSInteger)videoBitRate {
|
||||
if (_isBackGround) return;
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AverageBitRate, (__bridge CFTypeRef)@(videoBitRate));
|
||||
NSArray *limit = @[@(videoBitRate * 1.5/8),@(1)];
|
||||
NSArray *limit = @[@(videoBitRate * 1.5/8), @(1)];
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_DataRateLimits, (__bridge CFArrayRef)limit);
|
||||
_currentVideoBitRate = videoBitRate;
|
||||
}
|
||||
|
||||
-(NSInteger)videoBitRate{
|
||||
- (NSInteger)videoBitRate {
|
||||
return _currentVideoBitRate;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
if(compressionSession != NULL)
|
||||
{
|
||||
- (void)dealloc {
|
||||
if (compressionSession != NULL) {
|
||||
VTCompressionSessionCompleteFrames(compressionSession, kCMTimeInvalid);
|
||||
|
||||
|
||||
VTCompressionSessionInvalidate(compressionSession);
|
||||
CFRelease(compressionSession);
|
||||
compressionSession = NULL;
|
||||
@@ -98,90 +98,86 @@
|
||||
}
|
||||
|
||||
#pragma mark -- LFVideoEncoder
|
||||
- (void)encodeVideoData:(CVImageBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp{
|
||||
if(_isBackGround) return;
|
||||
|
||||
frameCount ++;
|
||||
CMTime presentationTimeStamp = CMTimeMake(frameCount, 1000);
|
||||
- (void)encodeVideoData:(CVPixelBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp {
|
||||
if (_isBackGround) return;
|
||||
|
||||
frameCount++;
|
||||
CMTime presentationTimeStamp = CMTimeMake(frameCount, (int32_t)_configuration.videoFrameRate);
|
||||
VTEncodeInfoFlags flags;
|
||||
CMTime duration = CMTimeMake(1, (int32_t)_configuration.videoFrameRate);
|
||||
|
||||
|
||||
NSDictionary *properties = nil;
|
||||
if(frameCount % (int32_t)_configuration.videoMaxKeyframeInterval == 0){
|
||||
if (frameCount % (int32_t)_configuration.videoMaxKeyframeInterval == 0) {
|
||||
properties = @{(__bridge NSString *)kVTEncodeFrameOptionKey_ForceKeyFrame: @YES};
|
||||
}
|
||||
NSNumber *timeNumber = @(timeStamp);
|
||||
|
||||
|
||||
VTCompressionSessionEncodeFrame(compressionSession, pixelBuffer, presentationTimeStamp, duration, (__bridge CFDictionaryRef)properties, (__bridge_retained void *)timeNumber, &flags);
|
||||
}
|
||||
|
||||
- (void)stopEncoder{
|
||||
- (void)stopEncoder {
|
||||
VTCompressionSessionCompleteFrames(compressionSession, kCMTimeIndefinite);
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<LFVideoEncodingDelegate>)delegate{
|
||||
- (void)setDelegate:(id<LFVideoEncodingDelegate>)delegate {
|
||||
_h264Delegate = delegate;
|
||||
}
|
||||
|
||||
#pragma mark -- NSNotification
|
||||
- (void)willEnterBackground:(NSNotification*)notification{
|
||||
- (void)willEnterBackground:(NSNotification *)notification {
|
||||
_isBackGround = YES;
|
||||
}
|
||||
|
||||
- (void)willEnterForeground:(NSNotification*)notification{
|
||||
- (void)willEnterForeground:(NSNotification *)notification {
|
||||
[self initCompressionSession];
|
||||
_isBackGround = NO;
|
||||
}
|
||||
|
||||
#pragma mark -- VideoCallBack
|
||||
static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer)
|
||||
{
|
||||
if(!sampleBuffer) return;
|
||||
static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer){
|
||||
if (!sampleBuffer) return;
|
||||
CFArrayRef array = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true);
|
||||
if(!array) return;
|
||||
if (!array) return;
|
||||
CFDictionaryRef dic = (CFDictionaryRef)CFArrayGetValueAtIndex(array, 0);
|
||||
if(!dic) return;
|
||||
|
||||
if (!dic) return;
|
||||
|
||||
BOOL keyframe = !CFDictionaryContainsKey(dic, kCMSampleAttachmentKey_NotSync);
|
||||
uint64_t timeStamp = [((__bridge_transfer NSNumber*)VTFrameRef) longLongValue];
|
||||
|
||||
uint64_t timeStamp = [((__bridge_transfer NSNumber *)VTFrameRef) longLongValue];
|
||||
|
||||
LFHardwareVideoEncoder *videoEncoder = (__bridge LFHardwareVideoEncoder *)VTref;
|
||||
if(status != noErr){
|
||||
if (status != noErr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyframe && !videoEncoder->sps)
|
||||
{
|
||||
|
||||
if (keyframe && !videoEncoder->sps) {
|
||||
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);
|
||||
|
||||
|
||||
size_t sparameterSetSize, sparameterSetCount;
|
||||
const uint8_t *sparameterSet;
|
||||
OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0 );
|
||||
if (statusCode == noErr)
|
||||
{
|
||||
OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0);
|
||||
if (statusCode == noErr) {
|
||||
size_t pparameterSetSize, pparameterSetCount;
|
||||
const uint8_t *pparameterSet;
|
||||
OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0 );
|
||||
if (statusCode == noErr)
|
||||
{
|
||||
OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0);
|
||||
if (statusCode == noErr) {
|
||||
videoEncoder->sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];
|
||||
videoEncoder->pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];
|
||||
|
||||
if(videoEncoder->enabledWriteVideoFile){
|
||||
|
||||
if (videoEncoder->enabledWriteVideoFile) {
|
||||
NSMutableData *data = [[NSMutableData alloc] init];
|
||||
uint8_t header[] = {0x00,0x00,0x00,0x01};
|
||||
uint8_t header[] = {0x00, 0x00, 0x00, 0x01};
|
||||
[data appendBytes:header length:4];
|
||||
[data appendData:videoEncoder->sps];
|
||||
[data appendBytes:header length:4];
|
||||
[data appendData:videoEncoder->pps];
|
||||
fwrite(data.bytes, 1,data.length,videoEncoder->fp);
|
||||
fwrite(data.bytes, 1, data.length, videoEncoder->fp);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
|
||||
size_t length, totalLength;
|
||||
char *dataPointer;
|
||||
@@ -193,7 +189,7 @@ static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatu
|
||||
// Read the NAL unit length
|
||||
uint32_t NALUnitLength = 0;
|
||||
memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength);
|
||||
|
||||
|
||||
NALUnitLength = CFSwapInt32BigToHost(NALUnitLength);
|
||||
|
||||
LFVideoFrame *videoFrame = [LFVideoFrame new];
|
||||
@@ -202,56 +198,44 @@ static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatu
|
||||
videoFrame.isKeyFrame = keyframe;
|
||||
videoFrame.sps = videoEncoder->sps;
|
||||
videoFrame.pps = videoEncoder->pps;
|
||||
|
||||
if(videoEncoder.h264Delegate && [videoEncoder.h264Delegate respondsToSelector:@selector(videoEncoder:videoFrame:)]){
|
||||
|
||||
if (videoEncoder.h264Delegate && [videoEncoder.h264Delegate respondsToSelector:@selector(videoEncoder:videoFrame:)]) {
|
||||
[videoEncoder.h264Delegate videoEncoder:videoEncoder videoFrame:videoFrame];
|
||||
}
|
||||
|
||||
if(videoEncoder->enabledWriteVideoFile){
|
||||
|
||||
if (videoEncoder->enabledWriteVideoFile) {
|
||||
NSMutableData *data = [[NSMutableData alloc] init];
|
||||
if(keyframe){
|
||||
uint8_t header[] = {0x00,0x00,0x00,0x01};
|
||||
if (keyframe) {
|
||||
uint8_t header[] = {0x00, 0x00, 0x00, 0x01};
|
||||
[data appendBytes:header length:4];
|
||||
}else{
|
||||
uint8_t header[] = {0x00,0x00,0x01};
|
||||
} else {
|
||||
uint8_t header[] = {0x00, 0x00, 0x01};
|
||||
[data appendBytes:header length:3];
|
||||
}
|
||||
[data appendData:videoFrame.data];
|
||||
|
||||
fwrite(data.bytes, 1,data.length,videoEncoder->fp);
|
||||
|
||||
fwrite(data.bytes, 1, data.length, videoEncoder->fp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bufferOffset += AVCCHeaderLength + NALUnitLength;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)initForFilePath
|
||||
{
|
||||
char *path = [self GetFilePathByfileName:"IOSCamDemo.h264"];
|
||||
NSLog(@"%s",path);
|
||||
self->fp = fopen(path,"wb");
|
||||
- (void)initForFilePath {
|
||||
NSString *path = [self GetFilePathByfileName:@"IOSCamDemo.h264"];
|
||||
NSLog(@"%@", path);
|
||||
self->fp = fopen([path cStringUsingEncoding:NSUTF8StringEncoding], "wb");
|
||||
}
|
||||
|
||||
|
||||
- (char*)GetFilePathByfileName:(char*)filename
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
|
||||
- (NSString *)GetFilePathByfileName:(NSString*)filename {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *strName = [NSString stringWithFormat:@"%s",filename];
|
||||
|
||||
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:strName];
|
||||
|
||||
NSUInteger len = [writablePath length];
|
||||
|
||||
char *filepath = (char*)malloc(sizeof(char) * (len + 1));
|
||||
|
||||
[writablePath getCString:filepath maxLength:len + 1 encoding:[NSString defaultCStringEncoding]];
|
||||
|
||||
return filepath;
|
||||
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:filename];
|
||||
return writablePath;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -14,18 +14,17 @@
|
||||
/// 编码器编码后回调
|
||||
@protocol LFVideoEncodingDelegate <NSObject>
|
||||
@required
|
||||
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame*)frame;
|
||||
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame *)frame;
|
||||
@end
|
||||
|
||||
/// 编码器抽象的接口
|
||||
@protocol LFVideoEncoding <NSObject>
|
||||
@required
|
||||
- (void)encodeVideoData:(nullable CVImageBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp;
|
||||
- (void)stopEncoder;
|
||||
- (void)encodeVideoData:(nullable CVPixelBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp;
|
||||
@optional
|
||||
@property (nonatomic, assign) NSInteger videoBitRate;
|
||||
- (nullable instancetype)initWithVideoStreamConfiguration:(nullable LFLiveVideoConfiguration*)configuration;
|
||||
- (nullable instancetype)initWithVideoStreamConfiguration:(nullable LFLiveVideoConfiguration *)configuration;
|
||||
- (void)setDelegate:(nullable id<LFVideoEncodingDelegate>)delegate;
|
||||
|
||||
- (void)stopEncoder;
|
||||
@end
|
||||
|
||||
+19
-15
@@ -9,7 +9,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/// 音频码率
|
||||
typedef NS_ENUM(NSUInteger, LFLiveAudioBitRate) {
|
||||
typedef NS_ENUM (NSUInteger, LFLiveAudioBitRate) {
|
||||
/// 32Kbps 音频码率
|
||||
LFLiveAudioBitRate_32Kbps = 32000,
|
||||
/// 64Kbps 音频码率
|
||||
@@ -19,34 +19,36 @@ typedef NS_ENUM(NSUInteger, LFLiveAudioBitRate) {
|
||||
/// 128Kbps 音频码率
|
||||
LFLiveAudioBitRate_128Kbps = 128000,
|
||||
/// 默认音频码率,默认为 64Kbps
|
||||
LFLiveAudioBitRate_Default = LFLiveAudioBitRate_64Kbps
|
||||
LFLiveAudioBitRate_Default = LFLiveAudioBitRate_96Kbps
|
||||
};
|
||||
|
||||
/// 采样率 (默认44.1Hz iphoneg6以上48Hz)
|
||||
typedef NS_ENUM(NSUInteger, LFLiveAudioSampleRate){
|
||||
/// 44.1Hz 采样率
|
||||
/// 采样率 (默认44.1Hz)
|
||||
typedef NS_ENUM (NSUInteger, LFLiveAudioSampleRate){
|
||||
/// 16KHz 采样率
|
||||
LFLiveAudioSampleRate_16000Hz = 16000,
|
||||
/// 44.1KHz 采样率
|
||||
LFLiveAudioSampleRate_44100Hz = 44100,
|
||||
/// 48Hz 采样率
|
||||
/// 48KHz 采样率
|
||||
LFLiveAudioSampleRate_48000Hz = 48000,
|
||||
/// 默认音频码率,默认为 64Kbps
|
||||
LFLiveAudioSampleRate_Default = LFLiveAudioSampleRate_44100Hz
|
||||
};
|
||||
|
||||
/// Audio Live quality(音频质量)
|
||||
typedef NS_ENUM(NSUInteger, LFLiveAudioQuality){
|
||||
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 32Kbps
|
||||
typedef NS_ENUM (NSUInteger, LFLiveAudioQuality){
|
||||
/// 高音频质量 audio sample rate: 16KHz audio bitrate: numberOfChannels 1 : 32Kbps 2 : 64Kbps
|
||||
LFLiveAudioQuality_Low = 0,
|
||||
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 64Kbps
|
||||
/// 高音频质量 audio sample rate: 44KHz audio bitrate: 96Kbps
|
||||
LFLiveAudioQuality_Medium = 1,
|
||||
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 96Kbps
|
||||
/// 高音频质量 audio sample rate: 44MHz audio bitrate: 128Kbps
|
||||
LFLiveAudioQuality_High = 2,
|
||||
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 128Kbps
|
||||
/// 高音频质量 audio sample rate: 48MHz, audio bitrate: 128Kbps
|
||||
LFLiveAudioQuality_VeryHigh = 3,
|
||||
/// 默认音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 64Kbps
|
||||
LFLiveAudioQuality_Default = LFLiveAudioQuality_Medium
|
||||
/// 默认音频质量 audio sample rate: 44MHz, audio bitrate: 96Kbps
|
||||
LFLiveAudioQuality_Default = LFLiveAudioQuality_High
|
||||
};
|
||||
|
||||
@interface LFLiveAudioConfiguration : NSObject<NSCoding,NSCopying>
|
||||
@interface LFLiveAudioConfiguration : NSObject<NSCoding, NSCopying>
|
||||
|
||||
/// 默认音频配置
|
||||
+ (instancetype)defaultConfiguration;
|
||||
@@ -64,6 +66,8 @@ typedef NS_ENUM(NSUInteger, LFLiveAudioQuality){
|
||||
// 码率
|
||||
@property (nonatomic, assign) LFLiveAudioBitRate audioBitrate;
|
||||
/// flv编码音频头 44100 为0x12 0x10
|
||||
@property (nonatomic ,assign,readonly) char *asc;
|
||||
@property (nonatomic, assign, readonly) char *asc;
|
||||
/// 缓存区长度
|
||||
@property (nonatomic, assign,readonly) NSUInteger bufferLength;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// LFLiveAudioConfiguration.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/1.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFLiveAudioConfiguration.h"
|
||||
#import <sys/utsname.h>
|
||||
|
||||
@implementation LFLiveAudioConfiguration
|
||||
|
||||
#pragma mark -- LifyCycle
|
||||
+ (instancetype)defaultConfiguration {
|
||||
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration defaultConfigurationForQuality:LFLiveAudioQuality_Default];
|
||||
return audioConfig;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveAudioQuality)audioQuality {
|
||||
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration new];
|
||||
audioConfig.numberOfChannels = 2;
|
||||
switch (audioQuality) {
|
||||
case LFLiveAudioQuality_Low: {
|
||||
audioConfig.audioBitrate = audioConfig.numberOfChannels == 1 ? LFLiveAudioBitRate_32Kbps : LFLiveAudioBitRate_64Kbps;
|
||||
audioConfig.audioSampleRate = LFLiveAudioSampleRate_16000Hz;
|
||||
}
|
||||
break;
|
||||
case LFLiveAudioQuality_Medium: {
|
||||
audioConfig.audioBitrate = LFLiveAudioBitRate_96Kbps;
|
||||
audioConfig.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
|
||||
}
|
||||
break;
|
||||
case LFLiveAudioQuality_High: {
|
||||
audioConfig.audioBitrate = LFLiveAudioBitRate_128Kbps;
|
||||
audioConfig.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
|
||||
}
|
||||
break;
|
||||
case LFLiveAudioQuality_VeryHigh: {
|
||||
audioConfig.audioBitrate = LFLiveAudioBitRate_128Kbps;
|
||||
audioConfig.audioSampleRate = LFLiveAudioSampleRate_48000Hz;
|
||||
}
|
||||
break;
|
||||
default:{
|
||||
audioConfig.audioBitrate = LFLiveAudioBitRate_96Kbps;
|
||||
audioConfig.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return audioConfig;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_asc = malloc(2);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_asc) free(_asc);
|
||||
}
|
||||
|
||||
#pragma mark Setter
|
||||
- (void)setAudioSampleRate:(LFLiveAudioSampleRate)audioSampleRate {
|
||||
_audioSampleRate = audioSampleRate;
|
||||
NSInteger sampleRateIndex = [self sampleRateIndex:audioSampleRate];
|
||||
self.asc[0] = 0x10 | ((sampleRateIndex>>1) & 0x7);
|
||||
self.asc[1] = ((sampleRateIndex & 0x1)<<7) | ((self.numberOfChannels & 0xF) << 3);
|
||||
}
|
||||
|
||||
- (void)setNumberOfChannels:(NSUInteger)numberOfChannels {
|
||||
_numberOfChannels = numberOfChannels;
|
||||
NSInteger sampleRateIndex = [self sampleRateIndex:self.audioSampleRate];
|
||||
self.asc[0] = 0x10 | ((sampleRateIndex>>1) & 0x7);
|
||||
self.asc[1] = ((sampleRateIndex & 0x1)<<7) | ((numberOfChannels & 0xF) << 3);
|
||||
}
|
||||
|
||||
- (NSUInteger)bufferLength{
|
||||
return 1024*2*self.numberOfChannels;
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (NSInteger)sampleRateIndex:(NSInteger)frequencyInHz {
|
||||
NSInteger sampleRateIndex = 0;
|
||||
switch (frequencyInHz) {
|
||||
case 96000:
|
||||
sampleRateIndex = 0;
|
||||
break;
|
||||
case 88200:
|
||||
sampleRateIndex = 1;
|
||||
break;
|
||||
case 64000:
|
||||
sampleRateIndex = 2;
|
||||
break;
|
||||
case 48000:
|
||||
sampleRateIndex = 3;
|
||||
break;
|
||||
case 44100:
|
||||
sampleRateIndex = 4;
|
||||
break;
|
||||
case 32000:
|
||||
sampleRateIndex = 5;
|
||||
break;
|
||||
case 24000:
|
||||
sampleRateIndex = 6;
|
||||
break;
|
||||
case 22050:
|
||||
sampleRateIndex = 7;
|
||||
break;
|
||||
case 16000:
|
||||
sampleRateIndex = 8;
|
||||
break;
|
||||
case 12000:
|
||||
sampleRateIndex = 9;
|
||||
break;
|
||||
case 11025:
|
||||
sampleRateIndex = 10;
|
||||
break;
|
||||
case 8000:
|
||||
sampleRateIndex = 11;
|
||||
break;
|
||||
case 7350:
|
||||
sampleRateIndex = 12;
|
||||
break;
|
||||
default:
|
||||
sampleRateIndex = 15;
|
||||
}
|
||||
return sampleRateIndex;
|
||||
}
|
||||
|
||||
#pragma mark -- Encoder
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:@(self.numberOfChannels) forKey:@"numberOfChannels"];
|
||||
[aCoder encodeObject:@(self.audioSampleRate) forKey:@"audioSampleRate"];
|
||||
[aCoder encodeObject:@(self.audioBitrate) forKey:@"audioBitrate"];
|
||||
[aCoder encodeObject:[NSString stringWithUTF8String:self.asc] forKey:@"asc"];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super init];
|
||||
_numberOfChannels = [[aDecoder decodeObjectForKey:@"numberOfChannels"] unsignedIntegerValue];
|
||||
_audioSampleRate = [[aDecoder decodeObjectForKey:@"audioSampleRate"] unsignedIntegerValue];
|
||||
_audioBitrate = [[aDecoder decodeObjectForKey:@"audioBitrate"] unsignedIntegerValue];
|
||||
_asc = strdup([[aDecoder decodeObjectForKey:@"asc"] cStringUsingEncoding:NSUTF8StringEncoding]);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other {
|
||||
if (other == self) {
|
||||
return YES;
|
||||
} else if (![super isEqual:other]) {
|
||||
return NO;
|
||||
} else {
|
||||
LFLiveAudioConfiguration *object = other;
|
||||
return object.numberOfChannels == self.numberOfChannels &&
|
||||
object.audioBitrate == self.audioBitrate &&
|
||||
strcmp(object.asc, self.asc) == 0 &&
|
||||
object.audioSampleRate == self.audioSampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger hash = 0;
|
||||
NSArray *values = @[@(_numberOfChannels),
|
||||
@(_audioSampleRate),
|
||||
[NSString stringWithUTF8String:self.asc],
|
||||
@(_audioBitrate)];
|
||||
|
||||
for (NSObject *value in values) {
|
||||
hash ^= value.hash;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(nullable NSZone *)zone {
|
||||
LFLiveAudioConfiguration *other = [self.class defaultConfiguration];
|
||||
return other;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *desc = @"".mutableCopy;
|
||||
[desc appendFormat:@"<LFLiveAudioConfiguration: %p>", self];
|
||||
[desc appendFormat:@" numberOfChannels:%zi", self.numberOfChannels];
|
||||
[desc appendFormat:@" audioSampleRate:%zi", self.audioSampleRate];
|
||||
[desc appendFormat:@" audioBitrate:%zi", self.audioBitrate];
|
||||
[desc appendFormat:@" audioHeader:%@", [NSString stringWithUTF8String:self.asc]];
|
||||
return desc;
|
||||
}
|
||||
|
||||
@end
|
||||
+11
-11
@@ -10,7 +10,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/// 视频分辨率(都是16:9 当此设备不支持当前分辨率,自动降低一级)
|
||||
typedef NS_ENUM(NSUInteger, LFLiveVideoSessionPreset){
|
||||
typedef NS_ENUM (NSUInteger, LFLiveVideoSessionPreset){
|
||||
/// 低分辨率
|
||||
LFCaptureSessionPreset360x640 = 0,
|
||||
/// 中分辨率
|
||||
@@ -20,7 +20,7 @@ typedef NS_ENUM(NSUInteger, LFLiveVideoSessionPreset){
|
||||
};
|
||||
|
||||
/// 视频质量
|
||||
typedef NS_ENUM(NSUInteger, LFLiveVideoQuality){
|
||||
typedef NS_ENUM (NSUInteger, LFLiveVideoQuality){
|
||||
/// 分辨率: 360 *640 帧数:15 码率:500Kps
|
||||
LFLiveVideoQuality_Low1 = 0,
|
||||
/// 分辨率: 360 *640 帧数:24 码率:800Kps
|
||||
@@ -43,25 +43,28 @@ typedef NS_ENUM(NSUInteger, LFLiveVideoQuality){
|
||||
LFLiveVideoQuality_Default = LFLiveVideoQuality_Low2
|
||||
};
|
||||
|
||||
@interface LFLiveVideoConfiguration : NSObject<NSCoding,NSCopying>
|
||||
@interface LFLiveVideoConfiguration : NSObject<NSCoding, NSCopying>
|
||||
|
||||
/// 默认视频配置
|
||||
+ (instancetype)defaultConfiguration;
|
||||
/// 视频配置(质量)
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality;
|
||||
|
||||
/// 视频配置(质量 & 方向)
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality orientation:(UIInterfaceOrientation)orientation;
|
||||
/// 视频配置(质量 & 是否是横屏)
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality landscape:(BOOL)landscape;
|
||||
|
||||
#pragma mark - Attribute
|
||||
///=============================================================================
|
||||
/// @name Attribute
|
||||
///=============================================================================
|
||||
/// 视频的分辨率,宽高务必设定为 2 的倍数,否则解码播放时可能出现绿边
|
||||
/// 视频的分辨率,宽高务必设定为 2 的倍数,否则解码播放时可能出现绿边(这个videoSizeRespectingAspectRatio设置为YES则可能会改变)
|
||||
@property (nonatomic, assign) CGSize videoSize;
|
||||
|
||||
/// 输出图像是否等比例,默认为NO
|
||||
@property (nonatomic, assign) BOOL videoSizeRespectingAspectRatio;
|
||||
|
||||
/// 视频输出方向
|
||||
@property (nonatomic, assign) UIInterfaceOrientation orientation;
|
||||
@property (nonatomic, assign) BOOL landscape;
|
||||
|
||||
/// 视频的帧率,即 fps
|
||||
@property (nonatomic, assign) NSUInteger videoFrameRate;
|
||||
@@ -88,9 +91,6 @@ typedef NS_ENUM(NSUInteger, LFLiveVideoQuality){
|
||||
@property (nonatomic, assign) LFLiveVideoSessionPreset sessionPreset;
|
||||
|
||||
///< ≈sde3分辨率
|
||||
@property (nonatomic, assign,readonly) NSString *avSessionPreset;
|
||||
|
||||
///< 是否裁剪
|
||||
@property (nonatomic, assign,readonly) BOOL isClipVideo;
|
||||
@property (nonatomic, assign, readonly) NSString *avSessionPreset;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,364 @@
|
||||
//
|
||||
// LFLiveVideoConfiguration.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/1.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFLiveVideoConfiguration.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
|
||||
@implementation LFLiveVideoConfiguration
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
|
||||
+ (instancetype)defaultConfiguration {
|
||||
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Default];
|
||||
return configuration;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality {
|
||||
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:videoQuality landscape:NO];
|
||||
return configuration;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality landscape:(BOOL)landscape {
|
||||
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration new];
|
||||
switch (videoQuality) {
|
||||
case LFLiveVideoQuality_Low1:{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset360x640;
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 500 * 1000;
|
||||
configuration.videoMaxBitRate = 600 * 1000;
|
||||
configuration.videoMinBitRate = 400 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Low2:{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset360x640;
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 600 * 1000;
|
||||
configuration.videoMaxBitRate = 720 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Low3: {
|
||||
configuration.sessionPreset = LFCaptureSessionPreset360x640;
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 600 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium1:{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset540x960;
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium2:{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset540x960;
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium3:{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset540x960;
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 1000 * 1000;
|
||||
configuration.videoMaxBitRate = 1200 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High1:{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 1000 * 1000;
|
||||
configuration.videoMaxBitRate = 1200 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High2:{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 1200 * 1000;
|
||||
configuration.videoMaxBitRate = 1440 * 1000;
|
||||
configuration.videoMinBitRate = 800 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High3:{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 1200 * 1000;
|
||||
configuration.videoMaxBitRate = 1440 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
configuration.sessionPreset = [configuration supportSessionPreset:configuration.sessionPreset];
|
||||
configuration.videoMaxKeyframeInterval = configuration.videoFrameRate*2;
|
||||
configuration.landscape = landscape;
|
||||
CGSize size = configuration.videoSize;
|
||||
if (landscape) {
|
||||
configuration.videoSize = CGSizeMake(size.height, size.width);
|
||||
} else {
|
||||
configuration.videoSize = CGSizeMake(size.width, size.height);
|
||||
}
|
||||
return configuration;
|
||||
|
||||
}
|
||||
|
||||
#pragma mark -- Setter Getter
|
||||
- (NSString *)avSessionPreset {
|
||||
NSString *avSessionPreset = nil;
|
||||
switch (self.sessionPreset) {
|
||||
case LFCaptureSessionPreset360x640:{
|
||||
avSessionPreset = AVCaptureSessionPreset640x480;
|
||||
}
|
||||
break;
|
||||
case LFCaptureSessionPreset540x960:{
|
||||
avSessionPreset = AVCaptureSessionPresetiFrame960x540;
|
||||
}
|
||||
break;
|
||||
case LFCaptureSessionPreset720x1280:{
|
||||
avSessionPreset = AVCaptureSessionPreset1280x720;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
avSessionPreset = AVCaptureSessionPreset640x480;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return avSessionPreset;
|
||||
}
|
||||
|
||||
- (CGSize)videoSize{
|
||||
if(_videoSizeRespectingAspectRatio){
|
||||
return self.aspectRatioVideoSize;
|
||||
}
|
||||
return _videoSize;
|
||||
}
|
||||
|
||||
- (void)setVideoMaxBitRate:(NSUInteger)videoMaxBitRate {
|
||||
if (videoMaxBitRate <= _videoBitRate) return;
|
||||
_videoMaxBitRate = videoMaxBitRate;
|
||||
}
|
||||
|
||||
- (void)setVideoMinBitRate:(NSUInteger)videoMinBitRate {
|
||||
if (videoMinBitRate >= _videoBitRate) return;
|
||||
_videoMinBitRate = videoMinBitRate;
|
||||
}
|
||||
|
||||
- (void)setVideoMaxFrameRate:(NSUInteger)videoMaxFrameRate {
|
||||
if (videoMaxFrameRate <= _videoFrameRate) return;
|
||||
_videoMaxFrameRate = videoMaxFrameRate;
|
||||
}
|
||||
|
||||
- (void)setVideoMinFrameRate:(NSUInteger)videoMinFrameRate {
|
||||
if (videoMinFrameRate >= _videoFrameRate) return;
|
||||
_videoMinFrameRate = videoMinFrameRate;
|
||||
}
|
||||
|
||||
- (void)setSessionPreset:(LFLiveVideoSessionPreset)sessionPreset{
|
||||
_sessionPreset = sessionPreset;
|
||||
_sessionPreset = [self supportSessionPreset:sessionPreset];
|
||||
}
|
||||
|
||||
#pragma mark -- Custom Method
|
||||
- (LFLiveVideoSessionPreset)supportSessionPreset:(LFLiveVideoSessionPreset)sessionPreset {
|
||||
AVCaptureSession *session = [[AVCaptureSession alloc] init];
|
||||
AVCaptureDevice *inputCamera;
|
||||
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
||||
for (AVCaptureDevice *device in devices){
|
||||
if ([device position] == AVCaptureDevicePositionFront){
|
||||
inputCamera = device;
|
||||
}
|
||||
}
|
||||
AVCaptureDeviceInput *videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:inputCamera error:nil];
|
||||
|
||||
if ([session canAddInput:videoInput]){
|
||||
[session addInput:videoInput];
|
||||
}
|
||||
|
||||
if (![session canSetSessionPreset:self.avSessionPreset]) {
|
||||
if (sessionPreset == LFCaptureSessionPreset720x1280) {
|
||||
sessionPreset = LFCaptureSessionPreset540x960;
|
||||
if (![session canSetSessionPreset:self.avSessionPreset]) {
|
||||
sessionPreset = LFCaptureSessionPreset360x640;
|
||||
}
|
||||
} else if (sessionPreset == LFCaptureSessionPreset540x960) {
|
||||
sessionPreset = LFCaptureSessionPreset360x640;
|
||||
}
|
||||
}
|
||||
return sessionPreset;
|
||||
}
|
||||
|
||||
- (CGSize)captureOutVideoSize{
|
||||
CGSize videoSize = CGSizeZero;
|
||||
switch (_sessionPreset) {
|
||||
case LFCaptureSessionPreset360x640:{
|
||||
videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFCaptureSessionPreset540x960:{
|
||||
videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFCaptureSessionPreset720x1280:{
|
||||
videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
|
||||
default:{
|
||||
videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(self.landscape){
|
||||
return CGSizeMake(videoSize.height, videoSize.width);
|
||||
}
|
||||
return videoSize;
|
||||
}
|
||||
|
||||
- (CGSize)aspectRatioVideoSize{
|
||||
CGSize size = AVMakeRectWithAspectRatioInsideRect(self.captureOutVideoSize, CGRectMake(0, 0, _videoSize.width, _videoSize.height)).size;
|
||||
NSInteger width = ceil(size.width);
|
||||
NSInteger height = ceil(size.height);
|
||||
if(width %2 != 0) width = width - 1;
|
||||
if(height %2 != 0) height = height - 1;
|
||||
return CGSizeMake(width, height);
|
||||
}
|
||||
|
||||
#pragma mark -- encoder
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:[NSValue valueWithCGSize:self.videoSize] forKey:@"videoSize"];
|
||||
[aCoder encodeObject:@(self.videoFrameRate) forKey:@"videoFrameRate"];
|
||||
[aCoder encodeObject:@(self.videoMaxFrameRate) forKey:@"videoMaxFrameRate"];
|
||||
[aCoder encodeObject:@(self.videoMinFrameRate) forKey:@"videoMinFrameRate"];
|
||||
[aCoder encodeObject:@(self.videoMaxKeyframeInterval) forKey:@"videoMaxKeyframeInterval"];
|
||||
[aCoder encodeObject:@(self.videoBitRate) forKey:@"videoBitRate"];
|
||||
[aCoder encodeObject:@(self.videoMaxBitRate) forKey:@"videoMaxBitRate"];
|
||||
[aCoder encodeObject:@(self.videoMinBitRate) forKey:@"videoMinBitRate"];
|
||||
[aCoder encodeObject:@(self.sessionPreset) forKey:@"sessionPreset"];
|
||||
[aCoder encodeObject:@(self.landscape) forKey:@"landscape"];
|
||||
[aCoder encodeObject:@(self.videoSizeRespectingAspectRatio) forKey:@"videoSizeRespectingAspectRatio"];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super init];
|
||||
_videoSize = [[aDecoder decodeObjectForKey:@"videoSize"] CGSizeValue];
|
||||
_videoFrameRate = [[aDecoder decodeObjectForKey:@"videoFrameRate"] unsignedIntegerValue];
|
||||
_videoMaxFrameRate = [[aDecoder decodeObjectForKey:@"videoMaxFrameRate"] unsignedIntegerValue];
|
||||
_videoMinFrameRate = [[aDecoder decodeObjectForKey:@"videoMinFrameRate"] unsignedIntegerValue];
|
||||
_videoMaxKeyframeInterval = [[aDecoder decodeObjectForKey:@"videoMaxKeyframeInterval"] unsignedIntegerValue];
|
||||
_videoBitRate = [[aDecoder decodeObjectForKey:@"videoBitRate"] unsignedIntegerValue];
|
||||
_videoMaxBitRate = [[aDecoder decodeObjectForKey:@"videoMaxBitRate"] unsignedIntegerValue];
|
||||
_videoMinBitRate = [[aDecoder decodeObjectForKey:@"videoMinBitRate"] unsignedIntegerValue];
|
||||
_sessionPreset = [[aDecoder decodeObjectForKey:@"sessionPreset"] unsignedIntegerValue];
|
||||
_landscape = [[aDecoder decodeObjectForKey:@"landscape"] unsignedIntegerValue];
|
||||
_videoSizeRespectingAspectRatio = [[aDecoder decodeObjectForKey:@"videoSizeRespectingAspectRatio"] unsignedIntegerValue];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger hash = 0;
|
||||
NSArray *values = @[[NSValue valueWithCGSize:self.videoSize],
|
||||
@(self.videoFrameRate),
|
||||
@(self.videoMaxFrameRate),
|
||||
@(self.videoMinFrameRate),
|
||||
@(self.videoMaxKeyframeInterval),
|
||||
@(self.videoBitRate),
|
||||
@(self.videoMaxBitRate),
|
||||
@(self.videoMinBitRate),
|
||||
self.avSessionPreset,
|
||||
@(self.sessionPreset),
|
||||
@(self.landscape),
|
||||
@(self.videoSizeRespectingAspectRatio)];
|
||||
|
||||
for (NSObject *value in values) {
|
||||
hash ^= value.hash;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other {
|
||||
if (other == self) {
|
||||
return YES;
|
||||
} else if (![super isEqual:other]) {
|
||||
return NO;
|
||||
} else {
|
||||
LFLiveVideoConfiguration *object = other;
|
||||
return CGSizeEqualToSize(object.videoSize, self.videoSize) &&
|
||||
object.videoFrameRate == self.videoFrameRate &&
|
||||
object.videoMaxFrameRate == self.videoMaxFrameRate &&
|
||||
object.videoMinFrameRate == self.videoMinFrameRate &&
|
||||
object.videoMaxKeyframeInterval == self.videoMaxKeyframeInterval &&
|
||||
object.videoBitRate == self.videoBitRate &&
|
||||
object.videoMaxBitRate == self.videoMaxBitRate &&
|
||||
object.videoMinBitRate == self.videoMinBitRate &&
|
||||
[object.avSessionPreset isEqualToString:self.avSessionPreset] &&
|
||||
object.sessionPreset == self.sessionPreset &&
|
||||
object.landscape == self.landscape &&
|
||||
object.videoSizeRespectingAspectRatio == self.videoSizeRespectingAspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(nullable NSZone *)zone {
|
||||
LFLiveVideoConfiguration *other = [self.class defaultConfiguration];
|
||||
return other;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *desc = @"".mutableCopy;
|
||||
[desc appendFormat:@"<LFLiveVideoConfiguration: %p>", self];
|
||||
[desc appendFormat:@" videoSize:%@", NSStringFromCGSize(self.videoSize)];
|
||||
[desc appendFormat:@" videoSizeRespectingAspectRatio:%zi",self.videoSizeRespectingAspectRatio];
|
||||
[desc appendFormat:@" videoFrameRate:%zi", self.videoFrameRate];
|
||||
[desc appendFormat:@" videoMaxFrameRate:%zi", self.videoMaxFrameRate];
|
||||
[desc appendFormat:@" videoMinFrameRate:%zi", self.videoMinFrameRate];
|
||||
[desc appendFormat:@" videoMaxKeyframeInterval:%zi", self.videoMaxKeyframeInterval];
|
||||
[desc appendFormat:@" videoBitRate:%zi", self.videoBitRate];
|
||||
[desc appendFormat:@" videoMaxBitRate:%zi", self.videoMaxBitRate];
|
||||
[desc appendFormat:@" videoMinBitRate:%zi", self.videoMinBitRate];
|
||||
[desc appendFormat:@" avSessionPreset:%@", self.avSessionPreset];
|
||||
[desc appendFormat:@" sessionPreset:%zi", self.sessionPreset];
|
||||
[desc appendFormat:@" landscape:%zi", self.landscape];
|
||||
return desc;
|
||||
}
|
||||
|
||||
@end
|
||||
+7
-2
@@ -1,5 +1,10 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
#if __has_include(<GPUImage/GPUImage.h>)
|
||||
#import <GPUImage/GPUImage.h>
|
||||
#elif __has_include("GPUImage/GPUImage.h")
|
||||
#import "GPUImage/GPUImage.h"
|
||||
#else
|
||||
#import "GPUImage.h"
|
||||
#endif
|
||||
@interface LFGPUImageBeautyFilter : GPUImageFilter {
|
||||
}
|
||||
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform highp vec2 singleStepOffset;
|
||||
uniform highp vec4 params;
|
||||
uniform highp float brightness;
|
||||
|
||||
const highp vec3 W = vec3(0.299, 0.587, 0.114);
|
||||
const highp mat3 saturateMatrix = mat3(
|
||||
1.1102, -0.0598, -0.061,
|
||||
-0.0774, 1.0826, -0.1186,
|
||||
-0.0228, -0.0228, 1.1772);
|
||||
highp vec2 blurCoordinates[24];
|
||||
|
||||
highp float hardLight(highp float color) {
|
||||
if (color <= 0.5)
|
||||
color = color * color * 2.0;
|
||||
else
|
||||
color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0);
|
||||
return color;
|
||||
}
|
||||
|
||||
void main(){
|
||||
highp vec3 centralColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
blurCoordinates[0] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -10.0);
|
||||
blurCoordinates[1] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 10.0);
|
||||
blurCoordinates[2] = textureCoordinate.xy + singleStepOffset * vec2(-10.0, 0.0);
|
||||
blurCoordinates[3] = textureCoordinate.xy + singleStepOffset * vec2(10.0, 0.0);
|
||||
blurCoordinates[4] = textureCoordinate.xy + singleStepOffset * vec2(5.0, -8.0);
|
||||
blurCoordinates[5] = textureCoordinate.xy + singleStepOffset * vec2(5.0, 8.0);
|
||||
blurCoordinates[6] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, 8.0);
|
||||
blurCoordinates[7] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, -8.0);
|
||||
blurCoordinates[8] = textureCoordinate.xy + singleStepOffset * vec2(8.0, -5.0);
|
||||
blurCoordinates[9] = textureCoordinate.xy + singleStepOffset * vec2(8.0, 5.0);
|
||||
blurCoordinates[10] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, 5.0);
|
||||
blurCoordinates[11] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, -5.0);
|
||||
blurCoordinates[12] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -6.0);
|
||||
blurCoordinates[13] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 6.0);
|
||||
blurCoordinates[14] = textureCoordinate.xy + singleStepOffset * vec2(6.0, 0.0);
|
||||
blurCoordinates[15] = textureCoordinate.xy + singleStepOffset * vec2(-6.0, 0.0);
|
||||
blurCoordinates[16] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, -4.0);
|
||||
blurCoordinates[17] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, 4.0);
|
||||
blurCoordinates[18] = textureCoordinate.xy + singleStepOffset * vec2(4.0, -4.0);
|
||||
blurCoordinates[19] = textureCoordinate.xy + singleStepOffset * vec2(4.0, 4.0);
|
||||
blurCoordinates[20] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, -2.0);
|
||||
blurCoordinates[21] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, 2.0);
|
||||
blurCoordinates[22] = textureCoordinate.xy + singleStepOffset * vec2(2.0, -2.0);
|
||||
blurCoordinates[23] = textureCoordinate.xy + singleStepOffset * vec2(2.0, 2.0);
|
||||
|
||||
highp float sampleColor = centralColor.g * 22.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[0]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[1]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[2]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[3]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[4]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[5]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[6]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[7]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[8]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[9]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[10]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[11]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[12]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[13]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[14]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[15]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[16]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[17]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[18]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[19]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[20]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[21]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[22]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[23]).g * 3.0;
|
||||
|
||||
sampleColor = sampleColor / 62.0;
|
||||
|
||||
highp float highPass = centralColor.g - sampleColor + 0.5;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
highPass = hardLight(highPass);
|
||||
}
|
||||
highp float lumance = dot(centralColor, W);
|
||||
|
||||
highp float alpha = pow(lumance, params.r);
|
||||
|
||||
highp vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
|
||||
|
||||
smoothColor.r = clamp(pow(smoothColor.r, params.g), 0.0, 1.0);
|
||||
smoothColor.g = clamp(pow(smoothColor.g, params.g), 0.0, 1.0);
|
||||
smoothColor.b = clamp(pow(smoothColor.b, params.g), 0.0, 1.0);
|
||||
|
||||
highp vec3 lvse = vec3(1.0)-(vec3(1.0)-smoothColor)*(vec3(1.0)-centralColor);
|
||||
highp vec3 bianliang = max(smoothColor, centralColor);
|
||||
highp vec3 rouguang = 2.0*centralColor*smoothColor + centralColor*centralColor - 2.0*centralColor*centralColor*smoothColor;
|
||||
|
||||
gl_FragColor = vec4(mix(centralColor, lvse, alpha), 1.0);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, bianliang, alpha);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, rouguang, params.b);
|
||||
|
||||
highp vec3 satcolor = gl_FragColor.rgb * saturateMatrix;
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, satcolor, params.a);
|
||||
gl_FragColor.rgb = vec3(gl_FragColor.rgb + vec3(brightness));
|
||||
}
|
||||
|
||||
);
|
||||
#else
|
||||
NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mediump vec2 singleStepOffset;
|
||||
uniform mediump vec4 params;
|
||||
uniform mediump float brightness;
|
||||
const mediump mat3 saturateMatrix = mat3(
|
||||
1.1102, -0.0598, -0.061,
|
||||
-0.0774, 1.0826, -0.1186,
|
||||
-0.0228, -0.0228, 1.1772);
|
||||
const mediump vec3 W = vec3(0.299, 0.587, 0.114);
|
||||
mediump vec2 blurCoordinates[24];
|
||||
|
||||
mediump float hardLight(mediump float color){
|
||||
if (color <= 0.5)
|
||||
color = color * color * 2.0;
|
||||
else
|
||||
color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0);
|
||||
return color;
|
||||
}
|
||||
|
||||
void main(){
|
||||
mediump vec3 centralColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
blurCoordinates[0] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -10.0);
|
||||
blurCoordinates[1] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 10.0);
|
||||
blurCoordinates[2] = textureCoordinate.xy + singleStepOffset * vec2(-10.0, 0.0);
|
||||
blurCoordinates[3] = textureCoordinate.xy + singleStepOffset * vec2(10.0, 0.0);
|
||||
blurCoordinates[4] = textureCoordinate.xy + singleStepOffset * vec2(5.0, -8.0);
|
||||
blurCoordinates[5] = textureCoordinate.xy + singleStepOffset * vec2(5.0, 8.0);
|
||||
blurCoordinates[6] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, 8.0);
|
||||
blurCoordinates[7] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, -8.0);
|
||||
blurCoordinates[8] = textureCoordinate.xy + singleStepOffset * vec2(8.0, -5.0);
|
||||
blurCoordinates[9] = textureCoordinate.xy + singleStepOffset * vec2(8.0, 5.0);
|
||||
blurCoordinates[10] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, 5.0);
|
||||
blurCoordinates[11] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, -5.0);
|
||||
blurCoordinates[12] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -6.0);
|
||||
blurCoordinates[13] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 6.0);
|
||||
blurCoordinates[14] = textureCoordinate.xy + singleStepOffset * vec2(6.0, 0.0);
|
||||
blurCoordinates[15] = textureCoordinate.xy + singleStepOffset * vec2(-6.0, 0.0);
|
||||
blurCoordinates[16] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, -4.0);
|
||||
blurCoordinates[17] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, 4.0);
|
||||
blurCoordinates[18] = textureCoordinate.xy + singleStepOffset * vec2(4.0, -4.0);
|
||||
blurCoordinates[19] = textureCoordinate.xy + singleStepOffset * vec2(4.0, 4.0);
|
||||
blurCoordinates[20] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, -2.0);
|
||||
blurCoordinates[21] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, 2.0);
|
||||
blurCoordinates[22] = textureCoordinate.xy + singleStepOffset * vec2(2.0, -2.0);
|
||||
blurCoordinates[23] = textureCoordinate.xy + singleStepOffset * vec2(2.0, 2.0);
|
||||
|
||||
mediump float sampleColor = centralColor.g * 22.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[0]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[1]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[2]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[3]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[4]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[5]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[6]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[7]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[8]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[9]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[10]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[11]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[12]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[13]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[14]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[15]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[16]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[17]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[18]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[19]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[20]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[21]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[22]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[23]).g * 3.0;
|
||||
|
||||
sampleColor = sampleColor / 62.0;
|
||||
|
||||
mediump float highPass = centralColor.g - sampleColor + 0.5;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
highPass = hardLight(highPass);
|
||||
}
|
||||
mediump float luminance = dot(centralColor, W);
|
||||
|
||||
mediump float alpha = pow(luminance, params);
|
||||
|
||||
mediump vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
|
||||
|
||||
smoothColor.r = clamp(pow(smoothColor.r, params.g), 0.0, 1.0);
|
||||
smoothColor.g = clamp(pow(smoothColor.g, params.g), 0.0, 1.0);
|
||||
smoothColor.b = clamp(pow(smoothColor.b, params.g), 0.0, 1.0);
|
||||
|
||||
mediump vec3 lvse = vec3(1.0)-(vec3(1.0)-smoothColor)*(vec3(1.0)-centralColor);
|
||||
mediump vec3 bianliang = max(smoothColor, centralColor);
|
||||
mediump vec3 rouguang = 2.0*centralColor*smoothColor + centralColor*centralColor - 2.0*centralColor*centralColor*smoothColor;
|
||||
|
||||
gl_FragColor = vec4(mix(centralColor, lvse, alpha), 1.0);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, bianliang, alpha);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, rouguang, params.b);
|
||||
|
||||
mediump vec3 satcolor = gl_FragColor.rgb * saturateMatrix;
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, satcolor, params.a);
|
||||
gl_FragColor.rgb = vec3(gl_FragColor.rgb + vec3(brightness));
|
||||
}
|
||||
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation LFGPUImageBeautyFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageBeautyFragmentShaderString])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_toneLevel = 0.5;
|
||||
_beautyLevel = 0.5;
|
||||
_brightLevel = 0.5;
|
||||
[self setParams:_beautyLevel tone:_toneLevel];
|
||||
[self setBrightLevel:_brightLevel];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex {
|
||||
[super setInputSize:newSize atIndex:textureIndex];
|
||||
inputTextureSize = newSize;
|
||||
|
||||
CGPoint offset = CGPointMake(2.0f / inputTextureSize.width, 2.0 / inputTextureSize.height);
|
||||
[self setPoint:offset forUniformName:@"singleStepOffset"];
|
||||
}
|
||||
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel {
|
||||
_beautyLevel = beautyLevel;
|
||||
[self setParams:_beautyLevel tone:_toneLevel];
|
||||
}
|
||||
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel {
|
||||
_brightLevel = brightLevel;
|
||||
[self setFloat:0.6 * (-0.5 + brightLevel) forUniformName:@"brightness"];
|
||||
}
|
||||
|
||||
- (void)setParams:(CGFloat)beauty tone:(CGFloat)tone {
|
||||
GPUVector4 fBeautyParam;
|
||||
fBeautyParam.one = 1.0 - 0.6 * beauty;
|
||||
fBeautyParam.two = 1.0 - 0.3 * beauty;
|
||||
fBeautyParam.three = 0.1 + 0.3 * tone;
|
||||
fBeautyParam.four = 0.1 + 0.3 * tone;
|
||||
[self setFloatVec4:fBeautyParam forUniform:@"params"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#if __has_include(<GPUImage/GPUImage.h>)
|
||||
#import <GPUImage/GPUImage.h>
|
||||
#elif __has_include("GPUImage/GPUImage.h")
|
||||
#import "GPUImage/GPUImage.h"
|
||||
#else
|
||||
#import "GPUImage.h"
|
||||
#endif
|
||||
|
||||
@interface LFGPUImageEmptyFilter : GPUImageFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
#import "LFGPUImageEmptyFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kLFGPUImageEmptyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main(){
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((textureColor.rgb), textureColor.w);
|
||||
}
|
||||
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageInvertFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main(){
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((textureColor.rgb), textureColor.w);
|
||||
}
|
||||
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation LFGPUImageEmptyFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageEmptyFragmentShaderString])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
@interface LFFrame : NSObject
|
||||
|
||||
@property (nonatomic, assign) uint64_t timestamp;
|
||||
@property (nonatomic, assign,) uint64_t timestamp;
|
||||
@property (nonatomic, strong) NSData *data;
|
||||
///< flv或者rtmp包头
|
||||
@property (nonatomic, strong) NSData *header;
|
||||
@@ -16,11 +16,15 @@
|
||||
@property (nonatomic, assign) CGSize videoSize; ///< 上传的分辨率
|
||||
@property (nonatomic, assign) BOOL isRtmp; ///< 上传方式(TCP or RTMP)
|
||||
|
||||
@property (nonatomic, assign) CGFloat elapsedMilli; ///< 距离上次统计的时间 单位ms
|
||||
@property (nonatomic, assign) CGFloat timeStamp; ///< 当前的时间戳,从而计算1s内数据
|
||||
@property (nonatomic, assign) CGFloat dataFlow; ///< 总流量
|
||||
@property (nonatomic, assign) CGFloat bandwidth; ///< 1s内总带宽
|
||||
@property (nonatomic, assign) CGFloat currentBandwidth; ///< 上次的带宽
|
||||
|
||||
@property (nonatomic, assign) NSInteger dropFrame; ///< 丢掉的帧数
|
||||
@property (nonatomic, assign) NSInteger totalFrame; ///< 总帧数
|
||||
|
||||
@property (nonatomic, assign) NSInteger capturedAudioCount; ///< 1s内音频捕获个数
|
||||
@property (nonatomic, assign) NSInteger capturedVideoCount; ///< 1s内视频捕获个数
|
||||
@property (nonatomic, assign) NSInteger currentCapturedAudioCount; ///< 上次的音频捕获个数
|
||||
+9
-8
@@ -11,7 +11,7 @@
|
||||
#import "LFLiveVideoConfiguration.h"
|
||||
|
||||
/// 流状态
|
||||
typedef NS_ENUM(NSUInteger, LFLiveState){
|
||||
typedef NS_ENUM (NSUInteger, LFLiveState){
|
||||
/// 准备
|
||||
LFLiveReady = 0,
|
||||
/// 连接中
|
||||
@@ -24,12 +24,12 @@ typedef NS_ENUM(NSUInteger, LFLiveState){
|
||||
LFLiveError = 4
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger,LFLiveSocketErrorCode) {
|
||||
LFLiveSocketError_PreView = 201,///< 预览失败
|
||||
LFLiveSocketError_GetStreamInfo = 202,///< 获取流媒体信息失败
|
||||
LFLiveSocketError_ConnectSocket = 203,///< 连接socket失败
|
||||
LFLiveSocketError_Verification = 204,///< 验证服务器失败
|
||||
LFLiveSocketError_ReConnectTimeOut = 205///< 重新连接服务器超时
|
||||
typedef NS_ENUM (NSUInteger, LFLiveSocketErrorCode) {
|
||||
LFLiveSocketError_PreView = 201, ///< 预览失败
|
||||
LFLiveSocketError_GetStreamInfo = 202, ///< 获取流媒体信息失败
|
||||
LFLiveSocketError_ConnectSocket = 203, ///< 连接socket失败
|
||||
LFLiveSocketError_Verification = 204, ///< 验证服务器失败
|
||||
LFLiveSocketError_ReConnectTimeOut = 205 ///< 重新连接服务器超时
|
||||
};
|
||||
|
||||
@interface LFLiveStreamInfo : NSObject
|
||||
@@ -45,6 +45,7 @@ typedef NS_ENUM(NSUInteger,LFLiveSocketErrorCode) {
|
||||
@property (nonatomic, strong) LFLiveAudioConfiguration *audioConfiguration;
|
||||
///视频配置
|
||||
@property (nonatomic, strong) LFLiveVideoConfiguration *videoConfiguration;
|
||||
|
||||
///是否丢帧
|
||||
@property (nonatomic, assign) BOOL needDropFrame;
|
||||
|
||||
@end
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// LFStreamRtmpSocket.h
|
||||
// LFStreamRTMPSocket.h
|
||||
// LaiFeng
|
||||
//
|
||||
// Created by admin on 16/5/18.
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import "LFStreamSocket.h"
|
||||
|
||||
@interface LFStreamRtmpSocket : NSObject<LFStreamSocket>
|
||||
@interface LFStreamRTMPSocket : NSObject<LFStreamSocket>
|
||||
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
@@ -0,0 +1,567 @@
|
||||
//
|
||||
// LFStreamRTMPSocket.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by admin on 16/5/18.
|
||||
// Copyright © 2016年 live Interactive. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFStreamRTMPSocket.h"
|
||||
|
||||
#if __has_include(<pili-librtmp/rtmp.h>)
|
||||
#import <pili-librtmp/rtmp.h>
|
||||
#elif __has_include("pili-librtmp/rtmp.h")
|
||||
#import "pili-librtmp/rtmp.h"
|
||||
#else
|
||||
#import "rtmp.h"
|
||||
#endif
|
||||
|
||||
static const NSInteger RetryTimesBreaken = 20; ///< 重连1分钟 3秒一次 一共20次
|
||||
static const NSInteger RetryTimesMargin = 3;
|
||||
|
||||
|
||||
#define RTMP_RECEIVE_TIMEOUT 2
|
||||
#define DATA_ITEMS_MAX_COUNT 100
|
||||
#define RTMP_DATA_RESERVE_SIZE 400
|
||||
#define RTMP_HEAD_SIZE (sizeof(RTMPPacket) + RTMP_MAX_HEADER_SIZE)
|
||||
|
||||
#define SAVC(x) static const AVal av_ ## x = AVC(#x)
|
||||
|
||||
static const AVal av_setDataFrame = AVC("@setDataFrame");
|
||||
static const AVal av_SDKVersion = AVC("LFLiveKit 1.8.0");
|
||||
SAVC(onMetaData);
|
||||
SAVC(duration);
|
||||
SAVC(width);
|
||||
SAVC(height);
|
||||
SAVC(videocodecid);
|
||||
SAVC(videodatarate);
|
||||
SAVC(framerate);
|
||||
SAVC(audiocodecid);
|
||||
SAVC(audiodatarate);
|
||||
SAVC(audiosamplerate);
|
||||
SAVC(audiosamplesize);
|
||||
//SAVC(audiochannels);
|
||||
SAVC(stereo);
|
||||
SAVC(encoder);
|
||||
//SAVC(av_stereo);
|
||||
SAVC(fileSize);
|
||||
SAVC(avc1);
|
||||
SAVC(mp4a);
|
||||
|
||||
@interface LFStreamRTMPSocket ()<LFStreamingBufferDelegate>
|
||||
{
|
||||
PILI_RTMP *_rtmp;
|
||||
}
|
||||
@property (nonatomic, weak) id<LFStreamSocketDelegate> delegate;
|
||||
@property (nonatomic, strong) LFLiveStreamInfo *stream;
|
||||
@property (nonatomic, strong) LFStreamingBuffer *buffer;
|
||||
@property (nonatomic, strong) LFLiveDebug *debugInfo;
|
||||
@property (nonatomic, strong) dispatch_queue_t rtmpSendQueue;
|
||||
//错误信息
|
||||
@property (nonatomic, assign) RTMPError error;
|
||||
@property (nonatomic, assign) NSInteger retryTimes4netWorkBreaken;
|
||||
@property (nonatomic, assign) NSInteger reconnectInterval;
|
||||
@property (nonatomic, assign) NSInteger reconnectCount;
|
||||
|
||||
@property (atomic, assign) BOOL isSending;
|
||||
@property (nonatomic, assign) BOOL isConnected;
|
||||
@property (nonatomic, assign) BOOL isConnecting;
|
||||
@property (nonatomic, assign) BOOL isReconnecting;
|
||||
|
||||
@property (nonatomic, assign) BOOL sendVideoHead;
|
||||
@property (nonatomic, assign) BOOL sendAudioHead;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFStreamRTMPSocket
|
||||
|
||||
#pragma mark -- LFStreamSocket
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo *)stream{
|
||||
return [self initWithStream:stream reconnectInterval:0 reconnectCount:0];
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo *)stream reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount{
|
||||
if (!stream) @throw [NSException exceptionWithName:@"LFStreamRtmpSocket init error" reason:@"stream is nil" userInfo:nil];
|
||||
if (self = [super init]) {
|
||||
_stream = stream;
|
||||
if (reconnectInterval > 0) _reconnectInterval = reconnectInterval;
|
||||
else _reconnectInterval = RetryTimesMargin;
|
||||
|
||||
if (reconnectCount > 0) _reconnectCount = reconnectCount;
|
||||
else _reconnectCount = RetryTimesBreaken;
|
||||
|
||||
[self addObserver:self forKeyPath:@"isSending" options:NSKeyValueObservingOptionNew context:nil];//这里改成observer主要考虑一直到发送出错情况下,可以继续发送
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
[self removeObserver:self forKeyPath:@"isSending"];
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
dispatch_async(self.rtmpSendQueue, ^{
|
||||
[self _start];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_start {
|
||||
if (!_stream) return;
|
||||
if (_isConnecting) return;
|
||||
if (_rtmp != NULL) return;
|
||||
self.debugInfo.streamId = self.stream.streamId;
|
||||
self.debugInfo.uploadUrl = self.stream.url;
|
||||
self.debugInfo.isRtmp = YES;
|
||||
[self RTMP264_Connect:(char *)[_stream.url cStringUsingEncoding:NSASCIIStringEncoding]];
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
dispatch_async(self.rtmpSendQueue, ^{
|
||||
[self _stop];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_stop {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]) {
|
||||
[self.delegate socketStatus:self status:LFLiveStop];
|
||||
}
|
||||
if (_rtmp != NULL) {
|
||||
PILI_RTMP_Close(_rtmp, &_error);
|
||||
PILI_RTMP_Free(_rtmp);
|
||||
_rtmp = NULL;
|
||||
}
|
||||
[self clean];
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
}
|
||||
|
||||
- (void)sendFrame:(LFFrame *)frame {
|
||||
if (!frame) return;
|
||||
[self.buffer appendObject:frame];
|
||||
if(!self.isSending){
|
||||
[self sendFrame];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<LFStreamSocketDelegate>)delegate {
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (void)sendFrame {
|
||||
dispatch_async(self.rtmpSendQueue, ^{
|
||||
if (!self.isSending && self.buffer.list.count > 0) {
|
||||
self.isSending = YES;
|
||||
|
||||
if (!_isConnected || _isReconnecting || _isConnecting || !_rtmp){
|
||||
self.isSending = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用发送接口
|
||||
LFFrame *frame = [self.buffer popFirstObject];
|
||||
if ([frame isKindOfClass:[LFVideoFrame class]]) {
|
||||
if (!self.sendVideoHead) {
|
||||
self.sendVideoHead = YES;
|
||||
if(!((LFVideoFrame*)frame).sps || !((LFVideoFrame*)frame).pps){
|
||||
self.isSending = NO;
|
||||
return;
|
||||
}
|
||||
[self sendVideoHeader:(LFVideoFrame *)frame];
|
||||
} else {
|
||||
[self sendVideo:(LFVideoFrame *)frame];
|
||||
}
|
||||
} else {
|
||||
if (!self.sendAudioHead) {
|
||||
self.sendAudioHead = YES;
|
||||
if(!((LFAudioFrame*)frame).audioInfo){
|
||||
self.isSending = NO;
|
||||
return;
|
||||
}
|
||||
[self sendAudioHeader:(LFAudioFrame *)frame];
|
||||
} else {
|
||||
[self sendAudio:frame];
|
||||
}
|
||||
}
|
||||
|
||||
//debug更新
|
||||
self.debugInfo.totalFrame++;
|
||||
self.debugInfo.dropFrame += self.buffer.lastDropFrames;
|
||||
self.buffer.lastDropFrames = 0;
|
||||
|
||||
self.debugInfo.dataFlow += frame.data.length;
|
||||
self.debugInfo.elapsedMilli = CACurrentMediaTime() * 1000 - self.debugInfo.timeStamp;
|
||||
if (self.debugInfo.elapsedMilli < 1000) {
|
||||
self.debugInfo.bandwidth += frame.data.length;
|
||||
if ([frame isKindOfClass:[LFAudioFrame class]]) {
|
||||
self.debugInfo.capturedAudioCount++;
|
||||
} else {
|
||||
self.debugInfo.capturedVideoCount++;
|
||||
}
|
||||
|
||||
self.debugInfo.unSendCount = self.buffer.list.count;
|
||||
} else {
|
||||
self.debugInfo.currentBandwidth = self.debugInfo.bandwidth;
|
||||
self.debugInfo.currentCapturedAudioCount = self.debugInfo.capturedAudioCount;
|
||||
self.debugInfo.currentCapturedVideoCount = self.debugInfo.capturedVideoCount;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(socketDebug:debugInfo:)]) {
|
||||
[self.delegate socketDebug:self debugInfo:self.debugInfo];
|
||||
}
|
||||
self.debugInfo.bandwidth = 0;
|
||||
self.debugInfo.capturedAudioCount = 0;
|
||||
self.debugInfo.capturedVideoCount = 0;
|
||||
self.debugInfo.timeStamp = CACurrentMediaTime() * 1000;
|
||||
}
|
||||
|
||||
//修改发送状态
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
//< 这里只为了不循环调用sendFrame方法 调用栈是保证先出栈再进栈
|
||||
self.isSending = NO;
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)clean {
|
||||
_isConnecting = NO;
|
||||
_isReconnecting = NO;
|
||||
_isSending = NO;
|
||||
_isConnected = NO;
|
||||
_sendAudioHead = NO;
|
||||
_sendVideoHead = NO;
|
||||
self.debugInfo = nil;
|
||||
[self.buffer removeAllObject];
|
||||
self.retryTimes4netWorkBreaken = 0;
|
||||
}
|
||||
|
||||
- (NSInteger)RTMP264_Connect:(char *)push_url {
|
||||
//由于摄像头的timestamp是一直在累加,需要每次得到相对时间戳
|
||||
//分配与初始化
|
||||
if (_isConnecting) return -1;
|
||||
|
||||
_isConnecting = YES;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]) {
|
||||
[self.delegate socketStatus:self status:LFLivePending];
|
||||
}
|
||||
|
||||
if (_rtmp != NULL) {
|
||||
PILI_RTMP_Close(_rtmp, &_error);
|
||||
PILI_RTMP_Free(_rtmp);
|
||||
}
|
||||
|
||||
_rtmp = PILI_RTMP_Alloc();
|
||||
PILI_RTMP_Init(_rtmp);
|
||||
|
||||
//设置URL
|
||||
if (PILI_RTMP_SetupURL(_rtmp, push_url, &_error) == FALSE) {
|
||||
//log(LOG_ERR, "RTMP_SetupURL() failed!");
|
||||
goto Failed;
|
||||
}
|
||||
|
||||
_rtmp->m_errorCallback = RTMPErrorCallback;
|
||||
_rtmp->m_connCallback = ConnectionTimeCallback;
|
||||
_rtmp->m_userData = (__bridge void *)self;
|
||||
_rtmp->m_msgCounter = 1;
|
||||
_rtmp->Link.timeout = RTMP_RECEIVE_TIMEOUT;
|
||||
|
||||
//设置可写,即发布流,这个函数必须在连接前使用,否则无效
|
||||
PILI_RTMP_EnableWrite(_rtmp);
|
||||
|
||||
//连接服务器
|
||||
if (PILI_RTMP_Connect(_rtmp, NULL, &_error) == FALSE) {
|
||||
goto Failed;
|
||||
}
|
||||
|
||||
//连接流
|
||||
if (PILI_RTMP_ConnectStream(_rtmp, 0, &_error) == FALSE) {
|
||||
goto Failed;
|
||||
}
|
||||
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]) {
|
||||
[self.delegate socketStatus:self status:LFLiveStart];
|
||||
}
|
||||
|
||||
[self sendMetaData];
|
||||
|
||||
_isConnected = YES;
|
||||
_isConnecting = NO;
|
||||
_isReconnecting = NO;
|
||||
_isSending = NO;
|
||||
_retryTimes4netWorkBreaken = 0;
|
||||
return 0;
|
||||
|
||||
Failed:
|
||||
PILI_RTMP_Close(_rtmp, &_error);
|
||||
PILI_RTMP_Free(_rtmp);
|
||||
_rtmp = NULL;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidError:errorCode:)]) {
|
||||
[self.delegate socketDidError:self errorCode:LFLiveSocketError_ConnectSocket];
|
||||
}
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]) {
|
||||
[self.delegate socketStatus:self status:LFLiveError];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#pragma mark -- Rtmp Send
|
||||
|
||||
- (void)sendMetaData {
|
||||
PILI_RTMPPacket packet;
|
||||
|
||||
char pbuf[2048], *pend = pbuf + sizeof(pbuf);
|
||||
|
||||
packet.m_nChannel = 0x03; // control channel (invoke)
|
||||
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
|
||||
packet.m_packetType = RTMP_PACKET_TYPE_INFO;
|
||||
packet.m_nTimeStamp = 0;
|
||||
packet.m_nInfoField2 = _rtmp->m_stream_id;
|
||||
packet.m_hasAbsTimestamp = TRUE;
|
||||
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
|
||||
|
||||
char *enc = packet.m_body;
|
||||
enc = AMF_EncodeString(enc, pend, &av_setDataFrame);
|
||||
enc = AMF_EncodeString(enc, pend, &av_onMetaData);
|
||||
|
||||
*enc++ = AMF_OBJECT;
|
||||
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_duration, 0.0);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_fileSize, 0.0);
|
||||
|
||||
// videosize
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_width, _stream.videoConfiguration.videoSize.width);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_height, _stream.videoConfiguration.videoSize.height);
|
||||
|
||||
// video
|
||||
enc = AMF_EncodeNamedString(enc, pend, &av_videocodecid, &av_avc1);
|
||||
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_videodatarate, _stream.videoConfiguration.videoBitRate / 1000.f);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_framerate, _stream.videoConfiguration.videoFrameRate);
|
||||
|
||||
// audio
|
||||
enc = AMF_EncodeNamedString(enc, pend, &av_audiocodecid, &av_mp4a);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiodatarate, _stream.audioConfiguration.audioBitrate);
|
||||
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiosamplerate, _stream.audioConfiguration.audioSampleRate);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiosamplesize, 16.0);
|
||||
enc = AMF_EncodeNamedBoolean(enc, pend, &av_stereo, _stream.audioConfiguration.numberOfChannels == 2);
|
||||
|
||||
// sdk version
|
||||
enc = AMF_EncodeNamedString(enc, pend, &av_encoder, &av_SDKVersion);
|
||||
|
||||
*enc++ = 0;
|
||||
*enc++ = 0;
|
||||
*enc++ = AMF_OBJECT_END;
|
||||
|
||||
packet.m_nBodySize = (uint32_t)(enc - packet.m_body);
|
||||
if (!PILI_RTMP_SendPacket(_rtmp, &packet, FALSE, &_error)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendVideoHeader:(LFVideoFrame *)videoFrame {
|
||||
|
||||
unsigned char *body = NULL;
|
||||
NSInteger iIndex = 0;
|
||||
NSInteger rtmpLength = 1024;
|
||||
const char *sps = videoFrame.sps.bytes;
|
||||
const char *pps = videoFrame.pps.bytes;
|
||||
NSInteger sps_len = videoFrame.sps.length;
|
||||
NSInteger pps_len = videoFrame.pps.length;
|
||||
|
||||
body = (unsigned char *)malloc(rtmpLength);
|
||||
memset(body, 0, rtmpLength);
|
||||
|
||||
body[iIndex++] = 0x17;
|
||||
body[iIndex++] = 0x00;
|
||||
|
||||
body[iIndex++] = 0x00;
|
||||
body[iIndex++] = 0x00;
|
||||
body[iIndex++] = 0x00;
|
||||
|
||||
body[iIndex++] = 0x01;
|
||||
body[iIndex++] = sps[1];
|
||||
body[iIndex++] = sps[2];
|
||||
body[iIndex++] = sps[3];
|
||||
body[iIndex++] = 0xff;
|
||||
|
||||
/*sps*/
|
||||
body[iIndex++] = 0xe1;
|
||||
body[iIndex++] = (sps_len >> 8) & 0xff;
|
||||
body[iIndex++] = sps_len & 0xff;
|
||||
memcpy(&body[iIndex], sps, sps_len);
|
||||
iIndex += sps_len;
|
||||
|
||||
/*pps*/
|
||||
body[iIndex++] = 0x01;
|
||||
body[iIndex++] = (pps_len >> 8) & 0xff;
|
||||
body[iIndex++] = (pps_len) & 0xff;
|
||||
memcpy(&body[iIndex], pps, pps_len);
|
||||
iIndex += pps_len;
|
||||
|
||||
[self sendPacket:RTMP_PACKET_TYPE_VIDEO data:body size:iIndex nTimestamp:0];
|
||||
free(body);
|
||||
}
|
||||
|
||||
- (void)sendVideo:(LFVideoFrame *)frame {
|
||||
|
||||
NSInteger i = 0;
|
||||
NSInteger rtmpLength = frame.data.length + 9;
|
||||
unsigned char *body = (unsigned char *)malloc(rtmpLength);
|
||||
memset(body, 0, rtmpLength);
|
||||
|
||||
if (frame.isKeyFrame) {
|
||||
body[i++] = 0x17; // 1:Iframe 7:AVC
|
||||
} else {
|
||||
body[i++] = 0x27; // 2:Pframe 7:AVC
|
||||
}
|
||||
body[i++] = 0x01; // AVC NALU
|
||||
body[i++] = 0x00;
|
||||
body[i++] = 0x00;
|
||||
body[i++] = 0x00;
|
||||
body[i++] = (frame.data.length >> 24) & 0xff;
|
||||
body[i++] = (frame.data.length >> 16) & 0xff;
|
||||
body[i++] = (frame.data.length >> 8) & 0xff;
|
||||
body[i++] = (frame.data.length) & 0xff;
|
||||
memcpy(&body[i], frame.data.bytes, frame.data.length);
|
||||
|
||||
[self sendPacket:RTMP_PACKET_TYPE_VIDEO data:body size:(rtmpLength) nTimestamp:frame.timestamp];
|
||||
free(body);
|
||||
}
|
||||
|
||||
- (NSInteger)sendPacket:(unsigned int)nPacketType data:(unsigned char *)data size:(NSInteger)size nTimestamp:(uint64_t)nTimestamp {
|
||||
NSInteger rtmpLength = size;
|
||||
PILI_RTMPPacket rtmp_pack;
|
||||
PILI_RTMPPacket_Reset(&rtmp_pack);
|
||||
PILI_RTMPPacket_Alloc(&rtmp_pack, (uint32_t)rtmpLength);
|
||||
|
||||
rtmp_pack.m_nBodySize = (uint32_t)size;
|
||||
memcpy(rtmp_pack.m_body, data, size);
|
||||
rtmp_pack.m_hasAbsTimestamp = 0;
|
||||
rtmp_pack.m_packetType = nPacketType;
|
||||
if (_rtmp) rtmp_pack.m_nInfoField2 = _rtmp->m_stream_id;
|
||||
rtmp_pack.m_nChannel = 0x04;
|
||||
rtmp_pack.m_headerType = RTMP_PACKET_SIZE_LARGE;
|
||||
if (RTMP_PACKET_TYPE_AUDIO == nPacketType && size != 4) {
|
||||
rtmp_pack.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
|
||||
}
|
||||
rtmp_pack.m_nTimeStamp = (uint32_t)nTimestamp;
|
||||
|
||||
NSInteger nRet = [self RtmpPacketSend:&rtmp_pack];
|
||||
|
||||
PILI_RTMPPacket_Free(&rtmp_pack);
|
||||
return nRet;
|
||||
}
|
||||
|
||||
- (NSInteger)RtmpPacketSend:(PILI_RTMPPacket *)packet {
|
||||
if (_rtmp && PILI_RTMP_IsConnected(_rtmp)) {
|
||||
int success = PILI_RTMP_SendPacket(_rtmp, packet, 0, &_error);
|
||||
return success;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (void)sendAudioHeader:(LFAudioFrame *)audioFrame {
|
||||
|
||||
NSInteger rtmpLength = audioFrame.audioInfo.length + 2; /*spec data长度,一般是2*/
|
||||
unsigned char *body = (unsigned char *)malloc(rtmpLength);
|
||||
memset(body, 0, rtmpLength);
|
||||
|
||||
/*AF 00 + AAC RAW data*/
|
||||
body[0] = 0xAF;
|
||||
body[1] = 0x00;
|
||||
memcpy(&body[2], audioFrame.audioInfo.bytes, audioFrame.audioInfo.length); /*spec_buf是AAC sequence header数据*/
|
||||
[self sendPacket:RTMP_PACKET_TYPE_AUDIO data:body size:rtmpLength nTimestamp:0];
|
||||
free(body);
|
||||
}
|
||||
|
||||
- (void)sendAudio:(LFFrame *)frame {
|
||||
|
||||
NSInteger rtmpLength = frame.data.length + 2; /*spec data长度,一般是2*/
|
||||
unsigned char *body = (unsigned char *)malloc(rtmpLength);
|
||||
memset(body, 0, rtmpLength);
|
||||
|
||||
/*AF 01 + AAC RAW data*/
|
||||
body[0] = 0xAF;
|
||||
body[1] = 0x01;
|
||||
memcpy(&body[2], frame.data.bytes, frame.data.length);
|
||||
[self sendPacket:RTMP_PACKET_TYPE_AUDIO data:body size:rtmpLength nTimestamp:frame.timestamp];
|
||||
free(body);
|
||||
}
|
||||
|
||||
// 断线重连
|
||||
- (void)reconnect {
|
||||
dispatch_async(self.rtmpSendQueue, ^{
|
||||
_isReconnecting = NO;
|
||||
if (_isConnected) return;
|
||||
|
||||
[self _stop];
|
||||
[self _start];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -- CallBack
|
||||
void RTMPErrorCallback(RTMPError *error, void *userData) {
|
||||
LFStreamRTMPSocket *socket = (__bridge LFStreamRTMPSocket *)userData;
|
||||
if (error->code < 0) {
|
||||
if (socket.retryTimes4netWorkBreaken++ < socket.reconnectCount && !socket.isReconnecting) {
|
||||
socket.isConnected = NO;
|
||||
socket.isConnecting = NO;
|
||||
socket.isReconnecting = YES;
|
||||
[socket performSelectorOnMainThread:@selector(reconnect) withObject:nil waitUntilDone:socket.reconnectInterval];
|
||||
} else if (socket.retryTimes4netWorkBreaken >= socket.reconnectCount) {
|
||||
if (socket.delegate && [socket.delegate respondsToSelector:@selector(socketStatus:status:)]) {
|
||||
[socket.delegate socketStatus:socket status:LFLiveError];
|
||||
}
|
||||
if (socket.delegate && [socket.delegate respondsToSelector:@selector(socketDidError:errorCode:)]) {
|
||||
[socket.delegate socketDidError:socket errorCode:LFLiveSocketError_ReConnectTimeOut];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionTimeCallback(PILI_CONNECTION_TIME *conn_time, void *userData) {
|
||||
//LFStreamRTMPSocket *socket = (__bridge LFStreamRTMPSocket*)userData;
|
||||
}
|
||||
|
||||
#pragma mark -- LFStreamingBufferDelegate
|
||||
- (void)streamingBuffer:(nullable LFStreamingBuffer *)buffer bufferState:(LFLiveBuffferState)state{
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketBufferStatus:status:)]){
|
||||
[self.delegate socketBufferStatus:self status:state];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- Observer
|
||||
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
|
||||
if([keyPath isEqualToString:@"isSending"]){
|
||||
if(!self.isSending){
|
||||
[self sendFrame];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- Getter Setter
|
||||
|
||||
- (LFStreamingBuffer *)buffer {
|
||||
if (!_buffer) {
|
||||
_buffer = [[LFStreamingBuffer alloc] init];
|
||||
_buffer.delegate = self;
|
||||
_buffer.needDropFrame = self.stream.needDropFrame;
|
||||
}
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
- (LFLiveDebug *)debugInfo {
|
||||
if (!_debugInfo) {
|
||||
_debugInfo = [[LFLiveDebug alloc] init];
|
||||
}
|
||||
return _debugInfo;
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)rtmpSendQueue{
|
||||
if(!_rtmpSendQueue){
|
||||
_rtmpSendQueue = dispatch_queue_create("com.youku.LaiFeng.RtmpSendQueue", NULL);
|
||||
}
|
||||
return _rtmpSendQueue;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// LFStreamSocket.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by admin on 16/5/3.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFStreamingBuffer.h"
|
||||
#import "LFLiveDebug.h"
|
||||
|
||||
@protocol LFStreamSocket;
|
||||
@protocol LFStreamSocketDelegate <NSObject>
|
||||
|
||||
/** callback buffer current status (回调当前缓冲区情况,可实现相关切换帧率 码率等策略)*/
|
||||
- (void)socketBufferStatus:(nullable id <LFStreamSocket>)socket status:(LFLiveBuffferState)status;
|
||||
/** callback socket current status (回调当前网络情况) */
|
||||
- (void)socketStatus:(nullable id <LFStreamSocket>)socket status:(LFLiveState)status;
|
||||
/** callback socket errorcode */
|
||||
- (void)socketDidError:(nullable id <LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode;
|
||||
@optional
|
||||
/** callback debugInfo */
|
||||
- (void)socketDebug:(nullable id <LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug *)debugInfo;
|
||||
@end
|
||||
|
||||
@protocol LFStreamSocket <NSObject>
|
||||
- (void)start;
|
||||
- (void)stop;
|
||||
- (void)sendFrame:(nullable LFFrame *)frame;
|
||||
- (void)setDelegate:(nullable id <LFStreamSocketDelegate>)delegate;
|
||||
@optional
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo *)stream;
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo *)stream reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount;
|
||||
@end
|
||||
+14
-8
@@ -11,10 +11,10 @@
|
||||
#import "LFVideoFrame.h"
|
||||
|
||||
/** current buffer status */
|
||||
typedef NS_ENUM(NSUInteger, LFLiveBuffferState) {
|
||||
typedef NS_ENUM (NSUInteger, LFLiveBuffferState) {
|
||||
LFLiveBuffferUnknown = 0, //< 未知
|
||||
LFLiveBuffferIncrease = 1, //< 缓冲区状态好可以增加码率
|
||||
LFLiveBuffferDecline = 2 //< 缓冲区状态差应该降低码率
|
||||
LFLiveBuffferIncrease = 1, //< 缓冲区状态差应该降低码率
|
||||
LFLiveBuffferDecline = 2 //< 缓冲区状态好应该提升码率
|
||||
};
|
||||
|
||||
@class LFStreamingBuffer;
|
||||
@@ -22,25 +22,31 @@ typedef NS_ENUM(NSUInteger, LFLiveBuffferState) {
|
||||
@protocol LFStreamingBufferDelegate <NSObject>
|
||||
@optional
|
||||
/** 当前buffer变动(增加or减少) 根据buffer中的updateInterval时间回调*/
|
||||
- (void)streamingBuffer:(nullable LFStreamingBuffer * )buffer bufferState:(LFLiveBuffferState)state;
|
||||
- (void)streamingBuffer:(nullable LFStreamingBuffer *)buffer bufferState:(LFLiveBuffferState)state;
|
||||
@end
|
||||
|
||||
@interface LFStreamingBuffer : NSObject
|
||||
|
||||
/** The needDropFrame control Dynamic frame loss ,default is YES */
|
||||
@property (nonatomic, assign) BOOL needDropFrame;
|
||||
|
||||
/** The delegate of the buffer. buffer callback */
|
||||
@property (nullable,nonatomic, weak) id <LFStreamingBufferDelegate> delegate;
|
||||
@property (nullable, nonatomic, weak) id <LFStreamingBufferDelegate> delegate;
|
||||
|
||||
/** current frame buffer */
|
||||
@property (nonatomic, strong, readonly) NSMutableArray <LFFrame*>* _Nonnull list;
|
||||
@property (nonatomic, strong, readonly) NSMutableArray <LFFrame *> *_Nonnull list;
|
||||
|
||||
/** buffer count max size default 1000 */
|
||||
@property (nonatomic, assign) NSUInteger maxCount;
|
||||
|
||||
/** count of drop frames in last time */
|
||||
@property (nonatomic, assign) NSInteger lastDropFrames;
|
||||
|
||||
/** add frame to buffer */
|
||||
- (void)appendObject:(nullable LFFrame*)frame;
|
||||
- (void)appendObject:(nullable LFFrame *)frame;
|
||||
|
||||
/** pop the first frome buffer */
|
||||
- (nullable LFFrame*)popFirstObject;
|
||||
- (nullable LFFrame *)popFirstObject;
|
||||
|
||||
/** remove all objects from Buffer */
|
||||
- (void)removeAllObject;
|
||||
+82
-72
@@ -18,8 +18,8 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
|
||||
dispatch_semaphore_t _lock;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray <LFFrame*>*sortList;
|
||||
@property (nonatomic, strong, readwrite) NSMutableArray <LFFrame*>*list;
|
||||
@property (nonatomic, strong) NSMutableArray <LFFrame *> *sortList;
|
||||
@property (nonatomic, strong, readwrite) NSMutableArray <LFFrame *> *list;
|
||||
@property (nonatomic, strong) NSMutableArray *thresholdList;
|
||||
|
||||
/** 处理buffer缓冲区情况 */
|
||||
@@ -32,31 +32,35 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
|
||||
|
||||
@implementation LFStreamingBuffer
|
||||
|
||||
- (instancetype)init{
|
||||
if(self = [super init]){
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
self.updateInterval = defaultUpdateInterval;
|
||||
self.callBackInterval = defaultCallBackInterval;
|
||||
self.maxCount = defaultSendBufferMaxCount;
|
||||
self.lastDropFrames = 0;
|
||||
self.startTimer = NO;
|
||||
self.needDropFrame = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
- (void)dealloc {
|
||||
}
|
||||
|
||||
#pragma mark -- Custom
|
||||
- (void)appendObject:(LFFrame*)frame{
|
||||
if(!frame) return;
|
||||
if(!_startTimer){
|
||||
- (void)appendObject:(LFFrame *)frame {
|
||||
if (!frame) return;
|
||||
if (!_startTimer) {
|
||||
_startTimer = YES;
|
||||
[self tick];
|
||||
}
|
||||
|
||||
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
if(self.sortList.count < defaultSortBufferMaxCount){
|
||||
if (self.sortList.count < defaultSortBufferMaxCount) {
|
||||
[self.sortList addObject:frame];
|
||||
}else{
|
||||
} else {
|
||||
///< 排序
|
||||
[self.sortList addObject:frame];
|
||||
NSArray *sortedSendQuery = [self.sortList sortedArrayUsingFunction:frameDataCompare context:NULL];
|
||||
@@ -66,52 +70,59 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
|
||||
[self removeExpireFrame];
|
||||
/// 添加至缓冲区
|
||||
LFFrame *firstFrame = [self.sortList lfPopFirstObject];
|
||||
|
||||
if(firstFrame) [self.list addObject:firstFrame];
|
||||
|
||||
if (firstFrame) [self.list addObject:firstFrame];
|
||||
}
|
||||
dispatch_semaphore_signal(_lock);
|
||||
}
|
||||
|
||||
- (LFFrame*)popFirstObject{
|
||||
- (LFFrame *)popFirstObject {
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
LFFrame *firstFrame = [self.list lfPopFirstObject];
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return firstFrame;
|
||||
}
|
||||
|
||||
- (void)removeAllObject{
|
||||
- (void)removeAllObject {
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
[self.list removeAllObjects];
|
||||
dispatch_semaphore_signal(_lock);
|
||||
}
|
||||
|
||||
- (void)removeExpireFrame{
|
||||
if(self.list.count < self.maxCount) return;
|
||||
|
||||
NSArray *pFrames = [self expirePFrames];///< 第一个P到第一个I之间的p帧
|
||||
if(pFrames && pFrames.count > 0){
|
||||
[self.list removeObjectsInArray:pFrames];
|
||||
return;
|
||||
- (void)removeExpireFrame {
|
||||
if (self.list.count < self.maxCount) return;
|
||||
|
||||
if(self.needDropFrame){
|
||||
NSArray *pFrames = [self expirePFrames];///< 第一个P到第一个I之间的p帧
|
||||
self.lastDropFrames += [pFrames count];
|
||||
if (pFrames && pFrames.count > 0) {
|
||||
[self.list removeObjectsInArray:pFrames];
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *iFrames = [self expireIFrames];///< 删除一个I帧(但一个I帧可能对应多个nal)
|
||||
self.lastDropFrames += [iFrames count];
|
||||
if (iFrames) {
|
||||
[self.list removeObjectsInArray:iFrames];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.list removeAllObjects];
|
||||
}else{
|
||||
[self.list lfPopFirstObject];
|
||||
}
|
||||
|
||||
NSArray *iFrames = [self expireIFrames];///< 删除一个I帧(但一个I帧可能对应多个nal)
|
||||
if(iFrames){
|
||||
[self.list removeObjectsInArray:iFrames];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.list removeAllObjects];
|
||||
}
|
||||
|
||||
- (NSArray*)expirePFrames{
|
||||
- (NSArray *)expirePFrames {
|
||||
NSMutableArray *pframes = [[NSMutableArray alloc] init];
|
||||
for(NSInteger index = 0;index < self.list.count;index++){
|
||||
for (NSInteger index = 0; index < self.list.count; index++) {
|
||||
LFFrame *frame = [self.list objectAtIndex:index];
|
||||
if([frame isKindOfClass:[LFVideoFrame class]]){
|
||||
LFVideoFrame *videoFrame = (LFVideoFrame*)frame;
|
||||
if(videoFrame.isKeyFrame && pframes.count > 0){
|
||||
if ([frame isKindOfClass:[LFVideoFrame class]]) {
|
||||
LFVideoFrame *videoFrame = (LFVideoFrame *)frame;
|
||||
if (videoFrame.isKeyFrame && pframes.count > 0) {
|
||||
break;
|
||||
}else if(!videoFrame.isKeyFrame){
|
||||
} else if (!videoFrame.isKeyFrame) {
|
||||
[pframes addObject:frame];
|
||||
}
|
||||
}
|
||||
@@ -119,13 +130,13 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
|
||||
return pframes;
|
||||
}
|
||||
|
||||
- (NSArray*)expireIFrames{
|
||||
- (NSArray *)expireIFrames {
|
||||
NSMutableArray *iframes = [[NSMutableArray alloc] init];
|
||||
uint64_t timeStamp = 0;
|
||||
for(NSInteger index = 0;index < self.list.count;index++){
|
||||
for (NSInteger index = 0; index < self.list.count; index++) {
|
||||
LFFrame *frame = [self.list objectAtIndex:index];
|
||||
if([frame isKindOfClass:[LFVideoFrame class]] && ((LFVideoFrame*)frame).isKeyFrame){
|
||||
if(timeStamp != 0 && timeStamp != frame.timestamp) break;
|
||||
if ([frame isKindOfClass:[LFVideoFrame class]] && ((LFVideoFrame *)frame).isKeyFrame) {
|
||||
if (timeStamp != 0 && timeStamp != frame.timestamp) break;
|
||||
[iframes addObject:frame];
|
||||
timeStamp = frame.timestamp;
|
||||
}
|
||||
@@ -134,91 +145,90 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
|
||||
}
|
||||
|
||||
NSInteger frameDataCompare(id obj1, id obj2, void *context){
|
||||
LFFrame* frame1 = (LFFrame*) obj1;
|
||||
LFFrame *frame2 = (LFFrame*) obj2;
|
||||
|
||||
LFFrame *frame1 = (LFFrame *)obj1;
|
||||
LFFrame *frame2 = (LFFrame *)obj2;
|
||||
|
||||
if (frame1.timestamp == frame2.timestamp)
|
||||
return NSOrderedSame;
|
||||
else if(frame1.timestamp > frame2.timestamp)
|
||||
else if (frame1.timestamp > frame2.timestamp)
|
||||
return NSOrderedDescending;
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
|
||||
- (LFLiveBuffferState)currentBufferState{
|
||||
- (LFLiveBuffferState)currentBufferState {
|
||||
NSInteger currentCount = 0;
|
||||
NSInteger increaseCount = 0;
|
||||
NSInteger decreaseCount = 0;
|
||||
|
||||
for(NSNumber *number in self.thresholdList){
|
||||
if(number.integerValue >= currentCount){
|
||||
increaseCount ++;
|
||||
}else{
|
||||
decreaseCount ++;
|
||||
|
||||
for (NSNumber *number in self.thresholdList) {
|
||||
if (number.integerValue >= currentCount) {
|
||||
increaseCount++;
|
||||
} else {
|
||||
decreaseCount++;
|
||||
}
|
||||
currentCount = [number integerValue];
|
||||
}
|
||||
|
||||
if(increaseCount >= self.callBackInterval){
|
||||
|
||||
if (increaseCount >= self.callBackInterval) {
|
||||
return LFLiveBuffferIncrease;
|
||||
}
|
||||
|
||||
if(decreaseCount >= self.callBackInterval){
|
||||
|
||||
if (decreaseCount >= self.callBackInterval) {
|
||||
return LFLiveBuffferDecline;
|
||||
}
|
||||
|
||||
|
||||
return LFLiveBuffferUnknown;
|
||||
}
|
||||
|
||||
#pragma mark -- Setter Getter
|
||||
- (NSMutableArray*)list{
|
||||
if(!_list){
|
||||
- (NSMutableArray *)list {
|
||||
if (!_list) {
|
||||
_list = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _list;
|
||||
}
|
||||
|
||||
- (NSMutableArray*)sortList{
|
||||
if(!_sortList){
|
||||
- (NSMutableArray *)sortList {
|
||||
if (!_sortList) {
|
||||
_sortList = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _sortList;
|
||||
}
|
||||
|
||||
- (NSMutableArray*)thresholdList{
|
||||
if(!_thresholdList){
|
||||
- (NSMutableArray *)thresholdList {
|
||||
if (!_thresholdList) {
|
||||
_thresholdList = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _thresholdList;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -- 采样
|
||||
- (void)tick{
|
||||
- (void)tick {
|
||||
/** 采样 3个阶段 如果网络都是好或者都是差给回调 */
|
||||
_currentInterval += self.updateInterval;
|
||||
|
||||
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
[self.thresholdList addObject:@(self.list.count)];
|
||||
dispatch_semaphore_signal(_lock);
|
||||
|
||||
if(self.currentInterval >= self.callBackInterval){
|
||||
|
||||
if (self.currentInterval >= self.callBackInterval) {
|
||||
LFLiveBuffferState state = [self currentBufferState];
|
||||
if(state == LFLiveBuffferIncrease){
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(streamingBuffer:bufferState:)]){
|
||||
if (state == LFLiveBuffferIncrease) {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(streamingBuffer:bufferState:)]) {
|
||||
[self.delegate streamingBuffer:self bufferState:LFLiveBuffferIncrease];
|
||||
}
|
||||
}else if(state == LFLiveBuffferDecline){
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(streamingBuffer:bufferState:)]){
|
||||
} else if (state == LFLiveBuffferDecline) {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(streamingBuffer:bufferState:)]) {
|
||||
[self.delegate streamingBuffer:self bufferState:LFLiveBuffferDecline];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
self.currentInterval = 0;
|
||||
[self.thresholdList removeAllObjects];
|
||||
}
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.updateInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
__weak typeof(_self) self = _self;
|
||||
__strong typeof(_self) self = _self;
|
||||
[self tick];
|
||||
});
|
||||
}
|
||||
+4
-4
@@ -11,10 +11,10 @@
|
||||
@interface NSMutableArray (YYAdd)
|
||||
|
||||
/**
|
||||
Removes and returns the object with the lowest-valued index in the array.
|
||||
If the array is empty, it just returns nil.
|
||||
|
||||
@return The first object, or nil.
|
||||
Removes and returns the object with the lowest-valued index in the array.
|
||||
If the array is empty, it just returns nil.
|
||||
|
||||
@return The first object, or nil.
|
||||
*/
|
||||
- (nullable id)lfPopFirstObject;
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
//
|
||||
// LFLiveSession.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFAudioFrame.h"
|
||||
#import "LFVideoFrame.h"
|
||||
#import "LFLiveAudioConfiguration.h"
|
||||
#import "LFLiveVideoConfiguration.h"
|
||||
#import "LFLiveDebug.h"
|
||||
|
||||
typedef void (^ LFRequestComplete)(_Nullable id info,NSError *_Nullable errorMsg);
|
||||
|
||||
/// 流类型
|
||||
typedef NS_ENUM(NSUInteger, LFLiveType){
|
||||
/// rtmp格式
|
||||
LFLiveRTMP = 0,
|
||||
/// tcp 传输flv格式
|
||||
LFLiveFLV = 1,
|
||||
};
|
||||
|
||||
@class LFLiveSession;
|
||||
@protocol LFLiveSessionDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
/** live status changed will callback */
|
||||
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state;
|
||||
/** live debug info callback */
|
||||
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug*)debugInfo;
|
||||
/** callback socket errorcode */
|
||||
- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode;
|
||||
@end
|
||||
|
||||
@class LFLiveStreamInfo;
|
||||
|
||||
@interface LFLiveSession : NSObject
|
||||
|
||||
#pragma mark - Attribute
|
||||
///=============================================================================
|
||||
/// @name Attribute
|
||||
///=============================================================================
|
||||
/** The delegate of the capture. captureData callback */
|
||||
@property (nullable,nonatomic, weak) id<LFLiveSessionDelegate> delegate;
|
||||
|
||||
/** The running control start capture or stop capture*/
|
||||
@property (nonatomic, assign) BOOL running;
|
||||
|
||||
/** The preView will show OpenGL ES view*/
|
||||
@property (nonatomic, strong,null_resettable) UIView *preView;
|
||||
|
||||
/** The captureDevicePosition control camraPosition ,default front*/
|
||||
@property (nonatomic, assign) AVCaptureDevicePosition captureDevicePosition;
|
||||
|
||||
/** The beautyFace control capture shader filter empty or beautiy */
|
||||
@property (nonatomic, assign) BOOL beautyFace;
|
||||
|
||||
/** The beautyLevel control beautyFace Level, default 0.5, between 0.0 ~ 1.0 */
|
||||
@property (nonatomic, assign) CGFloat beautyLevel;
|
||||
|
||||
/** The brightLevel control brightness Level, default 0.5, between 0.0 ~ 1.0 */
|
||||
@property (nonatomic, assign) CGFloat brightLevel;
|
||||
|
||||
/** The torch control camera zoom scale default 1.0, between 1.0 ~ 3.0 */
|
||||
@property (nonatomic, assign) CGFloat zoomScale;
|
||||
|
||||
/** The torch control capture flash is on or off */
|
||||
@property (nonatomic, assign) BOOL torch;
|
||||
|
||||
/** The mirror control mirror of front camera is on or off */
|
||||
@property (nonatomic, assign) BOOL mirror;
|
||||
|
||||
/** The muted control callbackAudioData,muted will memset 0.*/
|
||||
@property (nonatomic,assign) BOOL muted;
|
||||
|
||||
/** The stream control upload and package*/
|
||||
@property (nullable,nonatomic, strong,readonly) LFLiveStreamInfo * streamInfo;
|
||||
|
||||
/** The status of the stream .*/
|
||||
@property (nonatomic,assign,readonly) LFLiveState state;
|
||||
|
||||
/** The showDebugInfo control streamInfo and uploadInfo(1s) *.*/
|
||||
@property (nonatomic,assign) BOOL showDebugInfo;
|
||||
|
||||
/** The reconnectInterval control reconnect timeInterval(重连间隔) *.*/
|
||||
@property (nonatomic,assign) NSUInteger reconnectInterval;
|
||||
|
||||
/** The reconnectCount control reconnect count (重连次数) *.*/
|
||||
@property (nonatomic,assign) NSUInteger reconnectCount;
|
||||
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
/// @name Initializer
|
||||
///=============================================================================
|
||||
- (nullable instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
*/
|
||||
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration*)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration*)videoConfiguration liveType:(LFLiveType)liveType NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** The start stream .*/
|
||||
- (void)startLive:(nonnull LFLiveStreamInfo*)streamInfo;
|
||||
|
||||
/** The stop stream .*/
|
||||
- (void)stopLive;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
//
|
||||
// LFLiveSession.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFLiveSession.h"
|
||||
#import "LFVideoCapture.h"
|
||||
#import "LFAudioCapture.h"
|
||||
#import "LFHardwareVideoEncoder.h"
|
||||
#import "LFHardwareAudioEncoder.h"
|
||||
#import "LFStreamRtmpSocket.h"
|
||||
#import "LFStreamTcpSocket.h"
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
|
||||
#define LFLiveReportKey @"com.youku.liveSessionReport"
|
||||
|
||||
@interface LFLiveSession ()<LFAudioCaptureDelegate,LFVideoCaptureDelegate,LFAudioEncodingDelegate,LFVideoEncodingDelegate,LFStreamSocketDelegate>
|
||||
{
|
||||
dispatch_semaphore_t _lock;
|
||||
}
|
||||
///流媒体格式
|
||||
@property (nonatomic, assign) LFLiveType liveType;
|
||||
///音频配置
|
||||
@property (nonatomic, strong) LFLiveAudioConfiguration *audioConfiguration;
|
||||
///视频配置
|
||||
@property (nonatomic, strong) LFLiveVideoConfiguration *videoConfiguration;
|
||||
/// 声音采集
|
||||
@property (nonatomic, strong) LFAudioCapture *audioCaptureSource;
|
||||
/// 视频采集
|
||||
@property (nonatomic, strong) LFVideoCapture *videoCaptureSource;
|
||||
/// 音频编码
|
||||
@property (nonatomic, strong) id<LFAudioEncoding> audioEncoder;
|
||||
/// 视频编码
|
||||
@property (nonatomic, strong) id<LFVideoEncoding> videoEncoder;
|
||||
/// 上传
|
||||
@property (nonatomic, strong) id<LFStreamSocket> socket;
|
||||
|
||||
#pragma mark -- 内部标识
|
||||
/// 上报
|
||||
@property (nonatomic, copy) dispatch_block_t reportBlock;
|
||||
/// debugInfo
|
||||
@property (nonatomic, strong) LFLiveDebug *debugInfo;
|
||||
/// streamInfo
|
||||
@property (nonatomic, strong) LFLiveStreamInfo *streamInfo;
|
||||
/// uploading
|
||||
@property (nonatomic, assign) BOOL uploading;
|
||||
/// state
|
||||
@property (nonatomic,assign,readwrite) LFLiveState state;
|
||||
|
||||
@end
|
||||
|
||||
/** 时间戳 */
|
||||
#define NOW (CACurrentMediaTime()*1000)
|
||||
@interface LFLiveSession ()
|
||||
|
||||
@property (nonatomic, assign) uint64_t timestamp;
|
||||
@property (nonatomic, assign) BOOL isFirstFrame;
|
||||
@property (nonatomic, assign) uint64_t currentTimestamp;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFLiveSession
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithAudioConfiguration:(LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(LFLiveVideoConfiguration *)videoConfiguration liveType:(LFLiveType)liveType{
|
||||
if(!audioConfiguration || !videoConfiguration) @throw [NSException exceptionWithName:@"LFLiveSession init error" reason:@"audioConfiguration or videoConfiguration is nil " userInfo:nil];
|
||||
if(self = [super init]){
|
||||
_audioConfiguration = audioConfiguration;
|
||||
_videoConfiguration = videoConfiguration;
|
||||
_liveType = liveType;
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
self.audioCaptureSource.running = NO;
|
||||
self.videoCaptureSource.running = NO;
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (void)startLive:(LFLiveStreamInfo*)streamInfo{
|
||||
if(!streamInfo) return;
|
||||
_streamInfo = streamInfo;
|
||||
_streamInfo.videoConfiguration = _videoConfiguration;
|
||||
_streamInfo.audioConfiguration = _audioConfiguration;
|
||||
[self.socket start];
|
||||
}
|
||||
|
||||
- (void)stopLive{
|
||||
self.uploading = NO;
|
||||
[self.socket stop];
|
||||
}
|
||||
|
||||
#pragma mark -- CaptureDelegate
|
||||
- (void)captureOutput:(nullable LFAudioCapture*)capture audioBuffer:(AudioBufferList)inBufferList{
|
||||
[self.audioEncoder encodeAudioData:inBufferList timeStamp:self.currentTimestamp];
|
||||
}
|
||||
|
||||
- (void)captureOutput:(nullable LFVideoCapture*)capture pixelBuffer:(nullable CVImageBufferRef)pixelBuffer{
|
||||
[self.videoEncoder encodeVideoData:pixelBuffer timeStamp:self.currentTimestamp];
|
||||
}
|
||||
|
||||
#pragma mark -- EncoderDelegate
|
||||
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame*)frame{
|
||||
if(self.uploading) [self.socket sendFrame:frame];//<上传
|
||||
}
|
||||
|
||||
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame*)frame{
|
||||
if(self.uploading) [self.socket sendFrame:frame];//<上传
|
||||
}
|
||||
|
||||
#pragma mark -- LFStreamTcpSocketDelegate
|
||||
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status{
|
||||
if(status == LFLiveStart){
|
||||
if(!self.uploading){
|
||||
self.timestamp = 0;
|
||||
self.isFirstFrame = YES;
|
||||
self.uploading = YES;
|
||||
}
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.state = status;
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:liveStateDidChange:)]){
|
||||
[self.delegate liveSession:self liveStateDidChange:status];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:errorCode:)]){
|
||||
[self.delegate liveSession:self errorCode:errorCode];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug*)debugInfo{
|
||||
self.debugInfo = debugInfo;
|
||||
if(self.showDebugInfo){
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:debugInfo:)]){
|
||||
[self.delegate liveSession:self debugInfo:debugInfo];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status{
|
||||
NSUInteger videoBitRate = [_videoEncoder videoBitRate];
|
||||
if(status == LFLiveBuffferIncrease){
|
||||
if(videoBitRate < _videoConfiguration.videoMaxBitRate){
|
||||
videoBitRate = videoBitRate + 50 * 1000;
|
||||
[_videoEncoder setVideoBitRate:videoBitRate];
|
||||
}
|
||||
}else{
|
||||
if(videoBitRate > _videoConfiguration.videoMinBitRate){
|
||||
videoBitRate = videoBitRate - 100 * 1000;
|
||||
[_videoEncoder setVideoBitRate:videoBitRate];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- Getter Setter
|
||||
- (void)setRunning:(BOOL)running{
|
||||
if(_running == running) return;
|
||||
[self willChangeValueForKey:@"running"];
|
||||
_running = running;
|
||||
[self didChangeValueForKey:@"running"];
|
||||
self.videoCaptureSource.running = _running;
|
||||
self.audioCaptureSource.running = _running;
|
||||
}
|
||||
|
||||
- (void)setPreView:(UIView *)preView{
|
||||
[self.videoCaptureSource setPreView:preView];
|
||||
}
|
||||
|
||||
- (UIView*)preView{
|
||||
return self.videoCaptureSource.preView;
|
||||
}
|
||||
|
||||
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition{
|
||||
[self.videoCaptureSource setCaptureDevicePosition:captureDevicePosition];
|
||||
}
|
||||
|
||||
- (AVCaptureDevicePosition)captureDevicePosition{
|
||||
return self.videoCaptureSource.captureDevicePosition;
|
||||
}
|
||||
|
||||
- (void)setBeautyFace:(BOOL)beautyFace{
|
||||
[self.videoCaptureSource setBeautyFace:beautyFace];
|
||||
}
|
||||
|
||||
- (BOOL)beautyFace{
|
||||
return self.videoCaptureSource.beautyFace;
|
||||
}
|
||||
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel {
|
||||
[self.videoCaptureSource setBeautyLevel:beautyLevel];
|
||||
}
|
||||
|
||||
- (CGFloat)beautyLevel {
|
||||
return self.videoCaptureSource.beautyLevel;
|
||||
}
|
||||
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel {
|
||||
[self.videoCaptureSource setBrightLevel:brightLevel];
|
||||
}
|
||||
|
||||
- (CGFloat)brightLevel {
|
||||
return self.videoCaptureSource.brightLevel;
|
||||
}
|
||||
|
||||
- (void)setZoomScale:(CGFloat)zoomScale {
|
||||
[self.videoCaptureSource setZoomScale:zoomScale];
|
||||
}
|
||||
|
||||
- (CGFloat)zoomScale {
|
||||
return self.videoCaptureSource.zoomScale;
|
||||
}
|
||||
|
||||
- (void)setTorch:(BOOL)torch {
|
||||
[self.videoCaptureSource setTorch:torch];
|
||||
}
|
||||
|
||||
- (BOOL)torch {
|
||||
return self.videoCaptureSource.torch;
|
||||
}
|
||||
|
||||
- (void)setMirror:(BOOL)mirror {
|
||||
[self.videoCaptureSource setMirror:mirror];
|
||||
}
|
||||
|
||||
- (BOOL)mirror {
|
||||
return self.videoCaptureSource.mirror;
|
||||
}
|
||||
|
||||
- (void)setMuted:(BOOL)muted{
|
||||
[self.audioCaptureSource setMuted:muted];
|
||||
}
|
||||
|
||||
- (BOOL)muted{
|
||||
return self.audioCaptureSource.muted;
|
||||
}
|
||||
|
||||
- (LFAudioCapture*)audioCaptureSource{
|
||||
if(!_audioCaptureSource){
|
||||
_audioCaptureSource = [[LFAudioCapture alloc] initWithAudioConfiguration:_audioConfiguration];
|
||||
_audioCaptureSource.delegate = self;
|
||||
}
|
||||
return _audioCaptureSource;
|
||||
}
|
||||
|
||||
- (LFVideoCapture*)videoCaptureSource{
|
||||
if(!_videoCaptureSource){
|
||||
_videoCaptureSource = [[LFVideoCapture alloc] initWithVideoConfiguration:_videoConfiguration];
|
||||
_videoCaptureSource.delegate = self;
|
||||
}
|
||||
return _videoCaptureSource;
|
||||
}
|
||||
|
||||
- (id<LFAudioEncoding>)audioEncoder{
|
||||
if(!_audioEncoder){
|
||||
_audioEncoder = [[LFHardwareAudioEncoder alloc] initWithAudioStreamConfiguration:_audioConfiguration];
|
||||
[_audioEncoder setDelegate:self];
|
||||
}
|
||||
return _audioEncoder;
|
||||
}
|
||||
|
||||
- (id<LFVideoEncoding>)videoEncoder{
|
||||
if(!_videoEncoder){
|
||||
_videoEncoder = [[LFHardwareVideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
|
||||
[_videoEncoder setDelegate:self];
|
||||
}
|
||||
return _videoEncoder;
|
||||
}
|
||||
|
||||
- (id<LFStreamSocket>)socket{
|
||||
if(!_socket){
|
||||
if(self.liveType == LFLiveRTMP){
|
||||
_socket = [[LFStreamRtmpSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
|
||||
}else if(self.liveType == LFLiveFLV){
|
||||
_socket = [[LFStreamTcpSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
|
||||
}
|
||||
[_socket setDelegate:self];
|
||||
}
|
||||
return _socket;
|
||||
}
|
||||
|
||||
- (LFLiveStreamInfo*)streamInfo{
|
||||
if(!_streamInfo){
|
||||
_streamInfo = [[LFLiveStreamInfo alloc] init];
|
||||
}
|
||||
return _streamInfo;
|
||||
}
|
||||
|
||||
- (uint64_t)currentTimestamp{
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
uint64_t currentts = 0;
|
||||
if(_isFirstFrame == true) {
|
||||
_timestamp = NOW;
|
||||
_isFirstFrame = false;
|
||||
currentts = 0;
|
||||
} else {
|
||||
currentts = NOW - _timestamp;
|
||||
}
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return currentts;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,257 +0,0 @@
|
||||
//
|
||||
// LFVideoCapture.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/1.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFVideoCapture.h"
|
||||
#import "GPUImage.h"
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
#import "LFGPUImageEmptyFilter.h"
|
||||
|
||||
@interface LFVideoCapture ()
|
||||
|
||||
@property(nonatomic, strong) GPUImageVideoCamera *videoCamera;
|
||||
@property(nonatomic, weak) LFGPUImageBeautyFilter *beautyFilter;
|
||||
@property(nonatomic, strong) GPUImageOutput<GPUImageInput> *filter;
|
||||
@property(nonatomic, strong) GPUImageOutput<GPUImageInput> *output;
|
||||
@property(nonatomic, strong) GPUImageCropFilter *cropfilter;
|
||||
@property(nonatomic, strong) GPUImageView *gpuImageView;
|
||||
@property(nonatomic, strong) LFLiveVideoConfiguration *configuration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFVideoCapture
|
||||
@synthesize torch = _torch;
|
||||
@synthesize beautyLevel = _beautyLevel;
|
||||
@synthesize brightLevel = _brightLevel;
|
||||
@synthesize zoomScale = _zoomScale;
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithVideoConfiguration:(LFLiveVideoConfiguration *)configuration{
|
||||
if(self = [super init]){
|
||||
_configuration = configuration;
|
||||
_videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:_configuration.avSessionPreset cameraPosition:AVCaptureDevicePositionFront];
|
||||
_videoCamera.outputImageOrientation = _configuration.orientation;
|
||||
_videoCamera.horizontallyMirrorFrontFacingCamera = NO;
|
||||
_videoCamera.horizontallyMirrorRearFacingCamera = NO;
|
||||
_videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
|
||||
|
||||
_gpuImageView = [[GPUImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
[_gpuImageView setFillMode:kGPUImageFillModePreserveAspectRatioAndFill];
|
||||
[_gpuImageView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
|
||||
[_gpuImageView setInputRotation:kGPUImageFlipHorizonal atIndex:0];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
|
||||
self.beautyFace = YES;
|
||||
self.beautyLevel = 0.5;
|
||||
self.brightLevel = 0.5;
|
||||
self.zoomScale = 1.0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
[UIApplication sharedApplication].idleTimerDisabled = NO;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_videoCamera stopCameraCapture];
|
||||
}
|
||||
|
||||
#pragma mark -- Setter Getter
|
||||
- (void)setRunning:(BOOL)running{
|
||||
if(_running == running) return;
|
||||
_running = running;
|
||||
|
||||
if(!_running){
|
||||
[UIApplication sharedApplication].idleTimerDisabled = NO;
|
||||
[_videoCamera stopCameraCapture];
|
||||
}else{
|
||||
[UIApplication sharedApplication].idleTimerDisabled = YES;
|
||||
[_videoCamera startCameraCapture];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setPreView:(UIView *)preView{
|
||||
if(_gpuImageView.superview) [_gpuImageView removeFromSuperview];
|
||||
[preView insertSubview:_gpuImageView atIndex:0];
|
||||
}
|
||||
|
||||
- (UIView*)preView{
|
||||
return _gpuImageView.superview;
|
||||
}
|
||||
|
||||
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition{
|
||||
[_videoCamera rotateCamera];
|
||||
_videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
|
||||
if (captureDevicePosition == AVCaptureDevicePositionFront) {
|
||||
[_gpuImageView setInputRotation:kGPUImageFlipHorizonal atIndex:0];
|
||||
} else {
|
||||
[_gpuImageView setInputRotation:kGPUImageNoRotation atIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
- (AVCaptureDevicePosition)captureDevicePosition{
|
||||
return [_videoCamera cameraPosition];
|
||||
}
|
||||
|
||||
- (void)setVideoFrameRate:(NSInteger)videoFrameRate{
|
||||
if(videoFrameRate <= 0) return;
|
||||
if(videoFrameRate == _videoCamera.frameRate) return;
|
||||
_videoCamera.frameRate = (uint32_t)videoFrameRate;
|
||||
}
|
||||
|
||||
- (NSInteger)videoFrameRate{
|
||||
return _videoCamera.frameRate;
|
||||
}
|
||||
|
||||
- (void)setTorch:(BOOL)torch {
|
||||
BOOL ret;
|
||||
if(!_videoCamera.captureSession) return;
|
||||
AVCaptureSession* session = (AVCaptureSession*)_videoCamera.captureSession;
|
||||
[session beginConfiguration];
|
||||
if (_videoCamera.inputCamera) {
|
||||
if (_videoCamera.inputCamera.torchAvailable) {
|
||||
NSError* err = nil;
|
||||
if ([_videoCamera.inputCamera lockForConfiguration:&err]) {
|
||||
[_videoCamera.inputCamera setTorchMode:( torch ? AVCaptureTorchModeOn : AVCaptureTorchModeOff ) ];
|
||||
[_videoCamera.inputCamera unlockForConfiguration];
|
||||
ret = (_videoCamera.inputCamera.torchMode == AVCaptureTorchModeOn);
|
||||
} else {
|
||||
NSLog(@"Error while locking device for torch: %@", err);
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
NSLog(@"Torch not available in current camera input");
|
||||
}
|
||||
}
|
||||
[session commitConfiguration];
|
||||
_torch = ret;
|
||||
}
|
||||
- (BOOL)torch {
|
||||
return _videoCamera.inputCamera.torchMode;
|
||||
}
|
||||
- (void)setMirror:(BOOL)mirror {
|
||||
_videoCamera.horizontallyMirrorFrontFacingCamera = mirror;
|
||||
_videoCamera.horizontallyMirrorRearFacingCamera = mirror;
|
||||
}
|
||||
- (BOOL)mirror {
|
||||
return _videoCamera.horizontallyMirrorFrontFacingCamera;
|
||||
}
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel {
|
||||
_beautyLevel = beautyLevel;
|
||||
if (_beautyFilter) {
|
||||
[_beautyFilter setBeautyLevel:_beautyLevel];
|
||||
}
|
||||
}
|
||||
- (CGFloat)beautyLevel {
|
||||
return _beautyLevel;
|
||||
}
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel {
|
||||
_brightLevel = brightLevel;
|
||||
if (_beautyFilter) {
|
||||
[_beautyFilter setBrightLevel:brightLevel];
|
||||
}
|
||||
}
|
||||
- (CGFloat)brightLevel {
|
||||
return _brightLevel;
|
||||
}
|
||||
- (void)setZoomScale:(CGFloat)zoomScale {
|
||||
if (self.videoCamera && self.videoCamera.inputCamera) {
|
||||
AVCaptureDevice* device = (AVCaptureDevice*)self.videoCamera.inputCamera;
|
||||
if ([device lockForConfiguration:nil]) {
|
||||
device.videoZoomFactor = zoomScale;
|
||||
[device unlockForConfiguration];
|
||||
_zoomScale = zoomScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)zoomScale {
|
||||
return _zoomScale;
|
||||
}
|
||||
|
||||
- (void)setBeautyFace:(BOOL)beautyFace{
|
||||
if(_beautyFace == beautyFace) return;
|
||||
|
||||
_beautyFace = beautyFace;
|
||||
[_filter removeAllTargets];
|
||||
[_cropfilter removeAllTargets];
|
||||
[_videoCamera removeAllTargets];
|
||||
|
||||
if (_beautyFace) {
|
||||
_output = [[LFGPUImageEmptyFilter alloc] init];
|
||||
_filter = [[LFGPUImageBeautyFilter alloc] init];
|
||||
_beautyFilter = _filter;
|
||||
__weak typeof(self) _self = self;
|
||||
[_output setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
|
||||
[_self processVideo:output];
|
||||
}];
|
||||
} else {
|
||||
_filter = [[LFGPUImageEmptyFilter alloc] init];
|
||||
_beautyFilter = nil;
|
||||
__weak typeof(self) _self = self;
|
||||
[_filter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
|
||||
[_self processVideo:output];
|
||||
}];
|
||||
}
|
||||
|
||||
if (_configuration.isClipVideo) {
|
||||
if (_configuration.orientation == UIInterfaceOrientationPortrait || _configuration.orientation == UIInterfaceOrientationPortraitUpsideDown){
|
||||
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(0.125, 0, 0.75, 1)];
|
||||
} else {
|
||||
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(0, 0.125, 1, 0.75)];
|
||||
}
|
||||
[_videoCamera addTarget:_cropfilter];
|
||||
[_cropfilter addTarget:_filter];
|
||||
} else {
|
||||
[_videoCamera addTarget:_filter];
|
||||
}
|
||||
|
||||
if (_beautyFace) {
|
||||
[_filter addTarget:_output];
|
||||
[_output addTarget:_gpuImageView];
|
||||
} else {
|
||||
[_filter addTarget:_gpuImageView];
|
||||
}
|
||||
|
||||
if (_videoCamera.cameraPosition == AVCaptureDevicePositionFront) {
|
||||
[_gpuImageView setInputRotation:kGPUImageFlipHorizonal atIndex:0];
|
||||
} else {
|
||||
[_gpuImageView setInputRotation:kGPUImageNoRotation atIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- Custom Method
|
||||
- (void)processVideo:(GPUImageOutput *)output{
|
||||
__weak typeof(self) _self = self;
|
||||
@autoreleasepool {
|
||||
GPUImageFramebuffer *imageFramebuffer = output.framebufferForOutput;
|
||||
CVPixelBufferRef pixelBuffer = [imageFramebuffer pixelBuffer];
|
||||
|
||||
if(pixelBuffer && _self.delegate && [_self.delegate respondsToSelector:@selector(captureOutput:pixelBuffer:)]){
|
||||
[_self.delegate captureOutput:_self pixelBuffer:pixelBuffer];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Notification
|
||||
|
||||
- (void)willEnterBackground:(NSNotification*)notification{
|
||||
[UIApplication sharedApplication].idleTimerDisabled = NO;
|
||||
[_videoCamera pauseCameraCapture];
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
glFinish();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)willEnterForeground:(NSNotification*)notification{
|
||||
[_videoCamera resumeCameraCapture];
|
||||
[UIApplication sharedApplication].idleTimerDisabled = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,181 +0,0 @@
|
||||
//
|
||||
// LFHardwareAudioEncoder.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFHardwareAudioEncoder.h"
|
||||
|
||||
@interface LFHardwareAudioEncoder (){
|
||||
AudioConverterRef m_converter;
|
||||
char *aacBuf;
|
||||
}
|
||||
@property (nonatomic, strong) LFLiveAudioConfiguration *configuration;
|
||||
@property (nonatomic, weak) id<LFAudioEncodingDelegate> aacDeleage;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFHardwareAudioEncoder
|
||||
|
||||
- (instancetype)initWithAudioStreamConfiguration:(LFLiveAudioConfiguration *)configuration{
|
||||
if(self = [super init]){
|
||||
_configuration = configuration;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
if(aacBuf) free(aacBuf);
|
||||
}
|
||||
|
||||
#pragma mark -- LFAudioEncoder
|
||||
- (void)setDelegate:(id<LFAudioEncodingDelegate>)delegate{
|
||||
_aacDeleage = delegate;
|
||||
}
|
||||
|
||||
- (void)encodeAudioData:(AudioBufferList)inBufferList timeStamp:(uint64_t)timeStamp{
|
||||
if (![self createAudioConvert]){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!aacBuf){
|
||||
aacBuf = malloc(inBufferList.mBuffers[0].mDataByteSize);
|
||||
}
|
||||
|
||||
// 初始化一个输出缓冲列表
|
||||
AudioBufferList outBufferList;
|
||||
outBufferList.mNumberBuffers = 1;
|
||||
outBufferList.mBuffers[0].mNumberChannels = inBufferList.mBuffers[0].mNumberChannels;
|
||||
outBufferList.mBuffers[0].mDataByteSize = inBufferList.mBuffers[0].mDataByteSize; // 设置缓冲区大小
|
||||
outBufferList.mBuffers[0].mData = aacBuf; // 设置AAC缓冲区
|
||||
UInt32 outputDataPacketSize = 1;
|
||||
if (AudioConverterFillComplexBuffer(m_converter, inputDataProc, &inBufferList, &outputDataPacketSize, &outBufferList, NULL) != noErr){
|
||||
return;
|
||||
}
|
||||
LFAudioFrame *audioFrame = [LFAudioFrame new];
|
||||
audioFrame.timestamp = timeStamp;
|
||||
audioFrame.data = [NSData dataWithBytes:aacBuf length:outBufferList.mBuffers[0].mDataByteSize];
|
||||
|
||||
char exeData[2];
|
||||
exeData[0] = _configuration.asc[0];
|
||||
exeData[1] = _configuration.asc[1];
|
||||
audioFrame.audioInfo =[NSData dataWithBytes:exeData length:2];
|
||||
if(self.aacDeleage && [self.aacDeleage respondsToSelector:@selector(audioEncoder:audioFrame:)]){
|
||||
[self.aacDeleage audioEncoder:self audioFrame:audioFrame];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopEncoder{
|
||||
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
-(BOOL)createAudioConvert{ //根据输入样本初始化一个编码转换器
|
||||
if (m_converter != nil){
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
AudioStreamBasicDescription inputFormat = {0};
|
||||
inputFormat.mSampleRate = _configuration.audioSampleRate;
|
||||
inputFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
inputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
inputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;
|
||||
inputFormat.mFramesPerPacket = 1;
|
||||
inputFormat.mBitsPerChannel = 16;
|
||||
inputFormat.mBytesPerFrame = inputFormat.mBitsPerChannel / 8 * inputFormat.mChannelsPerFrame;
|
||||
inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
|
||||
|
||||
AudioStreamBasicDescription outputFormat; // 这里开始是输出音频格式
|
||||
memset(&outputFormat, 0, sizeof(outputFormat));
|
||||
outputFormat.mSampleRate = inputFormat.mSampleRate; // 采样率保持一致
|
||||
outputFormat.mFormatID = kAudioFormatMPEG4AAC; // AAC编码 kAudioFormatMPEG4AAC kAudioFormatMPEG4AAC_HE_V2
|
||||
outputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;;
|
||||
outputFormat.mFramesPerPacket = 1024; // AAC一帧是1024个字节
|
||||
|
||||
const OSType subtype = kAudioFormatMPEG4AAC;
|
||||
AudioClassDescription requestedCodecs[2] = {
|
||||
{
|
||||
kAudioEncoderComponentType,
|
||||
subtype,
|
||||
kAppleSoftwareAudioCodecManufacturer
|
||||
},
|
||||
{
|
||||
kAudioEncoderComponentType,
|
||||
subtype,
|
||||
kAppleHardwareAudioCodecManufacturer
|
||||
}
|
||||
};
|
||||
OSStatus result = AudioConverterNewSpecific(&inputFormat, &outputFormat, 2, requestedCodecs, &m_converter);
|
||||
|
||||
|
||||
if(result != noErr) return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(AudioClassDescription*)getAudioClassDescriptionWithType:(UInt32)type fromManufacturer:(UInt32)manufacturer { // 获得相应的编码器
|
||||
static AudioClassDescription audioDesc;
|
||||
|
||||
UInt32 encoderSpecifier = type, size = 0;
|
||||
OSStatus status;
|
||||
|
||||
memset(&audioDesc, 0, sizeof(audioDesc));
|
||||
status = AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size);
|
||||
if (status) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
uint32_t count = size / sizeof(AudioClassDescription);
|
||||
AudioClassDescription descs[count];
|
||||
AudioFormatGetProperty(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size, descs);
|
||||
for (uint32_t i = 0; i < count; i++){
|
||||
if ((type == descs[i].mSubType) && (manufacturer == descs[i].mManufacturer)){
|
||||
memcpy(&audioDesc, &descs[i], sizeof(audioDesc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return &audioDesc;
|
||||
}
|
||||
|
||||
#pragma mark -- AudioCallBack
|
||||
OSStatus inputDataProc(AudioConverterRef inConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData,AudioStreamPacketDescription **outDataPacketDescription, void *inUserData) { //<span style="font-family: Arial, Helvetica, sans-serif;">AudioConverterFillComplexBuffer 编码过程中,会要求这个函数来填充输入数据,也就是原始PCM数据</span>
|
||||
AudioBufferList bufferList = *(AudioBufferList*)inUserData;
|
||||
ioData->mBuffers[0].mNumberChannels = 1;
|
||||
ioData->mBuffers[0].mData = bufferList.mBuffers[0].mData;
|
||||
ioData->mBuffers[0].mDataByteSize = bufferList.mBuffers[0].mDataByteSize;
|
||||
return noErr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add ADTS header at the beginning of each and every AAC packet.
|
||||
* This is needed as MediaCodec encoder generates a packet of raw
|
||||
* AAC data.
|
||||
*
|
||||
* Note the packetLen must count in the ADTS header itself.
|
||||
* See: http://wiki.multimedia.cx/index.php?title=ADTS
|
||||
* Also: http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Channel_Configurations
|
||||
**/
|
||||
- (NSData*)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength {
|
||||
int adtsLength = 7;
|
||||
char *packet = malloc(sizeof(char) * adtsLength);
|
||||
// Variables Recycled by addADTStoPacket
|
||||
int profile = 2; //AAC LC
|
||||
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
|
||||
int freqIdx = 4; //44.1KHz
|
||||
int chanCfg = (int)channel; //MPEG-4 Audio Channel Configuration. 1 Channel front-center
|
||||
NSUInteger fullLength = adtsLength + rawDataLength;
|
||||
// fill in ADTS data
|
||||
packet[0] = (char)0xFF; // 11111111 = syncword
|
||||
packet[1] = (char)0xF9; // 1111 1 00 1 = syncword MPEG-2 Layer CRC
|
||||
packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
|
||||
packet[3] = (char)(((chanCfg&3)<<6) + (fullLength>>11));
|
||||
packet[4] = (char)((fullLength&0x7FF) >> 3);
|
||||
packet[5] = (char)(((fullLength&7)<<5) + 0x1F);
|
||||
packet[6] = (char)0xFC;
|
||||
NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES];
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,233 +0,0 @@
|
||||
//
|
||||
// LFLiveAudioConfiguration.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/1.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFLiveAudioConfiguration.h"
|
||||
#import <sys/utsname.h>
|
||||
|
||||
@implementation LFLiveAudioConfiguration
|
||||
|
||||
#pragma mark -- LifyCycle
|
||||
+ (instancetype)defaultConfiguration{
|
||||
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration defaultConfigurationForQuality:LFLiveAudioQuality_Default];
|
||||
return audioConfig;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveAudioQuality)audioQuality{
|
||||
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration new];
|
||||
audioConfig.numberOfChannels = 2;
|
||||
switch (audioQuality) {
|
||||
case LFLiveAudioQuality_Default:{
|
||||
audioConfig.audioBitrate = LFLiveAudioBitRate_64Kbps;
|
||||
}
|
||||
break;
|
||||
case LFLiveAudioQuality_Low:{
|
||||
audioConfig.audioBitrate = LFLiveAudioBitRate_32Kbps;
|
||||
}
|
||||
case LFLiveAudioQuality_High:{
|
||||
audioConfig.audioBitrate = LFLiveAudioBitRate_96Kbps;
|
||||
}
|
||||
case LFLiveAudioQuality_VeryHigh:{
|
||||
audioConfig.audioBitrate = LFLiveAudioBitRate_128Kbps;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
audioConfig.audioSampleRate = [self.class isNewThaniPhone6] ? LFLiveAudioSampleRate_48000Hz : LFLiveAudioSampleRate_44100Hz;
|
||||
|
||||
return audioConfig;
|
||||
}
|
||||
|
||||
- (instancetype)init{
|
||||
if(self = [super init]){
|
||||
_asc = malloc(2);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
if(_asc) free(_asc);
|
||||
}
|
||||
|
||||
#pragma mark Setter
|
||||
- (void)setAudioSampleRate:(LFLiveAudioSampleRate)audioSampleRate{
|
||||
_audioSampleRate = audioSampleRate;
|
||||
NSInteger sampleRateIndex = [self sampleRateIndex:audioSampleRate];
|
||||
self.asc[0] = 0x10 | ((sampleRateIndex>>1) & 0x3);
|
||||
self.asc[1] = ((sampleRateIndex & 0x1)<<7) | ((self.numberOfChannels & 0xF) << 3);
|
||||
}
|
||||
|
||||
- (void)setNumberOfChannels:(NSUInteger)numberOfChannels{
|
||||
_numberOfChannels = numberOfChannels;
|
||||
NSInteger sampleRateIndex = [self sampleRateIndex:self.audioSampleRate];
|
||||
self.asc[0] = 0x10 | ((sampleRateIndex>>1) & 0x3);
|
||||
self.asc[1] = ((sampleRateIndex & 0x1)<<7) | ((numberOfChannels & 0xF) << 3);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (NSInteger)sampleRateIndex:(NSInteger)frequencyInHz{
|
||||
NSInteger sampleRateIndex = 0;
|
||||
switch(frequencyInHz) {
|
||||
case 96000:
|
||||
sampleRateIndex = 0;
|
||||
break;
|
||||
case 88200:
|
||||
sampleRateIndex = 1;
|
||||
break;
|
||||
case 64000:
|
||||
sampleRateIndex = 2;
|
||||
break;
|
||||
case 48000:
|
||||
sampleRateIndex = 3;
|
||||
break;
|
||||
case 44100:
|
||||
sampleRateIndex = 4;
|
||||
break;
|
||||
case 32000:
|
||||
sampleRateIndex = 5;
|
||||
break;
|
||||
case 24000:
|
||||
sampleRateIndex = 6;
|
||||
break;
|
||||
case 22050:
|
||||
sampleRateIndex = 7;
|
||||
break;
|
||||
case 16000:
|
||||
sampleRateIndex = 8;
|
||||
break;
|
||||
case 12000:
|
||||
sampleRateIndex = 9;
|
||||
break;
|
||||
case 11025:
|
||||
sampleRateIndex = 10;
|
||||
break;
|
||||
case 8000:
|
||||
sampleRateIndex = 11;
|
||||
break;
|
||||
case 7350:
|
||||
sampleRateIndex = 12;
|
||||
break;
|
||||
default:
|
||||
sampleRateIndex = 15;
|
||||
}
|
||||
return sampleRateIndex;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -- DeviceCategory
|
||||
+(NSString*)deviceName{
|
||||
struct utsname systemInfo;
|
||||
uname(&systemInfo);
|
||||
|
||||
return [NSString stringWithCString:systemInfo.machine
|
||||
encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
//@"iPad4,1" on 5th Generation iPad (iPad Air) - Wifi
|
||||
//@"iPad4,2" on 5th Generation iPad (iPad Air) - Cellular
|
||||
//@"iPad4,4" on 2nd Generation iPad Mini - Wifi
|
||||
//@"iPad4,5" on 2nd Generation iPad Mini - Cellular
|
||||
//@"iPad4,7" on 3rd Generation iPad Mini - Wifi (model A1599)
|
||||
//@"iPhone7,1" on iPhone 6 Plus
|
||||
//@"iPhone7,2" on iPhone 6
|
||||
//@"iPhone8,1" on iPhone 6S
|
||||
//@"iPhone8,2" on iPhone 6S Plus
|
||||
|
||||
+(BOOL) isNewThaniPhone6{
|
||||
NSString *device = [self deviceName];
|
||||
NSLog(@"device %@", device);
|
||||
if (device == nil) {
|
||||
return NO;
|
||||
}
|
||||
NSArray *array = [device componentsSeparatedByString:@","];
|
||||
if (array.count <2) {
|
||||
return NO;
|
||||
}
|
||||
NSString *model = [array objectAtIndex:0];
|
||||
NSLog(@"model %@", model);
|
||||
if ([model hasPrefix:@"iPhone"]) {
|
||||
NSString *str1 = [model substringFromIndex:[@"iPhone" length]];
|
||||
NSUInteger num = [str1 integerValue];
|
||||
NSLog(@"num %lu", (unsigned long)num);
|
||||
if (num > 7) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
if ([model hasPrefix:@"iPad"]) {
|
||||
NSString *str1 = [model substringFromIndex:[@"iPad" length]];
|
||||
NSUInteger num = [str1 integerValue];
|
||||
if (num > 4) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark -- Encoder
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:@(self.numberOfChannels) forKey:@"numberOfChannels"];
|
||||
[aCoder encodeObject:@(self.audioSampleRate) forKey:@"audioSampleRate"];
|
||||
[aCoder encodeObject:@(self.audioBitrate) forKey:@"audioBitrate"];
|
||||
[aCoder encodeObject:[NSString stringWithUTF8String:self.asc] forKey:@"asc"];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super init];
|
||||
_numberOfChannels = [[aDecoder decodeObjectForKey:@"numberOfChannels"] unsignedIntegerValue];
|
||||
_audioSampleRate = [[aDecoder decodeObjectForKey:@"audioSampleRate"] unsignedIntegerValue];
|
||||
_audioBitrate = [[aDecoder decodeObjectForKey:@"audioBitrate"] unsignedIntegerValue];
|
||||
_asc = strdup([[aDecoder decodeObjectForKey:@"asc"] cStringUsingEncoding:NSUTF8StringEncoding]);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other{
|
||||
if (other == self) {
|
||||
return YES;
|
||||
} else if (![super isEqual:other]) {
|
||||
return NO;
|
||||
} else {
|
||||
LFLiveAudioConfiguration *object = other;
|
||||
return object.numberOfChannels == self.numberOfChannels &&
|
||||
object.audioBitrate == self.audioBitrate &&
|
||||
strcmp(object.asc, self.asc) == 0 &&
|
||||
object.audioSampleRate == self.audioSampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger hash = 0;
|
||||
NSArray *values = @[@(_numberOfChannels),
|
||||
@(_audioSampleRate),
|
||||
[NSString stringWithUTF8String:self.asc],
|
||||
@(_audioBitrate)];
|
||||
|
||||
for (NSObject *value in values) {
|
||||
hash ^= value.hash;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(nullable NSZone *)zone{
|
||||
LFLiveAudioConfiguration *other = [self.class defaultConfiguration];
|
||||
return other;
|
||||
}
|
||||
|
||||
- (NSString *)description{
|
||||
NSMutableString *desc = @"".mutableCopy;
|
||||
[desc appendFormat:@"<LFLiveAudioConfiguration: %p>",self];
|
||||
[desc appendFormat:@" numberOfChannels:%zi",self.numberOfChannels];
|
||||
[desc appendFormat:@" audioSampleRate:%zi",self.audioSampleRate];
|
||||
[desc appendFormat:@" audioBitrate:%zi",self.audioBitrate];
|
||||
[desc appendFormat:@" audioHeader:%@",[NSString stringWithUTF8String:self.asc]];
|
||||
return desc;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,309 +0,0 @@
|
||||
//
|
||||
// LFLiveVideoConfiguration.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/1.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFLiveVideoConfiguration.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation LFLiveVideoConfiguration
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
+ (instancetype)defaultConfiguration{
|
||||
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Default];
|
||||
return configuration;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality{
|
||||
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:videoQuality orientation:UIInterfaceOrientationPortrait];
|
||||
return configuration;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality orientation:(UIInterfaceOrientation)orientation{
|
||||
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration new];
|
||||
switch (videoQuality) {
|
||||
case LFLiveVideoQuality_Low1:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset360x640;
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 500 * 1000;
|
||||
configuration.videoMaxBitRate = 600 * 1000;
|
||||
configuration.videoMinBitRate = 400 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Low2:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset360x640;
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 600 * 1000;
|
||||
configuration.videoMaxBitRate = 720 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Low3:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset360x640;
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 600 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium1:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset540x960;
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium2:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset540x960;
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium3:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset540x960;
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 1000 * 1000;
|
||||
configuration.videoMaxBitRate = 1200 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High1:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 1000 * 1000;
|
||||
configuration.videoMaxBitRate = 1200 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High2:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 1200 * 1000;
|
||||
configuration.videoMaxBitRate = 1440 * 1000;
|
||||
configuration.videoMinBitRate = 800 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High3:
|
||||
{
|
||||
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 1200 * 1000;
|
||||
configuration.videoMaxBitRate = 1440 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
configuration.sessionPreset = [configuration supportSessionPreset:configuration.sessionPreset];
|
||||
configuration.videoMaxKeyframeInterval = configuration.videoFrameRate*2;
|
||||
configuration.orientation = orientation;
|
||||
CGSize size = configuration.videoSize;
|
||||
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown){
|
||||
configuration.videoSize = CGSizeMake(size.width, size.height);
|
||||
}else{
|
||||
configuration.videoSize = CGSizeMake(size.height, size.width);
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
#pragma mark -- Setter Getter
|
||||
- (NSString*)avSessionPreset{
|
||||
NSString *avSessionPreset = nil;
|
||||
switch (self.sessionPreset) {
|
||||
case LFCaptureSessionPreset360x640:
|
||||
{
|
||||
avSessionPreset = AVCaptureSessionPreset640x480;
|
||||
}
|
||||
break;
|
||||
case LFCaptureSessionPreset540x960:
|
||||
{
|
||||
avSessionPreset = AVCaptureSessionPresetiFrame960x540;
|
||||
}
|
||||
break;
|
||||
case LFCaptureSessionPreset720x1280:
|
||||
{
|
||||
avSessionPreset = AVCaptureSessionPreset1280x720;
|
||||
}
|
||||
break;
|
||||
default:{
|
||||
avSessionPreset = AVCaptureSessionPreset640x480;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return avSessionPreset;
|
||||
}
|
||||
|
||||
- (void)setVideoMaxBitRate:(NSUInteger)videoMaxBitRate{
|
||||
if(videoMaxBitRate <= _videoBitRate) return;
|
||||
_videoMaxBitRate = videoMaxBitRate;
|
||||
}
|
||||
|
||||
- (void)setVideoMinBitRate:(NSUInteger)videoMinBitRate{
|
||||
if(videoMinBitRate >= _videoBitRate) return;
|
||||
_videoMinBitRate = videoMinBitRate;
|
||||
}
|
||||
|
||||
- (void)setVideoMaxFrameRate:(NSUInteger)videoMaxFrameRate{
|
||||
if(videoMaxFrameRate <= _videoFrameRate) return;
|
||||
_videoMaxFrameRate = videoMaxFrameRate;
|
||||
}
|
||||
|
||||
- (void)setVideoMinFrameRate:(NSUInteger)videoMinFrameRate{
|
||||
if(videoMinFrameRate >= _videoFrameRate) return;
|
||||
_videoMinFrameRate = videoMinFrameRate;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -- Custom Method
|
||||
- (LFLiveVideoSessionPreset)supportSessionPreset:(LFLiveVideoSessionPreset)sessionPreset{
|
||||
NSString *avSessionPreset = [self avSessionPreset];
|
||||
AVCaptureSession *session = [[AVCaptureSession alloc] init];
|
||||
|
||||
if(![session canSetSessionPreset:avSessionPreset]){
|
||||
if(sessionPreset == LFCaptureSessionPreset720x1280){
|
||||
sessionPreset = LFCaptureSessionPreset540x960;
|
||||
if(![session canSetSessionPreset:avSessionPreset]){
|
||||
sessionPreset = LFCaptureSessionPreset360x640;
|
||||
}
|
||||
}else if(sessionPreset == LFCaptureSessionPreset540x960){
|
||||
sessionPreset = LFCaptureSessionPreset360x640;
|
||||
}
|
||||
}
|
||||
return sessionPreset;
|
||||
}
|
||||
|
||||
- (BOOL)isClipVideo{
|
||||
return self.sessionPreset == LFCaptureSessionPreset360x640 ? YES : NO;
|
||||
}
|
||||
|
||||
#pragma mark -- encoder
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:[NSValue valueWithCGSize:self.videoSize] forKey:@"videoSize"];
|
||||
[aCoder encodeObject:@(self.videoFrameRate) forKey:@"videoFrameRate"];
|
||||
[aCoder encodeObject:@(self.videoMaxKeyframeInterval) forKey:@"videoMaxKeyframeInterval"];
|
||||
[aCoder encodeObject:@(self.videoBitRate) forKey:@"videoBitRate"];
|
||||
[aCoder encodeObject:@(self.sessionPreset) forKey:@"sessionPreset"];
|
||||
[aCoder encodeObject:@(self.orientation) forKey:@"orientation"];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super init];
|
||||
_videoSize = [[aDecoder decodeObjectForKey:@"videoSize"] CGSizeValue];
|
||||
_videoFrameRate = [[aDecoder decodeObjectForKey:@"videoFrameRate"] unsignedIntegerValue];
|
||||
_videoMaxKeyframeInterval = [[aDecoder decodeObjectForKey:@"videoMaxKeyframeInterval"] unsignedIntegerValue];
|
||||
_videoBitRate = [[aDecoder decodeObjectForKey:@"videoBitRate"] unsignedIntegerValue];
|
||||
_sessionPreset = [[aDecoder decodeObjectForKey:@"sessionPreset"] unsignedIntegerValue];
|
||||
_orientation = [[aDecoder decodeObjectForKey:@"orientation"] unsignedIntegerValue];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger hash = 0;
|
||||
NSArray *values = @[[NSValue valueWithCGSize:self.videoSize],
|
||||
@(self.videoFrameRate),
|
||||
@(self.videoMaxFrameRate),
|
||||
@(self.videoMinFrameRate),
|
||||
@(self.videoMaxKeyframeInterval),
|
||||
@(self.videoBitRate),
|
||||
@(self.videoMaxBitRate),
|
||||
@(self.videoMinBitRate),
|
||||
@(self.isClipVideo),
|
||||
self.avSessionPreset,
|
||||
@(self.sessionPreset),
|
||||
@(self.orientation),];
|
||||
|
||||
for (NSObject *value in values) {
|
||||
hash ^= value.hash;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if (other == self) {
|
||||
return YES;
|
||||
} else if (![super isEqual:other]) {
|
||||
return NO;
|
||||
} else {
|
||||
LFLiveVideoConfiguration *object = other;
|
||||
return CGSizeEqualToSize(object.videoSize, self.videoSize) &&
|
||||
object.videoFrameRate == self.videoFrameRate &&
|
||||
object.videoMaxFrameRate == self.videoMaxFrameRate &&
|
||||
object.videoMinFrameRate == self.videoMinFrameRate &&
|
||||
object.videoMaxKeyframeInterval == self.videoMaxKeyframeInterval &&
|
||||
object.videoBitRate == self.videoBitRate &&
|
||||
object.videoMaxBitRate == self.videoMaxBitRate &&
|
||||
object.videoMinBitRate == self.videoMinBitRate &&
|
||||
object.isClipVideo == self.isClipVideo &&
|
||||
[object.avSessionPreset isEqualToString:self.avSessionPreset] &&
|
||||
object.sessionPreset == self.sessionPreset &&
|
||||
object.orientation == self.orientation;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(nullable NSZone *)zone{
|
||||
LFLiveVideoConfiguration *other = [self.class defaultConfiguration];
|
||||
return other;
|
||||
}
|
||||
|
||||
- (NSString *)description{
|
||||
NSMutableString *desc = @"".mutableCopy;
|
||||
[desc appendFormat:@"<LFLiveVideoConfiguration: %p>",self];
|
||||
[desc appendFormat:@" videoSize:%@",NSStringFromCGSize(self.videoSize)];
|
||||
[desc appendFormat:@" videoFrameRate:%zi",self.videoFrameRate];
|
||||
[desc appendFormat:@" videoMaxFrameRate:%zi",self.videoMaxFrameRate];
|
||||
[desc appendFormat:@" videoMinFrameRate:%zi",self.videoMinFrameRate];
|
||||
[desc appendFormat:@" videoMaxKeyframeInterval:%zi",self.videoMaxKeyframeInterval];
|
||||
[desc appendFormat:@" videoBitRate:%zi",self.videoBitRate];
|
||||
[desc appendFormat:@" videoMaxBitRate:%zi",self.videoMaxBitRate];
|
||||
[desc appendFormat:@" videoMinBitRate:%zi",self.videoMinBitRate];
|
||||
[desc appendFormat:@" isClipVideo:%zi",self.isClipVideo];
|
||||
[desc appendFormat:@" avSessionPreset:%@",self.avSessionPreset];
|
||||
[desc appendFormat:@" sessionPreset:%zi",self.sessionPreset];
|
||||
[desc appendFormat:@" orientation:%zi",self.orientation];
|
||||
return desc;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,276 +0,0 @@
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform highp vec2 singleStepOffset;
|
||||
uniform highp vec4 params;
|
||||
uniform highp float brightness;
|
||||
|
||||
const highp vec3 W = vec3(0.299,0.587,0.114);
|
||||
const highp mat3 saturateMatrix = mat3(
|
||||
1.1102,-0.0598,-0.061,
|
||||
-0.0774,1.0826,-0.1186,
|
||||
-0.0228,-0.0228,1.1772);
|
||||
highp vec2 blurCoordinates[24];
|
||||
|
||||
highp float hardLight(highp float color) {
|
||||
if(color <= 0.5)
|
||||
color = color * color * 2.0;
|
||||
else
|
||||
color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0);
|
||||
return color;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec3 centralColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
blurCoordinates[0] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -10.0);
|
||||
blurCoordinates[1] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 10.0);
|
||||
blurCoordinates[2] = textureCoordinate.xy + singleStepOffset * vec2(-10.0, 0.0);
|
||||
blurCoordinates[3] = textureCoordinate.xy + singleStepOffset * vec2(10.0, 0.0);
|
||||
blurCoordinates[4] = textureCoordinate.xy + singleStepOffset * vec2(5.0, -8.0);
|
||||
blurCoordinates[5] = textureCoordinate.xy + singleStepOffset * vec2(5.0, 8.0);
|
||||
blurCoordinates[6] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, 8.0);
|
||||
blurCoordinates[7] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, -8.0);
|
||||
blurCoordinates[8] = textureCoordinate.xy + singleStepOffset * vec2(8.0, -5.0);
|
||||
blurCoordinates[9] = textureCoordinate.xy + singleStepOffset * vec2(8.0, 5.0);
|
||||
blurCoordinates[10] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, 5.0);
|
||||
blurCoordinates[11] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, -5.0);
|
||||
blurCoordinates[12] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -6.0);
|
||||
blurCoordinates[13] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 6.0);
|
||||
blurCoordinates[14] = textureCoordinate.xy + singleStepOffset * vec2(6.0, 0.0);
|
||||
blurCoordinates[15] = textureCoordinate.xy + singleStepOffset * vec2(-6.0, 0.0);
|
||||
blurCoordinates[16] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, -4.0);
|
||||
blurCoordinates[17] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, 4.0);
|
||||
blurCoordinates[18] = textureCoordinate.xy + singleStepOffset * vec2(4.0, -4.0);
|
||||
blurCoordinates[19] = textureCoordinate.xy + singleStepOffset * vec2(4.0, 4.0);
|
||||
blurCoordinates[20] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, -2.0);
|
||||
blurCoordinates[21] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, 2.0);
|
||||
blurCoordinates[22] = textureCoordinate.xy + singleStepOffset * vec2(2.0, -2.0);
|
||||
blurCoordinates[23] = textureCoordinate.xy + singleStepOffset * vec2(2.0, 2.0);
|
||||
|
||||
highp float sampleColor = centralColor.g * 22.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[0]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[1]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[2]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[3]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[4]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[5]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[6]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[7]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[8]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[9]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[10]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[11]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[12]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[13]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[14]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[15]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[16]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[17]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[18]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[19]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[20]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[21]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[22]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[23]).g * 3.0;
|
||||
|
||||
sampleColor = sampleColor / 62.0;
|
||||
|
||||
highp float highPass = centralColor.g - sampleColor + 0.5;
|
||||
|
||||
for(int i = 0; i < 5;i++)
|
||||
{
|
||||
highPass = hardLight(highPass);
|
||||
}
|
||||
highp float lumance = dot(centralColor, W);
|
||||
|
||||
highp float alpha = pow(lumance, params.r);
|
||||
|
||||
highp vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
|
||||
|
||||
smoothColor.r = clamp(pow(smoothColor.r, params.g),0.0,1.0);
|
||||
smoothColor.g = clamp(pow(smoothColor.g, params.g),0.0,1.0);
|
||||
smoothColor.b = clamp(pow(smoothColor.b, params.g),0.0,1.0);
|
||||
|
||||
highp vec3 lvse = vec3(1.0)-(vec3(1.0)-smoothColor)*(vec3(1.0)-centralColor);
|
||||
highp vec3 bianliang = max(smoothColor, centralColor);
|
||||
highp vec3 rouguang = 2.0*centralColor*smoothColor + centralColor*centralColor - 2.0*centralColor*centralColor*smoothColor;
|
||||
|
||||
gl_FragColor = vec4(mix(centralColor, lvse, alpha), 1.0);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, bianliang, alpha);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, rouguang, params.b);
|
||||
|
||||
highp vec3 satcolor = gl_FragColor.rgb * saturateMatrix;
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, satcolor, params.a);
|
||||
gl_FragColor.rgb = vec3(gl_FragColor.rgb + vec3(brightness));
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mediump vec2 singleStepOffset;
|
||||
uniform mediump vec4 params;
|
||||
uniform mediump float brightness;
|
||||
const mediump mat3 saturateMatrix = mat3(
|
||||
1.1102,-0.0598,-0.061,
|
||||
-0.0774,1.0826,-0.1186,
|
||||
-0.0228,-0.0228,1.1772);
|
||||
const mediump vec3 W = vec3(0.299,0.587,0.114);
|
||||
mediump vec2 blurCoordinates[24];
|
||||
|
||||
mediump float hardLight(mediump float color)
|
||||
{
|
||||
if(color <= 0.5)
|
||||
color = color * color * 2.0;
|
||||
else
|
||||
color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0);
|
||||
return color;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 centralColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
blurCoordinates[0] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -10.0);
|
||||
blurCoordinates[1] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 10.0);
|
||||
blurCoordinates[2] = textureCoordinate.xy + singleStepOffset * vec2(-10.0, 0.0);
|
||||
blurCoordinates[3] = textureCoordinate.xy + singleStepOffset * vec2(10.0, 0.0);
|
||||
blurCoordinates[4] = textureCoordinate.xy + singleStepOffset * vec2(5.0, -8.0);
|
||||
blurCoordinates[5] = textureCoordinate.xy + singleStepOffset * vec2(5.0, 8.0);
|
||||
blurCoordinates[6] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, 8.0);
|
||||
blurCoordinates[7] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, -8.0);
|
||||
blurCoordinates[8] = textureCoordinate.xy + singleStepOffset * vec2(8.0, -5.0);
|
||||
blurCoordinates[9] = textureCoordinate.xy + singleStepOffset * vec2(8.0, 5.0);
|
||||
blurCoordinates[10] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, 5.0);
|
||||
blurCoordinates[11] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, -5.0);
|
||||
blurCoordinates[12] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -6.0);
|
||||
blurCoordinates[13] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 6.0);
|
||||
blurCoordinates[14] = textureCoordinate.xy + singleStepOffset * vec2(6.0, 0.0);
|
||||
blurCoordinates[15] = textureCoordinate.xy + singleStepOffset * vec2(-6.0, 0.0);
|
||||
blurCoordinates[16] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, -4.0);
|
||||
blurCoordinates[17] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, 4.0);
|
||||
blurCoordinates[18] = textureCoordinate.xy + singleStepOffset * vec2(4.0, -4.0);
|
||||
blurCoordinates[19] = textureCoordinate.xy + singleStepOffset * vec2(4.0, 4.0);
|
||||
blurCoordinates[20] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, -2.0);
|
||||
blurCoordinates[21] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, 2.0);
|
||||
blurCoordinates[22] = textureCoordinate.xy + singleStepOffset * vec2(2.0, -2.0);
|
||||
blurCoordinates[23] = textureCoordinate.xy + singleStepOffset * vec2(2.0, 2.0);
|
||||
|
||||
mediump float sampleColor = centralColor.g * 22.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[0]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[1]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[2]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[3]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[4]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[5]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[6]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[7]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[8]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[9]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[10]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[11]).g;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[12]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[13]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[14]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[15]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[16]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[17]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[18]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[19]).g * 2.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[20]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[21]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[22]).g * 3.0;
|
||||
sampleColor += texture2D(inputImageTexture, blurCoordinates[23]).g * 3.0;
|
||||
|
||||
sampleColor = sampleColor / 62.0;
|
||||
|
||||
mediump float highPass = centralColor.g - sampleColor + 0.5;
|
||||
|
||||
for(int i = 0; i < 5;i++)
|
||||
{
|
||||
highPass = hardLight(highPass);
|
||||
}
|
||||
mediump float luminance = dot(centralColor, W);
|
||||
|
||||
mediump float alpha = pow(luminance, params);
|
||||
|
||||
mediump vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
|
||||
|
||||
smoothColor.r = clamp(pow(smoothColor.r, params.g),0.0,1.0);
|
||||
smoothColor.g = clamp(pow(smoothColor.g, params.g),0.0,1.0);
|
||||
smoothColor.b = clamp(pow(smoothColor.b, params.g),0.0,1.0);
|
||||
|
||||
mediump vec3 lvse = vec3(1.0)-(vec3(1.0)-smoothColor)*(vec3(1.0)-centralColor);
|
||||
mediump vec3 bianliang = max(smoothColor, centralColor);
|
||||
mediump vec3 rouguang = 2.0*centralColor*smoothColor + centralColor*centralColor - 2.0*centralColor*centralColor*smoothColor;
|
||||
|
||||
gl_FragColor = vec4(mix(centralColor, lvse, alpha), 1.0);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, bianliang, alpha);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, rouguang, params.b);
|
||||
|
||||
mediump vec3 satcolor = gl_FragColor.rgb * saturateMatrix;
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, satcolor, params.a);
|
||||
gl_FragColor.rgb = vec3(gl_FragColor.rgb + vec3(brightness));
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation LFGPUImageBeautyFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageBeautyFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
_toneLevel = 0.5;
|
||||
_beautyLevel = 0.5;
|
||||
_brightLevel = 0.5;
|
||||
[self setParams:_beautyLevel tone:_toneLevel];
|
||||
[self setBrightLevel:_brightLevel];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex
|
||||
{
|
||||
CGSize oldInputSize = inputTextureSize;
|
||||
[super setInputSize:newSize atIndex:textureIndex];
|
||||
inputTextureSize = newSize;
|
||||
|
||||
CGPoint offset = CGPointMake(2.0f / inputTextureSize.width, 2.0 / inputTextureSize.height);
|
||||
[self setPoint:offset forUniformName:@"singleStepOffset"];
|
||||
}
|
||||
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel
|
||||
{
|
||||
_beautyLevel = beautyLevel;
|
||||
[self setParams:_beautyLevel tone:_toneLevel];
|
||||
}
|
||||
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel
|
||||
{
|
||||
_brightLevel = brightLevel;
|
||||
[self setFloat:0.6 * (-0.5 + brightLevel) forUniformName:@"brightness"];
|
||||
}
|
||||
|
||||
- (void)setParams:(CGFloat)beauty tone:(CGFloat)tone {
|
||||
GPUVector4 fBeautyParam;
|
||||
fBeautyParam.one = 1.0 - 0.6 * beauty;
|
||||
fBeautyParam.two = 1.0 - 0.3 * beauty;
|
||||
fBeautyParam.three = 0.1 + 0.3 * tone;
|
||||
fBeautyParam.four = 0.1 + 0.3 * tone;
|
||||
[self setFloatVec4:fBeautyParam forUniform:@"params"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface LFGPUImageEmptyFilter : GPUImageFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,46 +0,0 @@
|
||||
#import "LFGPUImageEmptyFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kLFGPUImageEmptyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((textureColor.rgb), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageInvertFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((textureColor.rgb), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation LFGPUImageEmptyFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageEmptyFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// LFFlvPackage.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFStreamPackage.h"
|
||||
|
||||
@interface LFFlvPackage : NSObject<LFStreamPackage>
|
||||
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
/// @name Initializer
|
||||
///=============================================================================
|
||||
- (nullable instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
@end
|
||||
@@ -1,345 +0,0 @@
|
||||
//
|
||||
// LFFlvPackage.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFFlvPackage.h"
|
||||
#include "flv/flv.h"
|
||||
#include "flv/info.h"
|
||||
|
||||
#define kTagLength (4)
|
||||
#define kAVCPacketHeaderSize (5)
|
||||
static const byte kAudioDataHeader = 0xAF;
|
||||
#define swap_uint32_ htonl
|
||||
|
||||
@interface LFFlvPackage (){
|
||||
dispatch_semaphore_t _lock;
|
||||
NSData *_sps;
|
||||
NSData *_pps;
|
||||
NSData *_spec;
|
||||
CGSize _videoSize;
|
||||
FILE *fp;
|
||||
BOOL enabledWriteVideoFile;
|
||||
BOOL enabledWriteFlvHeaderVideoFile;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFFlvPackage
|
||||
|
||||
- (instancetype)initWithVideoSize:(CGSize)videoSize{
|
||||
if(CGSizeEqualToSize(videoSize, CGSizeZero)) @throw [NSException exceptionWithName:@"LFFlvPackage init error" reason:@"video size is zero" userInfo:nil];
|
||||
if(self = [super init]){
|
||||
_videoSize = videoSize;
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
#ifdef DEBUG
|
||||
enabledWriteVideoFile = NO;
|
||||
[self initForFilePath];
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
|
||||
}
|
||||
|
||||
#pragma mark -- LFStreamPackage Delegate
|
||||
- (NSData*)aaCPacket:(LFAudioFrame*)audioFrame{
|
||||
NSMutableData *result = [[NSMutableData alloc] init];
|
||||
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if(!_spec){
|
||||
_spec = audioFrame.audioInfo;
|
||||
}
|
||||
|
||||
if(!_sps || !_pps){
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// write audio data
|
||||
uint32 kAACPacketSize = 2;
|
||||
|
||||
NSInteger buffer_size = kAACPacketSize + audioFrame.data.length + FLV_TAG_SIZE;
|
||||
NSInteger packet_size = buffer_size + kTagLength;
|
||||
|
||||
[result appendData:[[self class] flvTagHeader:FLV_TAG_TYPE_AUDIO size:(int32_t)audioFrame.data.length + kAACPacketSize timeStamp:(uint32)audioFrame.timestamp]];
|
||||
|
||||
byte format[2] = { kAudioDataHeader, 0x01};
|
||||
[result appendBytes:format length:sizeof(format)];
|
||||
[result appendData:audioFrame.data];
|
||||
|
||||
uint32 pre_size = swap_uint32_(packet_size-4);
|
||||
[result appendBytes:&pre_size length:sizeof(uint32)];
|
||||
|
||||
audioFrame.header = [[self class] flvHeads:_videoSize.width videoHeight:_videoSize.height sps:_sps pps:_pps audioHeader:_spec];
|
||||
if(enabledWriteVideoFile){
|
||||
if(!enabledWriteFlvHeaderVideoFile){
|
||||
enabledWriteFlvHeaderVideoFile = YES;
|
||||
fwrite(audioFrame.header.bytes, 1,audioFrame.header.length,self->fp);
|
||||
}
|
||||
}
|
||||
|
||||
if(enabledWriteVideoFile) {
|
||||
fwrite(result.bytes, 1, result.length,self->fp);
|
||||
}
|
||||
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSData*)h264Packet:(LFVideoFrame*)videoFrame{
|
||||
NSMutableData *result = [[NSMutableData alloc] init];
|
||||
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if(!_sps || !_pps){
|
||||
_sps = videoFrame.sps;
|
||||
_pps = videoFrame.pps;
|
||||
}
|
||||
|
||||
if(!_spec){
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return nil;
|
||||
}
|
||||
|
||||
videoFrame.header = [[self class] flvHeads:_videoSize.width videoHeight:_videoSize.height sps:_sps pps:_pps audioHeader:_spec];
|
||||
if(enabledWriteVideoFile){
|
||||
if(!enabledWriteFlvHeaderVideoFile){
|
||||
enabledWriteFlvHeaderVideoFile = YES;
|
||||
fwrite(videoFrame.header.bytes, 1,videoFrame.header.length,self->fp);
|
||||
}
|
||||
}
|
||||
|
||||
// write video data
|
||||
// Size + buffer size(4 bytes)
|
||||
uint32 kAVCPacketSize = kAVCPacketHeaderSize + 4;
|
||||
|
||||
size_t buffer_size = kAVCPacketSize + videoFrame.data.length + FLV_TAG_SIZE;
|
||||
size_t packet_size = buffer_size + kTagLength;
|
||||
|
||||
[result appendData:[[self class] flvTagHeader:FLV_TAG_TYPE_VIDEO size:(int32_t)videoFrame.data.length + kAVCPacketSize timeStamp:(uint32)videoFrame.timestamp]];
|
||||
[result appendData:[[self class] h264PacketHeader:videoFrame.isKeyFrame nalu:true]];
|
||||
|
||||
// write length
|
||||
size_t size = videoFrame.data.length;
|
||||
byte length[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
length[0] = (size >> 24) & 0xff;
|
||||
length[1] = (size >> 16) & 0xff;
|
||||
length[2] = (size >> 8) & 0xff;
|
||||
length[3] = (size >> 0) & 0xff;
|
||||
[result appendBytes:length length:sizeof(length)];
|
||||
|
||||
// write tag data
|
||||
[result appendData:videoFrame.data];
|
||||
|
||||
uint32 pre_size = swap_uint32_(packet_size-4);
|
||||
[result appendBytes:&pre_size length:sizeof(uint32)];
|
||||
|
||||
if(enabledWriteVideoFile) {
|
||||
fwrite(result.bytes, 1, result.length,self->fp);
|
||||
}
|
||||
|
||||
dispatch_semaphore_signal(_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark -- FLV
|
||||
int stream_buffer_write_offset = 0;
|
||||
static size_t stream_buffer_write(const void * in_buffer, size_t size, void * user_data) {
|
||||
memcpy(user_data+stream_buffer_write_offset, in_buffer, size);
|
||||
stream_buffer_write_offset += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
+ (NSData*)flvHeader{
|
||||
NSMutableData *result = [[NSMutableData alloc] init];
|
||||
// 写入 flv header信息 /*<464c5601 05000000 09000000 00>*/
|
||||
flv_header header = { };
|
||||
uint32_be offset = swap_uint32_(FLV_HEADER_SIZE);
|
||||
byte extend[kTagLength] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
[result appendBytes:FLV_SIGNATURE length:sizeof(header.signature)];
|
||||
uint8 version[] = {FLV_VERSION};
|
||||
[result appendBytes:&version length:1];
|
||||
uint8 flag[] = {FLV_FLAG_VIDEO | FLV_FLAG_AUDIO};
|
||||
[result appendBytes:&flag length:1];
|
||||
[result appendBytes:&offset length:sizeof(uint32_be)];
|
||||
[result appendBytes:extend length:kTagLength];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSData*)flvTagHeader:(uint8)type size:(uint32)size timeStamp:(uint32)timeStamp{
|
||||
flv_tag tag;
|
||||
tag.type = type;
|
||||
tag.body_length = uint32_to_uint24_be(size);
|
||||
flv_tag_set_timestamp(&tag, timeStamp);
|
||||
tag.stream_id = uint32_to_uint24_be(0);
|
||||
|
||||
return [NSData dataWithBytes:&tag length:FLV_TAG_SIZE];
|
||||
}
|
||||
|
||||
+ (NSData*)h264PacketHeader:(BOOL)keyFrame nalu:(BOOL)nalu{
|
||||
byte header[kAVCPacketHeaderSize] = { 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
header[0] = (keyFrame ? 0x10 : 0x20) | 0x07;
|
||||
header[1] = nalu ? 0x01 : 0x00; // 1: AVC NALU 0: AVC sequence header
|
||||
// 后三个字节为Composition time,在AVC中无用
|
||||
return [NSData dataWithBytes:header length:sizeof(header)];
|
||||
}
|
||||
|
||||
+ (NSData*)metaData:(NSInteger)width height:(NSInteger)height{
|
||||
NSMutableData *result = [[NSMutableData alloc] init];
|
||||
|
||||
flv_metadata meta;
|
||||
meta.on_metadata_name = amf_str("onMetaData");
|
||||
meta.on_metadata = amf_associative_array_new();
|
||||
amf_associative_array_add(meta.on_metadata, "width",
|
||||
amf_number_new(width));
|
||||
amf_associative_array_add(meta.on_metadata, "height",
|
||||
amf_number_new(height));
|
||||
amf_associative_array_add(meta.on_metadata, "videocodecid",
|
||||
amf_number_new((number64)FLV_VIDEO_TAG_CODEC_AVC));
|
||||
//usage = base::IntToString(params_.audio_sample_rate);
|
||||
//amf_associative_array_add(meta.on_metadata, "audiosamplerate",
|
||||
// amf_str(usage.c_str()));
|
||||
//usage = base::IntToString(params_.audio_sample_size);
|
||||
//amf_associative_array_add(meta.on_metadata, "audiosamplesize",
|
||||
// amf_str(usage.c_str()));
|
||||
amf_associative_array_add(meta.on_metadata, "stereo", amf_boolean_new(1)); // 对AAC格式: 总为 1
|
||||
amf_associative_array_add(meta.on_metadata, "audiocodecid",
|
||||
amf_number_new((number64)FLV_AUDIO_TAG_SOUND_FORMAT_AAC));
|
||||
// create the onMetaData tag
|
||||
uint32 on_metadata_name_size = (uint32)amf_data_size(meta.on_metadata_name);
|
||||
uint32 on_metadata_size = (uint32)amf_data_size(meta.on_metadata);
|
||||
uint32 meta_size = on_metadata_name_size + on_metadata_size;
|
||||
|
||||
size_t buffer_size = meta_size + FLV_TAG_SIZE;
|
||||
size_t packet_size = true ? buffer_size + kTagLength : buffer_size;
|
||||
[result appendData:[[self class] flvTagHeader:FLV_TAG_TYPE_META size:meta_size timeStamp:0]];
|
||||
|
||||
byte metaName[1024] = {0};
|
||||
byte metaData[1024] = {0};
|
||||
|
||||
stream_buffer_write_offset = 0;
|
||||
size_t metanamelen = amf_data_write(meta.on_metadata_name, stream_buffer_write, metaName);
|
||||
|
||||
stream_buffer_write_offset = 0;
|
||||
size_t metalen = amf_data_write(meta.on_metadata, stream_buffer_write, metaData);
|
||||
|
||||
amf_data_free(meta.on_metadata_name);
|
||||
amf_data_free(meta.on_metadata);
|
||||
|
||||
[result appendBytes:metaName length:metanamelen];
|
||||
[result appendBytes:metaData length:metalen];
|
||||
uint32 pre_size = swap_uint32_(packet_size-4);//为解决第一个pretagsize多了4个而减去4
|
||||
[result appendBytes:&pre_size length:sizeof(uint32)];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSData*)flvTagWithVideoHeader:(NSData*)sps pps:(NSData*)pps{
|
||||
NSMutableData *result = [[NSMutableData alloc] init];
|
||||
// 封装AVC sequence header
|
||||
const size_t kExtendSize = 11;
|
||||
size_t buffer_size = sps.length + pps.length + kExtendSize;
|
||||
|
||||
// AVCPacket header size
|
||||
size_t body_size = kAVCPacketHeaderSize + buffer_size;
|
||||
size_t packet_size = body_size + FLV_TAG_SIZE;
|
||||
// AVCDecoderConfigurationRecord
|
||||
[result appendData:[[self class] flvTagHeader:FLV_TAG_TYPE_VIDEO size:(UInt32)body_size timeStamp:0]];
|
||||
[result appendData:[[self class] h264PacketHeader:YES nalu:NO]];
|
||||
|
||||
uint8 configuration1[] = {0x01};
|
||||
[result appendBytes:&configuration1 length:1];
|
||||
[result appendBytes:&sps.bytes[1] length:1];
|
||||
[result appendBytes:&sps.bytes[2] length:1];
|
||||
[result appendBytes:&sps.bytes[3] length:1];
|
||||
uint8 configuration2[] = {0xff};
|
||||
[result appendBytes:&configuration2 length:1];
|
||||
|
||||
// sps
|
||||
uint8 sps1[] = {0xe1};
|
||||
[result appendBytes:&sps1 length:1];
|
||||
uint8 sps2[] = {(sps.length >> 8) & 0xff};
|
||||
[result appendBytes:&sps2 length:1];
|
||||
uint8 sps3[] = {sps.length & 0xff};
|
||||
[result appendBytes:&sps3 length:1];
|
||||
[result appendBytes:sps.bytes length:sps.length];
|
||||
|
||||
|
||||
// pps
|
||||
uint8 pps1[] = {0x01};
|
||||
[result appendBytes:&pps1 length:1];
|
||||
uint8 pps2[] = {(pps.length >> 8) & 0xff};
|
||||
[result appendBytes:&pps2 length:1];
|
||||
uint8 pps3[] = {pps.length & 0xff};
|
||||
[result appendBytes:&pps3 length:1];
|
||||
[result appendBytes:pps.bytes length:pps.length];
|
||||
|
||||
uint32 pre_size = swap_uint32_(packet_size);
|
||||
[result appendBytes:&pre_size length:sizeof(uint32)];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSData*)flvTagWithAudioHeader:(NSData*)audioInfo timeStamp:(uint32)timeStamp{
|
||||
NSMutableData *result = [[NSMutableData alloc] init];
|
||||
const size_t kAACPacketHeaderSize = 2;
|
||||
|
||||
size_t body_size = kAACPacketHeaderSize + audioInfo.length;
|
||||
size_t packet_size = body_size + FLV_TAG_SIZE;
|
||||
|
||||
[result appendData:[[self class] flvTagHeader:FLV_TAG_TYPE_AUDIO size:(UInt32)body_size timeStamp:timeStamp]];
|
||||
|
||||
byte format[kAACPacketHeaderSize] = { kAudioDataHeader, 0x01};
|
||||
format[1] = 0x00;
|
||||
[result appendBytes:format length:sizeof(format)];
|
||||
[result appendBytes:audioInfo.bytes length:audioInfo.length];
|
||||
|
||||
uint32 pre_size = swap_uint32_(packet_size);
|
||||
[result appendBytes:&pre_size length:sizeof(uint32)];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSData*)flvHeads:(NSInteger)videoWidth videoHeight:(NSInteger)videoHeight sps:(NSData*)sps pps:(NSData*)pps audioHeader:(NSData*)audioHeader{
|
||||
NSMutableData *result = [[NSMutableData alloc] init];
|
||||
// 写FLV头
|
||||
[result appendData:[[self class] flvHeader]];
|
||||
// 写 Meta 相关信息
|
||||
[result appendData:[[self class] metaData:videoWidth height:videoHeight]];
|
||||
// 写音频编码头信息
|
||||
[result appendData:[[self class] flvTagWithAudioHeader:audioHeader timeStamp:0]];
|
||||
// 写视频编码头信息
|
||||
[result appendData:[[self class] flvTagWithVideoHeader:sps pps:pps]];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -- Debug.. store video to local
|
||||
- (void)initForFilePath{
|
||||
NSString *path = [self GetFilePathByfileName:"flv_publish_x1.flv"];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
||||
self->fp = fopen([path cStringUsingEncoding:NSUTF8StringEncoding],"wb");
|
||||
}
|
||||
|
||||
- (NSString*)GetFilePathByfileName:(char*)filename{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask,YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
NSString *strName = [NSString stringWithFormat:@"%s",filename];
|
||||
|
||||
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:strName];
|
||||
|
||||
|
||||
return writablePath;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// LFStreamPackage.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by 倾慕 on 16/5/2.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "LFAudioFrame.h"
|
||||
#import "LFVideoFrame.h"
|
||||
|
||||
/// 编码器抽象的接口
|
||||
@protocol LFStreamPackage <NSObject>
|
||||
@required
|
||||
- (nullable instancetype)initWithVideoSize:(CGSize)videoSize;
|
||||
- (nullable NSData*)aaCPacket:(nullable LFAudioFrame*)audioFrame;
|
||||
- (nullable NSData*)h264Packet:(nullable LFVideoFrame*)videoFrame;
|
||||
@end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
$Id: amf.h 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __AMF_H__
|
||||
#define __AMF_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* AMF data types */
|
||||
#define AMF_TYPE_NUMBER ((byte)0x00)
|
||||
#define AMF_TYPE_BOOLEAN ((byte)0x01)
|
||||
#define AMF_TYPE_STRING ((byte)0x02)
|
||||
#define AMF_TYPE_OBJECT ((byte)0x03)
|
||||
#define AMF_TYPE_NULL ((byte)0x05)
|
||||
#define AMF_TYPE_UNDEFINED ((byte)0x06)
|
||||
/* #define AMF_TYPE_REFERENCE ((byte)0x07) */
|
||||
#define AMF_TYPE_ASSOCIATIVE_ARRAY ((byte)0x08)
|
||||
#define AMF_TYPE_END ((byte)0x09)
|
||||
#define AMF_TYPE_ARRAY ((byte)0x0A)
|
||||
#define AMF_TYPE_DATE ((byte)0x0B)
|
||||
/* #define AMF_TYPE_SIMPLEOBJECT ((byte)0x0D) */
|
||||
#define AMF_TYPE_XML ((byte)0x0F)
|
||||
#define AMF_TYPE_CLASS ((byte)0x10)
|
||||
|
||||
/* AMF error codes */
|
||||
#define AMF_ERROR_OK ((byte)0x00)
|
||||
#define AMF_ERROR_EOF ((byte)0x01)
|
||||
#define AMF_ERROR_UNKNOWN_TYPE ((byte)0x02)
|
||||
#define AMF_ERROR_END_TAG ((byte)0x03)
|
||||
#define AMF_ERROR_NULL_POINTER ((byte)0x04)
|
||||
#define AMF_ERROR_MEMORY ((byte)0x05)
|
||||
#define AMF_ERROR_UNSUPPORTED_TYPE ((byte)0x06)
|
||||
|
||||
typedef struct __amf_node * p_amf_node;
|
||||
|
||||
/* string type */
|
||||
typedef struct __amf_string {
|
||||
uint16 size;
|
||||
byte * mbstr;
|
||||
} amf_string;
|
||||
|
||||
/* array type */
|
||||
typedef struct __amf_list {
|
||||
uint32 size;
|
||||
p_amf_node first_element;
|
||||
p_amf_node last_element;
|
||||
} amf_list;
|
||||
|
||||
/* date type */
|
||||
typedef struct __amf_date {
|
||||
number64 milliseconds;
|
||||
sint16 timezone;
|
||||
} amf_date;
|
||||
|
||||
/* XML string type */
|
||||
typedef struct __amf_xmlstring {
|
||||
uint32 size;
|
||||
byte * mbstr;
|
||||
} amf_xmlstring;
|
||||
|
||||
/* class type */
|
||||
typedef struct __amf_class {
|
||||
amf_string name;
|
||||
amf_list elements;
|
||||
} amf_class;
|
||||
|
||||
/* structure encapsulating the various AMF objects */
|
||||
typedef struct __amf_data {
|
||||
byte type;
|
||||
byte error_code;
|
||||
union {
|
||||
number64 number_data;
|
||||
uint8 boolean_data;
|
||||
amf_string string_data;
|
||||
amf_list list_data;
|
||||
amf_date date_data;
|
||||
amf_xmlstring xmlstring_data;
|
||||
amf_class class_data;
|
||||
};
|
||||
} amf_data;
|
||||
|
||||
/* node used in lists, relies on amf_data */
|
||||
typedef struct __amf_node {
|
||||
amf_data * data;
|
||||
p_amf_node prev;
|
||||
p_amf_node next;
|
||||
} amf_node;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Pluggable backend support */
|
||||
typedef size_t (*amf_read_proc)(void * out_buffer, size_t size, void * user_data);
|
||||
typedef size_t (*amf_write_proc)(const void * in_buffer, size_t size, void * user_data);
|
||||
|
||||
/* read AMF data */
|
||||
amf_data * amf_data_read(amf_read_proc read_proc, void * user_data);
|
||||
|
||||
/* write AMF data */
|
||||
size_t amf_data_write(const amf_data * data, amf_write_proc write_proc, void * user_data);
|
||||
|
||||
/* generic functions */
|
||||
|
||||
/* allocate an AMF data object */
|
||||
amf_data * amf_data_new(byte type);
|
||||
/* load AMF data from buffer */
|
||||
amf_data * amf_data_buffer_read(byte * buffer, size_t maxbytes);
|
||||
/* load AMF data from stream */
|
||||
amf_data * amf_data_file_read(FILE * stream);
|
||||
/* AMF data size */
|
||||
size_t amf_data_size(const amf_data * data);
|
||||
/* write encoded AMF data into a buffer */
|
||||
size_t amf_data_buffer_write(amf_data * data, byte * buffer, size_t maxbytes);
|
||||
/* write encoded AMF data into a stream */
|
||||
size_t amf_data_file_write(const amf_data * data, FILE * stream);
|
||||
/* get the type of AMF data */
|
||||
byte amf_data_get_type(const amf_data * data);
|
||||
/* get the error code of AMF data */
|
||||
byte amf_data_get_error_code(const amf_data * data);
|
||||
/* return a new copy of AMF data */
|
||||
amf_data * amf_data_clone(const amf_data * data);
|
||||
/* release the memory of AMF data */
|
||||
void amf_data_free(amf_data * data);
|
||||
/* dump AMF data into a stream as text */
|
||||
void amf_data_dump(FILE * stream, const amf_data * data, int indent_level);
|
||||
|
||||
/* return a null AMF object with the specified error code attached to it */
|
||||
amf_data * amf_data_error(byte error_code);
|
||||
|
||||
/* number functions */
|
||||
amf_data * amf_number_new(number64 value);
|
||||
amf_data * amf_number_double(double value);
|
||||
number64 amf_number_get_value(const amf_data * data);
|
||||
void amf_number_set_value(amf_data * data, number64 value);
|
||||
|
||||
/* boolean functions */
|
||||
amf_data * amf_boolean_new(uint8 value);
|
||||
uint8 amf_boolean_get_value(const amf_data * data);
|
||||
void amf_boolean_set_value(amf_data * data, uint8 value);
|
||||
|
||||
/* string functions */
|
||||
amf_data * amf_string_new(byte * str, uint16 size);
|
||||
amf_data * amf_str(const char * str);
|
||||
uint16 amf_string_get_size(const amf_data * data);
|
||||
byte * amf_string_get_bytes(const amf_data * data);
|
||||
|
||||
/* object functions */
|
||||
amf_data * amf_object_new(void);
|
||||
uint32 amf_object_size(const amf_data * data);
|
||||
amf_data * amf_object_add(amf_data * data, const char * name, amf_data * element);
|
||||
amf_data * amf_object_get(const amf_data * data, const char * name);
|
||||
amf_data * amf_object_set(amf_data * data, const char * name, amf_data * element);
|
||||
amf_data * amf_object_delete(amf_data * data, const char * name);
|
||||
amf_node * amf_object_first(const amf_data * data);
|
||||
amf_node * amf_object_last(const amf_data * data);
|
||||
amf_node * amf_object_next(amf_node * node);
|
||||
amf_node * amf_object_prev(amf_node * node);
|
||||
amf_data * amf_object_get_name(amf_node * node);
|
||||
amf_data * amf_object_get_data(amf_node * node);
|
||||
|
||||
/* null functions */
|
||||
#define amf_null_new() amf_data_new(AMF_TYPE_NULL)
|
||||
|
||||
/* undefined functions */
|
||||
#define amf_undefined_new() amf_data_new(AMF_TYPE_UNDEFINED)
|
||||
|
||||
/* associative array functions */
|
||||
amf_data * amf_associative_array_new(void);
|
||||
#define amf_associative_array_size(d) amf_object_size(d)
|
||||
#define amf_associative_array_add(d, n, e) amf_object_add(d, n, e)
|
||||
#define amf_associative_array_get(d, n) amf_object_get(d, n)
|
||||
#define amf_associative_array_set(d, n, e) amf_object_set(d, n, e)
|
||||
#define amf_associative_array_delete(d, n) amf_object_delete(d, n)
|
||||
#define amf_associative_array_first(d) amf_object_first(d)
|
||||
#define amf_associative_array_last(d) amf_object_last(d)
|
||||
#define amf_associative_array_next(n) amf_object_next(n)
|
||||
#define amf_associative_array_prev(n) amf_object_prev(n)
|
||||
#define amf_associative_array_get_name(n) amf_object_get_name(n)
|
||||
#define amf_associative_array_get_data(n) amf_object_get_data(n)
|
||||
|
||||
/* array functions */
|
||||
amf_data * amf_array_new(void);
|
||||
uint32 amf_array_size(const amf_data * data);
|
||||
amf_data * amf_array_push(amf_data * data, amf_data * element);
|
||||
amf_data * amf_array_pop(amf_data * data);
|
||||
amf_node * amf_array_first(const amf_data * data);
|
||||
amf_node * amf_array_last(const amf_data * data);
|
||||
amf_node * amf_array_next(amf_node * node);
|
||||
amf_node * amf_array_prev(amf_node * node);
|
||||
amf_data * amf_array_get(amf_node * node);
|
||||
amf_data * amf_array_get_at(const amf_data * data, uint32 n);
|
||||
amf_data * amf_array_delete(amf_data * data, amf_node * node);
|
||||
amf_data * amf_array_insert_before(amf_data * data, amf_node * node, amf_data * element);
|
||||
amf_data * amf_array_insert_after(amf_data * data, amf_node * node, amf_data * element);
|
||||
|
||||
/* date functions */
|
||||
amf_data * amf_date_new(number64 milliseconds, sint16 timezone);
|
||||
number64 amf_date_get_milliseconds(const amf_data * data);
|
||||
sint16 amf_date_get_timezone(const amf_data * data);
|
||||
time_t amf_date_to_time_t(const amf_data * data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __AMF_H__ */
|
||||
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
$Id: avc.c 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "avc.h"
|
||||
|
||||
/**
|
||||
bit buffer handling
|
||||
*/
|
||||
typedef struct __bit_buffer {
|
||||
byte * start;
|
||||
size_t size;
|
||||
byte * current;
|
||||
uint8 read_bits;
|
||||
} bit_buffer;
|
||||
|
||||
static void skip_bits(bit_buffer * bb, size_t nbits) {
|
||||
bb->current = bb->current + ((nbits + bb->read_bits) / 8);
|
||||
bb->read_bits = (uint8)((bb->read_bits + nbits) % 8);
|
||||
}
|
||||
|
||||
static uint8 get_bit(bit_buffer * bb) {
|
||||
uint8 ret;
|
||||
ret = (*(bb->current) >> (7 - bb->read_bits)) & 0x1;
|
||||
if (bb->read_bits == 7) {
|
||||
bb->read_bits = 0;
|
||||
bb->current++;
|
||||
}
|
||||
else {
|
||||
bb->read_bits++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32 get_bits(bit_buffer * bb, size_t nbits) {
|
||||
uint32 i, ret;
|
||||
ret = 0;
|
||||
for (i = 0; i < nbits; i++) {
|
||||
ret = (ret << 1) + get_bit(bb);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32 exp_golomb_ue(bit_buffer * bb) {
|
||||
uint8 bit, significant_bits;
|
||||
significant_bits = 0;
|
||||
bit = get_bit(bb);
|
||||
while (bit == 0) {
|
||||
significant_bits++;
|
||||
bit = get_bit(bb);
|
||||
}
|
||||
return (1 << significant_bits) + get_bits(bb, significant_bits) - 1;
|
||||
}
|
||||
|
||||
static sint32 exp_golomb_se(bit_buffer * bb) {
|
||||
sint32 ret;
|
||||
ret = exp_golomb_ue(bb);
|
||||
if ((ret & 0x1) == 0) {
|
||||
return -(ret >> 1);
|
||||
}
|
||||
else {
|
||||
return (ret + 1) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* AVC type definitions */
|
||||
|
||||
#define AVC_SEQUENCE_HEADER 0
|
||||
#define AVC_NALU 1
|
||||
#define AVC_END_OF_SEQUENCE 2
|
||||
|
||||
typedef struct __AVCDecoderConfigurationRecord {
|
||||
uint8 configurationVersion;
|
||||
uint8 AVCProfileIndication;
|
||||
uint8 profile_compatibility;
|
||||
uint8 AVCLevelIndication;
|
||||
uint8 lengthSizeMinusOne;
|
||||
uint8 numOfSequenceParameterSets;
|
||||
} AVCDecoderConfigurationRecord;
|
||||
|
||||
int read_avc_decoder_configuration_record(flv_stream * f, AVCDecoderConfigurationRecord * adcr) {
|
||||
if (flv_read_tag_body(f, &adcr->configurationVersion, 1) == 1
|
||||
&& flv_read_tag_body(f, &adcr->AVCProfileIndication, 1) == 1
|
||||
&& flv_read_tag_body(f, &adcr->profile_compatibility, 1) == 1
|
||||
&& flv_read_tag_body(f, &adcr->AVCLevelIndication, 1) == 1
|
||||
&& flv_read_tag_body(f, &adcr->lengthSizeMinusOne, 1) == 1
|
||||
&& flv_read_tag_body(f, &adcr->numOfSequenceParameterSets, 1) == 1) {
|
||||
return FLV_OK;
|
||||
}
|
||||
else {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void parse_scaling_list(uint32 size, bit_buffer * bb) {
|
||||
uint32 last_scale, next_scale, i;
|
||||
sint32 delta_scale;
|
||||
last_scale = 8;
|
||||
next_scale = 8;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (next_scale != 0) {
|
||||
delta_scale = exp_golomb_se(bb);
|
||||
next_scale = (last_scale + delta_scale + 256) % 256;
|
||||
}
|
||||
if (next_scale != 0) {
|
||||
last_scale = next_scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Parses a SPS NALU to retrieve video width and height
|
||||
*/
|
||||
static void parse_sps(byte * sps, size_t sps_size, uint32 * width, uint32 * height) {
|
||||
bit_buffer bb;
|
||||
uint32 profile, pic_order_cnt_type, width_in_mbs, height_in_map_units;
|
||||
uint32 i, size, left, right, top, bottom;
|
||||
uint8 frame_mbs_only_flag;
|
||||
|
||||
bb.start = sps;
|
||||
bb.size = sps_size;
|
||||
bb.current = sps;
|
||||
bb.read_bits = 0;
|
||||
|
||||
/* skip first byte, since we already know we're parsing a SPS */
|
||||
skip_bits(&bb, 8);
|
||||
/* get profile */
|
||||
profile = get_bits(&bb, 8);
|
||||
/* skip 4 bits + 4 zeroed bits + 8 bits = 32 bits = 4 bytes */
|
||||
skip_bits(&bb, 16);
|
||||
|
||||
/* read sps id, first exp-golomb encoded value */
|
||||
exp_golomb_ue(&bb);
|
||||
|
||||
if (profile == 100 || profile == 110 || profile == 122 || profile == 144) {
|
||||
/* chroma format idx */
|
||||
if (exp_golomb_ue(&bb) == 3) {
|
||||
skip_bits(&bb, 1);
|
||||
}
|
||||
/* bit depth luma minus8 */
|
||||
exp_golomb_ue(&bb);
|
||||
/* bit depth chroma minus8 */
|
||||
exp_golomb_ue(&bb);
|
||||
/* Qpprime Y Zero Transform Bypass flag */
|
||||
skip_bits(&bb, 1);
|
||||
/* Seq Scaling Matrix Present Flag */
|
||||
if (get_bit(&bb)) {
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* Seq Scaling List Present Flag */
|
||||
if (get_bit(&bb)) {
|
||||
parse_scaling_list(i < 6 ? 16 : 64, &bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* log2_max_frame_num_minus4 */
|
||||
exp_golomb_ue(&bb);
|
||||
/* pic_order_cnt_type */
|
||||
pic_order_cnt_type = exp_golomb_ue(&bb);
|
||||
if (pic_order_cnt_type == 0) {
|
||||
/* log2_max_pic_order_cnt_lsb_minus4 */
|
||||
exp_golomb_ue(&bb);
|
||||
}
|
||||
else if (pic_order_cnt_type == 1) {
|
||||
/* delta_pic_order_always_zero_flag */
|
||||
skip_bits(&bb, 1);
|
||||
/* offset_for_non_ref_pic */
|
||||
exp_golomb_se(&bb);
|
||||
/* offset_for_top_to_bottom_field */
|
||||
exp_golomb_se(&bb);
|
||||
size = exp_golomb_ue(&bb);
|
||||
for (i = 0; i < size; i++) {
|
||||
/* offset_for_ref_frame */
|
||||
exp_golomb_se(&bb);
|
||||
}
|
||||
}
|
||||
/* num_ref_frames */
|
||||
exp_golomb_ue(&bb);
|
||||
/* gaps_in_frame_num_value_allowed_flag */
|
||||
skip_bits(&bb, 1);
|
||||
/* pic_width_in_mbs */
|
||||
width_in_mbs = exp_golomb_ue(&bb) + 1;
|
||||
/* pic_height_in_map_units */
|
||||
height_in_map_units = exp_golomb_ue(&bb) + 1;
|
||||
/* frame_mbs_only_flag */
|
||||
frame_mbs_only_flag = get_bit(&bb);
|
||||
if (!frame_mbs_only_flag) {
|
||||
/* mb_adaptive_frame_field */
|
||||
skip_bits(&bb, 1);
|
||||
}
|
||||
/* direct_8x8_inference_flag */
|
||||
skip_bits(&bb, 1);
|
||||
/* frame_cropping */
|
||||
left = right = top = bottom = 0;
|
||||
if (get_bit(&bb)) {
|
||||
left = exp_golomb_ue(&bb) * 2;
|
||||
right = exp_golomb_ue(&bb) * 2;
|
||||
top = exp_golomb_ue(&bb) * 2;
|
||||
bottom = exp_golomb_ue(&bb) * 2;
|
||||
if (!frame_mbs_only_flag) {
|
||||
top *= 2;
|
||||
bottom *= 2;
|
||||
}
|
||||
}
|
||||
/* width */
|
||||
*width = width_in_mbs * 16 - (left + right);
|
||||
/* height */
|
||||
*height = height_in_map_units * 16 - (top + bottom);
|
||||
if (!frame_mbs_only_flag) {
|
||||
*height *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Tries to read the resolution of the current video packet.
|
||||
We assume to be at the first byte of the video data.
|
||||
*/
|
||||
int read_avc_resolution(flv_stream * f, uint32 body_length, uint32 * width, uint32 * height) {
|
||||
byte avc_packet_type;
|
||||
uint24 composition_time;
|
||||
AVCDecoderConfigurationRecord adcr;
|
||||
uint16 sps_size;
|
||||
byte * sps_buffer;
|
||||
|
||||
/* make sure we have enough bytes to read in the current tag */
|
||||
if (body_length < sizeof(byte) + sizeof(uint24) + sizeof(AVCDecoderConfigurationRecord)) {
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
/* determine whether we're reading an AVCDecoderConfigurationRecord */
|
||||
if (flv_read_tag_body(f, &avc_packet_type, 1) < 1) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
if (avc_packet_type != AVC_SEQUENCE_HEADER) {
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
/* read the composition time */
|
||||
if (flv_read_tag_body(f, &composition_time, sizeof(uint24)) < sizeof(uint24)) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
/* we need to read an AVCDecoderConfigurationRecord */
|
||||
if (read_avc_decoder_configuration_record(f, &adcr) == FLV_ERROR_EOF) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
/* number of SequenceParameterSets */
|
||||
if ((adcr.numOfSequenceParameterSets & 0x1F) == 0) {
|
||||
/* no SPS, return */
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
/** read the first SequenceParameterSet found */
|
||||
/* SPS size */
|
||||
if (flv_read_tag_body(f, &sps_size, sizeof(uint16)) < sizeof(uint16)) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
sps_size = swap_uint16(sps_size);
|
||||
|
||||
/* read the SPS entirely */
|
||||
sps_buffer = (byte *) malloc((size_t)sps_size);
|
||||
if (sps_buffer == NULL) {
|
||||
return FLV_ERROR_MEMORY;
|
||||
}
|
||||
if (flv_read_tag_body(f, sps_buffer, (size_t)sps_size) < (size_t)sps_size) {
|
||||
free(sps_buffer);
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
/* parse SPS to determine video resolution */
|
||||
parse_sps(sps_buffer, (size_t)sps_size, width, height);
|
||||
|
||||
free(sps_buffer);
|
||||
return FLV_OK;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
$Id: avc.h 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __AVC_H__
|
||||
#define __AVC_H__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "flv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int read_avc_resolution(flv_stream * f, uint32 body_length, uint32 * width, uint32 * height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __AVC_H__ */
|
||||
@@ -1,498 +0,0 @@
|
||||
/*
|
||||
$Id: flv.c 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "flv.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void flv_tag_set_timestamp(flv_tag * tag, uint32 timestamp) {
|
||||
tag->timestamp = uint32_to_uint24_be(timestamp);
|
||||
tag->timestamp_extended = (uint8)((timestamp & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
/* FLV stream functions */
|
||||
flv_stream * flv_open(const char * file) {
|
||||
flv_stream * stream = (flv_stream *) malloc(sizeof(flv_stream));
|
||||
if (stream == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
stream->flvin = fopen(file, "rb");
|
||||
if (stream->flvin == NULL) {
|
||||
free(stream);
|
||||
return NULL;
|
||||
}
|
||||
stream->current_tag_body_length = 0;
|
||||
stream->current_tag_body_overflow = 0;
|
||||
stream->current_tag_offset = 0;
|
||||
stream->state = FLV_STREAM_STATE_START;
|
||||
return stream;
|
||||
}
|
||||
|
||||
int flv_read_header(flv_stream * stream, flv_header * header) {
|
||||
if (stream == NULL
|
||||
|| stream->flvin == NULL
|
||||
|| feof(stream->flvin)
|
||||
|| stream->state != FLV_STREAM_STATE_START) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (fread(&header->signature, sizeof(header->signature), 1, stream->flvin) == 0
|
||||
|| fread(&header->version, sizeof(header->version), 1, stream->flvin) == 0
|
||||
|| fread(&header->flags, sizeof(header->flags), 1, stream->flvin) == 0
|
||||
|| fread(&header->offset, sizeof(header->offset), 1, stream->flvin) == 0) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (header->signature[0] != 'F'
|
||||
|| header->signature[1] != 'L'
|
||||
|| header->signature[2] != 'V') {
|
||||
return FLV_ERROR_NO_FLV;
|
||||
}
|
||||
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
int flv_read_prev_tag_size(flv_stream * stream, uint32 * prev_tag_size) {
|
||||
uint32_be val;
|
||||
if (stream == NULL
|
||||
|| stream->flvin == NULL
|
||||
|| feof(stream->flvin)) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
/* skip remaining tag body bytes */
|
||||
if (stream->state == FLV_STREAM_STATE_TAG_BODY) {
|
||||
lfs_fseek(stream->flvin, stream->current_tag_offset + FLV_TAG_SIZE + uint24_be_to_uint32(stream->current_tag.body_length), SEEK_SET);
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
}
|
||||
|
||||
if (stream->state == FLV_STREAM_STATE_PREV_TAG_SIZE) {
|
||||
if (fread(&val, sizeof(uint32_be), 1, stream->flvin) == 0) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
else {
|
||||
stream->state = FLV_STREAM_STATE_TAG;
|
||||
*prev_tag_size = swap_uint32(val);
|
||||
return FLV_OK;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
int flv_read_tag(flv_stream * stream, flv_tag * tag) {
|
||||
if (stream == NULL
|
||||
|| stream->flvin == NULL
|
||||
|| feof(stream->flvin)) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
/* skip header */
|
||||
if (stream->state == FLV_STREAM_STATE_START) {
|
||||
lfs_fseek(stream->flvin, FLV_HEADER_SIZE, SEEK_CUR);
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
}
|
||||
|
||||
/* skip current tag body */
|
||||
if (stream->state == FLV_STREAM_STATE_TAG_BODY) {
|
||||
lfs_fseek(stream->flvin, stream->current_tag_offset + FLV_TAG_SIZE + uint24_be_to_uint32(stream->current_tag.body_length), SEEK_SET);
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
}
|
||||
|
||||
/* skip previous tag size */
|
||||
if (stream->state == FLV_STREAM_STATE_PREV_TAG_SIZE) {
|
||||
lfs_fseek(stream->flvin, sizeof(uint32_be), SEEK_CUR);
|
||||
stream->state = FLV_STREAM_STATE_TAG;
|
||||
}
|
||||
|
||||
if (stream->state == FLV_STREAM_STATE_TAG) {
|
||||
stream->current_tag_offset = lfs_ftell(stream->flvin);
|
||||
|
||||
if (fread(&tag->type, sizeof(tag->type), 1, stream->flvin) == 0
|
||||
|| fread(&tag->body_length, sizeof(tag->body_length), 1, stream->flvin) == 0
|
||||
|| fread(&tag->timestamp, sizeof(tag->timestamp), 1, stream->flvin) == 0
|
||||
|| fread(&tag->timestamp_extended, sizeof(tag->timestamp_extended), 1, stream->flvin) == 0
|
||||
|| fread(&tag->stream_id, sizeof(tag->stream_id), 1, stream->flvin) == 0) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
else {
|
||||
memcpy(&stream->current_tag, tag, sizeof(flv_tag));
|
||||
stream->current_tag_body_length = uint24_be_to_uint32(tag->body_length);
|
||||
stream->current_tag_body_overflow = 0;
|
||||
stream->state = FLV_STREAM_STATE_TAG_BODY;
|
||||
return FLV_OK;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
int flv_read_audio_tag(flv_stream * stream, flv_audio_tag * tag) {
|
||||
if (stream == NULL
|
||||
|| stream->flvin == NULL
|
||||
|| feof(stream->flvin)
|
||||
|| stream->state != FLV_STREAM_STATE_TAG_BODY) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (stream->current_tag_body_length == 0) {
|
||||
return FLV_ERROR_EMPTY_TAG;
|
||||
}
|
||||
|
||||
if (fread(tag, sizeof(flv_audio_tag), 1, stream->flvin) == 0) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (stream->current_tag_body_length >= sizeof(flv_audio_tag)) {
|
||||
stream->current_tag_body_length -= sizeof(flv_audio_tag);
|
||||
}
|
||||
else {
|
||||
stream->current_tag_body_overflow = sizeof(flv_audio_tag) - stream->current_tag_body_length;
|
||||
stream->current_tag_body_length = 0;
|
||||
}
|
||||
|
||||
if (stream->current_tag_body_length == 0) {
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
if (stream->current_tag_body_overflow > 0) {
|
||||
lfs_fseek(stream->flvin, -(file_offset_t)stream->current_tag_body_overflow, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
int flv_read_video_tag(flv_stream * stream, flv_video_tag * tag) {
|
||||
if (stream == NULL
|
||||
|| stream->flvin == NULL
|
||||
|| feof(stream->flvin)
|
||||
|| stream->state != FLV_STREAM_STATE_TAG_BODY) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (stream->current_tag_body_length == 0) {
|
||||
return FLV_ERROR_EMPTY_TAG;
|
||||
}
|
||||
|
||||
if (fread(tag, sizeof(flv_video_tag), 1, stream->flvin) == 0) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (stream->current_tag_body_length >= sizeof(flv_video_tag)) {
|
||||
stream->current_tag_body_length -= sizeof(flv_video_tag);
|
||||
}
|
||||
else {
|
||||
stream->current_tag_body_overflow = sizeof(flv_video_tag) - stream->current_tag_body_length;
|
||||
stream->current_tag_body_length = 0;
|
||||
}
|
||||
|
||||
if (stream->current_tag_body_length == 0) {
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
if (stream->current_tag_body_overflow > 0) {
|
||||
lfs_fseek(stream->flvin, -(file_offset_t)stream->current_tag_body_overflow, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
int flv_read_metadata(flv_stream * stream, amf_data ** name, amf_data ** data) {
|
||||
amf_data * d;
|
||||
byte error_code;
|
||||
size_t data_size;
|
||||
|
||||
if (stream == NULL
|
||||
|| stream->flvin == NULL
|
||||
|| feof(stream->flvin)
|
||||
|| stream->state != FLV_STREAM_STATE_TAG_BODY) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (stream->current_tag_body_length == 0) {
|
||||
return FLV_ERROR_EMPTY_TAG;
|
||||
}
|
||||
|
||||
/* read metadata name */
|
||||
d = amf_data_file_read(stream->flvin);
|
||||
*name = d;
|
||||
error_code = amf_data_get_error_code(d);
|
||||
if (error_code == AMF_ERROR_EOF) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
else if (error_code != AMF_ERROR_OK) {
|
||||
return FLV_ERROR_INVALID_METADATA_NAME;
|
||||
}
|
||||
|
||||
/* if only name can be read, metadata are invalid */
|
||||
data_size = amf_data_size(d);
|
||||
if (stream->current_tag_body_length > data_size) {
|
||||
stream->current_tag_body_length -= (uint32)data_size;
|
||||
}
|
||||
else {
|
||||
stream->current_tag_body_length = 0;
|
||||
stream->current_tag_body_overflow = (uint32)data_size - stream->current_tag_body_length;
|
||||
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
if (stream->current_tag_body_overflow > 0) {
|
||||
lfs_fseek(stream->flvin, -(file_offset_t)stream->current_tag_body_overflow, SEEK_CUR);
|
||||
}
|
||||
|
||||
return FLV_ERROR_INVALID_METADATA;
|
||||
}
|
||||
|
||||
/* read metadata contents */
|
||||
d = amf_data_file_read(stream->flvin);
|
||||
*data = d;
|
||||
error_code = amf_data_get_error_code(d);
|
||||
if (error_code == AMF_ERROR_EOF) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
if (error_code != AMF_ERROR_OK) {
|
||||
return FLV_ERROR_INVALID_METADATA;
|
||||
}
|
||||
|
||||
data_size = amf_data_size(d);
|
||||
if (stream->current_tag_body_length >= data_size) {
|
||||
stream->current_tag_body_length -= (uint32)data_size;
|
||||
}
|
||||
else {
|
||||
stream->current_tag_body_overflow = (uint32)data_size - stream->current_tag_body_length;
|
||||
stream->current_tag_body_length = 0;
|
||||
}
|
||||
|
||||
if (stream->current_tag_body_length == 0) {
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
if (stream->current_tag_body_overflow > 0) {
|
||||
lfs_fseek(stream->flvin, -(file_offset_t)stream->current_tag_body_overflow, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
size_t flv_read_tag_body(flv_stream * stream, void * buffer, size_t buffer_size) {
|
||||
size_t bytes_number;
|
||||
|
||||
if (stream == NULL
|
||||
|| stream->flvin == NULL
|
||||
|| feof(stream->flvin)
|
||||
|| stream->state != FLV_STREAM_STATE_TAG_BODY) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytes_number = (buffer_size > stream->current_tag_body_length) ? stream->current_tag_body_length : buffer_size;
|
||||
bytes_number = fread(buffer, sizeof(byte), bytes_number, stream->flvin);
|
||||
|
||||
stream->current_tag_body_length -= (uint32)bytes_number;
|
||||
|
||||
if (stream->current_tag_body_length == 0) {
|
||||
stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
|
||||
}
|
||||
|
||||
return bytes_number;
|
||||
}
|
||||
|
||||
file_offset_t flv_get_current_tag_offset(flv_stream * stream) {
|
||||
return (stream != NULL) ? stream->current_tag_offset : 0;
|
||||
}
|
||||
|
||||
file_offset_t flv_get_offset(flv_stream * stream) {
|
||||
return (stream != NULL) ? lfs_ftell(stream->flvin) : 0;
|
||||
}
|
||||
|
||||
void flv_reset(flv_stream * stream) {
|
||||
/* go back to beginning of file */
|
||||
if (stream != NULL && stream->flvin != NULL) {
|
||||
stream->current_tag_body_length = 0;
|
||||
stream->current_tag_offset = 0;
|
||||
stream->state = FLV_STREAM_STATE_START;
|
||||
|
||||
lfs_fseek(stream->flvin, 0, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
void flv_close(flv_stream * stream) {
|
||||
if (stream != NULL) {
|
||||
if (stream->flvin != NULL) {
|
||||
fclose(stream->flvin);
|
||||
}
|
||||
free(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/* FLV stdio writing helper functions */
|
||||
size_t flv_write_header(FILE * out, const flv_header * header) {
|
||||
if (fwrite(&header->signature, sizeof(header->signature), 1, out) == 0)
|
||||
return 0;
|
||||
if (fwrite(&header->version, sizeof(header->version), 1, out) == 0)
|
||||
return 0;
|
||||
if (fwrite(&header->flags, sizeof(header->flags), 1, out) == 0)
|
||||
return 0;
|
||||
if (fwrite(&header->offset, sizeof(header->offset), 1, out) == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t flv_write_tag(FILE * out, const flv_tag * tag) {
|
||||
if (fwrite(&tag->type, sizeof(tag->type), 1, out) == 0)
|
||||
return 0;
|
||||
|
||||
if (fwrite(&tag->body_length, sizeof(tag->body_length), 1, out) == 0)
|
||||
return 0;
|
||||
|
||||
if (fwrite(&tag->timestamp, sizeof(tag->timestamp), 1, out) == 0)
|
||||
return 0;
|
||||
if (fwrite(&tag->timestamp_extended, sizeof(tag->timestamp_extended), 1, out) == 0)
|
||||
return 0;
|
||||
if (fwrite(&tag->stream_id, sizeof(tag->stream_id), 1, out) == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FLV event based parser */
|
||||
int flv_parse(const char * file, flv_parser * parser) {
|
||||
flv_header header;
|
||||
flv_tag tag;
|
||||
flv_audio_tag at;
|
||||
flv_video_tag vt;
|
||||
amf_data * name, * data;
|
||||
uint32 prev_tag_size;
|
||||
int retval;
|
||||
|
||||
if (parser == NULL) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
parser->stream = flv_open(file);
|
||||
if (parser->stream == NULL) {
|
||||
return FLV_ERROR_OPEN_READ;
|
||||
}
|
||||
|
||||
retval = flv_read_header(parser->stream, &header);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (parser->on_header != NULL) {
|
||||
retval = parser->on_header(&header, parser);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
while (flv_read_tag(parser->stream, &tag) == FLV_OK) {
|
||||
if (parser->on_tag != NULL) {
|
||||
retval = parser->on_tag(&tag, parser);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.type == FLV_TAG_TYPE_AUDIO) {
|
||||
retval = flv_read_audio_tag(parser->stream, &at);
|
||||
if (retval == FLV_ERROR_EOF) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
if (retval != FLV_ERROR_EMPTY_TAG && parser->on_audio_tag != NULL) {
|
||||
retval = parser->on_audio_tag(&tag, at, parser);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tag.type == FLV_TAG_TYPE_VIDEO) {
|
||||
retval = flv_read_video_tag(parser->stream, &vt);
|
||||
if (retval == FLV_ERROR_EOF) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
if (retval != FLV_ERROR_EMPTY_TAG && parser->on_video_tag != NULL) {
|
||||
retval = parser->on_video_tag(&tag, vt, parser);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tag.type == FLV_TAG_TYPE_META) {
|
||||
name = data = NULL;
|
||||
retval = flv_read_metadata(parser->stream, &name, &data);
|
||||
if (retval == FLV_ERROR_EOF) {
|
||||
amf_data_free(name);
|
||||
amf_data_free(data);
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
else if (retval == FLV_OK && parser->on_metadata_tag != NULL) {
|
||||
retval = parser->on_metadata_tag(&tag, name, data, parser);
|
||||
if (retval != FLV_OK) {
|
||||
amf_data_free(name);
|
||||
amf_data_free(data);
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
amf_data_free(name);
|
||||
amf_data_free(data);
|
||||
}
|
||||
else {
|
||||
if (parser->on_unknown_tag != NULL) {
|
||||
retval = parser->on_unknown_tag(&tag, parser);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = flv_read_prev_tag_size(parser->stream, &prev_tag_size);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
if (parser->on_prev_tag_size != NULL) {
|
||||
retval = parser->on_prev_tag_size(prev_tag_size, parser);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parser->on_stream_end != NULL) {
|
||||
retval = parser->on_stream_end(parser);
|
||||
if (retval != FLV_OK) {
|
||||
flv_close(parser->stream);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
flv_close(parser->stream);
|
||||
return FLV_OK;
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
$Id: flv.h 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __FLV_H__
|
||||
#define __FLV_H__
|
||||
|
||||
/* Configuration of the sources */
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "amf.h"
|
||||
|
||||
/* error statuses */
|
||||
#define FLV_OK 0
|
||||
#define FLV_ERROR_OPEN_READ 1
|
||||
#define FLV_ERROR_NO_FLV 2
|
||||
#define FLV_ERROR_EOF 3
|
||||
#define FLV_ERROR_MEMORY 4
|
||||
#define FLV_ERROR_EMPTY_TAG 5
|
||||
#define FLV_ERROR_INVALID_METADATA_NAME 6
|
||||
#define FLV_ERROR_INVALID_METADATA 7
|
||||
|
||||
/* flv file format structure and definitions */
|
||||
|
||||
/* FLV file header */
|
||||
#define FLV_SIGNATURE "FLV"
|
||||
#define FLV_VERSION ((uint8)0x01)
|
||||
|
||||
#define FLV_FLAG_VIDEO ((uint8)0x01)
|
||||
#define FLV_FLAG_AUDIO ((uint8)0x04)
|
||||
|
||||
typedef struct __flv_header {
|
||||
byte signature[3]; /* always "FLV" */
|
||||
uint8 version; /* should be 1 */
|
||||
uint8_bitmask flags;
|
||||
uint32_be offset; /* always 9 */
|
||||
} flv_header;
|
||||
|
||||
#define FLV_HEADER_SIZE 9
|
||||
|
||||
#define flv_header_has_video(header) ((header).flags & FLV_FLAG_VIDEO)
|
||||
#define flv_header_has_audio(header) ((header).flags & FLV_FLAG_AUDIO)
|
||||
#define flv_header_get_offset(header) (swap_uint32((header).offset))
|
||||
|
||||
/* FLV tag */
|
||||
#define FLV_TAG_TYPE_AUDIO ((uint8)0x08)
|
||||
#define FLV_TAG_TYPE_VIDEO ((uint8)0x09)
|
||||
#define FLV_TAG_TYPE_META ((uint8)0x12)
|
||||
|
||||
typedef struct __flv_tag {
|
||||
uint8 type;
|
||||
uint24_be body_length; /* in bytes, total tag size minus 11 */
|
||||
uint24_be timestamp; /* milli-seconds */
|
||||
uint8 timestamp_extended; /* timestamp extension */
|
||||
uint24_be stream_id; /* reserved, must be "\0\0\0" */
|
||||
/* body comes next */
|
||||
} flv_tag;
|
||||
|
||||
#define FLV_TAG_SIZE 11
|
||||
|
||||
#define flv_tag_get_body_length(tag) (uint24_be_to_uint32((tag).body_length))
|
||||
#define flv_tag_get_timestamp(tag) \
|
||||
(uint24_be_to_uint32((tag).timestamp) + ((tag).timestamp_extended << 24))
|
||||
#define flv_tag_get_stream_id(tag) (uint24_be_to_uint32((tag).stream_id))
|
||||
|
||||
/* audio tag */
|
||||
#define FLV_AUDIO_TAG_SOUND_TYPE_MONO 0
|
||||
#define FLV_AUDIO_TAG_SOUND_TYPE_STEREO 1
|
||||
|
||||
#define FLV_AUDIO_TAG_SOUND_SIZE_8 0
|
||||
#define FLV_AUDIO_TAG_SOUND_SIZE_16 1
|
||||
|
||||
#define FLV_AUDIO_TAG_SOUND_RATE_5_5 0
|
||||
#define FLV_AUDIO_TAG_SOUND_RATE_11 1
|
||||
#define FLV_AUDIO_TAG_SOUND_RATE_22 2
|
||||
#define FLV_AUDIO_TAG_SOUND_RATE_44 3
|
||||
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_LINEAR_PCM 0
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_ADPCM 1
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_MP3 2
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_LINEAR_PCM_LE 3
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_NELLYMOSER_16_MONO 4
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_NELLYMOSER_8_MONO 5
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_NELLYMOSER 6
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_G711_A 7
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_G711_MU 8
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_RESERVED 9
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_AAC 10
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_SPEEX 11
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_MP3_8 14
|
||||
#define FLV_AUDIO_TAG_SOUND_FORMAT_DEVICE_SPECIFIC 15
|
||||
|
||||
typedef byte flv_audio_tag;
|
||||
|
||||
#define flv_audio_tag_sound_type(tag) (((tag) & 0x01) >> 0)
|
||||
#define flv_audio_tag_sound_size(tag) (((tag) & 0x02) >> 1)
|
||||
#define flv_audio_tag_sound_rate(tag) (((tag) & 0x0C) >> 2)
|
||||
#define flv_audio_tag_sound_format(tag) (((tag) & 0xF0) >> 4)
|
||||
|
||||
/* video tag */
|
||||
#define FLV_VIDEO_TAG_CODEC_JPEG 1
|
||||
#define FLV_VIDEO_TAG_CODEC_SORENSEN_H263 2
|
||||
#define FLV_VIDEO_TAG_CODEC_SCREEN_VIDEO 3
|
||||
#define FLV_VIDEO_TAG_CODEC_ON2_VP6 4
|
||||
#define FLV_VIDEO_TAG_CODEC_ON2_VP6_ALPHA 5
|
||||
#define FLV_VIDEO_TAG_CODEC_SCREEN_VIDEO_V2 6
|
||||
#define FLV_VIDEO_TAG_CODEC_AVC 7
|
||||
|
||||
#define FLV_VIDEO_TAG_FRAME_TYPE_KEYFRAME 1
|
||||
#define FLV_VIDEO_TAG_FRAME_TYPE_INTERFRAME 2
|
||||
#define FLV_VIDEO_TAG_FRAME_TYPE_DISPOSABLE_INTERFRAME 3
|
||||
#define FLV_VIDEO_TAG_FRAME_TYPE_GENERATED_KEYFRAME 4
|
||||
#define FLV_VIDEO_TAG_FRAME_TYPE_COMMAND_FRAME 5
|
||||
|
||||
typedef byte flv_video_tag;
|
||||
|
||||
#define flv_video_tag_codec_id(tag) (((tag) & 0x0F) >> 0)
|
||||
#define flv_video_tag_frame_type(tag) (((tag) & 0xF0) >> 4)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* FLV helper functions */
|
||||
void flv_tag_set_timestamp(flv_tag * tag, uint32 timestamp);
|
||||
|
||||
/* FLV stream */
|
||||
#define FLV_STREAM_STATE_START 0
|
||||
#define FLV_STREAM_STATE_TAG 1
|
||||
#define FLV_STREAM_STATE_TAG_BODY 2
|
||||
#define FLV_STREAM_STATE_PREV_TAG_SIZE 3
|
||||
|
||||
typedef struct __flv_stream {
|
||||
FILE * flvin;
|
||||
uint8 state;
|
||||
flv_tag current_tag;
|
||||
file_offset_t current_tag_offset;
|
||||
uint32 current_tag_body_length;
|
||||
uint32 current_tag_body_overflow;
|
||||
} flv_stream;
|
||||
|
||||
/* FLV stream functions */
|
||||
flv_stream * flv_open(const char * file);
|
||||
int flv_read_header(flv_stream * stream, flv_header * header);
|
||||
int flv_read_prev_tag_size(flv_stream * stream, uint32 * prev_tag_size);
|
||||
int flv_read_tag(flv_stream * stream, flv_tag * tag);
|
||||
int flv_read_audio_tag(flv_stream * stream, flv_audio_tag * tag);
|
||||
int flv_read_video_tag(flv_stream * stream, flv_video_tag * tag);
|
||||
int flv_read_metadata(flv_stream * stream, amf_data ** name, amf_data ** data);
|
||||
size_t flv_read_tag_body(flv_stream * stream, void * buffer, size_t buffer_size);
|
||||
file_offset_t flv_get_current_tag_offset(flv_stream * stream);
|
||||
file_offset_t flv_get_offset(flv_stream * stream);
|
||||
void flv_reset(flv_stream * stream);
|
||||
void flv_close(flv_stream * stream);
|
||||
|
||||
/* FLV stdio writing helper functions */
|
||||
size_t flv_write_header(FILE * out, const flv_header * header);
|
||||
size_t flv_write_tag(FILE * out, const flv_tag * tag);
|
||||
|
||||
/* FLV event based parser */
|
||||
typedef struct __flv_parser {
|
||||
flv_stream * stream;
|
||||
void * user_data;
|
||||
int (* on_header)(flv_header * header, struct __flv_parser * parser);
|
||||
int (* on_tag)(flv_tag * tag, struct __flv_parser * parser);
|
||||
int (* on_metadata_tag)(flv_tag * tag, amf_data * name, amf_data * data, struct __flv_parser * parser);
|
||||
int (* on_audio_tag)(flv_tag * tag, flv_audio_tag audio_tag, struct __flv_parser * parser);
|
||||
int (* on_video_tag)(flv_tag * tag, flv_video_tag audio_tag, struct __flv_parser * parser);
|
||||
int (* on_unknown_tag)(flv_tag * tag, struct __flv_parser * parser);
|
||||
int (* on_prev_tag_size)(uint32 size, struct __flv_parser * parser);
|
||||
int (* on_stream_end)(struct __flv_parser * parser);
|
||||
} flv_parser;
|
||||
|
||||
int flv_parse(const char * file, flv_parser * parser);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __FLV_H__ */
|
||||
@@ -1,626 +0,0 @@
|
||||
/*
|
||||
$Id: info.c 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "info.h"
|
||||
#include "avc.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#pragma warning(disable:4244)
|
||||
/*
|
||||
compute Sorensen H.263 video size
|
||||
*/
|
||||
static int compute_h263_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
|
||||
byte header[9];
|
||||
uint24_be psc_be;
|
||||
uint32 psc;
|
||||
|
||||
/* make sure we have enough bytes to read in the current tag */
|
||||
if (body_length >= 9) {
|
||||
if (flv_read_tag_body(flv_in, header, 9) < 9) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
psc_be.b[0] = header[0];
|
||||
psc_be.b[1] = header[1];
|
||||
psc_be.b[2] = header[2];
|
||||
psc = uint24_be_to_uint32(psc_be) >> 7;
|
||||
if (psc == 1) {
|
||||
uint32 psize = ((header[3] & 0x03) << 1) + ((header[4] >> 7) & 0x01);
|
||||
switch (psize) {
|
||||
case 0:
|
||||
info->video_width = ((header[4] & 0x7f) << 1) + ((header[5] >> 7) & 0x01);
|
||||
info->video_height = ((header[5] & 0x7f) << 1) + ((header[6] >> 7) & 0x01);
|
||||
break;
|
||||
case 1:
|
||||
info->video_width = ((header[4] & 0x7f) << 9) + (header[5] << 1) + ((header[6] >> 7) & 0x01);
|
||||
info->video_height = ((header[6] & 0x7f) << 9) + (header[7] << 1) + ((header[8] >> 7) & 0x01);
|
||||
break;
|
||||
case 2:
|
||||
info->video_width = 352;
|
||||
info->video_height = 288;
|
||||
break;
|
||||
case 3:
|
||||
info->video_width = 176;
|
||||
info->video_height = 144;
|
||||
break;
|
||||
case 4:
|
||||
info->video_width = 128;
|
||||
info->video_height = 96;
|
||||
break;
|
||||
case 5:
|
||||
info->video_width = 320;
|
||||
info->video_height = 240;
|
||||
break;
|
||||
case 6:
|
||||
info->video_width = 160;
|
||||
info->video_height = 120;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
compute Screen video size
|
||||
*/
|
||||
static int compute_screen_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
|
||||
byte header[4];
|
||||
|
||||
/* make sure we have enough bytes to read in the current tag */
|
||||
if (body_length >= 4) {
|
||||
if (flv_read_tag_body(flv_in, header, 4) < 4) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
info->video_width = ((header[0] & 0x0f) << 8) + header[1];
|
||||
info->video_height = ((header[2] & 0x0f) << 8) + header[3];
|
||||
}
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
compute On2 VP6 video size
|
||||
*/
|
||||
static int compute_vp6_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
|
||||
byte header[7], offset;
|
||||
|
||||
/* make sure we have enough bytes to read in the current tag */
|
||||
if (body_length >= 7) {
|
||||
if (flv_read_tag_body(flv_in, header, 7) < 7) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
/* two bytes offset if VP6 0 */
|
||||
offset = (header[1] & 0x01 || !(header[2] & 0x06)) << 1;
|
||||
info->video_width = (header[4 + offset] << 4) - (header[0] >> 4);
|
||||
info->video_height = (header[3 + offset] << 4) - (header[0] & 0x0f);
|
||||
|
||||
}
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
compute On2 VP6 with Alpha video size
|
||||
*/
|
||||
static int compute_vp6_alpha_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
|
||||
byte header[10], offset;
|
||||
|
||||
/* make sure we have enough bytes to read in the current tag */
|
||||
if (body_length >= 10) {
|
||||
if (flv_read_tag_body(flv_in, header, 10) < 10) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
/* two bytes offset if VP6 0 */
|
||||
offset = (header[4] & 0x01 || !(header[5] & 0x06)) << 1;
|
||||
info->video_width = (header[7 + offset] << 4) - (header[0] >> 4);
|
||||
info->video_height = (header[6 + offset] << 4) - (header[0] & 0x0f);
|
||||
}
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
compute AVC (H.264) video size (experimental)
|
||||
*/
|
||||
static int compute_avc_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
|
||||
return read_avc_resolution(flv_in, body_length, &(info->video_width), &(info->video_height));
|
||||
}
|
||||
|
||||
/*
|
||||
compute video width and height from the first video frame
|
||||
*/
|
||||
static int compute_video_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
|
||||
switch (info->video_codec) {
|
||||
case FLV_VIDEO_TAG_CODEC_SORENSEN_H263:
|
||||
return compute_h263_size(flv_in, info, body_length);
|
||||
case FLV_VIDEO_TAG_CODEC_SCREEN_VIDEO:
|
||||
case FLV_VIDEO_TAG_CODEC_SCREEN_VIDEO_V2:
|
||||
return compute_screen_size(flv_in, info, body_length);
|
||||
case FLV_VIDEO_TAG_CODEC_ON2_VP6:
|
||||
return compute_vp6_size(flv_in, info, body_length);
|
||||
case FLV_VIDEO_TAG_CODEC_ON2_VP6_ALPHA:
|
||||
return compute_vp6_alpha_size(flv_in, info, body_length);
|
||||
case FLV_VIDEO_TAG_CODEC_AVC:
|
||||
return compute_avc_size(flv_in, info, body_length);
|
||||
default:
|
||||
return FLV_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
read the flv file thoroughly to get all necessary information.
|
||||
|
||||
we need to check :
|
||||
- timestamp of first audio for audio delay
|
||||
- whether we have audio and video
|
||||
- first frames codecs (audio, video)
|
||||
- total audio and video data sizes
|
||||
- keyframe offsets and timestamps
|
||||
- whether the last video frame is a keyframe
|
||||
- last keyframe timestamp
|
||||
- onMetaData tag total size
|
||||
- total tags size
|
||||
- first tag after onMetaData offset
|
||||
- last timestamp
|
||||
- real video data size, number of frames, duration to compute framerate and video data rate
|
||||
- real audio data size, duration to compute audio data rate
|
||||
- video headers to find width and height. (depends on the encoding)
|
||||
*/
|
||||
int get_flv_info(flv_stream * flv_in, flv_info * info) {
|
||||
uint32 prev_timestamp_video;
|
||||
uint32 prev_timestamp_audio;
|
||||
uint32 prev_timestamp_meta;
|
||||
uint8 timestamp_extended_video;
|
||||
uint8 timestamp_extended_audio;
|
||||
uint8 timestamp_extended_meta;
|
||||
uint8 have_video_size;
|
||||
uint8 have_first_timestamp;
|
||||
uint32 tag_number;
|
||||
int result;
|
||||
flv_tag ft;
|
||||
|
||||
info->have_video = 0;
|
||||
info->have_audio = 0;
|
||||
info->video_width = 0;
|
||||
info->video_height = 0;
|
||||
info->video_codec = 0;
|
||||
info->video_frames_number = 0;
|
||||
info->audio_codec = 0;
|
||||
info->audio_size = 0;
|
||||
info->audio_rate = 0;
|
||||
info->audio_stereo = 0;
|
||||
info->video_data_size = 0;
|
||||
info->audio_data_size = 0;
|
||||
info->meta_data_size = 0;
|
||||
info->real_video_data_size = 0;
|
||||
info->real_audio_data_size = 0;
|
||||
info->video_first_timestamp = 0;
|
||||
info->audio_first_timestamp = 0;
|
||||
info->first_timestamp = 0;
|
||||
info->can_seek_to_end = 0;
|
||||
info->have_keyframes = 0;
|
||||
info->last_keyframe_timestamp = 0;
|
||||
info->on_metadata_size = 0;
|
||||
info->on_metadata_offset = 0;
|
||||
info->biggest_tag_body_size = 0;
|
||||
info->last_timestamp = 0;
|
||||
info->video_frame_duration = 0;
|
||||
info->audio_frame_duration = 0;
|
||||
info->total_prev_tags_size = 0;
|
||||
info->have_on_last_second = 0;
|
||||
info->original_on_metadata = NULL;
|
||||
info->keyframes = NULL;
|
||||
info->times = NULL;
|
||||
info->filepositions = NULL;
|
||||
|
||||
/*
|
||||
read FLV header
|
||||
*/
|
||||
|
||||
if (flv_read_header(flv_in, &(info->header)) != FLV_OK) {
|
||||
return FLV_ERROR_NO_FLV;
|
||||
}
|
||||
|
||||
info->keyframes = amf_object_new();
|
||||
info->times = amf_array_new();
|
||||
info->filepositions = amf_array_new();
|
||||
amf_object_add(info->keyframes, "times", info->times);
|
||||
amf_object_add(info->keyframes, "filepositions", info->filepositions);
|
||||
|
||||
/* first empty previous tag size */
|
||||
info->total_prev_tags_size = sizeof(uint32_be);
|
||||
|
||||
/* first timestamp */
|
||||
have_first_timestamp = 0;
|
||||
|
||||
/* extended timestamp initialization */
|
||||
prev_timestamp_video = 0;
|
||||
prev_timestamp_audio = 0;
|
||||
prev_timestamp_meta = 0;
|
||||
timestamp_extended_video = 0;
|
||||
timestamp_extended_audio = 0;
|
||||
timestamp_extended_meta = 0;
|
||||
tag_number = 0;
|
||||
have_video_size = 0;
|
||||
|
||||
while (flv_read_tag(flv_in, &ft) == FLV_OK) {
|
||||
file_offset_t offset;
|
||||
uint32 body_length;
|
||||
uint32 timestamp;
|
||||
|
||||
offset = flv_get_current_tag_offset(flv_in);
|
||||
body_length = flv_tag_get_body_length(ft);
|
||||
timestamp = flv_tag_get_timestamp(ft);
|
||||
|
||||
/* extended timestamp fixing */
|
||||
if (ft.type == FLV_TAG_TYPE_META) {
|
||||
if (timestamp < prev_timestamp_meta
|
||||
&& prev_timestamp_meta - timestamp > 0xF00000) {
|
||||
++timestamp_extended_meta;
|
||||
}
|
||||
prev_timestamp_meta = timestamp;
|
||||
if (timestamp_extended_meta > 0) {
|
||||
timestamp += timestamp_extended_meta << 24;
|
||||
}
|
||||
}
|
||||
else if (ft.type == FLV_TAG_TYPE_AUDIO) {
|
||||
if (timestamp < prev_timestamp_audio
|
||||
&& prev_timestamp_audio - timestamp > 0xF00000) {
|
||||
++timestamp_extended_audio;
|
||||
}
|
||||
prev_timestamp_audio = timestamp;
|
||||
if (timestamp_extended_audio > 0) {
|
||||
timestamp += timestamp_extended_audio << 24;
|
||||
}
|
||||
}
|
||||
else if (ft.type == FLV_TAG_TYPE_VIDEO) {
|
||||
if (timestamp < prev_timestamp_video
|
||||
&& prev_timestamp_video - timestamp > 0xF00000) {
|
||||
++timestamp_extended_video;
|
||||
}
|
||||
prev_timestamp_video = timestamp;
|
||||
if (timestamp_extended_video > 0) {
|
||||
timestamp += timestamp_extended_video << 24;
|
||||
}
|
||||
}
|
||||
|
||||
/* non-zero starting timestamp handling */
|
||||
if (!have_first_timestamp && ft.type != FLV_TAG_TYPE_META) {
|
||||
info->first_timestamp = timestamp;
|
||||
have_first_timestamp = 1;
|
||||
}
|
||||
if (timestamp > 0) {
|
||||
timestamp -= info->first_timestamp;
|
||||
}
|
||||
|
||||
/* update the info struct only if the tag is valid */
|
||||
if (ft.type == FLV_TAG_TYPE_META
|
||||
|| ft.type == FLV_TAG_TYPE_AUDIO
|
||||
|| ft.type == FLV_TAG_TYPE_VIDEO) {
|
||||
if (info->biggest_tag_body_size < body_length) {
|
||||
info->biggest_tag_body_size = body_length;
|
||||
}
|
||||
info->last_timestamp = timestamp;
|
||||
}
|
||||
|
||||
if (ft.type == FLV_TAG_TYPE_META) {
|
||||
amf_data *tag_name, *data;
|
||||
int retval;
|
||||
tag_name = data = NULL;
|
||||
|
||||
if (body_length == 0) {
|
||||
} else {
|
||||
retval = flv_read_metadata(flv_in, &tag_name, &data);
|
||||
if (retval == FLV_ERROR_EOF) {
|
||||
amf_data_free(tag_name);
|
||||
amf_data_free(data);
|
||||
return FLV_ERROR_EOF;
|
||||
} else if (retval == FLV_ERROR_INVALID_METADATA_NAME) {
|
||||
} else if (retval == FLV_ERROR_INVALID_METADATA) {
|
||||
}
|
||||
}
|
||||
|
||||
/* check metadata name */
|
||||
if (body_length > 0 && amf_data_get_type(tag_name) == AMF_TYPE_STRING) {
|
||||
char * name = (char *)amf_string_get_bytes(tag_name);
|
||||
size_t len = (size_t)amf_string_get_size(tag_name);
|
||||
|
||||
/* get info only on the first onMetaData we read */
|
||||
if (info->on_metadata_size == 0 && !strncmp(name, "onMetaData", len)) {
|
||||
info->on_metadata_size = body_length + FLV_TAG_SIZE + sizeof(uint32_be);
|
||||
info->on_metadata_offset = offset;
|
||||
|
||||
amf_data_free(data);
|
||||
}
|
||||
else {
|
||||
if (!strncmp(name, "onLastSecond", len)) {
|
||||
info->have_on_last_second = 1;
|
||||
}
|
||||
info->meta_data_size += (body_length + FLV_TAG_SIZE);
|
||||
info->total_prev_tags_size += sizeof(uint32_be);
|
||||
if (data != NULL) {
|
||||
amf_data_free(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* just ignore metadata that don't have a proper name */
|
||||
else {
|
||||
info->meta_data_size += (body_length + FLV_TAG_SIZE);
|
||||
info->total_prev_tags_size += sizeof(uint32_be);
|
||||
amf_data_free(data);
|
||||
}
|
||||
amf_data_free(tag_name);
|
||||
}
|
||||
else if (ft.type == FLV_TAG_TYPE_VIDEO) {
|
||||
flv_video_tag vt;
|
||||
|
||||
/* do not take video frame into account if body length is zero and we ignore errors */
|
||||
if (body_length == 0) {
|
||||
} else {
|
||||
if (flv_read_video_tag(flv_in, &vt) != FLV_OK) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (info->have_video != 1) {
|
||||
info->have_video = 1;
|
||||
info->video_codec = flv_video_tag_codec_id(vt);
|
||||
info->video_first_timestamp = timestamp;
|
||||
}
|
||||
|
||||
if (have_video_size != 1
|
||||
&& flv_video_tag_frame_type(vt) == FLV_VIDEO_TAG_FRAME_TYPE_KEYFRAME) {
|
||||
/* read first video frame to get critical info */
|
||||
result = compute_video_size(flv_in, info, body_length - sizeof(flv_video_tag));
|
||||
if (result != FLV_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (info->video_width > 0 && info->video_height > 0) {
|
||||
have_video_size = 1;
|
||||
}
|
||||
/* if we cannot fetch that information from the first tag, we'll try
|
||||
for each following video key frame */
|
||||
}
|
||||
|
||||
/* add keyframe to list */
|
||||
if (flv_video_tag_frame_type(vt) == FLV_VIDEO_TAG_FRAME_TYPE_KEYFRAME) {
|
||||
/* do not add keyframe if the previous one has the same timestamp */
|
||||
if (!info->have_keyframes
|
||||
|| (info->have_keyframes && info->last_keyframe_timestamp != timestamp)) {
|
||||
info->have_keyframes = 1;
|
||||
info->last_keyframe_timestamp = timestamp;
|
||||
amf_array_push(info->times, amf_number_new(timestamp / 1000.0));
|
||||
amf_array_push(info->filepositions, amf_number_new((number64)offset));
|
||||
}
|
||||
/* is last frame a key frame ? if so, we can seek to end */
|
||||
info->can_seek_to_end = 1;
|
||||
}
|
||||
else {
|
||||
info->can_seek_to_end = 0;
|
||||
}
|
||||
|
||||
info->real_video_data_size += (body_length - 1);
|
||||
}
|
||||
|
||||
info->video_frames_number++;
|
||||
|
||||
/*
|
||||
we assume all video frames have the same size as the first one:
|
||||
probably bogus but only used in case there's no audio in the file
|
||||
*/
|
||||
if (info->video_frame_duration == 0) {
|
||||
info->video_frame_duration = timestamp - info->video_first_timestamp;
|
||||
}
|
||||
|
||||
info->video_data_size += (body_length + FLV_TAG_SIZE);
|
||||
info->total_prev_tags_size += sizeof(uint32_be);
|
||||
}
|
||||
else if (ft.type == FLV_TAG_TYPE_AUDIO) {
|
||||
flv_audio_tag at;
|
||||
|
||||
/* do not take audio frame into account if body length is zero and we ignore errors */
|
||||
if (body_length == 0) {
|
||||
} else {
|
||||
if (flv_read_audio_tag(flv_in, &at) != FLV_OK) {
|
||||
return FLV_ERROR_EOF;
|
||||
}
|
||||
|
||||
if (info->have_audio != 1) {
|
||||
info->have_audio = 1;
|
||||
info->audio_codec = flv_audio_tag_sound_format(at);
|
||||
info->audio_rate = flv_audio_tag_sound_rate(at);
|
||||
info->audio_size = flv_audio_tag_sound_size(at);
|
||||
info->audio_stereo = flv_audio_tag_sound_type(at);
|
||||
info->audio_first_timestamp = timestamp;
|
||||
}
|
||||
/* we assume all audio frames have the same size as the first one */
|
||||
if (info->audio_frame_duration == 0) {
|
||||
info->audio_frame_duration = timestamp - info->audio_first_timestamp;
|
||||
}
|
||||
|
||||
info->real_audio_data_size += (body_length - 1);
|
||||
}
|
||||
|
||||
info->audio_data_size += (body_length + FLV_TAG_SIZE);
|
||||
info->total_prev_tags_size += sizeof(uint32_be);
|
||||
}
|
||||
else {
|
||||
return 7;
|
||||
}
|
||||
++tag_number;
|
||||
}
|
||||
|
||||
return FLV_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
compute the metadata
|
||||
*/
|
||||
void compute_metadata(flv_info * info, flv_metadata * meta) {
|
||||
uint32 new_on_metadata_size, on_last_second_size;
|
||||
file_offset_t data_size, total_filesize;
|
||||
number64 duration, video_data_rate, framerate;
|
||||
amf_data * amf_total_filesize;
|
||||
amf_data * amf_total_data_size;
|
||||
amf_node * node_t;
|
||||
amf_node * node_f;
|
||||
|
||||
meta->on_last_second_name = amf_str("onLastSecond");
|
||||
meta->on_last_second = amf_associative_array_new();
|
||||
meta->on_metadata_name = amf_str("onMetaData");
|
||||
meta->on_metadata = amf_associative_array_new();
|
||||
|
||||
amf_associative_array_add(meta->on_metadata, "hasMetadata", amf_boolean_new(1));
|
||||
amf_associative_array_add(meta->on_metadata, "hasVideo", amf_boolean_new(info->have_video));
|
||||
amf_associative_array_add(meta->on_metadata, "hasAudio", amf_boolean_new(info->have_audio));
|
||||
|
||||
if (info->have_audio) {
|
||||
duration = (info->last_timestamp - info->first_timestamp + info->audio_frame_duration) / 1000.0;
|
||||
}
|
||||
else {
|
||||
duration = (info->last_timestamp - info->first_timestamp + info->video_frame_duration) / 1000.0;
|
||||
}
|
||||
amf_associative_array_add(meta->on_metadata, "duration", amf_number_new(duration));
|
||||
|
||||
amf_associative_array_add(meta->on_metadata, "lasttimestamp", amf_number_new(info->last_timestamp / 1000.0));
|
||||
amf_associative_array_add(meta->on_metadata, "lastkeyframetimestamp", amf_number_new(info->last_keyframe_timestamp / 1000.0));
|
||||
|
||||
if (info->video_width > 0)
|
||||
amf_associative_array_add(meta->on_metadata, "width", amf_number_new(info->video_width));
|
||||
if (info->video_height > 0)
|
||||
amf_associative_array_add(meta->on_metadata, "height", amf_number_new(info->video_height));
|
||||
|
||||
video_data_rate = ((info->real_video_data_size / 1024.0) * 8.0) / duration;
|
||||
amf_associative_array_add(meta->on_metadata, "videodatarate", amf_number_new(video_data_rate));
|
||||
|
||||
framerate = info->video_frames_number / duration;
|
||||
amf_associative_array_add(meta->on_metadata, "framerate", amf_number_new(framerate));
|
||||
|
||||
if (info->have_audio) {
|
||||
number64 audio_khz, audio_sample_rate;
|
||||
number64 audio_data_rate = ((info->real_audio_data_size / 1024.0) * 8.0) / duration;
|
||||
amf_associative_array_add(meta->on_metadata, "audiodatarate", amf_number_new(audio_data_rate));
|
||||
|
||||
audio_khz = 0.0;
|
||||
switch (info->audio_rate) {
|
||||
case FLV_AUDIO_TAG_SOUND_RATE_5_5: audio_khz = 5500.0; break;
|
||||
case FLV_AUDIO_TAG_SOUND_RATE_11: audio_khz = 11000.0; break;
|
||||
case FLV_AUDIO_TAG_SOUND_RATE_22: audio_khz = 22050.0; break;
|
||||
case FLV_AUDIO_TAG_SOUND_RATE_44: audio_khz = 44100.0; break;
|
||||
}
|
||||
amf_associative_array_add(meta->on_metadata, "audiosamplerate", amf_number_new(audio_khz));
|
||||
audio_sample_rate = 0.0;
|
||||
switch (info->audio_size) {
|
||||
case FLV_AUDIO_TAG_SOUND_SIZE_8: audio_sample_rate = 8.0; break;
|
||||
case FLV_AUDIO_TAG_SOUND_SIZE_16: audio_sample_rate = 16.0; break;
|
||||
}
|
||||
amf_associative_array_add(meta->on_metadata, "audiosamplesize", amf_number_new(audio_sample_rate));
|
||||
amf_associative_array_add(meta->on_metadata, "stereo", amf_boolean_new(info->audio_stereo == FLV_AUDIO_TAG_SOUND_TYPE_STEREO));
|
||||
}
|
||||
|
||||
/* to be computed later */
|
||||
amf_total_filesize = amf_number_new(0);
|
||||
amf_associative_array_add(meta->on_metadata, "filesize", amf_total_filesize);
|
||||
|
||||
if (info->have_video) {
|
||||
amf_associative_array_add(meta->on_metadata, "videosize", amf_number_new((number64)info->video_data_size));
|
||||
}
|
||||
if (info->have_audio) {
|
||||
amf_associative_array_add(meta->on_metadata, "audiosize", amf_number_new((number64)info->audio_data_size));
|
||||
}
|
||||
|
||||
/* to be computed later */
|
||||
amf_total_data_size = amf_number_new(0);
|
||||
amf_associative_array_add(meta->on_metadata, "datasize", amf_total_data_size);
|
||||
|
||||
amf_associative_array_add(meta->on_metadata, "metadatacreator", amf_str("xingmeng"));
|
||||
|
||||
amf_associative_array_add(meta->on_metadata, "metadatadate", amf_date_new((number64)time(NULL)*1000, 0));
|
||||
if (info->have_audio) {
|
||||
amf_associative_array_add(meta->on_metadata, "audiocodecid", amf_number_new((number64)info->audio_codec));
|
||||
}
|
||||
if (info->have_video) {
|
||||
amf_associative_array_add(meta->on_metadata, "videocodecid", amf_number_new((number64)info->video_codec));
|
||||
}
|
||||
if (info->have_audio && info->have_video) {
|
||||
number64 audio_delay = ((sint32)info->audio_first_timestamp - (sint32)info->video_first_timestamp) / 1000.0;
|
||||
amf_associative_array_add(meta->on_metadata, "audiodelay", amf_number_new((number64)audio_delay));
|
||||
}
|
||||
amf_associative_array_add(meta->on_metadata, "canSeekToEnd", amf_boolean_new(info->can_seek_to_end));
|
||||
|
||||
/* only add empty cuepoints if we don't preserve existing tags OR if the existing tags don't have cuepoints */
|
||||
if ((amf_associative_array_get(info->original_on_metadata, "cuePoints") == NULL)) {
|
||||
amf_associative_array_add(meta->on_metadata, "hasCuePoints", amf_boolean_new(0));
|
||||
amf_associative_array_add(meta->on_metadata, "cuePoints", amf_array_new());
|
||||
}
|
||||
amf_associative_array_add(meta->on_metadata, "hasKeyframes", amf_boolean_new(info->have_keyframes));
|
||||
amf_associative_array_add(meta->on_metadata, "keyframes", info->keyframes);
|
||||
|
||||
/*
|
||||
When we know the final size, we can recompute te offsets for the filepositions, and the final datasize.
|
||||
*/
|
||||
new_on_metadata_size = FLV_TAG_SIZE + sizeof(uint32_be) +
|
||||
(uint32)(amf_data_size(meta->on_metadata_name) + amf_data_size(meta->on_metadata));
|
||||
on_last_second_size = (uint32)(amf_data_size(meta->on_last_second_name) + amf_data_size(meta->on_last_second));
|
||||
|
||||
node_t = amf_array_first(info->times);
|
||||
node_f = amf_array_first(info->filepositions);
|
||||
while (node_t != NULL || node_f != NULL) {
|
||||
amf_data * amf_filepos = amf_array_get(node_f);
|
||||
number64 offset = amf_number_get_value(amf_filepos) + new_on_metadata_size - info->on_metadata_size;
|
||||
number64 timestamp = amf_number_get_value(amf_array_get(node_t));
|
||||
|
||||
/* after the onLastSecond event we need to take in account the tag size */
|
||||
if (!info->have_on_last_second && (info->last_timestamp - timestamp * 1000) <= 1000) {
|
||||
offset += (FLV_TAG_SIZE + on_last_second_size + sizeof(uint32_be));
|
||||
}
|
||||
|
||||
amf_number_set_value(amf_filepos, offset);
|
||||
node_t = amf_array_next(node_t);
|
||||
node_f = amf_array_next(node_f);
|
||||
}
|
||||
|
||||
/* compute data size, ie. size of metadata excluding prev_tag_size */
|
||||
data_size = info->meta_data_size + FLV_TAG_SIZE +
|
||||
(uint32)(amf_data_size(meta->on_metadata_name) + amf_data_size(meta->on_metadata));
|
||||
if (!info->have_on_last_second) {
|
||||
data_size += (uint32)on_last_second_size + FLV_TAG_SIZE;
|
||||
}
|
||||
amf_number_set_value(amf_total_data_size, (number64)data_size);
|
||||
|
||||
/* compute total file size */
|
||||
total_filesize = FLV_HEADER_SIZE + info->total_prev_tags_size + info->video_data_size +
|
||||
info->audio_data_size + info->meta_data_size + new_on_metadata_size;
|
||||
|
||||
if (!info->have_on_last_second) {
|
||||
/* if we have to add onLastSecond, we must count the header and new prevTagSize we add */
|
||||
total_filesize += (uint32)(FLV_TAG_SIZE + on_last_second_size + sizeof(uint32_be));
|
||||
}
|
||||
|
||||
amf_number_set_value(amf_total_filesize, (number64)total_filesize);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
$Id: info.h 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __INFO_H__
|
||||
#define __INFO_H__
|
||||
#include "flv.h"
|
||||
|
||||
typedef struct __flv_info {
|
||||
flv_header header;
|
||||
uint8 have_video;
|
||||
uint8 have_audio;
|
||||
uint32 video_width;
|
||||
uint32 video_height;
|
||||
uint8 video_codec;
|
||||
uint32 video_frames_number;
|
||||
uint8 audio_codec;
|
||||
uint8 audio_size;
|
||||
uint8 audio_rate;
|
||||
uint8 audio_stereo;
|
||||
file_offset_t video_data_size;
|
||||
file_offset_t audio_data_size;
|
||||
file_offset_t meta_data_size;
|
||||
file_offset_t real_video_data_size;
|
||||
file_offset_t real_audio_data_size;
|
||||
uint32 video_first_timestamp;
|
||||
uint32 audio_first_timestamp;
|
||||
uint32 first_timestamp;
|
||||
uint8 can_seek_to_end;
|
||||
uint8 have_keyframes;
|
||||
uint32 last_keyframe_timestamp;
|
||||
uint32 on_metadata_size;
|
||||
file_offset_t on_metadata_offset;
|
||||
uint32 biggest_tag_body_size;
|
||||
uint32 last_timestamp;
|
||||
uint32 video_frame_duration;
|
||||
uint32 audio_frame_duration;
|
||||
file_offset_t total_prev_tags_size;
|
||||
uint8 have_on_last_second;
|
||||
amf_data * original_on_metadata;
|
||||
amf_data * keyframes;
|
||||
amf_data * times;
|
||||
amf_data * filepositions;
|
||||
} flv_info;
|
||||
|
||||
typedef struct __flv_metadata {
|
||||
amf_data * on_last_second_name;
|
||||
amf_data * on_last_second;
|
||||
amf_data * on_metadata_name;
|
||||
amf_data * on_metadata;
|
||||
} flv_metadata;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int get_flv_info(flv_stream * flv_in, flv_info * info);
|
||||
|
||||
void compute_metadata(flv_info * info, flv_metadata * meta);
|
||||
|
||||
void compute_current_metadata(flv_info * info, flv_metadata * meta);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __INFO_H__ */
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
$Id: types.c 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "types.h"
|
||||
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
|
||||
/* swap 64 bits doubles */
|
||||
typedef union __convert_u {
|
||||
uint64 i;
|
||||
number64 f;
|
||||
} convert_u;
|
||||
|
||||
number64 swap_number64(number64 n) {
|
||||
convert_u c;
|
||||
c.f = n;
|
||||
c.i = (((c.i & 0x00000000000000FFULL) << 56) |
|
||||
((c.i & 0x000000000000FF00ULL) << 40) |
|
||||
((c.i & 0x0000000000FF0000ULL) << 24) |
|
||||
((c.i & 0x00000000FF000000ULL) << 8) |
|
||||
((c.i & 0x000000FF00000000ULL) >> 8) |
|
||||
((c.i & 0x0000FF0000000000ULL) >> 24) |
|
||||
((c.i & 0x00FF000000000000ULL) >> 40) |
|
||||
((c.i & 0xFF00000000000000ULL) >> 56));
|
||||
return c.f;
|
||||
}
|
||||
#endif /* !defined WORDS_BIGENDIAN */
|
||||
|
||||
/* convert native integers into 24 bits big endian integers */
|
||||
uint24_be uint32_to_uint24_be(uint32 l) {
|
||||
uint24_be r;
|
||||
r.b[0] = (uint8)((l & 0x00FF0000U) >> 16);
|
||||
r.b[1] = (uint8)((l & 0x0000FF00U) >> 8);
|
||||
r.b[2] = (uint8) (l & 0x000000FFU);
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
/*
|
||||
These functions assume fpos_t is a 64-bit signed integer
|
||||
*/
|
||||
|
||||
file_offset_t lfs_ftell(FILE * stream) {
|
||||
fpos_t p;
|
||||
if (fgetpos(stream, &p) == 0) {
|
||||
return (file_offset_t)p;
|
||||
}
|
||||
else {
|
||||
return -1LL;
|
||||
}
|
||||
}
|
||||
|
||||
int lfs_fseek(FILE * stream, file_offset_t offset, int whence) {
|
||||
fpos_t p;
|
||||
if (fgetpos(stream, &p) == 0) {
|
||||
switch (whence) {
|
||||
case SEEK_CUR: p += offset; break;
|
||||
case SEEK_SET: p = offset; break;
|
||||
/*case SEEK_END:; not implemented here */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
fsetpos(stream, &p);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* WIN32 */
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
$Id: types.h 231 2011-06-27 13:46:19Z marc.noirot $
|
||||
|
||||
FLV Metadata updater
|
||||
|
||||
Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
|
||||
|
||||
This file is part of FLVMeta.
|
||||
|
||||
FLVMeta is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FLVMeta is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FLVMeta; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __TYPES_H__
|
||||
#define __TYPES_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef uint8_t byte, uint8, uint8_bitmask;
|
||||
|
||||
typedef uint16_t uint16, uint16_be, uint16_le;
|
||||
|
||||
typedef int16_t sint16, sint16_be, sint16_le;
|
||||
|
||||
typedef uint32_t uint32, uint32_be, uint32_le;
|
||||
|
||||
typedef int32_t sint32, sint32_be, sint32_le;
|
||||
|
||||
typedef struct __uint24 {
|
||||
uint8 b[3];
|
||||
} uint24, uint24_be, uint24_le;
|
||||
|
||||
typedef uint64_t uint64, uint64_le, uint64_be;
|
||||
|
||||
typedef int64_t sint64, sint64_le, sint64_be;
|
||||
|
||||
//typedef
|
||||
//#if SIZEOF_FLOAT == 8
|
||||
//float
|
||||
//#elif SIZEOF_DOUBLE == 8
|
||||
//double
|
||||
//#elif SIZEOF_LONG_DOUBLE == 8
|
||||
//long double
|
||||
//#else
|
||||
//uint64_t
|
||||
//#endif
|
||||
//number64, number64_le, number64_be;
|
||||
|
||||
typedef double number64, number64_le, number64_be;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
|
||||
# define swap_uint16(x) (x)
|
||||
# define swap_sint16(x) (x)
|
||||
# define swap_uint32(x) (x)
|
||||
# define swap_number64(x) (x)
|
||||
|
||||
#else /* !defined WORDS_BIGENDIAN */
|
||||
|
||||
/* swap 16 bits integers */
|
||||
# define swap_uint16(x) ((uint16)((((x) & 0x00FFU) << 8) | \
|
||||
(((x) & 0xFF00U) >> 8)))
|
||||
# define swap_sint16(x) ((sint16)((((x) & 0x00FF) << 8) | \
|
||||
(((x) & 0xFF00) >> 8)))
|
||||
|
||||
/* swap 32 bits integers */
|
||||
# define swap_uint32(x) ((uint32)((((x) & 0x000000FFU) << 24) | \
|
||||
(((x) & 0x0000FF00U) << 8) | \
|
||||
(((x) & 0x00FF0000U) >> 8) | \
|
||||
(((x) & 0xFF000000U) >> 24)))
|
||||
|
||||
/* swap 64 bits doubles */
|
||||
number64 swap_number64(number64);
|
||||
|
||||
#endif /* WORDS_BIGENDIAN */
|
||||
|
||||
/* convert big endian 24 bits integers to native integers */
|
||||
# define uint24_be_to_uint32(x) ((uint32)(((x).b[0] << 16) | \
|
||||
((x).b[1] << 8) | (x).b[2]))
|
||||
|
||||
/* convert native integers into 24 bits big endian integers */
|
||||
uint24_be uint32_to_uint24_be(uint32);
|
||||
|
||||
/* large file support */
|
||||
#ifdef HAVE_FSEEKO
|
||||
# define lfs_ftell ftello
|
||||
# define lfs_fseek fseeko
|
||||
|
||||
# define FILE_OFFSET_T_64_BITS 1
|
||||
typedef off_t file_offset_t;
|
||||
|
||||
#else /* !HAVE_SEEKO */
|
||||
|
||||
# ifdef WIN32
|
||||
|
||||
# define FILE_OFFSET_T_64_BITS 1
|
||||
typedef long long int file_offset_t;
|
||||
|
||||
/* Win32 large file support */
|
||||
file_offset_t lfs_ftell(FILE * stream);
|
||||
int lfs_fseek(FILE * stream, file_offset_t offset, int whence);
|
||||
|
||||
# else /* !defined WIN32 */
|
||||
|
||||
# define lfs_ftell ftell
|
||||
# define lfs_fseek fseek
|
||||
|
||||
typedef long file_offset_t;
|
||||
|
||||
# endif /* WIN32 */
|
||||
|
||||
#endif /* HAVE_FSEEKO */
|
||||
|
||||
/* file offset printf specifier */
|
||||
#ifdef FILE_OFFSET_T_64_BITS
|
||||
# define FILE_OFFSET_PRINTF_FORMAT "ll"
|
||||
#else
|
||||
# define FILE_OFFSET_PRINTF_FORMAT "l"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __TYPES_H__ */
|
||||
@@ -1,501 +0,0 @@
|
||||
//
|
||||
// LFStreamRtmpSocket.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by admin on 16/5/18.
|
||||
// Copyright © 2016年 live Interactive. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFStreamRtmpSocket.h"
|
||||
#import "rtmp.h"
|
||||
#import "YYDispatchQueuePool.h"
|
||||
|
||||
static const NSInteger RetryTimesBreaken = 20;///< 重连1分钟 3秒一次 一共20次
|
||||
static const NSInteger RetryTimesMargin = 3;
|
||||
|
||||
static dispatch_queue_t YYRtmpSendQueue() {
|
||||
return YYDispatchQueueGetForQOS(NSQualityOfServiceUserInitiated);
|
||||
}
|
||||
|
||||
#define DATA_ITEMS_MAX_COUNT 100
|
||||
#define RTMP_DATA_RESERVE_SIZE 400
|
||||
#define RTMP_HEAD_SIZE (sizeof(RTMPPacket)+RTMP_MAX_HEADER_SIZE)
|
||||
|
||||
#define SAVC(x) static const AVal av_##x = AVC(#x)
|
||||
|
||||
static const AVal av_setDataFrame = AVC("@setDataFrame");
|
||||
static const AVal av_SDKVersion = AVC("LFLiveKit 1.6");
|
||||
SAVC(onMetaData);
|
||||
SAVC(duration);
|
||||
SAVC(width);
|
||||
SAVC(height);
|
||||
SAVC(videocodecid);
|
||||
SAVC(videodatarate);
|
||||
SAVC(framerate);
|
||||
SAVC(audiocodecid);
|
||||
SAVC(audiodatarate);
|
||||
SAVC(audiosamplerate);
|
||||
SAVC(audiosamplesize);
|
||||
SAVC(audiochannels);
|
||||
SAVC(stereo);
|
||||
SAVC(encoder);
|
||||
SAVC(av_stereo);
|
||||
SAVC(fileSize);
|
||||
SAVC(avc1);
|
||||
SAVC(mp4a);
|
||||
|
||||
@interface LFStreamRtmpSocket ()<LFStreamingBufferDelegate>
|
||||
{
|
||||
PILI_RTMP* _rtmp;
|
||||
}
|
||||
@property (nonatomic, weak) id<LFStreamSocketDelegate> delegate;
|
||||
@property (nonatomic, strong) LFLiveStreamInfo *stream;
|
||||
@property (nonatomic, strong) LFStreamingBuffer *buffer;
|
||||
@property (nonatomic, strong) LFLiveDebug *debugInfo;
|
||||
//错误信息
|
||||
@property (nonatomic, assign) RTMPError error;
|
||||
@property (nonatomic, assign) NSInteger retryTimes4netWorkBreaken;
|
||||
@property (nonatomic, assign) NSInteger reconnectInterval;
|
||||
@property (nonatomic, assign) NSInteger reconnectCount;
|
||||
|
||||
@property (nonatomic, assign) BOOL isSending;
|
||||
@property (nonatomic, assign) BOOL isConnected;
|
||||
@property (nonatomic, assign) BOOL isConnecting;
|
||||
@property (nonatomic, assign) BOOL isReconnecting;
|
||||
|
||||
@property (nonatomic, assign) BOOL sendVideoHead;
|
||||
@property (nonatomic, assign) BOOL sendAudioHead;
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFStreamRtmpSocket
|
||||
|
||||
#pragma mark -- LFStreamSocket
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream videoSize:(CGSize)videoSize reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount{
|
||||
if(!stream) @throw [NSException exceptionWithName:@"LFStreamRtmpSocket init error" reason:@"stream is nil" userInfo:nil];
|
||||
if(self = [super init]){
|
||||
_stream = stream;
|
||||
if(reconnectInterval > 0) _reconnectInterval = reconnectInterval;
|
||||
else _reconnectInterval = RetryTimesMargin;
|
||||
|
||||
if(reconnectCount > 0) _reconnectCount = reconnectCount;
|
||||
else _reconnectCount = RetryTimesBreaken;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) start{
|
||||
dispatch_async(YYRtmpSendQueue(), ^{
|
||||
if(!_stream) return;
|
||||
if(_isConnecting) return;
|
||||
if(_rtmp != NULL) return;
|
||||
self.debugInfo.streamId = self.stream.streamId;
|
||||
self.debugInfo.uploadUrl = self.stream.url;
|
||||
self.debugInfo.isRtmp = YES;
|
||||
[self clean];
|
||||
[self RTMP264_Connect:(char*)[_stream.url cStringUsingEncoding:NSASCIIStringEncoding]];
|
||||
});
|
||||
}
|
||||
|
||||
- (void) stop{
|
||||
dispatch_async(YYRtmpSendQueue(), ^{
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveStop];
|
||||
}
|
||||
if(_rtmp != NULL){
|
||||
PILI_RTMP_Close(_rtmp, &_error);
|
||||
PILI_RTMP_Free(_rtmp);
|
||||
_rtmp = NULL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void) sendFrame:(LFFrame*)frame{
|
||||
dispatch_async(YYRtmpSendQueue(), ^{
|
||||
if(!frame) return;
|
||||
[self.buffer appendObject:frame];
|
||||
[self sendFrame];
|
||||
});
|
||||
}
|
||||
|
||||
- (void) setDelegate:(id<LFStreamSocketDelegate>)delegate{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (void)sendFrame{
|
||||
if(!self.isSending && self.buffer.list.count > 0){
|
||||
self.isSending = YES;
|
||||
|
||||
if(!_isConnected || _isReconnecting || _isConnecting || !_rtmp) return;
|
||||
|
||||
// 调用发送接口
|
||||
LFFrame *frame = [self.buffer popFirstObject];
|
||||
if([frame isKindOfClass:[LFVideoFrame class]]){
|
||||
if(!self.sendVideoHead){
|
||||
self.sendVideoHead = YES;
|
||||
[self sendVideoHeader:(LFVideoFrame*)frame];
|
||||
}else{
|
||||
[self sendVideo:(LFVideoFrame*)frame];
|
||||
}
|
||||
}else{
|
||||
if(!self.sendAudioHead){
|
||||
self.sendAudioHead = YES;
|
||||
[self sendAudioHeader:(LFAudioFrame*)frame];
|
||||
}else{
|
||||
[self sendAudio:frame];
|
||||
}
|
||||
|
||||
}
|
||||
self.debugInfo.dataFlow += frame.data.length;
|
||||
if(CACurrentMediaTime()*1000 - self.debugInfo.timeStamp < 1000) {
|
||||
self.debugInfo.bandwidth += frame.data.length;
|
||||
if([frame isKindOfClass:[LFAudioFrame class]]){
|
||||
self.debugInfo.capturedAudioCount ++;
|
||||
}else{
|
||||
self.debugInfo.capturedVideoCount ++;
|
||||
}
|
||||
self.debugInfo.unSendCount = self.buffer.list.count;
|
||||
}else {
|
||||
self.debugInfo.currentBandwidth = self.debugInfo.bandwidth;
|
||||
self.debugInfo.currentCapturedAudioCount = self.debugInfo.capturedAudioCount;
|
||||
self.debugInfo.currentCapturedVideoCount = self.debugInfo.capturedVideoCount;
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketDebug:debugInfo:)]){
|
||||
[self.delegate socketDebug:self debugInfo:self.debugInfo];
|
||||
}
|
||||
|
||||
self.debugInfo.bandwidth = 0;
|
||||
self.debugInfo.capturedAudioCount = 0;
|
||||
self.debugInfo.capturedVideoCount = 0;
|
||||
self.debugInfo.timeStamp = CACurrentMediaTime()*1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clean{
|
||||
_isConnecting = NO;
|
||||
_isReconnecting = NO;
|
||||
_isSending = NO;
|
||||
_isConnected = NO;
|
||||
_sendAudioHead = NO;
|
||||
_sendVideoHead = NO;
|
||||
self.debugInfo = nil;
|
||||
[self.buffer removeAllObject];
|
||||
self.retryTimes4netWorkBreaken = 0;
|
||||
}
|
||||
|
||||
-(NSInteger) RTMP264_Connect:(char *)push_url{
|
||||
//由于摄像头的timestamp是一直在累加,需要每次得到相对时间戳
|
||||
//分配与初始化
|
||||
if(_isConnecting) return -1;
|
||||
|
||||
_isConnecting = YES;
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLivePending];
|
||||
}
|
||||
|
||||
if(_rtmp != NULL){
|
||||
PILI_RTMP_Close(_rtmp, &_error);
|
||||
PILI_RTMP_Free(_rtmp);
|
||||
}
|
||||
|
||||
_rtmp = PILI_RTMP_Alloc();
|
||||
PILI_RTMP_Init(_rtmp);
|
||||
|
||||
//设置URL
|
||||
if (PILI_RTMP_SetupURL(_rtmp, push_url, &_error) < 0){
|
||||
//log(LOG_ERR, "RTMP_SetupURL() failed!");
|
||||
goto Failed;
|
||||
}
|
||||
|
||||
_rtmp->m_errorCallback = RTMPErrorCallback;
|
||||
_rtmp->m_connCallback = ConnectionTimeCallback;
|
||||
_rtmp->m_userData = (__bridge void*)self;
|
||||
_rtmp->m_msgCounter = 1;
|
||||
//设置可写,即发布流,这个函数必须在连接前使用,否则无效
|
||||
PILI_RTMP_EnableWrite(_rtmp);
|
||||
|
||||
//连接服务器
|
||||
if (PILI_RTMP_Connect(_rtmp, NULL, &_error) < 0){
|
||||
goto Failed;
|
||||
}
|
||||
|
||||
//连接流
|
||||
if (PILI_RTMP_ConnectStream(_rtmp, 0, &_error) < 0) {
|
||||
goto Failed;
|
||||
}
|
||||
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveStart];
|
||||
}
|
||||
|
||||
[self sendMetaData];
|
||||
|
||||
_isConnected = YES;
|
||||
_isConnecting = NO;
|
||||
_isReconnecting = NO;
|
||||
_isSending = NO;
|
||||
_retryTimes4netWorkBreaken = 0;
|
||||
return 0;
|
||||
|
||||
Failed:
|
||||
PILI_RTMP_Close(_rtmp, &_error);
|
||||
PILI_RTMP_Free(_rtmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#pragma mark -- Rtmp Send
|
||||
|
||||
- (void)sendMetaData {
|
||||
PILI_RTMPPacket packet;
|
||||
|
||||
char pbuf[2048], *pend = pbuf+sizeof(pbuf);
|
||||
|
||||
packet.m_nChannel = 0x03; // control channel (invoke)
|
||||
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
|
||||
packet.m_packetType = RTMP_PACKET_TYPE_INFO;
|
||||
packet.m_nTimeStamp = 0;
|
||||
packet.m_nInfoField2 = _rtmp->m_stream_id;
|
||||
packet.m_hasAbsTimestamp = TRUE;
|
||||
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
|
||||
|
||||
char *enc = packet.m_body;
|
||||
enc = AMF_EncodeString(enc, pend, &av_setDataFrame);
|
||||
enc = AMF_EncodeString(enc, pend, &av_onMetaData);
|
||||
|
||||
*enc++ = AMF_OBJECT;
|
||||
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_duration, 0.0);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_fileSize, 0.0);
|
||||
|
||||
// videosize
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_width, _stream.videoConfiguration.videoSize.width);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_height, _stream.videoConfiguration.videoSize.height);
|
||||
|
||||
// video
|
||||
enc = AMF_EncodeNamedString(enc, pend, &av_videocodecid, &av_avc1);
|
||||
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_videodatarate, _stream.videoConfiguration.videoBitRate / 1000.f);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_framerate, _stream.videoConfiguration.videoFrameRate);
|
||||
|
||||
// audio
|
||||
enc = AMF_EncodeNamedString(enc, pend, &av_audiocodecid, &av_mp4a);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiodatarate, _stream.audioConfiguration.audioBitrate);
|
||||
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiosamplerate, _stream.audioConfiguration.audioSampleRate);
|
||||
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiosamplesize, 16.0);
|
||||
enc = AMF_EncodeNamedBoolean(enc, pend, &av_stereo, _stream.audioConfiguration.numberOfChannels==2);
|
||||
|
||||
// sdk version
|
||||
enc = AMF_EncodeNamedString(enc, pend, &av_encoder, &av_SDKVersion);
|
||||
|
||||
*enc++ = 0;
|
||||
*enc++ = 0;
|
||||
*enc++ = AMF_OBJECT_END;
|
||||
|
||||
packet.m_nBodySize = enc - packet.m_body;
|
||||
if(!PILI_RTMP_SendPacket(_rtmp, &packet, FALSE, &_error)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendVideoHeader:(LFVideoFrame*)videoFrame {
|
||||
if(!videoFrame || !videoFrame.sps || !videoFrame.pps) return;
|
||||
|
||||
unsigned char * body=NULL;
|
||||
NSInteger iIndex = 0;
|
||||
NSInteger rtmpLength = 1024;
|
||||
const char *sps = videoFrame.sps.bytes;
|
||||
const char *pps = videoFrame.pps.bytes;
|
||||
NSInteger sps_len = videoFrame.sps.length;
|
||||
NSInteger pps_len = videoFrame.pps.length;
|
||||
|
||||
body = (unsigned char*)malloc(rtmpLength);
|
||||
memset(body,0,rtmpLength);
|
||||
|
||||
body[iIndex++] = 0x17;
|
||||
body[iIndex++] = 0x00;
|
||||
|
||||
body[iIndex++] = 0x00;
|
||||
body[iIndex++] = 0x00;
|
||||
body[iIndex++] = 0x00;
|
||||
|
||||
body[iIndex++] = 0x01;
|
||||
body[iIndex++] = sps[1];
|
||||
body[iIndex++] = sps[2];
|
||||
body[iIndex++] = sps[3];
|
||||
body[iIndex++] = 0xff;
|
||||
|
||||
/*sps*/
|
||||
body[iIndex++] = 0xe1;
|
||||
body[iIndex++] = (sps_len >> 8) & 0xff;
|
||||
body[iIndex++] = sps_len & 0xff;
|
||||
memcpy(&body[iIndex],sps,sps_len);
|
||||
iIndex += sps_len;
|
||||
|
||||
/*pps*/
|
||||
body[iIndex++] = 0x01;
|
||||
body[iIndex++] = (pps_len >> 8) & 0xff;
|
||||
body[iIndex++] = (pps_len) & 0xff;
|
||||
memcpy(&body[iIndex], pps, pps_len);
|
||||
iIndex += pps_len;
|
||||
|
||||
[self sendPacket:RTMP_PACKET_TYPE_VIDEO data:body size:iIndex nTimestamp:0];
|
||||
free(body);
|
||||
}
|
||||
|
||||
|
||||
- (void)sendVideo:(LFVideoFrame*)frame{
|
||||
if(!frame || !frame.data || frame.data.length < 11) return;
|
||||
|
||||
NSInteger i = 0;
|
||||
NSInteger rtmpLength = frame.data.length+9;
|
||||
unsigned char *body = (unsigned char*)malloc(rtmpLength);
|
||||
memset(body,0,rtmpLength);
|
||||
|
||||
if(frame.isKeyFrame){
|
||||
body[i++] = 0x17;// 1:Iframe 7:AVC
|
||||
} else{
|
||||
body[i++] = 0x27;// 2:Pframe 7:AVC
|
||||
}
|
||||
body[i++] = 0x01;// AVC NALU
|
||||
body[i++] = 0x00;
|
||||
body[i++] = 0x00;
|
||||
body[i++] = 0x00;
|
||||
body[i++] = (frame.data.length >> 24) & 0xff;
|
||||
body[i++] = (frame.data.length >> 16) & 0xff;
|
||||
body[i++] = (frame.data.length >> 8) & 0xff;
|
||||
body[i++] = (frame.data.length ) & 0xff;
|
||||
memcpy(&body[i],frame.data.bytes,frame.data.length);
|
||||
|
||||
[self sendPacket:RTMP_PACKET_TYPE_VIDEO data:body size:(rtmpLength) nTimestamp:frame.timestamp];
|
||||
free(body);
|
||||
}
|
||||
|
||||
-(NSInteger) sendPacket:(unsigned int)nPacketType data:(unsigned char *)data size:(NSInteger) size nTimestamp:(uint64_t) nTimestamp{
|
||||
NSInteger rtmpLength = size;
|
||||
PILI_RTMPPacket rtmp_pack;
|
||||
PILI_RTMPPacket_Reset(&rtmp_pack);
|
||||
PILI_RTMPPacket_Alloc(&rtmp_pack,(uint32_t)rtmpLength);
|
||||
|
||||
rtmp_pack.m_nBodySize = (uint32_t)size;
|
||||
memcpy(rtmp_pack.m_body,data,size);
|
||||
rtmp_pack.m_hasAbsTimestamp = 0;
|
||||
rtmp_pack.m_packetType = nPacketType;
|
||||
if(_rtmp) rtmp_pack.m_nInfoField2 = _rtmp->m_stream_id;
|
||||
rtmp_pack.m_nChannel = 0x04;
|
||||
rtmp_pack.m_headerType = RTMP_PACKET_SIZE_LARGE;
|
||||
if (RTMP_PACKET_TYPE_AUDIO == nPacketType && size !=4){
|
||||
rtmp_pack.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
|
||||
}
|
||||
rtmp_pack.m_nTimeStamp = (uint32_t)nTimestamp;
|
||||
|
||||
NSInteger nRet = [self RtmpPacketSend:&rtmp_pack];
|
||||
|
||||
PILI_RTMPPacket_Free(&rtmp_pack);
|
||||
return nRet;
|
||||
}
|
||||
|
||||
- (NSInteger)RtmpPacketSend:(PILI_RTMPPacket*)packet{
|
||||
if (PILI_RTMP_IsConnected(_rtmp)){
|
||||
int success = PILI_RTMP_SendPacket(_rtmp,packet,0,&_error);
|
||||
if(success){
|
||||
self.isSending = NO;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self sendFrame];
|
||||
});
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (void)sendAudioHeader:(LFAudioFrame*)audioFrame{
|
||||
if(!audioFrame || !audioFrame.audioInfo) return;
|
||||
|
||||
NSInteger rtmpLength = audioFrame.audioInfo.length + 2;/*spec data长度,一般是2*/
|
||||
unsigned char * body = (unsigned char*)malloc(rtmpLength);
|
||||
memset(body,0,rtmpLength);
|
||||
|
||||
/*AF 00 + AAC RAW data*/
|
||||
body[0] = 0xAF;
|
||||
body[1] = 0x00;
|
||||
memcpy(&body[2],audioFrame.audioInfo.bytes,audioFrame.audioInfo.length); /*spec_buf是AAC sequence header数据*/
|
||||
[self sendPacket:RTMP_PACKET_TYPE_AUDIO data:body size:rtmpLength nTimestamp:0];
|
||||
free(body);
|
||||
}
|
||||
|
||||
- (void)sendAudio:(LFFrame*)frame {
|
||||
if(!frame) return;
|
||||
|
||||
NSInteger rtmpLength = frame.data.length + 2;/*spec data长度,一般是2*/
|
||||
unsigned char * body = (unsigned char*)malloc(rtmpLength);
|
||||
memset(body,0,rtmpLength);
|
||||
|
||||
/*AF 01 + AAC RAW data*/
|
||||
body[0] = 0xAF;
|
||||
body[1] = 0x01;
|
||||
memcpy(&body[2],frame.data.bytes,frame.data.length);
|
||||
[self sendPacket:RTMP_PACKET_TYPE_AUDIO data:body size:rtmpLength nTimestamp:frame.timestamp];
|
||||
free(body);
|
||||
}
|
||||
|
||||
// 断线重连
|
||||
-(void) reconnect {
|
||||
dispatch_async(YYRtmpSendQueue(), ^{
|
||||
_isReconnecting = NO;
|
||||
if(_isConnected) return;
|
||||
|
||||
[self stop];
|
||||
[self start];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -- CallBack
|
||||
void RTMPErrorCallback(RTMPError *error, void *userData){
|
||||
LFStreamRtmpSocket *socket = (__bridge LFStreamRtmpSocket*)userData;
|
||||
if(error->code < 0){
|
||||
if(socket.retryTimes4netWorkBreaken++ < socket.reconnectCount && !socket.isReconnecting){
|
||||
socket.isConnected = NO;
|
||||
socket.isConnecting = NO;
|
||||
socket.isReconnecting = YES;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(socket.reconnectInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[socket reconnect];
|
||||
});
|
||||
}else if(socket.retryTimes4netWorkBreaken >= socket.reconnectCount){
|
||||
if(socket.delegate && [socket.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[socket.delegate socketStatus:socket status:LFLiveError];
|
||||
}
|
||||
if(socket.delegate && [socket.delegate respondsToSelector:@selector(socketDidError:errorCode:)]){
|
||||
[socket.delegate socketDidError:socket errorCode:LFLiveSocketError_ReConnectTimeOut];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionTimeCallback(PILI_CONNECTION_TIME* conn_time, void *userData){
|
||||
//LFStreamRtmpSocket *socket = (__bridge LFStreamRtmpSocket*)userData;
|
||||
}
|
||||
|
||||
#pragma mark -- Getter Setter
|
||||
|
||||
- (LFStreamingBuffer*)buffer{
|
||||
if(!_buffer){
|
||||
_buffer = [[LFStreamingBuffer alloc] init];
|
||||
_buffer.delegate = self;
|
||||
}
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
- (LFLiveDebug*)debugInfo{
|
||||
if(!_debugInfo){
|
||||
_debugInfo = [[LFLiveDebug alloc] init];
|
||||
}
|
||||
return _debugInfo;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,37 +0,0 @@
|
||||
//
|
||||
// LFStreamSocket.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by admin on 16/5/3.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFStreamingBuffer.h"
|
||||
#import "LFLiveDebug.h"
|
||||
|
||||
@protocol LFStreamSocket ;
|
||||
@protocol LFStreamSocketDelegate <NSObject>
|
||||
|
||||
/** callback buffer current status (回调当前缓冲区情况,可实现相关切换帧率 码率等策略)*/
|
||||
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status;
|
||||
/** callback socket current status (回调当前网络情况) */
|
||||
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status;
|
||||
/** callback socket errorcode */
|
||||
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode;
|
||||
@optional
|
||||
/** callback debugInfo */
|
||||
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug*)debugInfo;
|
||||
@end
|
||||
|
||||
@protocol LFStreamSocket <NSObject>
|
||||
- (void) start;
|
||||
- (void) stop;
|
||||
- (void) sendFrame:(nullable LFFrame*)frame;
|
||||
- (void) setDelegate:(nullable id<LFStreamSocketDelegate>)delegate;
|
||||
@optional
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream;
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream videoSize:(CGSize)videoSize;
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream videoSize:(CGSize)videoSize reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount;
|
||||
@end
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// LFStreamTcpSocket.h
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by admin on 16/5/3.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFStreamSocket.h"
|
||||
|
||||
@interface LFStreamTcpSocket : NSObject<LFStreamSocket>
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
/// @name Initializer
|
||||
///=============================================================================
|
||||
- (nullable instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
@end
|
||||
@@ -1,331 +0,0 @@
|
||||
//
|
||||
// LFStreamTcpSocket.m
|
||||
// LFLiveKit
|
||||
//
|
||||
// Created by admin on 16/5/3.
|
||||
// Copyright © 2016年 倾慕. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LFStreamTcpSocket.h"
|
||||
#import "GCDAsyncSocket.h"
|
||||
#import "LFFlvPackage.h"
|
||||
|
||||
static const NSInteger RetryTimesBreaken = 20;///< 重连3分钟 3秒一次 一共60次
|
||||
static const NSInteger RetryTimesMargin = 3;
|
||||
const NSInteger TCP_RECEIVE_TIMEOUT = -1;
|
||||
|
||||
@interface LFStreamTcpSocket () <LFStreamingBufferDelegate,GCDAsyncSocketDelegate>
|
||||
|
||||
@property (nonatomic, strong) GCDAsyncSocket * socket;
|
||||
@property (nonatomic, strong) dispatch_queue_t socketQueue;
|
||||
@property (nonatomic, strong) LFStreamingBuffer *buffer;
|
||||
@property (nonatomic, strong) LFLiveStreamInfo *stream;
|
||||
@property (nonatomic, weak) id<LFStreamSocketDelegate> delegate;
|
||||
@property (nonatomic, strong) id<LFStreamPackage> package;
|
||||
@property (nonatomic, strong) LFLiveDebug *debugInfo;
|
||||
@property (nonatomic, assign) CGSize videoSize;
|
||||
|
||||
@property (nonatomic, assign) BOOL isSending;
|
||||
@property (nonatomic, assign) BOOL isConnecting;
|
||||
@property (nonatomic, assign) BOOL isReconnecting;
|
||||
@property (nonatomic, assign) BOOL isConnected;
|
||||
@property (nonatomic, assign) NSInteger retryTimes4netWorkBreaken;
|
||||
@property (nonatomic, assign) NSInteger reconnectInterval;
|
||||
@property (nonatomic, assign) NSInteger reconnectCount;
|
||||
@property (nonatomic, assign) BOOL needSendHeader;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFStreamTcpSocket
|
||||
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream videoSize:(CGSize)videoSize reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount{
|
||||
if(!stream) @throw [NSException exceptionWithName:@"LFStreamTcpSocket init error" reason:@"stream is nil" userInfo:nil];
|
||||
if(CGSizeEqualToSize(videoSize, CGSizeZero)) @throw [NSException exceptionWithName:@"LFStreamTcpSocket init error" reason:@"videoSize is zero" userInfo:nil];
|
||||
if(self = [super init]){
|
||||
_stream = stream;
|
||||
_videoSize = videoSize;
|
||||
if(reconnectInterval > 0) _reconnectInterval = reconnectInterval;
|
||||
else _reconnectInterval = RetryTimesMargin;
|
||||
|
||||
if(reconnectCount > 0) _reconnectCount = reconnectCount;
|
||||
else _reconnectCount = RetryTimesBreaken;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -- LFStreamSocket
|
||||
- (void) start{
|
||||
if(!_stream) return;
|
||||
if(_isConnecting) return;
|
||||
if(_socket.isConnected) return;
|
||||
[self clean];
|
||||
|
||||
self.debugInfo.streamId = self.stream.streamId;
|
||||
self.debugInfo.uploadUrl = self.stream.url;
|
||||
self.debugInfo.videoSize = self.videoSize;
|
||||
self.debugInfo.isRtmp = NO;
|
||||
|
||||
if(![self.socket connectToHost:_stream.host onPort:_stream.port withTimeout:5 error:nil]){
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveError];
|
||||
}
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketDidError:errorCode:)]){
|
||||
[self.delegate socketDidError:self errorCode:LFLiveSocketError_ConnectSocket];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLivePending];
|
||||
}
|
||||
_isConnecting = YES;
|
||||
|
||||
}
|
||||
|
||||
- (void) stop{
|
||||
[self.socket disconnect];
|
||||
[self clean];
|
||||
}
|
||||
|
||||
- (void)sendFrame:(LFFrame *)frame{
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(self.socketQueue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if(!frame) return;
|
||||
if([frame isKindOfClass:[LFAudioFrame class]]){
|
||||
NSData *packageData = [self.package aaCPacket:(LFAudioFrame*)frame];///< 打包flv
|
||||
if(!packageData) return;
|
||||
frame.data = packageData;
|
||||
}else{
|
||||
NSData *packageData = [self.package h264Packet:(LFVideoFrame*)frame];///< 打包flv
|
||||
if(!packageData) return;
|
||||
frame.data = packageData;
|
||||
}
|
||||
|
||||
[self.buffer appendObject:frame];
|
||||
[self sendFrame];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<LFStreamSocketDelegate>)delegate{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (void)sendFrame{
|
||||
if(!self.isSending && self.buffer.list.count > 0 && _isConnected){
|
||||
self.isSending = YES;
|
||||
LFFrame *frame = [self.buffer popFirstObject];
|
||||
if(self.needSendHeader){///< flvHeader 插入到队列最前面去
|
||||
NSMutableData * mutableData = [[NSMutableData alloc] init];
|
||||
[mutableData appendData:frame.header];
|
||||
[mutableData appendData:frame.data];
|
||||
frame.data = mutableData;
|
||||
self.needSendHeader = NO;
|
||||
}
|
||||
[self.socket writeData:frame.data withTimeout:TCP_RECEIVE_TIMEOUT tag:1];
|
||||
|
||||
self.debugInfo.dataFlow += frame.data.length;
|
||||
if(CACurrentMediaTime()*1000 - self.debugInfo.timeStamp < 1000) {
|
||||
self.debugInfo.bandwidth += frame.data.length;
|
||||
if([frame isKindOfClass:[LFAudioFrame class]]){
|
||||
self.debugInfo.capturedAudioCount ++;
|
||||
}else{
|
||||
self.debugInfo.capturedVideoCount ++;
|
||||
}
|
||||
self.debugInfo.unSendCount = self.buffer.list.count;
|
||||
}else {
|
||||
self.debugInfo.currentBandwidth = self.debugInfo.bandwidth;
|
||||
self.debugInfo.currentCapturedAudioCount = self.debugInfo.capturedAudioCount;
|
||||
self.debugInfo.currentCapturedVideoCount = self.debugInfo.capturedVideoCount;
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketDebug:debugInfo:)]){
|
||||
[self.delegate socketDebug:self debugInfo:self.debugInfo];
|
||||
}
|
||||
|
||||
self.debugInfo.bandwidth = 0;
|
||||
self.debugInfo.capturedAudioCount = 0;
|
||||
self.debugInfo.capturedVideoCount = 0;
|
||||
self.debugInfo.timeStamp = CACurrentMediaTime()*1000;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clean{
|
||||
_isConnected = NO;
|
||||
_isConnecting = NO;
|
||||
_isReconnecting = NO;
|
||||
_isSending = NO;
|
||||
_retryTimes4netWorkBreaken = 0;
|
||||
_needSendHeader = NO;
|
||||
self.debugInfo = nil;
|
||||
[self.buffer removeAllObject];
|
||||
}
|
||||
|
||||
// 断线重连
|
||||
-(void) reconnect {
|
||||
_isReconnecting = NO;
|
||||
if(_isConnected) return;
|
||||
if([self.socket isConnected]) return;
|
||||
|
||||
if(![self.socket connectToHost:_stream.host onPort:_stream.port withTimeout:5 error:nil]){
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveError];
|
||||
}
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketDidError:errorCode:)]){
|
||||
[self.delegate socketDidError:self errorCode:LFLiveSocketError_ConnectSocket];
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -- GCDAsyncSocketDelegate
|
||||
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
|
||||
NSLog(@"onSocket:%p didConnectToHost:%@ port:%hu", sock, host, port);
|
||||
[sock readDataWithTimeout:-1 tag:0];
|
||||
if(_isConnected) return;
|
||||
[self.socket writeData:self.verificationData withTimeout:-1 tag:0];
|
||||
}
|
||||
|
||||
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
|
||||
[sock readDataWithTimeout:-1 tag:0];
|
||||
if(_isConnected) return;
|
||||
if([self verificationDataValid:data]){
|
||||
NSLog(@"服务器验证成功,准备发送数据");
|
||||
_isConnected = YES;
|
||||
_isConnecting = NO;
|
||||
_isReconnecting = NO;
|
||||
_retryTimes4netWorkBreaken = 0;// 计数器清零
|
||||
_needSendHeader = YES;
|
||||
self.isSending = NO;
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveStart];
|
||||
}
|
||||
}else{
|
||||
NSLog(@"服务器验证失败");
|
||||
[self clean];
|
||||
[self.socket disconnect];
|
||||
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveError];
|
||||
}
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketDidError:errorCode:)]){
|
||||
[self.delegate socketDidError:self errorCode:LFLiveSocketError_Verification];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
|
||||
NSLog(@"onSocket:%p socketDidDisconnectWithError:%@", sock, err);
|
||||
if(err){
|
||||
if(self.retryTimes4netWorkBreaken++ < _reconnectCount && !self.isReconnecting){
|
||||
_isConnected = NO;
|
||||
_isConnecting = NO;
|
||||
_isReconnecting = YES;
|
||||
|
||||
[self.socket disconnect];
|
||||
///< 连接超时
|
||||
if(err.code == 3){
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveError];
|
||||
}
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketDidError:errorCode:)]){
|
||||
[self.delegate socketDidError:self errorCode:LFLiveSocketError_ConnectSocket];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_reconnectInterval * NSEC_PER_SEC)), self.socketQueue, ^{
|
||||
[self reconnect];
|
||||
});
|
||||
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLivePending];
|
||||
}
|
||||
}else if(self.retryTimes4netWorkBreaken >= _reconnectCount){
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveError];
|
||||
}
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketDidError:errorCode:)]){
|
||||
[self.delegate socketDidError:self errorCode:LFLiveSocketError_ReConnectTimeOut];
|
||||
}
|
||||
}
|
||||
}else{
|
||||
[self clean];
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveStop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
|
||||
if(tag > 0){
|
||||
self.isSending = NO;
|
||||
[self sendFrame];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark --BufferDelegate
|
||||
- (void)streamingBuffer:(nullable LFStreamingBuffer*)buffer bufferState:(LFLiveBuffferState)state{
|
||||
if(self.isConnected){
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketBufferStatus:status:)]){
|
||||
[self.delegate socketBufferStatus:self status:state];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -- Getter Setter
|
||||
- (dispatch_queue_t)socketQueue{
|
||||
if(!_socketQueue){
|
||||
_socketQueue = dispatch_queue_create("com.youku.LaiFeng.live.socketQueue", NULL);
|
||||
}
|
||||
return _socketQueue;
|
||||
}
|
||||
|
||||
- (GCDAsyncSocket*)socket{
|
||||
if(!_socket){
|
||||
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.socketQueue socketQueue:self.socketQueue];
|
||||
}
|
||||
return _socket;
|
||||
}
|
||||
|
||||
- (LFStreamingBuffer*)buffer{
|
||||
if(!_buffer){
|
||||
_buffer = [[LFStreamingBuffer alloc] init];
|
||||
_buffer.delegate = self;
|
||||
}
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
- (id<LFStreamPackage>)package{
|
||||
if(!_package){
|
||||
_package = [[LFFlvPackage alloc] initWithVideoSize:self.videoSize];
|
||||
}
|
||||
return _package;
|
||||
}
|
||||
|
||||
- (LFLiveDebug*)debugInfo{
|
||||
if(!_debugInfo){
|
||||
_debugInfo = [[LFLiveDebug alloc] init];
|
||||
}
|
||||
return _debugInfo;
|
||||
}
|
||||
|
||||
#pragma mark -- 服务器验证
|
||||
- (NSData*)verificationData{
|
||||
/** 结构体专为NSData **/
|
||||
if(!self.stream) return nil;
|
||||
#warning TODO send verficationData to server
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)verificationDataValid:(NSData*)data{
|
||||
/** NSData专为结构体 **/
|
||||
if(!self.stream) return NO;
|
||||
if(!data) return NO;
|
||||
#warning TODO server give client data,verification
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
#CocoaPods
|
||||
Pods/
|
||||
Podfile.lock
|
||||
@@ -7,219 +7,213 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
57B42059E84CC681C5C99B68 /* libPods-LFLiveKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D506B5639A1D45519536773 /* libPods-LFLiveKitDemo.a */; };
|
||||
840762F11D07C7D0000FD0BF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 840762F01D07C7D0000FD0BF /* main.m */; };
|
||||
840762F41D07C7D0000FD0BF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 840762F31D07C7D0000FD0BF /* AppDelegate.m */; };
|
||||
840762F71D07C7D0000FD0BF /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 840762F61D07C7D0000FD0BF /* ViewController.m */; };
|
||||
840762FA1D07C7D0000FD0BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 840762F81D07C7D0000FD0BF /* Main.storyboard */; };
|
||||
840762FC1D07C7D0000FD0BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 840762FB1D07C7D0000FD0BF /* Assets.xcassets */; };
|
||||
840762FF1D07C7D0000FD0BF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 840762FD1D07C7D0000FD0BF /* LaunchScreen.storyboard */; };
|
||||
840763291D07C894000FD0BF /* UIControl+YYAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 840763241D07C894000FD0BF /* UIControl+YYAdd.m */; };
|
||||
8407632A1D07C894000FD0BF /* UIView+YYAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 840763261D07C894000FD0BF /* UIView+YYAdd.m */; };
|
||||
8407632B1D07C894000FD0BF /* LFLivePreview.m in Sources */ = {isa = PBXBuildFile; fileRef = 840763281D07C894000FD0BF /* LFLivePreview.m */; };
|
||||
840763351D07C899000FD0BF /* camra_beauty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8407632D1D07C899000FD0BF /* camra_beauty@2x.png */; };
|
||||
840763361D07C899000FD0BF /* camra_beauty@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8407632E1D07C899000FD0BF /* camra_beauty@3x.png */; };
|
||||
840763371D07C899000FD0BF /* camra_beauty_close@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8407632F1D07C899000FD0BF /* camra_beauty_close@2x.png */; };
|
||||
840763381D07C899000FD0BF /* camra_beauty_close@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 840763301D07C899000FD0BF /* camra_beauty_close@3x.png */; };
|
||||
840763391D07C899000FD0BF /* camra_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 840763311D07C899000FD0BF /* camra_preview@2x.png */; };
|
||||
8407633A1D07C899000FD0BF /* camra_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 840763321D07C899000FD0BF /* camra_preview@3x.png */; };
|
||||
8407633B1D07C899000FD0BF /* close_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 840763331D07C899000FD0BF /* close_preview@2x.png */; };
|
||||
8407633C1D07C899000FD0BF /* close_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 840763341D07C899000FD0BF /* close_preview@3x.png */; };
|
||||
63B306871D75D2A5006CF72B /* pili_rtmp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63B306861D75D2A5006CF72B /* pili_rtmp.framework */; };
|
||||
84D8B42B1D75778B00752B56 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B42A1D75778B00752B56 /* main.m */; };
|
||||
84D8B42E1D75778B00752B56 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B42D1D75778B00752B56 /* AppDelegate.m */; };
|
||||
84D8B4311D75778B00752B56 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B4301D75778B00752B56 /* ViewController.m */; };
|
||||
84D8B4341D75778B00752B56 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4321D75778B00752B56 /* Main.storyboard */; };
|
||||
84D8B4361D75778B00752B56 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4351D75778B00752B56 /* Assets.xcassets */; };
|
||||
84D8B4391D75778B00752B56 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4371D75778B00752B56 /* LaunchScreen.storyboard */; };
|
||||
84D8B44B1D75781200752B56 /* LFLivePreview.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B44A1D75781200752B56 /* LFLivePreview.m */; };
|
||||
84D8B45B1D75782700752B56 /* UIControl+YYAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B44E1D75782700752B56 /* UIControl+YYAdd.m */; };
|
||||
84D8B45C1D75782700752B56 /* UIView+YYAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B4501D75782700752B56 /* UIView+YYAdd.m */; };
|
||||
84D8B45D1D75782700752B56 /* camra_beauty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4521D75782700752B56 /* camra_beauty@2x.png */; };
|
||||
84D8B45E1D75782700752B56 /* camra_beauty@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4531D75782700752B56 /* camra_beauty@3x.png */; };
|
||||
84D8B45F1D75782700752B56 /* camra_beauty_close@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4541D75782700752B56 /* camra_beauty_close@2x.png */; };
|
||||
84D8B4601D75782700752B56 /* camra_beauty_close@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4551D75782700752B56 /* camra_beauty_close@3x.png */; };
|
||||
84D8B4611D75782700752B56 /* camra_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4561D75782700752B56 /* camra_preview@2x.png */; };
|
||||
84D8B4621D75782700752B56 /* camra_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4571D75782700752B56 /* camra_preview@3x.png */; };
|
||||
84D8B4631D75782700752B56 /* close_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4581D75782700752B56 /* close_preview@2x.png */; };
|
||||
84D8B4641D75782700752B56 /* close_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4591D75782700752B56 /* close_preview@3x.png */; };
|
||||
84D8B4651D75782700752B56 /* ios-29x29.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B45A1D75782700752B56 /* ios-29x29.png */; };
|
||||
84D8B4681D75783F00752B56 /* GPUImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4661D75783F00752B56 /* GPUImage.framework */; };
|
||||
84D8B4791D757A5500752B56 /* LFLiveKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4451D75779200752B56 /* LFLiveKit.framework */; };
|
||||
84D8B4CF1D757F0700752B56 /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4CE1D757F0700752B56 /* libstdc++.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
84D8B4441D75779200752B56 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 84D8B3901D7574D600752B56;
|
||||
remoteInfo = LFLiveKit;
|
||||
};
|
||||
84D8B4761D757A3000752B56 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 84D8B38F1D7574D600752B56;
|
||||
remoteInfo = LFLiveKit;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
195672426061368F86F1F4FA /* Pods-LFLiveKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LFLiveKitDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-LFLiveKitDemo/Pods-LFLiveKitDemo.release.xcconfig"; sourceTree = "<group>"; };
|
||||
5D506B5639A1D45519536773 /* libPods-LFLiveKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LFLiveKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7FAA55DD93CD7AB58E7A977A /* Pods-LFLiveKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LFLiveKitDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LFLiveKitDemo/Pods-LFLiveKitDemo.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
840762EC1D07C7D0000FD0BF /* LFLiveKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LFLiveKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
840762F01D07C7D0000FD0BF /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
840762F21D07C7D0000FD0BF /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
840762F31D07C7D0000FD0BF /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
840762F51D07C7D0000FD0BF /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
||||
840762F61D07C7D0000FD0BF /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
||||
840762F91D07C7D0000FD0BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
840762FB1D07C7D0000FD0BF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
840762FE1D07C7D0000FD0BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
840763001D07C7D0000FD0BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
840763091D07C7D0000FD0BF /* LFLiveKitDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LFLiveKitDemoTests.m; sourceTree = "<group>"; };
|
||||
8407630B1D07C7D0000FD0BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
840763141D07C7D0000FD0BF /* LFLiveKitDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LFLiveKitDemoUITests.m; sourceTree = "<group>"; };
|
||||
840763161D07C7D0000FD0BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
840763231D07C894000FD0BF /* UIControl+YYAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+YYAdd.h"; sourceTree = "<group>"; };
|
||||
840763241D07C894000FD0BF /* UIControl+YYAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+YYAdd.m"; sourceTree = "<group>"; };
|
||||
840763251D07C894000FD0BF /* UIView+YYAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+YYAdd.h"; sourceTree = "<group>"; };
|
||||
840763261D07C894000FD0BF /* UIView+YYAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+YYAdd.m"; sourceTree = "<group>"; };
|
||||
840763271D07C894000FD0BF /* LFLivePreview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLivePreview.h; sourceTree = "<group>"; };
|
||||
840763281D07C894000FD0BF /* LFLivePreview.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLivePreview.m; sourceTree = "<group>"; };
|
||||
8407632D1D07C899000FD0BF /* camra_beauty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@2x.png"; sourceTree = "<group>"; };
|
||||
8407632E1D07C899000FD0BF /* camra_beauty@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@3x.png"; sourceTree = "<group>"; };
|
||||
8407632F1D07C899000FD0BF /* camra_beauty_close@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@2x.png"; sourceTree = "<group>"; };
|
||||
840763301D07C899000FD0BF /* camra_beauty_close@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@3x.png"; sourceTree = "<group>"; };
|
||||
840763311D07C899000FD0BF /* camra_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@2x.png"; sourceTree = "<group>"; };
|
||||
840763321D07C899000FD0BF /* camra_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@3x.png"; sourceTree = "<group>"; };
|
||||
840763331D07C899000FD0BF /* close_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@2x.png"; sourceTree = "<group>"; };
|
||||
840763341D07C899000FD0BF /* close_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@3x.png"; sourceTree = "<group>"; };
|
||||
63B306861D75D2A5006CF72B /* pili_rtmp.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = pili_rtmp.framework; path = ../Vendor/pili_rtmp.framework; sourceTree = "<group>"; };
|
||||
84D8B4261D75778B00752B56 /* LFLiveKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LFLiveKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84D8B42A1D75778B00752B56 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
84D8B42C1D75778B00752B56 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
84D8B42D1D75778B00752B56 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
84D8B42F1D75778B00752B56 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
||||
84D8B4301D75778B00752B56 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
||||
84D8B4331D75778B00752B56 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
84D8B4351D75778B00752B56 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
84D8B4381D75778B00752B56 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
84D8B43A1D75778B00752B56 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LFLiveKit.xcodeproj; path = ../LFLiveKit/LFLiveKit.xcodeproj; sourceTree = "<group>"; };
|
||||
84D8B4491D75781200752B56 /* LFLivePreview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLivePreview.h; sourceTree = "<group>"; };
|
||||
84D8B44A1D75781200752B56 /* LFLivePreview.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLivePreview.m; sourceTree = "<group>"; };
|
||||
84D8B44D1D75782700752B56 /* UIControl+YYAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+YYAdd.h"; sourceTree = "<group>"; };
|
||||
84D8B44E1D75782700752B56 /* UIControl+YYAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+YYAdd.m"; sourceTree = "<group>"; };
|
||||
84D8B44F1D75782700752B56 /* UIView+YYAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+YYAdd.h"; sourceTree = "<group>"; };
|
||||
84D8B4501D75782700752B56 /* UIView+YYAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+YYAdd.m"; sourceTree = "<group>"; };
|
||||
84D8B4521D75782700752B56 /* camra_beauty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@2x.png"; sourceTree = "<group>"; };
|
||||
84D8B4531D75782700752B56 /* camra_beauty@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@3x.png"; sourceTree = "<group>"; };
|
||||
84D8B4541D75782700752B56 /* camra_beauty_close@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@2x.png"; sourceTree = "<group>"; };
|
||||
84D8B4551D75782700752B56 /* camra_beauty_close@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@3x.png"; sourceTree = "<group>"; };
|
||||
84D8B4561D75782700752B56 /* camra_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@2x.png"; sourceTree = "<group>"; };
|
||||
84D8B4571D75782700752B56 /* camra_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@3x.png"; sourceTree = "<group>"; };
|
||||
84D8B4581D75782700752B56 /* close_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@2x.png"; sourceTree = "<group>"; };
|
||||
84D8B4591D75782700752B56 /* close_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@3x.png"; sourceTree = "<group>"; };
|
||||
84D8B45A1D75782700752B56 /* ios-29x29.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ios-29x29.png"; sourceTree = "<group>"; };
|
||||
84D8B4661D75783F00752B56 /* GPUImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GPUImage.framework; path = ../Vendor/GPUImage.framework; sourceTree = "<group>"; };
|
||||
84D8B4CE1D757F0700752B56 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
840762E91D07C7D0000FD0BF /* Frameworks */ = {
|
||||
84D8B4231D75778B00752B56 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
57B42059E84CC681C5C99B68 /* libPods-LFLiveKitDemo.a in Frameworks */,
|
||||
63B306871D75D2A5006CF72B /* pili_rtmp.framework in Frameworks */,
|
||||
84D8B4CF1D757F0700752B56 /* libstdc++.tbd in Frameworks */,
|
||||
84D8B4791D757A5500752B56 /* LFLiveKit.framework in Frameworks */,
|
||||
84D8B4681D75783F00752B56 /* GPUImage.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
713DA9EBCA308093C74917F9 /* Frameworks */ = {
|
||||
84D8B41D1D75778B00752B56 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5D506B5639A1D45519536773 /* libPods-LFLiveKitDemo.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840762E31D07C7D0000FD0BF = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840762EE1D07C7D0000FD0BF /* LFLiveKitDemo */,
|
||||
840763081D07C7D0000FD0BF /* LFLiveKitDemoTests */,
|
||||
840763131D07C7D0000FD0BF /* LFLiveKitDemoUITests */,
|
||||
840762ED1D07C7D0000FD0BF /* Products */,
|
||||
9BA1F10CECEAF692D0035AED /* Pods */,
|
||||
713DA9EBCA308093C74917F9 /* Frameworks */,
|
||||
63B306861D75D2A5006CF72B /* pili_rtmp.framework */,
|
||||
84D8B4CE1D757F0700752B56 /* libstdc++.tbd */,
|
||||
84D8B4661D75783F00752B56 /* GPUImage.framework */,
|
||||
84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */,
|
||||
84D8B4281D75778B00752B56 /* LFLiveKitDemo */,
|
||||
84D8B4271D75778B00752B56 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840762ED1D07C7D0000FD0BF /* Products */ = {
|
||||
84D8B4271D75778B00752B56 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840762EC1D07C7D0000FD0BF /* LFLiveKitDemo.app */,
|
||||
84D8B4261D75778B00752B56 /* LFLiveKitDemo.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840762EE1D07C7D0000FD0BF /* LFLiveKitDemo */ = {
|
||||
84D8B4281D75778B00752B56 /* LFLiveKitDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840763221D07C894000FD0BF /* category */,
|
||||
840763271D07C894000FD0BF /* LFLivePreview.h */,
|
||||
840763281D07C894000FD0BF /* LFLivePreview.m */,
|
||||
840762F21D07C7D0000FD0BF /* AppDelegate.h */,
|
||||
840762F31D07C7D0000FD0BF /* AppDelegate.m */,
|
||||
840762F51D07C7D0000FD0BF /* ViewController.h */,
|
||||
840762F61D07C7D0000FD0BF /* ViewController.m */,
|
||||
840762F81D07C7D0000FD0BF /* Main.storyboard */,
|
||||
840762FB1D07C7D0000FD0BF /* Assets.xcassets */,
|
||||
840762FD1D07C7D0000FD0BF /* LaunchScreen.storyboard */,
|
||||
840763001D07C7D0000FD0BF /* Info.plist */,
|
||||
840762EF1D07C7D0000FD0BF /* Supporting Files */,
|
||||
84D8B42C1D75778B00752B56 /* AppDelegate.h */,
|
||||
84D8B42D1D75778B00752B56 /* AppDelegate.m */,
|
||||
84D8B42F1D75778B00752B56 /* ViewController.h */,
|
||||
84D8B4301D75778B00752B56 /* ViewController.m */,
|
||||
84D8B4491D75781200752B56 /* LFLivePreview.h */,
|
||||
84D8B44A1D75781200752B56 /* LFLivePreview.m */,
|
||||
84D8B44C1D75782700752B56 /* category */,
|
||||
84D8B4511D75782700752B56 /* images */,
|
||||
84D8B4321D75778B00752B56 /* Main.storyboard */,
|
||||
84D8B4351D75778B00752B56 /* Assets.xcassets */,
|
||||
84D8B4371D75778B00752B56 /* LaunchScreen.storyboard */,
|
||||
84D8B43A1D75778B00752B56 /* Info.plist */,
|
||||
84D8B4291D75778B00752B56 /* Supporting Files */,
|
||||
);
|
||||
path = LFLiveKitDemo;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840762EF1D07C7D0000FD0BF /* Supporting Files */ = {
|
||||
84D8B4291D75778B00752B56 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8407632C1D07C899000FD0BF /* images */,
|
||||
840762F01D07C7D0000FD0BF /* main.m */,
|
||||
84D8B42A1D75778B00752B56 /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840763081D07C7D0000FD0BF /* LFLiveKitDemoTests */ = {
|
||||
84D8B4411D75779200752B56 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840763091D07C7D0000FD0BF /* LFLiveKitDemoTests.m */,
|
||||
8407630B1D07C7D0000FD0BF /* Info.plist */,
|
||||
84D8B4451D75779200752B56 /* LFLiveKit.framework */,
|
||||
);
|
||||
path = LFLiveKitDemoTests;
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840763131D07C7D0000FD0BF /* LFLiveKitDemoUITests */ = {
|
||||
84D8B44C1D75782700752B56 /* category */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840763141D07C7D0000FD0BF /* LFLiveKitDemoUITests.m */,
|
||||
840763161D07C7D0000FD0BF /* Info.plist */,
|
||||
);
|
||||
path = LFLiveKitDemoUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840763221D07C894000FD0BF /* category */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
840763231D07C894000FD0BF /* UIControl+YYAdd.h */,
|
||||
840763241D07C894000FD0BF /* UIControl+YYAdd.m */,
|
||||
840763251D07C894000FD0BF /* UIView+YYAdd.h */,
|
||||
840763261D07C894000FD0BF /* UIView+YYAdd.m */,
|
||||
84D8B44D1D75782700752B56 /* UIControl+YYAdd.h */,
|
||||
84D8B44E1D75782700752B56 /* UIControl+YYAdd.m */,
|
||||
84D8B44F1D75782700752B56 /* UIView+YYAdd.h */,
|
||||
84D8B4501D75782700752B56 /* UIView+YYAdd.m */,
|
||||
);
|
||||
path = category;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8407632C1D07C899000FD0BF /* images */ = {
|
||||
84D8B4511D75782700752B56 /* images */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8407632D1D07C899000FD0BF /* camra_beauty@2x.png */,
|
||||
8407632E1D07C899000FD0BF /* camra_beauty@3x.png */,
|
||||
8407632F1D07C899000FD0BF /* camra_beauty_close@2x.png */,
|
||||
840763301D07C899000FD0BF /* camra_beauty_close@3x.png */,
|
||||
840763311D07C899000FD0BF /* camra_preview@2x.png */,
|
||||
840763321D07C899000FD0BF /* camra_preview@3x.png */,
|
||||
840763331D07C899000FD0BF /* close_preview@2x.png */,
|
||||
840763341D07C899000FD0BF /* close_preview@3x.png */,
|
||||
84D8B4521D75782700752B56 /* camra_beauty@2x.png */,
|
||||
84D8B4531D75782700752B56 /* camra_beauty@3x.png */,
|
||||
84D8B4541D75782700752B56 /* camra_beauty_close@2x.png */,
|
||||
84D8B4551D75782700752B56 /* camra_beauty_close@3x.png */,
|
||||
84D8B4561D75782700752B56 /* camra_preview@2x.png */,
|
||||
84D8B4571D75782700752B56 /* camra_preview@3x.png */,
|
||||
84D8B4581D75782700752B56 /* close_preview@2x.png */,
|
||||
84D8B4591D75782700752B56 /* close_preview@3x.png */,
|
||||
84D8B45A1D75782700752B56 /* ios-29x29.png */,
|
||||
);
|
||||
path = images;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9BA1F10CECEAF692D0035AED /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7FAA55DD93CD7AB58E7A977A /* Pods-LFLiveKitDemo.debug.xcconfig */,
|
||||
195672426061368F86F1F4FA /* Pods-LFLiveKitDemo.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
840762EB1D07C7D0000FD0BF /* LFLiveKitDemo */ = {
|
||||
84D8B4251D75778B00752B56 /* LFLiveKitDemo */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 840763191D07C7D0000FD0BF /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */;
|
||||
buildConfigurationList = 84D8B43D1D75778B00752B56 /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */;
|
||||
buildPhases = (
|
||||
7E4C7C5523618A0595228010 /* 📦 Check Pods Manifest.lock */,
|
||||
840762E81D07C7D0000FD0BF /* Sources */,
|
||||
840762E91D07C7D0000FD0BF /* Frameworks */,
|
||||
840762EA1D07C7D0000FD0BF /* Resources */,
|
||||
E4007BB7D4B0E165011BF22F /* 📦 Embed Pods Frameworks */,
|
||||
64FCFF97E6544B1C8F282394 /* 📦 Copy Pods Resources */,
|
||||
84D8B4221D75778B00752B56 /* Sources */,
|
||||
84D8B4231D75778B00752B56 /* Frameworks */,
|
||||
84D8B4241D75778B00752B56 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
84D8B4771D757A3000752B56 /* PBXTargetDependency */,
|
||||
);
|
||||
name = LFLiveKitDemo;
|
||||
productName = LFLiveKitDemo;
|
||||
productReference = 840762EC1D07C7D0000FD0BF /* LFLiveKitDemo.app */;
|
||||
productReference = 84D8B4261D75778B00752B56 /* LFLiveKitDemo.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
840762E41D07C7D0000FD0BF /* Project object */ = {
|
||||
84D8B41E1D75778B00752B56 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0730;
|
||||
ORGANIZATIONNAME = admin;
|
||||
TargetAttributes = {
|
||||
840762EB1D07C7D0000FD0BF = {
|
||||
CreatedOnToolsVersion = 7.3;
|
||||
84D8B4251D75778B00752B56 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 840762E71D07C7D0000FD0BF /* Build configuration list for PBXProject "LFLiveKitDemo" */;
|
||||
buildConfigurationList = 84D8B4211D75778B00752B56 /* Build configuration list for PBXProject "LFLiveKitDemo" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
@@ -227,114 +221,91 @@
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 840762E31D07C7D0000FD0BF;
|
||||
productRefGroup = 840762ED1D07C7D0000FD0BF /* Products */;
|
||||
mainGroup = 84D8B41D1D75778B00752B56;
|
||||
productRefGroup = 84D8B4271D75778B00752B56 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
{
|
||||
ProductGroup = 84D8B4411D75779200752B56 /* Products */;
|
||||
ProjectRef = 84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */;
|
||||
},
|
||||
);
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
840762EB1D07C7D0000FD0BF /* LFLiveKitDemo */,
|
||||
84D8B4251D75778B00752B56 /* LFLiveKitDemo */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
84D8B4451D75779200752B56 /* LFLiveKit.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = LFLiveKit.framework;
|
||||
remoteRef = 84D8B4441D75779200752B56 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
840762EA1D07C7D0000FD0BF /* Resources */ = {
|
||||
84D8B4241D75778B00752B56 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
840763371D07C899000FD0BF /* camra_beauty_close@2x.png in Resources */,
|
||||
840762FF1D07C7D0000FD0BF /* LaunchScreen.storyboard in Resources */,
|
||||
840763351D07C899000FD0BF /* camra_beauty@2x.png in Resources */,
|
||||
840762FC1D07C7D0000FD0BF /* Assets.xcassets in Resources */,
|
||||
8407633A1D07C899000FD0BF /* camra_preview@3x.png in Resources */,
|
||||
840763381D07C899000FD0BF /* camra_beauty_close@3x.png in Resources */,
|
||||
8407633C1D07C899000FD0BF /* close_preview@3x.png in Resources */,
|
||||
8407633B1D07C899000FD0BF /* close_preview@2x.png in Resources */,
|
||||
840762FA1D07C7D0000FD0BF /* Main.storyboard in Resources */,
|
||||
840763391D07C899000FD0BF /* camra_preview@2x.png in Resources */,
|
||||
840763361D07C899000FD0BF /* camra_beauty@3x.png in Resources */,
|
||||
84D8B45F1D75782700752B56 /* camra_beauty_close@2x.png in Resources */,
|
||||
84D8B4391D75778B00752B56 /* LaunchScreen.storyboard in Resources */,
|
||||
84D8B45D1D75782700752B56 /* camra_beauty@2x.png in Resources */,
|
||||
84D8B4361D75778B00752B56 /* Assets.xcassets in Resources */,
|
||||
84D8B4621D75782700752B56 /* camra_preview@3x.png in Resources */,
|
||||
84D8B4601D75782700752B56 /* camra_beauty_close@3x.png in Resources */,
|
||||
84D8B4651D75782700752B56 /* ios-29x29.png in Resources */,
|
||||
84D8B4641D75782700752B56 /* close_preview@3x.png in Resources */,
|
||||
84D8B4631D75782700752B56 /* close_preview@2x.png in Resources */,
|
||||
84D8B4341D75778B00752B56 /* Main.storyboard in Resources */,
|
||||
84D8B4611D75782700752B56 /* camra_preview@2x.png in Resources */,
|
||||
84D8B45E1D75782700752B56 /* camra_beauty@3x.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
64FCFF97E6544B1C8F282394 /* 📦 Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "📦 Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LFLiveKitDemo/Pods-LFLiveKitDemo-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
7E4C7C5523618A0595228010 /* 📦 Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "📦 Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E4007BB7D4B0E165011BF22F /* 📦 Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "📦 Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LFLiveKitDemo/Pods-LFLiveKitDemo-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
840762E81D07C7D0000FD0BF /* Sources */ = {
|
||||
84D8B4221D75778B00752B56 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8407632B1D07C894000FD0BF /* LFLivePreview.m in Sources */,
|
||||
840762F71D07C7D0000FD0BF /* ViewController.m in Sources */,
|
||||
840763291D07C894000FD0BF /* UIControl+YYAdd.m in Sources */,
|
||||
840762F41D07C7D0000FD0BF /* AppDelegate.m in Sources */,
|
||||
8407632A1D07C894000FD0BF /* UIView+YYAdd.m in Sources */,
|
||||
840762F11D07C7D0000FD0BF /* main.m in Sources */,
|
||||
84D8B45B1D75782700752B56 /* UIControl+YYAdd.m in Sources */,
|
||||
84D8B4311D75778B00752B56 /* ViewController.m in Sources */,
|
||||
84D8B45C1D75782700752B56 /* UIView+YYAdd.m in Sources */,
|
||||
84D8B42E1D75778B00752B56 /* AppDelegate.m in Sources */,
|
||||
84D8B44B1D75781200752B56 /* LFLivePreview.m in Sources */,
|
||||
84D8B42B1D75778B00752B56 /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
84D8B4771D757A3000752B56 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = LFLiveKit;
|
||||
targetProxy = 84D8B4761D757A3000752B56 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
840762F81D07C7D0000FD0BF /* Main.storyboard */ = {
|
||||
84D8B4321D75778B00752B56 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
840762F91D07C7D0000FD0BF /* Base */,
|
||||
84D8B4331D75778B00752B56 /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
840762FD1D07C7D0000FD0BF /* LaunchScreen.storyboard */ = {
|
||||
84D8B4371D75778B00752B56 /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
840762FE1D07C7D0000FD0BF /* Base */,
|
||||
84D8B4381D75778B00752B56 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
@@ -342,7 +313,7 @@
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
840763171D07C7D0000FD0BF /* Debug */ = {
|
||||
84D8B43B1D75778B00752B56 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
@@ -379,15 +350,14 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
840763181D07C7D0000FD0BF /* Release */ = {
|
||||
84D8B43C1D75778B00752B56 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
@@ -418,38 +388,47 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8407631A1D07C7D0000FD0BF /* Debug */ = {
|
||||
84D8B43E1D75778B00752B56 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7FAA55DD93CD7AB58E7A977A /* Pods-LFLiveKitDemo.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../Vendor\"/**",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INFOPLIST_FILE = LFLiveKitDemo/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKitDemo;
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
OTHER_LDFLAGS = "-all_load";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKitDemo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8407631B1D07C7D0000FD0BF /* Release */ = {
|
||||
84D8B43F1D75778B00752B56 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 195672426061368F86F1F4FA /* Pods-LFLiveKitDemo.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../Vendor\"/**",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INFOPLIST_FILE = LFLiveKitDemo/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKitDemo;
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
OTHER_LDFLAGS = "-all_load";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKitDemo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
@@ -457,25 +436,25 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
840762E71D07C7D0000FD0BF /* Build configuration list for PBXProject "LFLiveKitDemo" */ = {
|
||||
84D8B4211D75778B00752B56 /* Build configuration list for PBXProject "LFLiveKitDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
840763171D07C7D0000FD0BF /* Debug */,
|
||||
840763181D07C7D0000FD0BF /* Release */,
|
||||
84D8B43B1D75778B00752B56 /* Debug */,
|
||||
84D8B43C1D75778B00752B56 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
840763191D07C7D0000FD0BF /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */ = {
|
||||
84D8B43D1D75778B00752B56 /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8407631A1D07C7D0000FD0BF /* Debug */,
|
||||
8407631B1D07C7D0000FD0BF /* Release */,
|
||||
84D8B43E1D75778B00752B56 /* Debug */,
|
||||
84D8B43F1D75778B00752B56 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 840762E41D07C7D0000FD0BF /* Project object */;
|
||||
rootObject = 84D8B41E1D75778B00752B56 /* Project object */;
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
+4
-24
@@ -14,7 +14,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "840762EB1D07C7D0000FD0BF"
|
||||
BlueprintIdentifier = "84D8B4251D75778B00752B56"
|
||||
BuildableName = "LFLiveKitDemo.app"
|
||||
BlueprintName = "LFLiveKitDemo"
|
||||
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
|
||||
@@ -28,31 +28,11 @@
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "840763041D07C7D0000FD0BF"
|
||||
BuildableName = "LFLiveKitDemoTests.xctest"
|
||||
BlueprintName = "LFLiveKitDemoTests"
|
||||
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8407630F1D07C7D0000FD0BF"
|
||||
BuildableName = "LFLiveKitDemoUITests.xctest"
|
||||
BlueprintName = "LFLiveKitDemoUITests"
|
||||
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "840762EB1D07C7D0000FD0BF"
|
||||
BlueprintIdentifier = "84D8B4251D75778B00752B56"
|
||||
BuildableName = "LFLiveKitDemo.app"
|
||||
BlueprintName = "LFLiveKitDemo"
|
||||
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
|
||||
@@ -75,7 +55,7 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "840762EB1D07C7D0000FD0BF"
|
||||
BlueprintIdentifier = "84D8B4251D75778B00752B56"
|
||||
BuildableName = "LFLiveKitDemo.app"
|
||||
BlueprintName = "LFLiveKitDemo"
|
||||
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
|
||||
@@ -94,7 +74,7 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "840762EB1D07C7D0000FD0BF"
|
||||
BlueprintIdentifier = "84D8B4251D75778B00752B56"
|
||||
BuildableName = "LFLiveKitDemo.app"
|
||||
BlueprintName = "LFLiveKitDemo"
|
||||
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
|
||||
|
||||
+1
-11
@@ -12,17 +12,7 @@
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>840762EB1D07C7D0000FD0BF</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>840763041D07C7D0000FD0BF</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>8407630F1D07C7D0000FD0BF</key>
|
||||
<key>84D8B4251D75778B00752B56</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:LFLiveKitDemo.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
Generated
BIN
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
// AppDelegate.h
|
||||
// LFLiveKitDemo
|
||||
//
|
||||
// Created by admin on 16/6/8.
|
||||
// Created by admin on 16/8/30.
|
||||
// Copyright © 2016年 admin. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// AppDelegate.m
|
||||
// LFLiveKitDemo
|
||||
//
|
||||
// Created by admin on 16/6/8.
|
||||
// Created by admin on 16/8/30.
|
||||
// Copyright © 2016年 admin. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
@@ -29,36 +29,6 @@
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user