Compare commits

..

161 Commits

Author SHA1 Message Date
chenliming 8c2596c23e fix Third party library external introduction 2016-08-30 16:46:25 +08:00
chenliming 681b1cd107 Modified time stamp logic 2016-08-30 12:17:45 +08:00
chenliming 13094108e3 fix bug https://github.com/LaiFengiOS/LFLiveKit/issues/61 2016-08-30 11:01:24 +08:00
chenliming abd55c5511 fix bug https://github.com/LaiFengiOS/LFLiveKit/issues/67 2016-08-29 20:57:53 +08:00
chenliming fc52abe7bd fix code format 2016-08-29 18:05:37 +08:00
chenliming fa10e28281 Support sampling rate 16K
Modify 44.1K iphone6s sound problem
Add a local record sound(aac)
2016-08-29 17:58:58 +08:00
chenliming c624fbe78c iphone6 after audio 48000 2016-08-24 14:25:39 +08:00
小歪~~~ 19a355e697 Merge pull request #44 from zzgo/master
Add currentImage
2016-08-24 13:44:21 +08:00
chenliming 7799f0f613 fix bug https://github.com/LaiFengiOS/LFLiveKit/issues/60 2016-08-22 21:10:23 +08:00
chenliming ecf3b557cf fix memory leak 2016-08-19 16:01:17 +08:00
chenliming 58d83ed0dc modify capture sessionPreset(1280X720) bug set videoSize 360 640 2016-08-16 18:09:42 +08:00
小歪~~~ b167bda396 Merge pull request #54 from Michael-Owen/master
修复画面抖动模糊的问题
2016-08-16 18:06:05 +08:00
Michael 336a6e2432 修复画面抖动模糊的问题,presentationTimeStamp根据帧率计算 2016-08-16 17:18:29 +08:00
Michael 8acddcda90 修复画面抖动模糊的问题 2016-08-16 13:51:04 +08:00
chenliming cf2fc51fea add build sh 2016-08-15 20:25:41 +08:00
chenliming ae5f68661e update version 2016-08-12 21:06:42 +08:00
chenliming e30467dc13 fix videoSizeRespectingAspectRatio show error 2016-08-11 14:18:15 +08:00
chenliming a217868694 support External input video or audio ,set LFLiveCaptureTypeMask 2016-08-10 14:23:26 +08:00
小歪~~~ 3053a16cd3 Merge pull request #47 from toss156/master
fix #46 never mirrored rear facing camera
2016-08-09 17:21:32 +08:00
toss156 d2d7140312 update project file 2016-08-09 17:12:09 +08:00
toss156 e93549619e never mirror the rear facing camera 2016-08-09 16:51:49 +08:00
toss156 0196b1be79 Merge branch 'master' of https://github.com/LaiFengiOS/LFLiveKit
# By chenliming
# Via chenliming
* 'master' of https://github.com/LaiFengiOS/LFLiveKit:
  update waterMark postion and set outVideoSize
  Support output arbitrary size and fix iphone4  1920*720 crash

Conflicts:
	LFLiveKit/capture/LFVideoCapture.m
	LFLiveKitDemo/LFLiveKitDemo.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate
2016-08-05 15:33:27 +08:00
toss156 23ab30e668 Merge branch 'master' of https://github.com/LaiFengiOS/LFLiveKit
# By chenliming (35) and others
# Via GitHub (2) and chenliming (1)
* 'master' of https://github.com/LaiFengiOS/LFLiveKit: (37 commits)
  水印
  add watermark ..
  Update LFLiveKit.podspec
  change the AVAudioSession Category before start, or it will fail
  update..
  update
  update ..
  add a icon
  update read
  update readme
  update
  modify read
  modify read
  update travis 
  update read
  update version2.0
  add  iOS  encoder
  add carthage
  update version
  update version
  ...

Conflicts:
	LFLiveKit.xcodeproj/project.pbxproj
	LFLiveKit.xcworkspace/xcuserdata/feng.xcuserdatad/UserInterfaceState.xcuserstate
	LFLiveKit/LFLiveSession.m
	LFLiveKit/capture/LFVideoCapture.m
2016-08-05 15:27:08 +08:00
chenliming 42bd5c1f6b update waterMark postion and set outVideoSize 2016-08-05 14:06:11 +08:00
chenliming 41393ffed8 Support output arbitrary size and fix iphone4 1920*720 crash 2016-08-05 12:20:17 +08:00
zz 2b236b2a35 add currentImage
改currentImageView 为currentImage
2016-08-05 12:05:39 +08:00
zz e59699b7fd add currentImage
改currentImageView 为currentImage
2016-08-05 12:05:07 +08:00
zz e9f3c66a3b add currentImage
改currentImageView为currentImage
2016-08-05 12:04:14 +08:00
zz dc1dc18824 add currentImage
改currentImageView为currentImage
2016-08-05 12:03:15 +08:00
zz 014e73f44c add currentImage
改currentImageView为currentImage
2016-08-05 12:01:48 +08:00
zz ee7c25727b add currentImageView
获取当前视频截图
2016-08-05 11:36:45 +08:00
zz 79f0ba930b add currentImageView
获取当前视频截图
2016-08-05 11:34:37 +08:00
zz c5edbfba8e add currentImageView
获取当前视频截图
2016-08-05 11:32:47 +08:00
zz 91c689a41f add currentImageView
获取当前视频截图
2016-08-05 11:18:46 +08:00
chenliming ba48a45c0a 水印 2016-08-04 14:05:52 +08:00
chenliming af09e79e74 add watermark .. 2016-08-04 13:59:43 +08:00
小歪~~~ e4327de704 Merge pull request #38 from zhangyu528/master
Update LFLiveKit.podspec
2016-08-03 13:49:34 +08:00
Justin 9760129763 Update LFLiveKit.podspec
fix issue  #36 关于use_frameworks!的问题
2016-08-03 13:23:46 +08:00
小歪~~~ dc7f8d1d10 Merge pull request #37 from Guikunzhi/master
change the AVAudioSession Category before start, or it will fail
2016-08-03 12:18:24 +08:00
琨君 8f8bedb93d change the AVAudioSession Category before start, or it will fail 2016-08-03 10:41:55 +08:00
chenliming 9428dc2c7f update.. 2016-08-02 16:06:07 +08:00
chenliming e6c0d148ab update 2016-08-02 16:02:30 +08:00
chenliming 0ca5c18c19 update .. 2016-08-02 16:01:12 +08:00
chenliming cb98f0e70d add a icon 2016-08-02 15:55:45 +08:00
chenliming 06f1e40fba update read 2016-08-02 10:17:39 +08:00
chenliming dfbc4a51ff update readme 2016-08-01 19:46:18 +08:00
chenliming f045bb14e4 update 2016-08-01 17:26:00 +08:00
chenliming ebafc22eca modify read 2016-08-01 17:25:09 +08:00
chenliming 6e3e92a6b9 modify read 2016-08-01 17:23:14 +08:00
chenliming 26a47ffccc update travis 2016-08-01 17:12:42 +08:00
chenliming e57d3e3806 update read 2016-08-01 17:08:52 +08:00
chenliming b73bfee6ac update version2.0 2016-08-01 16:45:50 +08:00
chenliming 09398d0cb3 add iOS encoder 2016-08-01 16:42:02 +08:00
chenliming f69b586d70 add carthage 2016-08-01 16:19:36 +08:00
chenliming cfb84d31fb update version 2016-08-01 14:54:46 +08:00
chenliming 984bcb8e15 update version 2016-08-01 14:21:40 +08:00
chenliming d7164f8b76 update... 2016-08-01 13:52:51 +08:00
chenliming fb7ad31bf0 update.... 2016-08-01 13:49:59 +08:00
chenliming ec61b1f7ec update.... 2016-08-01 13:27:41 +08:00
chenliming ba1280af3a update... 2016-08-01 13:25:03 +08:00
chenliming 9e99357113 update。。。 2016-08-01 12:28:52 +08:00
chenliming 8057a3e013 update.. 2016-08-01 12:16:37 +08:00
chenliming 981b0dd2cd update... 2016-08-01 12:13:57 +08:00
chenliming d93d4b9bf5 support carthage 2016-08-01 11:50:05 +08:00
chenliming df6164fed1 modify podspec 2016-08-01 11:24:19 +08:00
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
toss156 edcb2efaa0 never mirrored rear facing camera 2016-07-27 10:06:17 +08:00
chenliming e47f3154a8 modify black screen bug 2016-07-26 18:27:16 +08:00
feng 3842ff80bf Merge pull request #7 from LaiFengiOS/master
update to last version
2016-07-26 18:20:15 +08:00
toss156 d3d5f6cb24 fix mirror bug: present view is different with the stream 2016-07-26 18:15:23 +08:00
chenliming 7062cdcf7c modify bug https://github.com/LaiFengiOS/LFLiveKit/issues/11#issuecomment-235147191 2016-07-26 11:20:34 +08:00
toss156 37bfd377d6 rename h264 encode lib to avoid duplicate symbol error 2016-07-26 11:16:43 +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
chenliming 91e72e2211 update 1.7.3 2016-07-15 14:32:55 +08:00
chenliming 5cc50b6d47 修改pod spec 2016-07-15 14:13:35 +08:00
chenliming 659dfb81d0 删除flv格式 只保留rtmp 2016-07-15 14:10:49 +08:00
chenliming 4865bdc508 connect error 2016-07-15 14:06:50 +08:00
chenliming e63026415a demo支持后台传输 2016-07-15 12:18:02 +08:00
chenliming 21335ed3f0 修改横竖屏接口 ,只支持横屏或者竖屏,内部实现了旋转,但暂时不支持横竖切换(因为需要服务器的支持比较复杂) 2016-07-15 12:10:31 +08:00
feng ba1f7a383c Merge pull request #4 from LaiFengiOS/master
update to last version
2016-07-15 09:29:21 +08:00
chenliming b109289bc1 添加超时时间 2016-07-14 21:55:36 +08:00
chenliming 1e56fc2941 这样改也可以哦 😄 2016-07-14 21:50:20 +08:00
chenliming 14a3b2eb4b 后台应用(例如微信视频) 回到前台没有声音 2016-07-14 21:37:08 +08:00
chenliming 510d796ab1 commit reconnect 2016-07-14 21:14:01 +08:00
chenliming 48dac15d54 update demo version 2016-07-14 20:51:16 +08:00
chenliming 13bc9ad1eb update version 2016-07-14 19:59:01 +08:00
chenliming 1e6e42f2c6 线程变为单线程 2016-07-14 19:58:26 +08:00
chenliming ed2d37e6f4 update 2016-07-14 19:39:50 +08:00
chenliming fbbf5d9da5 麦克风被占用问题 2016-07-14 19:37:36 +08:00
chenliming 8f57142e5a 修改内存bug 2016-07-14 19:28:56 +08:00
chenliming d198058ce3 修改停止直播的bug 2016-07-14 17:57:27 +08:00
chenliming 3ea70b4780 短线重连的bug 2016-07-14 16:50:44 +08:00
chenliming 8b0224626a update demo 2016-07-13 17:40:23 +08:00
chenliming 41fe9d1070 update version 2016-07-13 17:30:51 +08:00
chenliming d7b981c516 fix 内存bug 2016-07-13 17:30:17 +08:00
chenliming 09a4627886 Revert "test"
This reverts commit a140408a6a.
2016-07-13 12:19:08 +08:00
chenliming a140408a6a test 2016-07-13 12:17:23 +08:00
chenliming 3539bae752 Revert "update"
This reverts commit 40b1f7d37f.
2016-07-13 12:01:14 +08:00
chenliming 40b1f7d37f update 2016-07-13 11:58:53 +08:00
chenliming 5d9bbd2b5b update 2016-07-12 17:17:26 +08:00
feng 519b685855 Merge pull request #3 from LaiFengiOS/master
update to last version
2016-07-12 16:38:26 +08:00
chenliming 909b49c8e9 声明。。。 2016-07-12 16:25:04 +08:00
chenliming aea2fe1a7e ... 2016-07-12 16:11:12 +08:00
chenliming 3279030e13 update 2016-07-12 16:08:29 +08:00
chenliming 135910e1d0 update version 2016-07-12 16:07:49 +08:00
chenliming 5f893de671 add error callback 2016-07-12 16:05:23 +08:00
小歪~~~ 55e896a4b6 Merge pull request #8 from toss156/master
优化美颜,美颜程度可调节,  调整默认参数配置
2016-07-12 14:08:57 +08:00
toss156 759daed2bc 调整默认参数配置 2016-07-12 13:26:01 +08:00
toss156 392770247c 优化美颜,美颜程度可调节 2016-07-12 11:58:11 +08:00
feng c3d506c2f6 Merge pull request #2 from LaiFengiOS/master
update to last version
2016-07-11 15:43:59 +08:00
chenliming a60d7c504c 删除几个⚠️ 2016-07-11 12:00:52 +08:00
chenliming 9a4270ccb7 优化丢帧策略 2016-07-11 11:02:54 +08:00
chenliming 270a23218e 修改丢帧一个小bug 2016-07-07 19:45:06 +08:00
feng 8aa0091432 Merge pull request #1 from LaiFengiOS/master
update to last version
2016-06-24 09:46:39 +08:00
chenliming 037dbdef7a update version 2016-06-22 15:24:03 +08:00
小歪~~~ f91bb1c889 Merge pull request #3 from toss156/develop
添加发送metadata
2016-06-17 18:32:35 +08:00
toss156 7101ae842b fix beauty face bug 2016-06-17 17:05:51 +08:00
toss156 f5f37755df 优化美颜效果 2016-06-17 14:31:01 +08:00
toss156 43a82bffbb port to pili-librtmp 2016-06-17 09:57:01 +08:00
toss156 d7af9fb4eb 添加发送metadata 2016-06-16 16:41:51 +08:00
chenliming d25498162c add demo 2016-06-08 12:32:46 +08:00
chenliming 6f3ab33e8b update version 2016-06-08 10:56:33 +08:00
chenliming 62d0cee977 去除警告 2016-06-08 10:55:16 +08:00
chenliming ba7743d6ee 修改。。 2016-06-08 10:42:02 +08:00
chenliming dca255052b modify... 2016-06-08 10:34:44 +08:00
chenliming b51c53e93b modify read 2016-06-08 10:09:26 +08:00
chenliming 21763f5c1d modify spec 2016-06-08 10:01:31 +08:00
318 changed files with 17284 additions and 3621 deletions
+22 -1
View File
@@ -1,4 +1,25 @@
# infer
infer-out/
#CocoaPods
# Xcode
.DS_Store
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
*.xcworkspace
!default.xcworkspace
xcuserdata
profile
*.moved-aside
DerivedData
.idea/
# CocoaPods
Pods/
Podfile.lock
+2 -2
View File
@@ -1,7 +1,7 @@
language: objective-c
osx_image: xcode7
xcode_workspace: LFLiveKit.xcworkspace
xcode_project: LFLiveKit.xcodeproj
xcode_scheme: LFLiveKit
script:
- xctool -workspace LFLiveKit.xcworkspace -scheme 'LFLiveKit' -configuration Release -sdk iphonesimulator -arch i386 build
- xctool -project LFLiveKit.xcodeproj -scheme 'LFLiveKit' -configuration Release -sdk iphonesimulator -arch i386 build
+10 -13
View File
@@ -2,24 +2,21 @@
Pod::Spec.new do |s|
s.name = "LFLiveKit"
s.version = "1.4.1"
s.summary = "objectc ios Live. LFLiveKit."
s.version = "2.2"
s.summary = "LaiFeng ios Live. LFLiveKit."
s.homepage = "https://github.com/chenliming777"
s.license = { :type => "MIT", :file => "FILE_LICENSE" }
s.license = { :type => "MIT", :file => "LICENSE" }
s.author = { "chenliming" => "chenliming777@qq.com" }
s.platform = :ios, "8.0"
s.ios.deployment_target = "8.0"
s.source = { :git => "https://github.com/LaiFengiOS/LFLiveKit", :tag => "#{s.version}" }
s.source_files = "LFLiveKit/**/*.{h,m}"
s.public_header_files = "LFLiveKit/**/*.h"
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,mm,cpp,c}"
#s.public_header_files = "LFLiveKit/LFLiveKit/**/*.h"
s.public_header_files = ['LFLiveKit/LFLiveKit/*.h', 'LFLiveKit/LFLiveKit/objects/*.h', 'LFLiveKit/LFLiveKit/configuration/*.h']
s.frameworks = "VideoToolbox", "AudioToolbox","AVFoundation","Foundation","UIKit"
s.library = "z"
s.libraries = "c++", "z"
s.requires_arc = true
s.dependency "CocoaAsyncSocket", "~> 7.4.1"
s.dependency 'LMGPUImage', '~> 0.1.9'
s.dependency "librtmp-iOS", "~> 1.1.0"
end
-679
View File
@@ -1,679 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
84001F8E1D0015D10026C63F /* LFLiveKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001F8D1D0015D10026C63F /* LFLiveKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
84001F951D0015D10026C63F /* LFLiveKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001F8A1D0015D10026C63F /* LFLiveKit.framework */; };
84001F9A1D0015D10026C63F /* LFLiveKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001F991D0015D10026C63F /* LFLiveKitTests.m */; };
84001FD11D0016380026C63F /* LFAudioCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FA51D0016380026C63F /* LFAudioCapture.h */; };
84001FD21D0016380026C63F /* LFAudioCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FA61D0016380026C63F /* LFAudioCapture.m */; };
84001FD31D0016380026C63F /* LFVideoCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FA71D0016380026C63F /* LFVideoCapture.h */; };
84001FD41D0016380026C63F /* LFVideoCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FA81D0016380026C63F /* LFVideoCapture.m */; };
84001FD51D0016380026C63F /* LFAudioEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FAA1D0016380026C63F /* LFAudioEncoding.h */; };
84001FD61D0016380026C63F /* LFHardwareAudioEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FAB1D0016380026C63F /* LFHardwareAudioEncoder.h */; };
84001FD71D0016380026C63F /* LFHardwareAudioEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FAC1D0016380026C63F /* LFHardwareAudioEncoder.m */; };
84001FD81D0016380026C63F /* LFHardwareVideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FAD1D0016380026C63F /* LFHardwareVideoEncoder.h */; };
84001FD91D0016380026C63F /* LFHardwareVideoEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FAE1D0016380026C63F /* LFHardwareVideoEncoder.m */; };
84001FDA1D0016380026C63F /* LFVideoEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FAF1D0016380026C63F /* LFVideoEncoding.h */; };
84001FDB1D0016380026C63F /* LFLiveAudioConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FB11D0016380026C63F /* LFLiveAudioConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
84001FDC1D0016380026C63F /* LFLiveAudioConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FB21D0016380026C63F /* LFLiveAudioConfiguration.m */; };
84001FDD1D0016380026C63F /* LFLiveVideoConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FB31D0016380026C63F /* LFLiveVideoConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
84001FDE1D0016380026C63F /* LFLiveVideoConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FB41D0016380026C63F /* LFLiveVideoConfiguration.m */; };
84001FDF1D0016380026C63F /* LFGPUImageBeautyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FB61D0016380026C63F /* LFGPUImageBeautyFilter.h */; };
84001FE01D0016380026C63F /* LFGPUImageBeautyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FB71D0016380026C63F /* LFGPUImageBeautyFilter.m */; };
84001FE11D0016380026C63F /* LFGPUImageEmptyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FB81D0016380026C63F /* LFGPUImageEmptyFilter.h */; };
84001FE21D0016380026C63F /* LFGPUImageEmptyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FB91D0016380026C63F /* LFGPUImageEmptyFilter.m */; };
84001FE31D0016380026C63F /* LFLiveSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FBA1D0016380026C63F /* LFLiveSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
84001FE41D0016380026C63F /* LFLiveSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FBB1D0016380026C63F /* LFLiveSession.m */; };
84001FE51D0016380026C63F /* LFAudioFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FBD1D0016380026C63F /* LFAudioFrame.h */; };
84001FE61D0016380026C63F /* LFAudioFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FBE1D0016380026C63F /* LFAudioFrame.m */; };
84001FE71D0016380026C63F /* LFFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FBF1D0016380026C63F /* LFFrame.h */; };
84001FE81D0016380026C63F /* LFFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FC01D0016380026C63F /* LFFrame.m */; };
84001FE91D0016380026C63F /* LFLiveDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FC11D0016380026C63F /* LFLiveDebug.h */; settings = {ATTRIBUTES = (Public, ); }; };
84001FEA1D0016380026C63F /* LFLiveDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FC21D0016380026C63F /* LFLiveDebug.m */; };
84001FEB1D0016380026C63F /* LFLiveStreamInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FC31D0016380026C63F /* LFLiveStreamInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
84001FEC1D0016380026C63F /* LFLiveStreamInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FC41D0016380026C63F /* LFLiveStreamInfo.m */; };
84001FED1D0016380026C63F /* LFVideoFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FC51D0016380026C63F /* LFVideoFrame.h */; };
84001FEE1D0016380026C63F /* LFVideoFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FC61D0016380026C63F /* LFVideoFrame.m */; };
84001FEF1D0016380026C63F /* LFStreamingBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FCA1D0016380026C63F /* LFStreamingBuffer.h */; };
84001FF01D0016380026C63F /* LFStreamingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FCB1D0016380026C63F /* LFStreamingBuffer.m */; };
84001FF11D0016380026C63F /* LFStreamRtmpSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FCC1D0016380026C63F /* LFStreamRtmpSocket.h */; };
84001FF21D0016380026C63F /* LFStreamRtmpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FCD1D0016380026C63F /* LFStreamRtmpSocket.m */; };
84001FF31D0016380026C63F /* LFStreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FCE1D0016380026C63F /* LFStreamSocket.h */; };
84001FF41D0016380026C63F /* NSMutableArray+LFAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = 84001FCF1D0016380026C63F /* NSMutableArray+LFAdd.h */; };
84001FF51D0016380026C63F /* NSMutableArray+LFAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 84001FD01D0016380026C63F /* NSMutableArray+LFAdd.m */; };
84001FF71D0017590026C63F /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FF61D0017590026C63F /* AVFoundation.framework */; };
84001FF91D00175D0026C63F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FF81D00175D0026C63F /* Foundation.framework */; };
84001FFB1D0017630026C63F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FFA1D0017630026C63F /* UIKit.framework */; };
84001FFD1D0017680026C63F /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FFC1D0017680026C63F /* AudioToolbox.framework */; };
84001FFF1D00176C0026C63F /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84001FFE1D00176C0026C63F /* VideoToolbox.framework */; };
840020011D0017850026C63F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 840020001D0017850026C63F /* libz.tbd */; };
AD7F89B4621A7EFEBEA72D49 /* libPods-LFLiveKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
84001F961D0015D10026C63F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 84001F811D0015D10026C63F /* Project object */;
proxyType = 1;
remoteGlobalIDString = 84001F891D0015D10026C63F;
remoteInfo = LFLiveKit;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
84001F8A1D0015D10026C63F /* LFLiveKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LFLiveKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
84001F8D1D0015D10026C63F /* LFLiveKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LFLiveKit.h; sourceTree = "<group>"; };
84001F8F1D0015D10026C63F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
84001F941D0015D10026C63F /* LFLiveKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LFLiveKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
84001F991D0015D10026C63F /* LFLiveKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LFLiveKitTests.m; sourceTree = "<group>"; };
84001F9B1D0015D10026C63F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
84001FA51D0016380026C63F /* LFAudioCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioCapture.h; sourceTree = "<group>"; };
84001FA61D0016380026C63F /* LFAudioCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFAudioCapture.m; sourceTree = "<group>"; };
84001FA71D0016380026C63F /* LFVideoCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoCapture.h; sourceTree = "<group>"; };
84001FA81D0016380026C63F /* LFVideoCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoCapture.m; sourceTree = "<group>"; };
84001FAA1D0016380026C63F /* LFAudioEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioEncoding.h; sourceTree = "<group>"; };
84001FAB1D0016380026C63F /* LFHardwareAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHardwareAudioEncoder.h; sourceTree = "<group>"; };
84001FAC1D0016380026C63F /* LFHardwareAudioEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHardwareAudioEncoder.m; sourceTree = "<group>"; };
84001FAD1D0016380026C63F /* LFHardwareVideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHardwareVideoEncoder.h; sourceTree = "<group>"; };
84001FAE1D0016380026C63F /* LFHardwareVideoEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHardwareVideoEncoder.m; sourceTree = "<group>"; };
84001FAF1D0016380026C63F /* LFVideoEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoEncoding.h; sourceTree = "<group>"; };
84001FB11D0016380026C63F /* LFLiveAudioConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveAudioConfiguration.h; sourceTree = "<group>"; };
84001FB21D0016380026C63F /* LFLiveAudioConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveAudioConfiguration.m; sourceTree = "<group>"; };
84001FB31D0016380026C63F /* LFLiveVideoConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveVideoConfiguration.h; sourceTree = "<group>"; };
84001FB41D0016380026C63F /* LFLiveVideoConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveVideoConfiguration.m; sourceTree = "<group>"; };
84001FB61D0016380026C63F /* LFGPUImageBeautyFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFGPUImageBeautyFilter.h; sourceTree = "<group>"; };
84001FB71D0016380026C63F /* LFGPUImageBeautyFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFGPUImageBeautyFilter.m; sourceTree = "<group>"; };
84001FB81D0016380026C63F /* LFGPUImageEmptyFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFGPUImageEmptyFilter.h; sourceTree = "<group>"; };
84001FB91D0016380026C63F /* LFGPUImageEmptyFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFGPUImageEmptyFilter.m; sourceTree = "<group>"; };
84001FBA1D0016380026C63F /* LFLiveSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveSession.h; sourceTree = "<group>"; };
84001FBB1D0016380026C63F /* LFLiveSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveSession.m; sourceTree = "<group>"; };
84001FBD1D0016380026C63F /* LFAudioFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioFrame.h; sourceTree = "<group>"; };
84001FBE1D0016380026C63F /* LFAudioFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFAudioFrame.m; sourceTree = "<group>"; };
84001FBF1D0016380026C63F /* LFFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFFrame.h; sourceTree = "<group>"; };
84001FC01D0016380026C63F /* LFFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFFrame.m; sourceTree = "<group>"; };
84001FC11D0016380026C63F /* LFLiveDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveDebug.h; sourceTree = "<group>"; };
84001FC21D0016380026C63F /* LFLiveDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveDebug.m; sourceTree = "<group>"; };
84001FC31D0016380026C63F /* LFLiveStreamInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveStreamInfo.h; sourceTree = "<group>"; };
84001FC41D0016380026C63F /* LFLiveStreamInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveStreamInfo.m; sourceTree = "<group>"; };
84001FC51D0016380026C63F /* LFVideoFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoFrame.h; sourceTree = "<group>"; };
84001FC61D0016380026C63F /* LFVideoFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoFrame.m; sourceTree = "<group>"; };
84001FCA1D0016380026C63F /* LFStreamingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamingBuffer.h; sourceTree = "<group>"; };
84001FCB1D0016380026C63F /* LFStreamingBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamingBuffer.m; sourceTree = "<group>"; };
84001FCC1D0016380026C63F /* LFStreamRtmpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamRtmpSocket.h; sourceTree = "<group>"; };
84001FCD1D0016380026C63F /* LFStreamRtmpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamRtmpSocket.m; sourceTree = "<group>"; };
84001FCE1D0016380026C63F /* LFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamSocket.h; sourceTree = "<group>"; };
84001FCF1D0016380026C63F /* NSMutableArray+LFAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+LFAdd.h"; sourceTree = "<group>"; };
84001FD01D0016380026C63F /* NSMutableArray+LFAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+LFAdd.m"; sourceTree = "<group>"; };
84001FF61D0017590026C63F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
84001FF81D00175D0026C63F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
84001FFA1D0017630026C63F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
84001FFC1D0017680026C63F /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
84001FFE1D00176C0026C63F /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
840020001D0017850026C63F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
A17586B27CD6843997425CCF /* Pods-LFLiveKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LFLiveKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit.debug.xcconfig"; sourceTree = "<group>"; };
B75B965E6B94DE4CBCC82EA7 /* Pods-LFLiveKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LFLiveKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit.release.xcconfig"; sourceTree = "<group>"; };
B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LFLiveKit.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
84001F861D0015D10026C63F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
840020011D0017850026C63F /* libz.tbd in Frameworks */,
84001FFF1D00176C0026C63F /* VideoToolbox.framework in Frameworks */,
84001FFD1D0017680026C63F /* AudioToolbox.framework in Frameworks */,
84001FFB1D0017630026C63F /* UIKit.framework in Frameworks */,
84001FF91D00175D0026C63F /* Foundation.framework in Frameworks */,
84001FF71D0017590026C63F /* AVFoundation.framework in Frameworks */,
AD7F89B4621A7EFEBEA72D49 /* libPods-LFLiveKit.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
84001F911D0015D10026C63F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
84001F951D0015D10026C63F /* LFLiveKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0C07D14560B9E91EA1B59306 /* Frameworks */ = {
isa = PBXGroup;
children = (
840020001D0017850026C63F /* libz.tbd */,
84001FFE1D00176C0026C63F /* VideoToolbox.framework */,
84001FFC1D0017680026C63F /* AudioToolbox.framework */,
84001FFA1D0017630026C63F /* UIKit.framework */,
84001FF81D00175D0026C63F /* Foundation.framework */,
84001FF61D0017590026C63F /* AVFoundation.framework */,
B8CB02D2A92EA1F5A262F154 /* libPods-LFLiveKit.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
84001F801D0015D10026C63F = {
isa = PBXGroup;
children = (
84001F8C1D0015D10026C63F /* LFLiveKit */,
84001F981D0015D10026C63F /* LFLiveKitTests */,
84001F8B1D0015D10026C63F /* Products */,
EDD4B76A07A6817C79BB4E5C /* Pods */,
0C07D14560B9E91EA1B59306 /* Frameworks */,
);
sourceTree = "<group>";
};
84001F8B1D0015D10026C63F /* Products */ = {
isa = PBXGroup;
children = (
84001F8A1D0015D10026C63F /* LFLiveKit.framework */,
84001F941D0015D10026C63F /* LFLiveKitTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
84001F8C1D0015D10026C63F /* LFLiveKit */ = {
isa = PBXGroup;
children = (
84001F8D1D0015D10026C63F /* LFLiveKit.h */,
84001FBA1D0016380026C63F /* LFLiveSession.h */,
84001FBB1D0016380026C63F /* LFLiveSession.m */,
84001FBC1D0016380026C63F /* objects */,
84001FB01D0016380026C63F /* configuration */,
84001FA41D0016380026C63F /* capture */,
84001FA91D0016380026C63F /* coder */,
84001FB51D0016380026C63F /* filter */,
84001FC91D0016380026C63F /* publish */,
84001F8F1D0015D10026C63F /* Info.plist */,
);
path = LFLiveKit;
sourceTree = "<group>";
};
84001F981D0015D10026C63F /* LFLiveKitTests */ = {
isa = PBXGroup;
children = (
84001F991D0015D10026C63F /* LFLiveKitTests.m */,
84001F9B1D0015D10026C63F /* Info.plist */,
);
path = LFLiveKitTests;
sourceTree = "<group>";
};
84001FA41D0016380026C63F /* capture */ = {
isa = PBXGroup;
children = (
84001FA51D0016380026C63F /* LFAudioCapture.h */,
84001FA61D0016380026C63F /* LFAudioCapture.m */,
84001FA71D0016380026C63F /* LFVideoCapture.h */,
84001FA81D0016380026C63F /* LFVideoCapture.m */,
);
path = capture;
sourceTree = "<group>";
};
84001FA91D0016380026C63F /* coder */ = {
isa = PBXGroup;
children = (
84001FAA1D0016380026C63F /* LFAudioEncoding.h */,
84001FAB1D0016380026C63F /* LFHardwareAudioEncoder.h */,
84001FAC1D0016380026C63F /* LFHardwareAudioEncoder.m */,
84001FAD1D0016380026C63F /* LFHardwareVideoEncoder.h */,
84001FAE1D0016380026C63F /* LFHardwareVideoEncoder.m */,
84001FAF1D0016380026C63F /* LFVideoEncoding.h */,
);
path = coder;
sourceTree = "<group>";
};
84001FB01D0016380026C63F /* configuration */ = {
isa = PBXGroup;
children = (
84001FB11D0016380026C63F /* LFLiveAudioConfiguration.h */,
84001FB21D0016380026C63F /* LFLiveAudioConfiguration.m */,
84001FB31D0016380026C63F /* LFLiveVideoConfiguration.h */,
84001FB41D0016380026C63F /* LFLiveVideoConfiguration.m */,
);
path = configuration;
sourceTree = "<group>";
};
84001FB51D0016380026C63F /* filter */ = {
isa = PBXGroup;
children = (
84001FB61D0016380026C63F /* LFGPUImageBeautyFilter.h */,
84001FB71D0016380026C63F /* LFGPUImageBeautyFilter.m */,
84001FB81D0016380026C63F /* LFGPUImageEmptyFilter.h */,
84001FB91D0016380026C63F /* LFGPUImageEmptyFilter.m */,
);
path = filter;
sourceTree = "<group>";
};
84001FBC1D0016380026C63F /* objects */ = {
isa = PBXGroup;
children = (
84001FBD1D0016380026C63F /* LFAudioFrame.h */,
84001FBE1D0016380026C63F /* LFAudioFrame.m */,
84001FBF1D0016380026C63F /* LFFrame.h */,
84001FC01D0016380026C63F /* LFFrame.m */,
84001FC11D0016380026C63F /* LFLiveDebug.h */,
84001FC21D0016380026C63F /* LFLiveDebug.m */,
84001FC31D0016380026C63F /* LFLiveStreamInfo.h */,
84001FC41D0016380026C63F /* LFLiveStreamInfo.m */,
84001FC51D0016380026C63F /* LFVideoFrame.h */,
84001FC61D0016380026C63F /* LFVideoFrame.m */,
);
path = objects;
sourceTree = "<group>";
};
84001FC91D0016380026C63F /* publish */ = {
isa = PBXGroup;
children = (
84001FCE1D0016380026C63F /* LFStreamSocket.h */,
84001FCA1D0016380026C63F /* LFStreamingBuffer.h */,
84001FCB1D0016380026C63F /* LFStreamingBuffer.m */,
84001FCC1D0016380026C63F /* LFStreamRtmpSocket.h */,
84001FCD1D0016380026C63F /* LFStreamRtmpSocket.m */,
84001FCF1D0016380026C63F /* NSMutableArray+LFAdd.h */,
84001FD01D0016380026C63F /* NSMutableArray+LFAdd.m */,
);
name = publish;
path = upload;
sourceTree = "<group>";
};
EDD4B76A07A6817C79BB4E5C /* Pods */ = {
isa = PBXGroup;
children = (
A17586B27CD6843997425CCF /* Pods-LFLiveKit.debug.xcconfig */,
B75B965E6B94DE4CBCC82EA7 /* Pods-LFLiveKit.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
84001F871D0015D10026C63F /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
84001FDB1D0016380026C63F /* LFLiveAudioConfiguration.h in Headers */,
84001FDD1D0016380026C63F /* LFLiveVideoConfiguration.h in Headers */,
84001FE31D0016380026C63F /* LFLiveSession.h in Headers */,
84001FEB1D0016380026C63F /* LFLiveStreamInfo.h in Headers */,
84001FE91D0016380026C63F /* LFLiveDebug.h in Headers */,
84001FF11D0016380026C63F /* LFStreamRtmpSocket.h in Headers */,
84001FE71D0016380026C63F /* LFFrame.h in Headers */,
84001FEF1D0016380026C63F /* LFStreamingBuffer.h in Headers */,
84001FD61D0016380026C63F /* LFHardwareAudioEncoder.h in Headers */,
84001FDF1D0016380026C63F /* LFGPUImageBeautyFilter.h in Headers */,
84001FD31D0016380026C63F /* LFVideoCapture.h in Headers */,
84001FD11D0016380026C63F /* LFAudioCapture.h in Headers */,
84001FF41D0016380026C63F /* NSMutableArray+LFAdd.h in Headers */,
84001FE11D0016380026C63F /* LFGPUImageEmptyFilter.h in Headers */,
84001FDA1D0016380026C63F /* LFVideoEncoding.h in Headers */,
84001FE51D0016380026C63F /* LFAudioFrame.h in Headers */,
84001FED1D0016380026C63F /* LFVideoFrame.h in Headers */,
84001FD81D0016380026C63F /* LFHardwareVideoEncoder.h in Headers */,
84001FD51D0016380026C63F /* LFAudioEncoding.h in Headers */,
84001FF31D0016380026C63F /* LFStreamSocket.h in Headers */,
84001F8E1D0015D10026C63F /* LFLiveKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
84001F891D0015D10026C63F /* LFLiveKit */ = {
isa = PBXNativeTarget;
buildConfigurationList = 84001F9E1D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKit" */;
buildPhases = (
8EE9401DCA9508E918B7FB68 /* 📦 Check Pods Manifest.lock */,
84001F851D0015D10026C63F /* Sources */,
84001F861D0015D10026C63F /* Frameworks */,
84001F871D0015D10026C63F /* Headers */,
84001F881D0015D10026C63F /* Resources */,
817C22141AD3F2EB34365AA3 /* 📦 Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = LFLiveKit;
productName = LFLiveKit;
productReference = 84001F8A1D0015D10026C63F /* LFLiveKit.framework */;
productType = "com.apple.product-type.framework";
};
84001F931D0015D10026C63F /* LFLiveKitTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 84001FA11D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKitTests" */;
buildPhases = (
84001F901D0015D10026C63F /* Sources */,
84001F911D0015D10026C63F /* Frameworks */,
84001F921D0015D10026C63F /* Resources */,
);
buildRules = (
);
dependencies = (
84001F971D0015D10026C63F /* PBXTargetDependency */,
);
name = LFLiveKitTests;
productName = LFLiveKitTests;
productReference = 84001F941D0015D10026C63F /* LFLiveKitTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
84001F811D0015D10026C63F /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = admin;
TargetAttributes = {
84001F891D0015D10026C63F = {
CreatedOnToolsVersion = 7.3;
};
84001F931D0015D10026C63F = {
CreatedOnToolsVersion = 7.3;
};
};
};
buildConfigurationList = 84001F841D0015D10026C63F /* Build configuration list for PBXProject "LFLiveKit" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 84001F801D0015D10026C63F;
productRefGroup = 84001F8B1D0015D10026C63F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
84001F891D0015D10026C63F /* LFLiveKit */,
84001F931D0015D10026C63F /* LFLiveKitTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
84001F881D0015D10026C63F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
84001F921D0015D10026C63F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
817C22141AD3F2EB34365AA3 /* 📦 Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LFLiveKit/Pods-LFLiveKit-resources.sh\"\n";
showEnvVarsInLog = 0;
};
8EE9401DCA9508E918B7FB68 /* 📦 Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
84001F851D0015D10026C63F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
84001FE21D0016380026C63F /* LFGPUImageEmptyFilter.m in Sources */,
84001FE41D0016380026C63F /* LFLiveSession.m in Sources */,
84001FE61D0016380026C63F /* LFAudioFrame.m in Sources */,
84001FDC1D0016380026C63F /* LFLiveAudioConfiguration.m in Sources */,
84001FD41D0016380026C63F /* LFVideoCapture.m in Sources */,
84001FE81D0016380026C63F /* LFFrame.m in Sources */,
84001FF01D0016380026C63F /* LFStreamingBuffer.m in Sources */,
84001FF51D0016380026C63F /* NSMutableArray+LFAdd.m in Sources */,
84001FDE1D0016380026C63F /* LFLiveVideoConfiguration.m in Sources */,
84001FD21D0016380026C63F /* LFAudioCapture.m in Sources */,
84001FF21D0016380026C63F /* LFStreamRtmpSocket.m in Sources */,
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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
84001F901D0015D10026C63F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
84001F9A1D0015D10026C63F /* LFLiveKitTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
84001F971D0015D10026C63F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 84001F891D0015D10026C63F /* LFLiveKit */;
targetProxy = 84001F961D0015D10026C63F /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
84001F9C1D0015D10026C63F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
84001F9D1D0015D10026C63F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
84001F9F1D0015D10026C63F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A17586B27CD6843997425CCF /* Pods-LFLiveKit.debug.xcconfig */;
buildSettings = {
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = LFLiveKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
84001FA01D0015D10026C63F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B75B965E6B94DE4CBCC82EA7 /* Pods-LFLiveKit.release.xcconfig */;
buildSettings = {
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = LFLiveKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
84001FA21D0015D10026C63F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = LFLiveKitTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
84001FA31D0015D10026C63F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = LFLiveKitTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit.LFLiveKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
84001F841D0015D10026C63F /* Build configuration list for PBXProject "LFLiveKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
84001F9C1D0015D10026C63F /* Debug */,
84001F9D1D0015D10026C63F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
84001F9E1D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
84001F9F1D0015D10026C63F /* Debug */,
84001FA01D0015D10026C63F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
84001FA11D0015D10026C63F /* Build configuration list for PBXNativeTarget "LFLiveKitTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
84001FA21D0015D10026C63F /* Debug */,
84001FA31D0015D10026C63F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 84001F811D0015D10026C63F /* Project object */;
}
@@ -1,155 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "48F687CDE9990BD471D7883061F7E5D4"
BuildableName = "libCocoaAsyncSocket.a"
BlueprintName = "CocoaAsyncSocket"
ReferencedContainer = "container:Pods/Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6610362E50A6DE8BACCA1F2885CD9157"
BuildableName = "libGPUImage.a"
BlueprintName = "GPUImage"
ReferencedContainer = "container:Pods/Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5CFA1E6A87A325722DE7DCED3A58CCA1"
BuildableName = "libPods-LFLiveKit.a"
BlueprintName = "Pods-LFLiveKit"
ReferencedContainer = "container:Pods/Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84001F891D0015D10026C63F"
BuildableName = "LFLiveKit.framework"
BlueprintName = "LFLiveKit"
ReferencedContainer = "container:LFLiveKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84001F931D0015D10026C63F"
BuildableName = "LFLiveKitTests.xctest"
BlueprintName = "LFLiveKitTests"
ReferencedContainer = "container:LFLiveKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84001F931D0015D10026C63F"
BuildableName = "LFLiveKitTests.xctest"
BlueprintName = "LFLiveKitTests"
ReferencedContainer = "container:LFLiveKit.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84001F891D0015D10026C63F"
BuildableName = "LFLiveKit.framework"
BlueprintName = "LFLiveKit"
ReferencedContainer = "container:LFLiveKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84001F891D0015D10026C63F"
BuildableName = "LFLiveKit.framework"
BlueprintName = "LFLiveKit"
ReferencedContainer = "container:LFLiveKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84001F891D0015D10026C63F"
BuildableName = "LFLiveKit.framework"
BlueprintName = "LFLiveKit"
ReferencedContainer = "container:LFLiveKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:LFLiveKit.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
@@ -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.4.1</string>
<string>2.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
-15
View File
@@ -1,15 +0,0 @@
//
// LFLiveKit.h
// LFLiveKit
//
// Created by admin on 16/5/24.
// Copyright © 2016年 admin. All rights reserved.
//
#import "LFLiveSession.h"
#import "LFLiveAudioConfiguration.h"
#import "LFLiveVideoConfiguration.h"
#import "LFAudioFrame.h"
#import "LFFrame.h"
#import "LFLiveStreamInfo.h"
#import "LFVideoFrame.h"
@@ -0,0 +1,565 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
84D8B3D21D75753300752B56 /* LFAudioCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B39C1D75753300752B56 /* LFAudioCapture.h */; };
84D8B3D31D75753300752B56 /* LFAudioCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B39D1D75753300752B56 /* LFAudioCapture.m */; };
84D8B3D41D75753300752B56 /* LFVideoCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B39E1D75753300752B56 /* LFVideoCapture.h */; };
84D8B3D51D75753300752B56 /* LFVideoCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B39F1D75753300752B56 /* LFVideoCapture.m */; };
84D8B3D61D75753300752B56 /* LFAVEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3A21D75753300752B56 /* LFAVEncoder.h */; };
84D8B3D71D75753300752B56 /* LFAVEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3A31D75753300752B56 /* LFAVEncoder.mm */; };
84D8B3D81D75753300752B56 /* LFMP4Atom.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3A41D75753300752B56 /* LFMP4Atom.h */; };
84D8B3D91D75753300752B56 /* LFMP4Atom.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3A51D75753300752B56 /* LFMP4Atom.m */; };
84D8B3DA1D75753300752B56 /* LFNALUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3A61D75753300752B56 /* LFNALUnit.cpp */; };
84D8B3DB1D75753300752B56 /* LFNALUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3A71D75753300752B56 /* LFNALUnit.h */; };
84D8B3DC1D75753300752B56 /* LFVideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3A81D75753300752B56 /* LFVideoEncoder.h */; };
84D8B3DD1D75753300752B56 /* LFVideoEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3A91D75753300752B56 /* LFVideoEncoder.m */; };
84D8B3DE1D75753300752B56 /* LFAudioEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3AA1D75753300752B56 /* LFAudioEncoding.h */; };
84D8B3DF1D75753300752B56 /* LFH264VideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3AB1D75753300752B56 /* LFH264VideoEncoder.h */; };
84D8B3E01D75753300752B56 /* LFH264VideoEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3AC1D75753300752B56 /* LFH264VideoEncoder.mm */; };
84D8B3E11D75753300752B56 /* LFHardwareAudioEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3AD1D75753300752B56 /* LFHardwareAudioEncoder.h */; };
84D8B3E21D75753300752B56 /* LFHardwareAudioEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3AE1D75753300752B56 /* LFHardwareAudioEncoder.m */; };
84D8B3E31D75753300752B56 /* LFHardwareVideoEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3AF1D75753300752B56 /* LFHardwareVideoEncoder.h */; };
84D8B3E41D75753300752B56 /* LFHardwareVideoEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3B01D75753300752B56 /* LFHardwareVideoEncoder.m */; };
84D8B3E51D75753300752B56 /* LFVideoEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3B11D75753300752B56 /* LFVideoEncoding.h */; };
84D8B3E61D75753300752B56 /* LFLiveAudioConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3B31D75753300752B56 /* LFLiveAudioConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3E71D75753300752B56 /* LFLiveAudioConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3B41D75753300752B56 /* LFLiveAudioConfiguration.m */; };
84D8B3E81D75753300752B56 /* LFLiveVideoConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3B51D75753300752B56 /* LFLiveVideoConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3E91D75753300752B56 /* LFLiveVideoConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3B61D75753300752B56 /* LFLiveVideoConfiguration.m */; };
84D8B3EA1D75753300752B56 /* LFGPUImageBeautyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3B81D75753300752B56 /* LFGPUImageBeautyFilter.h */; };
84D8B3EB1D75753300752B56 /* LFGPUImageBeautyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3B91D75753300752B56 /* LFGPUImageBeautyFilter.m */; };
84D8B3EC1D75753300752B56 /* LFGPUImageEmptyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3BA1D75753300752B56 /* LFGPUImageEmptyFilter.h */; };
84D8B3ED1D75753300752B56 /* LFGPUImageEmptyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3BB1D75753300752B56 /* LFGPUImageEmptyFilter.m */; };
84D8B3EE1D75753300752B56 /* LFLiveKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3BC1D75753300752B56 /* LFLiveKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3EF1D75753300752B56 /* LFLiveSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3BD1D75753300752B56 /* LFLiveSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3F01D75753300752B56 /* LFLiveSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3BE1D75753300752B56 /* LFLiveSession.m */; };
84D8B3F11D75753300752B56 /* LFAudioFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C01D75753300752B56 /* LFAudioFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3F21D75753300752B56 /* LFAudioFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C11D75753300752B56 /* LFAudioFrame.m */; };
84D8B3F31D75753300752B56 /* LFFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C21D75753300752B56 /* LFFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3F41D75753300752B56 /* LFFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C31D75753300752B56 /* LFFrame.m */; };
84D8B3F51D75753300752B56 /* LFLiveDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C41D75753300752B56 /* LFLiveDebug.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3F61D75753300752B56 /* LFLiveDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C51D75753300752B56 /* LFLiveDebug.m */; };
84D8B3F71D75753300752B56 /* LFLiveStreamInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C61D75753300752B56 /* LFLiveStreamInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3F81D75753300752B56 /* LFLiveStreamInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C71D75753300752B56 /* LFLiveStreamInfo.m */; };
84D8B3F91D75753300752B56 /* LFVideoFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3C81D75753300752B56 /* LFVideoFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D8B3FA1D75753300752B56 /* LFVideoFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3C91D75753300752B56 /* LFVideoFrame.m */; };
84D8B3FB1D75753300752B56 /* LFStreamingBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3CB1D75753300752B56 /* LFStreamingBuffer.h */; };
84D8B3FC1D75753300752B56 /* LFStreamingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3CC1D75753300752B56 /* LFStreamingBuffer.m */; };
84D8B3FD1D75753300752B56 /* LFStreamRTMPSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3CD1D75753300752B56 /* LFStreamRTMPSocket.h */; };
84D8B3FE1D75753300752B56 /* LFStreamRtmpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3CE1D75753300752B56 /* LFStreamRtmpSocket.m */; };
84D8B3FF1D75753300752B56 /* LFStreamSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3CF1D75753300752B56 /* LFStreamSocket.h */; };
84D8B4001D75753300752B56 /* NSMutableArray+LFAdd.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D8B3D01D75753300752B56 /* NSMutableArray+LFAdd.h */; };
84D8B4011D75753300752B56 /* NSMutableArray+LFAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B3D11D75753300752B56 /* NSMutableArray+LFAdd.m */; };
84D8B4BF1D757EB800752B56 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4BE1D757EB800752B56 /* VideoToolbox.framework */; };
84D8B4C11D757EBE00752B56 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4C01D757EBE00752B56 /* AudioToolbox.framework */; };
84D8B4C31D757EC400752B56 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4C21D757EC400752B56 /* AVFoundation.framework */; };
84D8B4C51D757EC800752B56 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4C41D757EC800752B56 /* Foundation.framework */; };
84D8B4C71D757ECC00752B56 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4C61D757ECC00752B56 /* UIKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
84D8B3901D7574D600752B56 /* LFLiveKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LFLiveKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
84D8B39C1D75753300752B56 /* LFAudioCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioCapture.h; sourceTree = "<group>"; };
84D8B39D1D75753300752B56 /* LFAudioCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFAudioCapture.m; sourceTree = "<group>"; };
84D8B39E1D75753300752B56 /* LFVideoCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoCapture.h; sourceTree = "<group>"; };
84D8B39F1D75753300752B56 /* LFVideoCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoCapture.m; sourceTree = "<group>"; };
84D8B3A21D75753300752B56 /* LFAVEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAVEncoder.h; sourceTree = "<group>"; };
84D8B3A31D75753300752B56 /* LFAVEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LFAVEncoder.mm; sourceTree = "<group>"; };
84D8B3A41D75753300752B56 /* LFMP4Atom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFMP4Atom.h; sourceTree = "<group>"; };
84D8B3A51D75753300752B56 /* LFMP4Atom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFMP4Atom.m; sourceTree = "<group>"; };
84D8B3A61D75753300752B56 /* LFNALUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LFNALUnit.cpp; sourceTree = "<group>"; };
84D8B3A71D75753300752B56 /* LFNALUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFNALUnit.h; sourceTree = "<group>"; };
84D8B3A81D75753300752B56 /* LFVideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoEncoder.h; sourceTree = "<group>"; };
84D8B3A91D75753300752B56 /* LFVideoEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoEncoder.m; sourceTree = "<group>"; };
84D8B3AA1D75753300752B56 /* LFAudioEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioEncoding.h; sourceTree = "<group>"; };
84D8B3AB1D75753300752B56 /* LFH264VideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFH264VideoEncoder.h; sourceTree = "<group>"; };
84D8B3AC1D75753300752B56 /* LFH264VideoEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LFH264VideoEncoder.mm; sourceTree = "<group>"; };
84D8B3AD1D75753300752B56 /* LFHardwareAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHardwareAudioEncoder.h; sourceTree = "<group>"; };
84D8B3AE1D75753300752B56 /* LFHardwareAudioEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHardwareAudioEncoder.m; sourceTree = "<group>"; };
84D8B3AF1D75753300752B56 /* LFHardwareVideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHardwareVideoEncoder.h; sourceTree = "<group>"; };
84D8B3B01D75753300752B56 /* LFHardwareVideoEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHardwareVideoEncoder.m; sourceTree = "<group>"; };
84D8B3B11D75753300752B56 /* LFVideoEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoEncoding.h; sourceTree = "<group>"; };
84D8B3B31D75753300752B56 /* LFLiveAudioConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveAudioConfiguration.h; sourceTree = "<group>"; };
84D8B3B41D75753300752B56 /* LFLiveAudioConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveAudioConfiguration.m; sourceTree = "<group>"; };
84D8B3B51D75753300752B56 /* LFLiveVideoConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveVideoConfiguration.h; sourceTree = "<group>"; };
84D8B3B61D75753300752B56 /* LFLiveVideoConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveVideoConfiguration.m; sourceTree = "<group>"; };
84D8B3B81D75753300752B56 /* LFGPUImageBeautyFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFGPUImageBeautyFilter.h; sourceTree = "<group>"; };
84D8B3B91D75753300752B56 /* LFGPUImageBeautyFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFGPUImageBeautyFilter.m; sourceTree = "<group>"; };
84D8B3BA1D75753300752B56 /* LFGPUImageEmptyFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFGPUImageEmptyFilter.h; sourceTree = "<group>"; };
84D8B3BB1D75753300752B56 /* LFGPUImageEmptyFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFGPUImageEmptyFilter.m; sourceTree = "<group>"; };
84D8B3BC1D75753300752B56 /* LFLiveKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveKit.h; sourceTree = "<group>"; };
84D8B3BD1D75753300752B56 /* LFLiveSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveSession.h; sourceTree = "<group>"; };
84D8B3BE1D75753300752B56 /* LFLiveSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveSession.m; sourceTree = "<group>"; };
84D8B3C01D75753300752B56 /* LFAudioFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFAudioFrame.h; sourceTree = "<group>"; };
84D8B3C11D75753300752B56 /* LFAudioFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFAudioFrame.m; sourceTree = "<group>"; };
84D8B3C21D75753300752B56 /* LFFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFFrame.h; sourceTree = "<group>"; };
84D8B3C31D75753300752B56 /* LFFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFFrame.m; sourceTree = "<group>"; };
84D8B3C41D75753300752B56 /* LFLiveDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveDebug.h; sourceTree = "<group>"; };
84D8B3C51D75753300752B56 /* LFLiveDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveDebug.m; sourceTree = "<group>"; };
84D8B3C61D75753300752B56 /* LFLiveStreamInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLiveStreamInfo.h; sourceTree = "<group>"; };
84D8B3C71D75753300752B56 /* LFLiveStreamInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLiveStreamInfo.m; sourceTree = "<group>"; };
84D8B3C81D75753300752B56 /* LFVideoFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFVideoFrame.h; sourceTree = "<group>"; };
84D8B3C91D75753300752B56 /* LFVideoFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFVideoFrame.m; sourceTree = "<group>"; };
84D8B3CB1D75753300752B56 /* LFStreamingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamingBuffer.h; sourceTree = "<group>"; };
84D8B3CC1D75753300752B56 /* LFStreamingBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamingBuffer.m; sourceTree = "<group>"; };
84D8B3CD1D75753300752B56 /* LFStreamRTMPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamRTMPSocket.h; sourceTree = "<group>"; };
84D8B3CE1D75753300752B56 /* LFStreamRtmpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFStreamRtmpSocket.m; sourceTree = "<group>"; };
84D8B3CF1D75753300752B56 /* LFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFStreamSocket.h; sourceTree = "<group>"; };
84D8B3D01D75753300752B56 /* NSMutableArray+LFAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+LFAdd.h"; sourceTree = "<group>"; };
84D8B3D11D75753300752B56 /* NSMutableArray+LFAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+LFAdd.m"; sourceTree = "<group>"; };
84D8B4BE1D757EB800752B56 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
84D8B4C01D757EBE00752B56 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
84D8B4C21D757EC400752B56 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
84D8B4C41D757EC800752B56 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
84D8B4C61D757ECC00752B56 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
84D8B4C81D757ED100752B56 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
84D8B4CA1D757ED600752B56 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
84D8B38C1D7574D600752B56 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
84D8B4C71D757ECC00752B56 /* UIKit.framework in Frameworks */,
84D8B4C51D757EC800752B56 /* Foundation.framework in Frameworks */,
84D8B4C31D757EC400752B56 /* AVFoundation.framework in Frameworks */,
84D8B4C11D757EBE00752B56 /* AudioToolbox.framework in Frameworks */,
84D8B4BF1D757EB800752B56 /* VideoToolbox.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
84D8B3861D7574D600752B56 = {
isa = PBXGroup;
children = (
84D8B4CA1D757ED600752B56 /* libz.tbd */,
84D8B4C81D757ED100752B56 /* libstdc++.tbd */,
84D8B4C61D757ECC00752B56 /* UIKit.framework */,
84D8B4C41D757EC800752B56 /* Foundation.framework */,
84D8B4C21D757EC400752B56 /* AVFoundation.framework */,
84D8B4C01D757EBE00752B56 /* AudioToolbox.framework */,
84D8B4BE1D757EB800752B56 /* VideoToolbox.framework */,
84D8B3921D7574D600752B56 /* LFLiveKit */,
84D8B3911D7574D600752B56 /* Products */,
);
sourceTree = "<group>";
};
84D8B3911D7574D600752B56 /* Products */ = {
isa = PBXGroup;
children = (
84D8B3901D7574D600752B56 /* LFLiveKit.framework */,
);
name = Products;
sourceTree = "<group>";
};
84D8B3921D7574D600752B56 /* LFLiveKit */ = {
isa = PBXGroup;
children = (
84D8B39B1D75753300752B56 /* capture */,
84D8B3A01D75753300752B56 /* coder */,
84D8B3B21D75753300752B56 /* configuration */,
84D8B3B71D75753300752B56 /* filter */,
84D8B3BC1D75753300752B56 /* LFLiveKit.h */,
84D8B3BD1D75753300752B56 /* LFLiveSession.h */,
84D8B3BE1D75753300752B56 /* LFLiveSession.m */,
84D8B3BF1D75753300752B56 /* objects */,
84D8B3CA1D75753300752B56 /* publish */,
);
path = LFLiveKit;
sourceTree = "<group>";
};
84D8B39B1D75753300752B56 /* capture */ = {
isa = PBXGroup;
children = (
84D8B39C1D75753300752B56 /* LFAudioCapture.h */,
84D8B39D1D75753300752B56 /* LFAudioCapture.m */,
84D8B39E1D75753300752B56 /* LFVideoCapture.h */,
84D8B39F1D75753300752B56 /* LFVideoCapture.m */,
);
path = capture;
sourceTree = "<group>";
};
84D8B3A01D75753300752B56 /* coder */ = {
isa = PBXGroup;
children = (
84D8B3A11D75753300752B56 /* H264 */,
84D8B3AA1D75753300752B56 /* LFAudioEncoding.h */,
84D8B3AB1D75753300752B56 /* LFH264VideoEncoder.h */,
84D8B3AC1D75753300752B56 /* LFH264VideoEncoder.mm */,
84D8B3AD1D75753300752B56 /* LFHardwareAudioEncoder.h */,
84D8B3AE1D75753300752B56 /* LFHardwareAudioEncoder.m */,
84D8B3AF1D75753300752B56 /* LFHardwareVideoEncoder.h */,
84D8B3B01D75753300752B56 /* LFHardwareVideoEncoder.m */,
84D8B3B11D75753300752B56 /* LFVideoEncoding.h */,
);
path = coder;
sourceTree = "<group>";
};
84D8B3A11D75753300752B56 /* H264 */ = {
isa = PBXGroup;
children = (
84D8B3A21D75753300752B56 /* LFAVEncoder.h */,
84D8B3A31D75753300752B56 /* LFAVEncoder.mm */,
84D8B3A41D75753300752B56 /* LFMP4Atom.h */,
84D8B3A51D75753300752B56 /* LFMP4Atom.m */,
84D8B3A61D75753300752B56 /* LFNALUnit.cpp */,
84D8B3A71D75753300752B56 /* LFNALUnit.h */,
84D8B3A81D75753300752B56 /* LFVideoEncoder.h */,
84D8B3A91D75753300752B56 /* LFVideoEncoder.m */,
);
path = H264;
sourceTree = "<group>";
};
84D8B3B21D75753300752B56 /* configuration */ = {
isa = PBXGroup;
children = (
84D8B3B31D75753300752B56 /* LFLiveAudioConfiguration.h */,
84D8B3B41D75753300752B56 /* LFLiveAudioConfiguration.m */,
84D8B3B51D75753300752B56 /* LFLiveVideoConfiguration.h */,
84D8B3B61D75753300752B56 /* LFLiveVideoConfiguration.m */,
);
path = configuration;
sourceTree = "<group>";
};
84D8B3B71D75753300752B56 /* filter */ = {
isa = PBXGroup;
children = (
84D8B3B81D75753300752B56 /* LFGPUImageBeautyFilter.h */,
84D8B3B91D75753300752B56 /* LFGPUImageBeautyFilter.m */,
84D8B3BA1D75753300752B56 /* LFGPUImageEmptyFilter.h */,
84D8B3BB1D75753300752B56 /* LFGPUImageEmptyFilter.m */,
);
path = filter;
sourceTree = "<group>";
};
84D8B3BF1D75753300752B56 /* objects */ = {
isa = PBXGroup;
children = (
84D8B3C01D75753300752B56 /* LFAudioFrame.h */,
84D8B3C11D75753300752B56 /* LFAudioFrame.m */,
84D8B3C21D75753300752B56 /* LFFrame.h */,
84D8B3C31D75753300752B56 /* LFFrame.m */,
84D8B3C41D75753300752B56 /* LFLiveDebug.h */,
84D8B3C51D75753300752B56 /* LFLiveDebug.m */,
84D8B3C61D75753300752B56 /* LFLiveStreamInfo.h */,
84D8B3C71D75753300752B56 /* LFLiveStreamInfo.m */,
84D8B3C81D75753300752B56 /* LFVideoFrame.h */,
84D8B3C91D75753300752B56 /* LFVideoFrame.m */,
);
path = objects;
sourceTree = "<group>";
};
84D8B3CA1D75753300752B56 /* publish */ = {
isa = PBXGroup;
children = (
84D8B3CB1D75753300752B56 /* LFStreamingBuffer.h */,
84D8B3CC1D75753300752B56 /* LFStreamingBuffer.m */,
84D8B3CD1D75753300752B56 /* LFStreamRTMPSocket.h */,
84D8B3CE1D75753300752B56 /* LFStreamRtmpSocket.m */,
84D8B3CF1D75753300752B56 /* LFStreamSocket.h */,
84D8B3D01D75753300752B56 /* NSMutableArray+LFAdd.h */,
84D8B3D11D75753300752B56 /* NSMutableArray+LFAdd.m */,
);
path = publish;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
84D8B38D1D7574D600752B56 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
84D8B3F51D75753300752B56 /* LFLiveDebug.h in Headers */,
84D8B3E61D75753300752B56 /* LFLiveAudioConfiguration.h in Headers */,
84D8B3F11D75753300752B56 /* LFAudioFrame.h in Headers */,
84D8B3F71D75753300752B56 /* LFLiveStreamInfo.h in Headers */,
84D8B3E81D75753300752B56 /* LFLiveVideoConfiguration.h in Headers */,
84D8B3F31D75753300752B56 /* LFFrame.h in Headers */,
84D8B3EE1D75753300752B56 /* LFLiveKit.h in Headers */,
84D8B3EF1D75753300752B56 /* LFLiveSession.h in Headers */,
84D8B3F91D75753300752B56 /* LFVideoFrame.h in Headers */,
84D8B3D21D75753300752B56 /* LFAudioCapture.h in Headers */,
84D8B3FD1D75753300752B56 /* LFStreamRTMPSocket.h in Headers */,
84D8B3D81D75753300752B56 /* LFMP4Atom.h in Headers */,
84D8B3EA1D75753300752B56 /* LFGPUImageBeautyFilter.h in Headers */,
84D8B3D61D75753300752B56 /* LFAVEncoder.h in Headers */,
84D8B3DC1D75753300752B56 /* LFVideoEncoder.h in Headers */,
84D8B3EC1D75753300752B56 /* LFGPUImageEmptyFilter.h in Headers */,
84D8B3DE1D75753300752B56 /* LFAudioEncoding.h in Headers */,
84D8B4001D75753300752B56 /* NSMutableArray+LFAdd.h in Headers */,
84D8B3E31D75753300752B56 /* LFHardwareVideoEncoder.h in Headers */,
84D8B3D41D75753300752B56 /* LFVideoCapture.h in Headers */,
84D8B3FF1D75753300752B56 /* LFStreamSocket.h in Headers */,
84D8B3DB1D75753300752B56 /* LFNALUnit.h in Headers */,
84D8B3E11D75753300752B56 /* LFHardwareAudioEncoder.h in Headers */,
84D8B3E51D75753300752B56 /* LFVideoEncoding.h in Headers */,
84D8B3FB1D75753300752B56 /* LFStreamingBuffer.h in Headers */,
84D8B3DF1D75753300752B56 /* LFH264VideoEncoder.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
84D8B38F1D7574D600752B56 /* LFLiveKit */ = {
isa = PBXNativeTarget;
buildConfigurationList = 84D8B3981D7574D600752B56 /* Build configuration list for PBXNativeTarget "LFLiveKit" */;
buildPhases = (
84D8B38B1D7574D600752B56 /* Sources */,
84D8B38C1D7574D600752B56 /* Frameworks */,
84D8B38D1D7574D600752B56 /* Headers */,
84D8B38E1D7574D600752B56 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = LFLiveKit;
productName = LFLiveKit;
productReference = 84D8B3901D7574D600752B56 /* LFLiveKit.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
84D8B3871D7574D600752B56 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = admin;
TargetAttributes = {
84D8B38F1D7574D600752B56 = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 84D8B38A1D7574D600752B56 /* Build configuration list for PBXProject "LFLiveKit" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 84D8B3861D7574D600752B56;
productRefGroup = 84D8B3911D7574D600752B56 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
84D8B38F1D7574D600752B56 /* LFLiveKit */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
84D8B38E1D7574D600752B56 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
84D8B38B1D7574D600752B56 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
84D8B3EB1D75753300752B56 /* LFGPUImageBeautyFilter.m in Sources */,
84D8B3F41D75753300752B56 /* LFFrame.m in Sources */,
84D8B3F61D75753300752B56 /* LFLiveDebug.m in Sources */,
84D8B3D51D75753300752B56 /* LFVideoCapture.m in Sources */,
84D8B3F21D75753300752B56 /* LFAudioFrame.m in Sources */,
84D8B3F81D75753300752B56 /* LFLiveStreamInfo.m in Sources */,
84D8B3DA1D75753300752B56 /* LFNALUnit.cpp in Sources */,
84D8B3D71D75753300752B56 /* LFAVEncoder.mm in Sources */,
84D8B3D91D75753300752B56 /* LFMP4Atom.m in Sources */,
84D8B3E91D75753300752B56 /* LFLiveVideoConfiguration.m in Sources */,
84D8B3DD1D75753300752B56 /* LFVideoEncoder.m in Sources */,
84D8B3ED1D75753300752B56 /* LFGPUImageEmptyFilter.m in Sources */,
84D8B3F01D75753300752B56 /* LFLiveSession.m in Sources */,
84D8B3FE1D75753300752B56 /* LFStreamRtmpSocket.m in Sources */,
84D8B3E71D75753300752B56 /* LFLiveAudioConfiguration.m in Sources */,
84D8B3E21D75753300752B56 /* LFHardwareAudioEncoder.m in Sources */,
84D8B3E01D75753300752B56 /* LFH264VideoEncoder.mm in Sources */,
84D8B3FC1D75753300752B56 /* LFStreamingBuffer.m in Sources */,
84D8B3E41D75753300752B56 /* LFHardwareVideoEncoder.m in Sources */,
84D8B3FA1D75753300752B56 /* LFVideoFrame.m in Sources */,
84D8B3D31D75753300752B56 /* LFAudioCapture.m in Sources */,
84D8B4011D75753300752B56 /* NSMutableArray+LFAdd.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
84D8B3961D7574D600752B56 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
84D8B3971D7574D600752B56 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
84D8B3991D7574D600752B56 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"\"$(SRCROOT)/../Vendor/GPUImage.framework/Headers\"",
"\"$(SRCROOT)/../Vendor/pili_librtmp.framework/Headers\"",
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACH_O_TYPE = staticlib;
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
84D8B39A1D7574D600752B56 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"\"$(SRCROOT)/../Vendor/GPUImage.framework/Headers\"",
"\"$(SRCROOT)/../Vendor/pili_librtmp.framework/Headers\"",
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACH_O_TYPE = staticlib;
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
84D8B38A1D7574D600752B56 /* Build configuration list for PBXProject "LFLiveKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
84D8B3961D7574D600752B56 /* Debug */,
84D8B3971D7574D600752B56 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
84D8B3981D7574D600752B56 /* Build configuration list for PBXNativeTarget "LFLiveKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
84D8B3991D7574D600752B56 /* Debug */,
84D8B39A1D7574D600752B56 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 84D8B3871D7574D600752B56 /* Project object */;
}
+25
View File
@@ -0,0 +1,25 @@
//
// LFLiveKit.h
// LFLiveKit
//
// Created by admin on 16/5/24.
// Copyright © 2016年 admin. All rights reserved.
//
#import "LFLiveSession.h"
#import "LFLiveAudioConfiguration.h"
#import "LFLiveVideoConfiguration.h"
#import "LFAudioFrame.h"
#import "LFFrame.h"
#import "LFLiveStreamInfo.h"
#import "LFVideoFrame.h"
#import "LFLiveDebug.h"
//#import <LFLiveKit/LFLiveSession.h>
//#import <LFLiveKit/LFLiveAudioConfiguration.h>
//#import <LFLiveKit/LFLiveVideoConfiguration.h>
//#import <LFLiveKit/LFAudioFrame.h>
//#import <LFLiveKit/LFFrame.h>
//#import <LFLiveKit/LFLiveStreamInfo.h>
//#import <LFLiveKit/LFVideoFrame.h>
//#import <LFLiveKit/LFLiveDebug.h>
+151
View File
@@ -0,0 +1,151 @@
//
// LFLiveSession.h
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import "LFLiveStreamInfo.h"
#import "LFAudioFrame.h"
#import "LFVideoFrame.h"
#import "LFLiveAudioConfiguration.h"
#import "LFLiveVideoConfiguration.h"
#import "LFLiveDebug.h"
typedef NS_ENUM(NSInteger,LFLiveCaptureType) {
LFLiveCaptureAudio, //< capture only audio
LFLiveCaptureVideo, //< capture onlt video
LFLiveInputAudio, //< only audio (External input audio)
LFLiveInputVideo, //< only video (External input video)
};
///< 用来控制采集类型(可以内部采集也可以外部传入等各种组合,支持单音频与单视频,外部输入适用于录屏,无人机等外设介入)
typedef NS_ENUM(NSInteger,LFLiveCaptureTypeMask) {
LFLiveCaptureMaskAudio = (1 << LFLiveCaptureAudio), ///< only inner capture audio (no video)
LFLiveCaptureMaskVideo = (1 << LFLiveCaptureVideo), ///< only inner capture video (no audio)
LFLiveInputMaskAudio = (1 << LFLiveInputAudio), ///< only outer input audio (no video)
LFLiveInputMaskVideo = (1 << LFLiveInputVideo), ///< only outer input video (no audio)
LFLiveCaptureMaskAll = (LFLiveCaptureMaskAudio | LFLiveCaptureMaskVideo), ///< inner capture audio and video
LFLiveInputMaskAll = (LFLiveInputMaskAudio | LFLiveInputMaskVideo), ///< outer input audio and video(method see pushVideo and pushAudio)
LFLiveCaptureMaskAudioInputVideo = (LFLiveCaptureMaskAudio | LFLiveInputMaskVideo), ///< inner capture audio and outer input video(method pushVideo and setRunning)
LFLiveCaptureMaskVideoInputAudio = (LFLiveCaptureMaskVideo | LFLiveInputMaskAudio), ///< inner capture video and outer input audio(method pushAudio and setRunning)
LFLiveCaptureDefaultMask = LFLiveCaptureMaskAll ///< default is inner capture audio and video
};
@class LFLiveSession;
@protocol LFLiveSessionDelegate <NSObject>
@optional
/** live status changed will callback */
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state;
/** live debug info callback */
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug *)debugInfo;
/** callback socket errorcode */
- (void)liveSession:(nullable LFLiveSession *)session errorCode:(LFLiveSocketErrorCode)errorCode;
@end
@class LFLiveStreamInfo;
@interface LFLiveSession : NSObject
#pragma mark - Attribute
///=============================================================================
/// @name Attribute
///=============================================================================
/** The delegate of the capture. captureData callback */
@property (nullable, nonatomic, weak) id<LFLiveSessionDelegate> delegate;
/** The running control start capture or stop capture*/
@property (nonatomic, assign) BOOL running;
/** The preView will show OpenGL ES view*/
@property (nonatomic, strong, null_resettable) UIView *preView;
/** The captureDevicePosition control camraPosition ,default front*/
@property (nonatomic, assign) AVCaptureDevicePosition captureDevicePosition;
/** The beautyFace control capture shader filter empty or beautiy */
@property (nonatomic, assign) BOOL beautyFace;
/** The beautyLevel control beautyFace Level. Default is 0.5, between 0.0 ~ 1.0 */
@property (nonatomic, assign) CGFloat beautyLevel;
/** The brightLevel control brightness Level, Default is 0.5, between 0.0 ~ 1.0 */
@property (nonatomic, assign) CGFloat brightLevel;
/** The torch control camera zoom scale default 1.0, between 1.0 ~ 3.0 */
@property (nonatomic, assign) CGFloat zoomScale;
/** The torch control capture flash is on or off */
@property (nonatomic, assign) BOOL torch;
/** The mirror control mirror of front camera is on or off */
@property (nonatomic, assign) BOOL mirror;
/** The muted control callbackAudioData,muted will memset 0.*/
@property (nonatomic, assign) BOOL muted;
/* The adaptiveBitrate control auto adjust bitrate. Default is NO */
@property (nonatomic, assign) BOOL adaptiveBitrate;
/** The stream control upload and package*/
@property (nullable, nonatomic, strong, readonly) LFLiveStreamInfo *streamInfo;
/** The status of the stream .*/
@property (nonatomic, assign, readonly) LFLiveState state;
/** The captureType control inner or outer audio and video .*/
@property (nonatomic, assign, readonly) LFLiveCaptureTypeMask captureType;
/** The showDebugInfo control streamInfo and uploadInfo(1s) *.*/
@property (nonatomic, assign) BOOL showDebugInfo;
/** The reconnectInterval control reconnect timeInterval(重连间隔) *.*/
@property (nonatomic, assign) NSUInteger reconnectInterval;
/** The reconnectCount control reconnect count (重连次数) *.*/
@property (nonatomic, assign) NSUInteger reconnectCount;
/*** The warterMarkView control whether the watermark is displayed or not ,if set ni,will remove watermark,otherwise add.
set alpha represent mix.Position relative to outVideoSize.
*.*/
@property (nonatomic, strong, nullable) UIView *warterMarkView;
@property (nonatomic, strong,readonly ,nullable) UIImage *currentImage;
#pragma mark - Initializer
///=============================================================================
/// @name Initializer
///=============================================================================
- (nullable instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
/**
The designated initializer. Multiple instances with the same configuration will make the
capture unstable.
*/
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration;
/**
The designated initializer. Multiple instances with the same configuration will make the
capture unstable.
*/
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration captureType:(LFLiveCaptureTypeMask)captureType NS_DESIGNATED_INITIALIZER;
/** The start stream .*/
- (void)startLive:(nonnull LFLiveStreamInfo *)streamInfo;
/** The stop stream .*/
- (void)stopLive;
/** support outer input yuv or rgb video(set LFLiveCaptureTypeMask) .*/
- (void)pushVideo:(nullable CVPixelBufferRef)pixelBuffer;
/** support outer input pcm audio(set LFLiveCaptureTypeMask) .*/
- (void)pushAudio:(nullable NSData*)audioData;
@end
+412
View File
@@ -0,0 +1,412 @@
//
// LFLiveSession.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFLiveSession.h"
#import "LFVideoCapture.h"
#import "LFAudioCapture.h"
#import "LFHardwareVideoEncoder.h"
#import "LFHardwareAudioEncoder.h"
#import "LFH264VideoEncoder.h"
#import "LFStreamRTMPSocket.h"
#import "LFLiveStreamInfo.h"
#import "LFGPUImageBeautyFilter.h"
#import "LFH264VideoEncoder.h"
@interface LFLiveSession ()<LFAudioCaptureDelegate, LFVideoCaptureDelegate, LFAudioEncodingDelegate, LFVideoEncodingDelegate, LFStreamSocketDelegate>
/// 音频配置
@property (nonatomic, strong) LFLiveAudioConfiguration *audioConfiguration;
/// 视频配置
@property (nonatomic, strong) LFLiveVideoConfiguration *videoConfiguration;
/// 声音采集
@property (nonatomic, strong) LFAudioCapture *audioCaptureSource;
/// 视频采集
@property (nonatomic, strong) LFVideoCapture *videoCaptureSource;
/// 音频编码
@property (nonatomic, strong) id<LFAudioEncoding> audioEncoder;
/// 视频编码
@property (nonatomic, strong) id<LFVideoEncoding> videoEncoder;
/// 上传
@property (nonatomic, strong) id<LFStreamSocket> socket;
#pragma mark -- 内部标识
/// 调试信息
@property (nonatomic, strong) LFLiveDebug *debugInfo;
/// 流信息
@property (nonatomic, strong) LFLiveStreamInfo *streamInfo;
/// 是否开始上传
@property (nonatomic, assign) BOOL uploading;
/// 当前状态
@property (nonatomic, assign, readwrite) LFLiveState state;
/// 当前直播type
@property (nonatomic, assign, readwrite) LFLiveCaptureTypeMask captureType;
/// 时间戳锁
@property (nonatomic, strong) dispatch_semaphore_t lock;
@end
/**  时间戳 */
#define NOW (CACurrentMediaTime()*1000)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
@interface LFLiveSession ()
/// 上传相对时间戳
@property (nonatomic, assign) uint64_t relativeTimestamps;
/// 音视频是否对齐
@property (nonatomic, assign) BOOL AVAlignment;
/// 当前是否采集到了音频
@property (nonatomic, assign) BOOL hasCaptureAudio;
/// 当前是否采集到了关键帧
@property (nonatomic, assign) BOOL hasKeyFrameVideo;
@end
@implementation LFLiveSession
#pragma mark -- LifeCycle
- (instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration {
return [self initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration captureType:LFLiveCaptureDefaultMask];
}
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration captureType:(LFLiveCaptureTypeMask)captureType{
if((captureType & LFLiveCaptureMaskAudio || captureType & LFLiveInputMaskAudio) && !audioConfiguration) @throw [NSException exceptionWithName:@"LFLiveSession init error" reason:@"audioConfiguration is nil " userInfo:nil];
if((captureType & LFLiveCaptureMaskVideo || captureType & LFLiveInputMaskVideo) && !videoConfiguration) @throw [NSException exceptionWithName:@"LFLiveSession init error" reason:@"videoConfiguration is nil " userInfo:nil];
if (self = [super init]) {
_audioConfiguration = audioConfiguration;
_videoConfiguration = videoConfiguration;
_adaptiveBitrate = NO;
_captureType = captureType;
}
return self;
}
- (void)dealloc {
self.videoCaptureSource.running = NO;
self.audioCaptureSource.running = NO;
}
#pragma mark -- CustomMethod
- (void)startLive:(LFLiveStreamInfo *)streamInfo {
if (!streamInfo) return;
_streamInfo = streamInfo;
_streamInfo.videoConfiguration = _videoConfiguration;
_streamInfo.audioConfiguration = _audioConfiguration;
_streamInfo.needDropFrame = (self.captureType & LFLiveCaptureMaskVideo || self.captureType & LFLiveInputMaskVideo) ? YES : NO;//< 有视频执行丢帧算法
[self.socket start];
}
- (void)stopLive {
self.uploading = NO;
[self.socket stop];
self.socket = nil;
}
#pragma mark -- PrivateMethod
- (void)pushSendBuffer:(LFFrame*)frame{
if(self.relativeTimestamps == 0){
self.relativeTimestamps = frame.timestamp;
}
frame.timestamp = [self uploadTimestamp:frame.timestamp];
[self.socket sendFrame:frame];
}
- (void)pushVideo:(nullable CVPixelBufferRef)pixelBuffer{
if(self.captureType & LFLiveInputMaskVideo){
if (self.uploading) [self.videoEncoder encodeVideoData:pixelBuffer timeStamp:NOW];
}
}
- (void)pushAudio:(nullable NSData*)audioData{
if(self.captureType & LFLiveInputMaskAudio){
if (self.uploading) [self.audioEncoder encodeAudioData:audioData timeStamp:NOW];
}
}
#pragma mark -- CaptureDelegate
- (void)captureOutput:(nullable LFAudioCapture *)capture audioData:(nullable NSData*)audioData {
if (self.uploading) [self.audioEncoder encodeAudioData:audioData timeStamp:NOW];
}
- (void)captureOutput:(nullable LFVideoCapture *)capture pixelBuffer:(nullable CVPixelBufferRef)pixelBuffer {
if (self.uploading) [self.videoEncoder encodeVideoData:pixelBuffer timeStamp:NOW];
}
#pragma mark -- EncoderDelegate
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame *)frame {
//<上传 时间戳对齐
if (self.uploading){
self.hasCaptureAudio = YES;
if(self.AVAlignment) [self pushSendBuffer:frame];
}
}
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame *)frame {
//<上传 时间戳对齐
if (self.uploading){
if(frame.isKeyFrame && self.hasCaptureAudio) self.hasKeyFrameVideo = YES;
if(self.AVAlignment) [self pushSendBuffer:frame];
}
}
#pragma mark -- LFStreamTcpSocketDelegate
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status {
if (status == LFLiveStart) {
if (!self.uploading) {
self.AVAlignment = NO;
self.hasCaptureAudio = NO;
self.hasKeyFrameVideo = NO;
self.relativeTimestamps = 0;
self.uploading = YES;
}
} else if(status == LFLiveStop || status == LFLiveError){
self.uploading = NO;
}
dispatch_async(dispatch_get_main_queue(), ^{
self.state = status;
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:liveStateDidChange:)]) {
[self.delegate liveSession:self liveStateDidChange:status];
}
});
}
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:errorCode:)]) {
[self.delegate liveSession:self errorCode:errorCode];
}
});
}
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug *)debugInfo {
self.debugInfo = debugInfo;
if (self.showDebugInfo) {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:debugInfo:)]) {
[self.delegate liveSession:self debugInfo:debugInfo];
}
});
}
}
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status {
if((self.captureType & LFLiveCaptureMaskVideo || self.captureType & LFLiveInputMaskVideo) && self.adaptiveBitrate){
NSUInteger videoBitRate = [self.videoEncoder videoBitRate];
if (status == LFLiveBuffferDecline) {
if (videoBitRate < _videoConfiguration.videoMaxBitRate) {
videoBitRate = videoBitRate + 50 * 1000;
[self.videoEncoder setVideoBitRate:videoBitRate];
NSLog(@"Increase bitrate %@", @(videoBitRate));
}
} else {
if (videoBitRate > self.videoConfiguration.videoMinBitRate) {
videoBitRate = videoBitRate - 100 * 1000;
[self.videoEncoder setVideoBitRate:videoBitRate];
NSLog(@"Decline bitrate %@", @(videoBitRate));
}
}
}
}
#pragma mark -- Getter Setter
- (void)setRunning:(BOOL)running {
if (_running == running) return;
[self willChangeValueForKey:@"running"];
_running = running;
[self didChangeValueForKey:@"running"];
self.videoCaptureSource.running = _running;
self.audioCaptureSource.running = _running;
}
- (void)setPreView:(UIView *)preView {
[self willChangeValueForKey:@"preView"];
[self.videoCaptureSource setPreView:preView];
[self didChangeValueForKey:@"preView"];
}
- (UIView *)preView {
return self.videoCaptureSource.preView;
}
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition {
[self willChangeValueForKey:@"captureDevicePosition"];
[self.videoCaptureSource setCaptureDevicePosition:captureDevicePosition];
[self didChangeValueForKey:@"captureDevicePosition"];
}
- (AVCaptureDevicePosition)captureDevicePosition {
return self.videoCaptureSource.captureDevicePosition;
}
- (void)setBeautyFace:(BOOL)beautyFace {
[self willChangeValueForKey:@"beautyFace"];
[self.videoCaptureSource setBeautyFace:beautyFace];
[self didChangeValueForKey:@"beautyFace"];
}
- (BOOL)beautyFace {
return self.videoCaptureSource.beautyFace;
}
- (void)setBeautyLevel:(CGFloat)beautyLevel {
[self willChangeValueForKey:@"beautyLevel"];
[self.videoCaptureSource setBeautyLevel:beautyLevel];
[self didChangeValueForKey:@"beautyLevel"];
}
- (CGFloat)beautyLevel {
return self.videoCaptureSource.beautyLevel;
}
- (void)setBrightLevel:(CGFloat)brightLevel {
[self willChangeValueForKey:@"brightLevel"];
[self.videoCaptureSource setBrightLevel:brightLevel];
[self didChangeValueForKey:@"brightLevel"];
}
- (CGFloat)brightLevel {
return self.videoCaptureSource.brightLevel;
}
- (void)setZoomScale:(CGFloat)zoomScale {
[self willChangeValueForKey:@"zoomScale"];
[self.videoCaptureSource setZoomScale:zoomScale];
[self didChangeValueForKey:@"zoomScale"];
}
- (CGFloat)zoomScale {
return self.videoCaptureSource.zoomScale;
}
- (void)setTorch:(BOOL)torch {
[self willChangeValueForKey:@"torch"];
[self.videoCaptureSource setTorch:torch];
[self didChangeValueForKey:@"torch"];
}
- (BOOL)torch {
return self.videoCaptureSource.torch;
}
- (void)setMirror:(BOOL)mirror {
[self willChangeValueForKey:@"mirror"];
[self.videoCaptureSource setMirror:mirror];
[self didChangeValueForKey:@"mirror"];
}
- (BOOL)mirror {
return self.videoCaptureSource.mirror;
}
- (void)setMuted:(BOOL)muted {
[self willChangeValueForKey:@"muted"];
[self.audioCaptureSource setMuted:muted];
[self didChangeValueForKey:@"muted"];
}
- (BOOL)muted {
return self.audioCaptureSource.muted;
}
- (void)setWarterMarkView:(UIView *)warterMarkView{
[self.videoCaptureSource setWarterMarkView:warterMarkView];
}
- (nullable UIView*)warterMarkView{
return self.videoCaptureSource.warterMarkView;
}
- (nullable UIImage *)currentImage{
return self.videoCaptureSource.currentImage;
}
- (LFAudioCapture *)audioCaptureSource {
if (!_audioCaptureSource) {
if(self.captureType & LFLiveCaptureMaskAudio){
_audioCaptureSource = [[LFAudioCapture alloc] initWithAudioConfiguration:_audioConfiguration];
_audioCaptureSource.delegate = self;
}
}
return _audioCaptureSource;
}
- (LFVideoCapture *)videoCaptureSource {
if (!_videoCaptureSource) {
if(self.captureType & LFLiveCaptureMaskVideo){
_videoCaptureSource = [[LFVideoCapture alloc] initWithVideoConfiguration:_videoConfiguration];
_videoCaptureSource.delegate = self;
}
}
return _videoCaptureSource;
}
- (id<LFAudioEncoding>)audioEncoder {
if (!_audioEncoder) {
_audioEncoder = [[LFHardwareAudioEncoder alloc] initWithAudioStreamConfiguration:_audioConfiguration];
[_audioEncoder setDelegate:self];
}
return _audioEncoder;
}
- (id<LFVideoEncoding>)videoEncoder {
if (!_videoEncoder) {
if([[UIDevice currentDevice].systemVersion floatValue] < 8.0){
_videoEncoder = [[LFH264VideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
}else{
_videoEncoder = [[LFHardwareVideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
}
[_videoEncoder setDelegate:self];
}
return _videoEncoder;
}
- (id<LFStreamSocket>)socket {
if (!_socket) {
_socket = [[LFStreamRTMPSocket alloc] initWithStream:self.streamInfo reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
[_socket setDelegate:self];
}
return _socket;
}
- (LFLiveStreamInfo *)streamInfo {
if (!_streamInfo) {
_streamInfo = [[LFLiveStreamInfo alloc] init];
}
return _streamInfo;
}
- (dispatch_semaphore_t)lock{
if(!_lock){
_lock = dispatch_semaphore_create(1);
}
return _lock;
}
- (uint64_t)uploadTimestamp:(uint64_t)captureTimestamp{
dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER);
uint64_t currentts = 0;
currentts = captureTimestamp - self.relativeTimestamps;
dispatch_semaphore_signal(self.lock);
return currentts;
}
- (BOOL)AVAlignment{
if((self.captureType & LFLiveCaptureMaskAudio || self.captureType & LFLiveInputMaskAudio) &&
(self.captureType & LFLiveCaptureMaskVideo || self.captureType & LFLiveInputMaskVideo)
){
if(self.hasCaptureAudio && self.hasKeyFrameVideo) return YES;
else return NO;
}else{
return YES;
}
}
@end
@@ -17,7 +17,7 @@ extern NSString *_Nullable const LFAudioComponentFailedToCreateNotification;
@class LFAudioCapture;
/** LFAudioCapture callback audioData */
@protocol LFAudioCaptureDelegate <NSObject>
- (void)captureOutput:(nullable LFAudioCapture*)capture audioBuffer:(AudioBufferList)inBufferList;
- (void)captureOutput:(nullable LFAudioCapture *)capture audioData:(nullable NSData*)audioData;
@end
@@ -29,10 +29,10 @@ extern NSString *_Nullable const LFAudioComponentFailedToCreateNotification;
///=============================================================================
/** The delegate of the capture. captureData callback */
@property (nullable,nonatomic, weak) id<LFAudioCaptureDelegate> delegate;
@property (nullable, nonatomic, weak) id<LFAudioCaptureDelegate> delegate;
/** The muted control callbackAudioData,muted will memset 0.*/
@property (nonatomic,assign) BOOL muted;
@property (nonatomic, assign) BOOL muted;
/** The running control start capture or stop capture*/
@property (nonatomic, assign) BOOL running;
@@ -45,8 +45,8 @@ extern NSString *_Nullable const LFAudioComponentFailedToCreateNotification;
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
/**
The designated initializer. Multiple instances with the same configuration will make the
capture unstable.
The designated initializer. Multiple instances with the same configuration will make the
capture unstable.
*/
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
@@ -14,11 +14,11 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
@interface LFAudioCapture ()
@property (nonatomic, assign) AudioComponentInstance componetInstance;
@property (nonatomic, assign) AudioComponent component;
@property (nonatomic, strong) dispatch_queue_t taskQueue;
@property (nonatomic, assign) AudioComponentInstance componetInstance;
@property (nonatomic, assign) AudioComponent component;
@property (nonatomic, strong) dispatch_queue_t taskQueue;
@property (nonatomic, assign) BOOL isRunning;
@property (nonatomic, strong) LFLiveAudioConfiguration *configuration;
@property (nonatomic, strong,nullable) LFLiveAudioConfiguration *configuration;
@end
@@ -32,7 +32,7 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
self.taskQueue = dispatch_queue_create("com.youku.Laifeng.audioCapture.Queue", NULL);
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleRouteChange:)
@@ -43,18 +43,9 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
name: AVAudioSessionInterruptionNotification
object: session];
NSError *error = nil;
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers error:nil];
[session setMode:AVAudioSessionModeVideoRecording error:&error];
if (![session setActive:YES error:&error]) {
[self handleAudioComponentCreationFailure];
}
AudioComponentDescription acd;
acd.componentType = kAudioUnitType_Output;
//acd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
acd.componentSubType = kAudioUnitSubType_RemoteIO;
acd.componentManufacturer = kAudioUnitManufacturer_Apple;
acd.componentFlags = 0;
@@ -86,8 +77,8 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
AURenderCallbackStruct cb;
cb.inputProcRefCon = (__bridge void *)(self);
cb.inputProc = handleInputBuffer;
status = AudioUnitSetProperty(self.componetInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &desc, sizeof(desc));
status = AudioUnitSetProperty(self.componetInstance, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1, &cb, sizeof(cb));
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);
@@ -96,18 +87,19 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
}
[session setPreferredSampleRate:_configuration.audioSampleRate error:nil];
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:nil];
[session setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:nil];
[session setActive:YES error:nil];
}
return self;
}
- (void)dealloc{
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
dispatch_sync(self.taskQueue, ^{
if(self.componetInstance){
if (self.componetInstance) {
AudioOutputUnitStop(self.componetInstance);
AudioComponentInstanceDispose(self.componetInstance);
self.componetInstance = nil;
@@ -117,16 +109,17 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
}
#pragma mark -- Setter
- (void)setRunning:(BOOL)running{
if(_running == running) return;
- (void)setRunning:(BOOL)running {
if (_running == running) return;
_running = running;
if(_running){
if (_running) {
dispatch_async(self.taskQueue, ^{
self.isRunning = YES;
NSLog(@"MicrophoneSource: startRunning");
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:nil];
AudioOutputUnitStart(self.componetInstance);
});
}else{
} else {
self.isRunning = NO;
}
}
@@ -141,42 +134,44 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
#pragma mark -- NSNotification
- (void)handleRouteChange:(NSNotification *)notification {
AVAudioSession *session = [ AVAudioSession sharedInstance ];
NSString* seccReason = @"";
NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
NSString *seccReason = @"";
NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
// AVAudioSessionRouteDescription* prevRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey];
switch (reason) {
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
seccReason = @"The route changed because no suitable route is now available for the specified category.";
break;
case AVAudioSessionRouteChangeReasonWakeFromSleep:
seccReason = @"The route changed when the device woke up from sleep.";
break;
case AVAudioSessionRouteChangeReasonOverride:
seccReason = @"The output route was overridden by the app.";
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
seccReason = @"The category of the session object changed.";
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
seccReason = @"The previous audio output path is no longer available.";
break;
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
seccReason = @"A preferred new audio output path is now available.";
break;
case AVAudioSessionRouteChangeReasonUnknown:
default:
seccReason = @"The reason for the change is unknown.";
break;
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
seccReason = @"The route changed because no suitable route is now available for the specified category.";
break;
case AVAudioSessionRouteChangeReasonWakeFromSleep:
seccReason = @"The route changed when the device woke up from sleep.";
break;
case AVAudioSessionRouteChangeReasonOverride:
seccReason = @"The output route was overridden by the app.";
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
seccReason = @"The category of the session object changed.";
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
seccReason = @"The previous audio output path is no longer available.";
break;
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
seccReason = @"A preferred new audio output path is now available.";
break;
case AVAudioSessionRouteChangeReasonUnknown:
default:
seccReason = @"The reason for the change is unknown.";
break;
}
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];
@@ -188,27 +183,28 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
});
}
}
if (reason == AVAudioSessionInterruptionTypeEnded) {
reasonStr = @"AVAudioSessionInterruptionTypeEnded";
NSNumber* seccondReason = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey] ;
NSNumber *seccondReason = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];
switch ([seccondReason integerValue]) {
case AVAudioSessionInterruptionOptionShouldResume:
if (self.isRunning) {
dispatch_async(self.taskQueue, ^{
NSLog(@"MicrophoneSource: stopRunning");
AudioOutputUnitStart(self.componetInstance);
});
}
// Indicates that the audio session is active and immediately ready to be used. Your app can resume the audio operation that was interrupted.
break;
default:
break;
case AVAudioSessionInterruptionOptionShouldResume:
if (self.isRunning) {
dispatch_async(self.taskQueue, ^{
NSLog(@"MicrophoneSource: stopRunning");
AudioOutputUnitStart(self.componetInstance);
});
}
// Indicates that the audio session is active and immediately ready to be used. Your app can resume the audio operation that was interrupted.
break;
default:
break;
}
}
};
NSLog(@"handleInterruption: %@ reason %@",[notification name], reasonStr);
}
;
NSLog(@"handleInterruption: %@ reason %@", [notification name], reasonStr);
}
#pragma mark -- CallBack
@@ -220,43 +216,43 @@ static OSStatus handleInputBuffer(void *inRefCon,
AudioBufferList *ioData) {
@autoreleasepool {
LFAudioCapture *source = (__bridge LFAudioCapture *)inRefCon;
if(!source) return -1;
if (!source) return -1;
AudioBuffer buffer;
buffer.mData = NULL;
buffer.mDataByteSize = 0;
buffer.mNumberChannels = 1;
AudioBufferList buffers;
buffers.mNumberBuffers = 1;
buffers.mBuffers[0] = buffer;
OSStatus status = AudioUnitRender(source.componetInstance,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
&buffers);
if (!source.isRunning) {
dispatch_sync(source.taskQueue, ^{
NSLog(@"MicrophoneSource: stopRunning");
AudioOutputUnitStop(source.componetInstance);
});
return status;
}
if (source.muted) {
for (int i = 0; i < buffers.mNumberBuffers; i++) {
AudioBuffer ab = buffers.mBuffers[i];
memset(ab.mData, 0, ab.mDataByteSize);
}
}
if(!status) {
if(source.delegate && [source.delegate respondsToSelector:@selector(captureOutput:audioBuffer:)]){
[source.delegate captureOutput:source audioBuffer:buffers];
if (!status) {
if (source.delegate && [source.delegate respondsToSelector:@selector(captureOutput:audioData:)]) {
[source.delegate captureOutput:source audioData:[NSData dataWithBytes:buffers.mBuffers[0].mData length:buffers.mBuffers[0].mDataByteSize]];
}
}
return status;
@@ -13,7 +13,7 @@
@class LFVideoCapture;
/** LFVideoCapture callback videoData */
@protocol LFVideoCaptureDelegate <NSObject>
- (void)captureOutput:(nullable LFVideoCapture*)capture pixelBuffer:(nullable CVImageBufferRef)pixelBuffer;
- (void)captureOutput:(nullable LFVideoCapture *)capture pixelBuffer:(nullable CVPixelBufferRef)pixelBuffer;
@end
@interface LFVideoCapture : NSObject
@@ -24,13 +24,13 @@
///=============================================================================
/** The delegate of the capture. captureData callback */
@property (nullable,nonatomic, weak) id<LFVideoCaptureDelegate> delegate;
@property (nullable, nonatomic, weak) id<LFVideoCaptureDelegate> delegate;
/** The running control start capture or stop capture*/
@property (nonatomic, assign) BOOL running;
/** The preView will show OpenGL ES view*/
@property (null_resettable,nonatomic, strong) UIView * preView;
@property (null_resettable, nonatomic, strong) UIView *preView;
/** The captureDevicePosition control camraPosition ,default front*/
@property (nonatomic, assign) AVCaptureDevicePosition captureDevicePosition;
@@ -38,9 +38,28 @@
/** The beautyFace control capture shader filter empty or beautiy */
@property (nonatomic, assign) BOOL beautyFace;
/** The torch control capture flash is on or off */
@property (nonatomic, assign) BOOL torch;
/** The mirror control mirror of front camera is on or off */
@property (nonatomic, assign) BOOL mirror;
/** The beautyLevel control beautyFace Level, default 0.5, between 0.0 ~ 1.0 */
@property (nonatomic, assign) CGFloat beautyLevel;
/** The brightLevel control brightness Level, default 0.5, between 0.0 ~ 1.0 */
@property (nonatomic, assign) CGFloat brightLevel;
/** The torch control camera zoom scale default 1.0, between 1.0 ~ 3.0 */
@property (nonatomic, assign) CGFloat zoomScale;
/** The videoFrameRate control videoCapture output data count */
@property (nonatomic, assign) NSInteger videoFrameRate;
/*** The warterMarkView control whether the watermark is displayed or not ,if set ni,will remove watermark,otherwise add *.*/
@property (nonatomic, strong, nullable) UIView *warterMarkView;
@property (nonatomic, strong, nullable) UIImage *currentImage;
#pragma mark - Initializer
///=============================================================================
/// @name Initializer
@@ -49,8 +68,8 @@
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
/**
The designated initializer. Multiple instances with the same configuration will make the
capture unstable.
The designated initializer. Multiple instances with the same configuration will make the
capture unstable.
*/
- (nullable instancetype)initWithVideoConfiguration:(nullable LFLiveVideoConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
+360
View File
@@ -0,0 +1,360 @@
//
// LFVideoCapture.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/1.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFVideoCapture.h"
#import "GPUImage.h"
#import "LFGPUImageBeautyFilter.h"
#import "LFGPUImageEmptyFilter.h"
@interface LFVideoCapture ()
@property (nonatomic, strong) GPUImageVideoCamera *videoCamera;
@property (nonatomic, strong) LFGPUImageBeautyFilter *beautyFilter;
@property (nonatomic, strong) GPUImageOutput<GPUImageInput> *filter;
@property (nonatomic, strong) GPUImageCropFilter *cropfilter;
@property (nonatomic, strong) GPUImageOutput<GPUImageInput> *output;
@property (nonatomic, strong) GPUImageView *gpuImageView;
@property (nonatomic, strong) LFLiveVideoConfiguration *configuration;
@property (nonatomic, strong) GPUImageAlphaBlendFilter *blendFilter;
@property (nonatomic, strong) GPUImageUIElement *uiElementInput;
@property (nonatomic, strong) UIView *waterMarkContentView;
@end
@implementation LFVideoCapture
@synthesize torch = _torch;
@synthesize beautyLevel = _beautyLevel;
@synthesize brightLevel = _brightLevel;
@synthesize zoomScale = _zoomScale;
#pragma mark -- LifeCycle
- (instancetype)initWithVideoConfiguration:(LFLiveVideoConfiguration *)configuration {
if (self = [super init]) {
_configuration = configuration;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarChanged:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
self.beautyFace = YES;
self.beautyLevel = 0.5;
self.brightLevel = 0.5;
self.zoomScale = 1.0;
self.mirror = YES;
}
return self;
}
- (void)dealloc {
[UIApplication sharedApplication].idleTimerDisabled = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self.videoCamera stopCameraCapture];
if(self.gpuImageView){
[self.gpuImageView removeFromSuperview];
self.gpuImageView = nil;
}
}
#pragma mark -- Setter Getter
- (GPUImageVideoCamera *)videoCamera{
if(!_videoCamera){
_videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:_configuration.avSessionPreset cameraPosition:AVCaptureDevicePositionFront];
UIInterfaceOrientation statusBar = [[UIApplication sharedApplication] statusBarOrientation];
if (self.configuration.landscape) {
if (statusBar != UIInterfaceOrientationLandscapeLeft && statusBar != UIInterfaceOrientationLandscapeRight) {
@throw [NSException exceptionWithName:@"当前设置方向出错" reason:@"LFLiveVideoConfiguration landscape error" userInfo:nil];
} else {
_videoCamera.outputImageOrientation = statusBar;
}
} else {
if (statusBar != UIInterfaceOrientationPortrait && statusBar != UIInterfaceOrientationPortraitUpsideDown) {
@throw [NSException exceptionWithName:@"当前设置方向出错" reason:@"LFLiveVideoConfiguration landscape error" userInfo:nil];
} else {
_videoCamera.outputImageOrientation = statusBar;
}
}
_videoCamera.horizontallyMirrorFrontFacingCamera = YES;
_videoCamera.horizontallyMirrorRearFacingCamera = NO;
_videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
}
return _videoCamera;
}
- (void)setRunning:(BOOL)running {
if (_running == running) return;
_running = running;
if (!_running) {
[UIApplication sharedApplication].idleTimerDisabled = NO;
[self.videoCamera stopCameraCapture];
} else {
[UIApplication sharedApplication].idleTimerDisabled = YES;
[self reloadFilter];
[self.videoCamera startCameraCapture];
}
}
- (void)setPreView:(UIView *)preView {
if (self.gpuImageView.superview) [self.gpuImageView removeFromSuperview];
[preView insertSubview:self.gpuImageView atIndex:0];
self.gpuImageView.frame = CGRectMake(0, 0, preView.frame.size.width, preView.frame.size.height);
}
- (UIView *)preView {
return self.gpuImageView.superview;
}
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition {
[self.videoCamera rotateCamera];
self.videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
}
- (AVCaptureDevicePosition)captureDevicePosition {
return [self.videoCamera cameraPosition];
}
- (void)setVideoFrameRate:(NSInteger)videoFrameRate {
if (videoFrameRate <= 0) return;
if (videoFrameRate == self.videoCamera.frameRate) return;
self.videoCamera.frameRate = (uint32_t)videoFrameRate;
}
- (NSInteger)videoFrameRate {
return self.videoCamera.frameRate;
}
- (void)setTorch:(BOOL)torch {
BOOL ret;
if (!self.videoCamera.captureSession) return;
AVCaptureSession *session = (AVCaptureSession *)self.videoCamera.captureSession;
[session beginConfiguration];
if (self.videoCamera.inputCamera) {
if (self.videoCamera.inputCamera.torchAvailable) {
NSError *err = nil;
if ([self.videoCamera.inputCamera lockForConfiguration:&err]) {
[self.videoCamera.inputCamera setTorchMode:(torch ? AVCaptureTorchModeOn : AVCaptureTorchModeOff) ];
[self.videoCamera.inputCamera unlockForConfiguration];
ret = (self.videoCamera.inputCamera.torchMode == AVCaptureTorchModeOn);
} else {
NSLog(@"Error while locking device for torch: %@", err);
ret = false;
}
} else {
NSLog(@"Torch not available in current camera input");
}
}
[session commitConfiguration];
_torch = ret;
}
- (BOOL)torch {
return self.videoCamera.inputCamera.torchMode;
}
- (void)setMirror:(BOOL)mirror {
_mirror = mirror;
self.videoCamera.horizontallyMirrorFrontFacingCamera = mirror;
}
- (void)setBeautyFace:(BOOL)beautyFace{
_beautyFace = beautyFace;
[self reloadFilter];
}
- (void)setBeautyLevel:(CGFloat)beautyLevel {
_beautyLevel = beautyLevel;
if (self.beautyFilter) {
[self.beautyFilter setBeautyLevel:_beautyLevel];
}
}
- (CGFloat)beautyLevel {
return _beautyLevel;
}
- (void)setBrightLevel:(CGFloat)brightLevel {
_brightLevel = brightLevel;
if (self.beautyFilter) {
[self.beautyFilter setBrightLevel:brightLevel];
}
}
- (CGFloat)brightLevel {
return _brightLevel;
}
- (void)setZoomScale:(CGFloat)zoomScale {
if (self.videoCamera && self.videoCamera.inputCamera) {
AVCaptureDevice *device = (AVCaptureDevice *)self.videoCamera.inputCamera;
if ([device lockForConfiguration:nil]) {
device.videoZoomFactor = zoomScale;
[device unlockForConfiguration];
_zoomScale = zoomScale;
}
}
}
- (CGFloat)zoomScale {
return _zoomScale;
}
- (void)setWarterMarkView:(UIView *)warterMarkView{
if(_warterMarkView && _warterMarkView.superview){
[_warterMarkView removeFromSuperview];
_warterMarkView = nil;
}
_warterMarkView = warterMarkView;
self.blendFilter.mix = warterMarkView.alpha;
[self.waterMarkContentView addSubview:_warterMarkView];
[self reloadFilter];
}
- (GPUImageUIElement *)uiElementInput{
if(!_uiElementInput){
_uiElementInput = [[GPUImageUIElement alloc] initWithView:self.waterMarkContentView];
}
return _uiElementInput;
}
- (GPUImageAlphaBlendFilter *)blendFilter{
if(!_blendFilter){
_blendFilter = [[GPUImageAlphaBlendFilter alloc] init];
_blendFilter.mix = 1.0;
[_blendFilter disableSecondFrameCheck];
}
return _blendFilter;
}
- (UIView *)waterMarkContentView{
if(!_waterMarkContentView){
_waterMarkContentView = [UIView new];
_waterMarkContentView.frame = CGRectMake(0, 0, self.configuration.videoSize.width, self.configuration.videoSize.height);
_waterMarkContentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
return _waterMarkContentView;
}
- (GPUImageView *)gpuImageView{
if(!_gpuImageView){
_gpuImageView = [[GPUImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
[_gpuImageView setFillMode:kGPUImageFillModePreserveAspectRatioAndFill];
[_gpuImageView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
}
return _gpuImageView;
}
-(UIImage *)currentImage{
if(_filter){
[_filter useNextFrameForImageCapture];
return _filter.imageFromCurrentFramebuffer;
}
return nil;
}
#pragma mark -- Custom Method
- (void)processVideo:(GPUImageOutput *)output {
__weak typeof(self) _self = self;
@autoreleasepool {
GPUImageFramebuffer *imageFramebuffer = output.framebufferForOutput;
CVPixelBufferRef pixelBuffer = [imageFramebuffer pixelBuffer];
if (pixelBuffer && _self.delegate && [_self.delegate respondsToSelector:@selector(captureOutput:pixelBuffer:)]) {
[_self.delegate captureOutput:_self pixelBuffer:pixelBuffer];
}
}
}
- (void)reloadFilter{
[self.filter removeAllTargets];
[self.blendFilter removeAllTargets];
[self.uiElementInput removeAllTargets];
[self.videoCamera removeAllTargets];
[self.output removeAllTargets];
[self.cropfilter removeAllTargets];
if (self.beautyFace) {
self.output = [[LFGPUImageEmptyFilter alloc] init];
self.filter = [[LFGPUImageBeautyFilter alloc] init];
self.beautyFilter = (LFGPUImageBeautyFilter*)self.filter;
} else {
self.output = [[LFGPUImageEmptyFilter alloc] init];
self.filter = [[LFGPUImageEmptyFilter alloc] init];
self.beautyFilter = nil;
}
//< 480*640 比例为4:3 强制转换为16:9
if([self.configuration.avSessionPreset isEqualToString:AVCaptureSessionPreset640x480]){
CGRect cropRect = self.configuration.landscape ? CGRectMake(0, 0.125, 1, 0.75) : CGRectMake(0.125, 0, 0.75, 1);
self.cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:cropRect];
[self.videoCamera addTarget:self.cropfilter];
[self.cropfilter addTarget:self.filter];
}else{
[self.videoCamera addTarget:self.filter];
}
//< 添加水印
if(self.warterMarkView){
[self.filter addTarget:self.blendFilter];
[self.uiElementInput addTarget:self.blendFilter];
[self.blendFilter addTarget:self.gpuImageView];
[self.filter addTarget:self.output];
[self.uiElementInput update];
}else{
[self.filter addTarget:self.output];
[self.output addTarget:self.gpuImageView];
}
[self.filter forceProcessingAtSize:self.configuration.videoSize];
[self.output forceProcessingAtSize:self.configuration.videoSize];
[self.blendFilter forceProcessingAtSize:self.configuration.videoSize];
[self.uiElementInput forceProcessingAtSize:self.configuration.videoSize];
//< 输出数据
__weak typeof(self) _self = self;
[self.output setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
[_self processVideo:output];
}];
}
#pragma mark Notification
- (void)willEnterBackground:(NSNotification *)notification {
[UIApplication sharedApplication].idleTimerDisabled = NO;
[self.videoCamera pauseCameraCapture];
runSynchronouslyOnVideoProcessingQueue(^{
glFinish();
});
}
- (void)willEnterForeground:(NSNotification *)notification {
[self.videoCamera resumeCameraCapture];
[UIApplication sharedApplication].idleTimerDisabled = YES;
}
- (void)statusBarChanged:(NSNotification *)notification {
NSLog(@"UIApplicationWillChangeStatusBarOrientationNotification. UserInfo: %@", notification.userInfo);
UIInterfaceOrientation statusBar = [[UIApplication sharedApplication] statusBarOrientation];
if (self.configuration.landscape) {
if (statusBar == UIInterfaceOrientationLandscapeLeft) {
self.videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeRight;
} else if (statusBar == UIInterfaceOrientationLandscapeRight) {
self.videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeLeft;
}
} else {
if (statusBar == UIInterfaceOrientationPortrait) {
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortraitUpsideDown;
} else if (statusBar == UIInterfaceOrientationPortraitUpsideDown) {
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
}
}
}
@end
+35
View File
@@ -0,0 +1,35 @@
//
// AVEncoder.h
// Encoder Demo
//
// Created by Geraint Davies on 14/01/2013.
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVAssetWriter.h>
#import <AVFoundation/AVAssetWriterInput.h>
#import <AVFoundation/AVMediaFormat.h>
#import <AVFoundation/AVVideoSettings.h>
#import "sys/stat.h"
#import "LFVideoEncoder.h"
#import "LFMP4Atom.h"
typedef int (^encoder_handler_t)(NSArray *data, CMTimeValue ptsValue);
typedef int (^param_handler_t)(NSData *params);
@interface LFAVEncoder : NSObject
@property (atomic) NSUInteger bitrate;
+ (LFAVEncoder *)encoderForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate;
- (void)encodeWithBlock:(encoder_handler_t)block onParams:(param_handler_t)paramsHandler;
- (void)encodeFrame:(CMSampleBufferRef)sampleBuffer;
- (NSData *)getConfigData;
- (void)shutdown;
@property (readonly, atomic) int bitspersecond;
@end
+439
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 "LFAVEncoder.h"
#import "LFNALUnit.h"
static void *AVEncoderContext = &AVEncoderContext;
static unsigned int to_host(unsigned char *p){
return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
}
#define OUTPUT_FILE_SWITCH_POINT (50 * 1024 * 1024) // 50 MB switch point
#define MAX_FILENAME_INDEX 5 // filenames "capture1.mp4" wraps at capture5.mp4
@interface LFAVEncoder ()
{
// initial writer, used to obtain SPS/PPS from header
LFVideoEncoder *_headerWriter;
// main encoder/writer
LFVideoEncoder *_writer;
// writer output file (input to our extractor) and monitoring
NSFileHandle *_inputFile;
dispatch_queue_t _readQueue;
dispatch_source_t _readSource;
// index of current file name
BOOL _swapping;
int _currentFile;
int _height;
int _width;
// param set data
NSData *_avcC;
int _lengthSize;
// location of mdat
BOOL _foundMDAT;
uint64_t _posMDAT;
int _bytesToNextAtom;
BOOL _needParams;
// tracking if NALU is next frame
int _prev_nal_idc;
int _prev_nal_type;
// array of NSData comprising a single frame. each data is one nalu with no start code
NSMutableArray *_pendingNALU;
// FIFO for frame times
NSMutableArray *_times;
encoder_handler_t _outputBlock;
param_handler_t _paramsBlock;
// estimate bitrate over first second
int _bitspersecond;
CMTimeValue _firstpts;
}
@property (atomic) BOOL bitrateChanged;
@end
@implementation LFAVEncoder
@synthesize bitspersecond = _bitspersecond;
+ (LFAVEncoder *)encoderForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate {
LFAVEncoder *enc = [LFAVEncoder alloc];
[enc initForHeight:height andWidth:width bitrate:bitrate];
return enc;
}
- (NSString *)makeFilename {
NSString *filename = [NSString stringWithFormat:@"capture%d.mp4", _currentFile];
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
return path;
}
- (void)initForHeight:(int)height andWidth:(int)width bitrate:(int)bitrate {
_height = height;
_width = width;
_bitrate = bitrate;
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"params.mp4"];
_headerWriter = [LFVideoEncoder encoderForPath:path Height:height andWidth:width bitrate:(int)self.bitrate];
_times = [NSMutableArray arrayWithCapacity:10];
// swap between 3 filenames
_currentFile = 1;
_writer = [LFVideoEncoder encoderForPath:[self makeFilename] Height:height andWidth:width bitrate:(int)self.bitrate];
[self addObserver:self forKeyPath:NSStringFromSelector(@selector(bitrate)) options:0 context:AVEncoderContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (context == AVEncoderContext && [keyPath isEqualToString:NSStringFromSelector(@selector(bitrate))]) {
self.bitrateChanged = YES;
}
}
- (void)encodeWithBlock:(encoder_handler_t)block onParams:(param_handler_t)paramsHandler {
_outputBlock = block;
_paramsBlock = paramsHandler;
_needParams = YES;
_pendingNALU = nil;
_firstpts = -1;
_bitspersecond = 0;
}
- (BOOL)parseParams:(NSString *)path {
NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
struct stat s;
fstat([file fileDescriptor], &s);
LFMP4Atom *movie = [LFMP4Atom atomAt:0 size:(int)s.st_size type:(OSType)('file') inFile:file];
LFMP4Atom *moov = [movie childOfType:(OSType)('moov') startAt:0];
LFMP4Atom *trak = nil;
if (moov != nil) {
for (;; ) {
trak = [moov nextChild];
if (trak == nil) {
break;
}
if (trak.type == (OSType)('trak')) {
LFMP4Atom *tkhd = [trak childOfType:(OSType)('tkhd') startAt:0];
NSData *verflags = [tkhd readAt:0 size:4];
unsigned char *p = (unsigned char *)[verflags bytes];
if (p[3] & 1) {
break;
} else {
tkhd = nil;
}
}
}
}
LFMP4Atom *stsd = nil;
if (trak != nil) {
LFMP4Atom *media = [trak childOfType:(OSType)('mdia') startAt:0];
if (media != nil) {
LFMP4Atom *minf = [media childOfType:(OSType)('minf') startAt:0];
if (minf != nil) {
LFMP4Atom *stbl = [minf childOfType:(OSType)('stbl') startAt:0];
if (stbl != nil) {
stsd = [stbl childOfType:(OSType)('stsd') startAt:0];
}
}
}
}
if (stsd != nil) {
LFMP4Atom *avc1 = [stsd childOfType:(OSType)('avc1') startAt:8];
if (avc1 != nil) {
LFMP4Atom *esd = [avc1 childOfType:(OSType)('avcC') startAt:78];
if (esd != nil) {
// this is the avcC record that we are looking for
_avcC = [esd readAt:0 size:(int)esd.length];
if (_avcC != nil) {
// extract size of length field
unsigned char *p = (unsigned char *)[_avcC bytes];
_lengthSize = (p[4] & 3) + 1;
return YES;
}
}
}
}
return NO;
}
- (void)onParamsCompletion {
// the initial one-frame-only file has been completed
// Extract the avcC structure and then start monitoring the
// main file to extract video from the mdat chunk.
if ([self parseParams:_headerWriter.path]) {
if (_paramsBlock) {
_paramsBlock(_avcC);
}
_headerWriter = nil;
_swapping = NO;
_inputFile = [NSFileHandle fileHandleForReadingAtPath:_writer.path];
_readQueue = dispatch_queue_create("uk.co.gdcl.avencoder.read", DISPATCH_QUEUE_SERIAL);
_readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [_inputFile fileDescriptor], 0, _readQueue);
dispatch_source_set_event_handler(_readSource, ^{
[self onFileUpdate];
});
dispatch_resume(_readSource);
}
}
- (void)encodeFrame:(CMSampleBufferRef)sampleBuffer {
CMTime prestime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
NSNumber *pts = [NSNumber numberWithLongLong:prestime.value];
@synchronized(self){
if (_needParams) {
// the avcC record is needed for decoding and it's not written to the file until
// completion. We get round that by writing the first frame to two files; the first
// file (containing only one frame) is then finished, so we can extract the avcC record.
// Only when we've got that do we start reading from the main file.
_needParams = NO;
if ([_headerWriter encodeFrame:sampleBuffer]) {
[_headerWriter finishWithCompletionHandler:^{
[self onParamsCompletion];
}];
}
}
}
@synchronized(_times){
[_times addObject:pts];
}
@synchronized(self){
// switch output files when we reach a size limit
// to avoid runaway storage use.
if (!_swapping) {
struct stat st;
fstat([_inputFile fileDescriptor], &st);
if (st.st_size > OUTPUT_FILE_SWITCH_POINT || self.bitrateChanged) {
self.bitrateChanged = NO;
_swapping = YES;
LFVideoEncoder *oldVideo = _writer;
// construct a new writer to the next filename
if (++_currentFile > MAX_FILENAME_INDEX) {
_currentFile = 1;
}
//NSLog(@"Swap to file %d", _currentFile);
_writer = [LFVideoEncoder encoderForPath:[self makeFilename] Height:_height andWidth:_width bitrate:(int)self.bitrate];
// to do this seamlessly requires a few steps in the right order
// first, suspend the read source
if (_readSource) {
dispatch_source_cancel(_readSource);
// execute the next step as a block on the same queue, to be sure the suspend is done
dispatch_async(_readQueue, ^{
// finish the file, writing moov, before reading any more from the file
// since we don't yet know where the mdat ends
_readSource = nil;
[oldVideo finishWithCompletionHandler:^{
[self swapFiles:oldVideo.path];
}];
});
} else {
[self swapFiles:oldVideo.path];
}
}
}
[_writer encodeFrame:sampleBuffer];
}
}
- (void)swapFiles:(NSString *)oldPath {
// save current position
uint64_t pos = [_inputFile offsetInFile];
// re-read mdat length
[_inputFile seekToFileOffset:_posMDAT];
NSData *hdr = [_inputFile readDataOfLength:4];
unsigned char *p = (unsigned char *)[hdr bytes];
if (p) {
int lenMDAT = to_host(p);
// extract nalus from saved position to mdat end
uint64_t posEnd = _posMDAT + lenMDAT;
uint32_t cRead = (uint32_t)(posEnd - pos);
[_inputFile seekToFileOffset:pos];
[self readAndDeliver:cRead];
}
// close and remove file
[_inputFile closeFile];
_foundMDAT = false;
_bytesToNextAtom = 0;
[[NSFileManager defaultManager] removeItemAtPath:oldPath error:nil];
// open new file and set up dispatch source
_inputFile = [NSFileHandle fileHandleForReadingAtPath:_writer.path];
_readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [_inputFile fileDescriptor], 0, _readQueue);
dispatch_source_set_event_handler(_readSource, ^{
[self onFileUpdate];
});
dispatch_resume(_readSource);
_swapping = NO;
}
- (void)readAndDeliver:(uint32_t)cReady {
// Identify the individual NALUs and extract them
while (cReady > _lengthSize) {
NSData *lenField = [_inputFile readDataOfLength:_lengthSize];
cReady -= _lengthSize;
unsigned char *p = (unsigned char *)[lenField bytes];
unsigned int lenNALU = to_host(p);
if (lenNALU > cReady) {
// whole NALU not present -- seek back to start of NALU and wait for more
[_inputFile seekToFileOffset:[_inputFile offsetInFile] - 4];
break;
}
NSData *nalu = [_inputFile readDataOfLength:lenNALU];
cReady -= lenNALU;
[self onNALU:nalu];
}
}
- (void)onFileUpdate {
// called whenever there is more data to read in the main encoder output file.
struct stat s;
fstat([_inputFile fileDescriptor], &s);
int cReady = (int)(s.st_size - [_inputFile offsetInFile]);
// locate the mdat atom if needed
while (!_foundMDAT && (cReady > 8)) {
if (_bytesToNextAtom == 0) {
NSData *hdr = [_inputFile readDataOfLength:8];
cReady -= 8;
unsigned char *p = (unsigned char *)[hdr bytes];
int lenAtom = to_host(p);
unsigned int nameAtom = to_host(p+4);
if (nameAtom == (unsigned int)('mdat')) {
_foundMDAT = true;
_posMDAT = [_inputFile offsetInFile] - 8;
} else {
_bytesToNextAtom = lenAtom - 8;
}
}
if (_bytesToNextAtom > 0) {
int cThis = cReady < _bytesToNextAtom ? cReady : _bytesToNextAtom;
_bytesToNextAtom -= cThis;
[_inputFile seekToFileOffset:[_inputFile offsetInFile]+cThis];
cReady -= cThis;
}
}
if (!_foundMDAT) {
return;
}
// the mdat must be just encoded video.
[self readAndDeliver:cReady];
}
- (void)onEncodedFrame {
CMTimeValue pts = 0;
@synchronized(_times){
if ([_times count] > 0) {
NSNumber *time = _times[0];
pts = [time longLongValue];
[_times removeObjectAtIndex:0];
if (_firstpts < 0) {
_firstpts = pts;
}
if ((pts - _firstpts) < 1) {
int bytes = 0;
for (NSData *data in _pendingNALU) {
bytes += [data length];
}
_bitspersecond += (bytes * 8);
}
} else {
//NSLog(@"no pts for buffer");
}
}
if (_outputBlock != nil) {
_outputBlock(_pendingNALU, pts);
}
}
// combine multiple NALUs into a single frame, and in the process, convert to BSF
// by adding 00 00 01 startcodes before each NALU.
- (void)onNALU:(NSData *)nalu {
unsigned char *pNal = (unsigned char *)[nalu bytes];
int idc = pNal[0] & 0x60;
int naltype = pNal[0] & 0x1f;
if (_pendingNALU) {
LFNALUnit nal(pNal, (int)[nalu length]);
// we have existing data —is this the same frame?
// typically there are a couple of NALUs per frame in iOS encoding.
// This is not general-purpose: it assumes that arbitrary slice ordering is not allowed.
BOOL bNew = NO;
if ((idc != _prev_nal_idc) && ((idc * _prev_nal_idc) == 0)) {
bNew = YES;
} else if ((naltype != _prev_nal_type) && ((naltype == 5) || (_prev_nal_type == 5))) {
bNew = YES;
} else if ((naltype >= 1) && (naltype <= 5)) {
nal.Skip(8);
int first_mb = (int)nal.GetUE();
if (first_mb == 0) {
bNew = YES;
}
}
if (bNew) {
[self onEncodedFrame];
_pendingNALU = nil;
}
}
_prev_nal_type = naltype;
_prev_nal_idc = idc;
if (_pendingNALU == nil) {
_pendingNALU = [NSMutableArray arrayWithCapacity:2];
}
[_pendingNALU addObject:nalu];
}
- (NSData *)getConfigData {
return [_avcC copy];
}
- (void)shutdown {
@synchronized(self){
_readSource = nil;
if (_headerWriter) {
[_headerWriter finishWithCompletionHandler:^{
_headerWriter = nil;
}];
}
if (_writer) {
[_writer finishWithCompletionHandler:^{
_writer = nil;
}];
}
// !! wait for these to finish before returning and delete temp files
}
}
@end
+30
View File
@@ -0,0 +1,30 @@
//
// LFMP4Atom.h
// Encoder Demo
//
// Created by Geraint Davies on 15/01/2013.
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
//
#import <Foundation/Foundation.h>
@interface LFMP4Atom : NSObject
{
NSFileHandle *_file;
int64_t _offset;
int64_t _length;
OSType _type;
int64_t _nextChild;
}
@property OSType type;
@property int64_t length;
+ (LFMP4Atom *)atomAt:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle;
- (BOOL)init:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle;
- (NSData *)readAt:(int64_t)offset size:(int)length;
- (BOOL)setChildOffset:(int64_t)offset;
- (LFMP4Atom *)nextChild;
- (LFMP4Atom *)childOfType:(OSType)fourcc startAt:(int64_t)offset;
@end
+90
View File
@@ -0,0 +1,90 @@
//
// LFMP4Atom.m
// Encoder Demo
//
// Created by Geraint Davies on 15/01/2013.
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
//
#import "LFMP4Atom.h"
static unsigned int to_host(unsigned char *p){
return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
}
@implementation LFMP4Atom
@synthesize type = _type;
@synthesize length = _length;
+ (LFMP4Atom *)atomAt:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle {
LFMP4Atom *atom = [LFMP4Atom alloc];
if (![atom init:offset size:length type:fourcc inFile:handle]) {
return nil;
}
return atom;
}
- (BOOL)init:(int64_t)offset size:(int)length type:(OSType)fourcc inFile:(NSFileHandle *)handle {
_file = handle;
_offset = offset;
_length = length;
_type = fourcc;
_nextChild = 0;
return YES;
}
- (NSData *)readAt:(int64_t)offset size:(int)length {
[_file seekToFileOffset:_offset + offset];
return [_file readDataOfLength:length];
}
- (BOOL)setChildOffset:(int64_t)offset {
_nextChild = offset;
return YES;
}
- (LFMP4Atom *)nextChild {
if (_nextChild <= (_length - 8)) {
[_file seekToFileOffset:_offset + _nextChild];
NSData *data = [_file readDataOfLength:8];
int cHeader = 8;
unsigned char *p = (unsigned char *)[data bytes];
int64_t len = to_host(p);
OSType fourcc = to_host(p + 4);
if (len == 1) {
// 64-bit extended length
cHeader += 8;
data = [_file readDataOfLength:8];
p = (unsigned char *)[data bytes];
len = to_host(p);
len = (len << 32) + to_host(p + 4);
} else if (len == 0) {
// whole remaining parent space
len = _length - _nextChild;
}
if (fourcc == (OSType)('uuid')) {
cHeader += 16;
}
if ((len < 0) || ((len + _nextChild) > _length)) {
return nil;
}
int64_t offset = _nextChild + cHeader;
_nextChild += len;
len -= cHeader;
return [LFMP4Atom atomAt:offset+_offset size:(int)len type:fourcc inFile:_file];
}
return nil;
}
- (LFMP4Atom *)childOfType:(OSType)fourcc startAt:(int64_t)offset {
[self setChildOffset:offset];
LFMP4Atom *child = nil;
do {
child = [self nextChild];
} while ((child != nil) && (child.type != fourcc));
return child;
}
@end
+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 "LFNALUnit.h"
// --- core NAL Unit implementation ------------------------------
LFNALUnit::LFNALUnit()
: m_pStart(NULL),
m_cBytes(0){
}
bool
LFNALUnit::GetStartCode(const BYTE *& pBegin, const BYTE *& pStart, int& cRemain){
// start code is any number of 00 followed by 00 00 01
// We need to record the first 00 in pBegin and the first byte
// following the startcode in pStart.
// if no start code is found, pStart and cRemain should be unchanged.
const BYTE *pThis = pStart;
int cBytes = cRemain;
pBegin = NULL;
while (cBytes >= 4) {
if (pThis[0] == 0) {
// remember first 00
if (pBegin == NULL) {
pBegin = pThis;
}
if ((pThis[1] == 0) &&
(pThis[2] == 1)) {
// point to type byte of NAL unit
pStart = pThis + 3;
cRemain = cBytes - 3;
return true;
}
} else {
pBegin = NULL;
}
cBytes--;
pThis++;
}
return false;
}
bool
LFNALUnit::Parse(const BYTE *pBuffer, int cSpace, int LengthSize, bool bEnd){
// if we get the start code but not the whole
// NALU, we can return false but still have the length property valid
m_cBytes = 0;
ResetBitstream();
if (LengthSize > 0) {
m_pStartCodeStart = pBuffer;
if (LengthSize > cSpace) {
return false;
}
m_cBytes = 0;
for (int i = 0; i < LengthSize; i++) {
m_cBytes <<= 8;
m_cBytes += *pBuffer++;
}
if ((m_cBytes+LengthSize) <= cSpace) {
m_pStart = pBuffer;
return true;
}
} else {
// this is not length-delimited: we must look for start codes
const BYTE *pBegin;
if (GetStartCode(pBegin, pBuffer, cSpace)) {
m_pStart = pBuffer;
m_pStartCodeStart = pBegin;
// either we find another startcode, or we continue to the
// buffer end (if this is the last block of data)
if (GetStartCode(pBegin, pBuffer, cSpace)) {
m_cBytes = int(pBegin - m_pStart);
return true;
} else if (bEnd) {
// current element extends to end of buffer
m_cBytes = cSpace;
return true;
}
}
}
return false;
}
// bitwise access to data
void
LFNALUnit::ResetBitstream(){
m_idx = 0;
m_nBits = 0;
m_cZeros = 0;
}
void
LFNALUnit::Skip(int nBits){
if (nBits < m_nBits) {
m_nBits -= nBits;
} else {
nBits -= m_nBits;
while (nBits >= 8) {
GetBYTE();
nBits -= 8;
}
if (nBits) {
m_byte = GetBYTE();
m_nBits = 8;
m_nBits -= nBits;
}
}
}
// get the next byte, removing emulation prevention bytes
BYTE
LFNALUnit::GetBYTE(){
if (m_idx >= m_cBytes) {
return 0;
}
BYTE b = m_pStart[m_idx++];
// to avoid start-code emulation, a byte 0x03 is inserted
// after any 00 00 pair. Discard that here.
if (b == 0) {
m_cZeros++;
if ((m_idx < m_cBytes) && (m_cZeros == 2) && (m_pStart[m_idx] == 0x03)) {
m_idx++;
m_cZeros = 0;
}
} else {
m_cZeros = 0;
}
return b;
}
unsigned long
LFNALUnit::GetBit(){
if (m_nBits == 0) {
m_byte = GetBYTE();
m_nBits = 8;
}
m_nBits--;
return (m_byte >> m_nBits) & 0x1;
}
unsigned long
LFNALUnit::GetWord(int nBits){
unsigned long u = 0;
while (nBits > 0) {
u <<= 1;
u |= GetBit();
nBits--;
}
return u;
}
unsigned long
LFNALUnit::GetUE(){
// Exp-Golomb entropy coding: leading zeros, then a one, then
// the data bits. The number of leading zeros is the number of
// data bits, counting up from that number of 1s as the base.
// That is, if you see
// 0001010
// You have three leading zeros, so there are three data bits (010)
// counting up from a base of 111: thus 111 + 010 = 1001 = 9
int cZeros = 0;
while (GetBit() == 0) {
cZeros++;
}
return GetWord(cZeros) + ((1 << cZeros)-1);
}
long
LFNALUnit::GetSE(){
// same as UE but signed.
// basically the unsigned numbers are used as codes to indicate signed numbers in pairs
// in increasing value. Thus the encoded values
// 0, 1, 2, 3, 4
// mean
// 0, 1, -1, 2, -2 etc
unsigned long UE = GetUE();
bool bPositive = UE & 1;
long SE = (UE + 1) >> 1;
if (!bPositive) {
SE = -SE;
}
return SE;
}
// --- sequence params parsing ---------------
LFSeqParamSet::LFSeqParamSet()
: m_cx(0),
m_cy(0),
m_FrameBits(0){
// SetRect(&m_rcFrame, 0, 0, 0, 0);
}
void
ScalingList(int size, LFNALUnit *pnalu){
long lastScale = 8;
long nextScale = 8;
for (int j = 0; j < size; j++) {
if (nextScale != 0) {
long delta = pnalu->GetSE();
nextScale = (lastScale + delta + 256) %256;
}
int scaling_list_j = (nextScale == 0) ? (int)lastScale : (int)nextScale;
lastScale = scaling_list_j;
}
}
bool
LFSeqParamSet::Parse(LFNALUnit *pnalu){
if (pnalu->Type() != LFNALUnit::NAL_Sequence_Params) {
return false;
}
// with the UE/SE type encoding, we must decode all the values
// to get through to the ones we want
pnalu->ResetBitstream();
pnalu->Skip(8); // type
m_Profile =(int) pnalu->GetWord(8);
m_Compatibility = (BYTE)pnalu->GetWord(8);
m_Level = (int)pnalu->GetWord(8);
/*int seq_param_id =*/ pnalu->GetUE();
if ((m_Profile == 100) || (m_Profile == 110) || (m_Profile == 122) || (m_Profile == 144)) {
int chroma_fmt = (int)pnalu->GetUE();
if (chroma_fmt == 3) {
pnalu->Skip(1);
}
/* int bit_depth_luma_minus8 = */ pnalu->GetUE();
/* int bit_depth_chroma_minus8 = */ pnalu->GetUE();
pnalu->Skip(1);
int seq_scaling_matrix_present = (int)pnalu->GetBit();
if (seq_scaling_matrix_present) {
for (int i = 0; i < 8; i++) {
if (pnalu->GetBit()) {
if (i < 6) {
ScalingList(16, pnalu);
} else {
ScalingList(64, pnalu);
}
}
}
}
}
int log2_frame_minus4 = (int)pnalu->GetUE();
m_FrameBits = log2_frame_minus4 + 4;
int POCtype = (int)pnalu->GetUE();
if (POCtype == 0) {
/*int log2_poc_minus4 =*/ pnalu->GetUE();
} else if (POCtype == 1) {
pnalu->Skip(1); // delta always zero
/*int nsp_offset =*/ pnalu->GetSE();
/*int nsp_top_to_bottom = */ pnalu->GetSE();
int num_ref_in_cycle = (int)pnalu->GetUE();
for (int i = 0; i < num_ref_in_cycle; i++) {
/*int sf_offset =*/ pnalu->GetSE();
}
} else if (POCtype != 2) {
return false;
}
// else for POCtype == 2, no additional data in stream
/*int num_ref_frames =*/ pnalu->GetUE();
/*int gaps_allowed =*/ pnalu->GetBit();
int mbs_width = (int)pnalu->GetUE();
int mbs_height = (int)pnalu->GetUE();
m_cx = (mbs_width+1) * 16;
m_cy = (mbs_height+1) * 16;
// smoke test validation of sps
if ((m_cx > 2000) || (m_cy > 2000)) {
return false;
}
// if this is false, then sizes are field sizes and need adjusting
m_bFrameOnly = pnalu->GetBit() ? true : false;
if (!m_bFrameOnly) {
pnalu->Skip(1); // adaptive frame/field
}
pnalu->Skip(1); // direct 8x8
#if 0
SetRect(&m_rcFrame, 0, 0, 0, 0);
bool bCrop = pnalu->GetBit() ? true : false;
if (bCrop) {
// get cropping rect
// store as exclusive, pixel parameters relative to frame
m_rcFrame.left = pnalu->GetUE() * 2;
m_rcFrame.right = pnalu->GetUE() * 2;
m_rcFrame.top = pnalu->GetUE() * 2;
m_rcFrame.bottom = pnalu->GetUE() * 2;
}
if (!IsRectEmpty(&m_rcFrame)) {
m_rcFrame.right = m_cx - m_rcFrame.right;
m_rcFrame.bottom = m_cy - m_rcFrame.bottom;
}
#endif
// adjust rect from 2x2 units to pixels
if (!m_bFrameOnly) {
// adjust heights from field to frame
m_cy *= 2;
#if 0
m_rcFrame.top *= 2;
m_rcFrame.bottom *= 2;
#endif
}
// .. rest are not interesting yet
m_nalu = *pnalu;
return true;
}
// --- slice header --------------------
bool
LFSliceHeader::Parse(LFNALUnit *pnalu){
switch (pnalu->Type()) {
case LFNALUnit::NAL_IDR_Slice:
case LFNALUnit::NAL_Slice:
case LFNALUnit::NAL_PartitionA:
// all these begin with a slice header
break;
default:
return false;
}
// slice header has the 1-byte type, then one UE value,
// then the frame number.
pnalu->ResetBitstream();
pnalu->Skip(8); // NALU type
pnalu->GetUE(); // first mb in slice
pnalu->GetUE(); // slice type
pnalu->GetUE(); // pic param set id
m_framenum = (int)pnalu->GetWord(m_nBitsFrame);
return true;
}
// --- SEI ----------------------
LFSEIMessage::LFSEIMessage(LFNALUnit *pnalu){
m_pnalu = pnalu;
const BYTE *p = pnalu->Start();
p++; // nalu type byte
m_type = 0;
while (*p == 0xff) {
m_type += 255;
p++;
}
m_type += *p;
p++;
m_length = 0;
while (*p == 0xff) {
m_type += 255;
p++;
}
m_length += *p;
p++;
m_idxPayload = int(p - m_pnalu->Start());
}
LFavcCHeader::LFavcCHeader(const BYTE *header, int cBytes){
if (cBytes < 8) {
return;
}
const BYTE *pEnd = header + cBytes;
int cSeq = header[5] & 0x1f;
header += 6;
for (int i = 0; i < cSeq; i++) {
if ((header+2) > pEnd) {
return;
}
int cThis = (header[0] << 8) + header[1];
header += 2;
if ((header+cThis) > pEnd) {
return;
}
if (i == 0) {
LFNALUnit n(header, cThis);
m_sps = n;
}
header += cThis;
}
if ((header + 3) >= pEnd) {
return;
}
int cPPS = header[0];
if (cPPS > 0) {
int cThis = (header[1] << 8) + header[2];
header += 3;
LFNALUnit n(header, cThis);
m_pps = n;
}
}
+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 LFNALUnit
{
public:
LFNALUnit();
LFNALUnit(const BYTE* pStart, int len){
m_pStart = m_pStartCodeStart = pStart;
m_cBytes = len;
ResetBitstream();
}
virtual ~LFNALUnit() {
}
// assignment copies a pointer into a fixed buffer managed elsewhere. We do not copy the data
LFNALUnit(const LFNALUnit &r){
m_pStart = r.m_pStart;
m_cBytes = r.m_cBytes;
ResetBitstream();
}
const LFNALUnit& operator = (const LFNALUnit &r)
{
m_pStart = r.m_pStart;
m_cBytes = r.m_cBytes;
ResetBitstream();
return *this;
}
enum eNALType {
NAL_Slice = 1,
NAL_PartitionA = 2,
NAL_PartitionB = 3,
NAL_PartitionC = 4,
NAL_IDR_Slice = 5,
NAL_SEI = 6,
NAL_Sequence_Params = 7,
NAL_Picture_Params = 8,
NAL_AUD = 9,
};
// identify a NAL unit within a buffer.
// If LengthSize is non-zero, it is the number of bytes
// of length field we expect. Otherwise, we expect start-code
// delimiters.
bool Parse(const BYTE *pBuffer, int cSpace, int LengthSize, bool bEnd);
eNALType Type(){
if (m_pStart == NULL) {
return eNALType(0);
}
return eNALType(m_pStart[0] & 0x1F);
}
int Length(){
return m_cBytes;
}
const BYTE *Start(){
return m_pStart;
}
// bitwise access to data
void ResetBitstream();
void Skip(int nBits);
unsigned long GetWord(int nBits);
unsigned long GetUE();
long GetSE();
BYTE GetBYTE();
unsigned long GetBit();
const BYTE *StartCodeStart() {
return m_pStartCodeStart;
}
private:
bool GetStartCode(const BYTE *& pBegin, const BYTE *& pStart, int& cRemain);
private:
const BYTE *m_pStartCodeStart;
const BYTE *m_pStart;
int m_cBytes;
// bitstream access
int m_idx;
int m_nBits;
BYTE m_byte;
int m_cZeros;
};
// simple parser for the Sequence parameter set things that we need
class LFSeqParamSet
{
public:
LFSeqParamSet();
bool Parse(LFNALUnit *pnalu);
int FrameBits(){
return m_FrameBits;
}
long EncodedWidth(){
return m_cx;
}
long EncodedHeight(){
return m_cy;
}
#if 0
long CroppedWidth(){
if (IsRectEmpty(&m_rcFrame)) {
return EncodedWidth();
}
return m_rcFrame.right - m_rcFrame.left;
}
long CroppedHeight(){
if (IsRectEmpty(&m_rcFrame)) {
return EncodedHeight();
}
return m_rcFrame.bottom - m_rcFrame.top;
}
RECT *CropRect(){
return &m_rcFrame;
}
#endif
bool Interlaced(){
return !m_bFrameOnly;
}
unsigned int Profile() {
return m_Profile;
}
unsigned int Level() {
return m_Level;
}
BYTE Compat() {
return m_Compatibility;
}
LFNALUnit *NALU() {
return &m_nalu;
}
private:
LFNALUnit m_nalu;
int m_FrameBits;
long m_cx;
long m_cy;
// RECT m_rcFrame;
bool m_bFrameOnly;
int m_Profile;
int m_Level;
BYTE m_Compatibility;
};
// extract frame num from slice headers
class LFSliceHeader
{
public:
LFSliceHeader(int nBitsFrame)
: m_framenum(0),
m_nBitsFrame(nBitsFrame){
}
bool Parse(LFNALUnit *pnalu);
int FrameNum(){
return m_framenum;
}
private:
int m_framenum;
int m_nBitsFrame;
};
// SEI message structure
class LFSEIMessage
{
public:
LFSEIMessage(LFNALUnit* pnalu);
int Type() {
return m_type;
}
int Length() {
return m_length;
}
const BYTE *Payload() {
return m_pnalu->Start() + m_idxPayload;
}
private:
LFNALUnit *m_pnalu;
int m_type;
int m_length;
int m_idxPayload;
};
// avcC structure from MP4
class LFavcCHeader
{
public:
LFavcCHeader(const BYTE* header, int cBytes);
LFNALUnit *sps() {
return &m_sps;
}
LFNALUnit *pps() {
return &m_pps;
}
private:
LFNALUnit m_sps;
LFNALUnit m_pps;
};
+27
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 LFVideoEncoder : NSObject
@property NSString *path;
@property (nonatomic, readonly) NSUInteger bitrate;
+ (LFVideoEncoder *)encoderForPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate;
- (void)initPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate;
- (void)finishWithCompletionHandler:(void (^)(void))handler;
- (BOOL)encodeFrame:(CMSampleBufferRef)sampleBuffer;
@end
+76
View File
@@ -0,0 +1,76 @@
//
// LFVideoEncoder.m
// Encoder Demo
//
// Created by Geraint Davies on 14/01/2013.
// Copyright (c) 2013 GDCL http://www.gdcl.co.uk/license.htm
//
#import "LFVideoEncoder.h"
@implementation LFVideoEncoder
{
AVAssetWriter *_writer;
AVAssetWriterInput *_writerInput;
NSString *_path;
}
@synthesize path = _path;
+ (LFVideoEncoder *)encoderForPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate {
LFVideoEncoder *enc = [LFVideoEncoder alloc];
[enc initPath:path Height:height andWidth:width bitrate:bitrate];
return enc;
}
- (void)initPath:(NSString *)path Height:(int)height andWidth:(int)width bitrate:(int)bitrate {
self.path = path;
_bitrate = bitrate;
[[NSFileManager defaultManager] removeItemAtPath:self.path error:nil];
NSURL *url = [NSURL fileURLWithPath:self.path];
NSDictionary *settings = @{
AVVideoCodecKey: AVVideoCodecH264,
AVVideoWidthKey: @(width),
AVVideoHeightKey: @(height),
AVVideoCompressionPropertiesKey: @{
AVVideoAverageBitRateKey: @(self.bitrate),
AVVideoMaxKeyFrameIntervalKey: @(30 * 2),
AVVideoProfileLevelKey: AVVideoProfileLevelH264Baseline41,
AVVideoAllowFrameReorderingKey: @NO,
}
};
_writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:settings];
_writerInput.expectsMediaDataInRealTime = YES;
_writer = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeQuickTimeMovie error:nil];
[_writer addInput:_writerInput];
}
- (void)finishWithCompletionHandler:(void (^)(void))handler {
if (_writer.status == AVAssetWriterStatusWriting) {
[_writer finishWritingWithCompletionHandler:handler];
}
}
- (BOOL)encodeFrame:(CMSampleBufferRef)sampleBuffer {
if (CMSampleBufferDataIsReady(sampleBuffer)) {
if (_writer.status == AVAssetWriterStatusUnknown) {
CMTime startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
[_writer startWriting];
[_writer startSessionAtSourceTime:startTime];
}
if (_writer.status == AVAssetWriterStatusFailed) {
//NSLog(@"AVAssetWriterStatusFailed");
return NO;
}
if (_writerInput.readyForMoreMediaData == YES) {
[_writerInput appendSampleBuffer:sampleBuffer];
return YES;
}
}
return NO;
}
@end
@@ -15,17 +15,17 @@
/// 编码器编码后回调
@protocol LFAudioEncodingDelegate <NSObject>
@required
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame*)frame;
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame *)frame;
@end
/// 编码器抽象的接口
@protocol LFAudioEncoding <NSObject>
@required
- (void)encodeAudioData:(AudioBufferList)inBufferList timeStamp:(uint64_t)timeStamp;
- (void)encodeAudioData:(nullable NSData*)audioData timeStamp:(uint64_t)timeStamp;
- (void)stopEncoder;
@optional
- (nullable instancetype)initWithAudioStreamConfiguration:(nullable LFLiveAudioConfiguration*)configuration;
- (nullable instancetype)initWithAudioStreamConfiguration:(nullable LFLiveAudioConfiguration *)configuration;
- (void)setDelegate:(nullable id<LFAudioEncodingDelegate>)delegate;
- (nullable NSData*)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength;
- (nullable NSData *)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength;
@end
@@ -0,0 +1,16 @@
//
// LFH264VideoEncoder
// LFLiveKit
//
// Created by feng on 7/5/16.
// Copyright (c) 2014 zhanqi.tv. All rights reserved.
//
#import "LFVideoEncoding.h"
@interface LFH264VideoEncoder : NSObject <LFVideoEncoding> {
}
- (void)shutdown;
@end
@@ -0,0 +1,263 @@
//
// LFH264VideoEncoder
// LFLiveKit
//
// Created by feng on 7/5/16.
// Copyright (c) 2014 zhanqi.tv. All rights reserved.
//
#import <CoreMedia/CoreMedia.h>
#import <mach/mach_time.h>
#import "LFNALUnit.h"
#import "LFAVEncoder.h"
#import "LFH264VideoEncoder.h"
#import "LFVideoFrame.h"
@interface LFH264VideoEncoder() {
FILE *fp;
NSInteger frameCount;
BOOL enabledWriteVideoFile;
}
@property (nonatomic, strong) LFLiveVideoConfiguration *configuration;
@property (nonatomic, weak) id<LFVideoEncodingDelegate> h264Delegate;
@property (nonatomic) BOOL isBackGround;
@property (nonatomic) NSInteger currentVideoBitRate;
@property (nonatomic, strong) dispatch_queue_t sendQueue;
@property (nonatomic, strong) LFAVEncoder *encoder;
@property (nonatomic, strong) NSData *naluStartCode;
@property (nonatomic, strong) NSMutableData *videoSPSandPPS;
@property (nonatomic, strong) NSMutableData *spsData;
@property (nonatomic, strong) NSMutableData *ppsData;
@property (nonatomic, strong) NSMutableData *sei;
@property (nonatomic) CMTimeScale timescale;
@property (nonatomic, strong) NSMutableArray *orphanedFrames;
@property (nonatomic, strong) NSMutableArray *orphanedSEIFrames;
@property (nonatomic) CMTime lastPTS;
@end
@implementation LFH264VideoEncoder
#pragma mark -- LifeCycle
- (instancetype)initWithVideoStreamConfiguration:(LFLiveVideoConfiguration *)configuration {
if (self = [super init]) {
NSLog(@"USE LF264VideoEncoder");
_configuration = configuration;
[self initCompressionSession];
}
return self;
}
- (void)initCompressionSession{
_sendQueue = dispatch_queue_create("com.youku.laifeng.h264.sendframe", DISPATCH_QUEUE_SERIAL);
[self initializeNALUnitStartCode];
_lastPTS = kCMTimeInvalid;
_timescale = 1000;
frameCount = 0;
#ifdef DEBUG
enabledWriteVideoFile = NO;
[self initForFilePath];
#endif
_encoder = [LFAVEncoder encoderForHeight:(int)_configuration.videoSize.height andWidth:(int)_configuration.videoSize.width bitrate:(int)_configuration.videoBitRate];
[_encoder encodeWithBlock:^int(NSArray* dataArray, CMTimeValue ptsValue) {
[self incomingVideoFrames:dataArray ptsValue:ptsValue];
return 0;
} onParams:^int(NSData *data) {
[self generateSPSandPPS];
return 0;
}];
}
- (void) initializeNALUnitStartCode {
NSUInteger naluLength = 4;
uint8_t *nalu = (uint8_t*)malloc(naluLength * sizeof(uint8_t));
nalu[0] = 0x00;
nalu[1] = 0x00;
nalu[2] = 0x00;
nalu[3] = 0x01;
_naluStartCode = [NSData dataWithBytesNoCopy:nalu length:naluLength freeWhenDone:YES];
}
- (void) generateSPSandPPS {
NSData* config = _encoder.getConfigData;
if (!config) {
return;
}
LFavcCHeader avcC((const BYTE*)[config bytes], (int)[config length]);
LFSeqParamSet seqParams;
seqParams.Parse(avcC.sps());
NSData* spsData = [NSData dataWithBytes:avcC.sps()->Start() length:avcC.sps()->Length()];
NSData *ppsData = [NSData dataWithBytes:avcC.pps()->Start() length:avcC.pps()->Length()];
_spsData = [NSMutableData dataWithCapacity:avcC.sps()->Length()+_naluStartCode.length];
_ppsData = [NSMutableData dataWithCapacity:avcC.pps()->Length()+_naluStartCode.length];
[_spsData appendData:_naluStartCode];
[_spsData appendData:spsData];
[_ppsData appendData:_naluStartCode];
[_ppsData appendData:ppsData];
_videoSPSandPPS = [NSMutableData dataWithCapacity:avcC.sps()->Length() + avcC.pps()->Length() + _naluStartCode.length * 2];
[_videoSPSandPPS appendData:_naluStartCode];
[_videoSPSandPPS appendData:spsData];
[_videoSPSandPPS appendData:_naluStartCode];
[_videoSPSandPPS appendData:ppsData];
}
- (void)setVideoBitRate:(NSInteger)videoBitRate{
_currentVideoBitRate = videoBitRate;
_encoder.bitrate = _currentVideoBitRate;
}
- (NSInteger)videoBitRate{
return _currentVideoBitRate;
}
- (void)setDelegate:(id<LFVideoEncodingDelegate>)delegate{
_h264Delegate = delegate;
}
- (void)encodeVideoData:(CVPixelBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp {
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
CMVideoFormatDescriptionRef videoInfo = NULL;
CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &videoInfo);
CMTime frameTime = CMTimeMake(timeStamp, 1000);
CMTime duration = CMTimeMake(1, (int32_t)_configuration.videoFrameRate);
CMSampleTimingInfo timing = {duration, frameTime, kCMTimeInvalid};
CMSampleBufferRef sampleBuffer = NULL;
CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, YES, NULL, NULL, videoInfo, &timing, &sampleBuffer);
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
[_encoder encodeFrame:sampleBuffer];
CFRelease(videoInfo);
CFRelease(sampleBuffer);
frameCount++;
}
- (void)addOrphanedFramesFromArray:(NSArray*)frames {
for (NSData *data in frames) {
unsigned char* pNal = (unsigned char*)[data bytes];
int idc = pNal[0] & 0x60;
int naltype = pNal[0] & 0x1f;
if (idc == 0 && naltype == 6) { // SEI
[self.orphanedSEIFrames addObject:data];
} else {
[self.orphanedFrames addObject:data];
}
}
}
- (void)writeVideoFrames:(NSArray*)frames pts:(CMTime)pts {
NSMutableArray *totalFrames = [NSMutableArray array];
if (self.orphanedSEIFrames.count > 0) {
[totalFrames addObjectsFromArray:self.orphanedSEIFrames];
[self.orphanedSEIFrames removeAllObjects];
}
[totalFrames addObjectsFromArray:frames];
NSMutableData *aggregateFrameData = [NSMutableData data];
//BOOL hasKeyframe = NO;
for (NSData *data in totalFrames) {
unsigned char* pNal = (unsigned char*)[data bytes];
int idc = pNal[0] & 0x60;
int naltype = pNal[0] & 0x1f;
NSData *videoData = nil;
if (idc == 0 && naltype == 6) { // SEI
_sei = [NSMutableData dataWithData:data];
continue;
} else if (naltype == 5) { // IDR
//hasKeyframe = YES;
NSMutableData *IDRData = [NSMutableData dataWithData:_videoSPSandPPS];
if (_sei) {
[IDRData appendData:_naluStartCode];
[IDRData appendData:_sei];
_sei = nil;
}
[IDRData appendData:_naluStartCode];
[IDRData appendData:data];
videoData = IDRData;
} else {
NSMutableData *regularData = [NSMutableData dataWithData:_naluStartCode];
[regularData appendData:data];
videoData = regularData;
}
[aggregateFrameData appendData:videoData];
LFVideoFrame *videoFrame = [LFVideoFrame new];
const char *dataBuffer = (const char *)aggregateFrameData.bytes;
videoFrame.data = [NSMutableData dataWithBytes:dataBuffer + _naluStartCode.length length:aggregateFrameData.length - _naluStartCode.length];
videoFrame.timestamp = pts.value;
videoFrame.isKeyFrame = (naltype == 5);
videoFrame.sps = _spsData;
videoFrame.pps = _ppsData;
if(self.h264Delegate && [self.h264Delegate respondsToSelector:@selector(videoEncoder:videoFrame:)]){
[self.h264Delegate videoEncoder:self videoFrame:videoFrame];
}
}
if (self->enabledWriteVideoFile) {
fwrite(aggregateFrameData.bytes, 1, aggregateFrameData.length, self->fp);
}
}
- (void) incomingVideoFrames:(NSArray*)frames ptsValue:(CMTimeValue)ptsValue {
if (ptsValue == 0) {
[self addOrphanedFramesFromArray:frames];
return;
}
if (!_videoSPSandPPS) {
[self generateSPSandPPS];
}
CMTime pts = CMTimeMake(ptsValue, _timescale);
if (self.orphanedFrames.count > 0) {
CMTime ptsDiff = CMTimeSubtract(pts, _lastPTS);
NSUInteger orphanedFramesCount = self.orphanedFrames.count;
// NSLog(@"lastPTS before first orphaned frame: %lld", _lastPTS.value);
for (NSData *frame in self.orphanedFrames) {
CMTime fakePTSDiff = CMTimeMultiplyByFloat64(ptsDiff, 1.0/(orphanedFramesCount + 1));
CMTime fakePTS = CMTimeAdd(_lastPTS, fakePTSDiff);
// NSLog(@"orphan frame fakePTS: %lld", fakePTS.value);
[self writeVideoFrames:@[frame] pts:fakePTS];
}
// NSLog(@"pts after orphaned frame: %lld", pts.value);
[self.orphanedFrames removeAllObjects];
}
[self writeVideoFrames:frames pts:pts];
_lastPTS = pts;
}
- (void) dealloc {
[_encoder shutdown];
}
- (void)shutdown {
[_encoder encodeWithBlock:nil onParams:nil];
}
- (void)initForFilePath {
NSString *path = [self GetFilePathByfileName:@"IOSCamDemo.h264"];
NSLog(@"%@", path);
self->fp = fopen([path cStringUsingEncoding:NSUTF8StringEncoding], "wb");
}
- (NSString *)GetFilePathByfileName:(NSString*)filename {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:filename];
return writablePath;
}
@end
+295
View File
@@ -0,0 +1,295 @@
//
// LFHardwareAudioEncoder.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFHardwareAudioEncoder.h"
@interface LFHardwareAudioEncoder (){
AudioConverterRef m_converter;
char *leftBuf;
char *aacBuf;
NSInteger leftLength;
FILE *fp;
BOOL enabledWriteVideoFile;
}
@property (nonatomic, strong) LFLiveAudioConfiguration *configuration;
@property (nonatomic, weak) id<LFAudioEncodingDelegate> aacDeleage;
@end
@implementation LFHardwareAudioEncoder
- (instancetype)initWithAudioStreamConfiguration:(nullable LFLiveAudioConfiguration *)configuration {
if (self = [super init]) {
NSLog(@"USE LFHardwareAudioEncoder");
_configuration = configuration;
if (!leftBuf) {
leftBuf = malloc(_configuration.bufferLength);
}
if (!aacBuf) {
aacBuf = malloc(_configuration.bufferLength);
}
#ifdef DEBUG
enabledWriteVideoFile = NO;
[self initForFilePath];
#endif
}
return self;
}
- (void)dealloc {
if (aacBuf) free(aacBuf);
if (leftBuf) free(leftBuf);
}
#pragma mark -- LFAudioEncoder
- (void)setDelegate:(id<LFAudioEncodingDelegate>)delegate {
_aacDeleage = delegate;
}
- (void)encodeAudioData:(nullable NSData*)audioData timeStamp:(uint64_t)timeStamp {
if (![self createAudioConvert]) {
return;
}
if(leftLength + audioData.length >= self.configuration.bufferLength){
///<  发送
NSInteger totalSize = leftLength + audioData.length;
NSInteger encodeCount = totalSize/self.configuration.bufferLength;
char *totalBuf = malloc(totalSize);
char *p = totalBuf;
memset(totalBuf, (int)totalSize, 0);
memcpy(totalBuf, leftBuf, leftLength);
memcpy(totalBuf + leftLength, audioData.bytes, audioData.length);
for(NSInteger index = 0;index < encodeCount;index++){
[self encodeBuffer:p timeStamp:timeStamp];
p += self.configuration.bufferLength;
}
free(totalBuf);
leftLength = totalSize%self.configuration.bufferLength;
memset(leftBuf, 0, self.configuration.bufferLength);
memcpy(leftBuf, totalBuf + (totalSize -leftLength), leftLength);
}else{
///< 积累
memcpy(leftBuf+leftLength, audioData.bytes, audioData.length);
leftLength = leftLength + audioData.length;
}
}
- (void)encodeBuffer:(char*)buf timeStamp:(uint64_t)timeStamp{
AudioBuffer inBuffer;
inBuffer.mNumberChannels = 1;
inBuffer.mData = buf;
inBuffer.mDataByteSize = (UInt32)self.configuration.bufferLength;
AudioBufferList buffers;
buffers.mNumberBuffers = 1;
buffers.mBuffers[0] = inBuffer;
// 初始化一个输出缓冲列表
AudioBufferList outBufferList;
outBufferList.mNumberBuffers = 1;
outBufferList.mBuffers[0].mNumberChannels = inBuffer.mNumberChannels;
outBufferList.mBuffers[0].mDataByteSize = inBuffer.mDataByteSize; // 设置缓冲区大小
outBufferList.mBuffers[0].mData = aacBuf; // 设置AAC缓冲区
UInt32 outputDataPacketSize = 1;
if (AudioConverterFillComplexBuffer(m_converter, inputDataProc, &buffers, &outputDataPacketSize, &outBufferList, NULL) != noErr) {
return;
}
LFAudioFrame *audioFrame = [LFAudioFrame new];
audioFrame.timestamp = timeStamp;
audioFrame.data = [NSData dataWithBytes:aacBuf length:outBufferList.mBuffers[0].mDataByteSize];
char exeData[2];
exeData[0] = _configuration.asc[0];
exeData[1] = _configuration.asc[1];
audioFrame.audioInfo = [NSData dataWithBytes:exeData length:2];
if (self.aacDeleage && [self.aacDeleage respondsToSelector:@selector(audioEncoder:audioFrame:)]) {
[self.aacDeleage audioEncoder:self audioFrame:audioFrame];
}
if (self->enabledWriteVideoFile) {
NSData *adts = [self adtsData:_configuration.numberOfChannels rawDataLength:audioFrame.data.length];
fwrite(adts.bytes, 1, adts.length, self->fp);
fwrite(audioFrame.data.bytes, 1, audioFrame.data.length, self->fp);
}
}
- (void)stopEncoder {
}
#pragma mark -- CustomMethod
- (BOOL)createAudioConvert { //根据输入样本初始化一个编码转换器
if (m_converter != nil) {
return TRUE;
}
AudioStreamBasicDescription inputFormat = {0};
inputFormat.mSampleRate = _configuration.audioSampleRate;
inputFormat.mFormatID = kAudioFormatLinearPCM;
inputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
inputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;
inputFormat.mFramesPerPacket = 1;
inputFormat.mBitsPerChannel = 16;
inputFormat.mBytesPerFrame = inputFormat.mBitsPerChannel / 8 * inputFormat.mChannelsPerFrame;
inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
AudioStreamBasicDescription outputFormat; // 这里开始是输出音频格式
memset(&outputFormat, 0, sizeof(outputFormat));
outputFormat.mSampleRate = inputFormat.mSampleRate; // 采样率保持一致
outputFormat.mFormatID = kAudioFormatMPEG4AAC; // AAC编码 kAudioFormatMPEG4AAC kAudioFormatMPEG4AAC_HE_V2
outputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;;
outputFormat.mFramesPerPacket = 1024; // AAC一帧是1024个字节
const OSType subtype = kAudioFormatMPEG4AAC;
AudioClassDescription requestedCodecs[2] = {
{
kAudioEncoderComponentType,
subtype,
kAppleSoftwareAudioCodecManufacturer
},
{
kAudioEncoderComponentType,
subtype,
kAppleHardwareAudioCodecManufacturer
}
};
OSStatus result = AudioConverterNewSpecific(&inputFormat, &outputFormat, 2, requestedCodecs, &m_converter);;
UInt32 outputBitrate = _configuration.audioBitrate;
UInt32 propSize = sizeof(outputBitrate);
// UInt32 outputPacketSize = 0;
if(result == noErr) {
result = AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, propSize, &outputBitrate);
}
// if(result == noErr) {
// AudioConverterGetProperty(m_converter, kAudioConverterPropertyMaximumOutputPacketSize, &propSize, &outputPacketSize);
// }
return YES;
}
#pragma mark -- AudioCallBack
OSStatus inputDataProc(AudioConverterRef inConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription * *outDataPacketDescription, void *inUserData) { //<span style="font-family: Arial, Helvetica, sans-serif;">AudioConverterFillComplexBuffer 编码过程中,会要求这个函数来填充输入数据,也就是原始PCM数据</span>
AudioBufferList bufferList = *(AudioBufferList *)inUserData;
ioData->mBuffers[0].mNumberChannels = 1;
ioData->mBuffers[0].mData = bufferList.mBuffers[0].mData;
ioData->mBuffers[0].mDataByteSize = bufferList.mBuffers[0].mDataByteSize;
return noErr;
}
#pragma mark -- Custom Method
/**
* Add ADTS header at the beginning of each and every AAC packet.
* This is needed as MediaCodec encoder generates a packet of raw
* AAC data.
*
* Note the packetLen must count in the ADTS header itself.
* See: http://wiki.multimedia.cx/index.php?title=ADTS
* Also: http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Channel_Configurations
**/
- (NSData *)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength {
int adtsLength = 7;
char *packet = malloc(sizeof(char) * adtsLength);
// Variables Recycled by addADTStoPacket
int profile = 2; //AAC LC
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
NSInteger freqIdx = [self sampleRateIndex:self.configuration.audioSampleRate]; //44.1KHz
int chanCfg = (int)channel; //MPEG-4 Audio Channel Configuration. 1 Channel front-center
NSUInteger fullLength = adtsLength + rawDataLength;
// fill in ADTS data
packet[0] = (char)0xFF; // 11111111 = syncword
packet[1] = (char)0xF9; // 1111 1 00 1 = syncword MPEG-2 Layer CRC
packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
packet[3] = (char)(((chanCfg&3)<<6) + (fullLength>>11));
packet[4] = (char)((fullLength&0x7FF) >> 3);
packet[5] = (char)(((fullLength&7)<<5) + 0x1F);
packet[6] = (char)0xFC;
NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES];
return data;
}
- (NSInteger)sampleRateIndex:(NSInteger)frequencyInHz {
NSInteger sampleRateIndex = 0;
switch (frequencyInHz) {
case 96000:
sampleRateIndex = 0;
break;
case 88200:
sampleRateIndex = 1;
break;
case 64000:
sampleRateIndex = 2;
break;
case 48000:
sampleRateIndex = 3;
break;
case 44100:
sampleRateIndex = 4;
break;
case 32000:
sampleRateIndex = 5;
break;
case 24000:
sampleRateIndex = 6;
break;
case 22050:
sampleRateIndex = 7;
break;
case 16000:
sampleRateIndex = 8;
break;
case 12000:
sampleRateIndex = 9;
break;
case 11025:
sampleRateIndex = 10;
break;
case 8000:
sampleRateIndex = 11;
break;
case 7350:
sampleRateIndex = 12;
break;
default:
sampleRateIndex = 15;
}
return sampleRateIndex;
}
- (void)initForFilePath {
NSString *path = [self GetFilePathByfileName:@"IOSCamDemo_HW.aac"];
NSLog(@"%@", path);
self->fp = fopen([path cStringUsingEncoding:NSUTF8StringEncoding], "wb");
}
- (NSString *)GetFilePathByfileName:(NSString*)filename {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:filename];
return writablePath;
}
@end
@@ -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;
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval,(__bridge CFTypeRef)@(_configuration.videoMaxKeyframeInterval));
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration,(__bridge CFTypeRef)@(_configuration.videoMaxKeyframeInterval));
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate, (__bridge CFTypeRef)@(_configuration.videoFrameRate));
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AverageBitRate, (__bridge CFTypeRef)@(_configuration.videoBitRate));
NSArray *limit = @[@(_configuration.videoBitRate * 1.5/8),@(1)];
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_DataRateLimits, (__bridge CFArrayRef)limit);
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanFalse);
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Main_AutoLevel);
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse);
status = VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_H264EntropyMode, kVTH264EntropyMode_CABAC);
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)];
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_DataRateLimits, (__bridge CFArrayRef)limit);
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Main_AutoLevel);
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanTrue);
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_H264EntropyMode, kVTH264EntropyMode_CABAC);
VTCompressionSessionPrepareToEncodeFrames(compressionSession);
}
- (void)setVideoBitRate:(NSInteger)videoBitRate{
if(_isBackGround) return;
- (void)setVideoBitRate:(NSInteger)videoBitRate {
if (_isBackGround) return;
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AverageBitRate, (__bridge CFTypeRef)@(videoBitRate));
NSArray *limit = @[@(videoBitRate * 1.5/8),@(1)];
NSArray *limit = @[@(videoBitRate * 1.5/8), @(1)];
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_DataRateLimits, (__bridge CFArrayRef)limit);
_currentVideoBitRate = videoBitRate;
}
-(NSInteger)videoBitRate{
- (NSInteger)videoBitRate {
return _currentVideoBitRate;
}
- (void)dealloc{
if(compressionSession != NULL)
{
- (void)dealloc {
if (compressionSession != NULL) {
VTCompressionSessionCompleteFrames(compressionSession, kCMTimeInvalid);
VTCompressionSessionInvalidate(compressionSession);
CFRelease(compressionSession);
compressionSession = NULL;
@@ -98,90 +98,86 @@
}
#pragma mark -- LFVideoEncoder
- (void)encodeVideoData:(CVImageBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp{
if(_isBackGround) return;
frameCount ++;
CMTime presentationTimeStamp = CMTimeMake(frameCount, 1000);
- (void)encodeVideoData:(CVPixelBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp {
if (_isBackGround) return;
frameCount++;
CMTime presentationTimeStamp = CMTimeMake(frameCount, (int32_t)_configuration.videoFrameRate);
VTEncodeInfoFlags flags;
CMTime duration = CMTimeMake(1, (int32_t)_configuration.videoFrameRate);
NSDictionary *properties = nil;
if(frameCount % (int32_t)_configuration.videoMaxKeyframeInterval == 0){
if (frameCount % (int32_t)_configuration.videoMaxKeyframeInterval == 0) {
properties = @{(__bridge NSString *)kVTEncodeFrameOptionKey_ForceKeyFrame: @YES};
}
NSNumber *timeNumber = @(timeStamp);
VTCompressionSessionEncodeFrame(compressionSession, pixelBuffer, presentationTimeStamp, duration, (__bridge CFDictionaryRef)properties, (__bridge_retained void *)timeNumber, &flags);
}
- (void)stopEncoder{
- (void)stopEncoder {
VTCompressionSessionCompleteFrames(compressionSession, kCMTimeIndefinite);
}
- (void)setDelegate:(id<LFVideoEncodingDelegate>)delegate{
- (void)setDelegate:(id<LFVideoEncodingDelegate>)delegate {
_h264Delegate = delegate;
}
#pragma mark -- NSNotification
- (void)willEnterBackground:(NSNotification*)notification{
- (void)willEnterBackground:(NSNotification *)notification {
_isBackGround = YES;
}
- (void)willEnterForeground:(NSNotification*)notification{
- (void)willEnterForeground:(NSNotification *)notification {
[self initCompressionSession];
_isBackGround = NO;
}
#pragma mark -- VideoCallBack
static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer)
{
if(!sampleBuffer) return;
static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer){
if (!sampleBuffer) return;
CFArrayRef array = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true);
if(!array) return;
if (!array) return;
CFDictionaryRef dic = (CFDictionaryRef)CFArrayGetValueAtIndex(array, 0);
if(!dic) return;
if (!dic) return;
BOOL keyframe = !CFDictionaryContainsKey(dic, kCMSampleAttachmentKey_NotSync);
uint64_t timeStamp = [((__bridge_transfer NSNumber*)VTFrameRef) longLongValue];
uint64_t timeStamp = [((__bridge_transfer NSNumber *)VTFrameRef) longLongValue];
LFHardwareVideoEncoder *videoEncoder = (__bridge LFHardwareVideoEncoder *)VTref;
if(status != noErr){
if (status != noErr) {
return;
}
if (keyframe && !videoEncoder->sps)
{
if (keyframe && !videoEncoder->sps) {
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);
size_t sparameterSetSize, sparameterSetCount;
const uint8_t *sparameterSet;
OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0 );
if (statusCode == noErr)
{
OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0);
if (statusCode == noErr) {
size_t pparameterSetSize, pparameterSetCount;
const uint8_t *pparameterSet;
OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0 );
if (statusCode == noErr)
{
OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0);
if (statusCode == noErr) {
videoEncoder->sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];
videoEncoder->pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];
if(videoEncoder->enabledWriteVideoFile){
if (videoEncoder->enabledWriteVideoFile) {
NSMutableData *data = [[NSMutableData alloc] init];
uint8_t header[] = {0x00,0x00,0x00,0x01};
uint8_t header[] = {0x00, 0x00, 0x00, 0x01};
[data appendBytes:header length:4];
[data appendData:videoEncoder->sps];
[data appendBytes:header length:4];
[data appendData:videoEncoder->pps];
fwrite(data.bytes, 1,data.length,videoEncoder->fp);
fwrite(data.bytes, 1, data.length, videoEncoder->fp);
}
}
}
}
CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t length, totalLength;
char *dataPointer;
@@ -193,7 +189,7 @@ static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatu
// Read the NAL unit length
uint32_t NALUnitLength = 0;
memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength);
NALUnitLength = CFSwapInt32BigToHost(NALUnitLength);
LFVideoFrame *videoFrame = [LFVideoFrame new];
@@ -202,57 +198,44 @@ static void VideoCompressonOutputCallback(void *VTref, void *VTFrameRef, OSStatu
videoFrame.isKeyFrame = keyframe;
videoFrame.sps = videoEncoder->sps;
videoFrame.pps = videoEncoder->pps;
if(videoEncoder.h264Delegate && [videoEncoder.h264Delegate respondsToSelector:@selector(videoEncoder:videoFrame:)]){
if (videoEncoder.h264Delegate && [videoEncoder.h264Delegate respondsToSelector:@selector(videoEncoder:videoFrame:)]) {
[videoEncoder.h264Delegate videoEncoder:videoEncoder videoFrame:videoFrame];
}
if(videoEncoder->enabledWriteVideoFile){
if (videoEncoder->enabledWriteVideoFile) {
NSMutableData *data = [[NSMutableData alloc] init];
if(keyframe){
uint8_t header[] = {0x00,0x00,0x00,0x01};
if (keyframe) {
uint8_t header[] = {0x00, 0x00, 0x00, 0x01};
[data appendBytes:header length:4];
}else{
uint8_t header[] = {0x00,0x00,0x01};
} else {
uint8_t header[] = {0x00, 0x00, 0x01};
[data appendBytes:header length:3];
}
[data appendData:videoFrame.data];
fwrite(data.bytes, 1,data.length,videoEncoder->fp);
fwrite(data.bytes, 1, data.length, videoEncoder->fp);
}
bufferOffset += AVCCHeaderLength + NALUnitLength;
}
}
}
- (void)initForFilePath
{
char *path = [self GetFilePathByfileName:"IOSCamDemo.h264"];
NSLog(@"%s",path);
self->fp = fopen(path,"wb");
- (void)initForFilePath {
NSString *path = [self GetFilePathByfileName:@"IOSCamDemo.h264"];
NSLog(@"%@", path);
self->fp = fopen([path cStringUsingEncoding:NSUTF8StringEncoding], "wb");
}
- (char*)GetFilePathByfileName:(char*)filename
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
- (NSString *)GetFilePathByfileName:(NSString*)filename {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *strName = [NSString stringWithFormat:@"%s",filename];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:strName];
NSUInteger len = [writablePath length];
char *filepath = (char*)malloc(sizeof(char) * (len + 1));
[writablePath getCString:filepath maxLength:len + 1 encoding:[NSString defaultCStringEncoding]];
return filepath;
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:filename];
return writablePath;
}
@end
@@ -14,18 +14,17 @@
/// 编码器编码后回调
@protocol LFVideoEncodingDelegate <NSObject>
@required
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame*)frame;
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame *)frame;
@end
/// 编码器抽象的接口
@protocol LFVideoEncoding <NSObject>
@required
- (void)encodeVideoData:(nullable CVImageBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp;
- (void)stopEncoder;
- (void)encodeVideoData:(nullable CVPixelBufferRef)pixelBuffer timeStamp:(uint64_t)timeStamp;
@optional
@property (nonatomic, assign) NSInteger videoBitRate;
- (nullable instancetype)initWithVideoStreamConfiguration:(nullable LFLiveVideoConfiguration*)configuration;
- (nullable instancetype)initWithVideoStreamConfiguration:(nullable LFLiveVideoConfiguration *)configuration;
- (void)setDelegate:(nullable id<LFVideoEncodingDelegate>)delegate;
- (void)stopEncoder;
@end
@@ -9,7 +9,7 @@
#import <Foundation/Foundation.h>
/// 音频码率
typedef NS_ENUM(NSUInteger, LFLiveAudioBitRate) {
typedef NS_ENUM (NSUInteger, LFLiveAudioBitRate) {
/// 32Kbps 音频码率
LFLiveAudioBitRate_32Kbps = 32000,
/// 64Kbps 音频码率
@@ -19,34 +19,36 @@ typedef NS_ENUM(NSUInteger, LFLiveAudioBitRate) {
/// 128Kbps 音频码率
LFLiveAudioBitRate_128Kbps = 128000,
/// 默认音频码率,默认为 64Kbps
LFLiveAudioBitRate_Default = LFLiveAudioBitRate_64Kbps
LFLiveAudioBitRate_Default = LFLiveAudioBitRate_96Kbps
};
/// 采样率 (默认44.1Hz iphoneg6以上48Hz)
typedef NS_ENUM(NSUInteger, LFLiveAudioSampleRate){
/// 44.1Hz 采样率
/// 采样率 (默认44.1Hz)
typedef NS_ENUM (NSUInteger, LFLiveAudioSampleRate){
/// 16KHz 采样率
LFLiveAudioSampleRate_16000Hz = 16000,
/// 44.1KHz 采样率
LFLiveAudioSampleRate_44100Hz = 44100,
/// 48Hz 采样率
/// 48KHz 采样率
LFLiveAudioSampleRate_48000Hz = 48000,
/// 默认音频码率,默认为 64Kbps
LFLiveAudioSampleRate_Default = LFLiveAudioSampleRate_44100Hz
};
/// Audio Live quality(音频质量)
typedef NS_ENUM(NSUInteger, LFLiveAudioQuality){
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 32Kbps
typedef NS_ENUM (NSUInteger, LFLiveAudioQuality){
/// 高音频质量 audio sample rate: 16KHz audio bitrate: numberOfChannels 1 : 32Kbps 2 : 64Kbps
LFLiveAudioQuality_Low = 0,
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 64Kbps
/// 高音频质量 audio sample rate: 44KHz audio bitrate: 96Kbps
LFLiveAudioQuality_Medium = 1,
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 96Kbps
/// 高音频质量 audio sample rate: 44MHz audio bitrate: 128Kbps
LFLiveAudioQuality_High = 2,
/// 高音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 128Kbps
/// 高音频质量 audio sample rate: 48MHz, audio bitrate: 128Kbps
LFLiveAudioQuality_VeryHigh = 3,
/// 默认音频质量 audio sample rate: 44MHz(默认44.1Hz iphoneg6以上48Hz), audio bitrate: 64Kbps
LFLiveAudioQuality_Default = LFLiveAudioQuality_Medium
/// 默认音频质量 audio sample rate: 44MHz, audio bitrate: 96Kbps
LFLiveAudioQuality_Default = LFLiveAudioQuality_High
};
@interface LFLiveAudioConfiguration : NSObject<NSCoding,NSCopying>
@interface LFLiveAudioConfiguration : NSObject<NSCoding, NSCopying>
/// 默认音频配置
+ (instancetype)defaultConfiguration;
@@ -64,6 +66,8 @@ typedef NS_ENUM(NSUInteger, LFLiveAudioQuality){
// 码率
@property (nonatomic, assign) LFLiveAudioBitRate audioBitrate;
/// flv编码音频头 44100 为0x12 0x10
@property (nonatomic ,assign,readonly) char *asc;
@property (nonatomic, assign, readonly) char *asc;
/// 缓存区长度
@property (nonatomic, assign,readonly) NSUInteger bufferLength;
@end
@@ -0,0 +1,192 @@
//
// LFLiveAudioConfiguration.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/1.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFLiveAudioConfiguration.h"
#import <sys/utsname.h>
@implementation LFLiveAudioConfiguration
#pragma mark -- LifyCycle
+ (instancetype)defaultConfiguration {
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration defaultConfigurationForQuality:LFLiveAudioQuality_Default];
return audioConfig;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveAudioQuality)audioQuality {
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration new];
audioConfig.numberOfChannels = 2;
switch (audioQuality) {
case LFLiveAudioQuality_Low: {
audioConfig.audioBitrate = audioConfig.numberOfChannels == 1 ? LFLiveAudioBitRate_32Kbps : LFLiveAudioBitRate_64Kbps;
audioConfig.audioSampleRate = LFLiveAudioSampleRate_16000Hz;
}
break;
case LFLiveAudioQuality_Medium: {
audioConfig.audioBitrate = LFLiveAudioBitRate_96Kbps;
audioConfig.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
}
break;
case LFLiveAudioQuality_High: {
audioConfig.audioBitrate = LFLiveAudioBitRate_128Kbps;
audioConfig.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
}
break;
case LFLiveAudioQuality_VeryHigh: {
audioConfig.audioBitrate = LFLiveAudioBitRate_128Kbps;
audioConfig.audioSampleRate = LFLiveAudioSampleRate_48000Hz;
}
break;
default:{
audioConfig.audioBitrate = LFLiveAudioBitRate_96Kbps;
audioConfig.audioSampleRate = LFLiveAudioSampleRate_44100Hz;
}
break;
}
return audioConfig;
}
- (instancetype)init {
if (self = [super init]) {
_asc = malloc(2);
}
return self;
}
- (void)dealloc {
if (_asc) free(_asc);
}
#pragma mark Setter
- (void)setAudioSampleRate:(LFLiveAudioSampleRate)audioSampleRate {
_audioSampleRate = audioSampleRate;
NSInteger sampleRateIndex = [self sampleRateIndex:audioSampleRate];
self.asc[0] = 0x10 | ((sampleRateIndex>>1) & 0x7);
self.asc[1] = ((sampleRateIndex & 0x1)<<7) | ((self.numberOfChannels & 0xF) << 3);
}
- (void)setNumberOfChannels:(NSUInteger)numberOfChannels {
_numberOfChannels = numberOfChannels;
NSInteger sampleRateIndex = [self sampleRateIndex:self.audioSampleRate];
self.asc[0] = 0x10 | ((sampleRateIndex>>1) & 0x7);
self.asc[1] = ((sampleRateIndex & 0x1)<<7) | ((numberOfChannels & 0xF) << 3);
}
- (NSUInteger)bufferLength{
return 1024*2*self.numberOfChannels;
}
#pragma mark -- CustomMethod
- (NSInteger)sampleRateIndex:(NSInteger)frequencyInHz {
NSInteger sampleRateIndex = 0;
switch (frequencyInHz) {
case 96000:
sampleRateIndex = 0;
break;
case 88200:
sampleRateIndex = 1;
break;
case 64000:
sampleRateIndex = 2;
break;
case 48000:
sampleRateIndex = 3;
break;
case 44100:
sampleRateIndex = 4;
break;
case 32000:
sampleRateIndex = 5;
break;
case 24000:
sampleRateIndex = 6;
break;
case 22050:
sampleRateIndex = 7;
break;
case 16000:
sampleRateIndex = 8;
break;
case 12000:
sampleRateIndex = 9;
break;
case 11025:
sampleRateIndex = 10;
break;
case 8000:
sampleRateIndex = 11;
break;
case 7350:
sampleRateIndex = 12;
break;
default:
sampleRateIndex = 15;
}
return sampleRateIndex;
}
#pragma mark -- Encoder
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:@(self.numberOfChannels) forKey:@"numberOfChannels"];
[aCoder encodeObject:@(self.audioSampleRate) forKey:@"audioSampleRate"];
[aCoder encodeObject:@(self.audioBitrate) forKey:@"audioBitrate"];
[aCoder encodeObject:[NSString stringWithUTF8String:self.asc] forKey:@"asc"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
_numberOfChannels = [[aDecoder decodeObjectForKey:@"numberOfChannels"] unsignedIntegerValue];
_audioSampleRate = [[aDecoder decodeObjectForKey:@"audioSampleRate"] unsignedIntegerValue];
_audioBitrate = [[aDecoder decodeObjectForKey:@"audioBitrate"] unsignedIntegerValue];
_asc = strdup([[aDecoder decodeObjectForKey:@"asc"] cStringUsingEncoding:NSUTF8StringEncoding]);
return self;
}
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
} else if (![super isEqual:other]) {
return NO;
} else {
LFLiveAudioConfiguration *object = other;
return object.numberOfChannels == self.numberOfChannels &&
object.audioBitrate == self.audioBitrate &&
strcmp(object.asc, self.asc) == 0 &&
object.audioSampleRate == self.audioSampleRate;
}
}
- (NSUInteger)hash {
NSUInteger hash = 0;
NSArray *values = @[@(_numberOfChannels),
@(_audioSampleRate),
[NSString stringWithUTF8String:self.asc],
@(_audioBitrate)];
for (NSObject *value in values) {
hash ^= value.hash;
}
return hash;
}
- (id)copyWithZone:(nullable NSZone *)zone {
LFLiveAudioConfiguration *other = [self.class defaultConfiguration];
return other;
}
- (NSString *)description {
NSMutableString *desc = @"".mutableCopy;
[desc appendFormat:@"<LFLiveAudioConfiguration: %p>", self];
[desc appendFormat:@" numberOfChannels:%zi", self.numberOfChannels];
[desc appendFormat:@" audioSampleRate:%zi", self.audioSampleRate];
[desc appendFormat:@" audioBitrate:%zi", self.audioBitrate];
[desc appendFormat:@" audioHeader:%@", [NSString stringWithUTF8String:self.asc]];
return desc;
}
@end
@@ -10,7 +10,7 @@
#import <UIKit/UIKit.h>
/// 视频分辨率(都是16:9 当此设备不支持当前分辨率,自动降低一级)
typedef NS_ENUM(NSUInteger, LFLiveVideoSessionPreset){
typedef NS_ENUM (NSUInteger, LFLiveVideoSessionPreset){
/// 低分辨率
LFCaptureSessionPreset360x640 = 0,
/// 中分辨率
@@ -20,7 +20,7 @@ typedef NS_ENUM(NSUInteger, LFLiveVideoSessionPreset){
};
/// 视频质量
typedef NS_ENUM(NSUInteger, LFLiveVideoQuality){
typedef NS_ENUM (NSUInteger, LFLiveVideoQuality){
/// 分辨率: 360 *640 帧数:15 码率:500Kps
LFLiveVideoQuality_Low1 = 0,
/// 分辨率: 360 *640 帧数:24 码率:800Kps
@@ -43,25 +43,28 @@ typedef NS_ENUM(NSUInteger, LFLiveVideoQuality){
LFLiveVideoQuality_Default = LFLiveVideoQuality_Low2
};
@interface LFLiveVideoConfiguration : NSObject<NSCoding,NSCopying>
@interface LFLiveVideoConfiguration : NSObject<NSCoding, NSCopying>
/// 默认视频配置
+ (instancetype)defaultConfiguration;
/// 视频配置(质量)
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality;
/// 视频配置(质量 & 方向)
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality orientation:(UIInterfaceOrientation)orientation;
/// 视频配置(质量 & 是否是横屏)
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality landscape:(BOOL)landscape;
#pragma mark - Attribute
///=============================================================================
/// @name Attribute
///=============================================================================
/// 视频的分辨率,宽高务必设定为 2 的倍数,否则解码播放时可能出现绿边
/// 视频的分辨率,宽高务必设定为 2 的倍数,否则解码播放时可能出现绿边(这个videoSizeRespectingAspectRatio设置为YES则可能会改变)
@property (nonatomic, assign) CGSize videoSize;
/// 输出图像是否等比例,默认为NO
@property (nonatomic, assign) BOOL videoSizeRespectingAspectRatio;
/// 视频输出方向
@property (nonatomic, assign) UIInterfaceOrientation orientation;
@property (nonatomic, assign) BOOL landscape;
/// 视频的帧率,即 fps
@property (nonatomic, assign) NSUInteger videoFrameRate;
@@ -88,9 +91,6 @@ typedef NS_ENUM(NSUInteger, LFLiveVideoQuality){
@property (nonatomic, assign) LFLiveVideoSessionPreset sessionPreset;
///< ≈sde3分辨率
@property (nonatomic, assign,readonly) NSString *avSessionPreset;
///< 是否裁剪
@property (nonatomic, assign,readonly) BOOL isClipVideo;
@property (nonatomic, assign, readonly) NSString *avSessionPreset;
@end
@@ -0,0 +1,364 @@
//
// LFLiveVideoConfiguration.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/1.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFLiveVideoConfiguration.h"
#import <AVFoundation/AVFoundation.h>
@implementation LFLiveVideoConfiguration
#pragma mark -- LifeCycle
+ (instancetype)defaultConfiguration {
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Default];
return configuration;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality {
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:videoQuality landscape:NO];
return configuration;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality landscape:(BOOL)landscape {
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration new];
switch (videoQuality) {
case LFLiveVideoQuality_Low1:{
configuration.sessionPreset = LFCaptureSessionPreset360x640;
configuration.videoFrameRate = 15;
configuration.videoMaxFrameRate = 15;
configuration.videoMinFrameRate = 10;
configuration.videoBitRate = 500 * 1000;
configuration.videoMaxBitRate = 600 * 1000;
configuration.videoMinBitRate = 400 * 1000;
configuration.videoSize = CGSizeMake(360, 640);
}
break;
case LFLiveVideoQuality_Low2:{
configuration.sessionPreset = LFCaptureSessionPreset360x640;
configuration.videoFrameRate = 24;
configuration.videoMaxFrameRate = 24;
configuration.videoMinFrameRate = 12;
configuration.videoBitRate = 600 * 1000;
configuration.videoMaxBitRate = 720 * 1000;
configuration.videoMinBitRate = 500 * 1000;
configuration.videoSize = CGSizeMake(360, 640);
}
break;
case LFLiveVideoQuality_Low3: {
configuration.sessionPreset = LFCaptureSessionPreset360x640;
configuration.videoFrameRate = 30;
configuration.videoMaxFrameRate = 30;
configuration.videoMinFrameRate = 15;
configuration.videoBitRate = 800 * 1000;
configuration.videoMaxBitRate = 960 * 1000;
configuration.videoMinBitRate = 600 * 1000;
configuration.videoSize = CGSizeMake(360, 640);
}
break;
case LFLiveVideoQuality_Medium1:{
configuration.sessionPreset = LFCaptureSessionPreset540x960;
configuration.videoFrameRate = 15;
configuration.videoMaxFrameRate = 15;
configuration.videoMinFrameRate = 10;
configuration.videoBitRate = 800 * 1000;
configuration.videoMaxBitRate = 960 * 1000;
configuration.videoMinBitRate = 500 * 1000;
configuration.videoSize = CGSizeMake(540, 960);
}
break;
case LFLiveVideoQuality_Medium2:{
configuration.sessionPreset = LFCaptureSessionPreset540x960;
configuration.videoFrameRate = 24;
configuration.videoMaxFrameRate = 24;
configuration.videoMinFrameRate = 12;
configuration.videoBitRate = 800 * 1000;
configuration.videoMaxBitRate = 960 * 1000;
configuration.videoMinBitRate = 500 * 1000;
configuration.videoSize = CGSizeMake(540, 960);
}
break;
case LFLiveVideoQuality_Medium3:{
configuration.sessionPreset = LFCaptureSessionPreset540x960;
configuration.videoFrameRate = 30;
configuration.videoMaxFrameRate = 30;
configuration.videoMinFrameRate = 15;
configuration.videoBitRate = 1000 * 1000;
configuration.videoMaxBitRate = 1200 * 1000;
configuration.videoMinBitRate = 500 * 1000;
configuration.videoSize = CGSizeMake(540, 960);
}
break;
case LFLiveVideoQuality_High1:{
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
configuration.videoFrameRate = 15;
configuration.videoMaxFrameRate = 15;
configuration.videoMinFrameRate = 10;
configuration.videoBitRate = 1000 * 1000;
configuration.videoMaxBitRate = 1200 * 1000;
configuration.videoMinBitRate = 500 * 1000;
configuration.videoSize = CGSizeMake(720, 1280);
}
break;
case LFLiveVideoQuality_High2:{
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
configuration.videoFrameRate = 24;
configuration.videoMaxFrameRate = 24;
configuration.videoMinFrameRate = 12;
configuration.videoBitRate = 1200 * 1000;
configuration.videoMaxBitRate = 1440 * 1000;
configuration.videoMinBitRate = 800 * 1000;
configuration.videoSize = CGSizeMake(720, 1280);
}
break;
case LFLiveVideoQuality_High3:{
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
configuration.videoFrameRate = 30;
configuration.videoMaxFrameRate = 30;
configuration.videoMinFrameRate = 15;
configuration.videoBitRate = 1200 * 1000;
configuration.videoMaxBitRate = 1440 * 1000;
configuration.videoMinBitRate = 500 * 1000;
configuration.videoSize = CGSizeMake(720, 1280);
}
break;
default:
break;
}
configuration.sessionPreset = [configuration supportSessionPreset:configuration.sessionPreset];
configuration.videoMaxKeyframeInterval = configuration.videoFrameRate*2;
configuration.landscape = landscape;
CGSize size = configuration.videoSize;
if (landscape) {
configuration.videoSize = CGSizeMake(size.height, size.width);
} else {
configuration.videoSize = CGSizeMake(size.width, size.height);
}
return configuration;
}
#pragma mark -- Setter Getter
- (NSString *)avSessionPreset {
NSString *avSessionPreset = nil;
switch (self.sessionPreset) {
case LFCaptureSessionPreset360x640:{
avSessionPreset = AVCaptureSessionPreset640x480;
}
break;
case LFCaptureSessionPreset540x960:{
avSessionPreset = AVCaptureSessionPresetiFrame960x540;
}
break;
case LFCaptureSessionPreset720x1280:{
avSessionPreset = AVCaptureSessionPreset1280x720;
}
break;
default: {
avSessionPreset = AVCaptureSessionPreset640x480;
}
break;
}
return avSessionPreset;
}
- (CGSize)videoSize{
if(_videoSizeRespectingAspectRatio){
return self.aspectRatioVideoSize;
}
return _videoSize;
}
- (void)setVideoMaxBitRate:(NSUInteger)videoMaxBitRate {
if (videoMaxBitRate <= _videoBitRate) return;
_videoMaxBitRate = videoMaxBitRate;
}
- (void)setVideoMinBitRate:(NSUInteger)videoMinBitRate {
if (videoMinBitRate >= _videoBitRate) return;
_videoMinBitRate = videoMinBitRate;
}
- (void)setVideoMaxFrameRate:(NSUInteger)videoMaxFrameRate {
if (videoMaxFrameRate <= _videoFrameRate) return;
_videoMaxFrameRate = videoMaxFrameRate;
}
- (void)setVideoMinFrameRate:(NSUInteger)videoMinFrameRate {
if (videoMinFrameRate >= _videoFrameRate) return;
_videoMinFrameRate = videoMinFrameRate;
}
- (void)setSessionPreset:(LFLiveVideoSessionPreset)sessionPreset{
_sessionPreset = sessionPreset;
_sessionPreset = [self supportSessionPreset:sessionPreset];
}
#pragma mark -- Custom Method
- (LFLiveVideoSessionPreset)supportSessionPreset:(LFLiveVideoSessionPreset)sessionPreset {
AVCaptureSession *session = [[AVCaptureSession alloc] init];
AVCaptureDevice *inputCamera;
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices){
if ([device position] == AVCaptureDevicePositionFront){
inputCamera = device;
}
}
AVCaptureDeviceInput *videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:inputCamera error:nil];
if ([session canAddInput:videoInput]){
[session addInput:videoInput];
}
if (![session canSetSessionPreset:self.avSessionPreset]) {
if (sessionPreset == LFCaptureSessionPreset720x1280) {
sessionPreset = LFCaptureSessionPreset540x960;
if (![session canSetSessionPreset:self.avSessionPreset]) {
sessionPreset = LFCaptureSessionPreset360x640;
}
} else if (sessionPreset == LFCaptureSessionPreset540x960) {
sessionPreset = LFCaptureSessionPreset360x640;
}
}
return sessionPreset;
}
- (CGSize)captureOutVideoSize{
CGSize videoSize = CGSizeZero;
switch (_sessionPreset) {
case LFCaptureSessionPreset360x640:{
videoSize = CGSizeMake(360, 640);
}
break;
case LFCaptureSessionPreset540x960:{
videoSize = CGSizeMake(540, 960);
}
break;
case LFCaptureSessionPreset720x1280:{
videoSize = CGSizeMake(720, 1280);
}
break;
default:{
videoSize = CGSizeMake(360, 640);
}
break;
}
if(self.landscape){
return CGSizeMake(videoSize.height, videoSize.width);
}
return videoSize;
}
- (CGSize)aspectRatioVideoSize{
CGSize size = AVMakeRectWithAspectRatioInsideRect(self.captureOutVideoSize, CGRectMake(0, 0, _videoSize.width, _videoSize.height)).size;
NSInteger width = ceil(size.width);
NSInteger height = ceil(size.height);
if(width %2 != 0) width = width - 1;
if(height %2 != 0) height = height - 1;
return CGSizeMake(width, height);
}
#pragma mark -- encoder
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:[NSValue valueWithCGSize:self.videoSize] forKey:@"videoSize"];
[aCoder encodeObject:@(self.videoFrameRate) forKey:@"videoFrameRate"];
[aCoder encodeObject:@(self.videoMaxFrameRate) forKey:@"videoMaxFrameRate"];
[aCoder encodeObject:@(self.videoMinFrameRate) forKey:@"videoMinFrameRate"];
[aCoder encodeObject:@(self.videoMaxKeyframeInterval) forKey:@"videoMaxKeyframeInterval"];
[aCoder encodeObject:@(self.videoBitRate) forKey:@"videoBitRate"];
[aCoder encodeObject:@(self.videoMaxBitRate) forKey:@"videoMaxBitRate"];
[aCoder encodeObject:@(self.videoMinBitRate) forKey:@"videoMinBitRate"];
[aCoder encodeObject:@(self.sessionPreset) forKey:@"sessionPreset"];
[aCoder encodeObject:@(self.landscape) forKey:@"landscape"];
[aCoder encodeObject:@(self.videoSizeRespectingAspectRatio) forKey:@"videoSizeRespectingAspectRatio"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
_videoSize = [[aDecoder decodeObjectForKey:@"videoSize"] CGSizeValue];
_videoFrameRate = [[aDecoder decodeObjectForKey:@"videoFrameRate"] unsignedIntegerValue];
_videoMaxFrameRate = [[aDecoder decodeObjectForKey:@"videoMaxFrameRate"] unsignedIntegerValue];
_videoMinFrameRate = [[aDecoder decodeObjectForKey:@"videoMinFrameRate"] unsignedIntegerValue];
_videoMaxKeyframeInterval = [[aDecoder decodeObjectForKey:@"videoMaxKeyframeInterval"] unsignedIntegerValue];
_videoBitRate = [[aDecoder decodeObjectForKey:@"videoBitRate"] unsignedIntegerValue];
_videoMaxBitRate = [[aDecoder decodeObjectForKey:@"videoMaxBitRate"] unsignedIntegerValue];
_videoMinBitRate = [[aDecoder decodeObjectForKey:@"videoMinBitRate"] unsignedIntegerValue];
_sessionPreset = [[aDecoder decodeObjectForKey:@"sessionPreset"] unsignedIntegerValue];
_landscape = [[aDecoder decodeObjectForKey:@"landscape"] unsignedIntegerValue];
_videoSizeRespectingAspectRatio = [[aDecoder decodeObjectForKey:@"videoSizeRespectingAspectRatio"] unsignedIntegerValue];
return self;
}
- (NSUInteger)hash {
NSUInteger hash = 0;
NSArray *values = @[[NSValue valueWithCGSize:self.videoSize],
@(self.videoFrameRate),
@(self.videoMaxFrameRate),
@(self.videoMinFrameRate),
@(self.videoMaxKeyframeInterval),
@(self.videoBitRate),
@(self.videoMaxBitRate),
@(self.videoMinBitRate),
self.avSessionPreset,
@(self.sessionPreset),
@(self.landscape),
@(self.videoSizeRespectingAspectRatio)];
for (NSObject *value in values) {
hash ^= value.hash;
}
return hash;
}
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
} else if (![super isEqual:other]) {
return NO;
} else {
LFLiveVideoConfiguration *object = other;
return CGSizeEqualToSize(object.videoSize, self.videoSize) &&
object.videoFrameRate == self.videoFrameRate &&
object.videoMaxFrameRate == self.videoMaxFrameRate &&
object.videoMinFrameRate == self.videoMinFrameRate &&
object.videoMaxKeyframeInterval == self.videoMaxKeyframeInterval &&
object.videoBitRate == self.videoBitRate &&
object.videoMaxBitRate == self.videoMaxBitRate &&
object.videoMinBitRate == self.videoMinBitRate &&
[object.avSessionPreset isEqualToString:self.avSessionPreset] &&
object.sessionPreset == self.sessionPreset &&
object.landscape == self.landscape &&
object.videoSizeRespectingAspectRatio == self.videoSizeRespectingAspectRatio;
}
}
- (id)copyWithZone:(nullable NSZone *)zone {
LFLiveVideoConfiguration *other = [self.class defaultConfiguration];
return other;
}
- (NSString *)description {
NSMutableString *desc = @"".mutableCopy;
[desc appendFormat:@"<LFLiveVideoConfiguration: %p>", self];
[desc appendFormat:@" videoSize:%@", NSStringFromCGSize(self.videoSize)];
[desc appendFormat:@" videoSizeRespectingAspectRatio:%zi",self.videoSizeRespectingAspectRatio];
[desc appendFormat:@" videoFrameRate:%zi", self.videoFrameRate];
[desc appendFormat:@" videoMaxFrameRate:%zi", self.videoMaxFrameRate];
[desc appendFormat:@" videoMinFrameRate:%zi", self.videoMinFrameRate];
[desc appendFormat:@" videoMaxKeyframeInterval:%zi", self.videoMaxKeyframeInterval];
[desc appendFormat:@" videoBitRate:%zi", self.videoBitRate];
[desc appendFormat:@" videoMaxBitRate:%zi", self.videoMaxBitRate];
[desc appendFormat:@" videoMinBitRate:%zi", self.videoMinBitRate];
[desc appendFormat:@" avSessionPreset:%@", self.avSessionPreset];
[desc appendFormat:@" sessionPreset:%zi", self.sessionPreset];
[desc appendFormat:@" landscape:%zi", self.landscape];
return desc;
}
@end
+9
View File
@@ -0,0 +1,9 @@
#import "GPUImageFilter.h"
@interface LFGPUImageBeautyFilter : GPUImageFilter {
}
@property (nonatomic, assign) CGFloat beautyLevel;
@property (nonatomic, assign) CGFloat brightLevel;
@property (nonatomic, assign) CGFloat toneLevel;
@end
+268
View File
@@ -0,0 +1,268 @@
#import "LFGPUImageBeautyFilter.h"
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp vec2 singleStepOffset;
uniform highp vec4 params;
uniform highp float brightness;
const highp vec3 W = vec3(0.299, 0.587, 0.114);
const highp mat3 saturateMatrix = mat3(
1.1102, -0.0598, -0.061,
-0.0774, 1.0826, -0.1186,
-0.0228, -0.0228, 1.1772);
highp vec2 blurCoordinates[24];
highp float hardLight(highp float color) {
if (color <= 0.5)
color = color * color * 2.0;
else
color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0);
return color;
}
void main(){
highp vec3 centralColor = texture2D(inputImageTexture, textureCoordinate).rgb;
blurCoordinates[0] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -10.0);
blurCoordinates[1] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 10.0);
blurCoordinates[2] = textureCoordinate.xy + singleStepOffset * vec2(-10.0, 0.0);
blurCoordinates[3] = textureCoordinate.xy + singleStepOffset * vec2(10.0, 0.0);
blurCoordinates[4] = textureCoordinate.xy + singleStepOffset * vec2(5.0, -8.0);
blurCoordinates[5] = textureCoordinate.xy + singleStepOffset * vec2(5.0, 8.0);
blurCoordinates[6] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, 8.0);
blurCoordinates[7] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, -8.0);
blurCoordinates[8] = textureCoordinate.xy + singleStepOffset * vec2(8.0, -5.0);
blurCoordinates[9] = textureCoordinate.xy + singleStepOffset * vec2(8.0, 5.0);
blurCoordinates[10] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, 5.0);
blurCoordinates[11] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, -5.0);
blurCoordinates[12] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -6.0);
blurCoordinates[13] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 6.0);
blurCoordinates[14] = textureCoordinate.xy + singleStepOffset * vec2(6.0, 0.0);
blurCoordinates[15] = textureCoordinate.xy + singleStepOffset * vec2(-6.0, 0.0);
blurCoordinates[16] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, -4.0);
blurCoordinates[17] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, 4.0);
blurCoordinates[18] = textureCoordinate.xy + singleStepOffset * vec2(4.0, -4.0);
blurCoordinates[19] = textureCoordinate.xy + singleStepOffset * vec2(4.0, 4.0);
blurCoordinates[20] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, -2.0);
blurCoordinates[21] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, 2.0);
blurCoordinates[22] = textureCoordinate.xy + singleStepOffset * vec2(2.0, -2.0);
blurCoordinates[23] = textureCoordinate.xy + singleStepOffset * vec2(2.0, 2.0);
highp float sampleColor = centralColor.g * 22.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[0]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[1]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[2]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[3]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[4]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[5]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[6]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[7]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[8]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[9]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[10]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[11]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[12]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[13]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[14]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[15]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[16]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[17]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[18]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[19]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[20]).g * 3.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[21]).g * 3.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[22]).g * 3.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[23]).g * 3.0;
sampleColor = sampleColor / 62.0;
highp float highPass = centralColor.g - sampleColor + 0.5;
for (int i = 0; i < 5; i++) {
highPass = hardLight(highPass);
}
highp float lumance = dot(centralColor, W);
highp float alpha = pow(lumance, params.r);
highp vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
smoothColor.r = clamp(pow(smoothColor.r, params.g), 0.0, 1.0);
smoothColor.g = clamp(pow(smoothColor.g, params.g), 0.0, 1.0);
smoothColor.b = clamp(pow(smoothColor.b, params.g), 0.0, 1.0);
highp vec3 lvse = vec3(1.0)-(vec3(1.0)-smoothColor)*(vec3(1.0)-centralColor);
highp vec3 bianliang = max(smoothColor, centralColor);
highp vec3 rouguang = 2.0*centralColor*smoothColor + centralColor*centralColor - 2.0*centralColor*centralColor*smoothColor;
gl_FragColor = vec4(mix(centralColor, lvse, alpha), 1.0);
gl_FragColor.rgb = mix(gl_FragColor.rgb, bianliang, alpha);
gl_FragColor.rgb = mix(gl_FragColor.rgb, rouguang, params.b);
highp vec3 satcolor = gl_FragColor.rgb * saturateMatrix;
gl_FragColor.rgb = mix(gl_FragColor.rgb, satcolor, params.a);
gl_FragColor.rgb = vec3(gl_FragColor.rgb + vec3(brightness));
}
);
#else
NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
(
varying vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform mediump vec2 singleStepOffset;
uniform mediump vec4 params;
uniform mediump float brightness;
const mediump mat3 saturateMatrix = mat3(
1.1102, -0.0598, -0.061,
-0.0774, 1.0826, -0.1186,
-0.0228, -0.0228, 1.1772);
const mediump vec3 W = vec3(0.299, 0.587, 0.114);
mediump vec2 blurCoordinates[24];
mediump float hardLight(mediump float color){
if (color <= 0.5)
color = color * color * 2.0;
else
color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0);
return color;
}
void main(){
mediump vec3 centralColor = texture2D(inputImageTexture, textureCoordinate).rgb;
blurCoordinates[0] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -10.0);
blurCoordinates[1] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 10.0);
blurCoordinates[2] = textureCoordinate.xy + singleStepOffset * vec2(-10.0, 0.0);
blurCoordinates[3] = textureCoordinate.xy + singleStepOffset * vec2(10.0, 0.0);
blurCoordinates[4] = textureCoordinate.xy + singleStepOffset * vec2(5.0, -8.0);
blurCoordinates[5] = textureCoordinate.xy + singleStepOffset * vec2(5.0, 8.0);
blurCoordinates[6] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, 8.0);
blurCoordinates[7] = textureCoordinate.xy + singleStepOffset * vec2(-5.0, -8.0);
blurCoordinates[8] = textureCoordinate.xy + singleStepOffset * vec2(8.0, -5.0);
blurCoordinates[9] = textureCoordinate.xy + singleStepOffset * vec2(8.0, 5.0);
blurCoordinates[10] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, 5.0);
blurCoordinates[11] = textureCoordinate.xy + singleStepOffset * vec2(-8.0, -5.0);
blurCoordinates[12] = textureCoordinate.xy + singleStepOffset * vec2(0.0, -6.0);
blurCoordinates[13] = textureCoordinate.xy + singleStepOffset * vec2(0.0, 6.0);
blurCoordinates[14] = textureCoordinate.xy + singleStepOffset * vec2(6.0, 0.0);
blurCoordinates[15] = textureCoordinate.xy + singleStepOffset * vec2(-6.0, 0.0);
blurCoordinates[16] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, -4.0);
blurCoordinates[17] = textureCoordinate.xy + singleStepOffset * vec2(-4.0, 4.0);
blurCoordinates[18] = textureCoordinate.xy + singleStepOffset * vec2(4.0, -4.0);
blurCoordinates[19] = textureCoordinate.xy + singleStepOffset * vec2(4.0, 4.0);
blurCoordinates[20] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, -2.0);
blurCoordinates[21] = textureCoordinate.xy + singleStepOffset * vec2(-2.0, 2.0);
blurCoordinates[22] = textureCoordinate.xy + singleStepOffset * vec2(2.0, -2.0);
blurCoordinates[23] = textureCoordinate.xy + singleStepOffset * vec2(2.0, 2.0);
mediump float sampleColor = centralColor.g * 22.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[0]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[1]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[2]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[3]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[4]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[5]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[6]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[7]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[8]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[9]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[10]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[11]).g;
sampleColor += texture2D(inputImageTexture, blurCoordinates[12]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[13]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[14]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[15]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[16]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[17]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[18]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[19]).g * 2.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[20]).g * 3.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[21]).g * 3.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[22]).g * 3.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[23]).g * 3.0;
sampleColor = sampleColor / 62.0;
mediump float highPass = centralColor.g - sampleColor + 0.5;
for (int i = 0; i < 5; i++) {
highPass = hardLight(highPass);
}
mediump float luminance = dot(centralColor, W);
mediump float alpha = pow(luminance, params);
mediump vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
smoothColor.r = clamp(pow(smoothColor.r, params.g), 0.0, 1.0);
smoothColor.g = clamp(pow(smoothColor.g, params.g), 0.0, 1.0);
smoothColor.b = clamp(pow(smoothColor.b, params.g), 0.0, 1.0);
mediump vec3 lvse = vec3(1.0)-(vec3(1.0)-smoothColor)*(vec3(1.0)-centralColor);
mediump vec3 bianliang = max(smoothColor, centralColor);
mediump vec3 rouguang = 2.0*centralColor*smoothColor + centralColor*centralColor - 2.0*centralColor*centralColor*smoothColor;
gl_FragColor = vec4(mix(centralColor, lvse, alpha), 1.0);
gl_FragColor.rgb = mix(gl_FragColor.rgb, bianliang, alpha);
gl_FragColor.rgb = mix(gl_FragColor.rgb, rouguang, params.b);
mediump vec3 satcolor = gl_FragColor.rgb * saturateMatrix;
gl_FragColor.rgb = mix(gl_FragColor.rgb, satcolor, params.a);
gl_FragColor.rgb = vec3(gl_FragColor.rgb + vec3(brightness));
}
);
#endif
@implementation LFGPUImageBeautyFilter
- (id)init;
{
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageBeautyFragmentShaderString])) {
return nil;
}
_toneLevel = 0.5;
_beautyLevel = 0.5;
_brightLevel = 0.5;
[self setParams:_beautyLevel tone:_toneLevel];
[self setBrightLevel:_brightLevel];
return self;
}
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex {
[super setInputSize:newSize atIndex:textureIndex];
inputTextureSize = newSize;
CGPoint offset = CGPointMake(2.0f / inputTextureSize.width, 2.0 / inputTextureSize.height);
[self setPoint:offset forUniformName:@"singleStepOffset"];
}
- (void)setBeautyLevel:(CGFloat)beautyLevel {
_beautyLevel = beautyLevel;
[self setParams:_beautyLevel tone:_toneLevel];
}
- (void)setBrightLevel:(CGFloat)brightLevel {
_brightLevel = brightLevel;
[self setFloat:0.6 * (-0.5 + brightLevel) forUniformName:@"brightness"];
}
- (void)setParams:(CGFloat)beauty tone:(CGFloat)tone {
GPUVector4 fBeautyParam;
fBeautyParam.one = 1.0 - 0.6 * beauty;
fBeautyParam.two = 1.0 - 0.3 * beauty;
fBeautyParam.three = 0.1 + 0.3 * tone;
fBeautyParam.four = 0.1 + 0.3 * tone;
[self setFloatVec4:fBeautyParam forUniform:@"params"];
}
@end
+45
View File
@@ -0,0 +1,45 @@
#import "LFGPUImageEmptyFilter.h"
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const kLFGPUImageEmptyFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main(){
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4((textureColor.rgb), textureColor.w);
}
);
#else
NSString *const kGPUImageInvertFragmentShaderString = SHADER_STRING
(
varying vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main(){
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4((textureColor.rgb), textureColor.w);
}
);
#endif
@implementation LFGPUImageEmptyFilter
- (id)init;
{
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageEmptyFragmentShaderString])) {
return nil;
}
return self;
}
@end
@@ -10,7 +10,7 @@
@interface LFFrame : NSObject
@property (nonatomic, assign) uint64_t timestamp;
@property (nonatomic, assign,) uint64_t timestamp;
@property (nonatomic, strong) NSData *data;
///< flv或者rtmp包头
@property (nonatomic, strong) NSData *header;
@@ -16,11 +16,15 @@
@property (nonatomic, assign) CGSize videoSize; ///< 上传的分辨率
@property (nonatomic, assign) BOOL isRtmp; ///< 上传方式(TCP or RTMP
@property (nonatomic, assign) CGFloat elapsedMilli; ///< 距离上次统计的时间 单位ms
@property (nonatomic, assign) CGFloat timeStamp; ///< 当前的时间戳,从而计算1s内数据
@property (nonatomic, assign) CGFloat dataFlow; ///< 总流量
@property (nonatomic, assign) CGFloat bandwidth; ///< 1s内总带宽
@property (nonatomic, assign) CGFloat currentBandwidth; ///< 上次的带宽
@property (nonatomic, assign) NSInteger dropFrame; ///< 丢掉的帧数
@property (nonatomic, assign) NSInteger totalFrame; ///< 总帧数
@property (nonatomic, assign) NSInteger capturedAudioCount; ///< 1s内音频捕获个数
@property (nonatomic, assign) NSInteger capturedVideoCount; ///< 1s内视频捕获个数
@property (nonatomic, assign) NSInteger currentCapturedAudioCount; ///< 上次的音频捕获个数
+51
View File
@@ -0,0 +1,51 @@
//
// LFLiveStreamInfo.h
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 倾慕. All rights reserved.
// 真正的上传地址 token等
#import <Foundation/Foundation.h>
#import "LFLiveAudioConfiguration.h"
#import "LFLiveVideoConfiguration.h"
/// 流状态
typedef NS_ENUM (NSUInteger, LFLiveState){
/// 准备
LFLiveReady = 0,
/// 连接中
LFLivePending = 1,
/// 已连接
LFLiveStart = 2,
/// 已断开
LFLiveStop = 3,
/// 连接出错
LFLiveError = 4
};
typedef NS_ENUM (NSUInteger, LFLiveSocketErrorCode) {
LFLiveSocketError_PreView = 201, ///< 预览失败
LFLiveSocketError_GetStreamInfo = 202, ///< 获取流媒体信息失败
LFLiveSocketError_ConnectSocket = 203, ///< 连接socket失败
LFLiveSocketError_Verification = 204, ///< 验证服务器失败
LFLiveSocketError_ReConnectTimeOut = 205 ///< 重新连接服务器超时
};
@interface LFLiveStreamInfo : NSObject
@property (nonatomic, copy) NSString *streamId;
#pragma mark -- FLV
@property (nonatomic, copy) NSString *host;
@property (nonatomic, assign) NSInteger port;
#pragma mark -- RTMP
@property (nonatomic, copy) NSString *url; ///< 上传地址 (RTMP用就好了)
///音频配置
@property (nonatomic, strong) LFLiveAudioConfiguration *audioConfiguration;
///视频配置
@property (nonatomic, strong) LFLiveVideoConfiguration *videoConfiguration;
///是否丢帧
@property (nonatomic, assign) BOOL needDropFrame;
@end
@@ -1,5 +1,5 @@
//
// LFStreamRtmpSocket.h
// LFStreamRTMPSocket.h
// LaiFeng
//
// Created by admin on 16/5/18.
@@ -8,7 +8,7 @@
#import "LFStreamSocket.h"
@interface LFStreamRtmpSocket : NSObject<LFStreamSocket>
@interface LFStreamRTMPSocket : NSObject<LFStreamSocket>
#pragma mark - Initializer
///=============================================================================
@@ -0,0 +1,560 @@
//
// 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{
return [self initWithStream:stream reconnectInterval:0 reconnectCount:0];
}
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo *)stream reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount{
if (!stream) @throw [NSException exceptionWithName:@"LFStreamRtmpSocket init error" reason:@"stream is nil" userInfo:nil];
if (self = [super init]) {
_stream = stream;
if (reconnectInterval > 0) _reconnectInterval = reconnectInterval;
else _reconnectInterval = RetryTimesMargin;
if (reconnectCount > 0) _reconnectCount = reconnectCount;
else _reconnectCount = RetryTimesBreaken;
[self addObserver:self forKeyPath:@"isSending" options:NSKeyValueObservingOptionNew context:nil];//这里改成observer主要考虑一直到发送出错情况下,可以继续发送
}
return self;
}
- (void)dealloc{
[self removeObserver:self forKeyPath:@"isSending"];
}
- (void)start {
dispatch_async(self.rtmpSendQueue, ^{
[self _start];
});
}
- (void)_start {
if (!_stream) return;
if (_isConnecting) return;
if (_rtmp != NULL) return;
self.debugInfo.streamId = self.stream.streamId;
self.debugInfo.uploadUrl = self.stream.url;
self.debugInfo.isRtmp = YES;
[self RTMP264_Connect:(char *)[_stream.url cStringUsingEncoding:NSASCIIStringEncoding]];
}
- (void)stop {
dispatch_async(self.rtmpSendQueue, ^{
[self _stop];
});
}
- (void)_stop {
if (self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]) {
[self.delegate socketStatus:self status:LFLiveStop];
}
if (_rtmp != NULL) {
PILI_RTMP_Close(_rtmp, &_error);
PILI_RTMP_Free(_rtmp);
_rtmp = NULL;
}
[self clean];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
- (void)sendFrame:(LFFrame *)frame {
if (!frame) return;
[self.buffer appendObject:frame];
if(!self.isSending){
[self sendFrame];
}
}
- (void)setDelegate:(id<LFStreamSocketDelegate>)delegate {
_delegate = delegate;
}
#pragma mark -- CustomMethod
- (void)sendFrame {
dispatch_async(self.rtmpSendQueue, ^{
if (!self.isSending && self.buffer.list.count > 0) {
self.isSending = YES;
if (!_isConnected || _isReconnecting || _isConnecting || !_rtmp){
self.isSending = NO;
return;
}
// 调用发送接口
LFFrame *frame = [self.buffer popFirstObject];
if ([frame isKindOfClass:[LFVideoFrame class]]) {
if (!self.sendVideoHead) {
self.sendVideoHead = YES;
if(!((LFVideoFrame*)frame).sps || !((LFVideoFrame*)frame).pps){
self.isSending = NO;
return;
}
[self sendVideoHeader:(LFVideoFrame *)frame];
} else {
[self sendVideo:(LFVideoFrame *)frame];
}
} else {
if (!self.sendAudioHead) {
self.sendAudioHead = YES;
if(!((LFAudioFrame*)frame).audioInfo){
self.isSending = NO;
return;
}
[self sendAudioHeader:(LFAudioFrame *)frame];
} else {
[self sendAudio:frame];
}
}
//debug更新
self.debugInfo.totalFrame++;
self.debugInfo.dropFrame += self.buffer.lastDropFrames;
self.buffer.lastDropFrames = 0;
self.debugInfo.dataFlow += frame.data.length;
self.debugInfo.elapsedMilli = CACurrentMediaTime() * 1000 - self.debugInfo.timeStamp;
if (self.debugInfo.elapsedMilli < 1000) {
self.debugInfo.bandwidth += frame.data.length;
if ([frame isKindOfClass:[LFAudioFrame class]]) {
self.debugInfo.capturedAudioCount++;
} else {
self.debugInfo.capturedVideoCount++;
}
self.debugInfo.unSendCount = self.buffer.list.count;
} else {
self.debugInfo.currentBandwidth = self.debugInfo.bandwidth;
self.debugInfo.currentCapturedAudioCount = self.debugInfo.capturedAudioCount;
self.debugInfo.currentCapturedVideoCount = self.debugInfo.capturedVideoCount;
if (self.delegate && [self.delegate respondsToSelector:@selector(socketDebug:debugInfo:)]) {
[self.delegate socketDebug:self debugInfo:self.debugInfo];
}
self.debugInfo.bandwidth = 0;
self.debugInfo.capturedAudioCount = 0;
self.debugInfo.capturedVideoCount = 0;
self.debugInfo.timeStamp = CACurrentMediaTime() * 1000;
}
//修改发送状态
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//< 这里只为了不循环调用sendFrame方法 调用栈是保证先出栈再进栈
self.isSending = NO;
});
}
});
}
- (void)clean {
_isConnecting = NO;
_isReconnecting = NO;
_isSending = NO;
_isConnected = NO;
_sendAudioHead = NO;
_sendVideoHead = NO;
self.debugInfo = nil;
[self.buffer removeAllObject];
self.retryTimes4netWorkBreaken = 0;
}
- (NSInteger)RTMP264_Connect:(char *)push_url {
//由于摄像头的timestamp是一直在累加,需要每次得到相对时间戳
//分配与初始化
if (_isConnecting) return -1;
_isConnecting = YES;
if (self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]) {
[self.delegate socketStatus:self status:LFLivePending];
}
if (_rtmp != NULL) {
PILI_RTMP_Close(_rtmp, &_error);
PILI_RTMP_Free(_rtmp);
}
_rtmp = PILI_RTMP_Alloc();
PILI_RTMP_Init(_rtmp);
//设置URL
if (PILI_RTMP_SetupURL(_rtmp, push_url, &_error) == FALSE) {
//log(LOG_ERR, "RTMP_SetupURL() failed!");
goto Failed;
}
_rtmp->m_errorCallback = RTMPErrorCallback;
_rtmp->m_connCallback = ConnectionTimeCallback;
_rtmp->m_userData = (__bridge void *)self;
_rtmp->m_msgCounter = 1;
_rtmp->Link.timeout = RTMP_RECEIVE_TIMEOUT;
//设置可写,即发布流,这个函数必须在连接前使用,否则无效
PILI_RTMP_EnableWrite(_rtmp);
//连接服务器
if (PILI_RTMP_Connect(_rtmp, NULL, &_error) == FALSE) {
goto Failed;
}
//连接流
if (PILI_RTMP_ConnectStream(_rtmp, 0, &_error) == FALSE) {
goto Failed;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]) {
[self.delegate socketStatus:self status:LFLiveStart];
}
[self sendMetaData];
_isConnected = YES;
_isConnecting = NO;
_isReconnecting = NO;
_isSending = NO;
_retryTimes4netWorkBreaken = 0;
return 0;
Failed:
PILI_RTMP_Close(_rtmp, &_error);
PILI_RTMP_Free(_rtmp);
_rtmp = NULL;
if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidError:errorCode:)]) {
[self.delegate socketDidError:self errorCode:LFLiveSocketError_ConnectSocket];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]) {
[self.delegate socketStatus:self status:LFLiveError];
}
return -1;
}
#pragma mark -- Rtmp Send
- (void)sendMetaData {
PILI_RTMPPacket packet;
char pbuf[2048], *pend = pbuf + sizeof(pbuf);
packet.m_nChannel = 0x03; // control channel (invoke)
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_packetType = RTMP_PACKET_TYPE_INFO;
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = _rtmp->m_stream_id;
packet.m_hasAbsTimestamp = TRUE;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
char *enc = packet.m_body;
enc = AMF_EncodeString(enc, pend, &av_setDataFrame);
enc = AMF_EncodeString(enc, pend, &av_onMetaData);
*enc++ = AMF_OBJECT;
enc = AMF_EncodeNamedNumber(enc, pend, &av_duration, 0.0);
enc = AMF_EncodeNamedNumber(enc, pend, &av_fileSize, 0.0);
// videosize
enc = AMF_EncodeNamedNumber(enc, pend, &av_width, _stream.videoConfiguration.videoSize.width);
enc = AMF_EncodeNamedNumber(enc, pend, &av_height, _stream.videoConfiguration.videoSize.height);
// video
enc = AMF_EncodeNamedString(enc, pend, &av_videocodecid, &av_avc1);
enc = AMF_EncodeNamedNumber(enc, pend, &av_videodatarate, _stream.videoConfiguration.videoBitRate / 1000.f);
enc = AMF_EncodeNamedNumber(enc, pend, &av_framerate, _stream.videoConfiguration.videoFrameRate);
// audio
enc = AMF_EncodeNamedString(enc, pend, &av_audiocodecid, &av_mp4a);
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiodatarate, _stream.audioConfiguration.audioBitrate);
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiosamplerate, _stream.audioConfiguration.audioSampleRate);
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiosamplesize, 16.0);
enc = AMF_EncodeNamedBoolean(enc, pend, &av_stereo, _stream.audioConfiguration.numberOfChannels == 2);
// sdk version
enc = AMF_EncodeNamedString(enc, pend, &av_encoder, &av_SDKVersion);
*enc++ = 0;
*enc++ = 0;
*enc++ = AMF_OBJECT_END;
packet.m_nBodySize = (uint32_t)(enc - packet.m_body);
if (!PILI_RTMP_SendPacket(_rtmp, &packet, FALSE, &_error)) {
return;
}
}
- (void)sendVideoHeader:(LFVideoFrame *)videoFrame {
unsigned char *body = NULL;
NSInteger iIndex = 0;
NSInteger rtmpLength = 1024;
const char *sps = videoFrame.sps.bytes;
const char *pps = videoFrame.pps.bytes;
NSInteger sps_len = videoFrame.sps.length;
NSInteger pps_len = videoFrame.pps.length;
body = (unsigned char *)malloc(rtmpLength);
memset(body, 0, rtmpLength);
body[iIndex++] = 0x17;
body[iIndex++] = 0x00;
body[iIndex++] = 0x00;
body[iIndex++] = 0x00;
body[iIndex++] = 0x00;
body[iIndex++] = 0x01;
body[iIndex++] = sps[1];
body[iIndex++] = sps[2];
body[iIndex++] = sps[3];
body[iIndex++] = 0xff;
/*sps*/
body[iIndex++] = 0xe1;
body[iIndex++] = (sps_len >> 8) & 0xff;
body[iIndex++] = sps_len & 0xff;
memcpy(&body[iIndex], sps, sps_len);
iIndex += sps_len;
/*pps*/
body[iIndex++] = 0x01;
body[iIndex++] = (pps_len >> 8) & 0xff;
body[iIndex++] = (pps_len) & 0xff;
memcpy(&body[iIndex], pps, pps_len);
iIndex += pps_len;
[self sendPacket:RTMP_PACKET_TYPE_VIDEO data:body size:iIndex nTimestamp:0];
free(body);
}
- (void)sendVideo:(LFVideoFrame *)frame {
NSInteger i = 0;
NSInteger rtmpLength = frame.data.length + 9;
unsigned char *body = (unsigned char *)malloc(rtmpLength);
memset(body, 0, rtmpLength);
if (frame.isKeyFrame) {
body[i++] = 0x17; // 1:Iframe 7:AVC
} else {
body[i++] = 0x27; // 2:Pframe 7:AVC
}
body[i++] = 0x01; // AVC NALU
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = (frame.data.length >> 24) & 0xff;
body[i++] = (frame.data.length >> 16) & 0xff;
body[i++] = (frame.data.length >> 8) & 0xff;
body[i++] = (frame.data.length) & 0xff;
memcpy(&body[i], frame.data.bytes, frame.data.length);
[self sendPacket:RTMP_PACKET_TYPE_VIDEO data:body size:(rtmpLength) nTimestamp:frame.timestamp];
free(body);
}
- (NSInteger)sendPacket:(unsigned int)nPacketType data:(unsigned char *)data size:(NSInteger)size nTimestamp:(uint64_t)nTimestamp {
NSInteger rtmpLength = size;
PILI_RTMPPacket rtmp_pack;
PILI_RTMPPacket_Reset(&rtmp_pack);
PILI_RTMPPacket_Alloc(&rtmp_pack, (uint32_t)rtmpLength);
rtmp_pack.m_nBodySize = (uint32_t)size;
memcpy(rtmp_pack.m_body, data, size);
rtmp_pack.m_hasAbsTimestamp = 0;
rtmp_pack.m_packetType = nPacketType;
if (_rtmp) rtmp_pack.m_nInfoField2 = _rtmp->m_stream_id;
rtmp_pack.m_nChannel = 0x04;
rtmp_pack.m_headerType = RTMP_PACKET_SIZE_LARGE;
if (RTMP_PACKET_TYPE_AUDIO == nPacketType && size != 4) {
rtmp_pack.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
}
rtmp_pack.m_nTimeStamp = (uint32_t)nTimestamp;
NSInteger nRet = [self RtmpPacketSend:&rtmp_pack];
PILI_RTMPPacket_Free(&rtmp_pack);
return nRet;
}
- (NSInteger)RtmpPacketSend:(PILI_RTMPPacket *)packet {
if (_rtmp && PILI_RTMP_IsConnected(_rtmp)) {
int success = PILI_RTMP_SendPacket(_rtmp, packet, 0, &_error);
return success;
}
return -1;
}
- (void)sendAudioHeader:(LFAudioFrame *)audioFrame {
NSInteger rtmpLength = audioFrame.audioInfo.length + 2; /*spec data长度,一般是2*/
unsigned char *body = (unsigned char *)malloc(rtmpLength);
memset(body, 0, rtmpLength);
/*AF 00 + AAC RAW data*/
body[0] = 0xAF;
body[1] = 0x00;
memcpy(&body[2], audioFrame.audioInfo.bytes, audioFrame.audioInfo.length); /*spec_buf是AAC sequence header数据*/
[self sendPacket:RTMP_PACKET_TYPE_AUDIO data:body size:rtmpLength nTimestamp:0];
free(body);
}
- (void)sendAudio:(LFFrame *)frame {
NSInteger rtmpLength = frame.data.length + 2; /*spec data长度,一般是2*/
unsigned char *body = (unsigned char *)malloc(rtmpLength);
memset(body, 0, rtmpLength);
/*AF 01 + AAC RAW data*/
body[0] = 0xAF;
body[1] = 0x01;
memcpy(&body[2], frame.data.bytes, frame.data.length);
[self sendPacket:RTMP_PACKET_TYPE_AUDIO data:body size:rtmpLength nTimestamp:frame.timestamp];
free(body);
}
// 断线重连
- (void)reconnect {
dispatch_async(self.rtmpSendQueue, ^{
_isReconnecting = NO;
if (_isConnected) return;
[self _stop];
[self _start];
});
}
#pragma mark -- CallBack
void RTMPErrorCallback(RTMPError *error, void *userData) {
LFStreamRTMPSocket *socket = (__bridge LFStreamRTMPSocket *)userData;
if (error->code < 0) {
if (socket.retryTimes4netWorkBreaken++ < socket.reconnectCount && !socket.isReconnecting) {
socket.isConnected = NO;
socket.isConnecting = NO;
socket.isReconnecting = YES;
[socket performSelectorOnMainThread:@selector(reconnect) withObject:nil waitUntilDone:socket.reconnectInterval];
} else if (socket.retryTimes4netWorkBreaken >= socket.reconnectCount) {
if (socket.delegate && [socket.delegate respondsToSelector:@selector(socketStatus:status:)]) {
[socket.delegate socketStatus:socket status:LFLiveError];
}
if (socket.delegate && [socket.delegate respondsToSelector:@selector(socketDidError:errorCode:)]) {
[socket.delegate socketDidError:socket errorCode:LFLiveSocketError_ReConnectTimeOut];
}
}
}
}
void ConnectionTimeCallback(PILI_CONNECTION_TIME *conn_time, void *userData) {
//LFStreamRTMPSocket *socket = (__bridge LFStreamRTMPSocket*)userData;
}
#pragma mark -- LFStreamingBufferDelegate
- (void)streamingBuffer:(nullable LFStreamingBuffer *)buffer bufferState:(LFLiveBuffferState)state{
if(self.delegate && [self.delegate respondsToSelector:@selector(socketBufferStatus:status:)]){
[self.delegate socketBufferStatus:self status:state];
}
}
#pragma mark -- Observer
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if([keyPath isEqualToString:@"isSending"]){
if(!self.isSending){
[self sendFrame];
}
}
}
#pragma mark -- Getter Setter
- (LFStreamingBuffer *)buffer {
if (!_buffer) {
_buffer = [[LFStreamingBuffer alloc] init];
_buffer.delegate = self;
_buffer.needDropFrame = self.stream.needDropFrame;
}
return _buffer;
}
- (LFLiveDebug *)debugInfo {
if (!_debugInfo) {
_debugInfo = [[LFLiveDebug alloc] init];
}
return _debugInfo;
}
- (dispatch_queue_t)rtmpSendQueue{
if(!_rtmpSendQueue){
_rtmpSendQueue = dispatch_queue_create("com.youku.LaiFeng.RtmpSendQueue", NULL);
}
return _rtmpSendQueue;
}
@end
@@ -0,0 +1,36 @@
//
// LFStreamSocket.h
// LFLiveKit
//
// Created by admin on 16/5/3.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "LFLiveStreamInfo.h"
#import "LFStreamingBuffer.h"
#import "LFLiveDebug.h"
@protocol LFStreamSocket;
@protocol LFStreamSocketDelegate <NSObject>
/** callback buffer current status (回调当前缓冲区情况,可实现相关切换帧率 码率等策略)*/
- (void)socketBufferStatus:(nullable id <LFStreamSocket>)socket status:(LFLiveBuffferState)status;
/** callback socket current status (回调当前网络情况) */
- (void)socketStatus:(nullable id <LFStreamSocket>)socket status:(LFLiveState)status;
/** callback socket errorcode */
- (void)socketDidError:(nullable id <LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode;
@optional
/** callback debugInfo */
- (void)socketDebug:(nullable id <LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug *)debugInfo;
@end
@protocol LFStreamSocket <NSObject>
- (void)start;
- (void)stop;
- (void)sendFrame:(nullable LFFrame *)frame;
- (void)setDelegate:(nullable id <LFStreamSocketDelegate>)delegate;
@optional
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo *)stream;
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo *)stream reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount;
@end
@@ -11,10 +11,10 @@
#import "LFVideoFrame.h"
/** current buffer status */
typedef NS_ENUM(NSUInteger, LFLiveBuffferState) {
typedef NS_ENUM (NSUInteger, LFLiveBuffferState) {
LFLiveBuffferUnknown = 0, //< 未知
LFLiveBuffferIncrease = 1, //< 缓冲区状态好可以增加码率
LFLiveBuffferDecline = 2 //< 缓冲区状态应该降低码率
LFLiveBuffferIncrease = 1, //< 缓冲区状态差应该降低码率
LFLiveBuffferDecline = 2 //< 缓冲区状态应该提升码率
};
@class LFStreamingBuffer;
@@ -22,25 +22,31 @@ typedef NS_ENUM(NSUInteger, LFLiveBuffferState) {
@protocol LFStreamingBufferDelegate <NSObject>
@optional
/** 当前buffer变动(增加or减少) 根据buffer中的updateInterval时间回调*/
- (void)streamingBuffer:(nullable LFStreamingBuffer * )buffer bufferState:(LFLiveBuffferState)state;
- (void)streamingBuffer:(nullable LFStreamingBuffer *)buffer bufferState:(LFLiveBuffferState)state;
@end
@interface LFStreamingBuffer : NSObject
/** The needDropFrame control Dynamic frame loss ,default is YES */
@property (nonatomic, assign) BOOL needDropFrame;
/** The delegate of the buffer. buffer callback */
@property (nullable,nonatomic, weak) id <LFStreamingBufferDelegate> delegate;
@property (nullable, nonatomic, weak) id <LFStreamingBufferDelegate> delegate;
/** current frame buffer */
@property (nonatomic, strong, readonly) NSMutableArray <LFFrame*>* _Nonnull list;
@property (nonatomic, strong, readonly) NSMutableArray <LFFrame *> *_Nonnull list;
/** buffer count max size default 1000 */
@property (nonatomic, assign) NSUInteger maxCount;
/** count of drop frames in last time */
@property (nonatomic, assign) NSInteger lastDropFrames;
/** add frame to buffer */
- (void)appendObject:(nullable LFFrame*)frame;
- (void)appendObject:(nullable LFFrame *)frame;
/** pop the first frome buffer */
- (nullable LFFrame*)popFirstObject;
- (nullable LFFrame *)popFirstObject;
/** remove all objects from Buffer */
- (void)removeAllObject;
@@ -18,8 +18,8 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
dispatch_semaphore_t _lock;
}
@property (nonatomic, strong) NSMutableArray <LFFrame*>*sortList;
@property (nonatomic, strong, readwrite) NSMutableArray <LFFrame*>*list;
@property (nonatomic, strong) NSMutableArray <LFFrame *> *sortList;
@property (nonatomic, strong, readwrite) NSMutableArray <LFFrame *> *list;
@property (nonatomic, strong) NSMutableArray *thresholdList;
/** 处理buffer缓冲区情况 */
@@ -32,31 +32,35 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
@implementation LFStreamingBuffer
- (instancetype)init{
if(self = [super init]){
- (instancetype)init {
if (self = [super init]) {
_lock = dispatch_semaphore_create(1);
self.updateInterval = defaultUpdateInterval;
self.callBackInterval = defaultCallBackInterval;
self.maxCount = defaultSendBufferMaxCount;
self.lastDropFrames = 0;
self.startTimer = NO;
self.needDropFrame = YES;
}
return self;
}
- (void)dealloc{
- (void)dealloc {
}
#pragma mark -- Custom
- (void)appendObject:(LFFrame*)frame{
if(!frame) return;
if(!_startTimer){
- (void)appendObject:(LFFrame *)frame {
if (!frame) return;
if (!_startTimer) {
_startTimer = YES;
[self tick];
}
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
if(self.sortList.count < defaultSortBufferMaxCount){
if (self.sortList.count < defaultSortBufferMaxCount) {
[self.sortList addObject:frame];
}else{
} else {
///<
[self.sortList addObject:frame];
NSArray *sortedSendQuery = [self.sortList sortedArrayUsingFunction:frameDataCompare context:NULL];
@@ -66,52 +70,59 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
[self removeExpireFrame];
///
LFFrame *firstFrame = [self.sortList lfPopFirstObject];
if(firstFrame) [self.list addObject:firstFrame];
if (firstFrame) [self.list addObject:firstFrame];
}
dispatch_semaphore_signal(_lock);
}
- (LFFrame*)popFirstObject{
- (LFFrame *)popFirstObject {
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
LFFrame *firstFrame = [self.list lfPopFirstObject];
dispatch_semaphore_signal(_lock);
return firstFrame;
}
- (void)removeAllObject{
- (void)removeAllObject {
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
[self.list removeAllObjects];
dispatch_semaphore_signal(_lock);
}
- (void)removeExpireFrame{
if(self.list.count < self.maxCount) return;
NSArray *pFrames = [self expirePFrames];///< P到第一个I之间的p帧
if(pFrames && pFrames.count > 0){
[self.list removeObjectsInArray:pFrames];
return;
- (void)removeExpireFrame {
if (self.list.count < self.maxCount) return;
if(self.needDropFrame){
NSArray *pFrames = [self expirePFrames];///< P到第一个I之间的p帧
self.lastDropFrames += [pFrames count];
if (pFrames && pFrames.count > 0) {
[self.list removeObjectsInArray:pFrames];
return;
}
NSArray *iFrames = [self expireIFrames];///<  I帧I帧可能对应多个nal
self.lastDropFrames += [iFrames count];
if (iFrames) {
[self.list removeObjectsInArray:iFrames];
return;
}
[self.list removeAllObjects];
}else{
[self.list lfPopFirstObject];
}
LFFrame *firstIFrame = [self firstIFrame];
if(firstIFrame){
[self.list removeObject:firstIFrame];
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{
} else if (!videoFrame.isKeyFrame) {
[pframes addObject:frame];
}
}
@@ -119,102 +130,105 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
return pframes;
}
- (LFFrame*)firstIFrame{
for(NSInteger index = 0;index < self.list.count;index++){
- (NSArray *)expireIFrames {
NSMutableArray *iframes = [[NSMutableArray alloc] init];
uint64_t timeStamp = 0;
for (NSInteger index = 0; index < self.list.count; index++) {
LFFrame *frame = [self.list objectAtIndex:index];
if([frame isKindOfClass:[LFVideoFrame class]] && ((LFVideoFrame*)frame).isKeyFrame){
return frame;
if ([frame isKindOfClass:[LFVideoFrame class]] && ((LFVideoFrame *)frame).isKeyFrame) {
if (timeStamp != 0 && timeStamp != frame.timestamp) break;
[iframes addObject:frame];
timeStamp = frame.timestamp;
}
}
return nil;
return iframes;
}
NSInteger frameDataCompare(id obj1, id obj2, void *context){
LFFrame* frame1 = (LFFrame*) obj1;
LFFrame *frame2 = (LFFrame*) obj2;
LFFrame *frame1 = (LFFrame *)obj1;
LFFrame *frame2 = (LFFrame *)obj2;
if (frame1.timestamp == frame2.timestamp)
return NSOrderedSame;
else if(frame1.timestamp > frame2.timestamp)
else if (frame1.timestamp > frame2.timestamp)
return NSOrderedDescending;
return NSOrderedAscending;
}
- (LFLiveBuffferState)currentBufferState{
- (LFLiveBuffferState)currentBufferState {
NSInteger currentCount = 0;
NSInteger increaseCount = 0;
NSInteger decreaseCount = 0;
for(NSNumber *number in self.thresholdList){
if(number.integerValue >= currentCount){
increaseCount ++;
}else{
decreaseCount ++;
for (NSNumber *number in self.thresholdList) {
if (number.integerValue >= currentCount) {
increaseCount++;
} else {
decreaseCount++;
}
currentCount = [number integerValue];
}
if(increaseCount >= self.callBackInterval){
if (increaseCount >= self.callBackInterval) {
return LFLiveBuffferIncrease;
}
if(decreaseCount >= self.callBackInterval){
if (decreaseCount >= self.callBackInterval) {
return LFLiveBuffferDecline;
}
return LFLiveBuffferUnknown;
}
#pragma mark -- Setter Getter
- (NSMutableArray*)list{
if(!_list){
- (NSMutableArray *)list {
if (!_list) {
_list = [[NSMutableArray alloc] init];
}
return _list;
}
- (NSMutableArray*)sortList{
if(!_sortList){
- (NSMutableArray *)sortList {
if (!_sortList) {
_sortList = [[NSMutableArray alloc] init];
}
return _sortList;
}
- (NSMutableArray*)thresholdList{
if(!_thresholdList){
- (NSMutableArray *)thresholdList {
if (!_thresholdList) {
_thresholdList = [[NSMutableArray alloc] init];
}
return _thresholdList;
}
#pragma mark -- 采样
- (void)tick{
- (void)tick {
/** 采样 3个阶段 如果网络都是好或者都是差给回调 */
_currentInterval += self.updateInterval;
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
[self.thresholdList addObject:@(self.list.count)];
dispatch_semaphore_signal(_lock);
if(self.currentInterval >= self.callBackInterval){
if (self.currentInterval >= self.callBackInterval) {
LFLiveBuffferState state = [self currentBufferState];
if(state == LFLiveBuffferIncrease){
if(self.delegate && [self.delegate respondsToSelector:@selector(streamingBuffer:bufferState:)]){
if (state == LFLiveBuffferIncrease) {
if (self.delegate && [self.delegate respondsToSelector:@selector(streamingBuffer:bufferState:)]) {
[self.delegate streamingBuffer:self bufferState:LFLiveBuffferIncrease];
}
}else if(state == LFLiveBuffferDecline){
if(self.delegate && [self.delegate respondsToSelector:@selector(streamingBuffer:bufferState:)]){
} else if (state == LFLiveBuffferDecline) {
if (self.delegate && [self.delegate respondsToSelector:@selector(streamingBuffer:bufferState:)]) {
[self.delegate streamingBuffer:self bufferState:LFLiveBuffferDecline];
}
}
self.currentInterval = 0;
[self.thresholdList removeAllObjects];
}
__weak typeof(self) _self = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.updateInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
__weak typeof(_self) self = _self;
__strong typeof(_self) self = _self;
[self tick];
});
}
@@ -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;
-95
View File
@@ -1,95 +0,0 @@
//
// LFLiveSession.h
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import "LFLiveStreamInfo.h"
#import "LFAudioFrame.h"
#import "LFVideoFrame.h"
#import "LFLiveAudioConfiguration.h"
#import "LFLiveVideoConfiguration.h"
#import "LFLiveDebug.h"
typedef void (^ LFRequestComplete)(_Nullable id info,NSError *_Nullable errorMsg);
@class LFLiveSession;
@protocol LFLiveSessionDelegate <NSObject>
@optional
/** live status changed will callback */
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state;
/** live debug info callback */
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug*)debugInfo;
/** callback socket errorcode */
- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode;
@end
@class LFLiveStreamInfo;
@interface LFLiveSession : NSObject
#pragma mark - Attribute
///=============================================================================
/// @name Attribute
///=============================================================================
/** The delegate of the capture. captureData callback */
@property (nullable,nonatomic, weak) id<LFLiveSessionDelegate> delegate;
/** The running control start capture or stop capture*/
@property (nonatomic, assign) BOOL running;
/** The preView will show OpenGL ES view*/
@property (nonatomic, strong,null_resettable) UIView *preView;
/** The captureDevicePosition control camraPosition ,default front*/
@property (nonatomic, assign) AVCaptureDevicePosition captureDevicePosition;
/** The beautyFace control capture shader filter empty or beautiy */
@property (nonatomic, assign) BOOL beautyFace;
/** The muted control callbackAudioData,muted will memset 0.*/
@property (nonatomic,assign) BOOL muted;
/** The stream control upload and package*/
@property (nullable,nonatomic, strong,readonly) LFLiveStreamInfo * streamInfo;
/** The status of the stream .*/
@property (nonatomic,assign,readonly) LFLiveState state;
/** The showDebugInfo control streamInfo and uploadInfo(1s) *.*/
@property (nonatomic,assign) BOOL showDebugInfo;
/** The reconnectInterval control reconnect timeInterval(重连间隔) *.*/
@property (nonatomic,assign) NSUInteger reconnectInterval;
/** The reconnectCount control reconnect count (重连次数) *.*/
@property (nonatomic,assign) NSUInteger reconnectCount;
#pragma mark - Initializer
///=============================================================================
/// @name Initializer
///=============================================================================
- (nullable instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
/**
The designated initializer. Multiple instances with the same configuration will make the
capture unstable.
*/
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration*)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration*)videoConfiguration NS_DESIGNATED_INITIALIZER;
/** The start stream .*/
- (void)startLive:(nonnull LFLiveStreamInfo*)streamInfo;
/** The stop stream .*/
- (void)stopLive;
@end
-266
View File
@@ -1,266 +0,0 @@
//
// LFLiveSession.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFLiveSession.h"
#import "LFVideoCapture.h"
#import "LFAudioCapture.h"
#import "LFHardwareVideoEncoder.h"
#import "LFHardwareAudioEncoder.h"
#import "LFStreamRtmpSocket.h"
#import "LFLiveStreamInfo.h"
#define LFLiveReportKey @"com.youku.liveSessionReport"
@interface LFLiveSession ()<LFAudioCaptureDelegate,LFVideoCaptureDelegate,LFAudioEncodingDelegate,LFVideoEncodingDelegate,LFStreamSocketDelegate>
{
dispatch_semaphore_t _lock;
}
///音频配置
@property (nonatomic, strong) LFLiveAudioConfiguration *audioConfiguration;
///视频配置
@property (nonatomic, strong) LFLiveVideoConfiguration *videoConfiguration;
/// 声音采集
@property (nonatomic, strong) LFAudioCapture *audioCaptureSource;
/// 视频采集
@property (nonatomic, strong) LFVideoCapture *videoCaptureSource;
/// 音频编码
@property (nonatomic, strong) id<LFAudioEncoding> audioEncoder;
/// 视频编码
@property (nonatomic, strong) id<LFVideoEncoding> videoEncoder;
/// 上传
@property (nonatomic, strong) id<LFStreamSocket> socket;
#pragma mark -- 内部标识
/// 上报
@property (nonatomic, copy) dispatch_block_t reportBlock;
/// debugInfo
@property (nonatomic, strong) LFLiveDebug *debugInfo;
/// streamInfo
@property (nonatomic, strong) LFLiveStreamInfo *streamInfo;
/// uploading
@property (nonatomic, assign) BOOL uploading;
/// state
@property (nonatomic,assign,readwrite) LFLiveState state;
@end
/**  时间戳 */
#define NOW (CACurrentMediaTime()*1000)
@interface LFLiveSession ()
@property (nonatomic, assign) uint64_t timestamp;
@property (nonatomic, assign) BOOL isFirstFrame;
@property (nonatomic, assign) uint64_t currentTimestamp;
@end
@implementation LFLiveSession
#pragma mark -- LifeCycle
- (instancetype)initWithAudioConfiguration:(LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(LFLiveVideoConfiguration *)videoConfiguration{
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);
}
return self;
}
- (void)dealloc{
self.audioCaptureSource.running = NO;
self.videoCaptureSource.running = NO;
}
#pragma mark -- CustomMethod
- (void)startLive:(LFLiveStreamInfo*)streamInfo{
if(!streamInfo) return;
_streamInfo = streamInfo;
[self.socket start];
}
- (void)stopLive{
self.uploading = NO;
[self.socket stop];
}
#pragma mark -- CaptureDelegate
- (void)captureOutput:(nullable LFAudioCapture*)capture audioBuffer:(AudioBufferList)inBufferList{
[self.audioEncoder encodeAudioData:inBufferList timeStamp:self.currentTimestamp];
}
- (void)captureOutput:(nullable LFVideoCapture*)capture pixelBuffer:(nullable CVImageBufferRef)pixelBuffer{
[self.videoEncoder encodeVideoData:pixelBuffer timeStamp:self.currentTimestamp];
}
#pragma mark -- EncoderDelegate
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame*)frame{
if(self.uploading) [self.socket sendFrame:frame];//<上传
}
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame*)frame{
if(self.uploading) [self.socket sendFrame:frame];//<上传
}
#pragma mark -- LFStreamTcpSocketDelegate
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status{
if(status == LFLiveStart){
if(!self.uploading){
self.timestamp = 0;
self.isFirstFrame = YES;
self.uploading = YES;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
self.state = status;
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:liveStateDidChange:)]){
[self.delegate liveSession:self liveStateDidChange:status];
}
});
}
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode{
dispatch_async(dispatch_get_main_queue(), ^{
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:errorCode:)]){
[self.delegate liveSession:self errorCode:errorCode];
}
});
}
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug*)debugInfo{
self.debugInfo = debugInfo;
if(self.showDebugInfo){
dispatch_async(dispatch_get_main_queue(), ^{
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:debugInfo:)]){
[self.delegate liveSession:self debugInfo:debugInfo];
}
});
}
}
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status{
NSUInteger videoBitRate = [_videoEncoder videoBitRate];
if(status == LFLiveBuffferIncrease){
if(videoBitRate < _videoConfiguration.videoMaxBitRate){
videoBitRate = videoBitRate + 50*1024;
[_videoEncoder setVideoBitRate:videoBitRate];
}
}else{
if(videoBitRate > _videoConfiguration.videoMinBitRate){
videoBitRate = videoBitRate - 100*1024;
[_videoEncoder setVideoBitRate:videoBitRate];
}
}
}
#pragma mark -- Getter Setter
- (void)setRunning:(BOOL)running{
if(_running == running) return;
[self willChangeValueForKey:@"running"];
_running = running;
[self didChangeValueForKey:@"running"];
self.videoCaptureSource.running = _running;
self.audioCaptureSource.running = _running;
}
- (void)setPreView:(UIView *)preView{
[self.videoCaptureSource setPreView:preView];
}
- (UIView*)preView{
return self.videoCaptureSource.preView;
}
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition{
[self.videoCaptureSource setCaptureDevicePosition:captureDevicePosition];
}
- (AVCaptureDevicePosition)captureDevicePosition{
return self.videoCaptureSource.captureDevicePosition;
}
- (void)setBeautyFace:(BOOL)beautyFace{
[self.videoCaptureSource setBeautyFace:beautyFace];
}
- (BOOL)beautyFace{
return self.videoCaptureSource.beautyFace;
}
- (void)setMuted:(BOOL)muted{
[self.audioCaptureSource setMuted:muted];
}
- (BOOL)muted{
return self.audioCaptureSource.muted;
}
- (LFAudioCapture*)audioCaptureSource{
if(!_audioCaptureSource){
_audioCaptureSource = [[LFAudioCapture alloc] initWithAudioConfiguration:_audioConfiguration];
_audioCaptureSource.delegate = self;
}
return _audioCaptureSource;
}
- (LFVideoCapture*)videoCaptureSource{
if(!_videoCaptureSource){
_videoCaptureSource = [[LFVideoCapture alloc] initWithVideoConfiguration:_videoConfiguration];
_videoCaptureSource.delegate = self;
}
return _videoCaptureSource;
}
- (id<LFAudioEncoding>)audioEncoder{
if(!_audioEncoder){
_audioEncoder = [[LFHardwareAudioEncoder alloc] initWithAudioStreamConfiguration:_audioConfiguration];
[_audioEncoder setDelegate:self];
}
return _audioEncoder;
}
- (id<LFVideoEncoding>)videoEncoder{
if(!_videoEncoder){
_videoEncoder = [[LFHardwareVideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
[_videoEncoder setDelegate:self];
}
return _videoEncoder;
}
- (id<LFStreamSocket>)socket{
if(!_socket){
_socket = [[LFStreamRtmpSocket alloc] initWithStream:self.streamInfo];
[_socket setDelegate:self];
}
return _socket;
}
- (LFLiveStreamInfo*)streamInfo{
if(!_streamInfo){
_streamInfo = [[LFLiveStreamInfo alloc] init];
}
return _streamInfo;
}
- (uint64_t)currentTimestamp{
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
uint64_t currentts = 0;
if(_isFirstFrame == true) {
_timestamp = NOW;
_isFirstFrame = false;
currentts = 0;
}
else {
currentts = NOW - _timestamp;
}
dispatch_semaphore_signal(_lock);
return currentts;
}
@end
-190
View File
@@ -1,190 +0,0 @@
//
// LFVideoCapture.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/1.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFVideoCapture.h"
#import "GPUImage.h"
#import "LFGPUImageBeautyFilter.h"
#import "LFGPUImageEmptyFilter.h"
@interface LFVideoCapture ()
@property(nonatomic, strong) GPUImageVideoCamera *videoCamera;
@property(nonatomic, strong) GPUImageOutput<GPUImageInput> *filter;
@property(nonatomic, strong) GPUImageOutput<GPUImageInput> *emptyFilter;
@property(nonatomic, strong) GPUImageOutput<GPUImageInput> *beau;
@property(nonatomic, strong) GPUImageCropFilter *cropfilter;
@property(nonatomic, strong) GPUImageView *gpuImageView;
@property (nonatomic, strong) LFLiveVideoConfiguration *configuration;
@end
@implementation LFVideoCapture
#pragma mark -- LifeCycle
- (instancetype)initWithVideoConfiguration:(LFLiveVideoConfiguration *)configuration{
if(self = [super init]){
_configuration = configuration;
_videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:_configuration.avSessionPreset cameraPosition:AVCaptureDevicePositionFront];
_videoCamera.outputImageOrientation = _configuration.orientation;
_videoCamera.horizontallyMirrorFrontFacingCamera = NO;
_videoCamera.horizontallyMirrorRearFacingCamera = NO;
_videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
_gpuImageView = [[GPUImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
[_gpuImageView setFillMode:kGPUImageFillModePreserveAspectRatioAndFill];
[_gpuImageView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[_gpuImageView setInputRotation:kGPUImageFlipHorizonal atIndex:0];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
self.beautyFace = YES;
}
return self;
}
- (void)dealloc{
[UIApplication sharedApplication].idleTimerDisabled = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_videoCamera stopCameraCapture];
}
#pragma mark -- Setter Getter
- (void)setRunning:(BOOL)running{
if(_running == running) return;
_running = running;
if(!_running){
[UIApplication sharedApplication].idleTimerDisabled = NO;
[_videoCamera stopCameraCapture];
}else{
[UIApplication sharedApplication].idleTimerDisabled = YES;
[_videoCamera startCameraCapture];
}
}
- (void)setPreView:(UIView *)preView{
if(_gpuImageView.superview) [_gpuImageView removeFromSuperview];
[preView insertSubview:_gpuImageView atIndex:0];
}
- (UIView*)preView{
return _gpuImageView.superview;
}
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition{
[_videoCamera rotateCamera];
_videoCamera.frameRate = (int32_t)_configuration.videoFrameRate;
if(captureDevicePosition == AVCaptureDevicePositionFront) [_gpuImageView setInputRotation:kGPUImageFlipHorizonal atIndex:0];
else [_gpuImageView setInputRotation:kGPUImageNoRotation atIndex:0];
}
- (AVCaptureDevicePosition)captureDevicePosition{
return [_videoCamera cameraPosition];
}
- (void)setVideoFrameRate:(NSInteger)videoFrameRate{
if(videoFrameRate <= 0) return;
if(videoFrameRate == _videoCamera.frameRate) return;
_videoCamera.frameRate = (uint32_t)videoFrameRate;
}
- (NSInteger)videoFrameRate{
return _videoCamera.frameRate;
}
- (void)setBeautyFace:(BOOL)beautyFace{
if(_beautyFace == beautyFace) return;
_beautyFace = beautyFace;
[_emptyFilter removeAllTargets];
[_filter removeAllTargets];
[_cropfilter removeAllTargets];
[_videoCamera removeAllTargets];
[_beau removeAllTargets];
if(_beautyFace){
_filter = [[GPUImageUnsharpMaskFilter alloc] init];
[(GPUImageUnsharpMaskFilter*)_filter setIntensity:1.23];
[(GPUImageUnsharpMaskFilter*)_filter setBlurRadiusInPixels:13];
_beau = [[LFGPUImageBeautyFilter alloc] init];
_emptyFilter = [[LFGPUImageEmptyFilter alloc] init];
}else{
_filter = [[LFGPUImageEmptyFilter alloc] init];
}
if (beautyFace) {
__weak typeof(self) _self = self;
[_emptyFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
[_self processVideo:output];
}];
} else {
__weak typeof(self) _self = self;
[_filter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
[_self processVideo:output];
}];
}
if(_configuration.isClipVideo){
if(_configuration.orientation == UIInterfaceOrientationPortrait || _configuration.orientation == UIInterfaceOrientationPortraitUpsideDown){
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(0.125, 0, 0.75, 1)];
}else{
_cropfilter = [[GPUImageCropFilter alloc] initWithCropRegion:CGRectMake(0, 0.125, 1, 0.75)];
}
[_videoCamera addTarget:_cropfilter];
[_cropfilter addTarget:_filter];
}else{
[_videoCamera addTarget:_filter];
}
if (beautyFace) {
[_filter addTarget:_beau];
[_beau addTarget:_emptyFilter];
[_emptyFilter addTarget:_gpuImageView];
} else {
[_filter addTarget:_gpuImageView];
}
if(_videoCamera.cameraPosition == AVCaptureDevicePositionFront) [_gpuImageView setInputRotation:kGPUImageFlipHorizonal atIndex:0];
else [_gpuImageView setInputRotation:kGPUImageNoRotation atIndex:0];
}
#pragma mark -- Custom Method
- (void) processVideo:(GPUImageOutput *)output{
__weak typeof(self) _self = self;
@autoreleasepool {
GPUImageFramebuffer *imageFramebuffer = output.framebufferForOutput;
CVPixelBufferRef pixelBuffer = [imageFramebuffer pixelBuffer];
if(pixelBuffer && _self.delegate && [_self.delegate respondsToSelector:@selector(captureOutput:pixelBuffer:)]){
[_self.delegate captureOutput:_self pixelBuffer:pixelBuffer];
}
}
}
#pragma mark Notification
- (void)willEnterBackground:(NSNotification*)notification{
[UIApplication sharedApplication].idleTimerDisabled = NO;
[_videoCamera pauseCameraCapture];
runSynchronouslyOnVideoProcessingQueue(^{
glFinish();
});
}
- (void)willEnterForeground:(NSNotification*)notification{
[_videoCamera resumeCameraCapture];
[UIApplication sharedApplication].idleTimerDisabled = YES;
}
@end
-181
View File
@@ -1,181 +0,0 @@
//
// LFHardwareAudioEncoder.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFHardwareAudioEncoder.h"
@interface LFHardwareAudioEncoder (){
AudioConverterRef m_converter;
char *aacBuf;
}
@property (nonatomic, strong) LFLiveAudioConfiguration *configuration;
@property (nonatomic, weak) id<LFAudioEncodingDelegate> aacDeleage;
@end
@implementation LFHardwareAudioEncoder
- (instancetype)initWithAudioStreamConfiguration:(LFLiveAudioConfiguration *)configuration{
if(self = [super init]){
_configuration = configuration;
}
return self;
}
- (void)dealloc{
if(aacBuf) free(aacBuf);
}
#pragma mark -- LFAudioEncoder
- (void)setDelegate:(id<LFAudioEncodingDelegate>)delegate{
_aacDeleage = delegate;
}
- (void)encodeAudioData:(AudioBufferList)inBufferList timeStamp:(uint64_t)timeStamp{
if (![self createAudioConvert]){
return;
}
if(!aacBuf){
aacBuf = malloc(inBufferList.mBuffers[0].mDataByteSize);
}
// 初始化一个输出缓冲列表
AudioBufferList outBufferList;
outBufferList.mNumberBuffers = 1;
outBufferList.mBuffers[0].mNumberChannels = inBufferList.mBuffers[0].mNumberChannels;
outBufferList.mBuffers[0].mDataByteSize = inBufferList.mBuffers[0].mDataByteSize; // 设置缓冲区大小
outBufferList.mBuffers[0].mData = aacBuf; // 设置AAC缓冲区
UInt32 outputDataPacketSize = 1;
if (AudioConverterFillComplexBuffer(m_converter, inputDataProc, &inBufferList, &outputDataPacketSize, &outBufferList, NULL) != noErr){
return;
}
LFAudioFrame *audioFrame = [LFAudioFrame new];
audioFrame.timestamp = timeStamp;
audioFrame.data = [NSData dataWithBytes:aacBuf length:outBufferList.mBuffers[0].mDataByteSize];
char exeData[2];
exeData[0] = _configuration.asc[0];
exeData[1] = _configuration.asc[1];
audioFrame.audioInfo =[NSData dataWithBytes:exeData length:2];
if(self.aacDeleage && [self.aacDeleage respondsToSelector:@selector(audioEncoder:audioFrame:)]){
[self.aacDeleage audioEncoder:self audioFrame:audioFrame];
}
}
- (void)stopEncoder{
}
#pragma mark -- CustomMethod
-(BOOL)createAudioConvert{ //根据输入样本初始化一个编码转换器
if (m_converter != nil){
return TRUE;
}
AudioStreamBasicDescription inputFormat = {0};
inputFormat.mSampleRate = _configuration.audioSampleRate;
inputFormat.mFormatID = kAudioFormatLinearPCM;
inputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
inputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;
inputFormat.mFramesPerPacket = 1;
inputFormat.mBitsPerChannel = 16;
inputFormat.mBytesPerFrame = inputFormat.mBitsPerChannel / 8 * inputFormat.mChannelsPerFrame;
inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
AudioStreamBasicDescription outputFormat; // 这里开始是输出音频格式
memset(&outputFormat, 0, sizeof(outputFormat));
outputFormat.mSampleRate = inputFormat.mSampleRate; // 采样率保持一致
outputFormat.mFormatID = kAudioFormatMPEG4AAC; // AAC编码 kAudioFormatMPEG4AAC kAudioFormatMPEG4AAC_HE_V2
outputFormat.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;;
outputFormat.mFramesPerPacket = 1024; // AAC一帧是1024个字节
const OSType subtype = kAudioFormatMPEG4AAC;
AudioClassDescription requestedCodecs[2] = {
{
kAudioEncoderComponentType,
subtype,
kAppleSoftwareAudioCodecManufacturer
},
{
kAudioEncoderComponentType,
subtype,
kAppleHardwareAudioCodecManufacturer
}
};
OSStatus result = AudioConverterNewSpecific(&inputFormat, &outputFormat, 2, requestedCodecs, &m_converter);
if(result != noErr) return NO;
return YES;
}
-(AudioClassDescription*)getAudioClassDescriptionWithType:(UInt32)type fromManufacturer:(UInt32)manufacturer { // 获得相应的编码器
static AudioClassDescription audioDesc;
UInt32 encoderSpecifier = type, size = 0;
OSStatus status;
memset(&audioDesc, 0, sizeof(audioDesc));
status = AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size);
if (status) {
return nil;
}
uint32_t count = size / sizeof(AudioClassDescription);
AudioClassDescription descs[count];
status = AudioFormatGetProperty(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size, descs);
for (uint32_t i = 0; i < count; i++){
if ((type == descs[i].mSubType) && (manufacturer == descs[i].mManufacturer)){
memcpy(&audioDesc, &descs[i], sizeof(audioDesc));
break;
}
}
return &audioDesc;
}
#pragma mark -- AudioCallBack
OSStatus inputDataProc(AudioConverterRef inConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData,AudioStreamPacketDescription **outDataPacketDescription, void *inUserData) { //<span style="font-family: Arial, Helvetica, sans-serif;">AudioConverterFillComplexBuffer 编码过程中,会要求这个函数来填充输入数据,也就是原始PCM数据</span>
AudioBufferList bufferList = *(AudioBufferList*)inUserData;
ioData->mBuffers[0].mNumberChannels = 1;
ioData->mBuffers[0].mData = bufferList.mBuffers[0].mData;
ioData->mBuffers[0].mDataByteSize = bufferList.mBuffers[0].mDataByteSize;
return noErr;
}
/**
* Add ADTS header at the beginning of each and every AAC packet.
* This is needed as MediaCodec encoder generates a packet of raw
* AAC data.
*
* Note the packetLen must count in the ADTS header itself.
* See: http://wiki.multimedia.cx/index.php?title=ADTS
* Also: http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Channel_Configurations
**/
- (NSData*)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength {
int adtsLength = 7;
char *packet = malloc(sizeof(char) * adtsLength);
// Variables Recycled by addADTStoPacket
int profile = 2; //AAC LC
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
int freqIdx = 4; //44.1KHz
int chanCfg = (int)channel; //MPEG-4 Audio Channel Configuration. 1 Channel front-center
NSUInteger fullLength = adtsLength + rawDataLength;
// fill in ADTS data
packet[0] = (char)0xFF; // 11111111 = syncword
packet[1] = (char)0xF9; // 1111 1 00 1 = syncword MPEG-2 Layer CRC
packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
packet[3] = (char)(((chanCfg&3)<<6) + (fullLength>>11));
packet[4] = (char)((fullLength&0x7FF) >> 3);
packet[5] = (char)(((fullLength&7)<<5) + 0x1F);
packet[6] = (char)0xFC;
NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES];
return data;
}
@end
@@ -1,233 +0,0 @@
//
// LFLiveAudioConfiguration.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/1.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFLiveAudioConfiguration.h"
#import <sys/utsname.h>
@implementation LFLiveAudioConfiguration
#pragma mark -- LifyCycle
+ (instancetype)defaultConfiguration{
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration defaultConfigurationForQuality:LFLiveAudioQuality_Default];
return audioConfig;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveAudioQuality)audioQuality{
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration new];
audioConfig.numberOfChannels = 2;
switch (audioQuality) {
case LFLiveAudioQuality_Default:{
audioConfig.audioBitrate = LFLiveAudioBitRate_64Kbps;
}
break;
case LFLiveAudioQuality_Low:{
audioConfig.audioBitrate = LFLiveAudioBitRate_32Kbps;
}
case LFLiveAudioQuality_High:{
audioConfig.audioBitrate = LFLiveAudioBitRate_96Kbps;
}
case LFLiveAudioQuality_VeryHigh:{
audioConfig.audioBitrate = LFLiveAudioBitRate_128Kbps;
}
break;
default:
break;
}
audioConfig.audioSampleRate = [self.class isNewThaniPhone6] ? LFLiveAudioSampleRate_48000Hz : LFLiveAudioSampleRate_44100Hz;
return audioConfig;
}
- (instancetype)init{
if(self = [super init]){
_asc = malloc(2);
}
return self;
}
- (void)dealloc{
if(_asc) free(_asc);
}
#pragma mark Setter
- (void)setAudioSampleRate:(LFLiveAudioSampleRate)audioSampleRate{
_audioSampleRate = audioSampleRate;
NSInteger sampleRateIndex = [self sampleRateIndex:audioSampleRate];
self.asc[0] = 0x10 | ((sampleRateIndex>>1) & 0x3);
self.asc[1] = ((sampleRateIndex & 0x1)<<7) | ((self.numberOfChannels & 0xF) << 3);
}
- (void)setNumberOfChannels:(NSUInteger)numberOfChannels{
_numberOfChannels = numberOfChannels;
NSInteger sampleRateIndex = [self sampleRateIndex:self.audioSampleRate];
self.asc[0] = 0x10 | ((sampleRateIndex>>1) & 0x3);
self.asc[1] = ((sampleRateIndex & 0x1)<<7) | ((numberOfChannels & 0xF) << 3);
}
#pragma mark -- CustomMethod
- (NSInteger)sampleRateIndex:(NSInteger)frequencyInHz{
NSInteger sampleRateIndex = 0;
switch(frequencyInHz) {
case 96000:
sampleRateIndex = 0;
break;
case 88200:
sampleRateIndex = 1;
break;
case 64000:
sampleRateIndex = 2;
break;
case 48000:
sampleRateIndex = 3;
break;
case 44100:
sampleRateIndex = 4;
break;
case 32000:
sampleRateIndex = 5;
break;
case 24000:
sampleRateIndex = 6;
break;
case 22050:
sampleRateIndex = 7;
break;
case 16000:
sampleRateIndex = 8;
break;
case 12000:
sampleRateIndex = 9;
break;
case 11025:
sampleRateIndex = 10;
break;
case 8000:
sampleRateIndex = 11;
break;
case 7350:
sampleRateIndex = 12;
break;
default:
sampleRateIndex = 15;
}
return sampleRateIndex;
}
#pragma mark -- DeviceCategory
+(NSString*)deviceName{
struct utsname systemInfo;
uname(&systemInfo);
return [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
}
//@"iPad4,1" on 5th Generation iPad (iPad Air) - Wifi
//@"iPad4,2" on 5th Generation iPad (iPad Air) - Cellular
//@"iPad4,4" on 2nd Generation iPad Mini - Wifi
//@"iPad4,5" on 2nd Generation iPad Mini - Cellular
//@"iPad4,7" on 3rd Generation iPad Mini - Wifi (model A1599)
//@"iPhone7,1" on iPhone 6 Plus
//@"iPhone7,2" on iPhone 6
//@"iPhone8,1" on iPhone 6S
//@"iPhone8,2" on iPhone 6S Plus
+(BOOL) isNewThaniPhone6{
NSString *device = [self deviceName];
NSLog(@"device %@", device);
if (device == nil) {
return NO;
}
NSArray *array = [device componentsSeparatedByString:@","];
if (array.count <2) {
return NO;
}
NSString *model = [array objectAtIndex:0];
NSLog(@"model %@", model);
if ([model hasPrefix:@"iPhone"]) {
NSString *str1 = [model substringFromIndex:[@"iPhone" length]];
NSUInteger num = [str1 integerValue];
NSLog(@"num %lu", (unsigned long)num);
if (num > 7) {
return YES;
}
}
if ([model hasPrefix:@"iPad"]) {
NSString *str1 = [model substringFromIndex:[@"iPad" length]];
NSUInteger num = [str1 integerValue];
if (num > 4) {
return YES;
}
}
return NO;
}
#pragma mark -- Encoder
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:@(self.numberOfChannels) forKey:@"numberOfChannels"];
[aCoder encodeObject:@(self.audioSampleRate) forKey:@"audioSampleRate"];
[aCoder encodeObject:@(self.audioBitrate) forKey:@"audioBitrate"];
[aCoder encodeObject:[NSString stringWithUTF8String:self.asc] forKey:@"asc"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
_numberOfChannels = [[aDecoder decodeObjectForKey:@"numberOfChannels"] unsignedIntegerValue];
_audioSampleRate = [[aDecoder decodeObjectForKey:@"audioSampleRate"] unsignedIntegerValue];
_audioBitrate = [[aDecoder decodeObjectForKey:@"audioBitrate"] unsignedIntegerValue];
_asc = strdup([[aDecoder decodeObjectForKey:@"asc"] cStringUsingEncoding:NSUTF8StringEncoding]);
return self;
}
- (BOOL)isEqual:(id)other{
if (other == self) {
return YES;
} else if (![super isEqual:other]) {
return NO;
} else {
LFLiveAudioConfiguration *object = other;
return object.numberOfChannels == self.numberOfChannels &&
object.audioBitrate == self.audioBitrate &&
strcmp(object.asc, self.asc) == 0 &&
object.audioSampleRate == self.audioSampleRate;
}
}
- (NSUInteger)hash {
NSUInteger hash = 0;
NSArray *values = @[@(_numberOfChannels),
@(_audioSampleRate),
[NSString stringWithUTF8String:self.asc],
@(_audioBitrate)];
for (NSObject *value in values) {
hash ^= value.hash;
}
return hash;
}
- (id)copyWithZone:(nullable NSZone *)zone{
LFLiveAudioConfiguration *other = [self.class defaultConfiguration];
return other;
}
- (NSString *)description{
NSMutableString *desc = @"".mutableCopy;
[desc appendFormat:@"<LFLiveAudioConfiguration: %p>",self];
[desc appendFormat:@" numberOfChannels:%zi",self.numberOfChannels];
[desc appendFormat:@" audioSampleRate:%zi",self.audioSampleRate];
[desc appendFormat:@" audioBitrate:%zi",self.audioBitrate];
[desc appendFormat:@" audioHeader:%@",[NSString stringWithUTF8String:self.asc]];
return desc;
}
@end
@@ -1,299 +0,0 @@
//
// LFLiveVideoConfiguration.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/1.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import "LFLiveVideoConfiguration.h"
#import <AVFoundation/AVFoundation.h>
@implementation LFLiveVideoConfiguration
#pragma mark -- LifeCycle
+ (instancetype)defaultConfiguration{
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Default];
return configuration;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality{
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration defaultConfigurationForQuality:videoQuality orientation:UIInterfaceOrientationPortrait];
return configuration;
}
+ (instancetype)defaultConfigurationForQuality:(LFLiveVideoQuality)videoQuality orientation:(UIInterfaceOrientation)orientation{
LFLiveVideoConfiguration *configuration = [LFLiveVideoConfiguration new];
switch (videoQuality) {
case LFLiveVideoQuality_Low1:
{
configuration.sessionPreset = LFCaptureSessionPreset360x640;
configuration.videoFrameRate = 15;
configuration.videoMaxFrameRate = 15;
configuration.videoMinFrameRate = 10;
configuration.videoBitRate = 500 * 1024;
configuration.videoMaxBitRate = 600 * 1024;
configuration.videoMinBitRate = 250 * 1024;
}
break;
case LFLiveVideoQuality_Low2:
{
configuration.sessionPreset = LFCaptureSessionPreset360x640;
configuration.videoFrameRate = 24;
configuration.videoMaxFrameRate = 24;
configuration.videoMinFrameRate = 12;
configuration.videoBitRate = 800 * 1024;
configuration.videoMaxBitRate = 900 * 1024;
configuration.videoMinBitRate = 500 * 1024;
}
break;
case LFLiveVideoQuality_Low3:
{
configuration.sessionPreset = LFCaptureSessionPreset360x640;
configuration.videoFrameRate = 30;
configuration.videoMaxFrameRate = 30;
configuration.videoMinFrameRate = 15;
configuration.videoBitRate = 800 * 1024;
configuration.videoMaxBitRate = 900 * 1024;
configuration.videoMinBitRate = 500 * 1024;
}
break;
case LFLiveVideoQuality_Medium1:
{
configuration.sessionPreset = LFCaptureSessionPreset540x960;
configuration.videoFrameRate = 15;
configuration.videoMaxFrameRate = 15;
configuration.videoMinFrameRate = 10;
configuration.videoBitRate = 800 * 1024;
configuration.videoMaxBitRate = 900 * 1024;
configuration.videoMinBitRate = 500 * 1024;
}
break;
case LFLiveVideoQuality_Medium2:
{
configuration.sessionPreset = LFCaptureSessionPreset540x960;
configuration.videoFrameRate = 24;
configuration.videoMaxFrameRate = 24;
configuration.videoMinFrameRate = 12;
configuration.videoBitRate = 800 * 1024;
configuration.videoMaxBitRate = 900 * 1024;
configuration.videoMinBitRate = 500 * 1024;
}
break;
case LFLiveVideoQuality_Medium3:
{
configuration.sessionPreset = LFCaptureSessionPreset540x960;
configuration.videoFrameRate = 30;
configuration.videoMaxFrameRate = 30;
configuration.videoMinFrameRate = 15;
configuration.videoBitRate = 1000 * 1024;
configuration.videoMaxBitRate = 1200 * 1024;
configuration.videoMinBitRate = 500 * 1024;
}
break;
case LFLiveVideoQuality_High1:
{
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
configuration.videoFrameRate = 15;
configuration.videoMaxFrameRate = 15;
configuration.videoMinFrameRate = 10;
configuration.videoBitRate = 1000 * 1024;
configuration.videoMaxBitRate = 1200 * 1024;
configuration.videoMinBitRate = 500 * 1024;
}
break;
case LFLiveVideoQuality_High2:
{
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
configuration.videoFrameRate = 24;
configuration.videoMaxFrameRate = 24;
configuration.videoMinFrameRate = 12;
configuration.videoBitRate = 1200 * 1024;
configuration.videoMaxBitRate = 1300 * 1024;
configuration.videoMinBitRate = 800 * 1024;
}
break;
case LFLiveVideoQuality_High3:
{
configuration.sessionPreset = LFCaptureSessionPreset720x1280;
configuration.videoFrameRate = 30;
configuration.videoMaxFrameRate = 30;
configuration.videoMinFrameRate = 15;
configuration.videoBitRate = 1200 * 1024;
configuration.videoMaxBitRate = 1300 * 1024;
configuration.videoMinBitRate = 500 * 1024;
}
break;
default:
break;
}
configuration.sessionPreset = [configuration supportSessionPreset:configuration.sessionPreset];
configuration.videoMaxKeyframeInterval = configuration.videoFrameRate*2;
configuration.orientation = orientation;
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown){
configuration.videoSize = CGSizeMake(368, 640);
}else{
configuration.videoSize = CGSizeMake(640, 368);
}
return configuration;
}
#pragma mark -- Setter Getter
- (NSString*)avSessionPreset{
NSString *avSessionPreset = nil;
switch (self.sessionPreset) {
case LFCaptureSessionPreset360x640:
{
avSessionPreset = AVCaptureSessionPreset640x480;
}
break;
case LFCaptureSessionPreset540x960:
{
avSessionPreset = AVCaptureSessionPresetiFrame960x540;
}
break;
case LFCaptureSessionPreset720x1280:
{
avSessionPreset = AVCaptureSessionPreset1280x720;
}
break;
default:{
avSessionPreset = AVCaptureSessionPreset640x480;
}
break;
}
return avSessionPreset;
}
- (void)setVideoMaxBitRate:(NSUInteger)videoMaxBitRate{
if(videoMaxBitRate <= _videoBitRate) return;
_videoMaxBitRate = videoMaxBitRate;
}
- (void)setVideoMinBitRate:(NSUInteger)videoMinBitRate{
if(videoMinBitRate >= _videoBitRate) return;
_videoMinBitRate = videoMinBitRate;
}
- (void)setVideoMaxFrameRate:(NSUInteger)videoMaxFrameRate{
if(videoMaxFrameRate <= _videoFrameRate) return;
_videoMaxFrameRate = videoMaxFrameRate;
}
- (void)setVideoMinFrameRate:(NSUInteger)videoMinFrameRate{
if(videoMinFrameRate >= _videoFrameRate) return;
_videoMinFrameRate = videoMinFrameRate;
}
#pragma mark -- Custom Method
- (LFLiveVideoSessionPreset)supportSessionPreset:(LFLiveVideoSessionPreset)sessionPreset{
NSString *avSessionPreset = [self avSessionPreset];
AVCaptureSession *session = [[AVCaptureSession alloc] init];
if(![session canSetSessionPreset:avSessionPreset]){
if(sessionPreset == LFCaptureSessionPreset720x1280){
sessionPreset = LFCaptureSessionPreset540x960;
if(![session canSetSessionPreset:avSessionPreset]){
sessionPreset = LFCaptureSessionPreset360x640;
}
}else if(sessionPreset == LFCaptureSessionPreset540x960){
sessionPreset = LFCaptureSessionPreset360x640;
}
}
return sessionPreset;
}
- (BOOL)isClipVideo{
return self.sessionPreset == LFCaptureSessionPreset360x640 ? YES : NO;
}
#pragma mark -- encoder
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:[NSValue valueWithCGSize:self.videoSize] forKey:@"videoSize"];
[aCoder encodeObject:@(self.videoFrameRate) forKey:@"videoFrameRate"];
[aCoder encodeObject:@(self.videoMaxKeyframeInterval) forKey:@"videoMaxKeyframeInterval"];
[aCoder encodeObject:@(self.videoBitRate) forKey:@"videoBitRate"];
[aCoder encodeObject:@(self.sessionPreset) forKey:@"sessionPreset"];
[aCoder encodeObject:@(self.orientation) forKey:@"orientation"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
_videoSize = [[aDecoder decodeObjectForKey:@"videoSize"] CGSizeValue];
_videoFrameRate = [[aDecoder decodeObjectForKey:@"videoFrameRate"] unsignedIntegerValue];
_videoMaxKeyframeInterval = [[aDecoder decodeObjectForKey:@"videoMaxKeyframeInterval"] unsignedIntegerValue];
_videoBitRate = [[aDecoder decodeObjectForKey:@"videoBitRate"] unsignedIntegerValue];
_sessionPreset = [[aDecoder decodeObjectForKey:@"sessionPreset"] unsignedIntegerValue];
_orientation = [[aDecoder decodeObjectForKey:@"orientation"] unsignedIntegerValue];
return self;
}
- (NSUInteger)hash {
NSUInteger hash = 0;
NSArray *values = @[[NSValue valueWithCGSize:self.videoSize],
@(self.videoFrameRate),
@(self.videoMaxFrameRate),
@(self.videoMinFrameRate),
@(self.videoMaxKeyframeInterval),
@(self.videoBitRate),
@(self.videoMaxBitRate),
@(self.videoMinBitRate),
@(self.isClipVideo),
self.avSessionPreset,
@(self.sessionPreset),
@(self.orientation),];
for (NSObject *value in values) {
hash ^= value.hash;
}
return hash;
}
- (BOOL)isEqual:(id)other
{
if (other == self) {
return YES;
} else if (![super isEqual:other]) {
return NO;
} else {
LFLiveVideoConfiguration *object = other;
return CGSizeEqualToSize(object.videoSize, self.videoSize) &&
object.videoFrameRate == self.videoFrameRate &&
object.videoMaxFrameRate == self.videoMaxFrameRate &&
object.videoMinFrameRate == self.videoMinFrameRate &&
object.videoMaxKeyframeInterval == self.videoMaxKeyframeInterval &&
object.videoBitRate == self.videoBitRate &&
object.videoMaxBitRate == self.videoMaxBitRate &&
object.videoMinBitRate == self.videoMinBitRate &&
object.isClipVideo == self.isClipVideo &&
[object.avSessionPreset isEqualToString:self.avSessionPreset] &&
object.sessionPreset == self.sessionPreset &&
object.orientation == self.orientation;
}
}
- (id)copyWithZone:(nullable NSZone *)zone{
LFLiveVideoConfiguration *other = [self.class defaultConfiguration];
return other;
}
- (NSString *)description{
NSMutableString *desc = @"".mutableCopy;
[desc appendFormat:@"<LFLiveVideoConfiguration: %p>",self];
[desc appendFormat:@" videoSize:%@",NSStringFromCGSize(self.videoSize)];
[desc appendFormat:@" videoFrameRate:%zi",self.videoFrameRate];
[desc appendFormat:@" videoMaxFrameRate:%zi",self.videoMaxFrameRate];
[desc appendFormat:@" videoMinFrameRate:%zi",self.videoMinFrameRate];
[desc appendFormat:@" videoMaxKeyframeInterval:%zi",self.videoMaxKeyframeInterval];
[desc appendFormat:@" videoBitRate:%zi",self.videoBitRate];
[desc appendFormat:@" videoMaxBitRate:%zi",self.videoMaxBitRate];
[desc appendFormat:@" videoMinBitRate:%zi",self.videoMinBitRate];
[desc appendFormat:@" isClipVideo:%zi",self.isClipVideo];
[desc appendFormat:@" avSessionPreset:%@",self.avSessionPreset];
[desc appendFormat:@" sessionPreset:%zi",self.sessionPreset];
[desc appendFormat:@" orientation:%zi",self.orientation];
return desc;
}
@end
-478
View File
@@ -1,478 +0,0 @@
#import "LFGPUImageBeautyFilter.h"
#import "GPUImageGaussianBlurFilter.h"
#import "GPUImageTwoInputFilter.h"
#import "GPUImageBilateralFilter.h"
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const KLFGPUImageBeautyFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
varying highp vec2 textureCoordinate2;
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;
uniform lowp float excludeCircleRadius;
uniform lowp vec2 excludeCirclePoint;
uniform lowp float excludeBlurSize;
uniform highp float aspectRatio;
//247 217 211
void main()
{
lowp vec4 tmpColor = texture2D(inputImageTexture, textureCoordinate);
// gl_FragColor = tmpColor;
// return;
lowp vec4 sharpImageColor = texture2D(inputImageTexture, textureCoordinate);
lowp vec4 blurredImageColor = texture2D(inputImageTexture2, textureCoordinate2);
if((sharpImageColor.r > 0.372549 && sharpImageColor.g > 0.156863 && sharpImageColor.b > 0.078431 &&
sharpImageColor.r - sharpImageColor.g > 0.058823 && sharpImageColor.r - sharpImageColor.b > 0.058823) ||
(sharpImageColor.r > 0.784314 && sharpImageColor.g > 0.823530 && sharpImageColor.b > 0.666667 &&
abs(sharpImageColor.r - sharpImageColor.b) <= 0.058823 && sharpImageColor.r > sharpImageColor.b && sharpImageColor.g > sharpImageColor.b)) {
mediump float rpass;
mediump float gpass;
mediump float bpass;
mediump float hpass;
hpass = 0.5 / excludeBlurSize;
hpass = 1.0;
mediump float dis = distance(sharpImageColor, blurredImageColor);
bpass = min((0.4+1.0*(1.0*dis)), 1.0);
gl_FragColor = mix(sharpImageColor, blurredImageColor, 1.0-bpass);
} else {
mediump float rpass;
mediump float gpass;
mediump float bpass;
mediump float hpass;
hpass = 0.5 / excludeBlurSize;
hpass = 1.0;
mediump float dis = distance(sharpImageColor, blurredImageColor);
bpass = min((0.45+1.0*(1.0*dis)), 1.0);
gl_FragColor = mix(sharpImageColor, blurredImageColor, 1.0-bpass);
}
mediump float r;
mediump float g;
mediump float b;
mediump float status = 0.81;
// r = min((gl_FragColor.r+sharpImageColor.r - sharpImageColor.r*gl_FragColor.r), 1.0);
// g = min((gl_FragColor.g+sharpImageColor.g - sharpImageColor.g*gl_FragColor.g), 1.0);
// b = min((gl_FragColor.b+sharpImageColor.b - sharpImageColor.b*gl_FragColor.b), 1.0);
r = min((gl_FragColor.r*2.0 - gl_FragColor.r*gl_FragColor.r), 1.0);
g = min((gl_FragColor.g*2.0 - gl_FragColor.g*gl_FragColor.g), 1.0);
b = min((gl_FragColor.b*2.0 - gl_FragColor.b*gl_FragColor.b), 1.0);
r = min(status*gl_FragColor.r+1.05*(1.0-status)*r, 1.0);
g = min(status*gl_FragColor.g+1.15*(1.0-status)*g, 1.0);
b = min(status*gl_FragColor.b+1.25*(1.0-status)*b, 1.0);
gl_FragColor = vec4(r, g, b, 1.0);
//gl_FragColor = vec4(r, g, b, 1.0);
}
);
#else
NSString *const KLFGPUImageBeautyFragmentShaderString = SHADER_STRING
(
varying vec2 textureCoordinate;
varying vec2 textureCoordinate2;
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;
uniform float excludeCircleRadius;
uniform vec2 excludeCirclePoint;
uniform float excludeBlurSize;
uniform float aspectRatio;
void main()
{
// vec4 sharpImageColor = texture2D(inputImageTexture, textureCoordinate);
// vec4 blurredImageColor = texture2D(inputImageTexture2, textureCoordinate2);
//
// vec2 textureCoordinateToUse = vec2(textureCoordinate2.x, (textureCoordinate2.y * aspectRatio + 0.5 - 0.5 * aspectRatio));
// float distanceFromCenter = distance(excludeCirclePoint, textureCoordinateToUse);
//
// gl_FragColor = mix(sharpImageColor, blurredImageColor, smoothstep(excludeCircleRadius - excludeBlurSize, excludeCircleRadius, distanceFromCenter));
vec4 sharpImageColor = texture2D(inputImageTexture, textureCoordinate);
vec4 blurredImageColor = texture2D(inputImageTexture2, textureCoordinate2);
if(sharpImageColor.r > 0.372549/*0.372549*/) {
mediump float bpass;
bpass = min((sharpImageColor.r - blurredImageColor.r)*(sharpImageColor.r - blurredImageColor.r)*0.045455, 1.0);
//bpass = min((sharpImageColor.r - blurredImageColor.r)*(sharpImageColor.r - blurredImageColor.r)*0.038462, 1.0);
bpass = min((0.5+255.0*bpass), 1.0);
gl_FragColor = mix(sharpImageColor, blurredImageColor, 1.0-bpass);
} else {
gl_FragColor = vec4(sharpImageColor.r, sharpImageColor.g, sharpImageColor.b,1.0);
}
mediump float r;
mediump float g;
mediump float b;
r = min((gl_FragColor.r*2.0 - gl_FragColor.r*gl_FragColor.r), 1.0);
g = min((gl_FragColor.g*2.0 - gl_FragColor.g*gl_FragColor.g), 1.0);
b = min((gl_FragColor.b*2.0 - gl_FragColor.b*gl_FragColor.b), 1.0);
r = min(0.62*gl_FragColor.r+0.38*r, 1.0);
g = min(0.62*gl_FragColor.g+0.38*g, 1.0);
b = min(0.62*gl_FragColor.b+0.38*b, 1.0);
gl_FragColor = vec4(r, g, b, 1.0);
}
);
#endif
@implementation LFGPUImageBeautyFilter
@synthesize excludeCirclePoint = _excludeCirclePoint, excludeCircleRadius = _excludeCircleRadius, excludeBlurSize = _excludeBlurSize;
@synthesize blurRadiusInPixels = _blurRadiusInPixels;
@synthesize aspectRatio = _aspectRatio;
- (id)init;
{
if (!(self = [super init]))
{
return nil;
}
hasOverriddenAspectRatio = NO;
// First pass: apply a variable Gaussian blur
blurFilter = [[GPUImageGaussianBlurFilter alloc] init];
//blurFilter = [[GPUImageBilateralFilter alloc] init];
blurFilter.texelSpacingMultiplier = 0.55;
blurFilter.blurRadiusInPixels = 23.0;
[self addFilter:blurFilter];
// Second pass: combine the blurred image with the original sharp one
selectiveFocusFilter = [[GPUImageTwoInputFilter alloc] initWithFragmentShaderFromString:KLFGPUImageBeautyFragmentShaderString];
[self addFilter:selectiveFocusFilter];
// Texture location 0 needs to be the sharp image for both the blur and the second stage processing
[blurFilter addTarget:selectiveFocusFilter atTextureLocation:1];
// To prevent double updating of this filter, disable updates from the sharp image side
self.initialFilters = [NSArray arrayWithObjects:blurFilter, selectiveFocusFilter, nil];
self.terminalFilter = selectiveFocusFilter;
//self.blurRadiusInPixels = 5.0;
//960*540
self.blurRadiusInPixels = 13.0;
//1280*720
//self.blurRadiusInPixels = 11.0;
self.excludeCircleRadius = 13.0;
self.excludeCirclePoint = CGPointMake(0.5f, 0.5f);
self.excludeBlurSize = 13.0;
return self;
}
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
{
CGSize oldInputSize = inputTextureSize;
[super setInputSize:newSize atIndex:textureIndex];
inputTextureSize = newSize;
if ( (!CGSizeEqualToSize(oldInputSize, inputTextureSize)) && (!hasOverriddenAspectRatio) && (!CGSizeEqualToSize(newSize, CGSizeZero)) )
{
_aspectRatio = (inputTextureSize.width / inputTextureSize.height);
[selectiveFocusFilter setFloat:_aspectRatio forUniformName:@"aspectRatio"];
}
}
#pragma mark -
#pragma mark Accessors
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
{
//newValue = 25;
blurFilter.blurRadiusInPixels = newValue;
_excludeCircleRadius = newValue;
[selectiveFocusFilter setFloat:newValue forUniformName:@"excludeCircleRadius"];
_excludeBlurSize = newValue;
[selectiveFocusFilter setFloat:newValue forUniformName:@"excludeBlurSize"];
}
- (CGFloat)blurRadiusInPixels;
{
return blurFilter.blurRadiusInPixels;
}
- (void)setExcludeCirclePoint:(CGPoint)newValue;
{
_excludeCirclePoint = newValue;
[selectiveFocusFilter setPoint:newValue forUniformName:@"excludeCirclePoint"];
}
- (void)setExcludeCircleRadius:(CGFloat)newValue;
{
_excludeCircleRadius = newValue;
[selectiveFocusFilter setFloat:newValue forUniformName:@"excludeCircleRadius"];
}
- (void)setExcludeBlurSize:(CGFloat)newValue;
{
_excludeBlurSize = newValue;
[selectiveFocusFilter setFloat:newValue forUniformName:@"excludeBlurSize"];
}
- (void)setAspectRatio:(CGFloat)newValue;
{
hasOverriddenAspectRatio = YES;
_aspectRatio = newValue;
[selectiveFocusFilter setFloat:_aspectRatio forUniformName:@"aspectRatio"];
}
@end
//#import "LFGPUImageBeautyFilter.h"
//
//// Internal CombinationFilter(It should not be used outside)
//@interface GPUImageCombinationFilter : GPUImageThreeInputFilter
//{
// GLint smoothDegreeUniform;
//}
//
//@property (nonatomic, assign) CGFloat intensity;
//
//@end
//
//NSString *const kGPUImageBeautifyFragmentShaderString = SHADER_STRING
//(
// varying highp vec2 textureCoordinate;
// varying highp vec2 textureCoordinate2;
// varying highp vec2 textureCoordinate3;
//
// uniform sampler2D inputImageTexture;
// uniform sampler2D inputImageTexture2;
// uniform sampler2D inputImageTexture3;
// uniform mediump float smoothDegree;
//
// void main()
// {
//// highp vec4 bilateral = texture2D(inputImageTexture, textureCoordinate);
//// highp vec4 canny = texture2D(inputImageTexture2, textureCoordinate2);
//// highp vec4 origin = texture2D(inputImageTexture3,textureCoordinate3);
//// highp vec4 smooth;
//// lowp float r = origin.r;
//// lowp float g = origin.g;
//// lowp float b = origin.b;
//// if (canny.r < 0.2 && r > 0.3725 && g > 0.1568 && b > 0.0784 && r > b && (max(max(r, g), b) - min(min(r, g), b)) > 0.0588 && abs(r-g) > 0.0588) {
//// smooth = (1.0 - smoothDegree) * (origin - bilateral) + bilateral;
//// }
//// else {
//// smooth = origin;
//// }
//// smooth.r = log(1.0 + 0.2 * smooth.r)/log(1.2);
//// smooth.g = log(1.0 + 0.2 * smooth.g)/log(1.2);
//// smooth.b = log(1.0 + 0.2 * smooth.b)/log(1.2);
//// gl_FragColor = smooth;
//
//
// highp vec4 blurredImageColor = texture2D(inputImageTexture, textureCoordinate);
// highp vec4 canny = texture2D(inputImageTexture2, textureCoordinate2);
// highp vec4 sharpImageColor = texture2D(inputImageTexture3,textureCoordinate3);
//
// //gl_FragColor = canny;
//
//
//
//
// if((sharpImageColor.r > 0.372549 && sharpImageColor.g > 0.156863 && sharpImageColor.b > 0.078431 &&
// sharpImageColor.r - sharpImageColor.g > 0.058823 && sharpImageColor.r - sharpImageColor.b > 0.058823) ||
// (sharpImageColor.r > 0.784314 && sharpImageColor.g > 0.823530 && sharpImageColor.b > 0.666667 &&
// abs(sharpImageColor.r - sharpImageColor.b) <= 0.058823 && sharpImageColor.r > sharpImageColor.b && sharpImageColor.g > sharpImageColor.b)) {
// mediump float rpass;
// mediump float gpass;
// mediump float bpass;
// mediump float hpass;
// //hpass = 0.5 / excludeBlurSize;
// hpass = 1.0;
// //1280*720
// //bpass = min((sharpImageColor.r - blurredImageColor.r)*(sharpImageColor.r - blurredImageColor.r)*0.045455, 1.0);
// //960*540
// rpass = min(((sharpImageColor.r - blurredImageColor.r)*hpass), 1.0);
// gpass = min(((sharpImageColor.g - blurredImageColor.g)*hpass), 1.0);
// bpass = min(((sharpImageColor.b - blurredImageColor.b)*hpass), 1.0);
// bpass = min((0.0+1.0*(1.0*bpass)), 1.0);
// gpass = min((0.0+1.0*(1.0*gpass)), 1.0);
// rpass = min((0.0+1.0*(1.0*rpass)), 1.0);
// mediump float dis = distance(sharpImageColor, blurredImageColor);
// bpass = min((0.35+1.0*(1.0*dis)), 1.0);
// if(canny.r > 0.2) {
// bpass = min((0.45+1.0*(1.0*dis)), 1.0);
// }
// gl_FragColor = mix(sharpImageColor, blurredImageColor, 1.0-bpass);
//
//
// } else {
// mediump float rpass;
// mediump float gpass;
// mediump float bpass;
// mediump float hpass;
// //hpass = 0.5 / excludeBlurSize;
// hpass = 1.0;
// //1280*720
// //bpass = min((sharpImageColor.r - blurredImageColor.r)*(sharpImageColor.r - blurredImageColor.r)*0.045455, 1.0);
// //960*540
// rpass = min(((sharpImageColor.r - blurredImageColor.r)*hpass), 1.0);
// gpass = min(((sharpImageColor.g - blurredImageColor.g)*hpass), 1.0);
// bpass = min(((sharpImageColor.b - blurredImageColor.b)*hpass), 1.0);
// bpass = min((0.5+1.0*(1.0*bpass)), 1.0);
// gpass = min((0.5+1.0*(1.0*gpass)), 1.0);
// rpass = min((0.5+1.0*(1.0*rpass)), 1.0);
//
// // gpass = max(rpass, gpass);
// // bpass = max(gpass, bpass);
// // bpass = min((0.5+1.0*(1.0*bpass)), 1.0);
// //gl_FragColor = vec4(1, 1, 1, 1.0);
// mediump float dis = distance(sharpImageColor, blurredImageColor);
// bpass = min((0.45+1.0*(1.0*dis)), 1.0);
// if(canny.r > 0.2) {
// bpass = min((0.45+1.0*(1.0*dis)), 1.0);
// }
// gl_FragColor = mix(sharpImageColor, blurredImageColor, 1.0-bpass);
////
// }
//
//
// //gl_FragColor = canny;
//
//
// mediump float r;
// mediump float g;
// mediump float b;
// mediump float status = 0.8;
// r = min((gl_FragColor.r*2.0 - gl_FragColor.r*gl_FragColor.r), 1.0);
// g = min((gl_FragColor.g*2.0 - gl_FragColor.g*gl_FragColor.g), 1.0);
// b = min((gl_FragColor.b*2.0 - gl_FragColor.b*gl_FragColor.b), 1.0);
// r = min(status*gl_FragColor.r+(1.0-status)*r, 1.0);
// g = min(status*gl_FragColor.g+1.2*(1.0-status)*g, 1.0);
// b = min(status*gl_FragColor.b+1.3*(1.0-status)*b, 1.0);
// gl_FragColor = vec4(r, g, b, 1.0);
//
// //gl_FragColor = blurredImageColor;
//
//
//
//
// }
// );
//
//@implementation GPUImageCombinationFilter
//
//- (id)init {
// if (self = [super initWithFragmentShaderFromString:kGPUImageBeautifyFragmentShaderString]) {
// smoothDegreeUniform = [filterProgram uniformIndex:@"smoothDegree"];
// }
// self.intensity = 0.5;
// return self;
//}
//
//- (void)setIntensity:(CGFloat)intensity {
// _intensity = intensity;
// [self setFloat:intensity forUniform:smoothDegreeUniform program:filterProgram];
//}
//
//@end
//
//@implementation LFGPUImageBeautyFilter
//
//- (id)init;
//{
// if (!(self = [super init]))
// {
// return nil;
// }
//
// // First pass: face smoothing filter
//// bilateralFilter = [[GPUImageBilateralFilter alloc] init];
//// bilateralFilter.distanceNormalizationFactor = 1.0;
//// bilateralFilter.texelSpacingMultiplier = 0.75;
//// bilateralFilter.blurRadiusInPixels = 11.0;
//// [self addFilter:bilateralFilter];
//
// bilateralFilter = [[GPUImageGaussianBlurFilter alloc] init];
// bilateralFilter.texelSpacingMultiplier = 0.55;
// bilateralFilter.blurRadiusInPixels = 13.0;
// [self addFilter:bilateralFilter];
//
// // Second pass: edge detection
// cannyEdgeFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
// [self addFilter:cannyEdgeFilter];
//
// // Third pass: combination bilateral, edge detection and origin
// combinationFilter = [[GPUImageCombinationFilter alloc] init];
// [self addFilter:combinationFilter];
//
// // Adjust HSB
// hsbFilter = [[GPUImageHSBFilter alloc] init];
// [hsbFilter adjustBrightness:1.0];
// [hsbFilter adjustSaturation:1.0];
//
// [bilateralFilter addTarget:combinationFilter];
// [cannyEdgeFilter addTarget:combinationFilter];
//
// [combinationFilter addTarget:hsbFilter];
//
// self.initialFilters = [NSArray arrayWithObjects:bilateralFilter,cannyEdgeFilter,combinationFilter,nil];
// self.terminalFilter = hsbFilter;
//
// return self;
//}
//
//#pragma mark -
//#pragma mark GPUImageInput protocol
//
//- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
//{
// for (GPUImageOutput<GPUImageInput> *currentFilter in self.initialFilters)
// {
// if (currentFilter != self.inputFilterToIgnoreForUpdates)
// {
// if (currentFilter == combinationFilter) {
// textureIndex = 2;
// }
// [currentFilter newFrameReadyAtTime:frameTime atIndex:textureIndex];
// }
// }
//}
//
//- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
//{
// for (GPUImageOutput<GPUImageInput> *currentFilter in self.initialFilters)
// {
// if (currentFilter == combinationFilter) {
// textureIndex = 2;
// }
// [currentFilter setInputFramebuffer:newInputFramebuffer atIndex:textureIndex];
// }
//}
//
//@end
-46
View File
@@ -1,46 +0,0 @@
#import "LFGPUImageEmptyFilter.h"
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const kLFGPUImageEmptyFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4((textureColor.rgb), textureColor.w);
}
);
#else
NSString *const kGPUImageInvertFragmentShaderString = SHADER_STRING
(
varying vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4((textureColor.rgb), textureColor.w);
}
);
#endif
@implementation LFGPUImageEmptyFilter
- (id)init;
{
if (!(self = [super initWithFragmentShaderFromString:kLFGPUImageEmptyFragmentShaderString]))
{
return nil;
}
return self;
}
@end
-37
View File
@@ -1,37 +0,0 @@
//
// LFLiveStreamInfo.h
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 倾慕. All rights reserved.
// 真正的上传地址 token等
#import <Foundation/Foundation.h>
/// 流状态
typedef NS_ENUM(NSUInteger, LFLiveState){
/// 准备
LFLiveReady = 0,
/// 连接中
LFLivePending = 1,
/// 已连接
LFLiveStart = 2,
/// 已断开
LFLiveStop = 3,
/// 连接出错
LFLiveError = 4
};
typedef NS_ENUM(NSUInteger,LFLiveSocketErrorCode) {
LFLiveSocketError_PreView = 201,///< 预览失败
LFLiveSocketError_GetStreamInfo = 202,///< 获取流媒体信息失败
LFLiveSocketError_ConnectSocket = 203,///< 连接socket失败
LFLiveSocketError_Verification = 204,///< 验证服务器失败
LFLiveSocketError_ReConnectTimeOut = 205///< 重新连接服务器超时
};
@interface LFLiveStreamInfo : NSObject
@property (nonatomic, copy) NSString *url; ///< 上传地址 (RTMP用就好了)
@end
-342
View File
@@ -1,342 +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"
#define DATA_ITEMS_MAX_COUNT 100
#define RTMP_DATA_RESERVE_SIZE 400
#define RTMP_CONNECTION_TIMEOUT 1500
#define RTMP_RECEIVE_TIMEOUT 2
#define RTMP_HEAD_SIZE (sizeof(RTMPPacket)+RTMP_MAX_HEADER_SIZE)
@interface LFStreamRtmpSocket ()<LFStreamingBufferDelegate>
{
RTMP* _rtmp;
}
@property (nonatomic, weak) id<LFStreamSocketDelegate> delegate;
@property (nonatomic, strong) LFLiveStreamInfo *stream;
@property (nonatomic, strong) LFStreamingBuffer *buffer;
@property (nonatomic, strong) dispatch_queue_t socketQueue;
@property (nonatomic, assign) NSInteger retryTimes4netWorkBreaken;
@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
- (instancetype)initWithStream:(LFLiveStreamInfo*)stream{
if(!stream) @throw [NSException exceptionWithName:@"LFStreamRtmpSocket init error" reason:@"stream is nil" userInfo:nil];
if(self = [super init]){
_stream = stream;
}
return self;
}
- (void) start{
dispatch_async(self.socketQueue, ^{
if(!_stream) return;
if(_isConnecting) return;
if(_rtmp != NULL) return;
[self RTMP264_Connect:(char*)[_stream.url cStringUsingEncoding:NSASCIIStringEncoding]];
});
}
- (void) stop{
dispatch_async(self.socketQueue, ^{
if(_rtmp != NULL){
RTMP_Close(_rtmp);
RTMP_Free(_rtmp);
_rtmp = NULL;
}
[self clean];
});
}
- (void) sendFrame:(LFFrame*)frame{
__weak typeof(self) _self = self;
dispatch_async(self.socketQueue, ^{
__strong typeof(_self) self = _self;
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];
}
}
}
}
- (void)clean{
_isConnecting = NO;
_isReconnecting = NO;
_isSending = NO;
_isConnected = NO;
_sendAudioHead = NO;
_sendVideoHead = NO;
[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){
RTMP_Close(_rtmp);
RTMP_Free(_rtmp);
}
_rtmp = RTMP_Alloc();
RTMP_Init(_rtmp);
//设置URL
if (RTMP_SetupURL(_rtmp, push_url) < 0){
//log(LOG_ERR, "RTMP_SetupURL() failed!");
goto Failed;
}
//设置可写,即发布流,这个函数必须在连接前使用,否则无效
RTMP_EnableWrite(_rtmp);
_rtmp->Link.timeout = RTMP_RECEIVE_TIMEOUT;
//连接服务器
if (RTMP_Connect(_rtmp, NULL) < 0){
goto Failed;
}
//连接流
if (RTMP_ConnectStream(_rtmp, 0) < 0) {
goto Failed;
}
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
[self.delegate socketStatus:self status:LFLiveStart];
}
_isConnected = YES;
_isConnecting = NO;
_isReconnecting = NO;
_isSending = NO;
_retryTimes4netWorkBreaken = 0;
return 0;
Failed:
RTMP_Close(_rtmp);
RTMP_Free(_rtmp);
[self clean];
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
[self.delegate socketStatus:self status:LFLiveError];
}
return -1;
}
#pragma mark -- Rtmp Send
- (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;
RTMPPacket rtmp_pack;
RTMPPacket_Reset(&rtmp_pack);
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];
RTMPPacket_Free(&rtmp_pack);
return nRet;
}
- (NSInteger)RtmpPacketSend:(RTMPPacket*)packet{
if (RTMP_IsConnected(_rtmp)){
int success = RTMP_SendPacket(_rtmp,packet,0);
if(success){
self.isSending = NO;
[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);
}
#pragma mark -- Getter Setter
- (dispatch_queue_t)socketQueue{
if(!_socketQueue){
_socketQueue = dispatch_queue_create("com.youku.LaiFeng.live.socketQueue", NULL);
}
return _socketQueue;
}
- (LFStreamingBuffer*)buffer{
if(!_buffer){
_buffer = [[LFStreamingBuffer alloc] init];
_buffer.delegate = self;
}
return _buffer;
}
@end
-37
View File
@@ -1,37 +0,0 @@
//
// LFStreamSocket.h
// LFLiveKit
//
// Created by admin on 16/5/3.
// Copyright © 2016年 倾慕. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "LFLiveStreamInfo.h"
#import "LFStreamingBuffer.h"
#import "LFLiveDebug.h"
@protocol LFStreamSocket ;
@protocol LFStreamSocketDelegate <NSObject>
/** callback buffer current status (回调当前缓冲区情况,可实现相关切换帧率 码率等策略)*/
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status;
/** callback socket current status (回调当前网络情况) */
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status;
/** callback socket errorcode */
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode;
@optional
/** callback debugInfo */
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug*)debugInfo;
@end
@protocol LFStreamSocket <NSObject>
- (void) start;
- (void) stop;
- (void) sendFrame:(nullable LFFrame*)frame;
- (void) setDelegate:(nullable id<LFStreamSocketDelegate>)delegate;
@optional
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream;
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream videoSize:(CGSize)videoSize;
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream videoSize:(CGSize)videoSize reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount;
@end
@@ -0,0 +1,460 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
84D8B42B1D75778B00752B56 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B42A1D75778B00752B56 /* main.m */; };
84D8B42E1D75778B00752B56 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B42D1D75778B00752B56 /* AppDelegate.m */; };
84D8B4311D75778B00752B56 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B4301D75778B00752B56 /* ViewController.m */; };
84D8B4341D75778B00752B56 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4321D75778B00752B56 /* Main.storyboard */; };
84D8B4361D75778B00752B56 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4351D75778B00752B56 /* Assets.xcassets */; };
84D8B4391D75778B00752B56 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4371D75778B00752B56 /* LaunchScreen.storyboard */; };
84D8B44B1D75781200752B56 /* LFLivePreview.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B44A1D75781200752B56 /* LFLivePreview.m */; };
84D8B45B1D75782700752B56 /* UIControl+YYAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B44E1D75782700752B56 /* UIControl+YYAdd.m */; };
84D8B45C1D75782700752B56 /* UIView+YYAdd.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D8B4501D75782700752B56 /* UIView+YYAdd.m */; };
84D8B45D1D75782700752B56 /* camra_beauty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4521D75782700752B56 /* camra_beauty@2x.png */; };
84D8B45E1D75782700752B56 /* camra_beauty@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4531D75782700752B56 /* camra_beauty@3x.png */; };
84D8B45F1D75782700752B56 /* camra_beauty_close@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4541D75782700752B56 /* camra_beauty_close@2x.png */; };
84D8B4601D75782700752B56 /* camra_beauty_close@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4551D75782700752B56 /* camra_beauty_close@3x.png */; };
84D8B4611D75782700752B56 /* camra_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4561D75782700752B56 /* camra_preview@2x.png */; };
84D8B4621D75782700752B56 /* camra_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4571D75782700752B56 /* camra_preview@3x.png */; };
84D8B4631D75782700752B56 /* close_preview@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4581D75782700752B56 /* close_preview@2x.png */; };
84D8B4641D75782700752B56 /* close_preview@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B4591D75782700752B56 /* close_preview@3x.png */; };
84D8B4651D75782700752B56 /* ios-29x29.png in Resources */ = {isa = PBXBuildFile; fileRef = 84D8B45A1D75782700752B56 /* ios-29x29.png */; };
84D8B4681D75783F00752B56 /* GPUImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4661D75783F00752B56 /* GPUImage.framework */; };
84D8B4691D75783F00752B56 /* pili_librtmp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4671D75783F00752B56 /* pili_librtmp.framework */; };
84D8B4791D757A5500752B56 /* LFLiveKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4451D75779200752B56 /* LFLiveKit.framework */; };
84D8B4CF1D757F0700752B56 /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D8B4CE1D757F0700752B56 /* libstdc++.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
84D8B4441D75779200752B56 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 84D8B3901D7574D600752B56;
remoteInfo = LFLiveKit;
};
84D8B4761D757A3000752B56 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 84D8B38F1D7574D600752B56;
remoteInfo = LFLiveKit;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
84D8B4261D75778B00752B56 /* LFLiveKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LFLiveKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
84D8B42A1D75778B00752B56 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
84D8B42C1D75778B00752B56 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
84D8B42D1D75778B00752B56 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
84D8B42F1D75778B00752B56 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
84D8B4301D75778B00752B56 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
84D8B4331D75778B00752B56 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
84D8B4351D75778B00752B56 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
84D8B4381D75778B00752B56 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
84D8B43A1D75778B00752B56 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LFLiveKit.xcodeproj; path = ../LFLiveKit/LFLiveKit.xcodeproj; sourceTree = "<group>"; };
84D8B4491D75781200752B56 /* LFLivePreview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFLivePreview.h; sourceTree = "<group>"; };
84D8B44A1D75781200752B56 /* LFLivePreview.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFLivePreview.m; sourceTree = "<group>"; };
84D8B44D1D75782700752B56 /* UIControl+YYAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+YYAdd.h"; sourceTree = "<group>"; };
84D8B44E1D75782700752B56 /* UIControl+YYAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+YYAdd.m"; sourceTree = "<group>"; };
84D8B44F1D75782700752B56 /* UIView+YYAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+YYAdd.h"; sourceTree = "<group>"; };
84D8B4501D75782700752B56 /* UIView+YYAdd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+YYAdd.m"; sourceTree = "<group>"; };
84D8B4521D75782700752B56 /* camra_beauty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@2x.png"; sourceTree = "<group>"; };
84D8B4531D75782700752B56 /* camra_beauty@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty@3x.png"; sourceTree = "<group>"; };
84D8B4541D75782700752B56 /* camra_beauty_close@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@2x.png"; sourceTree = "<group>"; };
84D8B4551D75782700752B56 /* camra_beauty_close@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_beauty_close@3x.png"; sourceTree = "<group>"; };
84D8B4561D75782700752B56 /* camra_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@2x.png"; sourceTree = "<group>"; };
84D8B4571D75782700752B56 /* camra_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "camra_preview@3x.png"; sourceTree = "<group>"; };
84D8B4581D75782700752B56 /* close_preview@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@2x.png"; sourceTree = "<group>"; };
84D8B4591D75782700752B56 /* close_preview@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "close_preview@3x.png"; sourceTree = "<group>"; };
84D8B45A1D75782700752B56 /* ios-29x29.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ios-29x29.png"; sourceTree = "<group>"; };
84D8B4661D75783F00752B56 /* GPUImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GPUImage.framework; path = ../Vendor/GPUImage.framework; sourceTree = "<group>"; };
84D8B4671D75783F00752B56 /* pili_librtmp.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = pili_librtmp.framework; path = ../Vendor/pili_librtmp.framework; sourceTree = "<group>"; };
84D8B4CE1D757F0700752B56 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
84D8B4231D75778B00752B56 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
84D8B4CF1D757F0700752B56 /* libstdc++.tbd in Frameworks */,
84D8B4791D757A5500752B56 /* LFLiveKit.framework in Frameworks */,
84D8B4681D75783F00752B56 /* GPUImage.framework in Frameworks */,
84D8B4691D75783F00752B56 /* pili_librtmp.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
84D8B41D1D75778B00752B56 = {
isa = PBXGroup;
children = (
84D8B4CE1D757F0700752B56 /* libstdc++.tbd */,
84D8B4661D75783F00752B56 /* GPUImage.framework */,
84D8B4671D75783F00752B56 /* pili_librtmp.framework */,
84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */,
84D8B4281D75778B00752B56 /* LFLiveKitDemo */,
84D8B4271D75778B00752B56 /* Products */,
);
sourceTree = "<group>";
};
84D8B4271D75778B00752B56 /* Products */ = {
isa = PBXGroup;
children = (
84D8B4261D75778B00752B56 /* LFLiveKitDemo.app */,
);
name = Products;
sourceTree = "<group>";
};
84D8B4281D75778B00752B56 /* LFLiveKitDemo */ = {
isa = PBXGroup;
children = (
84D8B42C1D75778B00752B56 /* AppDelegate.h */,
84D8B42D1D75778B00752B56 /* AppDelegate.m */,
84D8B42F1D75778B00752B56 /* ViewController.h */,
84D8B4301D75778B00752B56 /* ViewController.m */,
84D8B4491D75781200752B56 /* LFLivePreview.h */,
84D8B44A1D75781200752B56 /* LFLivePreview.m */,
84D8B44C1D75782700752B56 /* category */,
84D8B4511D75782700752B56 /* images */,
84D8B4321D75778B00752B56 /* Main.storyboard */,
84D8B4351D75778B00752B56 /* Assets.xcassets */,
84D8B4371D75778B00752B56 /* LaunchScreen.storyboard */,
84D8B43A1D75778B00752B56 /* Info.plist */,
84D8B4291D75778B00752B56 /* Supporting Files */,
);
path = LFLiveKitDemo;
sourceTree = "<group>";
};
84D8B4291D75778B00752B56 /* Supporting Files */ = {
isa = PBXGroup;
children = (
84D8B42A1D75778B00752B56 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
84D8B4411D75779200752B56 /* Products */ = {
isa = PBXGroup;
children = (
84D8B4451D75779200752B56 /* LFLiveKit.framework */,
);
name = Products;
sourceTree = "<group>";
};
84D8B44C1D75782700752B56 /* category */ = {
isa = PBXGroup;
children = (
84D8B44D1D75782700752B56 /* UIControl+YYAdd.h */,
84D8B44E1D75782700752B56 /* UIControl+YYAdd.m */,
84D8B44F1D75782700752B56 /* UIView+YYAdd.h */,
84D8B4501D75782700752B56 /* UIView+YYAdd.m */,
);
path = category;
sourceTree = "<group>";
};
84D8B4511D75782700752B56 /* images */ = {
isa = PBXGroup;
children = (
84D8B4521D75782700752B56 /* camra_beauty@2x.png */,
84D8B4531D75782700752B56 /* camra_beauty@3x.png */,
84D8B4541D75782700752B56 /* camra_beauty_close@2x.png */,
84D8B4551D75782700752B56 /* camra_beauty_close@3x.png */,
84D8B4561D75782700752B56 /* camra_preview@2x.png */,
84D8B4571D75782700752B56 /* camra_preview@3x.png */,
84D8B4581D75782700752B56 /* close_preview@2x.png */,
84D8B4591D75782700752B56 /* close_preview@3x.png */,
84D8B45A1D75782700752B56 /* ios-29x29.png */,
);
path = images;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
84D8B4251D75778B00752B56 /* LFLiveKitDemo */ = {
isa = PBXNativeTarget;
buildConfigurationList = 84D8B43D1D75778B00752B56 /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */;
buildPhases = (
84D8B4221D75778B00752B56 /* Sources */,
84D8B4231D75778B00752B56 /* Frameworks */,
84D8B4241D75778B00752B56 /* Resources */,
);
buildRules = (
);
dependencies = (
84D8B4771D757A3000752B56 /* PBXTargetDependency */,
);
name = LFLiveKitDemo;
productName = LFLiveKitDemo;
productReference = 84D8B4261D75778B00752B56 /* LFLiveKitDemo.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
84D8B41E1D75778B00752B56 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = admin;
TargetAttributes = {
84D8B4251D75778B00752B56 = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 84D8B4211D75778B00752B56 /* Build configuration list for PBXProject "LFLiveKitDemo" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 84D8B41D1D75778B00752B56;
productRefGroup = 84D8B4271D75778B00752B56 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 84D8B4411D75779200752B56 /* Products */;
ProjectRef = 84D8B4401D75779200752B56 /* LFLiveKit.xcodeproj */;
},
);
projectRoot = "";
targets = (
84D8B4251D75778B00752B56 /* LFLiveKitDemo */,
);
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
84D8B4451D75779200752B56 /* LFLiveKit.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = LFLiveKit.framework;
remoteRef = 84D8B4441D75779200752B56 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
84D8B4241D75778B00752B56 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
84D8B45F1D75782700752B56 /* camra_beauty_close@2x.png in Resources */,
84D8B4391D75778B00752B56 /* LaunchScreen.storyboard in Resources */,
84D8B45D1D75782700752B56 /* camra_beauty@2x.png in Resources */,
84D8B4361D75778B00752B56 /* Assets.xcassets in Resources */,
84D8B4621D75782700752B56 /* camra_preview@3x.png in Resources */,
84D8B4601D75782700752B56 /* camra_beauty_close@3x.png in Resources */,
84D8B4651D75782700752B56 /* ios-29x29.png in Resources */,
84D8B4641D75782700752B56 /* close_preview@3x.png in Resources */,
84D8B4631D75782700752B56 /* close_preview@2x.png in Resources */,
84D8B4341D75778B00752B56 /* Main.storyboard in Resources */,
84D8B4611D75782700752B56 /* camra_preview@2x.png in Resources */,
84D8B45E1D75782700752B56 /* camra_beauty@3x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
84D8B4221D75778B00752B56 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
84D8B45B1D75782700752B56 /* UIControl+YYAdd.m in Sources */,
84D8B4311D75778B00752B56 /* ViewController.m in Sources */,
84D8B45C1D75782700752B56 /* UIView+YYAdd.m in Sources */,
84D8B42E1D75778B00752B56 /* AppDelegate.m in Sources */,
84D8B44B1D75781200752B56 /* LFLivePreview.m in Sources */,
84D8B42B1D75778B00752B56 /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
84D8B4771D757A3000752B56 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = LFLiveKit;
targetProxy = 84D8B4761D757A3000752B56 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
84D8B4321D75778B00752B56 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
84D8B4331D75778B00752B56 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
84D8B4371D75778B00752B56 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
84D8B4381D75778B00752B56 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
84D8B43B1D75778B00752B56 /* 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 = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
84D8B43C1D75778B00752B56 /* 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 = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
84D8B43E1D75778B00752B56 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../Vendor\"/**",
);
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = LFLiveKitDemo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-all_load";
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKitDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
84D8B43F1D75778B00752B56 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../Vendor\"/**",
);
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = LFLiveKitDemo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-all_load";
PRODUCT_BUNDLE_IDENTIFIER = com.youku.LFLiveKitDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
84D8B4211D75778B00752B56 /* Build configuration list for PBXProject "LFLiveKitDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
84D8B43B1D75778B00752B56 /* Debug */,
84D8B43C1D75778B00752B56 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
84D8B43D1D75778B00752B56 /* Build configuration list for PBXNativeTarget "LFLiveKitDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
84D8B43E1D75778B00752B56 /* Debug */,
84D8B43F1D75778B00752B56 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 84D8B41E1D75778B00752B56 /* Project object */;
}
@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:LFLiveKit.xcodeproj">
location = "self:LFLiveKitDemo.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 = "84D8B4251D75778B00752B56"
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 = "84D8B4251D75778B00752B56"
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 = "84D8B4251D75778B00752B56"
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 = "84D8B4251D75778B00752B56"
BuildableName = "LFLiveKitDemo.app"
BlueprintName = "LFLiveKitDemo"
ReferencedContainer = "container:LFLiveKitDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -4,7 +4,7 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>LFLiveKit.xcscheme_^#shared#^_</key>
<key>LFLiveKitDemo.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
@@ -12,12 +12,7 @@
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>84001F891D0015D10026C63F</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>84001F931D0015D10026C63F</key>
<key>84D8B4251D75778B00752B56</key>
<dict>
<key>primary</key>
<true/>
+17
View File
@@ -0,0 +1,17 @@
//
// AppDelegate.h
// LFLiveKitDemo
//
// Created by admin on 16/8/30.
// Copyright © 2016年 admin. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
+45
View File
@@ -0,0 +1,45 @@
//
// AppDelegate.m
// LFLiveKitDemo
//
// Created by admin on 16/8/30.
// Copyright © 2016年 admin. All rights reserved.
//
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// 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.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// 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.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// 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.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// 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.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end
@@ -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="" 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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -13,12 +13,28 @@
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<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>
+13
View File
@@ -0,0 +1,13 @@
//
// LFLivePreview.h
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 live Interactive. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface LFLivePreview : UIView
@end
+365
View File
@@ -0,0 +1,365 @@
//
// LFLivePreview.m
// LFLiveKit
//
// Created by 倾慕 on 16/5/2.
// Copyright © 2016年 live Interactive. All rights reserved.
//
#import "LFLivePreview.h"
#import "UIControl+YYAdd.h"
#import "UIView+YYAdd.h"
#import <LFLiveKit/LFLiveKit.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;
@property (nonatomic, strong) UIButton *cameraButton;
@property (nonatomic, strong) UIButton *closeButton;
@property (nonatomic, strong) UIButton *startLiveButton;
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, strong) LFLiveDebug *debugInfo;
@property (nonatomic, strong) LFLiveSession *session;
@property (nonatomic, strong) UILabel *stateLabel;
@end
@implementation LFLivePreview
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
[self requestAccessForVideo];
[self requestAccessForAudio];
[self addSubview:self.containerView];
[self.containerView addSubview:self.stateLabel];
[self.containerView addSubview:self.closeButton];
[self.containerView addSubview:self.cameraButton];
[self.containerView addSubview:self.beautyButton];
[self.containerView addSubview:self.startLiveButton];
}
return self;
}
#pragma mark -- Public Method
- (void)requestAccessForVideo {
__weak typeof(self) _self = self;
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (status) {
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;
}
}
- (void)requestAccessForAudio {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
switch (status) {
case AVAuthorizationStatusNotDetermined: {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
}];
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 {
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;
}
}
/** live debug info callback */
- (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 {
NSLog(@"errorCode: %ld", errorCode);
}
#pragma mark -- Getter Setter
- (LFLiveSession *)session {
if (!_session) {
/** 发现大家有不会用横屏的请注意啦,横屏需要在ViewController supportedInterfaceOrientations修改方向 默认竖屏 ****/
/** 发现大家有不会用横屏的请注意啦,横屏需要在ViewController supportedInterfaceOrientations修改方向 默认竖屏 ****/
/** 发现大家有不会用横屏的请注意啦,横屏需要在ViewController supportedInterfaceOrientations修改方向 默认竖屏 ****/
/***  默认分辨率368 640 音频:44.1 iphone6以上48 双声道 方向竖屏 ***/
LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];
videoConfiguration.videoSize = CGSizeMake(360, 640);
videoConfiguration.videoBitRate = 800*1024;
videoConfiguration.videoMaxBitRate = 1000*1024;
videoConfiguration.videoMinBitRate = 500*1024;
videoConfiguration.videoFrameRate = 24;
videoConfiguration.videoMaxKeyframeInterval = 48;
videoConfiguration.landscape = NO;
videoConfiguration.sessionPreset = LFCaptureSessionPreset720x1280;
_session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:videoConfiguration captureType:LFLiveCaptureDefaultMask];
/**   自己定制单声道 */
/*
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]];
*/
/**   自己定制高质量音频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];
*/
/**   自己定制高质量音频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.landscape = NO;
videoConfiguration.sessionPreset = LFCaptureSessionPreset360x640;
_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];
*/
_session.delegate = self;
_session.showDebugInfo = NO;
_session.preView = self;
UIImageView *imageView = [[UIImageView alloc] init];
imageView.alpha = 0.8;
imageView.frame = CGRectMake(100, 100, 29, 29);
imageView.image = [UIImage imageNamed:@"ios-29x29"];
_session.warterMarkView = imageView;
}
return _session;
}
- (UIView *)containerView {
if (!_containerView) {
_containerView = [UIView new];
_containerView.frame = self.bounds;
_containerView.backgroundColor = [UIColor clearColor];
_containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
return _containerView;
}
- (UILabel *)stateLabel {
if (!_stateLabel) {
_stateLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 80, 40)];
_stateLabel.text = @"未连接";
_stateLabel.textColor = [UIColor whiteColor];
_stateLabel.font = [UIFont boldSystemFontOfSize:14.f];
}
return _stateLabel;
}
- (UIButton *)closeButton {
if (!_closeButton) {
_closeButton = [UIButton new];
_closeButton.size = CGSizeMake(44, 44);
_closeButton.left = self.width - 10 - _closeButton.width;
_closeButton.top = 20;
[_closeButton setImage:[UIImage imageNamed:@"close_preview"] forState:UIControlStateNormal];
_closeButton.exclusiveTouch = YES;
[_closeButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
}];
}
return _closeButton;
}
- (UIButton *)cameraButton {
if (!_cameraButton) {
_cameraButton = [UIButton new];
_cameraButton.size = CGSizeMake(44, 44);
_cameraButton.origin = CGPointMake(_closeButton.left - 10 - _cameraButton.width, 20);
[_cameraButton setImage:[UIImage imageNamed:@"camra_preview"] forState:UIControlStateNormal];
_cameraButton.exclusiveTouch = YES;
__weak typeof(self) _self = self;
[_cameraButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
AVCaptureDevicePosition devicePositon = _self.session.captureDevicePosition;
_self.session.captureDevicePosition = (devicePositon == AVCaptureDevicePositionBack) ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;
}];
}
return _cameraButton;
}
- (UIButton *)beautyButton {
if (!_beautyButton) {
_beautyButton = [UIButton new];
_beautyButton.size = CGSizeMake(44, 44);
_beautyButton.origin = CGPointMake(_cameraButton.left - 10 - _beautyButton.width, 20);
[_beautyButton setImage:[UIImage imageNamed:@"camra_beauty"] forState:UIControlStateNormal];
[_beautyButton setImage:[UIImage imageNamed:@"camra_beauty_close"] forState:UIControlStateSelected];
_beautyButton.exclusiveTouch = YES;
__weak typeof(self) _self = self;
[_beautyButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
_self.session.beautyFace = !_self.session.beautyFace;
_self.beautyButton.selected = !_self.session.beautyFace;
}];
}
return _beautyButton;
}
- (UIButton *)startLiveButton {
if (!_startLiveButton) {
_startLiveButton = [UIButton new];
_startLiveButton.size = CGSizeMake(self.width - 60, 44);
_startLiveButton.left = 30;
_startLiveButton.bottom = self.height - 50;
_startLiveButton.layer.cornerRadius = _startLiveButton.height/2;
[_startLiveButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_startLiveButton.titleLabel setFont:[UIFont systemFontOfSize:16]];
[_startLiveButton setTitle:@"开始直播" forState:UIControlStateNormal];
[_startLiveButton setBackgroundColor:[UIColor colorWithRed:50 green:32 blue:245 alpha:1]];
_startLiveButton.exclusiveTouch = YES;
__weak typeof(self) _self = self;
[_startLiveButton addBlockForControlEvents:UIControlEventTouchUpInside block:^(id sender) {
_self.startLiveButton.selected = !_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/stream153";
[_self.session startLive:stream];
} else {
[_self.startLiveButton setTitle:@"开始直播" forState:UIControlStateNormal];
[_self.session stopLive];
}
}];
}
return _startLiveButton;
}
@end
@@ -0,0 +1,15 @@
//
// ViewController.h
// LFLiveKitDemo
//
// Created by admin on 16/8/30.
// Copyright © 2016年 admin. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
@@ -0,0 +1,32 @@
//
// ViewController.m
// LFLiveKitDemo
//
// Created by admin on 16/8/30.
// Copyright © 2016年 admin. All rights reserved.
//
#import "ViewController.h"
#import "LFLivePreview.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.view addSubview:[[LFLivePreview alloc] initWithFrame:self.view.bounds]];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
@end
@@ -0,0 +1,71 @@
//
// UIControl+YYAdd.h
//
//
// Created by guoyaoyuan on 13-4-5.
// Copyright (c) 2013 live Interactive. All rights reserved.
//
#import <UIKit/UIKit.h>
/**
Provides extensions for `UIControl`.
*/
@interface UIControl (YYAdd)
/**
Removes all targets and actions for a particular event (or events)
from an internal dispatch table.
*/
- (void)removeAllTargets;
/**
Adds or replaces a target and action for a particular event (or events)
to an internal dispatch table.
@param target The target object—that is, the object to which the
action message is sent. If this is nil, the responder
chain is searched for an object willing to respond to the
action message.
@param action A selector identifying an action message. It cannot be NULL.
@param controlEvents A bitmask specifying the control events for which the
action message is sent.
*/
- (void)setTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
/**
Adds a block for a particular event (or events) to an internal dispatch table.
It will cause a strong reference to @a block.
@param block The block which is invoked then the action message is
sent (cannot be nil). The block is retained.
@param controlEvents A bitmask specifying the control events for which the
action message is sent.
*/
- (void)addBlockForControlEvents:(UIControlEvents)controlEvents block:(void (^)(id sender))block;
/**
Adds or replaces a block for a particular event (or events) to an internal
dispatch table. It will cause a strong reference to @a block.
@param block The block which is invoked then the action message is
sent (cannot be nil). The block is retained.
@param controlEvents A bitmask specifying the control events for which the
action message is sent.
*/
- (void)setBlockForControlEvents:(UIControlEvents)controlEvents block:(void (^)(id sender))block;
/**
Removes all blocks for a particular event (or events) from an internal
dispatch table.
@param controlEvents A bitmask specifying the control events for which the
action message is sent.
*/
- (void)removeAllBlocksForControlEvents:(UIControlEvents)controlEvents;
@end
@@ -0,0 +1,105 @@
//
// UIControl+YYAdd.m
//
//
// Created by guoyaoyuan on 13-4-5.
// Copyright (c) 2013 live Interactive. All rights reserved.
//
#import "UIControl+YYAdd.h"
#import <objc/runtime.h>
static const int block_key;
@interface _LFUIControlBlockTarget : NSObject
@property (nonatomic, copy) void (^block)(id sender);
@property (nonatomic, assign) UIControlEvents events;
- (id)initWithBlock:(void (^)(id sender))block events:(UIControlEvents)events;
- (void)invoke:(id)sender;
@end
@implementation _LFUIControlBlockTarget
- (id)initWithBlock:(void (^)(id sender))block events:(UIControlEvents)events {
self = [super init];
if (self) {
self.block = block;
self.events = events;
}
return self;
}
- (void)invoke:(id)sender {
if (self.block) self.block(sender);
}
@end
@implementation UIControl (LFAdd)
- (void)removeAllTargets {
[[self allTargets] enumerateObjectsUsingBlock: ^(id object, BOOL *stop) {
[self removeTarget:object
action:NULL
forControlEvents:UIControlEventAllEvents];
}];
}
- (void)setTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents {
NSSet *targets = [self allTargets];
for (id currentTarget in targets) {
NSArray *actions = [self actionsForTarget:currentTarget forControlEvent:controlEvents];
for (NSString *currentAction in actions) {
[self removeTarget:currentTarget action:NSSelectorFromString(currentAction)
forControlEvents:controlEvents];
}
}
[self addTarget:target action:action forControlEvents:controlEvents];
}
- (void)addBlockForControlEvents:(UIControlEvents)controlEvents
block:(void (^)(id sender))block {
_LFUIControlBlockTarget *target = [[_LFUIControlBlockTarget alloc]
initWithBlock:block events:controlEvents];
[self addTarget:target action:@selector(invoke:) forControlEvents:controlEvents];
NSMutableArray *targets = [self _lf_allUIControlBlockTargets];
[targets addObject:target];
}
- (void)setBlockForControlEvents:(UIControlEvents)controlEvents
block:(void (^)(id sender))block {
[self removeAllBlocksForControlEvents:controlEvents];
[self addBlockForControlEvents:controlEvents block:block];
}
- (void)removeAllBlocksForControlEvents:(UIControlEvents)controlEvents {
NSMutableArray *targets = [self _lf_allUIControlBlockTargets];
NSMutableArray *removes = [NSMutableArray array];
[targets enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
_LFUIControlBlockTarget *target = (_LFUIControlBlockTarget *)obj;
if (target.events == controlEvents) {
[removes addObject:target];
[self removeTarget:target
action:@selector(invoke:)
forControlEvents:controlEvents];
}
}];
[targets removeObjectsInArray:removes];
}
- (NSMutableArray *)_lf_allUIControlBlockTargets {
NSMutableArray *targets = objc_getAssociatedObject(self, &block_key);
if (!targets) {
targets = [NSMutableArray array];
objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return targets;
}
@end
@@ -0,0 +1,132 @@
//
// UIView+Add.h
//
//
// Created by guoyaoyuan on 13-4-3.
// Copyright (c) 2013 live Interactive. All rights reserved.
//
#import <UIKit/UIKit.h>
/**
Provides extensions for `UIView`.
*/
@interface UIView (YYAdd)
/**
Create a snapshot image of the complete view hierarchy.
This method should be called in main thread.
*/
- (nullable UIImage *)snapshotImage;
/**
Create a snapshot PDF of the complete view hierarchy.
This method should be called in main thread.
*/
- (nullable NSData *)snapshotPDF;
/**
Shortcut to set the view.layer's shadow
@param color Shadow Color
@param offset Shadow offset
@param radius Shadow radius
*/
- (void)setLayerShadow:(nullable UIColor*)color offset:(CGSize)offset radius:(CGFloat)radius;
/**
* 设置阴影 郭liyuan+
*/
- (void) makeInsetShadow;
- (void) makeInsetShadowWithRadius:(float)radius Alpha:(float)alpha;
- (void) makeInsetShadowWithRadius:(float)radius Color:(nullable UIColor *)color Directions:(nullable NSArray *)directions;
/**
Remove all subviews.
@warning Never call this method inside your view's drawRect: method.
*/
- (void)removeAllSubviews;
/**
Returns the view's view controller (may be nil).
*/
@property (nonatomic, readonly,nullable) UIViewController *viewController;
@property (nonatomic) CGFloat left; ///< Shortcut for frame.origin.x.
@property (nonatomic) CGFloat top; ///< Shortcut for frame.origin.y
@property (nonatomic) CGFloat right; ///< Shortcut for frame.origin.x + frame.size.width
@property (nonatomic) CGFloat bottom; ///< Shortcut for frame.origin.y + frame.size.height
@property (nonatomic) CGFloat width; ///< Shortcut for frame.size.width.
@property (nonatomic) CGFloat height; ///< Shortcut for frame.size.height.
@property (nonatomic) CGFloat centerX; ///< Shortcut for center.x
@property (nonatomic) CGFloat centerY; ///< Shortcut for center.y
@property (nonatomic) CGPoint origin; ///< Shortcut for frame.origin.
@property (nonatomic) CGSize size; ///< Shortcut for frame.size.
@property (nonatomic, readonly) CGRect screenFrame; ///< View frame on the screen, taking into account scroll views.
/**
Returns the visible alpha on screen, taking into account superview and window.
*/
@property (nonatomic, readonly) CGFloat visibleAlpha;
/**
Converts a point from the receiver's coordinate system to that of the specified view or window.
@param point A point specified in the local coordinate system (bounds) of the receiver.
@param view The view or window into whose coordinate system point is to be converted.
If view is nil, this method instead converts to window base coordinates.
@return The point converted to the coordinate system of view.
*/
- (CGPoint)convertPoint:(CGPoint)point toViewOrWindow:(nullable UIView *)view;
/**
Converts a point from the coordinate system of a given view or window to that of the receiver.
@param point A point specified in the local coordinate system (bounds) of view.
@param view The view or window with point in its coordinate system.
If view is nil, this method instead converts from window base coordinates.
@return The point converted to the local coordinate system (bounds) of the receiver.
*/
- (CGPoint)convertPoint:(CGPoint)point fromViewOrWindow:(nullable UIView *)view;
/**
Converts a rectangle from the receiver's coordinate system to that of another view or window.
@param rect A rectangle specified in the local coordinate system (bounds) of the receiver.
@param view The view or window that is the target of the conversion operation. If view is nil, this method instead converts to window base coordinates.
@return The converted rectangle.
*/
- (CGRect)convertRect:(CGRect)rect toViewOrWindow:(nullable UIView *)view;
/**
Converts a rectangle from the coordinate system of another view or window to that of the receiver.
@param rect A rectangle specified in the local coordinate system (bounds) of view.
@param view The view or window with rect in its coordinate system.
If view is nil, this method instead converts from window base coordinates.
@return The converted rectangle.
*/
- (CGRect)convertRect:(CGRect)rect fromViewOrWindow:(nullable UIView *)view;
/**
* 返回响应者链上的任意Objc
*
* @param viewControllerCls 需要返回的Obj的类名,为nil时默认返回当前控制器
*
* @return viewController Or needCls
*/
- (nonnull id)viewControllerWithNeedViewOrViewController:(nullable Class)viewControllerCls
;
/// 移除所有子视图中 tableview、scrollview 的 delegate、datasource
- (void)clearScrollViewDelegate;
- (void)removeAllGestures;
- (void)removeAllGesturesWithSubViews;
/// 在 block 内禁用动画
+ (void)disableAnimationWithBlock:(nullable void (^)(void))block;
@end
@@ -0,0 +1,464 @@
//
// UIView+Add.m
//
//
// Created by guoyaoyuan on 13-4-3.
// Copyright (c) 2013 live Interactive. All rights reserved.
//
#import "UIView+YYAdd.h"
#import <QuartzCore/QuartzCore.h>
@implementation UIView (YYAdd)
- (UIImage *)snapshotImage {
UIImage *image = nil;
if ([self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}else{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return image;
}
- (NSData *)snapshotPDF {
CGRect bounds = self.bounds;
NSMutableData* data = [NSMutableData data];
CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData((__bridge CFMutableDataRef)data);
CGContextRef context = CGPDFContextCreate(consumer, &bounds, NULL);
CGDataConsumerRelease(consumer);
if (!context) return nil;
CGPDFContextBeginPage(context, NULL);
CGContextTranslateCTM(context, 0, bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
[self.layer renderInContext:context];
CGPDFContextEndPage(context);
CGPDFContextClose(context);
CGContextRelease(context);
return data;
}
- (void)setLayerShadow:(UIColor*)color offset:(CGSize)offset radius:(CGFloat)radius {
self.layer.shadowColor = color.CGColor;
self.layer.shadowOffset = offset;
self.layer.shadowRadius = radius;
self.layer.shadowOpacity = 1;
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
}
#define kShadowViewTag 2132
#define kValidDirections [NSArray arrayWithObjects: @"top", @"bottom", @"left", @"right",nil]
- (void) makeInsetShadow
{
NSArray *shadowDirections = [NSArray arrayWithObjects:@"top", @"bottom", @"left" , @"right" , nil];
UIColor *color = [UIColor colorWithRed:(0.0) green:(0.0) blue:(0.0) alpha:0.5];
UIView *shadowView = [self createShadowViewWithRadius:3 Color:color Directions:shadowDirections];
shadowView.tag = kShadowViewTag;
[self addSubview:shadowView];
}
- (void) makeInsetShadowWithRadius:(float)radius Alpha:(float)alpha
{
NSArray *shadowDirections = [NSArray arrayWithObjects:@"top", @"bottom", @"left" , @"right" , nil];
UIColor *color = [UIColor colorWithRed:(0.0) green:(0.0) blue:(0.0) alpha:alpha];
UIView *shadowView = [self createShadowViewWithRadius:radius Color:color Directions:shadowDirections];
shadowView.tag = kShadowViewTag;
[self addSubview:shadowView];
}
- (void) makeInsetShadowWithRadius:(float)radius Color:(UIColor *)color Directions:(NSArray *)directions
{
UIView *shadowView = [self createShadowViewWithRadius:radius Color:color Directions:directions];
shadowView.tag = kShadowViewTag;
[self addSubview:shadowView];
}
- (UIView *) createShadowViewWithRadius:(float)radius Color:(UIColor *)color Directions:(NSArray *)directions
{
UIView *shadowView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
shadowView.backgroundColor = [UIColor clearColor];
// Ignore duplicate direction
NSMutableDictionary *directionDict = [[NSMutableDictionary alloc] init];
for (NSString *direction in directions) [directionDict setObject:@"1" forKey:direction];
// for (NSString *direction in directionDict) {
// // Ignore invalid direction
// if ([kValidDirections containsObject:direction])
// {
// CAGradientLayer *shadow = [CAGradientLayer layer];
// shadow.locations = @[@(0.0),@(0.5)];
//
// if ([direction isEqualToString:@"top"]) {
// [shadow setStartPoint:CGPointMake(0.5, 0.0)];
// [shadow setEndPoint:CGPointMake(0.5, 1.0)];
// shadow.frame = CGRectMake(-radius, -radius, self.bounds.size.width + radius*2, radius);
// shadow.colors = [NSArray arrayWithObjects:(id)[self.backgroundColor CGColor], (id)[color CGColor], nil];
// }
// else if ([direction isEqualToString:@"bottom"])
// {
// [shadow setStartPoint:CGPointMake(0.5, 1.0)];
// [shadow setEndPoint:CGPointMake(0.5, 0.0)];
// shadow.frame = CGRectMake(-radius, self.bounds.size.height, self.bounds.size.width + radius*2, radius);
// shadow.colors = [NSArray arrayWithObjects:(id)[self.backgroundColor CGColor] ,(id)[color CGColor], nil];
// } else if ([direction isEqualToString:@"left"])
// {
// shadow.frame = CGRectMake(-radius, -radius, radius, self.bounds.size.height + radius*2);
// [shadow setStartPoint:CGPointMake(0.0, 0.5)];
// [shadow setEndPoint:CGPointMake(1.0, 0.5)];
// shadow.colors = [NSArray arrayWithObjects:(id)[self.backgroundColor CGColor],(id)[color CGColor], nil];
//
// } else if ([direction isEqualToString:@"right"])
// {
// shadow.frame = CGRectMake(self.bounds.size.width, -radius, radius, self.bounds.size.height + radius*2);
// [shadow setStartPoint:CGPointMake(1.0, 0.5)];
// [shadow setEndPoint:CGPointMake(0.0, 0.5)];
// shadow.colors = [NSArray arrayWithObjects:(id)[self.backgroundColor CGColor],(id)[color CGColor], nil];
// }
// // 后边一个颜色要和所加视图背景颜色一样
//
// [shadowView.layer insertSublayer:shadow atIndex:0];
// }
// }
for (NSString *direction in directionDict) {
// Ignore invalid direction
if ([kValidDirections containsObject:direction])
{
CALayer *shadow = [CAGradientLayer layer];
shadow.backgroundColor = color.CGColor;
if ([direction isEqualToString:@"top"]) {
shadow.frame = CGRectMake(0, 0, self.bounds.size.width, radius);
}
else if ([direction isEqualToString:@"bottom"])
{
shadow.frame = CGRectMake(0, self.bounds.size.height, self.bounds.size.width, radius);
} else if ([direction isEqualToString:@"left"])
{
shadow.frame = CGRectMake(0, 0, radius, self.bounds.size.height );
} else if ([direction isEqualToString:@"right"])
{
shadow.frame = CGRectMake(self.bounds.size.width, 0, radius, self.bounds.size.height);
}
[shadowView.layer insertSublayer:shadow atIndex:0];
}
}
return shadowView;
}
- (void)removeAllSubviews {
while (self.subviews.count) {
[self.subviews.lastObject removeFromSuperview];
}
}
- (UIViewController *)viewController {
for (UIView *view = self; view; view = view.superview) {
UIResponder *nextResponder = [view nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return (UIViewController *)nextResponder;
}
}
return nil;
}
- (nonnull id)viewControllerWithNeedViewOrViewController:(nullable Class)viewControllerCls
{
UIViewController *result = nil;
UIWindow * window = [[UIApplication sharedApplication] keyWindow];
if (window.windowLevel != UIWindowLevelNormal)
{
NSArray *windows = [[UIApplication sharedApplication] windows];
for(UIWindow * tmpWin in windows)
{
if (tmpWin.windowLevel == UIWindowLevelNormal)
{
window = tmpWin;
break;
}
}
}
UIView *frontView = self;
id nextResponse = [frontView nextResponder];
Class cls = viewControllerCls?viewControllerCls:UIViewController.class;
while (nextResponse) {
if ([nextResponse isKindOfClass:cls]) {
result = nextResponse;
break;
}
nextResponse = [nextResponse nextResponder];
}
if(!result) result = window.rootViewController;
return result;
}
- (CGFloat)left {
return self.frame.origin.x;
}
- (void)setLeft:(CGFloat)x {
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)top {
return self.frame.origin.y;
}
- (void)setTop:(CGFloat)y {
CGRect frame = self.frame;
frame.origin.y = y;
self.frame = frame;
}
- (CGFloat)right {
return self.frame.origin.x + self.frame.size.width;
}
- (void)setRight:(CGFloat)right {
CGRect frame = self.frame;
frame.origin.x = right - frame.size.width;
self.frame = frame;
}
- (CGFloat)bottom {
return self.frame.origin.y + self.frame.size.height;
}
- (void)setBottom:(CGFloat)bottom {
CGRect frame = self.frame;
frame.origin.y = bottom - frame.size.height;
self.frame = frame;
}
- (CGFloat)width {
return self.frame.size.width;
}
- (void)setWidth:(CGFloat)width {
CGRect frame = self.frame;
frame.size.width = width;
self.frame = frame;
}
- (CGFloat)height {
return self.frame.size.height;
}
- (void)setHeight:(CGFloat)height {
CGRect frame = self.frame;
frame.size.height = height;
self.frame = frame;
}
- (CGFloat)centerX {
return self.center.x;
}
- (void)setCenterX:(CGFloat)centerX {
self.center = CGPointMake(centerX, self.center.y);
}
- (CGFloat)centerY {
return self.center.y;
}
- (void)setCenterY:(CGFloat)centerY {
self.center = CGPointMake(self.center.x, centerY);
}
- (CGPoint)origin {
return self.frame.origin;
}
- (void)setOrigin:(CGPoint)origin {
CGRect frame = self.frame;
frame.origin = origin;
self.frame = frame;
}
- (CGSize)size {
return self.frame.size;
}
- (void)setSize:(CGSize)size {
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
- (CGRect)screenFrame {
CGPoint origin = CGPointZero;
for (UIView *view = self; view; view = view.superview) {
origin.x += view.left;
origin.y += view.top;
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scrollView = (UIScrollView *)view;
origin.x -= scrollView.contentOffset.x;
origin.y -= scrollView.contentOffset.y;
}
}
return CGRectMake(origin.x, origin.y, self.width, self.height);
}
- (CGFloat)visibleAlpha {
if ([self isKindOfClass:[UIWindow class]]) {
if (self.hidden) return 0;
return self.alpha;
}
if (!self.window) return 0;
CGFloat alpha = 1;
UIView *v = self;
while (v) {
if (v.hidden) {
alpha = 0;
break;
}
alpha *= v.alpha;
v = v.superview;
}
return alpha;
}
- (CGPoint)convertPoint:(CGPoint)point toViewOrWindow:(UIView *)view {
if (!view) {
if ([self isKindOfClass:[UIWindow class]]) {
return [((UIWindow *)self) convertPoint:point toWindow:nil];
} else {
return [self convertPoint:point toView:nil];
}
}
UIWindow *from = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window;
UIWindow *to = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
if ((!from || !to) || (from == to)) return [self convertPoint:point toView:view];
point = [self convertPoint:point toView:from];
point = [to convertPoint:point fromWindow:from];
point = [view convertPoint:point fromView:to];
return point;
}
- (CGPoint)convertPoint:(CGPoint)point fromViewOrWindow:(UIView *)view {
if (!view) {
if ([self isKindOfClass:[UIWindow class]]) {
return [((UIWindow *)self) convertPoint:point fromWindow:nil];
} else {
return [self convertPoint:point fromView:nil];
}
}
UIWindow *from = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
UIWindow *to = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window;
if ((!from || !to) || (from == to)) return [self convertPoint:point fromView:view];
point = [from convertPoint:point fromView:view];
point = [to convertPoint:point fromWindow:from];
point = [self convertPoint:point fromView:to];
return point;
}
- (CGRect)convertRect:(CGRect)rect toViewOrWindow:(UIView *)view {
if (!view) {
if ([self isKindOfClass:[UIWindow class]]) {
return [((UIWindow *)self) convertRect:rect toWindow:nil];
} else {
return [self convertRect:rect toView:nil];
}
}
UIWindow *from = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window;
UIWindow *to = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
if (!from || !to) return [self convertRect:rect toView:view];
if (from == to) return [self convertRect:rect toView:view];
rect = [self convertRect:rect toView:from];
rect = [to convertRect:rect fromWindow:from];
rect = [view convertRect:rect fromView:to];
return rect;
}
- (CGRect)convertRect:(CGRect)rect fromViewOrWindow:(UIView *)view {
if (!view) {
if ([self isKindOfClass:[UIWindow class]]) {
return [((UIWindow *)self) convertRect:rect fromWindow:nil];
} else {
return [self convertRect:rect fromView:nil];
}
}
UIWindow *from = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
UIWindow *to = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window;
if ((!from || !to) || (from == to)) return [self convertRect:rect fromView:view];
rect = [from convertRect:rect fromView:view];
rect = [to convertRect:rect fromWindow:from];
rect = [self convertRect:rect fromView:to];
return rect;
}
- (void)clearScrollViewDelegate {
if ([self isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)self).delegate = nil;
if ([self isKindOfClass:[UITableView class]]) {
((UITableView *)self).delegate = nil;
}
}
for (UIView *sub in self.subviews) {
[sub clearScrollViewDelegate];
}
}
- (void)removeAllGestures {
NSArray *gs = [self.gestureRecognizers copy];
for (UIGestureRecognizer *g in gs) {
[self removeGestureRecognizer:g];
}
}
- (void)removeAllGesturesWithSubViews {
[self removeAllGestures];
for (UIView *v in self.subviews) {
[v removeAllGesturesWithSubViews];
}
[UIView animateWithDuration:0 animations:^{
}];
}
+ (void)disableAnimationWithBlock:(void (^)(void))block {
if (!block) return;
BOOL aniEnabled = [CATransaction disableActions];
[CATransaction setDisableActions:YES];
block();
[CATransaction setDisableActions:aniEnabled];
}
@end
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Some files were not shown because too many files have changed in this diff Show More