Compare commits

...

40 Commits

Author SHA1 Message Date
chenliming ba19663059 update version 2016-08-01 11:22:16 +08:00
chenliming 0967800b23 update read 2016-08-01 11:01:55 +08:00
chenliming d0ef23d887 modify readme 2016-08-01 10:59:57 +08:00
chenliming 807d83c263 update podspec 2016-07-29 17:48:54 +08:00
chenliming 28d20814f6 update version 2016-07-29 17:43:33 +08:00
chenliming 4c385f6e89 compile question 2016-07-29 17:38:16 +08:00
chenliming 8708e04c0d modify cropSize 2016-07-29 17:08:45 +08:00
chenliming e47f3154a8 modify black screen bug 2016-07-26 18:27:16 +08:00
chenliming 7062cdcf7c modify bug https://github.com/LaiFengiOS/LFLiveKit/issues/11#issuecomment-235147191 2016-07-26 11:20:34 +08:00
chenliming bbe11292de modify bug https://github.com/LaiFengiOS/LFLiveKit/issues/22 2016-07-25 23:44:12 +08:00
小歪~~~ 2b51c69007 Merge pull request #21 from toss156/master
add LFH264VideoEncoder for iOS7
2016-07-25 15:25:35 +08:00
toss156 eb0edf85d2 Merge branch 'master' of https://github.com/toss156/LFLiveKit
# By chenliming (6) and bunnyirsa (1)
# Via GitHub (4) and chenliming (1)
* 'master' of https://github.com/toss156/LFLiveKit:
  update version
  add Note
  modify rtmp Continues to send after completion
  pod need 0.39,if need 1.0,please open pod file modify
  modify Weak network memory Has been increased
  修复首帧音视频不同步问题
  Modify Click the stop button to continue reconnection

Conflicts:
	LFLiveKit.xcodeproj/project.pbxproj
	LFLiveKit/LFLiveSession.m
	LFLiveKit/publish/LFStreamRTMPSocket.m
