美文网首页
iOS ReplayKit 录屏

iOS ReplayKit 录屏

作者: 索性流年 | 来源:发表于2020-04-07 14:12 被阅读0次

概述:

最近公司新增了个功能,就是录屏推流功能,使用的是系统的ReplayKit 框架.

问题难点:

1.扩展应用无法引入腾讯云 SDK 文件
2.使用ReplayKit扩展应用无法与主应用互传参数.

1.扩展应用无法引入腾讯云 SDK 文件解决方法

如若使用 CocoaPods安装的 三方库,先将其移除,依照官方文档方法手动导入,而后选择扩展应用使用同样的方法导入 SDK 即可

2.使用ReplayKit扩展应用无法与主应用互传参数解决方案.

这里使用的是 Scoket 短连接来实现通讯,
更多Scoket内容请看我关于Scoket实现文章https://www.jianshu.com/p/af99261795d1

创建ReplayKit

file -> new -> target
输入扩展项目名称,格式和正常创建文件无样

image.png

创建完成会目录会多出两个文件夹如图:

image.png

SampleHandler.m 中实现对应推流方法,这里已腾讯云为例

#import "SampleHandler.h"
#import <TXLiteAVSDK_Professional/TXLiteAVSDK.h>
#import<sys/socket.h>
#import<netinet/in.h>
#import<arpa/inet.h>
#import<ifaddrs.h>
#import<net/if.h>

static CMSampleBufferRef lastSampleBuffer;
static TXLivePush * s_txLivePublisher;
@interface SampleHandler ()<TXLivePushListener>
@end
@implementation SampleHandler
- (instancetype)init
{
    self = [super init];
    if (self) {
        [self txLiteAVSDKProfessional];
        [self accept];
    }
    return self;
}
#pragma mark - 腾讯云直播配置
-(void)txLiteAVSDKProfessional{
    NSString * const licenceURL = @"填写自己的 URL";
    NSString * const licenceKey = @"填写自己的 Key";
    //TXLiveBase 位于 "TXLiveBase.h" 头文件中
    [TXLiveBase setLicenceURL:licenceURL key:licenceKey];
}
- (void)initPublisher {
     if (s_txLivePublisher) {
        [s_txLivePublisher stopPush];
    }
    TXLivePushConfig * config = [[TXLivePushConfig alloc] init];
    config.customModeType |= CUSTOM_MODE_VIDEO_CAPTURE; //自定义视频模式
    config.enableAutoBitrate = YES;
    config.enableHWAcceleration = YES;

    config.customModeType |= CUSTOM_MODE_AUDIO_CAPTURE; //自定义音频模式
    config.audioSampleRate = AUDIO_SAMPLE_RATE_44100;   //系统录屏的音频采样率为44100
    config.audioChannels   = 1;

    config.autoSampleBufferSize = NO;
    config.sampleBufferSize = CGSizeMake(544,960);
    config.homeOrientation = HOME_ORIENTATION_DOWN;

    s_txLivePublisher = [[TXLivePush alloc] initWithConfig:config];
    s_txLivePublisher.delegate = self;
}
#pragma mark - 监听端口接收数据
-(void)accept{
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
       struct sockaddr_in servAddr;
       servAddr.sin_family = AF_INET;
       servAddr.sin_port = htons(9091);
       
       char tempChar[1000];
       const char * ip = strcpy(tempChar,(char *)[[self getIPAddress] UTF8String]);
       servAddr.sin_addr.s_addr = inet_addr(ip);
       
       int  serverSocker =  socket(AF_INET, SOCK_STREAM, 0);
       int sClient = bind(serverSocker, (const struct sockaddr *)&servAddr, sizeof(servAddr));
       
       if (sClient== -1) {
           NSLog(@"绑定失败");
           return;
       }
       int listenS = listen(serverSocker,1);
       if (listenS == -1) {
           NSLog(@"监听失败");
           return;
       }
       unsigned int nAddrlen = sizeof(servAddr);
       int linkSocket = accept(serverSocker,(struct sockaddr *)&servAddr, &nAddrlen);
       if (linkSocket == -1) {
           NSLog(@"接受链接失败%d",sClient);
           return ;
       }
       [self recv:linkSocket];
   });
}
#pragma mark - 接收数据
-(void)recv:(int)clintSocket{
    char butter[1024] = {0};
    ssize_t recvlen = recv(clintSocket, butter, sizeof(butter), 0);
    if (recvlen != -1) {
        NSString * pushUrl = [NSString stringWithUTF8String:butter];
        NSLog(@"msg:%@ code:%ld",pushUrl,recvlen);
        
        int star = [s_txLivePublisher startPush:pushUrl];
        if (star == 0) {
            NSLog(@"推流启动成功");
        }
        else{
            NSLog(@"推流启动失败");
        }
        if (lastSampleBuffer) {
            [s_txLivePublisher sendVideoSampleBuffer:lastSampleBuffer];
        }
        memset(butter, 0, sizeof(butter));
    }
//    close(clintSocket);
}


