Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed2d37e6f4 | |||
| fbbf5d9da5 | |||
| 8f57142e5a | |||
| d198058ce3 | |||
| 3ea70b4780 | |||
| 8b0224626a | |||
| 41fe9d1070 | |||
| d7b981c516 | |||
| 09a4627886 | |||
| a140408a6a | |||
| 3539bae752 | |||
| 40b1f7d37f | |||
| 5d9bbd2b5b | |||
| 909b49c8e9 | |||
| aea2fe1a7e | |||
| 3279030e13 | |||
| 135910e1d0 | |||
| 5f893de671 | |||
| 55e896a4b6 | |||
| 759daed2bc | |||
| 392770247c | |||
| c3d506c2f6 | |||
| a60d7c504c | |||
| 9a4270ccb7 | |||
| 270a23218e | |||
| 8aa0091432 |
+2
-1
@@ -2,7 +2,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "LFLiveKit"
|
||||
s.version = "1.6"
|
||||
s.version = "1.7.0"
|
||||
s.summary = "LaiFeng ios Live. LFLiveKit."
|
||||
s.homepage = "https://github.com/chenliming777"
|
||||
s.license = { :type => "MIT", :file => "LICENSE" }
|
||||
@@ -21,5 +21,6 @@ Pod::Spec.new do |s|
|
||||
s.dependency "CocoaAsyncSocket", "~> 7.4.1"
|
||||
s.dependency 'LMGPUImage', '~> 0.1.9'
|
||||
s.dependency "pili-librtmp", "~> 1.0.2"
|
||||
s.dependency "YYDispatchQueuePool"
|
||||
|
||||
end
|
||||
|
||||
BIN
Binary file not shown.
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.6</string>
|
||||
<string>1.7.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -60,6 +60,21 @@ typedef NS_ENUM(NSUInteger, LFLiveType){
|
||||
/** The beautyFace control capture shader filter empty or beautiy */
|
||||
@property (nonatomic, assign) BOOL beautyFace;
|
||||
|
||||
/** The beautyLevel control beautyFace Level, default 0.5, between 0.0 ~ 1.0 */
|
||||
@property (nonatomic, assign) CGFloat beautyLevel;
|
||||
|
||||
/** The brightLevel control brightness Level, default 0.5, between 0.0 ~ 1.0 */
|
||||
@property (nonatomic, assign) CGFloat brightLevel;
|
||||
|
||||
/** The torch control camera zoom scale default 1.0, between 1.0 ~ 3.0 */
|
||||
@property (nonatomic, assign) CGFloat zoomScale;
|
||||
|
||||
/** The torch control capture flash is on or off */
|
||||
@property (nonatomic, assign) BOOL torch;
|
||||
|
||||
/** The mirror control mirror of front camera is on or off */
|
||||
@property (nonatomic, assign) BOOL mirror;
|
||||
|
||||
/** The muted control callbackAudioData,muted will memset 0.*/
|
||||
@property (nonatomic,assign) BOOL muted;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#import "LFStreamRtmpSocket.h"
|
||||
#import "LFStreamTcpSocket.h"
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
|
||||
#define LFLiveReportKey @"com.youku.liveSessionReport"
|
||||
|
||||
@@ -153,12 +154,12 @@
|
||||
NSUInteger videoBitRate = [_videoEncoder videoBitRate];
|
||||
if(status == LFLiveBuffferIncrease){
|
||||
if(videoBitRate < _videoConfiguration.videoMaxBitRate){
|
||||
videoBitRate = videoBitRate + 50*1024;
|
||||
videoBitRate = videoBitRate + 50 * 1000;
|
||||
[_videoEncoder setVideoBitRate:videoBitRate];
|
||||
}
|
||||
}else{
|
||||
if(videoBitRate > _videoConfiguration.videoMinBitRate){
|
||||
videoBitRate = videoBitRate - 100*1024;
|
||||
videoBitRate = videoBitRate - 100 * 1000;
|
||||
[_videoEncoder setVideoBitRate:videoBitRate];
|
||||
}
|
||||
}
|
||||
@@ -198,6 +199,46 @@
|
||||
return self.videoCaptureSource.beautyFace;
|
||||
}
|
||||
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel {
|
||||
[self.videoCaptureSource setBeautyLevel:beautyLevel];
|
||||
}
|
||||
|
||||
- (CGFloat)beautyLevel {
|
||||
return self.videoCaptureSource.beautyLevel;
|
||||
}
|
||||
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel {
|
||||
[self.videoCaptureSource setBrightLevel:brightLevel];
|
||||
}
|
||||
|
||||
- (CGFloat)brightLevel {
|
||||
return self.videoCaptureSource.brightLevel;
|
||||
}
|
||||
|
||||
- (void)setZoomScale:(CGFloat)zoomScale {
|
||||
[self.videoCaptureSource setZoomScale:zoomScale];
|
||||
}
|
||||
|
||||
- (CGFloat)zoomScale {
|
||||
return self.videoCaptureSource.zoomScale;
|
||||
}
|
||||
|
||||
- (void)setTorch:(BOOL)torch {
|
||||
[self.videoCaptureSource setTorch:torch];
|
||||
}
|
||||
|
||||
- (BOOL)torch {
|
||||
return self.videoCaptureSource.torch;
|
||||
}
|
||||
|
||||
- (void)setMirror:(BOOL)mirror {
|
||||
[self.videoCaptureSource setMirror:mirror];
|
||||
}
|
||||
|
||||
- (BOOL)mirror {
|
||||
return self.videoCaptureSource.mirror;
|
||||
}
|
||||
|
||||
- (void)setMuted:(BOOL)muted{
|
||||
[self.audioCaptureSource setMuted:muted];
|
||||
}
|
||||
@@ -241,7 +282,7 @@
|
||||
- (id<LFStreamSocket>)socket{
|
||||
if(!_socket){
|
||||
if(self.liveType == LFLiveRTMP){
|
||||
_socket = [[LFStreamRtmpSocket alloc] initWithStream:self.streamInfo];
|
||||
_socket = [[LFStreamRtmpSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
|
||||
}else if(self.liveType == LFLiveFLV){
|
||||
_socket = [[LFStreamTcpSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
|
||||
}
|
||||
@@ -264,8 +305,7 @@
|
||||
_timestamp = NOW;
|
||||
_isFirstFrame = false;
|
||||
currentts = 0;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
currentts = NOW - _timestamp;
|
||||
}
|
||||
dispatch_semaphore_signal(_lock);
|
||||
|
||||
@@ -86,8 +86,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);
|
||||
|
||||
@@ -168,6 +168,9 @@ NSString *const LFAudioComponentFailedToCreateNotification = @"LFAudioComponentF
|
||||
seccReason = @"The reason for the change is unknown.";
|
||||
break;
|
||||
}
|
||||
NSLog(@"handleRouteChange reason is %@",seccReason);
|
||||
|
||||
[[AVAudioSession sharedInstance] setActive:YES error:nil];
|
||||
AVAudioSessionPortDescription *input = [[session.currentRoute.inputs count]?session.currentRoute.inputs:nil objectAtIndex:0];
|
||||
if (input.portType == AVAudioSessionPortHeadsetMic) {
|
||||
|
||||
|
||||
@@ -38,6 +38,21 @@
|
||||
/** 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;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
@interface LFVideoCapture ()
|
||||
|
||||
@property(nonatomic, strong) GPUImageVideoCamera *videoCamera;
|
||||
@property(nonatomic, weak) LFGPUImageBeautyFilter *beautyFilter;
|
||||
@property(nonatomic, strong) GPUImageOutput<GPUImageInput> *filter;
|
||||
@property(nonatomic, strong) GPUImageOutput<GPUImageInput> *output;
|
||||
@property(nonatomic, strong) GPUImageCropFilter *cropfilter;
|
||||
@@ -23,6 +24,10 @@
|
||||
@end
|
||||
|
||||
@implementation LFVideoCapture
|
||||
@synthesize torch = _torch;
|
||||
@synthesize beautyLevel = _beautyLevel;
|
||||
@synthesize brightLevel = _brightLevel;
|
||||
@synthesize zoomScale = _zoomScale;
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithVideoConfiguration:(LFLiveVideoConfiguration *)configuration{
|
||||
@@ -43,6 +48,9 @@
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
|
||||
self.beautyFace = YES;
|
||||
self.beautyLevel = 0.5;
|
||||
self.brightLevel = 0.5;
|
||||
self.zoomScale = 1.0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -100,6 +108,72 @@
|
||||
return _videoCamera.frameRate;
|
||||
}
|
||||
|
||||
- (void)setTorch:(BOOL)torch {
|
||||
BOOL ret;
|
||||
if(!_videoCamera.captureSession) return;
|
||||
AVCaptureSession* session = (AVCaptureSession*)_videoCamera.captureSession;
|
||||
[session beginConfiguration];
|
||||
if (_videoCamera.inputCamera) {
|
||||
if (_videoCamera.inputCamera.torchAvailable) {
|
||||
NSError* err = nil;
|
||||
if ([_videoCamera.inputCamera lockForConfiguration:&err]) {
|
||||
[_videoCamera.inputCamera setTorchMode:( torch ? AVCaptureTorchModeOn : AVCaptureTorchModeOff ) ];
|
||||
[_videoCamera.inputCamera unlockForConfiguration];
|
||||
ret = (_videoCamera.inputCamera.torchMode == AVCaptureTorchModeOn);
|
||||
} else {
|
||||
NSLog(@"Error while locking device for torch: %@", err);
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
NSLog(@"Torch not available in current camera input");
|
||||
}
|
||||
}
|
||||
[session commitConfiguration];
|
||||
_torch = ret;
|
||||
}
|
||||
- (BOOL)torch {
|
||||
return _videoCamera.inputCamera.torchMode;
|
||||
}
|
||||
- (void)setMirror:(BOOL)mirror {
|
||||
_videoCamera.horizontallyMirrorFrontFacingCamera = mirror;
|
||||
_videoCamera.horizontallyMirrorRearFacingCamera = mirror;
|
||||
}
|
||||
- (BOOL)mirror {
|
||||
return _videoCamera.horizontallyMirrorFrontFacingCamera;
|
||||
}
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel {
|
||||
_beautyLevel = beautyLevel;
|
||||
if (_beautyFilter) {
|
||||
[_beautyFilter setBeautyLevel:_beautyLevel];
|
||||
}
|
||||
}
|
||||
- (CGFloat)beautyLevel {
|
||||
return _beautyLevel;
|
||||
}
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel {
|
||||
_brightLevel = brightLevel;
|
||||
if (_beautyFilter) {
|
||||
[_beautyFilter setBrightLevel:brightLevel];
|
||||
}
|
||||
}
|
||||
- (CGFloat)brightLevel {
|
||||
return _brightLevel;
|
||||
}
|
||||
- (void)setZoomScale:(CGFloat)zoomScale {
|
||||
if (self.videoCamera && self.videoCamera.inputCamera) {
|
||||
AVCaptureDevice* device = (AVCaptureDevice*)self.videoCamera.inputCamera;
|
||||
if ([device lockForConfiguration:nil]) {
|
||||
device.videoZoomFactor = zoomScale;
|
||||
[device unlockForConfiguration];
|
||||
_zoomScale = zoomScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)zoomScale {
|
||||
return _zoomScale;
|
||||
}
|
||||
|
||||
- (void)setBeautyFace:(BOOL)beautyFace{
|
||||
if(_beautyFace == beautyFace) return;
|
||||
|
||||
@@ -111,14 +185,14 @@
|
||||
if (_beautyFace) {
|
||||
_output = [[LFGPUImageEmptyFilter alloc] init];
|
||||
_filter = [[LFGPUImageBeautyFilter alloc] init];
|
||||
|
||||
_beautyFilter = _filter;
|
||||
__weak typeof(self) _self = self;
|
||||
[_output setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
|
||||
[_self processVideo:output];
|
||||
}];
|
||||
} else {
|
||||
_filter = [[LFGPUImageEmptyFilter alloc] init];
|
||||
|
||||
_beautyFilter = nil;
|
||||
__weak typeof(self) _self = self;
|
||||
[_filter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
|
||||
[_self processVideo:output];
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
|
||||
uint32_t count = size / sizeof(AudioClassDescription);
|
||||
AudioClassDescription descs[count];
|
||||
status = AudioFormatGetProperty(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size, descs);
|
||||
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));
|
||||
|
||||
@@ -59,16 +59,16 @@
|
||||
}
|
||||
|
||||
_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));
|
||||
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)];
|
||||
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_DataRateLimits, (__bridge CFArrayRef)limit);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanFalse);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Main_AutoLevel);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse);
|
||||
VTSessionSetProperty(compressionSession, kVTCompressionPropertyKey_H264EntropyMode, kVTH264EntropyMode_CABAC);
|
||||
VTCompressionSessionPrepareToEncodeFrames(compressionSession);
|
||||
|
||||
}
|
||||
|
||||
@@ -31,9 +31,10 @@
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 500 * 1024;
|
||||
configuration.videoMaxBitRate = 600 * 1024;
|
||||
configuration.videoMinBitRate = 250 * 1024;
|
||||
configuration.videoBitRate = 500 * 1000;
|
||||
configuration.videoMaxBitRate = 600 * 1000;
|
||||
configuration.videoMinBitRate = 400 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Low2:
|
||||
@@ -42,9 +43,10 @@
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 800 * 1024;
|
||||
configuration.videoMaxBitRate = 900 * 1024;
|
||||
configuration.videoMinBitRate = 500 * 1024;
|
||||
configuration.videoBitRate = 600 * 1000;
|
||||
configuration.videoMaxBitRate = 720 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Low3:
|
||||
@@ -53,9 +55,10 @@
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 800 * 1024;
|
||||
configuration.videoMaxBitRate = 900 * 1024;
|
||||
configuration.videoMinBitRate = 500 * 1024;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 600 * 1000;
|
||||
configuration.videoSize = CGSizeMake(360, 640);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium1:
|
||||
@@ -64,9 +67,10 @@
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 800 * 1024;
|
||||
configuration.videoMaxBitRate = 900 * 1024;
|
||||
configuration.videoMinBitRate = 500 * 1024;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium2:
|
||||
@@ -75,9 +79,10 @@
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 800 * 1024;
|
||||
configuration.videoMaxBitRate = 900 * 1024;
|
||||
configuration.videoMinBitRate = 500 * 1024;
|
||||
configuration.videoBitRate = 800 * 1000;
|
||||
configuration.videoMaxBitRate = 960 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_Medium3:
|
||||
@@ -86,9 +91,10 @@
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 1000 * 1024;
|
||||
configuration.videoMaxBitRate = 1200 * 1024;
|
||||
configuration.videoMinBitRate = 500 * 1024;
|
||||
configuration.videoBitRate = 1000 * 1000;
|
||||
configuration.videoMaxBitRate = 1200 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(540, 960);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High1:
|
||||
@@ -97,9 +103,10 @@
|
||||
configuration.videoFrameRate = 15;
|
||||
configuration.videoMaxFrameRate = 15;
|
||||
configuration.videoMinFrameRate = 10;
|
||||
configuration.videoBitRate = 1000 * 1024;
|
||||
configuration.videoMaxBitRate = 1200 * 1024;
|
||||
configuration.videoMinBitRate = 500 * 1024;
|
||||
configuration.videoBitRate = 1000 * 1000;
|
||||
configuration.videoMaxBitRate = 1200 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High2:
|
||||
@@ -108,9 +115,10 @@
|
||||
configuration.videoFrameRate = 24;
|
||||
configuration.videoMaxFrameRate = 24;
|
||||
configuration.videoMinFrameRate = 12;
|
||||
configuration.videoBitRate = 1200 * 1024;
|
||||
configuration.videoMaxBitRate = 1300 * 1024;
|
||||
configuration.videoMinBitRate = 800 * 1024;
|
||||
configuration.videoBitRate = 1200 * 1000;
|
||||
configuration.videoMaxBitRate = 1440 * 1000;
|
||||
configuration.videoMinBitRate = 800 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
case LFLiveVideoQuality_High3:
|
||||
@@ -119,9 +127,10 @@
|
||||
configuration.videoFrameRate = 30;
|
||||
configuration.videoMaxFrameRate = 30;
|
||||
configuration.videoMinFrameRate = 15;
|
||||
configuration.videoBitRate = 1200 * 1024;
|
||||
configuration.videoMaxBitRate = 1300 * 1024;
|
||||
configuration.videoMinBitRate = 500 * 1024;
|
||||
configuration.videoBitRate = 1200 * 1000;
|
||||
configuration.videoMaxBitRate = 1440 * 1000;
|
||||
configuration.videoMinBitRate = 500 * 1000;
|
||||
configuration.videoSize = CGSizeMake(720, 1280);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -130,10 +139,11 @@
|
||||
configuration.sessionPreset = [configuration supportSessionPreset:configuration.sessionPreset];
|
||||
configuration.videoMaxKeyframeInterval = configuration.videoFrameRate*2;
|
||||
configuration.orientation = orientation;
|
||||
CGSize size = configuration.videoSize;
|
||||
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown){
|
||||
configuration.videoSize = CGSizeMake(368, 640);
|
||||
configuration.videoSize = CGSizeMake(size.width, size.height);
|
||||
}else{
|
||||
configuration.videoSize = CGSizeMake(640, 368);
|
||||
configuration.videoSize = CGSizeMake(size.height, size.width);
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
@interface LFGPUImageBeautyFilter : GPUImageFilter {
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) NSInteger beautyLevel;
|
||||
|
||||
@property (nonatomic, assign) CGFloat beautyLevel;
|
||||
@property (nonatomic, assign) CGFloat brightLevel;
|
||||
@property (nonatomic, assign) CGFloat toneLevel;
|
||||
@end
|
||||
|
||||
@@ -8,13 +8,17 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform highp vec2 singleStepOffset;
|
||||
uniform mediump float params;
|
||||
uniform highp vec4 params;
|
||||
uniform highp float brightness;
|
||||
|
||||
const highp vec3 W = vec3(0.299,0.587,0.114);
|
||||
highp vec2 blurCoordinates[20];
|
||||
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)
|
||||
{
|
||||
highp float hardLight(highp float color) {
|
||||
if(color <= 0.5)
|
||||
color = color * color * 2.0;
|
||||
else
|
||||
@@ -45,8 +49,12 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
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 * 20.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;
|
||||
@@ -67,8 +75,12 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
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 / 48.0;
|
||||
sampleColor = sampleColor / 62.0;
|
||||
|
||||
highp float highPass = centralColor.g - sampleColor + 0.5;
|
||||
|
||||
@@ -76,13 +88,27 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
{
|
||||
highPass = hardLight(highPass);
|
||||
}
|
||||
highp float luminance = dot(centralColor, W);
|
||||
highp float lumance = dot(centralColor, W);
|
||||
|
||||
highp float alpha = pow(luminance, params);
|
||||
highp float alpha = pow(lumance, params.r);
|
||||
|
||||
highp vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
|
||||
|
||||
gl_FragColor = vec4(mix(smoothColor.rgb, max(smoothColor, centralColor), alpha), 1.0);
|
||||
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
|
||||
@@ -93,10 +119,14 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mediump vec2 singleStepOffset;
|
||||
uniform mediump float params;
|
||||
|
||||
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[20];
|
||||
mediump vec2 blurCoordinates[24];
|
||||
|
||||
mediump float hardLight(mediump float color)
|
||||
{
|
||||
@@ -130,8 +160,12 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
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 * 20.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;
|
||||
@@ -152,8 +186,12 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
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 / 48.0;
|
||||
sampleColor = sampleColor / 62.0;
|
||||
|
||||
mediump float highPass = centralColor.g - sampleColor + 0.5;
|
||||
|
||||
@@ -167,7 +205,21 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
|
||||
mediump vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
|
||||
|
||||
gl_FragColor = vec4(mix(smoothColor.rgb, max(smoothColor, centralColor), alpha), 1.0);
|
||||
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
|
||||
@@ -181,9 +233,11 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.beautyLevel = 2
|
||||
;
|
||||
|
||||
_toneLevel = 0.5;
|
||||
_beautyLevel = 0.5;
|
||||
_brightLevel = 0.5;
|
||||
[self setParams:_beautyLevel tone:_toneLevel];
|
||||
[self setBrightLevel:_brightLevel];
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -197,27 +251,25 @@ NSString *const kLFGPUImageBeautyFragmentShaderString = SHADER_STRING
|
||||
[self setPoint:offset forUniformName:@"singleStepOffset"];
|
||||
}
|
||||
|
||||
- (void)setBeautyLevel:(NSInteger)level
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel
|
||||
{
|
||||
switch (level) {
|
||||
case 1:
|
||||
[self setFloat:1.0f forUniformName:@"params"];
|
||||
break;
|
||||
case 2:
|
||||
[self setFloat:0.8f forUniformName:@"params"];
|
||||
break;
|
||||
case 3:
|
||||
[self setFloat:0.6f forUniformName:@"params"];
|
||||
break;
|
||||
case 4:
|
||||
[self setFloat:0.4f forUniformName:@"params"];
|
||||
break;
|
||||
case 5:
|
||||
[self setFloat:0.33f forUniformName:@"params"];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
_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
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
|
||||
#import "LFStreamRtmpSocket.h"
|
||||
#import "rtmp.h"
|
||||
#import "YYDispatchQueuePool.h"
|
||||
|
||||
static const NSInteger RetryTimesBreaken = 20;///< 重连1分钟 3秒一次 一共20次
|
||||
static const NSInteger RetryTimesMargin = 3;
|
||||
|
||||
static dispatch_queue_t YYRtmpSendQueue() {
|
||||
return YYDispatchQueueGetForQOS(NSQualityOfServiceUserInitiated);
|
||||
}
|
||||
|
||||
#define DATA_ITEMS_MAX_COUNT 100
|
||||
#define RTMP_DATA_RESERVE_SIZE 400
|
||||
@@ -16,7 +24,7 @@
|
||||
#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.5.2");
|
||||
static const AVal av_SDKVersion = AVC("LFLiveKit 1.6");
|
||||
SAVC(onMetaData);
|
||||
SAVC(duration);
|
||||
SAVC(width);
|
||||
@@ -43,10 +51,12 @@ SAVC(mp4a);
|
||||
@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, strong) LFLiveDebug *debugInfo;
|
||||
//错误信息
|
||||
@property (nonatomic, assign) RTMPError error;
|
||||
@property (nonatomic, assign) NSInteger retryTimes4netWorkBreaken;
|
||||
@property (nonatomic, assign) NSInteger reconnectInterval;
|
||||
@property (nonatomic, assign) NSInteger reconnectCount;
|
||||
|
||||
@property (nonatomic, assign) BOOL isSending;
|
||||
@property (nonatomic, assign) BOOL isConnected;
|
||||
@@ -56,44 +66,54 @@ SAVC(mp4a);
|
||||
@property (nonatomic, assign) BOOL sendVideoHead;
|
||||
@property (nonatomic, assign) BOOL sendAudioHead;
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation LFStreamRtmpSocket
|
||||
|
||||
#pragma mark -- LFStreamSocket
|
||||
- (instancetype)initWithStream:(LFLiveStreamInfo*)stream{
|
||||
- (nullable instancetype)initWithStream:(nullable LFLiveStreamInfo*)stream videoSize:(CGSize)videoSize reconnectInterval:(NSInteger)reconnectInterval reconnectCount:(NSInteger)reconnectCount{
|
||||
if(!stream) @throw [NSException exceptionWithName:@"LFStreamRtmpSocket init error" reason:@"stream is nil" userInfo:nil];
|
||||
if(self = [super init]){
|
||||
_stream = stream;
|
||||
if(reconnectInterval > 0) _reconnectInterval = reconnectInterval;
|
||||
else _reconnectInterval = RetryTimesMargin;
|
||||
|
||||
if(reconnectCount > 0) _reconnectCount = reconnectCount;
|
||||
else _reconnectCount = RetryTimesBreaken;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) start{
|
||||
dispatch_async(self.socketQueue, ^{
|
||||
dispatch_async(YYRtmpSendQueue(), ^{
|
||||
if(!_stream) return;
|
||||
if(_isConnecting) return;
|
||||
if(_rtmp != NULL) return;
|
||||
|
||||
self.debugInfo.streamId = self.stream.streamId;
|
||||
self.debugInfo.uploadUrl = self.stream.url;
|
||||
self.debugInfo.isRtmp = YES;
|
||||
[self clean];
|
||||
[self RTMP264_Connect:(char*)[_stream.url cStringUsingEncoding:NSASCIIStringEncoding]];
|
||||
});
|
||||
}
|
||||
|
||||
- (void) stop{
|
||||
dispatch_async(self.socketQueue, ^{
|
||||
dispatch_async(YYRtmpSendQueue(), ^{
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveStop];
|
||||
}
|
||||
if(_rtmp != NULL){
|
||||
PILI_RTMP_Close(_rtmp, &_error);
|
||||
PILI_RTMP_Free(_rtmp);
|
||||
_rtmp = NULL;
|
||||
}
|
||||
[self clean];
|
||||
});
|
||||
}
|
||||
|
||||
- (void) sendFrame:(LFFrame*)frame{
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(self.socketQueue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
dispatch_async(YYRtmpSendQueue(), ^{
|
||||
if(!frame) return;
|
||||
[self.buffer appendObject:frame];
|
||||
[self sendFrame];
|
||||
@@ -108,9 +128,9 @@ SAVC(mp4a);
|
||||
- (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]]){
|
||||
@@ -129,6 +149,28 @@ SAVC(mp4a);
|
||||
}
|
||||
|
||||
}
|
||||
self.debugInfo.dataFlow += frame.data.length;
|
||||
if(CACurrentMediaTime()*1000 - self.debugInfo.timeStamp < 1000) {
|
||||
self.debugInfo.bandwidth += frame.data.length;
|
||||
if([frame isKindOfClass:[LFAudioFrame class]]){
|
||||
self.debugInfo.capturedAudioCount ++;
|
||||
}else{
|
||||
self.debugInfo.capturedVideoCount ++;
|
||||
}
|
||||
self.debugInfo.unSendCount = self.buffer.list.count;
|
||||
}else {
|
||||
self.debugInfo.currentBandwidth = self.debugInfo.bandwidth;
|
||||
self.debugInfo.currentCapturedAudioCount = self.debugInfo.capturedAudioCount;
|
||||
self.debugInfo.currentCapturedVideoCount = self.debugInfo.capturedVideoCount;
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketDebug:debugInfo:)]){
|
||||
[self.delegate socketDebug:self debugInfo:self.debugInfo];
|
||||
}
|
||||
|
||||
self.debugInfo.bandwidth = 0;
|
||||
self.debugInfo.capturedAudioCount = 0;
|
||||
self.debugInfo.capturedVideoCount = 0;
|
||||
self.debugInfo.timeStamp = CACurrentMediaTime()*1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +181,7 @@ SAVC(mp4a);
|
||||
_isConnected = NO;
|
||||
_sendAudioHead = NO;
|
||||
_sendVideoHead = NO;
|
||||
self.debugInfo = nil;
|
||||
[self.buffer removeAllObject];
|
||||
self.retryTimes4netWorkBreaken = 0;
|
||||
}
|
||||
@@ -167,6 +210,10 @@ SAVC(mp4a);
|
||||
goto Failed;
|
||||
}
|
||||
|
||||
_rtmp->m_errorCallback = RTMPErrorCallback;
|
||||
_rtmp->m_connCallback = ConnectionTimeCallback;
|
||||
_rtmp->m_userData = (__bridge void*)self;
|
||||
_rtmp->m_msgCounter = 1;
|
||||
//设置可写,即发布流,这个函数必须在连接前使用,否则无效
|
||||
PILI_RTMP_EnableWrite(_rtmp);
|
||||
|
||||
@@ -196,10 +243,6 @@ SAVC(mp4a);
|
||||
Failed:
|
||||
PILI_RTMP_Close(_rtmp, &_error);
|
||||
PILI_RTMP_Free(_rtmp);
|
||||
[self clean];
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[self.delegate socketStatus:self status:LFLiveError];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -251,7 +294,7 @@ Failed:
|
||||
*enc++ = 0;
|
||||
*enc++ = 0;
|
||||
*enc++ = AMF_OBJECT_END;
|
||||
|
||||
|
||||
packet.m_nBodySize = enc - packet.m_body;
|
||||
if(!PILI_RTMP_SendPacket(_rtmp, &packet, FALSE, &_error)) {
|
||||
return;
|
||||
@@ -268,7 +311,7 @@ Failed:
|
||||
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);
|
||||
|
||||
@@ -360,7 +403,10 @@ Failed:
|
||||
int success = PILI_RTMP_SendPacket(_rtmp,packet,0,&_error);
|
||||
if(success){
|
||||
self.isSending = NO;
|
||||
[self sendFrame];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self sendFrame];
|
||||
});
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -398,14 +444,44 @@ Failed:
|
||||
free(body);
|
||||
}
|
||||
|
||||
// 断线重连
|
||||
-(void) reconnect {
|
||||
dispatch_async(YYRtmpSendQueue(), ^{
|
||||
_isReconnecting = NO;
|
||||
if(_isConnected) return;
|
||||
|
||||
[self stop];
|
||||
[self start];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -- CallBack
|
||||
void RTMPErrorCallback(RTMPError *error, void *userData){
|
||||
LFStreamRtmpSocket *socket = (__bridge LFStreamRtmpSocket*)userData;
|
||||
if(error->code < 0){
|
||||
if(socket.retryTimes4netWorkBreaken++ < socket.reconnectCount && !socket.isReconnecting){
|
||||
socket.isConnected = NO;
|
||||
socket.isConnecting = NO;
|
||||
socket.isReconnecting = YES;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(socket.reconnectInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[socket reconnect];
|
||||
});
|
||||
}else if(socket.retryTimes4netWorkBreaken >= socket.reconnectCount){
|
||||
if(socket.delegate && [socket.delegate respondsToSelector:@selector(socketStatus:status:)]){
|
||||
[socket.delegate socketStatus:socket status:LFLiveError];
|
||||
}
|
||||
if(socket.delegate && [socket.delegate respondsToSelector:@selector(socketDidError:errorCode:)]){
|
||||
[socket.delegate socketDidError:socket errorCode:LFLiveSocketError_ReConnectTimeOut];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionTimeCallback(PILI_CONNECTION_TIME* conn_time, void *userData){
|
||||
//LFStreamRtmpSocket *socket = (__bridge LFStreamRtmpSocket*)userData;
|
||||
}
|
||||
|
||||
#pragma mark -- Getter Setter
|
||||
- (dispatch_queue_t)socketQueue{
|
||||
if(!_socketQueue){
|
||||
_socketQueue = dispatch_queue_create("com.youku.LaiFeng.live.socketQueue", NULL);
|
||||
}
|
||||
return _socketQueue;
|
||||
}
|
||||
|
||||
- (LFStreamingBuffer*)buffer{
|
||||
if(!_buffer){
|
||||
@@ -415,4 +491,11 @@ Failed:
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
- (LFLiveDebug*)debugInfo{
|
||||
if(!_debugInfo){
|
||||
_debugInfo = [[LFLiveDebug alloc] init];
|
||||
}
|
||||
return _debugInfo;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -94,9 +94,9 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
|
||||
return;
|
||||
}
|
||||
|
||||
LFFrame *firstIFrame = [self firstIFrame];
|
||||
if(firstIFrame){
|
||||
[self.list removeObject:firstIFrame];
|
||||
NSArray *iFrames = [self expireIFrames];///< 删除一个I帧(但一个I帧可能对应多个nal)
|
||||
if(iFrames){
|
||||
[self.list removeObjectsInArray:iFrames];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
|
||||
LFVideoFrame *videoFrame = (LFVideoFrame*)frame;
|
||||
if(videoFrame.isKeyFrame && pframes.count > 0){
|
||||
break;
|
||||
}else{
|
||||
}else if(!videoFrame.isKeyFrame){
|
||||
[pframes addObject:frame];
|
||||
}
|
||||
}
|
||||
@@ -119,14 +119,18 @@ static const NSUInteger defaultSendBufferMaxCount = 600;///< 最大缓冲区为6
|
||||
return pframes;
|
||||
}
|
||||
|
||||
- (LFFrame*)firstIFrame{
|
||||
- (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(timeStamp != 0 && timeStamp != frame.timestamp) break;
|
||||
[iframes addObject:frame];
|
||||
timeStamp = frame.timestamp;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
return iframes;
|
||||
}
|
||||
|
||||
NSInteger frameDataCompare(id obj1, id obj2, void *context){
|
||||
|
||||
Generated
BIN
Binary file not shown.
@@ -20,6 +20,7 @@
|
||||
@property (nonatomic, strong) UIView *containerView;
|
||||
@property (nonatomic, strong) LFLiveDebug *debugInfo;
|
||||
@property (nonatomic, strong) LFLiveSession *session;
|
||||
@property (nonatomic, strong) UILabel *stateLabel;
|
||||
|
||||
@end
|
||||
|
||||
@@ -31,6 +32,7 @@
|
||||
[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];
|
||||
@@ -57,7 +59,9 @@
|
||||
}
|
||||
case AVAuthorizationStatusAuthorized:{
|
||||
// 已经开启授权,可继续
|
||||
[_self.session setRunning:YES];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_self.session setRunning:YES];
|
||||
});
|
||||
break;
|
||||
}
|
||||
case AVAuthorizationStatusDenied:
|
||||
@@ -93,17 +97,36 @@
|
||||
#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: %lf", debugInfo.dataFlow);
|
||||
}
|
||||
|
||||
/** callback socket errorcode */
|
||||
- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode{
|
||||
|
||||
NSLog(@"errorCode: %ld", errorCode);
|
||||
}
|
||||
|
||||
#pragma mark -- Getter Setter
|
||||
@@ -111,6 +134,7 @@
|
||||
if(!_session){
|
||||
/*** 默认分辨率368 * 640 音频:44.1 iphone6以上48 双声道 方向竖屏 ***/
|
||||
_session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Medium2] liveType:LFLiveRTMP];
|
||||
_session.delegate = self;
|
||||
|
||||
/** 自己定制单声道 */
|
||||
/*
|
||||
@@ -196,7 +220,7 @@
|
||||
*/
|
||||
|
||||
|
||||
_session.running = YES;
|
||||
_session.delegate = self;
|
||||
_session.preView = self;
|
||||
}
|
||||
return _session;
|
||||
@@ -212,6 +236,16 @@
|
||||
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];
|
||||
@@ -278,7 +312,7 @@
|
||||
if(_self.startLiveButton.selected){
|
||||
[_self.startLiveButton setTitle:@"结束直播" forState:UIControlStateNormal];
|
||||
LFLiveStreamInfo *stream = [LFLiveStreamInfo new];
|
||||
stream.url = @"rtmp://30.96.179.95:1935/live/1234";
|
||||
stream.url = @"rtmp://live.hkstv.hk.lxdns.com:1935/live/stream789";
|
||||
//stream.url = @"rtmp://daniulive.com:1935/live/stream2399";
|
||||
[_self.session startLive:stream];
|
||||
}else{
|
||||
|
||||
@@ -3,6 +3,7 @@ platform :ios,'8.0'
|
||||
|
||||
target "LFLiveKitDemo" do
|
||||
|
||||
pod 'LFLiveKit', '~> 1.5.2'
|
||||
pod 'LFLiveKit', '~> 1.6.7'
|
||||
pod 'YYDispatchQueuePool'
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user