2016-07-25 15:18:57 +08:00
feng 5b097680f9 Merge pull request #6 from LaiFengiOS/master
update to last version
2016-07-25 15:01:40 +08:00
toss156 fb9ecaabad add LFH264VideoEncoder for iOS7 2016-07-25 15:00:24 +08:00
chenliming 6a971f872f update version 2016-07-22 11:56:12 +08:00
chenliming c86a88487d add Note 2016-07-22 11:48:07 +08:00
chenliming beac14592c modify rtmp Continues to send after completion 2016-07-22 11:44:32 +08:00
chenliming 24f8b42880 pod need 0.39,if need 1.0,please open pod file modify 2016-07-21 12:26:27 +08:00
chenliming 11848599b5 modify Weak network memory Has been increased 2016-07-20 17:57:38 +08:00
小歪~~~ d45e3770d6 Merge pull request #17 from bunnyirsa/develop
修复首帧音视频不同步问题
2016-07-20 14:02:37 +08:00
bunnyirsa d883f023ca 修复首帧音视频不同步问题 2016-07-19 19:37:48 +08:00
chenliming 0d269dcf25 Modify Click the stop button to continue reconnection 2016-07-19 14:33:25 +08:00
小歪~~~ 3b7ad944af Merge pull request #16 from toss156/master
Add Swift Demo
2016-07-19 13:42:28 +08:00
toss156 3e92282448 fix ci build error 2016-07-19 13:37:17 +08:00
toss156 f2f25ea19d add miss file 2016-07-19 12:45:38 +08:00
toss156 b910e120ff update demo 2016-07-19 12:33:38 +08:00
toss156 3d5c88416b update README.md 2016-07-19 12:23:10 +08:00
toss156 79744c0096 fix miss file error 2016-07-19 12:22:49 +08:00
toss156 191a603f98 add Swift Demo 2016-07-19 12:05:57 +08:00
小歪~~~ fff7d4513d Merge pull request #15 from toss156/master
update code style and remove unused cocoasynsocket
2016-07-19 10:32:56 +08:00
toss156 4f58fd019e update demo 2016-07-19 09:16:12 +08:00
toss156 a440270bb0 update podspec, remove unused cocoasynsocket 2016-07-19 09:09:35 +08:00
toss156 d57d987e67 format code style with unscrusify 2016-07-18 15:01:51 +08:00
feng 204aaa6d09 Merge pull request #5 from LaiFengiOS/master
update to last version
2016-07-18 13:12:38 +08:00
chenliming 3c58ff7e49 停止后clean一下 2016-07-15 15:26:06 +08:00
chenliming 320307bc2a update demo 2016-07-15 15:10:24 +08:00
chenliming 0e7b2475d7 update 2016-07-15 14:52:14 +08:00
chenliming 531c92df51 update read 2016-07-15 14:36:43 +08:00
feng ba1f7a383c Merge pull request #4 from LaiFengiOS/master
update to last version
2016-07-15 09:29:21 +08:00
feng 519b685855 Merge pull request #3 from LaiFengiOS/master
update to last version
2016-07-12 16:38:26 +08:00
84 changed files with 5601 additions and 2078 deletions
+5 -9
View File
@@ -2,25 +2,21 @@
Pod::Spec.new do |s|
s.name = "LFLiveKit"
s.version = "1.7.3"
s.version = "1.9.3"
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/**/*.{h,m}"
s.source_files = "LFLiveKit/**/*.{h,m,mm,cpp,c}"
s.public_header_files = "LFLiveKit/**/*.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"
end
+120 -39
View File
@@ -46,14 +46,25 @@
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 */; };
8493627A1D38B542002C8F13 /* LFStreamingBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 849362701D38B542002C8F13 /* LFStreamingBuffer.h */; };
8493627B1D38B542002C8F13 /* LFStreamingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 849362711D38B542002C8F13 /* LFStreamingBuffer.m */; };
8493627C1D38B542002C8F13 /* LFStreamRtmpSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 849362721D38B542002C8F13 /* LFStreamRtmpSocket.h */; };
8493627D1D38B542002C8F13 /* LFStreamRtmpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 849362731D38B542002C8F13 /* LFStreamRtmpSocket.m */; };
8493627E1D38B542002C8F13 /* LFStreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 849362741D38B542002C8F13 /* LFStreamSocket.h */; };
849362811D38B542002C8F13 /* NSMutableArray+LFAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = 849362781D38B542002C8F13 /* NSMutableArray+LFAdd.h */; };
849362821D38B542002C8F13 /* NSMutableArray+LFAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 849362791D38B542002C8F13 /* NSMutableArray+LFAdd.m */; };
AD7F89B4621A7EFEBEA72D49 /* libPods-LFLiveKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */; };
B289F1DB1D3DE77F00D9C7A5 /* LFStreamingBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = B289F1D41D3DE77F00D9C7A5 /* LFStreamingBuffer.h */; };
B289F1DC1D3DE77F00D9C7A5 /* LFStreamingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = B289F1D51D3DE77F00D9C7A5 /* LFStreamingBuffer.m */; };
B289F1DD1D3DE77F00D9C7A5 /* LFStreamRtmpSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = B289F1D61D3DE77F00D9C7A5 /* LFStreamRtmpSocket.h */; };
B289F1DE1D3DE77F00D9C7A5 /* LFStreamRtmpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = B289F1D71D3DE77F00D9C7A5 /* LFStreamRtmpSocket.m */; };
B289F1DF1D3DE77F00D9C7A5 /* LFStreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = B289F1D81D3DE77F00D9C7A5 /* LFStreamSocket.h */; };
B289F1E01D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = B289F1D91D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h */; };
B289F1E11D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = B289F1DA1D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m */; };
B2CD146D1D45F18B008082E8 /* AVEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD14621D45F18B008082E8 /* AVEncoder.h */; };
B2CD146E1D45F18B008082E8 /* AVEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2CD14631D45F18B008082E8 /* AVEncoder.mm */; };
B2CD146F1D45F18B008082E8 /* LICENSE.markdown in Sources */ = {isa = PBXBuildFile; fileRef = B2CD14641D45F18B008082E8 /* LICENSE.markdown */; };
B2CD14701D45F18B008082E8 /* MP4Atom.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD14651D45F18B008082E8 /* MP4Atom.h */; };
B2CD14711D45F18B008082E8 /* MP4Atom.m in Sources */ = {isa = PBXBuildFile; fileRef = B2CD14661D45F18B008082E8 /* MP4Atom.m */; };
B2CD14721D45F18B008082E8 /* NALUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2CD14671D45F18B008082E8 /* NALUnit.cpp */; };
B2CD14731D45F18B008082E8 /* NALUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD14681D45F18B008082E8 /* NALUnit.h */; };
B2CD14741D45F18B008082E8 /* VideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD14691D45F18B008082E8 /* VideoEncoder.h */; };
B2CD14751D45F18B008082E8 /* VideoEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = B2CD146A1D45F18B008082E8 /* VideoEncoder.m */; };
B2CD14761D45F18B008082E8 /* LFH264VideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B2CD146B1D45F18B008082E8 /* LFH264VideoEncoder.h */; };
B2CD14771D45F18B008082E8 /* LFH264VideoEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2CD146C1D45F18B008082E8 /* LFH264VideoEncoder.mm */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -109,14 +120,25 @@
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; };
849362701D38B542002C8F13 /* LFStreamingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamingBuffer.h; sourceTree = "<group>"; };
849362711D38B542002C8F13 /* LFStreamingBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamingBuffer.m; sourceTree = "<group>"; };
849362721D38B542002C8F13 /* LFStreamRtmpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamRtmpSocket.h; sourceTree = "<group>"; };
849362731D38B542002C8F13 /* LFStreamRtmpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamRtmpSocket.m; sourceTree = "<group>"; };
849362741D38B542002C8F13 /* LFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamSocket.h; sourceTree = "<group>"; };
849362781D38B542002C8F13 /* NSMutableArray+LFAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+LFAdd.h"; sourceTree = "<group>"; };
849362791D38B542002C8F13 /* NSMutableArray+LFAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+LFAdd.m"; 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>"; };
B289F1D41D3DE77F00D9C7A5 /* LFStreamingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LFStreamingBuffer.h; path = LFLiveKit/publish/LFStreamingBuffer.h; sourceTree = SOURCE_ROOT; };
B289F1D51D3DE77F00D9C7A5 /* LFStreamingBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LFStreamingBuffer.m; path = LFLiveKit/publish/LFStreamingBuffer.m; sourceTree = SOURCE_ROOT; };
B289F1D61D3DE77F00D9C7A5 /* LFStreamRtmpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LFStreamRtmpSocket.h; path = LFLiveKit/publish/LFStreamRtmpSocket.h; sourceTree = SOURCE_ROOT; };
B289F1D71D3DE77F00D9C7A5 /* LFStreamRtmpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LFStreamRtmpSocket.m; path = LFLiveKit/publish/LFStreamRtmpSocket.m; sourceTree = SOURCE_ROOT; };
B289F1D81D3DE77F00D9C7A5 /* LFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LFStreamSocket.h; path = LFLiveKit/publish/LFStreamSocket.h; sourceTree = SOURCE_ROOT; };
B289F1D91D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+LFAdd.h"; path = "LFLiveKit/publish/NSMutableArray+LFAdd.h"; sourceTree = SOURCE_ROOT; };
B289F1DA1D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+LFAdd.m"; path = "LFLiveKit/publish/NSMutableArray+LFAdd.m"; sourceTree = SOURCE_ROOT; };
B2CD14621D45F18B008082E8 /* AVEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVEncoder.h; sourceTree = "<group>"; };
B2CD14631D45F18B008082E8 /* AVEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AVEncoder.mm; sourceTree = "<group>"; };
B2CD14641D45F18B008082E8 /* LICENSE.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.markdown; sourceTree = "<group>"; };
B2CD14651D45F18B008082E8 /* MP4Atom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MP4Atom.h; sourceTree = "<group>"; };
B2CD14661D45F18B008082E8 /* MP4Atom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MP4Atom.m; sourceTree = "<group>"; };
B2CD14671D45F18B008082E8 /* NALUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NALUnit.cpp; sourceTree = "<group>"; };
B2CD14681D45F18B008082E8 /* NALUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NALUnit.h; sourceTree = "<group>"; };
B2CD14691D45F18B008082E8 /* VideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoEncoder.h; sourceTree = "<group>"; };
B2CD146A1D45F18B008082E8 /* VideoEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoEncoder.m; sourceTree = "<group>"; };
B2CD146B1D45F18B008082E8 /* LFH264VideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFH264VideoEncoder.h; sourceTree = "<group>"; };
B2CD146C1D45F18B008082E8 /* LFH264VideoEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LFH264VideoEncoder.mm; 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 */
@@ -192,7 +214,7 @@
84001FA41D0016380026C63F /* capture */,
84001FA91D0016380026C63F /* coder */,
84001FB51D0016380026C63F /* filter */,
8493626F1D38B542002C8F13 /* publish */,
84001FC91D0016380026C63F /* publish */,
84001F8F1D0015D10026C63F /* Info.plist */,
);
path = LFLiveKit;
@@ -221,6 +243,9 @@
84001FA91D0016380026C63F /* coder */ = {
isa = PBXGroup;
children = (
B2CD14611D45F18B008082E8 /* H264 */,
B2CD146B1D45F18B008082E8 /* LFH264VideoEncoder.h */,
B2CD146C1D45F18B008082E8 /* LFH264VideoEncoder.mm */,
84001FAA1D0016380026C63F /* LFAudioEncoding.h */,
84001FAB1D0016380026C63F /* LFHardwareAudioEncoder.h */,
84001FAC1D0016380026C63F /* LFHardwareAudioEncoder.m */,
@@ -270,18 +295,35 @@
path = objects;
sourceTree = "<group>";
};
8493626F1D38B542002C8F13 /* publish */ = {
84001FC91D0016380026C63F /* publish */ = {
isa = PBXGroup;
children = (
849362701D38B542002C8F13 /* LFStreamingBuffer.h */,
849362711D38B542002C8F13 /* LFStreamingBuffer.m */,
849362721D38B542002C8F13 /* LFStreamRtmpSocket.h */,
849362731D38B542002C8F13 /* LFStreamRtmpSocket.m */,
849362741D38B542002C8F13 /* LFStreamSocket.h */,
849362781D38B542002C8F13 /* NSMutableArray+LFAdd.h */,
849362791D38B542002C8F13 /* NSMutableArray+LFAdd.m */,
B289F1D41D3DE77F00D9C7A5 /* LFStreamingBuffer.h */,
B289F1D51D3DE77F00D9C7A5 /* LFStreamingBuffer.m */,
B289F1D61D3DE77F00D9C7A5 /* LFStreamRtmpSocket.h */,
B289F1D71D3DE77F00D9C7A5 /* LFStreamRtmpSocket.m */,
B289F1D81D3DE77F00D9C7A5 /* LFStreamSocket.h */,
B289F1D91D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h */,
B289F1DA1D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m */,
);
path = publish;
name = publish;
path = upload;
sourceTree = "<group>";
};
B2CD14611D45F18B008082E8 /* H264 */ = {
isa = PBXGroup;
children = (
B2CD14621D45F18B008082E8 /* AVEncoder.h */,
B2CD14631D45F18B008082E8 /* AVEncoder.mm */,
B2CD14641D45F18B008082E8 /* LICENSE.markdown */,
B2CD14651D45F18B008082E8 /* MP4Atom.h */,
B2CD14661D45F18B008082E8 /* MP4Atom.m */,
B2CD14671D45F18B008082E8 /* NALUnit.cpp */,
B2CD14681D45F18B008082E8 /* NALUnit.h */,
B2CD14691D45F18B008082E8 /* VideoEncoder.h */,
B2CD146A1D45F18B008082E8 /* VideoEncoder.m */,
);
path = H264;
sourceTree = "<group>";
};
EDD4B76A07A6817C79BB4E5C /* Pods */ = {
@@ -301,14 +343,17 @@
buildActionMask = 2147483647;
files = (
84001FDB1D0016380026C63F /* LFLiveAudioConfiguration.h in Headers */,
8493627C1D38B542002C8F13 /* LFStreamRtmpSocket.h in Headers */,
B289F1DD1D3DE77F00D9C7A5 /* LFStreamRtmpSocket.h in Headers */,
84001FDD1D0016380026C63F /* LFLiveVideoConfiguration.h in Headers */,
B2CD14701D45F18B008082E8 /* MP4Atom.h in Headers */,
84001FE31D0016380026C63F /* LFLiveSession.h in Headers */,
8493627A1D38B542002C8F13 /* LFStreamingBuffer.h in Headers */,
B289F1DB1D3DE77F00D9C7A5 /* LFStreamingBuffer.h in Headers */,
84001FEB1D0016380026C63F /* LFLiveStreamInfo.h in Headers */,
84001FE91D0016380026C63F /* LFLiveDebug.h in Headers */,
84001FE71D0016380026C63F /* LFFrame.h in Headers */,
B2CD14761D45F18B008082E8 /* LFH264VideoEncoder.h in Headers */,
84001FD61D0016380026C63F /* LFHardwareAudioEncoder.h in Headers */,
B289F1E01D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.h in Headers */,
84001FDF1D0016380026C63F /* LFGPUImageBeautyFilter.h in Headers */,
84001FD31D0016380026C63F /* LFVideoCapture.h in Headers */,
84001FD11D0016380026C63F /* LFAudioCapture.h in Headers */,
@@ -316,10 +361,12 @@
84001FDA1D0016380026C63F /* LFVideoEncoding.h in Headers */,
84001FE51D0016380026C63F /* LFAudioFrame.h in Headers */,
84001FED1D0016380026C63F /* LFVideoFrame.h in Headers */,
B2CD14741D45F18B008082E8 /* VideoEncoder.h in Headers */,
B2CD14731D45F18B008082E8 /* NALUnit.h in Headers */,
84001FD81D0016380026C63F /* LFHardwareVideoEncoder.h in Headers */,
849362811D38B542002C8F13 /* NSMutableArray+LFAdd.h in Headers */,
8493627E1D38B542002C8F13 /* LFStreamSocket.h in Headers */,
B289F1DF1D3DE77F00D9C7A5 /* LFStreamSocket.h in Headers */,
84001FD51D0016380026C63F /* LFAudioEncoding.h in Headers */,
B2CD146D1D45F18B008082E8 /* AVEncoder.h in Headers */,
84001F8E1D0015D10026C63F /* LFLiveKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -331,12 +378,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 84001F9E1D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKit" */;
buildPhases = (
8EE9401DCA9508E918B7FB68 /* 📦 Check Pods Manifest.lock */,
5ED199EAC89EE599F1E56B19 /* 📦 Check Pods Manifest.lock */,
84001F851D0015D10026C63F /* Sources */,
84001F861D0015D10026C63F /* Frameworks */,
84001F871D0015D10026C63F /* Headers */,
84001F881D0015D10026C63F /* Resources */,
817C22141AD3F2EB34365AA3 /* 📦 Copy Pods Resources */,
36D0848EAED7999C442A99BD /* 📦 Copy Pods Resources */,
);
buildRules = (
);
@@ -418,7 +465,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
817C22141AD3F2EB34365AA3 /* 📦 Copy Pods Resources */ = {
36D0848EAED7999C442A99BD /* 📦 Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -433,7 +480,7 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit-resources.sh\"\n";
showEnvVarsInLog = 0;
};
8EE9401DCA9508E918B7FB68 /* 📦 Check Pods Manifest.lock */ = {
5ED199EAC89EE599F1E56B19 /* 📦 Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -457,21 +504,27 @@
files = (
84001FE21D0016380026C63F /* LFGPUImageEmptyFilter.m in Sources */,
84001FE41D0016380026C63F /* LFLiveSession.m in Sources */,
B2CD14711D45F18B008082E8 /* MP4Atom.m in Sources */,
84001FE61D0016380026C63F /* LFAudioFrame.m in Sources */,
84001FDC1D0016380026C63F /* LFLiveAudioConfiguration.m in Sources */,
84001FD41D0016380026C63F /* LFVideoCapture.m in Sources */,
84001FE81D0016380026C63F /* LFFrame.m in Sources */,
849362821D38B542002C8F13 /* NSMutableArray+LFAdd.m in Sources */,
8493627B1D38B542002C8F13 /* LFStreamingBuffer.m in Sources */,
B2CD14721D45F18B008082E8 /* NALUnit.cpp in Sources */,
B289F1DC1D3DE77F00D9C7A5 /* LFStreamingBuffer.m in Sources */,
B289F1E11D3DE77F00D9C7A5 /* NSMutableArray+LFAdd.m in Sources */,
B2CD14771D45F18B008082E8 /* LFH264VideoEncoder.mm in Sources */,
84001FDE1D0016380026C63F /* LFLiveVideoConfiguration.m in Sources */,
84001FD21D0016380026C63F /* LFAudioCapture.m in Sources */,
8493627D1D38B542002C8F13 /* LFStreamRtmpSocket.m in Sources */,
B2CD14751D45F18B008082E8 /* VideoEncoder.m in Sources */,
B2CD146F1D45F18B008082E8 /* LICENSE.markdown in Sources */,
B289F1DE1D3DE77F00D9C7A5 /* LFStreamRtmpSocket.m in Sources */,
84001FD91D0016380026C63F /* LFHardwareVideoEncoder.m in Sources */,
84001FEC1D0016380026C63F /* LFLiveStreamInfo.m in Sources */,
84001FEA1D0016380026C63F /* LFLiveDebug.m in Sources */,
84001FEE1D0016380026C63F /* LFVideoFrame.m in Sources */,
84001FD71D0016380026C63F /* LFHardwareAudioEncoder.m in Sources */,
84001FE01D0016380026C63F /* LFGPUImageBeautyFilter.m in Sources */,
B2CD146E1D45F18B008082E8 /* AVEncoder.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -592,11 +645,25 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
ENABLE_BITCODE = YES;
INFOPLIST_FILE = LFLiveKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/LFLiveKit/publish/libpili-librtmp",
);
OTHER_CFLAGS = (
"$(inherited)",
"-isystem",
"\"${PODS_ROOT}/Headers/Public\"",
"-isystem",
"\"${PODS_ROOT}/Headers/Public/LMGPUImage\"",
"-isystem",
"\"${PODS_ROOT}/Headers/Public/pili-librtmp\"",
"-fembed-bitcode",
);
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -611,11 +678,25 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
ENABLE_BITCODE = YES;
INFOPLIST_FILE = LFLiveKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/LFLiveKit/publish/libpili-librtmp",
);
OTHER_CFLAGS = (
"$(inherited)",
"-isystem",
"\"${PODS_ROOT}/Headers/Public\"",
"-isystem",
"\"${PODS_ROOT}/Headers/Public/LMGPUImage\"",
"-isystem",
"\"${PODS_ROOT}/Headers/Public/pili-librtmp\"",
"-fembed-bitcode",
);
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -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>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.7.3</string>
<string>1.9.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+14 -14
View File
@@ -23,9 +23,9 @@
/** 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;
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug *)debugInfo;
/** callback socket errorcode */
- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode;
- (void)liveSession:(nullable LFLiveSession *)session errorCode:(LFLiveSocketErrorCode)errorCode;
@end
@class LFLiveStreamInfo;
@@ -37,13 +37,13 @@
/// @name Attribute
///=============================================================================
/** The delegate of the capture. captureData callback */
@property (nullable,nonatomic, weak) id<LFLiveSessionDelegate> delegate;
@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;
@property (nonatomic, strong, null_resettable) UIView *preView;
/** The captureDevicePosition control camraPosition ,default front*/
@property (nonatomic, assign) AVCaptureDevicePosition captureDevicePosition;
@@ -67,22 +67,22 @@
@property (nonatomic, assign) BOOL mirror;
/** The muted control callbackAudioData,muted will memset 0.*/
@property (nonatomic,assign) BOOL muted;
@property (nonatomic, assign) BOOL muted;
/** The stream control upload and package*/
@property (nullable,nonatomic, strong,readonly) LFLiveStreamInfo * streamInfo;
@property (nullable, nonatomic, strong, readonly) LFLiveStreamInfo *streamInfo;
/** The status of the stream .*/
@property (nonatomic,assign,readonly) LFLiveState state;
@property (nonatomic, assign, readonly) LFLiveState state;
/** The showDebugInfo control streamInfo and uploadInfo(1s) *.*/
@property (nonatomic,assign) BOOL showDebugInfo;
@property (nonatomic, assign) BOOL showDebugInfo;
/** The reconnectInterval control reconnect timeInterval(重连间隔) *.*/
@property (nonatomic,assign) NSUInteger reconnectInterval;
@property (nonatomic, assign) NSUInteger reconnectInterval;
/** The reconnectCount control reconnect count (重连次数) *.*/
@property (nonatomic,assign) NSUInteger reconnectCount;
@property (nonatomic, assign) NSUInteger reconnectCount;
#pragma mark - Initializer
///=============================================================================
@@ -92,13 +92,13 @@
+ (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*)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration*)videoConfiguration NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration NS_DESIGNATED_INITIALIZER;
/** The start stream .*/
- (void)startLive:(nonnull LFLiveStreamInfo*)streamInfo;
- (void)startLive:(nonnull LFLiveStreamInfo *)streamInfo;
/** The stop stream .*/
- (void)stopLive;
+78 -57
View File
@@ -11,13 +11,13 @@
#import "LFAudioCapture.h"
#import "LFHardwareVideoEncoder.h"
#import "LFHardwareAudioEncoder.h"
#import "LFStreamRtmpSocket.h"
#import "LFStreamRTMPSocket.h"
#import "LFLiveStreamInfo.h"
#import "LFGPUImageBeautyFilter.h"
#define LFLiveReportKey @"com.youku.liveSessionReport"
@interface LFLiveSession ()<LFAudioCaptureDelegate,LFVideoCaptureDelegate,LFAudioEncodingDelegate,LFVideoEncodingDelegate,LFStreamSocketDelegate>
@interface LFLiveSession ()<LFAudioCaptureDelegate, LFVideoCaptureDelegate, LFAudioEncodingDelegate, LFVideoEncodingDelegate, LFStreamSocketDelegate>
{
dispatch_semaphore_t _lock;
}
@@ -46,7 +46,7 @@
/// uploading
@property (nonatomic, assign) BOOL uploading;
/// state
@property (nonatomic,assign,readwrite) LFLiveState state;
@property (nonatomic, assign, readwrite) LFLiveState state;
@end
@@ -63,9 +63,9 @@
@implementation LFLiveSession
#pragma mark -- LifeCycle
- (instancetype)initWithAudioConfiguration:(LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(LFLiveVideoConfiguration *)videoConfiguration{
if(!audioConfiguration || !videoConfiguration) @throw [NSException exceptionWithName:@"LFLiveSession init error" reason:@"audioConfiguration or videoConfiguration is nil " userInfo:nil];
if(self = [super init]){
- (instancetype)initWithAudioConfiguration:(LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(LFLiveVideoConfiguration *)videoConfiguration {
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;
_lock = dispatch_semaphore_create(1);
@@ -73,88 +73,91 @@
return self;
}
- (void)dealloc{
- (void)dealloc {
self.audioCaptureSource.running = NO;
self.videoCaptureSource.running = NO;
}
#pragma mark -- CustomMethod
- (void)startLive:(LFLiveStreamInfo*)streamInfo{
if(!streamInfo) return;
- (void)startLive:(LFLiveStreamInfo *)streamInfo {
if (!streamInfo) return;
_streamInfo = streamInfo;
_streamInfo.videoConfiguration = _videoConfiguration;
_streamInfo.audioConfiguration = _audioConfiguration;
[self.socket start];
}
- (void)stopLive{
- (void)stopLive {
self.uploading = NO;
[self.socket stop];
self.socket = nil;
}
#pragma mark -- CaptureDelegate
- (void)captureOutput:(nullable LFAudioCapture*)capture audioBuffer:(AudioBufferList)inBufferList{
[self.audioEncoder encodeAudioData:inBufferList timeStamp:self.currentTimestamp];
- (void)captureOutput:(nullable LFAudioCapture *)capture audioBuffer:(AudioBufferList)inBufferList {
if (self.uploading) [self.audioEncoder encodeAudioData:inBufferList timeStamp:self.currentTimestamp];
}
- (void)captureOutput:(nullable LFVideoCapture*)capture pixelBuffer:(nullable CVImageBufferRef)pixelBuffer{
[self.videoEncoder encodeVideoData:pixelBuffer timeStamp:self.currentTimestamp];
- (void)captureOutput:(nullable LFVideoCapture *)capture pixelBuffer:(nullable CVImageBufferRef)pixelBuffer {
if (self.uploading) [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)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];//<上传
- (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){
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status {
if (status == LFLiveStart) {
if (!self.uploading) {
self.timestamp = 0;
self.isFirstFrame = YES;
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:)]){
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:liveStateDidChange:)]) {
[self.delegate liveSession:self liveStateDidChange:status];
}
});
}
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode{
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode {
dispatch_async(dispatch_get_main_queue(), ^{
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:errorCode:)]){
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{
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug *)debugInfo {
self.debugInfo = debugInfo;
if(self.showDebugInfo){
if (self.showDebugInfo) {
dispatch_async(dispatch_get_main_queue(), ^{
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:debugInfo:)]){
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:debugInfo:)]) {
[self.delegate liveSession:self debugInfo:debugInfo];
}
});
}
}
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status{
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status {
NSUInteger videoBitRate = [_videoEncoder videoBitRate];
if(status == LFLiveBuffferIncrease){
if(videoBitRate < _videoConfiguration.videoMaxBitRate){
if (status == LFLiveBuffferDecline) {
if (videoBitRate < _videoConfiguration.videoMaxBitRate) {
videoBitRate = videoBitRate + 50 * 1000;
[_videoEncoder setVideoBitRate:videoBitRate];
}
}else{
if(videoBitRate > _videoConfiguration.videoMinBitRate){
} else {
if (videoBitRate > _videoConfiguration.videoMinBitRate) {
videoBitRate = videoBitRate - 100 * 1000;
[_videoEncoder setVideoBitRate:videoBitRate];
}
@@ -162,8 +165,8 @@
}
#pragma mark -- Getter Setter
- (void)setRunning:(BOOL)running{
if(_running == running) return;
- (void)setRunning:(BOOL)running {
if (_running == running) return;
[self willChangeValueForKey:@"running"];
_running = running;
[self didChangeValueForKey:@"running"];
@@ -171,32 +174,40 @@
self.audioCaptureSource.running = _running;
}
- (void)setPreView:(UIView *)preView{
- (void)setPreView:(UIView *)preView {
[self willChangeValueForKey:@"preView"];
[self.videoCaptureSource setPreView:preView];
[self didChangeValueForKey:@"preView"];
}
- (UIView*)preView{
- (UIView *)preView {
return self.videoCaptureSource.preView;
}
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition{
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition {
[self willChangeValueForKey:@"captureDevicePosition"];
[self.videoCaptureSource setCaptureDevicePosition:captureDevicePosition];
[self didChangeValueForKey:@"captureDevicePosition"];
}
- (AVCaptureDevicePosition)captureDevicePosition{
- (AVCaptureDevicePosition)captureDevicePosition {
return self.videoCaptureSource.captureDevicePosition;
}
- (void)setBeautyFace:(BOOL)beautyFace{
- (void)setBeautyFace:(BOOL)beautyFace {
[self willChangeValueForKey:@"beautyFace"];
[self.videoCaptureSource setBeautyFace:beautyFace];
[self didChangeValueForKey:@"beautyFace"];
}
- (BOOL)beautyFace{
- (BOOL)beautyFace {
return self.videoCaptureSource.beautyFace;
}
- (void)setBeautyLevel:(CGFloat)beautyLevel {
[self willChangeValueForKey:@"beautyLevel"];
[self.videoCaptureSource setBeautyLevel:beautyLevel];
[self didChangeValueForKey:@"beautyLevel"];
}
- (CGFloat)beautyLevel {
@@ -204,7 +215,9 @@
}
- (void)setBrightLevel:(CGFloat)brightLevel {
[self willChangeValueForKey:@"brightLevel"];
[self.videoCaptureSource setBrightLevel:brightLevel];
[self didChangeValueForKey:@"brightLevel"];
}
- (CGFloat)brightLevel {
@@ -212,7 +225,9 @@
}
- (void)setZoomScale:(CGFloat)zoomScale {
[self willChangeValueForKey:@"zoomScale"];
[self.videoCaptureSource setZoomScale:zoomScale];
[self didChangeValueForKey:@"zoomScale"];
}
- (CGFloat)zoomScale {
@@ -220,7 +235,9 @@
}
- (void)setTorch:(BOOL)torch {
[self willChangeValueForKey:@"torch"];
[self.videoCaptureSource setTorch:torch];
[self didChangeValueForKey:@"torch"];
}
- (BOOL)torch {
@@ -228,72 +245,76 @@
}
- (void)setMirror:(BOOL)mirror {
[self willChangeValueForKey:@"mirror"];
[self.videoCaptureSource setMirror:mirror];
[self didChangeValueForKey:@"mirror"];
}
- (BOOL)mirror {
return self.videoCaptureSource.mirror;
}
- (void)setMuted:(BOOL)muted{
- (void)setMuted:(BOOL)muted {
[self willChangeValueForKey:@"muted"];
[self.audioCaptureSource setMuted:muted];
[self didChangeValueForKey:@"muted"];
}
- (BOOL)muted{
- (BOOL)muted {
return self.audioCaptureSource.muted;
}
- (LFAudioCapture*)audioCaptureSource{
if(!_audioCaptureSource){
- (LFAudioCapture *)audioCaptureSource {
if (!_audioCaptureSource) {
_audioCaptureSource = [[LFAudioCapture alloc] initWithAudioConfiguration:_audioConfiguration];
_audioCaptureSource.delegate = self;
}
return _audioCaptureSource;
}
- (LFVideoCapture*)videoCaptureSource{
if(!_videoCaptureSource){
- (LFVideoCapture *)videoCaptureSource {
if (!_videoCaptureSource) {
_videoCaptureSource = [[LFVideoCapture alloc] initWithVideoConfiguration:_videoConfiguration];
_videoCaptureSource.delegate = self;
}
return _videoCaptureSource;
}
- (id<LFAudioEncoding>)audioEncoder{
if(!_audioEncoder){
- (id<LFAudioEncoding>)audioEncoder {
if (!_audioEncoder) {
_audioEncoder = [[LFHardwareAudioEncoder alloc] initWithAudioStreamConfiguration:_audioConfiguration];
[_audioEncoder setDelegate:self];
}
return _audioEncoder;
}
- (id<LFVideoEncoding>)videoEncoder{
if(!_videoEncoder){
- (id<LFVideoEncoding>)videoEncoder {
if (!_videoEncoder) {
_videoEncoder = [[LFHardwareVideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
[_videoEncoder setDelegate:self];
}
return _videoEncoder;
}
- (id<LFStreamSocket>)socket{
if(!_socket){
_socket = [[LFStreamRtmpSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
- (id<LFStreamSocket>)socket {
if (!_socket) {
_socket = [[LFStreamRTMPSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
[_socket setDelegate:self];
}
return _socket;
}
- (LFLiveStreamInfo*)streamInfo{
if(!_streamInfo){
- (LFLiveStreamInfo *)streamInfo {
if (!_streamInfo) {
_streamInfo = [[LFLiveStreamInfo alloc] init];
}
return _streamInfo;
}
- (uint64_t)currentTimestamp{
- (uint64_t)currentTimestamp {
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
uint64_t currentts = 0;
if(_isFirstFrame == true) {
if (_isFirstFrame == true) {
_timestamp = NOW;
_isFirstFrame = false;
currentts = 0;
+5 -5
View File
@@ -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 audioBuffer:(AudioBufferList)inBufferList;
@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;
+98 -97
View File
@@ -14,9 +14,9 @@ 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;
@@ -25,54 +25,54 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
@implementation LFAudioCapture
#pragma mark -- LiftCycle
- (instancetype)initWithAudioConfiguration:(LFLiveAudioConfiguration *)configuration{
if(self = [super init]){
- (instancetype)initWithAudioConfiguration:(LFLiveAudioConfiguration *)configuration {
if (self = [super init]) {
_configuration = configuration;
self.isRunning = NO;
self.taskQueue = dispatch_queue_create("com.youku.Laifeng.audioCapture.Queue", NULL);
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleRouteChange:)
name: AVAudioSessionRouteChangeNotification
object: session];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleInterruption:)
name: AVAudioSessionInterruptionNotification
object: session];
[session setActive:YES error:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleRouteChange:)
name:AVAudioSessionRouteChangeNotification
object:session];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleInterruption:)
name:AVAudioSessionInterruptionNotification
object:session];
NSError *error = nil;
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[session setMode:AVAudioSessionModeVideoRecording error:&error];
[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
if (![session setActive:YES error:&error]) {
[self handleAudioComponentCreationFailure];
}
AudioComponentDescription acd;
acd.componentType = kAudioUnitType_Output;
acd.componentSubType = kAudioUnitSubType_RemoteIO;
acd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
acd.componentManufacturer = kAudioUnitManufacturer_Apple;
acd.componentFlags = 0;
acd.componentFlagsMask = 0;
self.component = AudioComponentFindNext(NULL, &acd);
OSStatus status = noErr;
status = AudioComponentInstanceNew(self.component, &_componetInstance);
if (noErr != status) {
[self handleAudioComponentCreationFailure];
}
UInt32 flagOne = 1;
AudioUnitSetProperty(self.componetInstance, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flagOne, sizeof(flagOne));
AudioStreamBasicDescription desc = {0};
desc.mSampleRate = _configuration.audioSampleRate;
desc.mFormatID = kAudioFormatLinearPCM;
@@ -82,32 +82,32 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
desc.mBitsPerChannel = 16;
desc.mBytesPerFrame = desc.mBitsPerChannel / 8 * desc.mChannelsPerFrame;
desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket;
AURenderCallbackStruct cb;
cb.inputProcRefCon = (__bridge void *)(self);
cb.inputProc = handleInputBuffer;
AudioUnitSetProperty(self.componetInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &desc, sizeof(desc));
AudioUnitSetProperty(self.componetInstance, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1, &cb, sizeof(cb));
status = AudioUnitInitialize(self.componetInstance);
if (noErr != status) {
[self handleAudioComponentCreationFailure];
}
[session setPreferredSampleRate:_configuration.audioSampleRate 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 +117,16 @@ 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");
AudioOutputUnitStart(self.componetInstance);
});
}else{
} else {
self.isRunning = NO;
}
}
@@ -141,44 +141,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);
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];
@@ -190,27 +190,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
@@ -222,42 +223,42 @@ 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:)]){
if (!status) {
if (source.delegate && [source.delegate respondsToSelector:@selector(captureOutput:audioBuffer:)]) {
[source.delegate captureOutput:source audioBuffer:buffers];
}
}
+5 -5
View File
@@ -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 CVImageBufferRef)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;
@@ -64,8 +64,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;
+102 -64
View File
@@ -30,40 +30,41 @@
@synthesize zoomScale = _zoomScale;
#pragma mark -- LifeCycle
- (instancetype)initWithVideoConfiguration:(LFLiveVideoConfiguration *)configuration{
if(self = [super init]){
- (instancetype)initWithVideoConfiguration:(LFLiveVideoConfiguration *)configuration {
if (self = [super init]) {
_configuration = configuration;
if([self pixelBufferImageSize].width < configuration.videoSize.width || [self pixelBufferImageSize].height < configuration.videoSize.height){
@throw [NSException exceptionWithName:@"当前videoSize大小出错" reason:@"LFLiveVideoConfiguration videoSize error" userInfo:nil];
return nil;
}
_videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:_configuration.avSessionPreset cameraPosition:AVCaptureDevicePositionFront];
UIInterfaceOrientation statusBar = [[UIApplication sharedApplication] statusBarOrientation];
if(configuration.landscape){
if(statusBar != UIInterfaceOrientationLandscapeLeft && statusBar != UIInterfaceOrientationLandscapeRight){
NSLog(@"当前设置方向出错");
NSLog(@"当前设置方向出错");
NSLog(@"当前设置方向出错");
if (configuration.landscape) {
if (statusBar != UIInterfaceOrientationLandscapeLeft && statusBar != UIInterfaceOrientationLandscapeRight) {
@throw [NSException exceptionWithName:@"当前设置方向出错" reason:@"LFLiveVideoConfiguration landscape error" userInfo:nil];
_videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeLeft;
}else{
} else {
_videoCamera.outputImageOrientation = statusBar;
}
}else{
if(statusBar != UIInterfaceOrientationPortrait && statusBar != UIInterfaceOrientationPortraitUpsideDown){
NSLog(@"当前设置方向出错");
NSLog(@"当前设置方向出错");
NSLog(@"当前设置方向出错");
} else {
if (statusBar != UIInterfaceOrientationPortrait && statusBar != UIInterfaceOrientationPortraitUpsideDown) {
@throw [NSException exceptionWithName:@"当前设置方向出错" reason:@"LFLiveVideoConfiguration landscape error" userInfo:nil];
_videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
}else{
} else {
_videoCamera.outputImageOrientation = statusBar;
}
}
_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];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarChanged:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
@@ -75,36 +76,36 @@
return self;
}
- (void)dealloc{
- (void)dealloc {
[UIApplication sharedApplication].idleTimerDisabled = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_videoCamera stopCameraCapture];
}
#pragma mark -- Setter Getter
- (void)setRunning:(BOOL)running{
if(_running == running) return;
- (void)setRunning:(BOOL)running {
if (_running == running) return;
_running = running;
if(!_running){
if (!_running) {
[UIApplication sharedApplication].idleTimerDisabled = NO;
[_videoCamera stopCameraCapture];
}else{
} else {
[UIApplication sharedApplication].idleTimerDisabled = YES;
[_videoCamera startCameraCapture];
}
}
- (void)setPreView:(UIView *)preView{
if(_gpuImageView.superview) [_gpuImageView removeFromSuperview];
- (void)setPreView:(UIView *)preView {
if (_gpuImageView.superview) [_gpuImageView removeFromSuperview];
[preView insertSubview:_gpuImageView atIndex:0];
}
- (UIView*)preView{
- (UIView *)preView {
return _gpuImageView.superview;
}
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition{
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition {
[_videoCamera rotateCamera];
_videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
if (captureDevicePosition == AVCaptureDevicePositionFront) {
@@ -114,30 +115,30 @@
}
}
- (AVCaptureDevicePosition)captureDevicePosition{
- (AVCaptureDevicePosition)captureDevicePosition {
return [_videoCamera cameraPosition];
}
- (void)setVideoFrameRate:(NSInteger)videoFrameRate{
if(videoFrameRate <= 0) return;
if(videoFrameRate == _videoCamera.frameRate) return;
- (void)setVideoFrameRate:(NSInteger)videoFrameRate {
if (videoFrameRate <= 0) return;
if (videoFrameRate == _videoCamera.frameRate) return;
_videoCamera.frameRate = (uint32_t)videoFrameRate;
}
- (NSInteger)videoFrameRate{
- (NSInteger)videoFrameRate {
return _videoCamera.frameRate;
}
- (void)setTorch:(BOOL)torch {
BOOL ret;
if(!_videoCamera.captureSession) return;
AVCaptureSession* session = (AVCaptureSession*)_videoCamera.captureSession;
if (!_videoCamera.captureSession) return;
AVCaptureSession *session = (AVCaptureSession *)_videoCamera.captureSession;
[session beginConfiguration];
if (_videoCamera.inputCamera) {
if (_videoCamera.inputCamera.torchAvailable) {
NSError* err = nil;
NSError *err = nil;
if ([_videoCamera.inputCamera lockForConfiguration:&err]) {
[_videoCamera.inputCamera setTorchMode:( torch ? AVCaptureTorchModeOn : AVCaptureTorchModeOff ) ];
[_videoCamera.inputCamera setTorchMode:(torch ? AVCaptureTorchModeOn : AVCaptureTorchModeOff) ];
[_videoCamera.inputCamera unlockForConfiguration];
ret = (_videoCamera.inputCamera.torchMode == AVCaptureTorchModeOn);
} else {
@@ -151,37 +152,45 @@
[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;
AVCaptureDevice *device = (AVCaptureDevice *)self.videoCamera.inputCamera;
if ([device lockForConfiguration:nil]) {
device.videoZoomFactor = zoomScale;
[device unlockForConfiguration];
@@ -194,14 +203,13 @@
return _zoomScale;
}
- (void)setBeautyFace:(BOOL)beautyFace{
if(_beautyFace == beautyFace) return;
- (void)setBeautyFace:(BOOL)beautyFace {
_beautyFace = beautyFace;
[_filter removeAllTargets];
[_cropfilter removeAllTargets];
[_videoCamera removeAllTargets];
if (_beautyFace) {
_output = [[LFGPUImageEmptyFilter alloc] init];
_filter = [[LFGPUImageBeautyFilter alloc] init];
@@ -218,17 +226,17 @@
[_self processVideo:output];
}];
}
CGSize imageSize = [self pixelBufferImageSize];
CGFloat cropLeft = (imageSize.width - self.configuration.videoSize.width)/2.0/imageSize.width;
CGFloat cropTop = (imageSize.height - self.configuration.videoSize.height)/2.0/imageSize.height;
if (_configuration.isClipVideo) {
if (_configuration.landscape){
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(0.125, 0, 0.75, 1)];
} else {
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(0, 0.125, 1, 0.75)];
}
if(cropLeft == 0 && cropTop == 0){
[_videoCamera addTarget:_filter];
}else{
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(cropLeft, cropTop, 1 - cropLeft*2, 1 - cropTop*2)];
[_videoCamera addTarget:_cropfilter];
[_cropfilter addTarget:_filter];
} else {
[_videoCamera addTarget:_filter];
}
if (_beautyFace) {
@@ -237,7 +245,7 @@
} else {
[_filter addTarget:_gpuImageView];
}
if (_videoCamera.cameraPosition == AVCaptureDevicePositionFront) {
[_gpuImageView setInputRotation:kGPUImageFlipHorizonal atIndex:0];
} else {
@@ -246,22 +254,22 @@
}
#pragma mark -- Custom Method
- (void)processVideo:(GPUImageOutput *)output{
- (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:)]){
if (pixelBuffer && _self.delegate && [_self.delegate respondsToSelector:@selector(captureOutput:pixelBuffer:)]) {
[_self.delegate captureOutput:_self pixelBuffer:pixelBuffer];
}
}
}
#pragma mark Notification
- (void)willEnterBackground:(NSNotification*)notification{
- (void)willEnterBackground:(NSNotification *)notification {
[UIApplication sharedApplication].idleTimerDisabled = NO;
[_videoCamera pauseCameraCapture];
runSynchronouslyOnVideoProcessingQueue(^{
@@ -269,27 +277,57 @@
});
}
- (void)willEnterForeground:(NSNotification*)notification{
- (void)willEnterForeground:(NSNotification *)notification {
[_videoCamera resumeCameraCapture];
[UIApplication sharedApplication].idleTimerDisabled = YES;
}
- (void)statusBarChanged:(NSNotification*)notification{
NSLog(@"UIApplicationWillChangeStatusBarOrientationNotification. UserInfo: %@", notification.userInfo);
- (void)statusBarChanged:(NSNotification *)notification {
NSLog(@"UIApplicationWillChangeStatusBarOrientationNotification. UserInfo: %@", notification.userInfo);
UIInterfaceOrientation statusBar = [[UIApplication sharedApplication] statusBarOrientation];
if(_configuration.landscape){
if(statusBar == UIInterfaceOrientationLandscapeLeft){
if (_configuration.landscape) {
if (statusBar == UIInterfaceOrientationLandscapeLeft) {
self.videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeRight;
}else if(statusBar == UIInterfaceOrientationLandscapeRight){
} else if (statusBar == UIInterfaceOrientationLandscapeRight) {
self.videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeLeft;
}
}else{
if(statusBar == UIInterfaceOrientationPortrait){
} else {
if (statusBar == UIInterfaceOrientationPortrait) {
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortraitUpsideDown;
}else if(statusBar == UIInterfaceOrientationPortraitUpsideDown){
} else if (statusBar == UIInterfaceOrientationPortraitUpsideDown) {
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
}
}
}
#pragma mark --
- (CGSize)pixelBufferImageSize{
CGSize videoSize = CGSizeZero;
switch (self.configuration.sessionPreset) {
case LFCaptureSessionPreset360x640:
{
videoSize = CGSizeMake(480, 640);
}
break;
case LFCaptureSessionPreset540x960:
{
videoSize = CGSizeMake(540, 960);
}
break;
case LFCaptureSessionPreset720x1280:
{
videoSize = CGSizeMake(720, 1280);
}
break;
default:
break;
}
if(self.configuration.landscape){
return CGSizeMake(videoSize.height, videoSize.width);
}
return videoSize;
}
@end
+36
View File
@@ -0,0 +1,36 @@
//
// 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 "VideoEncoder.h"
#import "MP4Atom.h"
typedef int (^encoder_handler_t)(NSArray *data, CMTimeValue ptsValue);
typedef int (^param_handler_t)(NSData *params);
@interface AVEncoder : NSObject
@property (atomic) NSUInteger bitrate;
+ (AVEncoder *)encoderForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate;
- (void)encodeWithBlock:(encoder_handler_t)block onParams:(param_handler_t)paramsHandler;
- (void)encodeFrame:(CMSampleBufferRef)sampleBuffer;
- (void)encodePixelBuffer:(CVPixelBufferRef)pixelBuffer pts:(CMTime)pts;
- (NSData *)getConfigData;
- (void)shutdown;
@property (readonly, atomic) int bitspersecond;
@end
+439
View File
@@ -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 "AVEncoder.h"
#import "NALUnit.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 AVEncoder ()
{
// initial writer, used to obtain SPS/PPS from header
VideoEncoder *_headerWriter;
// main encoder/writer
VideoEncoder *_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 AVEncoder
@synthesize bitspersecond = _bitspersecond;
+ (AVEncoder *)encoderForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate {
AVEncoder *enc = [AVEncoder 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 = [VideoEncoder encoderForPath:path Height:height andWidth:width bitrate:self.bitrate];
_times = [NSMutableArray arrayWithCapacity:10];
// swap between 3 filenames
_currentFile = 1;
_writer = [VideoEncoder encoderForPath:[self makeFilename] Height:height andWidth:width bitrate: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);
MP4Atom *movie = [MP4Atom atomAt:0 size:s.st_size type:(OSType)('file') inFile:file];
MP4Atom *moov = [movie childOfType:(OSType)('moov') startAt:0];
MP4Atom *trak = nil;
if (moov != nil) {
for (;; ) {
trak = [moov nextChild];
if (trak == nil) {
break;
}
if (trak.type == (OSType)('trak')) {
MP4Atom *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;
}
}
}
}
MP4Atom *stsd = nil;
if (trak != nil) {
MP4Atom *media = [trak childOfType:(OSType)('mdia') startAt:0];
if (media != nil) {
MP4Atom *minf = [media childOfType:(OSType)('minf') startAt:0];
if (minf != nil) {
MP4Atom *stbl = [minf childOfType:(OSType)('stbl') startAt:0];
if (stbl != nil) {
stsd = [stbl childOfType:(OSType)('stsd') startAt:0];
}
}
}
}
if (stsd != nil) {
MP4Atom *avc1 = [stsd childOfType:(OSType)('avc1') startAt:8];
if (avc1 != nil) {
MP4Atom *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: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;
VideoEncoder *oldVideo = _writer;
// construct a new writer to the next filename
if (++_currentFile > MAX_FILENAME_INDEX) {
_currentFile = 1;
}
//NSLog(@"Swap to file %d", _currentFile);
_writer = [VideoEncoder encoderForPath:[self makeFilename] Height:_height andWidth:_width bitrate: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 = 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) {
NALUnit nal(pNal, [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 = 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
+15
View File
@@ -0,0 +1,15 @@
# GDCL Source Code License
Last updated: 20th February 2013
**License Agreement for Source Code provided by GDCL**
This software is supplied to you by Geraint Davies Consulting Ltd ('GDCL') in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this software.
In consideration of your agreement to abide by the following terms, and subject to these terms, GDCL grants you a personal, non-exclusive license, to use, reproduce, modify and redistribute the software, with or without modifications, in source and/or binary forms; provided that if you redistribute the software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the software, and that in all cases attribution of GDCL as the original author of the source code shall be included in all such resulting software products or distributions.
Neither the name, trademarks, service marks or logos of Geraint Davies or GDCL may be used to endorse or promote products derived from the software without specific prior written permission from GDCL. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by GDCL herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the software may be incorporated.
The software is provided by GDCL on an "AS IS" basis. GDCL MAKE NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL GDCL BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF GDCL HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+30
View File
@@ -0,0 +1,30 @@
//
// MP4Atom.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 MP4Atom : NSObject
{
NSFileHandle *_file;
int64_t _offset;
int64_t _length;
OSType _type;
int64_t _nextChild;
}
@property OSType type;
@property int64_t length;
+ (MP4Atom *)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;
- (MP4Atom *)nextChild;
- (MP4Atom *)childOfType:(OSType)fourcc startAt:(int64_t)offset;
@end
+90
View File
@@ -0,0 +1,90 @@
//
// MP4Atom.m
// Encoder Demo
//
// Created by Geraint Davies on 15/01/2013.
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
//
#import "MP4Atom.h"
static unsigned int to_host(unsigned char *p){
return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
}
@implementation MP4Atom
@synthesize type = _type;
@synthesize length = _length;
+ (MP4Atom *)atomAt:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle {
MP4Atom *atom = [MP4Atom 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;
}
- (MP4Atom *)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 [MP4Atom atomAt:offset+_offset size:len type:fourcc inFile:_file];
}
return nil;
}
- (MP4Atom *)childOfType:(OSType)fourcc startAt:(int64_t)offset {
[self setChildOffset:offset];
MP4Atom *child = nil;
do {
child = [self nextChild];
} while ((child != nil) && (child.type != fourcc));
return child;
}
@end
+424
View File
@@ -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 "NALUnit.h"
// --- core NAL Unit implementation ------------------------------
NALUnit::NALUnit()
: m_pStart(NULL),
m_cBytes(0){
}
bool
NALUnit::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
NALUnit::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
NALUnit::ResetBitstream(){
m_idx = 0;
m_nBits = 0;
m_cZeros = 0;
}
void
NALUnit::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
NALUnit::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
NALUnit::GetBit(){
if (m_nBits == 0) {
m_byte = GetBYTE();
m_nBits = 8;
}
m_nBits--;
return (m_byte >> m_nBits) & 0x1;
}
unsigned long
NALUnit::GetWord(int nBits){
unsigned long u = 0;
while (nBits > 0) {
u <<= 1;
u |= GetBit();
nBits--;
}
return u;
}
unsigned long
NALUnit::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
NALUnit::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 ---------------
SeqParamSet::SeqParamSet()
: m_cx(0),
m_cy(0),
m_FrameBits(0){
// SetRect(&m_rcFrame, 0, 0, 0, 0);
}
void
ScalingList(int size, NALUnit *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) ? lastScale : nextScale;
lastScale = scaling_list_j;
}
}
bool
SeqParamSet::Parse(NALUnit *pnalu){
if (pnalu->Type() != NALUnit::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 = pnalu->GetWord(8);
m_Compatibility = (BYTE)pnalu->GetWord(8);
m_Level = 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 = 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 = 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 = pnalu->GetUE();
m_FrameBits = log2_frame_minus4 + 4;
int POCtype = 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 = 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 = pnalu->GetUE();
int mbs_height = 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
SliceHeader::Parse(NALUnit *pnalu){
switch (pnalu->Type()) {
case NALUnit::NAL_IDR_Slice:
case NALUnit::NAL_Slice:
case NALUnit::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 = pnalu->GetWord(m_nBitsFrame);
return true;
}
// --- SEI ----------------------
SEIMessage::SEIMessage(NALUnit *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());
}
avcCHeader::avcCHeader(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) {
NALUnit 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;
NALUnit n(header, cThis);
m_pps = n;
}
}
+242
View File
@@ -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 NALUnit
{
public:
NALUnit();
NALUnit(const BYTE* pStart, int len){
m_pStart = m_pStartCodeStart = pStart;
m_cBytes = len;
ResetBitstream();
}
virtual ~NALUnit() {
}
// assignment copies a pointer into a fixed buffer managed elsewhere. We do not copy the data
NALUnit(const NALUnit &r){
m_pStart = r.m_pStart;
m_cBytes = r.m_cBytes;
ResetBitstream();
}
const NALUnit& operator = (const NALUnit &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 SeqParamSet
{
public:
SeqParamSet();
bool Parse(NALUnit *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;
}
NALUnit *NALU() {
return &m_nalu;
}
private:
NALUnit 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 SliceHeader
{
public:
SliceHeader(int nBitsFrame)
: m_framenum(0),
m_nBitsFrame(nBitsFrame){
}
bool Parse(NALUnit *pnalu);
int FrameNum(){
return m_framenum;
}
private:
int m_framenum;
int m_nBitsFrame;
};
// SEI message structure
class SEIMessage
{
public:
SEIMessage(NALUnit* pnalu);
int Type() {
return m_type;
}
int Length() {
return m_length;
}
const BYTE *Payload() {
return m_pnalu->Start() + m_idxPayload;
}
private:
NALUnit *m_pnalu;
int m_type;
int m_length;
int m_idxPayload;
};
// avcC structure from MP4
class avcCHeader
{
public:
avcCHeader(const BYTE* header, int cBytes);
NALUnit *sps() {
return &m_sps;
}
NALUnit *pps() {
return &m_pps;
}
private:
NALUnit m_sps;
NALUnit m_pps;
};
+27
View File
@@ -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 VideoEncoder : NSObject
@property NSString *path;
@property (nonatomic, readonly) NSUInteger bitrate;
+ (VideoEncoder *)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
View File
@@ -0,0 +1,76 @@
//
// VideoEncoder.m
// Encoder Demo
//
// Created by Geraint Davies on 14/01/2013.
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
//
#import "VideoEncoder.h"
@implementation VideoEncoder
{
AVAssetWriter *_writer;
AVAssetWriterInput *_writerInput;
NSString *_path;
}
@synthesize path = _path;
+ (VideoEncoder *)encoderForPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate {
VideoEncoder *enc = [VideoEncoder 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
+3 -3
View File
@@ -15,7 +15,7 @@
/// 编码器编码后回调
@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
/// 编码器抽象的接口
@@ -24,8 +24,8 @@
- (void)encodeAudioData:(AudioBufferList)inBufferList 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
+16
View File
@@ -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
+270
View File
@@ -0,0 +1,270 @@
//
// 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 "NALUnit.h"
#import "AVEncoder.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) AVEncoder *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 = [AVEncoder encoderForHeight:_configuration.videoSize.height andWidth:_configuration.videoSize.width bitrate:_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;
}
avcCHeader avcC((const BYTE*)[config bytes], [config length]);
SeqParamSet 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 {
char *path = [self GetFilePathByfileName:"IOSCamDemo.h264"];
NSLog(@"%s", path);
self->fp = fopen(path, "wb");
}
- (char *)GetFilePathByfileName:(char *)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;
}
@end
+43 -44
View File
@@ -19,39 +19,40 @@
@implementation LFHardwareAudioEncoder
- (instancetype)initWithAudioStreamConfiguration:(LFLiveAudioConfiguration *)configuration{
if(self = [super init]){
- (instancetype)initWithAudioStreamConfiguration:(LFLiveAudioConfiguration *)configuration {
if (self = [super init]) {
NSLog(@"USE LFHardwareAudioEncoder");
_configuration = configuration;
}
return self;
}
- (void)dealloc{
if(aacBuf) free(aacBuf);
- (void)dealloc {
if (aacBuf) free(aacBuf);
}
#pragma mark -- LFAudioEncoder
- (void)setDelegate:(id<LFAudioEncodingDelegate>)delegate{
- (void)setDelegate:(id<LFAudioEncodingDelegate>)delegate {
_aacDeleage = delegate;
}
- (void)encodeAudioData:(AudioBufferList)inBufferList timeStamp:(uint64_t)timeStamp{
if (![self createAudioConvert]){
- (void)encodeAudioData:(AudioBufferList)inBufferList timeStamp:(uint64_t)timeStamp {
if (![self createAudioConvert]) {
return;
}
if(!aacBuf){
if (!aacBuf) {
aacBuf = malloc(inBufferList.mBuffers[0].mDataByteSize);
}
// 初始化一个输出缓冲列表
AudioBufferList outBufferList;
outBufferList.mNumberBuffers = 1;
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){
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];
@@ -61,22 +62,22 @@
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:)]){
audioFrame.audioInfo = [NSData dataWithBytes:exeData length:2];
if (self.aacDeleage && [self.aacDeleage respondsToSelector:@selector(audioEncoder:audioFrame:)]) {
[self.aacDeleage audioEncoder:self audioFrame:audioFrame];
}
}
- (void)stopEncoder{
- (void)stopEncoder {
}
#pragma mark -- CustomMethod
-(BOOL)createAudioConvert{ //根据输入样本初始化一个编码转换器
if (m_converter != nil){
- (BOOL)createAudioConvert { //根据输入样本初始化一个编码转换器
if (m_converter != nil) {
return TRUE;
}
AudioStreamBasicDescription inputFormat = {0};
inputFormat.mSampleRate = _configuration.audioSampleRate;
inputFormat.mFormatID = kAudioFormatLinearPCM;
@@ -86,14 +87,14 @@
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.mSampleRate = inputFormat.mSampleRate; // 采样率保持一致
outputFormat.mFormatID = kAudioFormatMPEG4AAC; // AAC编码 kAudioFormatMPEG4AAC kAudioFormatMPEG4AAC_HE_V2
outputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;;
outputFormat.mFramesPerPacket = 1024; // AAC一帧是1024个字节
outputFormat.mFramesPerPacket = 1024; // AAC一帧是1024个字节
const OSType subtype = kAudioFormatMPEG4AAC;
AudioClassDescription requestedCodecs[2] = {
{
@@ -108,30 +109,28 @@
}
};
OSStatus result = AudioConverterNewSpecific(&inputFormat, &outputFormat, 2, requestedCodecs, &m_converter);
if(result != noErr) return NO;
if (result != noErr) return NO;
return YES;
}
-(AudioClassDescription*)getAudioClassDescriptionWithType:(UInt32)type fromManufacturer:(UInt32)manufacturer { // 获得相应的编码器
- (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)){
for (uint32_t i = 0; i < count; i++) {
if ((type == descs[i].mSubType) && (manufacturer == descs[i].mManufacturer)) {
memcpy(&audioDesc, &descs[i], sizeof(audioDesc));
break;
}
@@ -140,11 +139,11 @@
}
#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;
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;
ioData->mBuffers[0].mData = bufferList.mBuffers[0].mData;
ioData->mBuffers[0].mDataByteSize = bufferList.mBuffers[0].mDataByteSize;
return noErr;
}
@@ -157,7 +156,7 @@ OSStatus inputDataProc(AudioConverterRef inConverter, UInt32 *ioNumberDataPacket
* 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 {
- (NSData *)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength {
int adtsLength = 7;
char *packet = malloc(sizeof(char) * adtsLength);
// Variables Recycled by addADTStoPacket
@@ -167,8 +166,8 @@ OSStatus inputDataProc(AudioConverterRef inConverter, UInt32 *ioNumberDataPacket
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[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);
+83 -90
View File
@@ -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_ProfileLevel, kVTProfileLevel_H264_Main_AutoLevel);
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse);
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 ++;
- (void)encodeVideoData:(CVImageBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp {
if (_isBackGround) return;
frameCount++;
CMTime presentationTimeStamp = CMTimeMake(frameCount, 1000);
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,55 +198,52 @@ 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 {
char *path = [self GetFilePathByfileName:"IOSCamDemo_HW.h264"];
NSLog(@"%s", path);
self->fp = fopen(path, "wb");
}
- (char*)GetFilePathByfileName:(char*)filename
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
- (char *)GetFilePathByfileName:(char *)filename {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *strName = [NSString stringWithFormat:@"%s",filename];
NSString *strName = [NSString stringWithFormat:@"%s", filename];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:strName];
NSUInteger len = [writablePath length];
char *filepath = (char*)malloc(sizeof(char) * (len + 1));
char *filepath = (char *)malloc(sizeof(char) * (len + 1));
[writablePath getCString:filepath maxLength:len + 1 encoding:[NSString defaultCStringEncoding]];
return filepath;
}
+2 -2
View File
@@ -14,7 +14,7 @@
/// 编码器编码后回调
@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
/// 编码器抽象的接口
@@ -24,7 +24,7 @@
- (void)stopEncoder;
@optional
@property (nonatomic, assign) NSInteger videoBitRate;
- (nullable instancetype)initWithVideoStreamConfiguration:(nullable LFLiveVideoConfiguration*)configuration;
- (nullable instancetype)initWithVideoStreamConfiguration:(nullable LFLiveVideoConfiguration *)configuration;
- (void)setDelegate:(nullable id<LFVideoEncodingDelegate>)delegate;
@end
@@ -9,7 +9,7 @@
#import <Foundation/Foundation.h>
/// 音频码率
typedef NS_ENUM(NSUInteger, LFLiveAudioBitRate) {
typedef NS_ENUM (NSUInteger, LFLiveAudioBitRate) {
/// 32Kbps 音频码率
LFLiveAudioBitRate_32Kbps = 32000,
/// 64Kbps 音频码率
@@ -23,7 +23,7 @@ typedef NS_ENUM(NSUInteger, LFLiveAudioBitRate) {
};
/// 采样率 (默认44.1Hz iphoneg6以上48Hz)
typedef NS_ENUM(NSUInteger, LFLiveAudioSampleRate){
typedef NS_ENUM (NSUInteger, LFLiveAudioSampleRate){
/// 44.1Hz 采样率
LFLiveAudioSampleRate_44100Hz = 44100,
/// 48Hz 采样率
@@ -33,7 +33,7 @@ typedef NS_ENUM(NSUInteger, LFLiveAudioSampleRate){
};
/// Audio Live quality(音频质量)
typedef NS_ENUM(NSUInteger, LFLiveAudioQuality){
typedef NS_ENUM (NSUInteger, LFLiveAudioQuality){
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 32Kbps
LFLiveAudioQuality_Low = 0,
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 64Kbps
@@ -46,7 +46,7 @@ typedef NS_ENUM(NSUInteger, LFLiveAudioQuality){
LFLiveAudioQuality_Default = LFLiveAudioQuality_Medium
};
@interface LFLiveAudioConfiguration : NSObject<NSCoding,NSCopying>
@interface LFLiveAudioConfiguration : NSObject<NSCoding, NSCopying>
/// 默认音频配置
+ (instancetype)defaultConfiguration;
@@ -64,6 +64,6 @@ 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;
@end
@@ -12,119 +12,117 @@
@implementation LFLiveAudioConfiguration
#pragma mark -- LifyCycle
+ (instancetype)defaultConfiguration{
+ (instancetype)defaultConfiguration {
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration defaultConfigurationForQuality:LFLiveAudioQuality_Default];
return audioConfig;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveAudioQuality)audioQuality{
+ (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;
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]){
- (instancetype)init {
if (self = [super init]) {
_asc = malloc(2);
}
return self;
}
- (void)dealloc{
if(_asc) free(_asc);
- (void)dealloc {
if (_asc) free(_asc);
}
#pragma mark Setter
- (void)setAudioSampleRate:(LFLiveAudioSampleRate)audioSampleRate{
- (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{
- (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:(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;
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{
+ (NSString *)deviceName {
struct utsname systemInfo;
uname(&systemInfo);
return [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
}
@@ -139,14 +137,14 @@
//@"iPhone8,1" on iPhone 6S
//@"iPhone8,2" on iPhone 6S Plus
+(BOOL) isNewThaniPhone6{
+ (BOOL)isNewThaniPhone6 {
NSString *device = [self deviceName];
NSLog(@"device %@", device);
if (device == nil) {
return NO;
}
NSArray *array = [device componentsSeparatedByString:@","];
if (array.count <2) {
if (array.count < 2) {
return NO;
}
NSString *model = [array objectAtIndex:0];
@@ -159,7 +157,7 @@
return YES;
}
}
if ([model hasPrefix:@"iPad"]) {
NSString *str1 = [model substringFromIndex:[@"iPad" length]];
NSUInteger num = [str1 integerValue];
@@ -167,7 +165,7 @@
return YES;
}
}
return NO;
}
@@ -188,7 +186,7 @@
return self;
}
- (BOOL)isEqual:(id)other{
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
} else if (![super isEqual:other]) {
@@ -196,9 +194,9 @@
} else {
LFLiveAudioConfiguration *object = other;
return object.numberOfChannels == self.numberOfChannels &&
object.audioBitrate == self.audioBitrate &&
strcmp(object.asc, self.asc) == 0 &&
object.audioSampleRate == self.audioSampleRate;
object.audioBitrate == self.audioBitrate &&
strcmp(object.asc, self.asc) == 0 &&
object.audioSampleRate == self.audioSampleRate;
}
}
@@ -208,25 +206,25 @@
@(_audioSampleRate),
[NSString stringWithUTF8String:self.asc],
@(_audioBitrate)];
for (NSObject *value in values) {
hash ^= value.hash;
}
return hash;
}
- (id)copyWithZone:(nullable NSZone *)zone{
- (id)copyWithZone:(nullable NSZone *)zone {
LFLiveAudioConfiguration *other = [self.class defaultConfiguration];
return other;
}
- (NSString *)description{
- (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]];
[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;
}
@@ -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,7 +43,7 @@ typedef NS_ENUM(NSUInteger, LFLiveVideoQuality){
LFLiveVideoQuality_Default = LFLiveVideoQuality_Low2
};
@interface LFLiveVideoConfiguration : NSObject<NSCoding,NSCopying>
@interface LFLiveVideoConfiguration : NSObject<NSCoding, NSCopying>
/// 默认视频配置
+ (instancetype)defaultConfiguration;
@@ -88,9 +88,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
+177 -195
View File
@@ -12,221 +12,207 @@
@implementation LFLiveVideoConfiguration
#pragma mark -- LifeCycle
+ (instancetype)defaultConfiguration{
+ (instancetype)defaultConfiguration {
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Default];
return configuration;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality{
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality {
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:videoQuality landscape:NO];
return configuration;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality landscape:(BOOL)landscape{
+ (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;
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){
if (landscape) {
configuration.videoSize = CGSizeMake(size.height, size.width);
}else{
} else {
configuration.videoSize = CGSizeMake(size.width, size.height);
}
return configuration;
}
#pragma mark -- Setter Getter
- (NSString*)avSessionPreset{
- (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;
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;
- (void)setVideoMaxBitRate:(NSUInteger)videoMaxBitRate {
if (videoMaxBitRate <= _videoBitRate) return;
_videoMaxBitRate = videoMaxBitRate;
}
- (void)setVideoMinBitRate:(NSUInteger)videoMinBitRate{
if(videoMinBitRate >= _videoBitRate) return;
- (void)setVideoMinBitRate:(NSUInteger)videoMinBitRate {
if (videoMinBitRate >= _videoBitRate) return;
_videoMinBitRate = videoMinBitRate;
}
- (void)setVideoMaxFrameRate:(NSUInteger)videoMaxFrameRate{
if(videoMaxFrameRate <= _videoFrameRate) return;
- (void)setVideoMaxFrameRate:(NSUInteger)videoMaxFrameRate {
if (videoMaxFrameRate <= _videoFrameRate) return;
_videoMaxFrameRate = videoMaxFrameRate;
}
- (void)setVideoMinFrameRate:(NSUInteger)videoMinFrameRate{
if(videoMinFrameRate >= _videoFrameRate) return;
- (void)setVideoMinFrameRate:(NSUInteger)videoMinFrameRate {
if (videoMinFrameRate >= _videoFrameRate) return;
_videoMinFrameRate = videoMinFrameRate;
}
- (void)setLandscape:(BOOL)landscape{
_landscape = landscape;
CGSize size = self.videoSize;
if(landscape){
self.videoSize = CGSizeMake(size.height, size.width);
}else{
self.videoSize = CGSizeMake(size.width, size.height);
}
}
#pragma mark -- Custom Method
- (LFLiveVideoSessionPreset)supportSessionPreset:(LFLiveVideoSessionPreset)sessionPreset{
- (LFLiveVideoSessionPreset)supportSessionPreset:(LFLiveVideoSessionPreset)sessionPreset {
NSString *avSessionPreset = [self avSessionPreset];
AVCaptureSession *session = [[AVCaptureSession alloc] init];
if(![session canSetSessionPreset:avSessionPreset]){
if(sessionPreset == LFCaptureSessionPreset720x1280){
if (![session canSetSessionPreset:avSessionPreset]) {
if (sessionPreset == LFCaptureSessionPreset720x1280) {
sessionPreset = LFCaptureSessionPreset540x960;
if(![session canSetSessionPreset:avSessionPreset]){
if (![session canSetSessionPreset:avSessionPreset]) {
sessionPreset = LFCaptureSessionPreset360x640;
}
}else if(sessionPreset == LFCaptureSessionPreset540x960){
} 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"];
@@ -258,60 +244,56 @@
@(self.videoBitRate),
@(self.videoMaxBitRate),
@(self.videoMinBitRate),
@(self.isClipVideo),
self.avSessionPreset,
@(self.sessionPreset),
@(self.landscape),];
@(self.landscape), ];
for (NSObject *value in values) {
hash ^= value.hash;
}
return hash;
}
- (BOOL)isEqual:(id)other
{
- (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.landscape == self.landscape;
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;
}
}
- (id)copyWithZone:(nullable NSZone *)zone{
- (id)copyWithZone:(nullable NSZone *)zone {
LFLiveVideoConfiguration *other = [self.class defaultConfiguration];
return other;
}
- (NSString *)description{
- (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:@" landscape:%zi",self.landscape];
[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:@" avSessionPreset:%@", self.avSessionPreset];
[desc appendFormat:@" sessionPreset:%zi", self.sessionPreset];
[desc appendFormat:@" landscape:%zi", self.landscape];
return desc;
}
+213 -220
View File
@@ -2,237 +2,233 @@
#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)
(
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));
}
);
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)
(
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));
}
);
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;
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageBeautyFragmentShaderString])) {
return nil;
}
_toneLevel = 0.5;
_beautyLevel = 0.5;
_brightLevel = 0.5;
@@ -241,27 +237,24 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
return self;
}
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex
{
- (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
{
- (void)setBeautyLevel:(CGFloat)beautyLevel {
_beautyLevel = beautyLevel;
[self setParams:_beautyLevel tone:_toneLevel];
}
- (void)setBrightLevel:(CGFloat)brightLevel
{
- (void)setBrightLevel:(CGFloat)brightLevel {
_brightLevel = brightLevel;
[self setFloat:0.6 * (-0.5 + brightLevel) forUniformName:@"brightness"];
}
}
- (void)setParams:(CGFloat)beauty tone:(CGFloat)tone {
GPUVector4 fBeautyParam;
+25 -26
View File
@@ -2,43 +2,42 @@
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const kLFGPUImageEmptyFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{
(
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);
}
);
(
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;
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageEmptyFragmentShaderString])) {
return nil;
}
return self;
}
+4
View File
@@ -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; ///< 上次的音频捕获个数
+7 -7
View File
@@ -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
@@ -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
///=============================================================================
+554
View File
@@ -0,0 +1,554 @@
//
// LFStreamRTMPSocket.m
// LFLiveKit
//
// Created by admin on 16/5/18.
// Copyright © 2016 live Interactive. All rights reserved.
//
#import "LFStreamRTMPSocket.h"
#import "rtmp.h"
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 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;
[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 = 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;
}
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
-519
View File
@@ -1,519 +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() {
YYDispatchQueuePool *pool = [[YYDispatchQueuePool alloc] initWithName:@"com.youku.laifeng.rtmpsendQueue" queueCount:1 qos:NSQualityOfServiceDefault];
dispatch_queue_t queue = [pool queue];
return queue;
}
#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.7.3");
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(), ^{
[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 clean];
[self RTMP264_Connect:(char*)[_stream.url cStringUsingEncoding:NSASCIIStringEncoding]];
}
- (void) stop{
dispatch_async(YYRtmpSendQueue(), ^{
[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;
}
}
- (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) == 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 = 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
+12 -12
View File
@@ -11,27 +11,27 @@
#import "LFStreamingBuffer.h"
#import "LFLiveDebug.h"
@protocol LFStreamSocket ;
@protocol LFStreamSocket;
@protocol LFStreamSocketDelegate <NSObject>
/** callback buffer current status (回调当前缓冲区情况,可实现相关切换帧率 码率等策略)*/
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status;
- (void)socketBufferStatus:(nullable id <LFStreamSocket>)socket status:(LFLiveBuffferState)status;
/** callback socket current status (回调当前网络情况) */
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status;
- (void)socketStatus:(nullable id <LFStreamSocket>)socket status:(LFLiveState)status;
/** callback socket errorcode */
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode;
- (void)socketDidError:(nullable id <LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode;
@optional
/** callback debugInfo */
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug*)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;
- (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;
- (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
+11 -8
View File
@@ -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,28 @@ 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 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;
+68 -64
View File
@@ -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,34 @@ 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;
}
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 +69,54 @@ 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;
- (void)removeExpireFrame {
if (self.list.count < self.maxCount) return;
NSArray *pFrames = [self expirePFrames];///< P到第一个I之间的p帧
if(pFrames && pFrames.count > 0){
self.lastDropFrames += [pFrames count];
if (pFrames && pFrames.count > 0) {
[self.list removeObjectsInArray:pFrames];
return;
}
NSArray *iFrames = [self expireIFrames];///<  I帧I帧可能对应多个nal
if(iFrames){
self.lastDropFrames += [iFrames count];
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 +124,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,85 +139,84 @@ 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];
}
+4 -4
View File
@@ -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;
@@ -7,191 +7,184 @@
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 */; };
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 */; };
81E848D8BD2C446C2DD4876A /* libPods-LFLiveKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6FD9F92833FE7856CDDD3CED /* libPods-LFLiveKitDemo.a */; };
B2D23E7F1D348F3D00B34CA8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B2D23E7E1D348F3D00B34CA8 /* main.m */; };
B2D23E821D348F3D00B34CA8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B2D23E811D348F3D00B34CA8 /* AppDelegate.m */; };
B2D23E851D348F3D00B34CA8 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B2D23E841D348F3D00B34CA8 /* ViewController.m */; };
B2D23E881D348F3D00B34CA8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2D23E861D348F3D00B34CA8 /* Main.storyboard */; };
B2D23E8D1D348F3D00B34CA8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2D23E8B1D348F3D00B34CA8 /* LaunchScreen.storyboard */; };
B2D23EAA1D348F7100B34CA8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B2D23E941D348F7100B34CA8 /* Assets.xcassets */; };
B2D23EAB1D348F7100B34CA8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2D23E961D348F7100B34CA8 /* LaunchScreen.storyboard */; };
B2D23EAC1D348F7100B34CA8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2D23E981D348F7100B34CA8 /* Main.storyboard */; };
B2D23EAD1D348F7100B34CA8 /* UIControl+YYAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = B2D23E9C1D348F7100B34CA8 /* UIControl+YYAdd.m */; };
B2D23EAE1D348F7100B34CA8 /* UIView+YYAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = B2D23E9E1D348F7100B34CA8 /* UIView+YYAdd.m */; };
B2D23EAF1D348F7100B34CA8 /* camra_beauty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B2D23EA01D348F7100B34CA8 /* camra_beauty@2x.png */; };
B2D23EB01D348F7100B34CA8 /* camra_beauty@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B2D23EA11D348F7100B34CA8 /* camra_beauty@3x.png */; };
B2D23EB11D348F7100B34CA8 /* camra_beauty_close@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B2D23EA21D348F7100B34CA8 /* camra_beauty_close@2x.png */; };
B2D23EB21D348F7100B34CA8 /* camra_beauty_close@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B2D23EA31D348F7100B34CA8 /* camra_beauty_close@3x.png */; };
B2D23EB31D348F7100B34CA8 /* camra_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B2D23EA41D348F7100B34CA8 /* camra_preview@2x.png */; };
B2D23EB41D348F7100B34CA8 /* camra_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B2D23EA51D348F7100B34CA8 /* camra_preview@3x.png */; };
B2D23EB51D348F7100B34CA8 /* close_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B2D23EA61D348F7100B34CA8 /* close_preview@2x.png */; };
B2D23EB61D348F7100B34CA8 /* close_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B2D23EA71D348F7100B34CA8 /* close_preview@3x.png */; };
B2D23EB71D348F7100B34CA8 /* LFLivePreview.m in Sources */ = {isa = PBXBuildFile; fileRef = B2D23EA91D348F7100B34CA8 /* LFLivePreview.m */; };
/* End PBXBuildFile 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>"; };
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>"; };
6FD9F92833FE7856CDDD3CED /* libPods-LFLiveKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LFLiveKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; };
8FAAEBE1A4F099C69588B394 /* 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>"; };
AFD491825C5DB2AD871189B5 /* 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>"; };
B2D23E7A1D348F3D00B34CA8 /* LFLiveKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LFLiveKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
B2D23E7E1D348F3D00B34CA8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
B2D23E801D348F3D00B34CA8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
B2D23E811D348F3D00B34CA8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
B2D23E831D348F3D00B34CA8 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
B2D23E841D348F3D00B34CA8 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
B2D23E871D348F3D00B34CA8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
B2D23E8C1D348F3D00B34CA8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
B2D23E8E1D348F3D00B34CA8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B2D23E941D348F7100B34CA8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B2D23E971D348F7100B34CA8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
B2D23E991D348F7100B34CA8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Main.storyboard; sourceTree = "<group>"; };
B2D23E9B1D348F7100B34CA8 /* UIControl+YYAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+YYAdd.h"; sourceTree = "<group>"; };
B2D23E9C1D348F7100B34CA8 /* UIControl+YYAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+YYAdd.m"; sourceTree = "<group>"; };
B2D23E9D1D348F7100B34CA8 /* UIView+YYAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+YYAdd.h"; sourceTree = "<group>"; };
B2D23E9E1D348F7100B34CA8 /* UIView+YYAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+YYAdd.m"; sourceTree = "<group>"; };
B2D23EA01D348F7100B34CA8 /* camra_beauty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@2x.png"; sourceTree = "<group>"; };
B2D23EA11D348F7100B34CA8 /* camra_beauty@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@3x.png"; sourceTree = "<group>"; };
B2D23EA21D348F7100B34CA8 /* camra_beauty_close@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@2x.png"; sourceTree = "<group>"; };
B2D23EA31D348F7100B34CA8 /* camra_beauty_close@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@3x.png"; sourceTree = "<group>"; };
B2D23EA41D348F7100B34CA8 /* camra_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@2x.png"; sourceTree = "<group>"; };
B2D23EA51D348F7100B34CA8 /* camra_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@3x.png"; sourceTree = "<group>"; };
B2D23EA61D348F7100B34CA8 /* close_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@2x.png"; sourceTree = "<group>"; };
B2D23EA71D348F7100B34CA8 /* close_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@3x.png"; sourceTree = "<group>"; };
B2D23EA81D348F7100B34CA8 /* LFLivePreview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLivePreview.h; sourceTree = "<group>"; };
B2D23EA91D348F7100B34CA8 /* LFLivePreview.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLivePreview.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
840762E91D07C7D0000FD0BF /* Frameworks */ = {
B2D23E771D348F3D00B34CA8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
57B42059E84CC681C5C99B68 /* libPods-LFLiveKitDemo.a in Frameworks */,
81E848D8BD2C446C2DD4876A /* libPods-LFLiveKitDemo.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
713DA9EBCA308093C74917F9 /* Frameworks */ = {
2A74A5AD65CD9450ED23C3E0 /* Pods */ = {
isa = PBXGroup;
children = (
5D506B5639A1D45519536773 /* libPods-LFLiveKitDemo.a */,
AFD491825C5DB2AD871189B5 /* Pods-LFLiveKitDemo.debug.xcconfig */,
8FAAEBE1A4F099C69588B394 /* Pods-LFLiveKitDemo.release.xcconfig */,
);
name = Frameworks;
name = Pods;
sourceTree = "<group>";
};
840762E31D07C7D0000FD0BF = {
B2D23E711D348F3D00B34CA8 = {
isa = PBXGroup;
children = (
840762EE1D07C7D0000FD0BF /* LFLiveKitDemo */,
840763081D07C7D0000FD0BF /* LFLiveKitDemoTests */,
840763131D07C7D0000FD0BF /* LFLiveKitDemoUITests */,
840762ED1D07C7D0000FD0BF /* Products */,
9BA1F10CECEAF692D0035AED /* Pods */,
713DA9EBCA308093C74917F9 /* Frameworks */,
B2D23E7C1D348F3D00B34CA8 /* LFLiveKitDemo */,
B2D23E7B1D348F3D00B34CA8 /* Products */,
2A74A5AD65CD9450ED23C3E0 /* Pods */,
E6AF2D0BFA2946745BB5F365 /* Frameworks */,
);
sourceTree = "<group>";
};
840762ED1D07C7D0000FD0BF /* Products */ = {
B2D23E7B1D348F3D00B34CA8 /* Products */ = {
isa = PBXGroup;
children = (
840762EC1D07C7D0000FD0BF /* LFLiveKitDemo.app */,
B2D23E7A1D348F3D00B34CA8 /* LFLiveKitDemo.app */,
);
name = Products;
sourceTree = "<group>";
};
840762EE1D07C7D0000FD0BF /* LFLiveKitDemo */ = {
B2D23E7C1D348F3D00B34CA8 /* LFLiveKitDemo */ = {
isa = PBXGroup;
children = (
840763221D07C894000FD0BF /* category */,
840763271D07C894000FD0BF /* LFLivePreview.h */,
840763281D07C894000FD0BF /* LFLivePreview.m */,
840762F21D07C7D0000FD0BF /* AppDelegate.h */,
840762F31D07C7D0000FD0BF /* AppDelegate.m */,
840762F51D07C7D0000FD0BF /* ViewController.h */,
840762F61D07C7D0000FD0BF /* ViewController.m */,
840762FB1D07C7D0000FD0BF /* Assets.xcassets */,
840762FD1D07C7D0000FD0BF /* LaunchScreen.storyboard */,
840763001D07C7D0000FD0BF /* Info.plist */,
840762EF1D07C7D0000FD0BF /* Supporting Files */,
B2D23E941D348F7100B34CA8 /* Assets.xcassets */,
B2D23E951D348F7100B34CA8 /* Base.lproj */,
B2D23E9A1D348F7100B34CA8 /* category */,
B2D23E9F1D348F7100B34CA8 /* images */,
B2D23EA81D348F7100B34CA8 /* LFLivePreview.h */,
B2D23EA91D348F7100B34CA8 /* LFLivePreview.m */,
B2D23E801D348F3D00B34CA8 /* AppDelegate.h */,
B2D23E811D348F3D00B34CA8 /* AppDelegate.m */,
B2D23E831D348F3D00B34CA8 /* ViewController.h */,
B2D23E841D348F3D00B34CA8 /* ViewController.m */,
B2D23E861D348F3D00B34CA8 /* Main.storyboard */,
B2D23E8B1D348F3D00B34CA8 /* LaunchScreen.storyboard */,
B2D23E8E1D348F3D00B34CA8 /* Info.plist */,
B2D23E7D1D348F3D00B34CA8 /* Supporting Files */,
);
path = LFLiveKitDemo;
sourceTree = "<group>";
};
840762EF1D07C7D0000FD0BF /* Supporting Files */ = {
B2D23E7D1D348F3D00B34CA8 /* Supporting Files */ = {
isa = PBXGroup;
children = (
8407632C1D07C899000FD0BF /* images */,
840762F01D07C7D0000FD0BF /* main.m */,
B2D23E7E1D348F3D00B34CA8 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
840763081D07C7D0000FD0BF /* LFLiveKitDemoTests */ = {
B2D23E951D348F7100B34CA8 /* Base.lproj */ = {
isa = PBXGroup;
children = (
840763091D07C7D0000FD0BF /* LFLiveKitDemoTests.m */,
8407630B1D07C7D0000FD0BF /* Info.plist */,
B2D23E961D348F7100B34CA8 /* LaunchScreen.storyboard */,
B2D23E981D348F7100B34CA8 /* Main.storyboard */,
);
path = LFLiveKitDemoTests;
path = Base.lproj;
sourceTree = "<group>";
};
840763131D07C7D0000FD0BF /* LFLiveKitDemoUITests */ = {
B2D23E9A1D348F7100B34CA8 /* 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 */,
B2D23E9B1D348F7100B34CA8 /* UIControl+YYAdd.h */,
B2D23E9C1D348F7100B34CA8 /* UIControl+YYAdd.m */,
B2D23E9D1D348F7100B34CA8 /* UIView+YYAdd.h */,
B2D23E9E1D348F7100B34CA8 /* UIView+YYAdd.m */,
);
path = category;
sourceTree = "<group>";
};
8407632C1D07C899000FD0BF /* images */ = {
B2D23E9F1D348F7100B34CA8 /* 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 */,
B2D23EA01D348F7100B34CA8 /* camra_beauty@2x.png */,
B2D23EA11D348F7100B34CA8 /* camra_beauty@3x.png */,
B2D23EA21D348F7100B34CA8 /* camra_beauty_close@2x.png */,
B2D23EA31D348F7100B34CA8 /* camra_beauty_close@3x.png */,
B2D23EA41D348F7100B34CA8 /* camra_preview@2x.png */,
B2D23EA51D348F7100B34CA8 /* camra_preview@3x.png */,
B2D23EA61D348F7100B34CA8 /* close_preview@2x.png */,
B2D23EA71D348F7100B34CA8 /* close_preview@3x.png */,
);
path = images;
sourceTree = "<group>";
};
9BA1F10CECEAF692D0035AED /* Pods */ = {
E6AF2D0BFA2946745BB5F365 /* Frameworks */ = {
isa = PBXGroup;
children = (
7FAA55DD93CD7AB58E7A977A /* Pods-LFLiveKitDemo.debug.xcconfig */,
195672426061368F86F1F4FA /* Pods-LFLiveKitDemo.release.xcconfig */,
6FD9F92833FE7856CDDD3CED /* libPods-LFLiveKitDemo.a */,
);
name = Pods;
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
840762EB1D07C7D0000FD0BF /* LFLiveKitDemo */ = {
B2D23E791D348F3D00B34CA8 /* LFLiveKitDemo */ = {
isa = PBXNativeTarget;
buildConfigurationList = 840763191D07C7D0000FD0BF /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */;
buildConfigurationList = B2D23E911D348F3D00B34CA8 /* 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 */,
6A9D2ED37E623D4A31A8D2C9 /* 📦 Check Pods Manifest.lock */,
B2D23E761D348F3D00B34CA8 /* Sources */,
B2D23E771D348F3D00B34CA8 /* Frameworks */,
B2D23E781D348F3D00B34CA8 /* Resources */,
34EEB2C8F5E0D371D13B66CA /* 📦 Copy Pods Resources */,
7336E9C92EDCA6C7449F2624 /* 📦 Embed Pods Frameworks */,
);
buildRules = (
);
@@ -199,29 +192,24 @@
);
name = LFLiveKitDemo;
productName = LFLiveKitDemo;
productReference = 840762EC1D07C7D0000FD0BF /* LFLiveKitDemo.app */;
productReference = B2D23E7A1D348F3D00B34CA8 /* LFLiveKitDemo.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
840762E41D07C7D0000FD0BF /* Project object */ = {
B2D23E721D348F3D00B34CA8 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = admin;
ORGANIZATIONNAME = zhanqi.tv;
TargetAttributes = {
840762EB1D07C7D0000FD0BF = {
B2D23E791D348F3D00B34CA8 = {
CreatedOnToolsVersion = 7.3;
SystemCapabilities = {
com.apple.BackgroundModes = {
enabled = 1;
};
};
};
};
};
buildConfigurationList = 840762E71D07C7D0000FD0BF /* Build configuration list for PBXProject "LFLiveKitDemo" */;
buildConfigurationList = B2D23E751D348F3D00B34CA8 /* Build configuration list for PBXProject "LFLiveKitDemo" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
@@ -229,38 +217,41 @@
en,
Base,
);
mainGroup = 840762E31D07C7D0000FD0BF;
productRefGroup = 840762ED1D07C7D0000FD0BF /* Products */;
mainGroup = B2D23E711D348F3D00B34CA8;
productRefGroup = B2D23E7B1D348F3D00B34CA8 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
840762EB1D07C7D0000FD0BF /* LFLiveKitDemo */,
B2D23E791D348F3D00B34CA8 /* LFLiveKitDemo */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
840762EA1D07C7D0000FD0BF /* Resources */ = {
B2D23E781D348F3D00B34CA8 /* 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 */,
840763391D07C899000FD0BF /* camra_preview@2x.png in Resources */,
840763361D07C899000FD0BF /* camra_beauty@3x.png in Resources */,
B2D23E8D1D348F3D00B34CA8 /* LaunchScreen.storyboard in Resources */,
B2D23EAA1D348F7100B34CA8 /* Assets.xcassets in Resources */,
B2D23EAC1D348F7100B34CA8 /* Main.storyboard in Resources */,
B2D23EAB1D348F7100B34CA8 /* LaunchScreen.storyboard in Resources */,
B2D23EB21D348F7100B34CA8 /* camra_beauty_close@3x.png in Resources */,
B2D23EAF1D348F7100B34CA8 /* camra_beauty@2x.png in Resources */,
B2D23EB61D348F7100B34CA8 /* close_preview@3x.png in Resources */,
B2D23EB41D348F7100B34CA8 /* camra_preview@3x.png in Resources */,
B2D23EB51D348F7100B34CA8 /* close_preview@2x.png in Resources */,
B2D23EB11D348F7100B34CA8 /* camra_beauty_close@2x.png in Resources */,
B2D23EB01D348F7100B34CA8 /* camra_beauty@3x.png in Resources */,
B2D23EB31D348F7100B34CA8 /* camra_preview@2x.png in Resources */,
B2D23E881D348F3D00B34CA8 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
64FCFF97E6544B1C8F282394 /* 📦 Copy Pods Resources */ = {
34EEB2C8F5E0D371D13B66CA /* 📦 Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -275,7 +266,7 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LFLiveKitDemo/Pods-LFLiveKitDemo-resources.sh\"\n";
showEnvVarsInLog = 0;
};
7E4C7C5523618A0595228010 /* 📦 Check Pods Manifest.lock */ = {
6A9D2ED37E623D4A31A8D2C9 /* 📦 Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -290,7 +281,7 @@
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 */ = {
7336E9C92EDCA6C7449F2624 /* 📦 Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -308,34 +299,58 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
840762E81D07C7D0000FD0BF /* Sources */ = {
B2D23E761D348F3D00B34CA8 /* 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 */,
B2D23EB71D348F7100B34CA8 /* LFLivePreview.m in Sources */,
B2D23E851D348F3D00B34CA8 /* ViewController.m in Sources */,
B2D23EAD1D348F7100B34CA8 /* UIControl+YYAdd.m in Sources */,
B2D23E821D348F3D00B34CA8 /* AppDelegate.m in Sources */,
B2D23EAE1D348F7100B34CA8 /* UIView+YYAdd.m in Sources */,
B2D23E7F1D348F3D00B34CA8 /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
840762FD1D07C7D0000FD0BF /* LaunchScreen.storyboard */ = {
B2D23E861D348F3D00B34CA8 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
840762FE1D07C7D0000FD0BF /* Base */,
B2D23E871D348F3D00B34CA8 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
B2D23E8B1D348F3D00B34CA8 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
B2D23E8C1D348F3D00B34CA8 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
B2D23E961D348F7100B34CA8 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
B2D23E971D348F7100B34CA8 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
B2D23E981D348F7100B34CA8 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
B2D23E991D348F7100B34CA8 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
840763171D07C7D0000FD0BF /* Debug */ = {
B2D23E8F1D348F3D00B34CA8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
@@ -358,6 +373,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -372,15 +388,17 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "$(inherited)";
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
LIBRARY_SEARCH_PATHS = "$(inherited)";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "$(inherited)";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
840763181D07C7D0000FD0BF /* Release */ = {
B2D23E901D348F3D00B34CA8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
@@ -403,6 +421,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -411,38 +430,46 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "$(inherited)";
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
LIBRARY_SEARCH_PATHS = "$(inherited)";
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "$(inherited)";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
8407631A1D07C7D0000FD0BF /* Debug */ = {
B2D23E921D348F3D00B34CA8 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7FAA55DD93CD7AB58E7A977A /* Pods-LFLiveKitDemo.debug.xcconfig */;
baseConfigurationReference = AFD491825C5DB2AD871189B5 /* Pods-LFLiveKitDemo.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
HEADER_SEARCH_PATHS = "$(inherited)";
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 = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = com.gameabc.LFLiveKitDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
8407631B1D07C7D0000FD0BF /* Release */ = {
B2D23E931D348F3D00B34CA8 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 195672426061368F86F1F4FA /* Pods-LFLiveKitDemo.release.xcconfig */;
baseConfigurationReference = 8FAAEBE1A4F099C69588B394 /* Pods-LFLiveKitDemo.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
HEADER_SEARCH_PATHS = "$(inherited)";
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 = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = com.gameabc.LFLiveKitDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
@@ -450,25 +477,25 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
840762E71D07C7D0000FD0BF /* Build configuration list for PBXProject "LFLiveKitDemo" */ = {
B2D23E751D348F3D00B34CA8 /* Build configuration list for PBXProject "LFLiveKitDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
840763171D07C7D0000FD0BF /* Debug */,
840763181D07C7D0000FD0BF /* Release */,
B2D23E8F1D348F3D00B34CA8 /* Debug */,
B2D23E901D348F3D00B34CA8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
840763191D07C7D0000FD0BF /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */ = {
B2D23E911D348F3D00B34CA8 /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
8407631A1D07C7D0000FD0BF /* Debug */,
8407631B1D07C7D0000FD0BF /* Release */,
B2D23E921D348F3D00B34CA8 /* Debug */,
B2D23E931D348F3D00B34CA8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 840762E41D07C7D0000FD0BF /* Project object */;
rootObject = B2D23E721D348F3D00B34CA8 /* Project object */;
}
@@ -27,6 +27,11 @@
<key>primary</key>
<true/>
</dict>
<key>B2D23E791D348F3D00B34CA8</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,91 @@
<?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 = "B2D23E791D348F3D00B34CA8"
BuildableName = "LFLiveKitDemo.app"
BlueprintName = "LFLiveKitDemo"
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B2D23E791D348F3D00B34CA8"
BuildableName = "LFLiveKitDemo.app"
BlueprintName = "LFLiveKitDemo"
ReferencedContainer = "container:LFLiveKitDemo.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">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B2D23E791D348F3D00B34CA8"
BuildableName = "LFLiveKitDemo.app"
BlueprintName = "LFLiveKitDemo"
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B2D23E791D348F3D00B34CA8"
BuildableName = "LFLiveKitDemo.app"
BlueprintName = "LFLiveKitDemo"
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,22 @@
<?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>LFLiveKitDemo.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>B2D23E791D348F3D00B34CA8</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "0"
version = "2.0">
</Bucket>
@@ -59,6 +59,11 @@
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
}
],
"info" : {
@@ -10,5 +10,4 @@
@interface LFLivePreview : UIView
@end
+172 -156
View File
@@ -11,6 +11,25 @@
#import "UIView+YYAdd.h"
#import "LFLiveSession.h"
inline static NSString *formatedSpeed(float bytes, float elapsed_milli) {
if (elapsed_milli <= 0) {
return @"N/A";
}
if (bytes <= 0) {
return @"0 KB/s";
}
float bytes_per_sec = ((float)bytes) * 1000.f / elapsed_milli;
if (bytes_per_sec >= 1000 * 1000) {
return [NSString stringWithFormat:@"%.2f MB/s", ((float)bytes_per_sec) / 1000 / 1000];
} else if (bytes_per_sec >= 1000) {
return [NSString stringWithFormat:@"%.1f KB/s", ((float)bytes_per_sec) / 1000];
} else {
return [NSString stringWithFormat:@"%ld B/s", (long)bytes_per_sec];
}
}
@interface LFLivePreview ()<LFLiveSessionDelegate>
@property (nonatomic, strong) UIButton *beautyButton;
@@ -26,8 +45,8 @@
@implementation LFLivePreview
- (instancetype)initWithFrame:(CGRect)frame{
if(self = [super initWithFrame:frame]){
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
[self requestAccessForVideo];
[self requestAccessForAudio];
@@ -42,198 +61,195 @@
}
#pragma mark -- Public Method
- (void)requestAccessForVideo{
- (void)requestAccessForVideo {
__weak typeof(self) _self = self;
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (status) {
case AVAuthorizationStatusNotDetermined:{
//
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
case AVAuthorizationStatusNotDetermined: {
//
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[_self.session setRunning:YES];
});
}
}];
break;
}
case AVAuthorizationStatusAuthorized:{
//
//dispatch_async(dispatch_get_main_queue(), ^{
[_self.session setRunning:YES];
//});
break;
}
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
// 访
break;
default:
break;
break;
}
case AVAuthorizationStatusAuthorized: {
//
dispatch_async(dispatch_get_main_queue(), ^{
[_self.session setRunning:YES];
});
break;
}
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
// 访
break;
default:
break;
}
}
- (void)requestAccessForAudio{
- (void)requestAccessForAudio {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
switch (status) {
case AVAuthorizationStatusNotDetermined:{
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
case AVAuthorizationStatusNotDetermined: {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
}];
break;
}
case AVAuthorizationStatusAuthorized:{
break;
}
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
break;
default:
break;
break;
}
case AVAuthorizationStatusAuthorized: {
break;
}
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
break;
default:
break;
}
}
#pragma mark -- LFStreamingSessionDelegate
/** live status changed will callback */
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state{
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state {
NSLog(@"liveStateDidChange: %ld", state);
switch (state) {
case LFLiveReady:
_stateLabel.text = @"未连接";
break;
case LFLivePending:
_stateLabel.text = @"连接中";
break;
case LFLiveStart:
_stateLabel.text = @"已连接";
break;
case LFLiveError:
_stateLabel.text = @"连接错误";
break;
case LFLiveStop:
_stateLabel.text = @"未连接";
break;
default:
break;
case LFLiveReady:
_stateLabel.text = @"未连接";
break;
case LFLivePending:
_stateLabel.text = @"连接中";
break;
case LFLiveStart:
_stateLabel.text = @"已连接";
break;
case LFLiveError:
_stateLabel.text = @"连接错误";
break;
case LFLiveStop:
_stateLabel.text = @"未连接";
break;
default:
break;
}
}
/** live debug info callback */
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug*)debugInfo{
NSLog(@"debugInfo: %lf", debugInfo.dataFlow);
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug *)debugInfo {
NSLog(@"debugInfo uploadSpeed: %@", formatedSpeed(debugInfo.currentBandwidth, debugInfo.elapsedMilli));
}
/** callback socket errorcode */
- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode{
- (void)liveSession:(nullable LFLiveSession *)session errorCode:(LFLiveSocketErrorCode)errorCode {
NSLog(@"errorCode: %ld", errorCode);
}
#pragma mark -- Getter Setter
- (LFLiveSession*)session{
if(!_session){
- (LFLiveSession *)session {
if (!_session) {
/** 发现大家有不会用横屏的请注意啦,横屏需要在ViewController supportedInterfaceOrientations修改方向 默认竖屏 ****/
/** 发现大家有不会用横屏的请注意啦,横屏需要在ViewController supportedInterfaceOrientations修改方向 默认竖屏 ****/
/** 发现大家有不会用横屏的请注意啦,横屏需要在ViewController supportedInterfaceOrientations修改方向 默认竖屏 ****/
/***  默认分辨率368 640 音频:44.1 iphone6以上48 双声道 方向竖屏 ***/
_session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Medium2 landscape:NO]];
_session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Medium3 landscape:NO]];
/**   自己定制单声道 */
/*
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 1;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_64Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:[LFLiveVideoConfiguration defaultConfiguration]];
*/
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 1;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_64Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:[LFLiveVideoConfiguration defaultConfiguration]];
*/
/**   自己定制高质量音频96K */
/*
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_96Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:[LFLiveVideoConfiguration defaultConfiguration]];
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_96Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:[LFLiveVideoConfiguration defaultConfiguration]];
*/
/**   自己定制高质量音频96K 分辨率设置为540*960 方向竖屏 */
/*
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_96Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];
videoConfiguration.videoSize = CGSizeMake(540, 960);
videoConfiguration.videoBitRate = 800*1024;
videoConfiguration.videoMaxBitRate = 1000*1024;
videoConfiguration.videoMinBitRate = 500*1024;
videoConfiguration.videoFrameRate = 24;
videoConfiguration.videoMaxKeyframeInterval = 48;
videoConfiguration.orientation = UIInterfaceOrientationPortrait;
videoConfiguration.sessionPreset = LFCaptureSessionPreset540x960;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration];
*/
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_96Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];
videoConfiguration.videoSize = CGSizeMake(540, 960);
videoConfiguration.videoBitRate = 800*1024;
videoConfiguration.videoMaxBitRate = 1000*1024;
videoConfiguration.videoMinBitRate = 500*1024;
videoConfiguration.videoFrameRate = 24;
videoConfiguration.videoMaxKeyframeInterval = 48;
videoConfiguration.orientation = UIInterfaceOrientationPortrait;
videoConfiguration.sessionPreset = LFCaptureSessionPreset540x960;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration];
*/
/**   自己定制高质量音频128K 分辨率设置为720*1280 方向竖屏 */
/*
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_128Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];
videoConfiguration.videoSize = CGSizeMake(720, 1280);
videoConfiguration.videoBitRate = 800*1024;
videoConfiguration.videoMaxBitRate = 1000*1024;
videoConfiguration.videoMinBitRate = 500*1024;
videoConfiguration.videoFrameRate = 15;
videoConfiguration.videoMaxKeyframeInterval = 30;
videoConfiguration.orientation = UIInterfaceOrientationPortrait;
videoConfiguration.sessionPreset = LFCaptureSessionPreset720x1280;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration];
*/
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_128Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];
videoConfiguration.videoSize = CGSizeMake(720, 1280);
videoConfiguration.videoBitRate = 800*1024;
videoConfiguration.videoMaxBitRate = 1000*1024;
videoConfiguration.videoMinBitRate = 500*1024;
videoConfiguration.videoFrameRate = 15;
videoConfiguration.videoMaxKeyframeInterval = 30;
videoConfiguration.orientation = UIInterfaceOrientationPortrait;
videoConfiguration.sessionPreset = LFCaptureSessionPreset720x1280;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration];
*/
/**   自己定制高质量音频128K 分辨率设置为720*1280 方向横屏 */
/*
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_128Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];
videoConfiguration.videoSize = CGSizeMake(1280, 720);
videoConfiguration.videoBitRate = 800*1024;
videoConfiguration.videoMaxBitRate = 1000*1024;
videoConfiguration.videoMinBitRate = 500*1024;
videoConfiguration.videoFrameRate = 15;
videoConfiguration.videoMaxKeyframeInterval = 30;
videoConfiguration.landscape = YES;
videoConfiguration.sessionPreset = LFCaptureSessionPreset720x1280;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration];
*/
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_128Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];
videoConfiguration.videoSize = CGSizeMake(1280, 720);
videoConfiguration.videoBitRate = 800*1024;
videoConfiguration.videoMaxBitRate = 1000*1024;
videoConfiguration.videoMinBitRate = 500*1024;
videoConfiguration.videoFrameRate = 15;
videoConfiguration.videoMaxKeyframeInterval = 30;
videoConfiguration.landscape = YES;
videoConfiguration.sessionPreset = LFCaptureSessionPreset720x1280;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration];
*/
_session.delegate = self;
_session.showDebugInfo = NO;
_session.preView = self;
}
return _session;
}
- (UIView*)containerView{
if(!_containerView){
- (UIView *)containerView {
if (!_containerView) {
_containerView = [UIView new];
_containerView.frame = self.bounds;
_containerView.backgroundColor = [UIColor clearColor];
@@ -242,8 +258,8 @@
return _containerView;
}
- (UILabel*)stateLabel{
if(!_stateLabel){
- (UILabel *)stateLabel {
if (!_stateLabel) {
_stateLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 80, 40)];
_stateLabel.text = @"未连接";
_stateLabel.textColor = [UIColor whiteColor];
@@ -252,8 +268,8 @@
return _stateLabel;
}
- (UIButton*)closeButton{
if(!_closeButton){
- (UIButton *)closeButton {
if (!_closeButton) {
_closeButton = [UIButton new];
_closeButton.size = CGSizeMake(44, 44);
_closeButton.left = self.width - 10 - _closeButton.width;
@@ -261,14 +277,14 @@
[_closeButton setImage:[UIImage imageNamed:@"close_preview"] forState:UIControlStateNormal];
_closeButton.exclusiveTouch = YES;
[_closeButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
}];
}
return _closeButton;
}
- (UIButton*)cameraButton{
if(!_cameraButton){
- (UIButton *)cameraButton {
if (!_cameraButton) {
_cameraButton = [UIButton new];
_cameraButton.size = CGSizeMake(44, 44);
_cameraButton.origin = CGPointMake(_closeButton.left - 10 - _cameraButton.width, 20);
@@ -283,11 +299,11 @@
return _cameraButton;
}
- (UIButton*)beautyButton{
if(!_beautyButton){
- (UIButton *)beautyButton {
if (!_beautyButton) {
_beautyButton = [UIButton new];
_beautyButton.size = CGSizeMake(44, 44);
_beautyButton.origin = CGPointMake(_cameraButton.left - 10 - _beautyButton.width,20);
_beautyButton.origin = CGPointMake(_cameraButton.left - 10 - _beautyButton.width, 20);
[_beautyButton setImage:[UIImage imageNamed:@"camra_beauty"] forState:UIControlStateSelected];
[_beautyButton setImage:[UIImage imageNamed:@"camra_beauty_close"] forState:UIControlStateNormal];
_beautyButton.exclusiveTouch = YES;
@@ -300,8 +316,8 @@
return _beautyButton;
}
- (UIButton*)startLiveButton{
if(!_startLiveButton){
- (UIButton *)startLiveButton {
if (!_startLiveButton) {
_startLiveButton = [UIButton new];
_startLiveButton.size = CGSizeMake(self.width - 60, 44);
_startLiveButton.left = 30;
@@ -315,13 +331,12 @@
__weak typeof(self) _self = self;
[_startLiveButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
_self.startLiveButton.selected = !_self.startLiveButton.selected;
if(_self.startLiveButton.selected){
if (_self.startLiveButton.selected) {
[_self.startLiveButton setTitle:@"结束直播" forState:UIControlStateNormal];
LFLiveStreamInfo *stream = [LFLiveStreamInfo new];
stream.url = @"rtmp://live.hkstv.hk.lxdns.com:1935/live/stream789";
//stream.url = @"rtmp://daniulive.com:1935/live/stream2399";
stream.url = @"rtmp://live.hkstv.hk.lxdns.com:1935/live/stream123";
[_self.session startLive:stream];
}else{
} else {
[_self.startLiveButton setTitle:@"开始直播" forState:UIControlStateNormal];
[_self.session stopLive];
}
@@ -331,3 +346,4 @@
}
@end
-8
View File
@@ -1,8 +0,0 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios,'8.0'
target "LFLiveKitDemo" do
pod 'LFLiveKit', '~> 1.5.2'
end
+2 -4
View File
@@ -26,13 +26,11 @@
// Dispose of any resources that can be recreated.
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
+4 -5
View File
@@ -1,8 +1,7 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios,'8.0'
platform :ios,'7.0'
target "LFLiveKitDemo" do
target 'LFLiveKitDemo' do
pod 'LFLiveKit', path: '../'
end
pod 'LFLiveKit', '~> 1.7.2'
end
+4
View File
@@ -0,0 +1,4 @@
#CocoaPods
Pods/
Podfile.lock
@@ -0,0 +1,404 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
B289F1BE1D3DCD3000D9C7A5 /* camra_beauty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B289F1B61D3DCD3000D9C7A5 /* camra_beauty@2x.png */; };
B289F1BF1D3DCD3000D9C7A5 /* camra_beauty@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B289F1B71D3DCD3000D9C7A5 /* camra_beauty@3x.png */; };
B289F1C01D3DCD3000D9C7A5 /* camra_beauty_close@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B289F1B81D3DCD3000D9C7A5 /* camra_beauty_close@2x.png */; };
B289F1C11D3DCD3000D9C7A5 /* camra_beauty_close@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B289F1B91D3DCD3000D9C7A5 /* camra_beauty_close@3x.png */; };
B289F1C21D3DCD3000D9C7A5 /* camra_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B289F1BA1D3DCD3000D9C7A5 /* camra_preview@2x.png */; };
B289F1C31D3DCD3000D9C7A5 /* camra_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B289F1BB1D3DCD3000D9C7A5 /* camra_preview@3x.png */; };
B289F1C41D3DCD3000D9C7A5 /* close_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B289F1BC1D3DCD3000D9C7A5 /* close_preview@2x.png */; };
B289F1C51D3DCD3000D9C7A5 /* close_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B289F1BD1D3DCD3000D9C7A5 /* close_preview@3x.png */; };
B2C8FAC51D3DB8B3008D44B5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C8FAC41D3DB8B3008D44B5 /* AppDelegate.swift */; };
B2C8FAC71D3DB8B3008D44B5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C8FAC61D3DB8B3008D44B5 /* ViewController.swift */; };
B2C8FACA1D3DB8B3008D44B5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2C8FAC81D3DB8B3008D44B5 /* Main.storyboard */; };
B2C8FACC1D3DB8B3008D44B5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B2C8FACB1D3DB8B3008D44B5 /* Assets.xcassets */; };
B2C8FACF1D3DB8B4008D44B5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2C8FACD1D3DB8B4008D44B5 /* LaunchScreen.storyboard */; };
D76381970CDF6883DA800952 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C703895C713844AE5F37BC53 /* libPods.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B289F1B61D3DCD3000D9C7A5 /* camra_beauty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@2x.png"; sourceTree = "<group>"; };
B289F1B71D3DCD3000D9C7A5 /* camra_beauty@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@3x.png"; sourceTree = "<group>"; };
B289F1B81D3DCD3000D9C7A5 /* camra_beauty_close@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@2x.png"; sourceTree = "<group>"; };
B289F1B91D3DCD3000D9C7A5 /* camra_beauty_close@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@3x.png"; sourceTree = "<group>"; };
B289F1BA1D3DCD3000D9C7A5 /* camra_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@2x.png"; sourceTree = "<group>"; };
B289F1BB1D3DCD3000D9C7A5 /* camra_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@3x.png"; sourceTree = "<group>"; };
B289F1BC1D3DCD3000D9C7A5 /* close_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@2x.png"; sourceTree = "<group>"; };
B289F1BD1D3DCD3000D9C7A5 /* close_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@3x.png"; sourceTree = "<group>"; };
B2C8FAC11D3DB8B3008D44B5 /* LFLiveKitSwiftDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LFLiveKitSwiftDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
B2C8FAC41D3DB8B3008D44B5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B2C8FAC61D3DB8B3008D44B5 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
B2C8FAC91D3DB8B3008D44B5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
B2C8FACB1D3DB8B3008D44B5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B2C8FACE1D3DB8B4008D44B5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
B2C8FAD01D3DB8B4008D44B5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B2C8FAD61D3DB9D6008D44B5 /* LFLiveKitSwiftDemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LFLiveKitSwiftDemo-Bridging-Header.h"; sourceTree = "<group>"; };
C5E86117C8AB61338C12909E /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
C703895C713844AE5F37BC53 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
FD5AE5787FDCE4BA8C28E2EE /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B2C8FABE1D3DB8B3008D44B5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D76381970CDF6883DA800952 /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0044BAB3BCE83EE63FB6F37C /* Pods */ = {
isa = PBXGroup;
children = (
FD5AE5787FDCE4BA8C28E2EE /* Pods.debug.xcconfig */,
C5E86117C8AB61338C12909E /* Pods.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
656DB83DA3620FA72C6B7CF7 /* Frameworks */ = {
isa = PBXGroup;
children = (
C703895C713844AE5F37BC53 /* libPods.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
B289F1B51D3DCD3000D9C7A5 /* images */ = {
isa = PBXGroup;
children = (
B289F1B61D3DCD3000D9C7A5 /* camra_beauty@2x.png */,
B289F1B71D3DCD3000D9C7A5 /* camra_beauty@3x.png */,
B289F1B81D3DCD3000D9C7A5 /* camra_beauty_close@2x.png */,
B289F1B91D3DCD3000D9C7A5 /* camra_beauty_close@3x.png */,
B289F1BA1D3DCD3000D9C7A5 /* camra_preview@2x.png */,
B289F1BB1D3DCD3000D9C7A5 /* camra_preview@3x.png */,
B289F1BC1D3DCD3000D9C7A5 /* close_preview@2x.png */,
B289F1BD1D3DCD3000D9C7A5 /* close_preview@3x.png */,
);
path = images;
sourceTree = "<group>";
};
B2C8FAB81D3DB8B3008D44B5 = {
isa = PBXGroup;
children = (
B2C8FAC31D3DB8B3008D44B5 /* LFLiveKitSwiftDemo */,
B2C8FAC21D3DB8B3008D44B5 /* Products */,
0044BAB3BCE83EE63FB6F37C /* Pods */,
656DB83DA3620FA72C6B7CF7 /* Frameworks */,
);
sourceTree = "<group>";
};
B2C8FAC21D3DB8B3008D44B5 /* Products */ = {
isa = PBXGroup;
children = (
B2C8FAC11D3DB8B3008D44B5 /* LFLiveKitSwiftDemo.app */,
);
name = Products;
sourceTree = "<group>";
};
B2C8FAC31D3DB8B3008D44B5 /* LFLiveKitSwiftDemo */ = {
isa = PBXGroup;
children = (
B289F1B51D3DCD3000D9C7A5 /* images */,
B2C8FAC41D3DB8B3008D44B5 /* AppDelegate.swift */,
B2C8FAC61D3DB8B3008D44B5 /* ViewController.swift */,
B2C8FAC81D3DB8B3008D44B5 /* Main.storyboard */,
B2C8FAD61D3DB9D6008D44B5 /* LFLiveKitSwiftDemo-Bridging-Header.h */,
B2C8FACB1D3DB8B3008D44B5 /* Assets.xcassets */,
B2C8FACD1D3DB8B4008D44B5 /* LaunchScreen.storyboard */,
B2C8FAD01D3DB8B4008D44B5 /* Info.plist */,
);
path = LFLiveKitSwiftDemo;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B2C8FAC01D3DB8B3008D44B5 /* LFLiveKitSwiftDemo */ = {
isa = PBXNativeTarget;
buildConfigurationList = B2C8FAD31D3DB8B4008D44B5 /* Build configuration list for PBXNativeTarget "LFLiveKitSwiftDemo" */;
buildPhases = (
BAC96C6840291F2B616FE902 /* Check Pods Manifest.lock */,
B2C8FABD1D3DB8B3008D44B5 /* Sources */,
B2C8FABE1D3DB8B3008D44B5 /* Frameworks */,
B2C8FABF1D3DB8B3008D44B5 /* Resources */,
7490D167BE18C3C7DC2FE381 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = LFLiveKitSwiftDemo;
productName = LFLiveKitSwiftDemo;
productReference = B2C8FAC11D3DB8B3008D44B5 /* LFLiveKitSwiftDemo.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
B2C8FAB91D3DB8B3008D44B5 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = zhanqi.tv;
TargetAttributes = {
B2C8FAC01D3DB8B3008D44B5 = {
CreatedOnToolsVersion = 7.3;
};
};
};
buildConfigurationList = B2C8FABC1D3DB8B3008D44B5 /* Build configuration list for PBXProject "LFLiveKitSwiftDemo" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = B2C8FAB81D3DB8B3008D44B5;
productRefGroup = B2C8FAC21D3DB8B3008D44B5 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
B2C8FAC01D3DB8B3008D44B5 /* LFLiveKitSwiftDemo */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B2C8FABF1D3DB8B3008D44B5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B289F1C01D3DCD3000D9C7A5 /* camra_beauty_close@2x.png in Resources */,
B2C8FACF1D3DB8B4008D44B5 /* LaunchScreen.storyboard in Resources */,
B289F1BE1D3DCD3000D9C7A5 /* camra_beauty@2x.png in Resources */,
B2C8FACC1D3DB8B3008D44B5 /* Assets.xcassets in Resources */,
B289F1C31D3DCD3000D9C7A5 /* camra_preview@3x.png in Resources */,
B289F1C11D3DCD3000D9C7A5 /* camra_beauty_close@3x.png in Resources */,
B289F1C51D3DCD3000D9C7A5 /* close_preview@3x.png in Resources */,
B289F1C41D3DCD3000D9C7A5 /* close_preview@2x.png in Resources */,
B2C8FACA1D3DB8B3008D44B5 /* Main.storyboard in Resources */,
B289F1C21D3DCD3000D9C7A5 /* camra_preview@2x.png in Resources */,
B289F1BF1D3DCD3000D9C7A5 /* camra_beauty@3x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
7490D167BE18C3C7DC2FE381 /* 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/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
BAC96C6840291F2B616FE902 /* 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 */
B2C8FABD1D3DB8B3008D44B5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B2C8FAC71D3DB8B3008D44B5 /* ViewController.swift in Sources */,
B2C8FAC51D3DB8B3008D44B5 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
B2C8FAC81D3DB8B3008D44B5 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
B2C8FAC91D3DB8B3008D44B5 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
B2C8FACD1D3DB8B4008D44B5 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
B2C8FACE1D3DB8B4008D44B5 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
B2C8FAD11D3DB8B4008D44B5 /* 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;
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;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
B2C8FAD21D3DB8B4008D44B5 /* 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;
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;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
B2C8FAD41D3DB8B4008D44B5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FD5AE5787FDCE4BA8C28E2EE /* Pods.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
INFOPLIST_FILE = LFLiveKitSwiftDemo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.gameabc.laifeng.LFLiveKitSwiftDemo.LFLiveKitSwiftDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "LFLiveKitSwiftDemo/LFLiveKitSwiftDemo-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
B2C8FAD51D3DB8B4008D44B5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C5E86117C8AB61338C12909E /* Pods.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
INFOPLIST_FILE = LFLiveKitSwiftDemo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.gameabc.laifeng.LFLiveKitSwiftDemo.LFLiveKitSwiftDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "LFLiveKitSwiftDemo/LFLiveKitSwiftDemo-Bridging-Header.h";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
B2C8FABC1D3DB8B3008D44B5 /* Build configuration list for PBXProject "LFLiveKitSwiftDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B2C8FAD11D3DB8B4008D44B5 /* Debug */,
B2C8FAD21D3DB8B4008D44B5 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B2C8FAD31D3DB8B4008D44B5 /* Build configuration list for PBXNativeTarget "LFLiveKitSwiftDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B2C8FAD41D3DB8B4008D44B5 /* Debug */,
B2C8FAD51D3DB8B4008D44B5 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B2C8FAB91D3DB8B3008D44B5 /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:LFLiveKitSwiftDemo.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,91 @@
<?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 = "B2C8FAC01D3DB8B3008D44B5"
BuildableName = "LFLiveKitSwiftDemo.app"
BlueprintName = "LFLiveKitSwiftDemo"
ReferencedContainer = "container:LFLiveKitSwiftDemo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B2C8FAC01D3DB8B3008D44B5"
BuildableName = "LFLiveKitSwiftDemo.app"
BlueprintName = "LFLiveKitSwiftDemo"
ReferencedContainer = "container:LFLiveKitSwiftDemo.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">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B2C8FAC01D3DB8B3008D44B5"
BuildableName = "LFLiveKitSwiftDemo.app"
BlueprintName = "LFLiveKitSwiftDemo"
ReferencedContainer = "container:LFLiveKitSwiftDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B2C8FAC01D3DB8B3008D44B5"
BuildableName = "LFLiveKitSwiftDemo.app"
BlueprintName = "LFLiveKitSwiftDemo"
ReferencedContainer = "container:LFLiveKitSwiftDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,22 @@
<?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>LFLiveKitSwiftDemo.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>B2C8FAC01D3DB8B3008D44B5</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:LFLiveKitSwiftDemo.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "0"
version = "2.0">
</Bucket>
@@ -0,0 +1,46 @@
//
// AppDelegate.swift
// LFLiveKitSwiftDemo
//
// Created by feng on 16/7/19.
// Copyright © 2016 zhanqi.tv. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
@@ -0,0 +1,38 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
@@ -0,0 +1,40 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -0,0 +1,10 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#ifndef LFLiveKitSwiftDemo_Bridging_H
#define LFLiveKitSwiftDemo_Bridging_H
#import <LFLiveKit.h>
#endif
@@ -0,0 +1,222 @@
//
// ViewController.swift
// LFLiveKitSwiftDemo
//
// Created by feng on 16/7/19.
// Copyright © 2016 zhanqi.tv. All rights reserved.
//
import UIKit
class ViewController: UIViewController, LFLiveSessionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.requestAccessForVideo()
self.requestAccessForAudio()
self.view.backgroundColor = UIColor.clearColor()
self.view.addSubview(containerView)
containerView.addSubview(stateLabel)
containerView.addSubview(closeButton)
containerView.addSubview(beautyButton)
containerView.addSubview(cameraButton)
containerView.addSubview(startLiveButton)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: AccessAuth
func requestAccessForVideo() -> Void {
let status = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
switch status {
//
case AVAuthorizationStatus.NotDetermined:
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { (granted) in
if (granted) {
dispatch_async(dispatch_get_main_queue(), {
self.session.running = true;
});
}
})
break;
//
case AVAuthorizationStatus.Authorized:
session.running = true;
break;
// 访
case AVAuthorizationStatus.Denied: break
case AVAuthorizationStatus.Restricted:break;
default:
break;
}
}
func requestAccessForAudio() -> Void {
let status = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeAudio)
switch status {
//
case AVAuthorizationStatus.NotDetermined:
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeAudio, completionHandler: { (granted) in
})
break;
//
case AVAuthorizationStatus.Authorized:
break;
// 访
case AVAuthorizationStatus.Denied: break
case AVAuthorizationStatus.Restricted:break;
default:
break;
}
}
//MARK: - Callbacks
//
func liveSession(session: LFLiveSession?, debugInfo: LFLiveDebug?) {
print("debugInfo: \(debugInfo?.currentBandwidth)")
}
func liveSession(session: LFLiveSession?, errorCode: LFLiveSocketErrorCode) {
print("errorCode: \(errorCode.rawValue)")
}
func liveSession(session: LFLiveSession?, liveStateDidChange state: LFLiveState) {
print("liveStateDidChange: \(state.rawValue)")
switch state {
case LFLiveState.Ready:
stateLabel.text = "未连接"
break;
case LFLiveState.Pending:
stateLabel.text = "连接中"
break;
case LFLiveState.Start:
stateLabel.text = "已连接"
break;
case LFLiveState.Error:
stateLabel.text = "连接错误"
break;
case LFLiveState.Stop:
stateLabel.text = "未连接"
break;
default:
stateLabel.text = "未知"
break;
}
}
//MARK: - Events
//
func didTappedStartLiveButton(button: UIButton) -> Void {
startLiveButton.selected = !startLiveButton.selected;
if (startLiveButton.selected) {
startLiveButton.setTitle("结束直播", forState: UIControlState.Normal)
let stream = LFLiveStreamInfo()
stream.url = "rtmp://30.96.179.95:1935/live/1234"
session.startLive(stream)
} else {
startLiveButton.setTitle("开始直播", forState: UIControlState.Normal)
session.stopLive()
}
}
//
func didTappedBeautyButton(button: UIButton) -> Void {
session.beautyFace = !session.beautyFace;
beautyButton.selected = !session.beautyFace;
}
//
func didTappedCameraButton(button: UIButton) -> Void {
let devicePositon = session.captureDevicePosition;
session.captureDevicePosition = (devicePositon == AVCaptureDevicePosition.Back) ? AVCaptureDevicePosition.Front : AVCaptureDevicePosition.Back;
}
//
func didTappedCloseButton(button: UIButton) -> Void {
}
//MARK: - Getters and Setters
//  368 640 44.1 iphone648
lazy var session: LFLiveSession = {
let audioConfiguration = LFLiveAudioConfiguration.defaultConfiguration()
let videoConfiguration = LFLiveVideoConfiguration.defaultConfigurationForQuality(LFLiveVideoQuality.Low3, landscape: false)
let session = LFLiveSession(audioConfiguration: audioConfiguration, videoConfiguration: videoConfiguration)
session?.delegate = self
session?.preView = self.view
return session!
}()
//
lazy var containerView: UIView = {
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height))
containerView.backgroundColor = UIColor.clearColor()
containerView.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleHeight]
return containerView
}()
// Label
lazy var stateLabel: UILabel = {
let stateLabel = UILabel(frame: CGRect(x: 20, y: 20, width: 80, height: 40))
stateLabel.text = "未连接"
stateLabel.textColor = UIColor.whiteColor()
stateLabel.font = UIFont.systemFontOfSize(14)
return stateLabel
}()
//
lazy var closeButton: UIButton = {
let closeButton = UIButton(frame: CGRect(x: self.view.frame.width - 10 - 44, y: 20, width: 44, height: 44))
closeButton.setImage(UIImage(named: "close_preview"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: #selector(didTappedCloseButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
return closeButton
}()
//
lazy var cameraButton: UIButton = {
let cameraButton = UIButton(frame: CGRect(x: self.view.frame.width - 54 * 2, y: 20, width: 44, height: 44))
cameraButton.setImage(UIImage(named: "camra_preview"), forState: UIControlState.Normal)
cameraButton.addTarget(self, action: #selector(didTappedCameraButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
return cameraButton
}()
//
lazy var beautyButton: UIButton = {
let beautyButton = UIButton(frame: CGRect(x: self.view.frame.width - 54 * 3, y: 20, width: 44, height: 44))
beautyButton.setImage(UIImage(named: "camra_preview"), forState: UIControlState.Selected)
beautyButton.setImage(UIImage(named: "camra_beauty_close"), forState: UIControlState.Normal)
beautyButton.addTarget(self, action: #selector(didTappedBeautyButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
return beautyButton
}()
//
lazy var startLiveButton: UIButton = {
let startLiveButton = UIButton(frame: CGRect(x: 30, y: self.view.frame.height - 50, width: self.view.frame.width - 10 - 44, height: 44))
startLiveButton.layer.cornerRadius = 22
startLiveButton.setTitleColor(UIColor.blackColor(), forState:UIControlState.Normal)
startLiveButton.setTitle("开始直播", forState: UIControlState.Normal)
startLiveButton.titleLabel!.font = UIFont.systemFontOfSize(14)
startLiveButton.backgroundColor = UIColor(colorLiteralRed: 50, green: 32, blue: 245, alpha: 1)
startLiveButton.addTarget(self, action: #selector(didTappedStartLiveButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
return startLiveButton
}()
//
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
override func shouldAutorotate() -> Bool {
return true
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

+4
View File
@@ -0,0 +1,4 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios,'7.0'
pod 'LFLiveKit', path: '../'
+7 -7
View File
@@ -1,10 +1,10 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios,'8.0'
platform :ios,'7.0'
target 'LFLiveKit' do
pod 'LMGPUImage', '~> 0.1.9'
pod 'pili-librtmp', '~> 1.0.3.1'
end
target "LFLiveKit" do
pod 'CocoaAsyncSocket', '~> 7.4.1'
pod 'pili-librtmp', '~> 1.0.2'
pod 'LMGPUImage', '~> 0.1.9'
pod 'YYDispatchQueuePool'
end
+95 -67
View File
@@ -1,101 +1,129 @@
LFLiveKit
==============
[![Build Status](https://travis-ci.org/LaiFengiOS/LFLiveKit.svg)](https://travis-ci.org/LaiFengiOS/LFLiveKit)&nbsp;
[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/chenliming777/LFLiveKit/master/LICENSE)&nbsp;
[![CocoaPods](http://img.shields.io/cocoapods/v/LFLiveKit.svg?style=flat)](http://cocoapods.org/?q=LFLiveKit)&nbsp;
[![Support](https://img.shields.io/badge/support-ios8%2B-orange.svg)](https://www.apple.com/nl/ios/)&nbsp;
![platform](https://img.shields.io/badge/platform-ios-ff69b4.svg)&nbsp;
LFLiveKit
LFLiveKit IOS mobile phone push codeDefault format support RTMP and FLVAt the same time, the structure is very easy to extend.
**LFLiveKit is a opensource RTMP streaming SDK for iOS.**
## Features
- [x] Background recording
- [x] Support horizontal vertical recording
- [x] Support Beauty Face With GPUImage
- [x] Support H264+AAC Hardware Encoding
- [x] Drop frames on bad network
- [x] Dynamic switching rate
- [x] Audio configuration
- [x] Video configuration
- [x] RTMP Transport
- [x] Switch camera position
- [x] Audio Mute
- [x] Support Send Buffer
- [x] Swift Support
- [ ] ~~FLV package and send~~
## Installation
#### CocoaPods
# To integrate LFLiveKit into your Xcode project using CocoaPods, specify it in your Podfile:
Podfile
To integrate LFLiveKit into your Xcode project using CocoaPods, specify it in your Podfile:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'LFLiveKit'
Then, run the following command:
# Then, run the following command:
$ pod install
Functional
### Manually
Background recording
Support horizontal vertical recording
GPUImage Beauty
H264 Hard coding
AAC Hard coding
Weak network lost frame
Dynamic switching rate
Audio configuration
Video configuration
RTMP Transport
Switch camera
Audio Mute
Support Send Buffer
FLV package and send
1. Download all the files in the `LFLiveKit` subdirectory.
2. Add the source files to your Xcode project.
3. Link with required frameworks:
* UIKit
* Foundation
* AVFoundation
* VideoToolbox
* AudioToolbox
* libz
5. Add `LMGPUImage and pili-librtmp`(static library) to your Xcode project.
Architecture
## Architecture:
capture: LFAudioCapture and LFVideoCapture
encode: LFHardwareAudioEncoder and LFHardwareVideoEncoder
publish: LFStreamRtmpSocket LFStreamTcpSocket
publish: LFStreamRtmpSocket
Usage
- (LFLiveSession*)session{
if(!_session){
_session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfiguration] liveType:LFLiveRTMP];
_session.running = YES;
## Usage:
#### Objective-C
```
- (LFLiveSession*)session {
if (!_session) {
_session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfiguration]];
_session.preView = self;
_session.delegate = self;
}
return _session;
}
- (LFLiveSession*)session{
if(!_session){
LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];
audioConfiguration.numberOfChannels = 2;
audioConfiguration.audioBitrate = LFLiveAudioBitRate_128Kbps;
audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];
videoConfiguration.videoSize = CGSizeMake(1280, 720);
videoConfiguration.videoBitRate = 800*1024;
videoConfiguration.videoMaxBitRate = 1000*1024;
videoConfiguration.videoMinBitRate = 500*1024;
videoConfiguration.videoFrameRate = 15;
videoConfiguration.videoMaxKeyframeInterval = 30;
videoConfiguration.orientation = UIInterfaceOrientationLandscapeLeft;
videoConfiguration.sessionPreset = LFCaptureSessionPreset720x1280;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration liveType:LFLiveRTMP];
_session.running = YES;
_session.preView = self;
}
return _session;
}
}
- (void)startLive {
LFLiveStreamInfo *streamInfo = [LFLiveStreamInfo new];
streamInfo.url = @"your server rtmp url";
[self.session startLive:streamInfo];
}
- (void)stopLive {
[self.session stopLive];
}
//MARK: - CallBack:
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange: (LFLiveState)state;
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug*)debugInfo;
- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode;
CallBack:
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange: (LFLiveState)state;
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug*)debugInfo;
- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode;
License
LFLiveKit is released under the MIT license. See LICENSE for details.
```
#### Swift
```
// import LFLiveKit in [ProjectName]-Bridging-Header.h
import <LFLiveKit.h>
//MARK: - Getters and Setters
lazy var session: LFLiveSession = {
let audioConfiguration = LFLiveAudioConfiguration.defaultConfiguration()
let videoConfiguration = LFLiveVideoConfiguration.defaultConfigurationForQuality(LFLiveVideoQuality.Low3, landscape: false)
let session = LFLiveSession(audioConfiguration: audioConfiguration, videoConfiguration: videoConfiguration)
session?.delegate = self
session?.preView = self.view
return session!
}()
//MARK: - Event
func startLive() -> Void {
let stream = LFLiveStreamInfo()
stream.url = "your server rtmp url";
session.startLive(stream)
}
func stopLive() -> Void {
session.stopLive()
}
//MARK: - Callback
func liveSession(session: LFLiveSession?, debugInfo: LFLiveDebug?)
func liveSession(session: LFLiveSession?, errorCode: LFLiveSocketErrorCode)
func liveSession(session: LFLiveSession?, liveStateDidChange state: LFLiveState)
```
## License
**LFLiveKit is released under the MIT license. See LICENSE for details.**
+643
View File
@@ -0,0 +1,643 @@
#
# Uncrustify Configuration File
# File Created With UncrustifyX 0.4.3 (252)
#
# Alignment
# ---------
## Alignment
# Align ObjC declaration params on colon
align_oc_decl_colon = true # boolean (false/true)
# Align function prototypes on operator keyword
align_on_operator = false # boolean (false/true)
# Align on tabstop
align_on_tabstop = false # boolean (false/true)
# Align open brace of single-line functions
align_single_line_brace = false # boolean (false/true)
# Align parameters in single-line functions with same name
align_same_func_call_params = false # boolean (false/true)
# Align single-line functions with function prototypes
align_single_line_func = false # boolean (false/true)
# Align variable definitions
align_func_params = false # boolean (false/true)
# Align with tabs
align_with_tabs = false # boolean (false/true)
# Keep non-indenting tabs
align_keep_tabs = false # boolean (false/true)
# Left-align numbers
align_number_left = false # boolean (false/true)
## Alignment Span
# Alignment span for ObjC message colons
align_oc_msg_colon_span = 16 # number
# Alignment span for ObjC message spec
align_oc_msg_spec_span = 0 # number
# Alignment span for assignment
align_assign_span = 0 # number
# Alignment span for variable definitions
align_var_def_span = 0 # number
## Alignment Style
# Alignment style for ampersand in typedefs
align_typedef_amp_style = 2 # number
# Alignment style for ampersand in variable definitions
align_var_def_amp_style = 1 # number
# Alignment style for star in typedefs
align_typedef_star_style = 2 # number
# Alignment style for star in variable definitions
align_var_def_star_style = 1 # number
## Other
# Align attribute after variable name
align_var_def_attribute = false # boolean (false/true)
# Align colon in struct bit fields
align_var_def_colon = false # boolean (false/true)
# Align comments after close brace and endif
align_right_cmt_mix = false # boolean (false/true)
# Align inline struct/enum/union variable definitions
align_var_def_inline = false # boolean (false/true)
# Align left shift operators on new lines
align_left_shift = true # boolean (false/true)
# Align macros wrapped with backslash and newline
align_nl_cont = false # boolean (false/true)
# Mix aligining prototype and variable declarations
align_mix_var_proto = false # boolean (false/true)
# Blank Lines
# -----------
## Newline After
# Newline after multiline comment
nl_after_multiline_comment = false # boolean (false/true)
## Newline Count After
# Newline count after function body
nl_after_func_body = 2 # number
## Other
# Remove blank lines after open brace
eat_blanks_after_open_brace = false # boolean (false/true)
# Remove blank lines before close brace
eat_blanks_before_close_brace = false # boolean (false/true)
# Code-Modifying
# --------------
## Braces
# Braces on chained if/elseif/else statements
mod_full_brace_if_chain = false # boolean (false/true)
## Other
# Move break in fully-braced case before close brace
mod_move_case_break = false # boolean (false/true)
# Remove empty return as last statement in function
mod_remove_empty_return = false # boolean (false/true)
## Parentheses
# Add parentheses on while and if statements around bool
mod_full_paren_if_bool = false # boolean (false/true)
## Semicolons
# Change optional semicolons to real semicolons
mod_pawn_semicolon = false # boolean (false/true)
# Remove superflous semicolons
mod_remove_extra_semicolon = false # boolean (false/true)
## Sorting
# Sort consecutive import statements
mod_sort_import = false # boolean (false/true)
# Sort include statements
mod_sort_include = false # boolean (false/true)
# Sort using statements
mod_sort_using = false # boolean (false/true)
# Comments
# --------
## Empty Lines
# Empty first line for multi-line C comments
cmt_c_nl_start = false # boolean (false/true)
# Empty first line for multi-line C++ comments
cmt_cpp_nl_start = false # boolean (false/true)
# Empty last line for multi-line C comments
cmt_c_nl_end = false # boolean (false/true)
# Empty last line for multi-line C++ comments
cmt_cpp_nl_end = false # boolean (false/true)
## Other
# Change C++ comments to C comments
cmt_cpp_to_c = false # boolean (false/true)
# Group C comments
cmt_c_group = false # boolean (false/true)
# Group C++ comments
cmt_cpp_group = false # boolean (false/true)
# Indent multi-line comments
cmt_indent_multi = true # boolean (false/true)
# Insert function comment if preprocessor statement
cmt_insert_before_preproc = false # boolean (false/true)
# Remove leading spaces from multi-line comments
cmt_multi_check_last = true # boolean (false/true)
# Stars on multi-line comments
cmt_star_cont = false # boolean (false/true)
# General
# -------
## Other
# Interpret >=
tok_split_gte = false # boolean (false/true)
# Indentation
# -----------
## Indentation
# Disable indenting class braces
indent_braces_no_class = false # boolean (false/true)
# Disable indenting function braces
indent_braces_no_func = false # boolean (false/true)
# Disable indenting struct braces
indent_braces_no_struct = false # boolean (false/true)
# Double indent size for Indentation options
indent_func_param_double = false # boolean (false/true)
# Indent ObjC block
indent_oc_block = true # boolean (false/true)
# Indent braces
indent_braces = false # boolean (false/true)
# Indent class body
indent_class = false # boolean (false/true)
# Indent class variable constructors
indent_func_ctor_var_param = false # boolean (false/true)
# Indent continued function call parameters
indent_func_call_param = false # boolean (false/true)
# Indent function call parameters in class declarations
indent_func_class_param = false # boolean (false/true)
# Indent function definition parameters
indent_func_def_param = false # boolean (false/true)
# Indent function prototype parameters
indent_func_proto_param = false # boolean (false/true)
# Indent template parameters
indent_template_param = false # boolean (false/true)
## Indentation Size
# Indent size based on brace parent
indent_brace_parent = false # boolean (false/true)
# Indentation column size
indent_columns = 4 # number
## Other
# Align continued statements at equals
indent_align_assign = true # boolean (false/true)
# Align strings broken by backslash
indent_align_string = false # boolean (false/true)
# Indent BOOL inside parentheses
indent_bool_paren = false # boolean (false/true)
# Indent after class colon
indent_class_colon = false # boolean (false/true)
# Indent code after access specifier
indent_access_spec_body = false # boolean (false/true)
# Indent comma inside parentheses
indent_comma_paren = false # boolean (false/true)
# Indent comments in first column
indent_col1_comment = false # boolean (false/true)
# Indent comments with tabs
indent_cmt_with_tabs = false # boolean (false/true)
# Indent content after open square followed by newline
indent_square_nl = false # boolean (false/true)
# Indent continued variable declarations
indent_var_def_cont = false # boolean (false/true)
# Indent else\nif
indent_else_if = false # boolean (false/true)
# Indent extern body
indent_extern = false # boolean (false/true)
# Indent namespace body
indent_namespace = false # boolean (false/true)
# Indent newline content after open parenthesis
indent_paren_nl = false # boolean (false/true)
# Indent relative single line comments
indent_relative_single_line_comments = false # boolean (false/true)
# Indent with tabs
indent_with_tabs = 0 # number
# Indentation of first BOOL expression
indent_first_bool_expr = false # boolean (false/true)
# Preserve SQL indentation
indent_preserve_sql = false # boolean (false/true)
# Line-Splitting
# --------------
## Splitting
# Split long for statements at semicolons
ls_for_split_full = false # boolean (false/true)
# Split long function prototypes/calls at commas
ls_func_split_full = false # boolean (false/true)
# Newlines
# --------
## Merging
# Change unbraced for statements into one-liner
nl_create_for_one_liner = false # boolean (false/true)
# Change unbraced if statements into one-liner
nl_create_if_one_liner = false # boolean (false/true)
# Change unbraced while statements into one-liner
nl_create_while_one_liner = false # boolean (false/true)
## Newline After
# Newline after brace close
nl_after_brace_close = false # boolean (false/true)
# Newline after brace open
nl_after_brace_open = false # boolean (false/true)
# Newline after brace open comment
nl_after_brace_open_cmt = false # boolean (false/true)
# Newline after case statement
nl_after_case = false # boolean (false/true)
# Newline after macro multi-line definition
nl_multi_line_define = false # boolean (false/true)
# Newline after return
nl_after_return = false # boolean (false/true)
# Newline after semicolon
nl_after_semicolon = false # boolean (false/true)
# Newline after virtual brace close
nl_after_vbrace_close = false # boolean (false/true)
# Newline after virtual brace open
nl_after_vbrace_open = false # boolean (false/true)
# Newline after virtual brace open with empty body
nl_after_vbrace_open_empty = false # boolean (false/true)
## Newline Before
# Newline before case statement
nl_before_case = false # boolean (false/true)
## Newline Between
# Newline between catch and open brace
nl_catch_brace = remove # string (add/force/ignore/remove)
# Newline between close brace and catch
nl_brace_catch = remove # string (add/force/ignore/remove)
# Newline between close brace and else
nl_brace_else = remove # string (add/force/ignore/remove)
# Newline between close brace and finally
nl_brace_finally = remove # string (add/force/ignore/remove)
# Newline between close brace and while
nl_brace_while = remove # string (add/force/ignore/remove)
# Newline between close parenthesis and open brace in multi line conditional
nl_multi_line_cond = false # boolean (false/true)
# Newline between do and open brace
nl_do_brace = remove # string (add/force/ignore/remove)
# Newline between else and if
nl_else_if = remove # string (add/force/ignore/remove)
# Newline between else and open brace
nl_else_brace = remove # string (add/force/ignore/remove)
# Newline between else if and open brace
nl_elseif_brace = remove # string (add/force/ignore/remove)
# Newline between enum and open brace
nl_enum_brace = remove # string (add/force/ignore/remove)
# Newline between finally and open brace
nl_finally_brace = remove # string (add/force/ignore/remove)
# Newline between for and open brace
nl_for_brace = remove # string (add/force/ignore/remove)
# Newline between function call and open brace
nl_fcall_brace = remove # string (add/force/ignore/remove)
# Newline between function signature and open brace
nl_fdef_brace = remove # string (add/force/ignore/remove)
# Newline between if and open brace
nl_if_brace = remove # string (add/force/ignore/remove)
# Newline between struct and open brace
nl_struct_brace = remove # string (add/force/ignore/remove)
# Newline between switch and open brace
nl_switch_brace = remove # string (add/force/ignore/remove)
# Newline between try and open brace
nl_try_brace = remove # string (add/force/ignore/remove)
# Newline between union and open brace
nl_union_brace = remove # string (add/force/ignore/remove)
# Newline between while and open brace
nl_while_brace = remove # string (add/force/ignore/remove)
## Other
# Alter newlines in #define macros
nl_define_macro = false # boolean (false/true)
# Blank lines after preprocessor if/else
nl_squeeze_ifdef = false # boolean (false/true)
# Collapse empty blocks between braces
nl_collapse_empty_body = false # boolean (false/true)
# Double space before close brace of struct/union/enum
nl_ds_struct_enum_close_brace = false # boolean (false/true)
# Double space commented entries in struct/enum
nl_ds_struct_enum_cmt = false # boolean (false/true)
## Splitting
# Dont split one-line braced assignments
nl_assign_leave_one_liners = false # boolean (false/true)
# Dont split one-line braced statements
nl_class_leave_one_liners = false # boolean (false/true)
# Dont split one-line enums
nl_enum_leave_one_liners = false # boolean (false/true)
# Dont split one-line function definitions
nl_func_leave_one_liners = false # boolean (false/true)
# Dont split one-line get/set functions
nl_getset_leave_one_liners = false # boolean (false/true)
# Dont split one-line if/else statements
nl_if_leave_one_liners = false # boolean (false/true)
# Other
# -----
## Other
# Force encoding UTF-8
utf8_force = false # boolean (false/true)
# UTF-8 output if file contains bytes > 128 and < 255
utf8_byte = false # boolean (false/true)
# Preprocessor
# ------------
## Indentation
# Indent preprocessor define
pp_define_at_level = false # boolean (false/true)
# Indent preprocessor if/else/endif
pp_if_indent_code = false # boolean (false/true)
# Indent preprocessor if/else/endif at level
pp_indent_at_level = false # boolean (false/true)
# Indent preprocessor regions
pp_region_indent_code = false # boolean (false/true)
# Spacing
# -------
## Other
# Balance spaces inside nested parentheses
sp_balance_nested_parens = false # boolean (false/true)
## Space After
# Space after ObjC block caret
sp_after_oc_block_caret = remove # string (add/force/ignore/remove)
# Space after ObjC colon
sp_after_oc_colon = remove # string (add/force/ignore/remove)
# Space after ObjC message colon
sp_after_send_oc_colon = remove # string (add/force/ignore/remove)
# Space after ObjC message receiver
sp_after_oc_msg_receiver = force # string (add/force/ignore/remove)
# Space after ObjC return type
sp_after_oc_return_type = remove # string (add/force/ignore/remove)
# Space after ObjC scope
sp_after_oc_scope = force # string (add/force/ignore/remove)
# Space after ObjC type
sp_after_oc_type = remove # string (add/force/ignore/remove)
# Space after cast
sp_after_cast = remove # string (add/force/ignore/remove)
# Space after comma
sp_after_comma = force # string (add/force/ignore/remove)
# Space after condition close parenthesis
sp_after_sparen = force # string (add/force/ignore/remove)
# Space after operator
sp_after_operator = force # string (add/force/ignore/remove)
# Space after pointer star
sp_after_ptr_star = remove # string (add/force/ignore/remove)
## Space Around
# Space around assignment operator
sp_assign = force # string (add/force/ignore/remove)
# Space around boolean operators
sp_bool = force # string (add/force/ignore/remove)
# Space around compare operators
sp_compare = force # string (add/force/ignore/remove)
# Space around ternary condition colon
sp_cond_colon = force # string (add/force/ignore/remove)
# Space around ternary condition question mark
sp_cond_question = force # string (add/force/ignore/remove)
## Space Before
# Space before ObjC block caret
sp_before_oc_block_caret = remove # string (add/force/ignore/remove)
# Space before ObjC colon
sp_before_oc_colon = remove # string (add/force/ignore/remove)
# Space before ObjC message colon
sp_before_send_oc_colon = remove # string (add/force/ignore/remove)
# Space before comma
sp_before_comma = remove # string (add/force/ignore/remove)
# Space before if/for/switch/while open parenthesis
sp_before_sparen = force # string (add/force/ignore/remove)
# Space before pointer star
sp_before_ptr_star = force # string (add/force/ignore/remove)
# Space before semicolon
sp_before_semi = remove # string (add/force/ignore/remove)
# Space before unnamed pointer star
sp_before_unnamed_ptr_star = force # string (add/force/ignore/remove)
## Space Between
# Space between @selector and open parenthesis
sp_after_oc_at_sel = remove # string (add/force/ignore/remove)
# Space between close brace and else
sp_brace_else = force # string (add/force/ignore/remove)
# Space between close parenthesis and open brace
sp_paren_brace = remove # string (add/force/ignore/remove)
# Space between closing brace and finally
sp_brace_finally = force # string (add/force/ignore/remove)
# Space between else and open brace
sp_else_brace = force # string (add/force/ignore/remove)
# Space between finally and open brace
sp_finally_brace = force # string (add/force/ignore/remove)
# Space between function name and empty parentheses
sp_func_call_paren_empty = remove # string (add/force/ignore/remove)
# Space between function name and open parenthesis
sp_func_call_paren = remove # string (add/force/ignore/remove)
# Space between function name and open parenthesis in function definition
sp_func_def_paren = remove # string (add/force/ignore/remove)
# Space between sizeof and open parenthesis
sp_sizeof_paren = remove # string (add/force/ignore/remove)
# Space between try and open brace
sp_try_brace = force # string (add/force/ignore/remove)
## Space Inside
# Space inside @selector() parens
sp_inside_oc_at_sel_parens = remove # string (add/force/ignore/remove)
# Space inside cast parentheses
sp_inside_paren_cast = remove # string (add/force/ignore/remove)
# Space inside function parentheses
sp_inside_fparen = remove # string (add/force/ignore/remove)
# Space inside if-condition close parenthesis
sp_inside_sparen_close = remove # string (add/force/ignore/remove)
# Space inside if-condition parentheses
sp_inside_sparen = remove # string (add/force/ignore/remove)
# Space inside parentheses
sp_inside_paren = remove # string (add/force/ignore/remove)