#pragma mark - 开始录屏
- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
    [self initPublisher];
}
#pragma mark - 暂停录制
- (void)broadcastPaused {
    [s_txLivePublisher setSendAudioSampleBufferMuted:YES];
}
#pragma mark - 恢复录制
- (void)broadcastResumed {
     [s_txLivePublisher setSendAudioSampleBufferMuted:NO];
}
#pragma mark - 录制完成
- (void)broadcastFinished {
    if (s_txLivePublisher) {
        [s_txLivePublisher stopPush];
        s_txLivePublisher = nil;
    }
}

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
    
    switch (sampleBufferType) {
        case RPSampleBufferTypeVideo:
            if (!CMSampleBufferIsValid(sampleBuffer))
                    return;
            //保存一帧在 startPush 时发送,防止推流启动后或切换横竖屏因无画面数据而推流不成功
            if (lastSampleBuffer) {
                CFRelease(lastSampleBuffer);
                lastSampleBuffer = NULL;
            }
            lastSampleBuffer = sampleBuffer;
            CFRetain(lastSampleBuffer);
            [s_txLivePublisher sendVideoSampleBuffer:sampleBuffer];
            break;
        case RPSampleBufferTypeAudioApp:
            // Handle audio sample buffer for app audio
            break;
        case RPSampleBufferTypeAudioMic:
            // Handle audio sample buffer for mic audio
            break;
            
        default:
            break;
    }
}
#pragma mark - 腾讯云事件通知
- (void)onPushEvent:(int)EvtID withParam:(NSDictionary *)param{
    NSLog(@"onPushEvent %d", EvtID);
    if (EvtID == PUSH_ERR_NET_DISCONNECT) {
        NSLog(@"腾讯云录屏失败");
    }  else if (EvtID == PUSH_EVT_PUSH_BEGIN) {
        NSLog(@"腾讯云录屏连接成功");
    } else if (EvtID == PUSH_WARNING_NET_BUSY) {
        NSLog(@"腾讯云录屏网络上行带宽不足");
    }
}
#pragma mark - 腾讯云状态通知
- (void)onNetStatus:(NSDictionary *)param{
    
}

@end

用户端发送数据代码

#pragma mark - 连接服务端
-(BOOL)clintSocket{
    clintSocket =  socket(AF_INET , SOCK_STREAM, 0);
    struct sockaddr_in servAddr;
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(9091);
    
    char tempChar[1000];
    const char * ip = strcpy(tempChar,(char *)[[self getIPAddress] UTF8String]);
    servAddr.sin_addr.s_addr = inet_addr(ip);
    

    int connetRect = connect(clintSocket, (const struct sockaddr *)&servAddr, sizeof(servAddr));
    
    if (connetRect == -1) {
        return NO;
    }else{
        return YES;
    }
}
-(void)sendMsa:(NSString *)sendText{
    ssize_t senlen =  send(clintSocket, sendText.UTF8String, strlen(sendText.UTF8String), 0);
    NSLog(@"发送了%ld个字符",senlen);
    //   6.关闭
    close(clintSocket);
}
#pragma mark - 获取 IP
- (NSString *)getIPAddress
{
    NSString *address = @"error";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    
    success = getifaddrs(&interfaces);
    if (success == 0) {
        
        temp_addr = interfaces;
        while (temp_addr != NULL) {
            if( temp_addr->ifa_addr->sa_family == AF_INET) {
                
                if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                }
            }
            
            temp_addr = temp_addr->ifa_next;
        }
    }
    
    // Free memory
    freeifaddrs(interfaces);
    
    return address;
}

相关文章

网友评论

      本文标题:iOS ReplayKit 录屏

      本文链接:https://www.haomeiwen.com/subject/jfvgphtx.html