├── Socket_Replykit ├── Socket_Replykit │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ViewController.h │ ├── Codec │ │ ├── RongRTCVideoEncoderSettings.m │ │ ├── RongRTCCodec.m │ │ ├── Decoder │ │ │ ├── RongRTCVideoDecoder.h │ │ │ └── RongRTCVideoDecoder.mm │ │ ├── Encoder │ │ │ ├── RongRTCVideoEncoder.h │ │ │ └── RongRTCVideoEncoder.mm │ │ ├── RongRTCVideoEncoderSettings.h │ │ ├── RongRTCCodecProtocol.h │ │ ├── RongRTCCodec.h │ │ ├── helpers.h │ │ └── helpers.mm │ ├── OtherTest │ │ ├── OtherTest.h │ │ └── OtherTest.m │ ├── AppDelegate.h │ ├── SceneDelegate.h │ ├── Socket │ │ ├── RongRTCThread.h │ │ ├── RongRTCSocketHeader.h │ │ ├── Client │ │ │ ├── RongRTCClientSocket.h │ │ │ └── RongRTCClientSocket.m │ │ ├── Server │ │ │ ├── RongRTCServerSocket.h │ │ │ └── RongRTCServerSocket.m │ │ ├── RongRTCBufferUtil.h │ │ ├── RongRTCSocket.h │ │ ├── RongRTCThread.m │ │ ├── RongRTCBufferUtil.m │ │ └── RongRTCSocket.m │ ├── main.m │ ├── AppDelegate.m │ ├── Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── ViewController.m │ └── SceneDelegate.m ├── Socket_Replykit.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── project.pbxproj ├── SocketReply │ ├── SampleHandler.h │ ├── Info.plist │ └── SampleHandler.m ├── SocketReplySetupUI │ ├── BroadcastSetupViewController.h │ ├── BroadcastSetupViewController.m │ └── Info.plist ├── Socket_ReplykitTests │ ├── Info.plist │ └── Socket_ReplykitTests.m └── Socket_ReplykitUITests │ ├── Info.plist │ └── Socket_ReplykitUITests.m ├── .gitignore └── README.md /Socket_Replykit/Socket_Replykit/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Socket_Replykit/SocketReply/SampleHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // SampleHandler.h 3 | // SocketReply 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SampleHandler : RPBroadcastSampleHandler 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Socket_Replykit 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/RongRTCVideoEncoderSettings.m: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCVideoEncoderSettings.m 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/13. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCVideoEncoderSettings.h" 10 | 11 | @implementation RongRTCVideoEncoderSettings 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/OtherTest/OtherTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // OtherTest.h 3 | // Socket_Replykit 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface OtherTest : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Socket_Replykit 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | @property (strong, nonatomic) UIWindow * window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Socket_Replykit/SocketReplySetupUI/BroadcastSetupViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BroadcastSetupViewController.h 3 | // SocketReplySetupUI 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface BroadcastSetupViewController : UIViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // Socket_Replykit 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SceneDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow * window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/RongRTCCodec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCCodec.m 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCCodec.h" 10 | 11 | @implementation RongRTCCodec 12 | -(BOOL)configWithSettings:(RongRTCVideoEncoderSettings *)settings onQueue:(dispatch_queue_t)queue{ 13 | return NO; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/Decoder/RongRTCVideoDecoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCVideoDecoder.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/14. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "RongRTCCodec.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface RongRTCVideoDecoder : RongRTCCodec 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/Encoder/RongRTCVideoEncoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCVideoEncoder.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/13. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "RongRTCCodec.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface RongRTCVideoEncoder : RongRTCCodec 16 | 17 | 18 | @end 19 | 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/RongRTCThread.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCThread.h 3 | // test 4 | // 5 | // Created by 孙承秀 on 2020/5/12. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | typedef void (^Block)(void); 13 | @interface RongRTCThread : NSObject 14 | - (void)excuteTaskWithBlock:(Block)block; 15 | - (void)run; 16 | - (void)stop; 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/RongRTCSocketHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCSocketHeader.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/13. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // pre header 12 | typedef struct { 13 | unsigned char pre[1];// pre 14 | NSUInteger dataLength; // data length 15 | CMTime pts; 16 | } PreHeader; 17 | 18 | // data header 19 | typedef struct { 20 | 21 | PreHeader preH; 22 | 23 | } DataHeader; 24 | 25 | 26 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Socket_Replykit 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | NSString * appDelegateClassName; 14 | @autoreleasepool { 15 | // Setup code that might create autoreleased objects goes here. 16 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 17 | } 18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 19 | } 20 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/Client/RongRTCClientSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCClientSocket.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/7. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RongRTCSocket.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface RongRTCClientSocket : RongRTCSocket 15 | 16 | /// 创建 client socket 17 | - (BOOL)createCliectSocket; 18 | 19 | /// client 开始编码发给 server 20 | /// @param sampleBuffer 要编码的 sampleBuffer 21 | - (void)encodeBuffer:(CMSampleBufferRef)sampleBuffer; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/RongRTCVideoEncoderSettings.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCVideoEncoderSettings.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/13. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface RongRTCVideoEncoderSettings : NSObject 14 | @property(nonatomic , copy)NSString *name; 15 | @property(nonatomic , assign)unsigned short width; 16 | @property(nonatomic , assign)unsigned short height; 17 | @property(nonatomic, assign) unsigned int startBitrate; 18 | @property(nonatomic, assign) unsigned int maxBitrate; 19 | @property(nonatomic, assign) unsigned int minBitrate; 20 | @property(nonatomic, assign) uint32_t maxFramerate; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/RongRTCCodecProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCCodecProtocol.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol RongRTCCodecProtocol 14 | 15 | #pragma mark - decoder 16 | 17 | /// 获取解码后的数据 18 | /// @param pixelBuffer 解码后的数据 19 | - (void)didGetDecodeBuffer:(CVPixelBufferRef)pixelBuffer; 20 | 21 | #pragma mark - encoder 22 | 23 | 24 | /// sps pps 数据 25 | /// @param sps sps 26 | /// @param pps pps 27 | - (void)spsData:(NSData *)sps ppsData:(NSData *)pps; 28 | 29 | /// nalu 数据 30 | /// @param naluData nalu 数据 31 | - (void)naluData:(NSData *)naluData; 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/Server/RongRTCServerSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCServerSocket.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/7. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RongRTCSocket.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @protocol RongRTCServerSocketProtocol; 15 | 16 | @interface RongRTCServerSocket : RongRTCSocket 17 | 18 | /** 19 | delegate 20 | */ 21 | @property(nonatomic , weak)id delegate; 22 | 23 | /// 创建 server socket 24 | - (BOOL)createServerSocket; 25 | 26 | @end 27 | 28 | 29 | @protocol RongRTCServerSocketProtocol 30 | 31 | /// 解码数据回调 32 | /// @param sampleBuffer 解码数据 33 | - (void)didProcessSampleBuffer:(CMSampleBufferRef)sampleBuffer; 34 | 35 | @end 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_ReplykitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_ReplykitUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/RongRTCBufferUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCBufferUtil.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/8. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface RongRTCBufferUtil : NSObject 15 | + (UIImage *)compressImage:(UIImage *)image newWidth:(CGFloat)newImageWidth; 16 | + (size_t)getCMTimeSize; 17 | +(CVPixelBufferRef)CVPixelBufferRefFromUiImage:(UIImage *)img; 18 | + (CMSampleBufferRef)sampleBufferFromPixbuffer:(CVPixelBufferRef)pixbuffer timeData:(NSData *)data; 19 | + (CMSampleBufferRef)sampleBufferFromPixbuffer:(CVPixelBufferRef)pixbuffer time:(CMTime)time; 20 | + (UIImage *)imageFromBuffer:(CMSampleBufferRef)buffer; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/RongRTCSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // RTCSocket.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/7. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | 14 | #import "RongRTCBufferUtil.h" 15 | NS_ASSUME_NONNULL_BEGIN 16 | #define CONNECTPORT 8888 17 | #define LOCK(lock) pthread_mutex_lock(&(lock)); 18 | 19 | #define UNLOCK(lock) pthread_mutex_lock(&(lock));; 20 | @interface RongRTCSocket : NSObject 21 | - (int)createSocket; 22 | - (NSString *)ip; 23 | @property (nonatomic, assign) int sock; 24 | - (BOOL)connect; 25 | - (BOOL)bind; 26 | - (BOOL)listen; 27 | - (void)receive; 28 | - (void)recvData; 29 | - (void)close; 30 | - (void)setSendBuffer; 31 | - (void)setRecvBuffer; 32 | - (void)setSendingTimeout; 33 | - (void)setRecvTimeout; 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_ReplykitTests/Socket_ReplykitTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // Socket_ReplykitTests.m 3 | // Socket_ReplykitTests 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Socket_ReplykitTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation Socket_ReplykitTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Socket_Replykit 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | if (@available(iOS 13,*)) { 20 | return YES; 21 | } else { 22 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 23 | UINavigationController *rootNavgationController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 24 | self.window.rootViewController = rootNavgationController; 25 | [self.window makeKeyAndVisible]; 26 | return YES; 27 | } 28 | 29 | 30 | return YES; 31 | } 32 | 33 | 34 | #pragma mark - UISceneSession lifecycle 35 | 36 | 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/RongRTCCodec.h: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCCodec.h 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import "RongRTCVideoEncoderSettings.h" 13 | #import "RongRTCCodecProtocol.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @interface RongRTCCodec : NSObject 18 | 19 | /// 编解码配置 20 | @property(nonatomic , strong , readonly)RongRTCVideoEncoderSettings *settings; 21 | 22 | /// 回调队列 23 | @property(nonatomic , strong , readonly)dispatch_queue_t callbackQueue; 24 | 25 | /// 代理 26 | @property(nonatomic , weak)id delegate; 27 | 28 | /// 使用编解码器之前的配置 29 | /// @param settings 配置 30 | /// @param queue 是否在指定队列里面回调代理方法,如果不传,默认在主线程回调代理方法 31 | - (BOOL)configWithSettings:(RongRTCVideoEncoderSettings *)settings onQueue:(dispatch_queue_t)queue; 32 | 33 | /// 编码 34 | /// @param sampleBuffer 编码 buffer 35 | - (void)encode:(CMSampleBufferRef)sampleBuffer ; 36 | 37 | /// 解码 38 | /// @param data 需要解码的数据 39 | -(void)decode:(NSData *)data; 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | -------------------------------------------------------------------------------- /Socket_Replykit/SocketReply/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | SocketReply 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionPointIdentifier 26 | com.apple.broadcast-services-upload 27 | NSExtensionPrincipalClass 28 | SampleHandler 29 | RPBroadcastProcessMode 30 | RPBroadcastProcessModeSampleBuffer 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Socket_Replykit/SocketReplySetupUI/BroadcastSetupViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BroadcastSetupViewController.m 3 | // SocketReplySetupUI 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "BroadcastSetupViewController.h" 10 | 11 | @implementation BroadcastSetupViewController 12 | 13 | // Call this method when the user has finished interacting with the view controller and a broadcast stream can start 14 | - (void)userDidFinishSetup { 15 | 16 | // URL of the resource where broadcast can be viewed that will be returned to the application 17 | NSURL *broadcastURL = [NSURL URLWithString:@"http://apple.com/broadcast/streamID"]; 18 | 19 | // Dictionary with setup information that will be provided to broadcast extension when broadcast is started 20 | NSDictionary *setupInfo = @{ @"broadcastName" : @"example" }; 21 | 22 | // Tell ReplayKit that the extension is finished setting up and can begin broadcasting 23 | [self.extensionContext completeRequestWithBroadcastURL:broadcastURL setupInfo:setupInfo]; 24 | } 25 | 26 | - (void)userDidCancelSetup { 27 | // Tell ReplayKit that the extension was cancelled by the user 28 | [self.extensionContext cancelRequestWithError:[NSError errorWithDomain:@"YourAppDomain" code:-1 userInfo:nil]]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Socket_Replykit/SocketReplySetupUI/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | SocketReplySetupUI 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | NSExtensionActivationRule 28 | 29 | NSExtensionActivationSupportsReplayKitStreaming 30 | 31 | 32 | 33 | NSExtensionPointIdentifier 34 | com.apple.broadcast-services-setupui 35 | NSExtensionPrincipalClass 36 | BroadcastSetupViewController 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/helpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // helpers.h 3 | // SCXVideoEncoder 4 | // 5 | // Created by 孙承秀 on 2019/11/22. 6 | // Copyright © 2019 RongCloud. All rights reserved. 7 | // 8 | 9 | #ifndef helpers_h 10 | #define helpers_h 11 | #include 12 | #include 13 | #include 14 | 15 | inline CFDictionaryRef CreateCFTypeDictionary(CFTypeRef* keys, 16 | CFTypeRef* values, 17 | size_t size) { 18 | return CFDictionaryCreate(kCFAllocatorDefault, keys, values, size, 19 | &kCFTypeDictionaryKeyCallBacks, 20 | &kCFTypeDictionaryValueCallBacks); 21 | } 22 | // Convenience function for setting a VT property. 23 | void SetVTSessionProperty(VTSessionRef session, CFStringRef key, int32_t value); 24 | 25 | // Convenience function for setting a VT property. 26 | void SetVTSessionProperty(VTSessionRef session, 27 | CFStringRef key, 28 | uint32_t value); 29 | 30 | // Convenience function for setting a VT property. 31 | void SetVTSessionProperty(VTSessionRef session, CFStringRef key, bool value); 32 | 33 | // Convenience function for setting a VT property. 34 | void SetVTSessionProperty(VTSessionRef session, 35 | CFStringRef key, 36 | CFStringRef value); 37 | 38 | #endif /* helpers_h */ 39 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_ReplykitUITests/Socket_ReplykitUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // Socket_ReplykitUITests.m 3 | // Socket_ReplykitUITests 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Socket_ReplykitUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation Socket_ReplykitUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | - (void)tearDown { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | } 29 | 30 | - (void)testExample { 31 | // UI tests must launch the application that they test. 32 | XCUIApplication *app = [[XCUIApplication alloc] init]; 33 | [app launch]; 34 | 35 | // Use recording to get started writing UI tests. 36 | // Use XCTAssert and related functions to verify your tests produce the correct results. 37 | } 38 | 39 | - (void)testLaunchPerformance { 40 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 41 | // This measures how long it takes to launch your application. 42 | [self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{ 43 | [[[XCUIApplication alloc] init] launch]; 44 | }]; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Socket_Replykit 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | #import "RongRTCServerSocket.h" 12 | @interface ViewController () 13 | @property (nonatomic, strong) RPSystemBroadcastPickerView *systemBroadcastPickerView; 14 | /** 15 | server socket 16 | */ 17 | @property(nonatomic , strong)RongRTCServerSocket *serverSocket; 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | // Do any additional setup after loading the view. 26 | [self.serverSocket createServerSocket]; 27 | self.systemBroadcastPickerView = [[RPSystemBroadcastPickerView alloc] initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, 80)]; 28 | self.systemBroadcastPickerView.preferredExtension = @"cn.rongcloud.sealrtc.RongRTCRP"; 29 | self.systemBroadcastPickerView.backgroundColor = [UIColor colorWithRed:53.0/255.0 green:129.0/255.0 blue:242.0/255.0 alpha:1.0]; 30 | self.systemBroadcastPickerView.showsMicrophoneButton = NO; 31 | [self.view addSubview:self.systemBroadcastPickerView]; 32 | } 33 | 34 | -(RongRTCServerSocket *)serverSocket{ 35 | if (!_serverSocket) { 36 | RongRTCServerSocket *socket = [[RongRTCServerSocket alloc] init]; 37 | socket.delegate = self; 38 | 39 | _serverSocket = socket; 40 | } 41 | return _serverSocket; 42 | } 43 | -(void)didProcessSampleBuffer:(CMSampleBufferRef)sampleBuffer{ 44 | // 这里拿到了最终的数据,比如最后可以使用融云的音视频SDK RTCLib 进行传输就可以了 45 | } 46 | @end 47 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Socket_Replykit/SocketReply/SampleHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // SampleHandler.m 3 | // SocketReply 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | 10 | #import "SampleHandler.h" 11 | #import "RongRTCClientSocket.h" 12 | @interface SampleHandler() 13 | 14 | /** 15 | client servert 16 | */ 17 | @property(nonatomic , strong)RongRTCClientSocket *clientSocket; 18 | @end 19 | @implementation SampleHandler 20 | 21 | - (void)broadcastStartedWithSetupInfo:(NSDictionary *)setupInfo { 22 | // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional. 23 | self.clientSocket = [[RongRTCClientSocket alloc] init]; 24 | [self.clientSocket createCliectSocket]; 25 | } 26 | 27 | - (void)broadcastPaused { 28 | // User has requested to pause the broadcast. Samples will stop being delivered. 29 | } 30 | 31 | - (void)broadcastResumed { 32 | // User has requested to resume the broadcast. Samples delivery will resume. 33 | } 34 | 35 | - (void)broadcastFinished { 36 | // User has requested to finish the broadcast. 37 | } 38 | 39 | - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { 40 | 41 | switch (sampleBufferType) { 42 | case RPSampleBufferTypeVideo: 43 | // Handle video sample buffer 44 | [self sendData:sampleBuffer]; 45 | break; 46 | case RPSampleBufferTypeAudioApp: 47 | // Handle audio sample buffer for app audio 48 | break; 49 | case RPSampleBufferTypeAudioMic: 50 | // Handle audio sample buffer for mic audio 51 | break; 52 | 53 | default: 54 | break; 55 | } 56 | } 57 | - (void)sendData:(CMSampleBufferRef)sampleBuffer{ 58 | 59 | [self.clientSocket encodeBuffer:sampleBuffer]; 60 | 61 | } 62 | @end 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | # CocoaPods 34 | # 35 | # We recommend against adding the Pods directory to your .gitignore. However 36 | # you should judge for yourself, the pros and cons are mentioned at: 37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 38 | # 39 | # Pods/ 40 | # 41 | # Add this line if you want to avoid checking in source code from the Xcode workspace 42 | # *.xcworkspace 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build/ 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. 54 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 57 | 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots/**/*.png 61 | fastlane/test_output 62 | 63 | # Code Injection 64 | # 65 | # After new code Injection tools there's a generated folder /iOSInjectionProject 66 | # https://github.com/johnno1962/injectionforxcode 67 | 68 | iOSInjectionProject/ 69 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/RongRTCThread.m: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCThread.m 3 | // test 4 | // 5 | // Created by 孙承秀 on 2020/5/12. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCThread.h" 10 | @interface RongRTCNSThread : NSThread 11 | 12 | @end 13 | @implementation RongRTCNSThread 14 | -(void)dealloc{ 15 | NSLog(@"rongrtc nsthread dealoc"); 16 | } 17 | 18 | @end 19 | @interface RongRTCThread() 20 | 21 | /** 22 | thread 23 | */ 24 | @property(nonatomic , strong)RongRTCNSThread *thread; 25 | 26 | /** 27 | stoped 28 | */ 29 | @property(nonatomic , assign)BOOL stoped; 30 | 31 | @end 32 | @implementation RongRTCThread 33 | -(instancetype)init{ 34 | if (self = [super init]) { 35 | self.stoped = NO; 36 | __weak typeof(self)weakSelf = self; 37 | self.thread = [[RongRTCNSThread alloc] initWithBlock:^{ 38 | NSLog(@"来了"); 39 | [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; 40 | while (weakSelf && !weakSelf.stoped) { 41 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 42 | } 43 | 44 | }]; 45 | } 46 | return self; 47 | } 48 | - (void)excuteTaskWithBlock:(Block)block{ 49 | if (!self.thread || !block) { 50 | return; 51 | } 52 | [self performSelector:@selector(_excuteTask:) onThread:self.thread withObject:block waitUntilDone:NO]; 53 | } 54 | - (void)run{ 55 | [self.thread start]; 56 | } 57 | - (void)stop{ 58 | if (!self.thread) { 59 | return; 60 | } 61 | [self performSelector:@selector(_stopRunloop) onThread:self.thread withObject:nil waitUntilDone:YES]; 62 | } 63 | - (void)_stopRunloop{ 64 | self.stoped = YES; 65 | CFRunLoopStop(CFRunLoopGetCurrent()); 66 | self.thread = nil; 67 | } 68 | - (void)_excuteTask:(void (^)(void))block{ 69 | block(); 70 | } 71 | -(void)dealloc{ 72 | NSLog(@"rongrtc thread dealoc"); 73 | [self stop]; 74 | } 75 | @end 76 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // Socket_Replykit 4 | // 5 | // Created by 孙承秀 on 2020/5/19. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "SceneDelegate.h" 10 | #import "ViewController.h" 11 | @interface SceneDelegate () 12 | 13 | @end 14 | 15 | @implementation SceneDelegate 16 | 17 | 18 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 19 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 20 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 21 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 22 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 23 | self.window.windowScene = (UIWindowScene*)scene; 24 | UINavigationController *rootNavgationController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 25 | self.window.rootViewController = rootNavgationController; 26 | [self.window makeKeyAndVisible]; 27 | 28 | } 29 | 30 | 31 | - (void)sceneDidDisconnect:(UIScene *)scene { 32 | // Called as the scene is being released by the system. 33 | // This occurs shortly after the scene enters the background, or when its session is discarded. 34 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 35 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 36 | } 37 | 38 | 39 | - (void)sceneDidBecomeActive:(UIScene *)scene { 40 | // Called when the scene has moved from an inactive state to an active state. 41 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 42 | } 43 | 44 | 45 | - (void)sceneWillResignActive:(UIScene *)scene { 46 | // Called when the scene will move from an active state to an inactive state. 47 | // This may occur due to temporary interruptions (ex. an incoming phone call). 48 | } 49 | 50 | 51 | - (void)sceneWillEnterForeground:(UIScene *)scene { 52 | // Called as the scene transitions from the background to the foreground. 53 | // Use this method to undo the changes made on entering the background. 54 | } 55 | 56 | 57 | - (void)sceneDidEnterBackground:(UIScene *)scene { 58 | // Called as the scene transitions from the foreground to the background. 59 | // Use this method to save data, release shared resources, and store enough scene-specific state information 60 | // to restore the scene back to its current state. 61 | } 62 | 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/helpers.mm: -------------------------------------------------------------------------------- 1 | // 2 | // helpers.c 3 | // SCXVideoEncoder 4 | // 5 | // Created by 孙承秀 on 2019/11/22. 6 | // Copyright © 2019 RongCloud. All rights reserved. 7 | // 8 | 9 | #include "helpers.h" 10 | #include 11 | #include 12 | #include 13 | // Copies characters from a CFStringRef into a std::string. 14 | std::string CFStringToString(const CFStringRef cf_string) { 15 | //RTC_DCHECK(cf_string); 16 | std::string std_string; 17 | // Get the size needed for UTF8 plus terminating character. 18 | size_t buffer_size = 19 | CFStringGetMaximumSizeForEncoding(CFStringGetLength(cf_string), 20 | kCFStringEncodingUTF8) + 21 | 1; 22 | std::unique_ptr buffer(new char[buffer_size]); 23 | if (CFStringGetCString(cf_string, buffer.get(), buffer_size, 24 | kCFStringEncodingUTF8)) { 25 | // Copy over the characters. 26 | std_string.assign(buffer.get()); 27 | } 28 | return std_string; 29 | } 30 | void SetVTSessionProperty(VTSessionRef session, 31 | CFStringRef key, 32 | int32_t value) { 33 | CFNumberRef cfNum = 34 | CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); 35 | OSStatus status = VTSessionSetProperty(session, key, cfNum); 36 | CFRelease(cfNum); 37 | if (status != noErr) { 38 | std::string key_string = CFStringToString(key); 39 | //RTC_LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string 40 | // << " to " << value << ": " << status; 41 | } 42 | } 43 | 44 | // Convenience function for setting a VT property. 45 | void SetVTSessionProperty(VTSessionRef session, 46 | CFStringRef key, 47 | uint32_t value) { 48 | int64_t value_64 = value; 49 | CFNumberRef cfNum = 50 | CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value_64); 51 | OSStatus status = VTSessionSetProperty(session, key, cfNum); 52 | CFRelease(cfNum); 53 | if (status != noErr) { 54 | std::string key_string = CFStringToString(key); 55 | //RTC_LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string 56 | // << " to " << value << ": " << status; 57 | } 58 | } 59 | 60 | // Convenience function for setting a VT property. 61 | void SetVTSessionProperty(VTSessionRef session, CFStringRef key, bool value) { 62 | CFBooleanRef cf_bool = (value) ? kCFBooleanTrue : kCFBooleanFalse; 63 | OSStatus status = VTSessionSetProperty(session, key, cf_bool); 64 | if (status != noErr) { 65 | std::string key_string = CFStringToString(key); 66 | //RTC_LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string 67 | // << " to " << value << ": " << status; 68 | } 69 | } 70 | 71 | // Convenience function for setting a VT property. 72 | void SetVTSessionProperty(VTSessionRef session, 73 | CFStringRef key, 74 | CFStringRef value) { 75 | OSStatus status = VTSessionSetProperty(session, key, value); 76 | if (status != noErr) { 77 | std::string key_string = CFStringToString(key); 78 | std::string val_string = CFStringToString(value); 79 | //RTC_LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string 80 | // << " to " << val_string << ": " << status; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/Client/RongRTCClientSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCClientSocket.m 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/7. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCClientSocket.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import "RongRTCThread.h" 16 | #import "RongRTCSocketHeader.h" 17 | #import "RongRTCVideoEncoder.h" 18 | @interface RongRTCClientSocket(){ 19 | pthread_mutex_t lock; 20 | } 21 | 22 | /** 23 | video encoder 24 | */ 25 | @property(nonatomic , strong)RongRTCVideoEncoder *encoder; 26 | 27 | /** 28 | encode queue 29 | */ 30 | @property(nonatomic , strong)dispatch_queue_t encodeQueue; 31 | @end 32 | @implementation RongRTCClientSocket 33 | - (BOOL)createCliectSocket{ 34 | if ([self createSocket] == -1) { 35 | return NO; 36 | } 37 | BOOL isC = [self connect]; 38 | [self setSendBuffer]; 39 | [self setSendingTimeout]; 40 | if (isC) { 41 | _encodeQueue = dispatch_queue_create("com.rongcloud.encodequeue", NULL); 42 | [self createVideoEncoder]; 43 | return YES; 44 | } else { 45 | return NO; 46 | } 47 | } 48 | - (void)createVideoEncoder{ 49 | self.encoder = [[RongRTCVideoEncoder alloc] init]; 50 | self.encoder.delegate = self; 51 | RongRTCVideoEncoderSettings *settings = [[RongRTCVideoEncoderSettings alloc] init]; 52 | settings.width = 720; 53 | settings.height = 1280; 54 | settings.startBitrate = 300; 55 | settings.maxFramerate = 30; 56 | settings.minBitrate = 1000; 57 | [self.encoder configWithSettings:settings onQueue:_encodeQueue]; 58 | } 59 | -(void)cliectSend:(NSData *)data{ 60 | 61 | //data length 62 | NSUInteger dataLength = data.length; 63 | 64 | // data header struct 65 | DataHeader dataH; 66 | memset((void *)&dataH, 0, sizeof(dataH)); 67 | 68 | // pre 69 | PreHeader preH; 70 | memset((void *)&preH, 0, sizeof(preH)); 71 | preH.pre[0] = '&'; 72 | preH.dataLength = dataLength; 73 | 74 | dataH.preH = preH; 75 | 76 | // buffer 77 | int headerlength = sizeof(dataH); 78 | int totalLength = dataLength + headerlength; 79 | 80 | // srcbuffer 81 | Byte *src = (Byte *)[data bytes]; 82 | 83 | // send buffer 84 | char *buffer = (char *)malloc(totalLength * sizeof(char)); 85 | memcpy(buffer, &dataH, headerlength); 86 | memcpy(buffer + headerlength, src, dataLength); 87 | 88 | // tosend 89 | [self sendBytes:buffer length:totalLength]; 90 | free(buffer); 91 | 92 | } 93 | - (void)encodeBuffer:(CMSampleBufferRef)sampleBuffer{ 94 | [self.encoder encode:sampleBuffer]; 95 | } 96 | 97 | - (void)sendBytes:(char *)bytes length:(int )length { 98 | LOCK(self->lock); 99 | int hasSendLength = 0; 100 | while (hasSendLength < length) { 101 | // connect socket success 102 | if (self.sock > 0) { 103 | // send 104 | int sendRes = send(self.sock, bytes, length - hasSendLength, 0); 105 | if (sendRes == -1 || sendRes == 0) { 106 | UNLOCK(self->lock); 107 | NSLog(@"😁😁😁😁😁send buffer error"); 108 | [self close]; 109 | break; 110 | } 111 | hasSendLength += sendRes; 112 | bytes += sendRes; 113 | 114 | } else { 115 | NSLog(@"😁😁😁😁😁client socket connect error"); 116 | UNLOCK(self->lock); 117 | } 118 | } 119 | UNLOCK(self->lock); 120 | 121 | } 122 | -(void)spsData:(NSData *)sps ppsData:(NSData *)pps{ 123 | [self cliectSend:sps]; 124 | [self cliectSend:pps]; 125 | } 126 | -(void)naluData:(NSData *)naluData{ 127 | [self cliectSend:naluData]; 128 | } 129 | -(void)dealloc{ 130 | 131 | NSLog(@"😁😁😁😁😁dealoc cliect socket"); 132 | } 133 | @end 134 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/RongRTCBufferUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCBufferUtil.m 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/8. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCBufferUtil.h" 10 | 11 | /// 下面的这些方法,一定要记得release,有的没有在方法里面release,但是在外面release了,要不然会内存泄漏 12 | @implementation RongRTCBufferUtil 13 | + (UIImage *)imageFromBuffer:(CMSampleBufferRef)buffer { 14 | 15 | CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(buffer); 16 | 17 | CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer]; 18 | 19 | CIContext *temporaryContext = [CIContext contextWithOptions:nil]; 20 | CGImageRef videoImage = [temporaryContext createCGImage:ciImage fromRect:CGRectMake(0, 0, CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer))]; 21 | 22 | UIImage *image = [UIImage imageWithCGImage:videoImage]; 23 | CGImageRelease(videoImage); 24 | 25 | return image; 26 | } 27 | 28 | + (UIImage *)compressImage:(UIImage *)image newWidth:(CGFloat)newImageWidth 29 | { 30 | if (!image) return nil; 31 | float imageWidth = image.size.width; 32 | float imageHeight = image.size.height; 33 | float width = newImageWidth; 34 | float height = image.size.height/(image.size.width/width); 35 | float widthScale = imageWidth /width; 36 | float heightScale = imageHeight /height; 37 | UIGraphicsBeginImageContext(CGSizeMake(width, height)); 38 | if (widthScale > heightScale) { 39 | [image drawInRect:CGRectMake(0, 0, imageWidth /heightScale , height)]; 40 | } 41 | else { 42 | [image drawInRect:CGRectMake(0, 0, width , imageHeight /widthScale)]; 43 | } 44 | UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 45 | UIGraphicsEndImageContext(); 46 | return newImage; 47 | 48 | } 49 | +(CVPixelBufferRef)CVPixelBufferRefFromUiImage:(UIImage *)img { 50 | 51 | CGSize size = img.size; 52 | CGImageRef image = [img CGImage]; 53 | 54 | NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 55 | [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, 56 | [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil]; 57 | CVPixelBufferRef pxbuffer = NULL; 58 | CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options, &pxbuffer); 59 | 60 | NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); 61 | 62 | CVPixelBufferLockBaseAddress(pxbuffer, 0); 63 | void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); 64 | NSParameterAssert(pxdata != NULL); 65 | 66 | CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 67 | CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, 4*size.width, rgbColorSpace, kCGImageAlphaPremultipliedFirst); 68 | NSParameterAssert(context); 69 | 70 | CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); 71 | 72 | CGColorSpaceRelease(rgbColorSpace); 73 | CGContextRelease(context); 74 | 75 | CVPixelBufferUnlockBaseAddress(pxbuffer, 0); 76 | 77 | return pxbuffer; 78 | } 79 | + (CMSampleBufferRef)sampleBufferFromPixbuffer:(CVPixelBufferRef)pixbuffer time:(CMTime)time{ 80 | 81 | CMSampleBufferRef sampleBuffer = NULL; 82 | 83 | // //获取视频信息 84 | CMVideoFormatDescriptionRef videoInfo = NULL; 85 | OSStatus result = CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixbuffer, &videoInfo); 86 | CMTime currentTime = time; 87 | 88 | // CMSampleTimingInfo timing = {currentTime, currentTime, kCMTimeInvalid}; 89 | CMSampleTimingInfo timing = {currentTime, currentTime, kCMTimeInvalid}; 90 | result = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault,pixbuffer, true, NULL, NULL, videoInfo, &timing, &sampleBuffer); 91 | CFRelease(videoInfo); 92 | return sampleBuffer; 93 | } 94 | 95 | + (size_t)getCMTimeSize{ 96 | size_t size = sizeof(CMTime); 97 | return size; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/Server/RongRTCServerSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCServerSocket.m 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/7. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCServerSocket.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | 18 | #import "RongRTCThread.h" 19 | #import "RongRTCSocketHeader.h" 20 | #import "RongRTCVideoDecoder.h" 21 | @interface RongRTCServerSocket() 22 | { 23 | pthread_mutex_t lock; 24 | int _frameTime; 25 | CMTime _lastPresentationTime; 26 | Float64 _currentMediaTime; 27 | Float64 _currentVideoTime; 28 | dispatch_queue_t _frameQueue; 29 | } 30 | @property (nonatomic, assign) int acceptSock; 31 | 32 | /** 33 | data length 34 | */ 35 | @property(nonatomic , assign)NSUInteger dataLength; 36 | 37 | /** 38 | timeData 39 | */ 40 | @property(nonatomic , strong)NSData *timeData; 41 | 42 | /** 43 | decoder queue 44 | */ 45 | @property(nonatomic , strong)dispatch_queue_t decoderQueue; 46 | 47 | /** 48 | decoder 49 | */ 50 | @property(nonatomic , strong)RongRTCVideoDecoder *decoder; 51 | @end 52 | @implementation RongRTCServerSocket 53 | 54 | - (BOOL)createServerSocket{ 55 | if ([self createSocket] == -1) { 56 | return NO; 57 | } 58 | [self setRecvBuffer]; 59 | [self setRecvTimeout]; 60 | BOOL isB = [self bind]; 61 | BOOL isL = [self listen]; 62 | 63 | if (isB && isL) { 64 | _decoderQueue = dispatch_queue_create("com.rongcloud.decoderQueue", NULL); 65 | _frameTime = 0; 66 | [self createDecoder]; 67 | [self receive]; 68 | return YES; 69 | } else { 70 | return NO; 71 | } 72 | } 73 | - (void)createDecoder{ 74 | self.decoder = [[RongRTCVideoDecoder alloc] init]; 75 | self.decoder.delegate = self; 76 | RongRTCVideoEncoderSettings *settings = [[RongRTCVideoEncoderSettings alloc] init]; 77 | settings.width = 720; 78 | settings.height = 1280; 79 | settings.startBitrate = 300; 80 | settings.maxFramerate = 30; 81 | settings.minBitrate = 1000; 82 | [self.decoder configWithSettings:settings onQueue:_decoderQueue]; 83 | } 84 | -(void)recvData{ 85 | struct sockaddr_in rest; 86 | socklen_t rest_size = sizeof(struct sockaddr_in); 87 | self.acceptSock = accept(self.sock, (struct sockaddr *) &rest, &rest_size); 88 | while (self.acceptSock != -1) { 89 | DataHeader dataH; 90 | memset(&dataH, 0, sizeof(dataH)); 91 | 92 | if (![self receveData:(char *)&dataH length:sizeof(dataH)]) { 93 | continue; 94 | } 95 | PreHeader preH = dataH.preH; 96 | char pre = preH.pre[0]; 97 | if (pre == '&') { 98 | // rongcloud socket 99 | NSUInteger dataLenght = preH.dataLength; 100 | char *buff = (char *)malloc(sizeof(char) * dataLenght); 101 | if ([self receveData:(char *)buff length:dataLenght]) { 102 | NSData *data = [NSData dataWithBytes:buff length:dataLenght]; 103 | [self.decoder decode:data]; 104 | free(buff); 105 | } 106 | } else { 107 | NSLog(@"😁😁😁😁😁pre is not &"); 108 | return; 109 | } 110 | } 111 | } 112 | - (BOOL)receveData:(char *)data length:(NSUInteger)length{ 113 | LOCK(lock); 114 | int recvLength = 0; 115 | while (recvLength < length) { 116 | ssize_t res = recv(self.acceptSock, data, length - recvLength, 0); 117 | if (res == -1 || res == 0) { 118 | UNLOCK(lock); 119 | NSLog(@"😁😁😁😁😁recv data error"); 120 | break; 121 | } 122 | recvLength += res; 123 | data += res; 124 | } 125 | UNLOCK(lock); 126 | return YES; 127 | } 128 | 129 | -(void)didGetDecodeBuffer:(CVPixelBufferRef)pixelBuffer { 130 | _frameTime += 1000; 131 | CMTime pts = CMTimeMake(_frameTime, 1000); 132 | CMSampleBufferRef sampleBuffer = [RongRTCBufferUtil sampleBufferFromPixbuffer:pixelBuffer time:pts]; 133 | // 查看解码数据是否有问题,如果image能显示,就说明对了。 134 | // 通过打断点 将鼠标放在 iamge 脑袋上,就可以看到数据了,点击那个小眼睛 135 | UIImage *image = [RongRTCBufferUtil imageFromBuffer:sampleBuffer]; 136 | [self.delegate didProcessSampleBuffer:sampleBuffer]; 137 | CFRelease(sampleBuffer); 138 | } 139 | 140 | -(void)close{ 141 | int res = close(self.acceptSock); 142 | self.acceptSock = -1; 143 | NSLog(@"😁😁😁😁😁shut down server: %d",res); 144 | [super close]; 145 | } 146 | -(void)dealloc{ 147 | NSLog(@"😁😁😁😁😁dealoc server socket"); 148 | } 149 | @end 150 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Socket/RongRTCSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCSocket.m 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/7. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCSocket.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import "RongRTCThread.h" 16 | @interface RongRTCSocket() 17 | 18 | /** 19 | rec thread 20 | */ 21 | @property(nonatomic , strong)RongRTCThread *recvThread; 22 | @end 23 | @implementation RongRTCSocket 24 | - (int)createSocket{ 25 | int sock = socket(AF_INET, SOCK_STREAM, 0); 26 | self.sock = sock; 27 | if (self.sock == -1) { 28 | close(self.sock); 29 | NSLog(@"😁😁😁😁😁socket error : %d",self.sock); 30 | } 31 | self.recvThread = [[RongRTCThread alloc] init]; 32 | [self.recvThread run]; 33 | return sock; 34 | } 35 | - (void)setSendBuffer{ 36 | int optVal = 1024 * 1024 * 2; 37 | int optLen = sizeof(int); 38 | int res = setsockopt(self.sock, SOL_SOCKET,SO_SNDBUF,(char*)&optVal,optLen ); 39 | NSLog(@"😁😁😁😁😁set send buffer:%d",res); 40 | } 41 | - (void)setRecvBuffer{ 42 | int optVal = 1024 * 1024 * 2; 43 | int optLen = sizeof(int); 44 | int res = setsockopt(self.sock, SOL_SOCKET,SO_RCVBUF,(char*)&optVal,optLen );; 45 | NSLog(@"😁😁😁😁😁set send buffer:%d",res); 46 | } 47 | - (void)setSendingTimeout{ 48 | struct timeval timeout = {10,0}; 49 | int res = setsockopt(self.sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int)); 50 | NSLog(@"😁😁😁😁😁set send timeout:%d",res); 51 | } 52 | - (void)setRecvTimeout{ 53 | struct timeval timeout = {10,0}; 54 | int res = setsockopt(self.sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int)); 55 | NSLog(@"😁😁😁😁😁set send timeout:%d",res); 56 | } 57 | - (BOOL)connect{ 58 | NSString *serverHost = [self ip]; 59 | struct hostent *server = gethostbyname([serverHost UTF8String]); 60 | if (server == NULL) { 61 | close(self.sock); 62 | NSLog(@"😁😁😁😁😁get host error"); 63 | return NO; 64 | } 65 | 66 | struct in_addr *remoteAddr = (struct in_addr *)server->h_addr_list[0]; 67 | struct sockaddr_in addr; 68 | addr.sin_family = AF_INET; 69 | addr.sin_addr = *remoteAddr; 70 | addr.sin_port = htons(CONNECTPORT); 71 | int res = connect(self.sock, (struct sockaddr *) &addr, sizeof(addr)); 72 | if (res == -1) { 73 | close(self.sock); 74 | NSLog(@"😁😁😁😁😁connect error"); 75 | return NO; 76 | } 77 | NSLog(@"😁😁😁😁😁socket connect to server success"); 78 | return YES; 79 | } 80 | - (BOOL)bind{ 81 | struct sockaddr_in client; 82 | client.sin_family = AF_INET; 83 | NSString *ipStr = [self ip]; 84 | if (ipStr.length <= 0) { 85 | return NO; 86 | } 87 | const char *ip = [ipStr cStringUsingEncoding:NSASCIIStringEncoding]; 88 | client.sin_addr.s_addr = inet_addr(ip); 89 | client.sin_port = htons(CONNECTPORT); 90 | int bd = bind(self.sock, (struct sockaddr *) &client, sizeof(client)); 91 | if (bd == -1) { 92 | close(self.sock); 93 | NSLog(@"😁😁😁😁😁bind error : %d",bd); 94 | return NO; 95 | } 96 | return YES; 97 | } 98 | 99 | - (BOOL)listen{ 100 | int ls = listen(self.sock, 128); 101 | if (ls == -1) { 102 | close(self.sock); 103 | NSLog(@"😁😁😁😁😁listen error : %d",ls); 104 | return NO; 105 | } 106 | return YES; 107 | } 108 | - (void)receive{ 109 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 110 | [self recvData]; 111 | }); 112 | } 113 | - (NSString *)ip{ 114 | NSString *ip = nil; 115 | struct ifaddrs *addrs = NULL; 116 | struct ifaddrs *tmpAddrs = NULL; 117 | BOOL res = getifaddrs(&addrs); 118 | if (res == 0) { 119 | tmpAddrs = addrs; 120 | while (tmpAddrs != NULL) { 121 | if(tmpAddrs->ifa_addr->sa_family == AF_INET) { 122 | // Check if interface is en0 which is the wifi connection on the iPhone 123 | NSLog(@"%@",[NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)tmpAddrs->ifa_addr)->sin_addr)]); 124 | if([[NSString stringWithUTF8String:tmpAddrs->ifa_name] isEqualToString:@"en0"]) { 125 | // Get NSString from C String 126 | ip = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)tmpAddrs->ifa_addr)->sin_addr)]; 127 | } 128 | } 129 | tmpAddrs = tmpAddrs->ifa_next; 130 | } 131 | } 132 | // Free memory 133 | freeifaddrs(addrs); 134 | NSLog(@"😁😁😁😁😁%@",ip); 135 | return ip; 136 | } 137 | -(void)close{ 138 | int res = close(self.sock); 139 | NSLog(@"😁😁😁😁😁shut down : %d",res); 140 | } 141 | - (void)recvData{ 142 | 143 | } 144 | -(void)dealloc{ 145 | [self.recvThread stop]; 146 | } 147 | @end 148 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/Decoder/RongRTCVideoDecoder.mm: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCVideoDecoder.m 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/14. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCVideoDecoder.h" 10 | #import 11 | 12 | #import "helpers.h" 13 | @interface RongRTCVideoDecoder(){ 14 | uint8_t *_sps; 15 | NSUInteger _spsSize; 16 | uint8_t *_pps; 17 | NSUInteger _ppsSize; 18 | CMVideoFormatDescriptionRef _videoFormatDescription; 19 | VTDecompressionSessionRef _decompressionSession; 20 | dispatch_queue_t _decodeQueue; 21 | } 22 | /** 23 | settings 24 | */ 25 | @property(nonatomic , strong )RongRTCVideoEncoderSettings *settings; 26 | 27 | /** 28 | callback queue 29 | */ 30 | @property(nonatomic , strong )dispatch_queue_t callbackQueue; 31 | 32 | 33 | @end 34 | void DecoderOutputCallback(void * CM_NULLABLE decompressionOutputRefCon, 35 | void * CM_NULLABLE sourceFrameRefCon, 36 | OSStatus status, 37 | VTDecodeInfoFlags infoFlags, 38 | CM_NULLABLE CVImageBufferRef imageBuffer, 39 | CMTime presentationTimeStamp, 40 | CMTime presentationDuration ) { 41 | if (status != noErr) { 42 | NSLog(@"😁 decoder callback error :%@", @(status)); 43 | return; 44 | } 45 | CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon; 46 | *outputPixelBuffer = CVPixelBufferRetain(imageBuffer); 47 | RongRTCVideoDecoder *decoder = (__bridge RongRTCVideoDecoder *)(decompressionOutputRefCon); 48 | if (decoder && decoder.callbackQueue) { 49 | dispatch_async(decoder.callbackQueue, ^{ 50 | if (decoder.delegate && [decoder.delegate respondsToSelector:@selector(didGetDecodeBuffer:)]) { 51 | [decoder.delegate didGetDecodeBuffer:imageBuffer]; 52 | } 53 | CVPixelBufferRelease(imageBuffer); 54 | }); 55 | } 56 | } 57 | @implementation RongRTCVideoDecoder 58 | 59 | @synthesize settings = _settings; 60 | @synthesize callbackQueue = _callbackQueue; 61 | 62 | 63 | -(BOOL)configWithSettings:(RongRTCVideoEncoderSettings *)settings onQueue:(dispatch_queue_t)queue{ 64 | self.settings = settings; 65 | if (queue) { 66 | _callbackQueue = queue; 67 | } else { 68 | _callbackQueue = dispatch_get_main_queue(); 69 | } 70 | _decodeQueue = dispatch_queue_create("com.rongcloud.encodeQueue", NULL); 71 | return YES; 72 | } 73 | - (BOOL)createVT{ 74 | if (_decompressionSession) { 75 | return YES; 76 | } 77 | const uint8_t * const parameterSetPointers[2] = {_sps, _pps}; 78 | const size_t parameterSetSizes[2] = {_spsSize, _ppsSize}; 79 | int naluHeaderLen = 4; 80 | OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault, 2, parameterSetPointers, parameterSetSizes, naluHeaderLen, &_videoFormatDescription ); 81 | if (status != noErr) { 82 | NSLog(@"😁😁😁😁😁CMVideoFormatDescriptionCreateFromH264ParameterSets error:%@", @(status)); 83 | return false; 84 | } 85 | NSDictionary *destinationImageBufferAttributes = 86 | @{ 87 | (id)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange], 88 | (id)kCVPixelBufferWidthKey: [NSNumber numberWithInteger:self.settings.width], 89 | (id)kCVPixelBufferHeightKey: [NSNumber numberWithInteger:self.settings.height], 90 | (id)kCVPixelBufferOpenGLCompatibilityKey: [NSNumber numberWithBool:true] 91 | }; 92 | VTDecompressionOutputCallbackRecord CallBack; 93 | CallBack.decompressionOutputCallback = DecoderOutputCallback; 94 | CallBack.decompressionOutputRefCon = (__bridge void * _Nullable)(self); 95 | status = VTDecompressionSessionCreate(kCFAllocatorDefault, _videoFormatDescription, NULL, (__bridge CFDictionaryRef _Nullable)(destinationImageBufferAttributes), &CallBack, &_decompressionSession); 96 | 97 | if (status != noErr) { 98 | NSLog(@"😁😁😁😁😁VTDecompressionSessionCreate error:%@", @(status)); 99 | return false; 100 | } 101 | status = VTSessionSetProperty(_decompressionSession, kVTDecompressionPropertyKey_RealTime,kCFBooleanTrue); 102 | 103 | return YES; 104 | } 105 | 106 | - (CVPixelBufferRef)decode:(uint8_t *)frame withSize:(uint32_t)frameSize { 107 | 108 | CVPixelBufferRef outputPixelBuffer = NULL; 109 | CMBlockBufferRef blockBuffer = NULL; 110 | CMBlockBufferFlags flag0 = 0; 111 | 112 | OSStatus status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, frame, frameSize, kCFAllocatorNull, NULL, 0, frameSize, flag0, &blockBuffer); 113 | 114 | if (status != kCMBlockBufferNoErr) { 115 | NSLog(@"😁😁😁😁😁VCMBlockBufferCreateWithMemoryBlock code=%d", (int)status); 116 | CFRelease(blockBuffer); 117 | return outputPixelBuffer; 118 | } 119 | 120 | CMSampleBufferRef sampleBuffer = NULL; 121 | const size_t sampleSizeArray[] = {frameSize}; 122 | 123 | status = CMSampleBufferCreateReady(kCFAllocatorDefault, blockBuffer, _videoFormatDescription, 1, 0, NULL, 1, sampleSizeArray, &sampleBuffer); 124 | 125 | if (status != noErr || !sampleBuffer) { 126 | NSLog(@"😁😁😁😁😁CMSampleBufferCreateReady failed status=%d", (int)status); 127 | CFRelease(blockBuffer); 128 | return outputPixelBuffer; 129 | } 130 | 131 | VTDecodeFrameFlags flag1 = kVTDecodeFrame_1xRealTimePlayback; 132 | VTDecodeInfoFlags infoFlag = kVTDecodeInfo_Asynchronous; 133 | 134 | status = VTDecompressionSessionDecodeFrame(_decompressionSession, sampleBuffer, flag1, &outputPixelBuffer, &infoFlag); 135 | 136 | if (status == kVTInvalidSessionErr) { 137 | NSLog(@"😁😁😁😁😁decode frame error with session err status =%d", (int)status); 138 | [self resetVT]; 139 | } else { 140 | if (status != noErr) { 141 | NSLog(@"😁😁😁😁😁decode frame error with status =%d", (int)status); 142 | } 143 | 144 | } 145 | 146 | CFRelease(sampleBuffer); 147 | CFRelease(blockBuffer); 148 | 149 | return outputPixelBuffer; 150 | } 151 | - (void)resetVT{ 152 | [self destorySession]; 153 | [self createVT]; 154 | } 155 | -(void)decode:(NSData *)data{ 156 | dispatch_async(_decodeQueue, ^{ 157 | uint8_t *frame = (uint8_t*)[data bytes]; 158 | uint32_t length = (uint32_t)data.length; 159 | uint32_t nalSize = (uint32_t)(length - 4); 160 | uint32_t *pNalSize = (uint32_t *)frame; 161 | *pNalSize = CFSwapInt32HostToBig(nalSize); 162 | 163 | int type = (frame[4] & 0x1F); 164 | CVPixelBufferRef pixelBuffer = NULL; 165 | switch (type) { 166 | case 0x05: 167 | if ([self createVT]) { 168 | pixelBuffer= [self decode:frame withSize:length]; 169 | } 170 | break; 171 | case 0x07: 172 | self->_spsSize = length - 4; 173 | self->_sps = (uint8_t *)malloc(self->_spsSize); 174 | memcpy(self->_sps, &frame[4], self->_spsSize); 175 | break; 176 | case 0x08: 177 | self->_ppsSize = length - 4; 178 | self->_pps = (uint8_t *)malloc(self->_ppsSize); 179 | memcpy(self->_pps, &frame[4], self->_ppsSize); 180 | break; 181 | default: 182 | if ([self createVT]) { 183 | pixelBuffer = [self decode:frame withSize:length]; 184 | } 185 | break; 186 | } 187 | }); 188 | } 189 | 190 | - (void)dealloc 191 | { 192 | [self destorySession]; 193 | 194 | } 195 | - (void)destorySession{ 196 | if (_decompressionSession) { 197 | VTDecompressionSessionInvalidate(_decompressionSession); 198 | CFRelease(_decompressionSession); 199 | _decompressionSession = NULL; 200 | } 201 | } 202 | @end 203 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/Codec/Encoder/RongRTCVideoEncoder.mm: -------------------------------------------------------------------------------- 1 | // 2 | // RongRTCVideoEncoder.m 3 | // SealRTC 4 | // 5 | // Created by 孙承秀 on 2020/5/13. 6 | // Copyright © 2020 RongCloud. All rights reserved. 7 | // 8 | 9 | #import "RongRTCVideoEncoder.h" 10 | 11 | #import "helpers.h" 12 | 13 | @interface RongRTCVideoEncoder(){ 14 | VTCompressionSessionRef _compressionSession; 15 | int _frameTime; 16 | dispatch_queue_t _encodeQueue; 17 | } 18 | /** 19 | settings 20 | */ 21 | @property(nonatomic , strong )RongRTCVideoEncoderSettings *settings; 22 | 23 | /** 24 | callback queue 25 | */ 26 | @property(nonatomic , strong )dispatch_queue_t callbackQueue; 27 | 28 | - (void)sendSpsAndPPSWithSampleBuffer:(CMSampleBufferRef)sampleBuffer; 29 | - (void)sendNaluData:(CMSampleBufferRef)sampleBuffer; 30 | @end 31 | 32 | void compressionOutputCallback(void *encoder, 33 | void *params, 34 | OSStatus status, 35 | VTEncodeInfoFlags infoFlags, 36 | CMSampleBufferRef sampleBuffer){ 37 | RongRTCVideoEncoder *videoEncoder = (__bridge RongRTCVideoEncoder *)encoder; 38 | if (status != noErr) { 39 | return; 40 | } 41 | if (infoFlags & kVTEncodeInfo_FrameDropped) { 42 | return; 43 | } 44 | BOOL isKeyFrame = NO; 45 | CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, 0); 46 | if (attachments != nullptr && CFArrayGetCount(attachments)) { 47 | CFDictionaryRef attachment = static_cast(CFArrayGetValueAtIndex(attachments, 0)) ; 48 | isKeyFrame = !CFDictionaryContainsKey(attachment, kCMSampleAttachmentKey_NotSync); 49 | } 50 | CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(sampleBuffer); 51 | CMBlockBufferRef contiguous_buffer = nullptr; 52 | if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) { 53 | status = CMBlockBufferCreateContiguous( 54 | nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer); 55 | if (status != noErr) { 56 | return; 57 | } 58 | } else { 59 | contiguous_buffer = block_buffer; 60 | CFRetain(contiguous_buffer); 61 | block_buffer = nullptr; 62 | } 63 | if (isKeyFrame) { 64 | [videoEncoder sendSpsAndPPSWithSampleBuffer:sampleBuffer]; 65 | } 66 | if (contiguous_buffer) { 67 | CFRelease(contiguous_buffer); 68 | } 69 | [videoEncoder sendNaluData:sampleBuffer]; 70 | } 71 | 72 | @implementation RongRTCVideoEncoder 73 | 74 | @synthesize settings = _settings; 75 | @synthesize callbackQueue = _callbackQueue; 76 | 77 | - (BOOL)configWithSettings:(RongRTCVideoEncoderSettings *)settings onQueue:(nonnull dispatch_queue_t)queue{ 78 | self.settings = settings; 79 | if (queue) { 80 | _callbackQueue = queue; 81 | } else { 82 | _callbackQueue = dispatch_get_main_queue(); 83 | } 84 | _encodeQueue = dispatch_queue_create("com.rongcloud.encodeQueue", NULL); 85 | if ([self resetCompressionSession:settings]) { 86 | _frameTime = 0; 87 | return YES; 88 | } else { 89 | return NO; 90 | } 91 | } 92 | - (BOOL)resetCompressionSession:(RongRTCVideoEncoderSettings *)settings { 93 | [self destroyCompressionSession]; 94 | OSStatus status = VTCompressionSessionCreate(nullptr, settings.width, settings.height, kCMVideoCodecType_H264, nullptr, nullptr, nullptr, compressionOutputCallback, (__bridge void * _Nullable)(self), &_compressionSession); 95 | if (status != noErr) { 96 | return NO; 97 | } 98 | [self configureCompressionSession:settings]; 99 | return YES; 100 | } 101 | - (void)configureCompressionSession:(RongRTCVideoEncoderSettings *)settings{ 102 | if (_compressionSession) { 103 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_RealTime, true); 104 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel); 105 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, false); 106 | 107 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, 10); 108 | uint32_t targetBps = settings.startBitrate * 1000; 109 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_AverageBitRate, targetBps); 110 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate, settings.maxFramerate); 111 | int bitRate = settings.width * settings.height * 3 * 4 * 4; 112 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_AverageBitRate, bitRate); 113 | int bitRateLimit = settings.width * settings.height * 3 * 4; 114 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_DataRateLimits, bitRateLimit); 115 | } 116 | } 117 | -(void)encode:(CMSampleBufferRef)sampleBuffer{ 118 | CFRetain(sampleBuffer); 119 | dispatch_async(_encodeQueue, ^{ 120 | CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer); 121 | CMTime pts = CMTimeMake(self->_frameTime++, 1000); 122 | VTEncodeInfoFlags flags; 123 | OSStatus res = VTCompressionSessionEncodeFrame(self->_compressionSession, 124 | imageBuffer, 125 | pts, 126 | kCMTimeInvalid, 127 | NULL, NULL, &flags); 128 | 129 | CFRelease(sampleBuffer); 130 | if (res != noErr) { 131 | NSLog(@"encode frame error:%d", (int)res); 132 | VTCompressionSessionInvalidate(self->_compressionSession); 133 | CFRelease(self->_compressionSession); 134 | self->_compressionSession = NULL; 135 | return; 136 | } 137 | }); 138 | 139 | } 140 | - (void)sendSpsAndPPSWithSampleBuffer:(CMSampleBufferRef)sampleBuffer{ 141 | CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer); 142 | const uint8_t *sps ; 143 | const uint8_t *pps; 144 | size_t spsSize ,ppsSize , spsCount,ppsCount; 145 | OSStatus spsStatus = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sps, &spsSize, &spsCount, NULL); 146 | OSStatus ppsStatus = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pps, &ppsSize, &ppsCount, NULL); 147 | if (spsStatus == noErr && ppsStatus == noErr) { 148 | const char bytes[] = "\x00\x00\x00\x01"; 149 | size_t length = (sizeof bytes) - 1; 150 | 151 | NSMutableData *spsData = [NSMutableData dataWithCapacity:4+ spsSize]; 152 | NSMutableData *ppsData = [NSMutableData dataWithCapacity:4 + ppsSize]; 153 | [spsData appendBytes:bytes length:length]; 154 | [spsData appendBytes:sps length:spsSize]; 155 | 156 | [ppsData appendBytes:bytes length:length]; 157 | [ppsData appendBytes:pps length:ppsSize]; 158 | if (self && self.callbackQueue) { 159 | dispatch_async(self.callbackQueue, ^{ 160 | if (self.delegate && [self.delegate respondsToSelector:@selector(spsData:ppsData:)]) { 161 | [self.delegate spsData:spsData ppsData:ppsData]; 162 | } 163 | }); 164 | } 165 | } else { 166 | NSLog(@"😁 sps status:%@,pps status:%@",@(spsStatus),@(ppsStatus)); 167 | } 168 | 169 | } 170 | - (void)sendNaluData:(CMSampleBufferRef)sampleBuffer{ 171 | size_t totalLength = 0; 172 | size_t lengthAtOffset=0; 173 | char *dataPointer; 174 | CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); 175 | OSStatus status1 = CMBlockBufferGetDataPointer(blockBuffer, 0, &lengthAtOffset, &totalLength, &dataPointer); 176 | if (status1 != noErr) { 177 | NSLog(@"video encoder error, status = %d", (int)status1); 178 | return; 179 | } 180 | static const int h264HeaderLength = 4; 181 | size_t bufferOffset = 0; 182 | while (bufferOffset < totalLength - h264HeaderLength) { 183 | 184 | uint32_t naluLength = 0; 185 | memcpy(&naluLength, dataPointer + bufferOffset, h264HeaderLength); 186 | naluLength = CFSwapInt32BigToHost(naluLength); 187 | 188 | const char bytes[] = "\x00\x00\x00\x01"; 189 | NSMutableData *naluData = [NSMutableData dataWithCapacity:4 + naluLength]; 190 | [naluData appendBytes:bytes length:4]; 191 | [naluData appendBytes:dataPointer + bufferOffset + h264HeaderLength length:naluLength]; 192 | if (self.callbackQueue) { 193 | dispatch_async(self.callbackQueue, ^{ 194 | if (self.delegate && [self.delegate respondsToSelector:@selector(naluData:)]) { 195 | [self.delegate naluData:naluData]; 196 | } 197 | }); 198 | } 199 | bufferOffset += naluLength + h264HeaderLength; 200 | } 201 | } 202 | - (void)destroyCompressionSession{ 203 | if (_compressionSession) { 204 | VTCompressionSessionInvalidate(_compressionSession); 205 | CFRelease(_compressionSession); 206 | _compressionSession = nullptr; 207 | } 208 | } 209 | - (void)dealloc 210 | { 211 | if (_compressionSession) { 212 | VTCompressionSessionCompleteFrames(_compressionSession, kCMTimeInvalid); 213 | VTCompressionSessionInvalidate(_compressionSession); 214 | CFRelease(_compressionSession); 215 | _compressionSession = NULL; 216 | } 217 | } 218 | @end 219 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit/OtherTest/OtherTest.m: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | // 软编 5 | 6 | 7 | //// 8 | //// OtherTest.m 9 | //// SealRTC 10 | //// 11 | //// Created by 孙承秀 on 2020/5/7. 12 | //// Copyright © 2020 RongCloud. All rights reserved. 13 | //// 14 | // 15 | //#import "OtherTest.h" 16 | //#import 17 | //#import 18 | //#import 19 | //#import 20 | //#import 21 | //#import "RongRTCThread.h" 22 | //#import "RongRTCSocketHeader.h" 23 | //#import "RongRTCVideoEncoder.h" 24 | //@interface OtherTest(){ 25 | // pthread_mutex_t lock; 26 | //} 27 | // 28 | ///** 29 | // thread 30 | // */ 31 | //@property(nonatomic , strong)RongRTCThread *thread; 32 | // 33 | ///** 34 | // video encoder 35 | // */ 36 | //@property(nonatomic , strong)RongRTCVideoEncoder *encoder; 37 | //@end 38 | //@implementation OtherTest 39 | //- (BOOL)createCliectSocket{ 40 | // if ([self createSocket] == -1) { 41 | // return NO; 42 | // } 43 | // BOOL isC = [self connect]; 44 | // [self setSendBuffer]; 45 | // [self setSendingTimeout]; 46 | // if (isC) { 47 | // self.thread = [[RongRTCThread alloc] init]; 48 | // [self.thread run]; 49 | // return YES; 50 | // } else { 51 | // return NO; 52 | // } 53 | //} 54 | // 55 | //-(void)cliectSend:(CMSampleBufferRef)sampleBuffer length:(size_t)length{ 56 | // // 发送字符串测试 57 | // // char sendData[32] = "hello service"; 58 | // // ssize_t size_t = send(self.sock, sendData, strlen(sendData), 0); 59 | // 60 | // if (@available(iOS 9.0, *)) { 61 | // @autoreleasepool { 62 | // 63 | // // time 64 | // CMTime currentTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); 65 | // 66 | // // compress 67 | // CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 68 | // CGImageRef image = NULL; 69 | // // data 70 | // OSStatus createdImage = VTCreateCGImageFromCVPixelBuffer(imageBuffer, NULL, &image); 71 | // UIImage * image1 = nil; 72 | // if (createdImage == noErr) { 73 | // image1 = [UIImage imageWithCGImage:image]; 74 | // } 75 | // image1 = [RongRTCBufferUtil compressImage:image1 newWidth:480]; 76 | // NSData *data= UIImageJPEGRepresentation(image1, 0.1); 77 | // CFRelease(image); 78 | // //data length 79 | // NSUInteger dataLength = data.length; 80 | // 81 | // // data header struct 82 | // DataHeader dataH; 83 | // memset((void *)&dataH, 0, sizeof(dataH)); 84 | // 85 | // // pre 86 | // PreHeader preH; 87 | // memset((void *)&preH, 0, sizeof(preH)); 88 | // preH.pre[0] = '&'; 89 | // preH.dataLength = dataLength; 90 | // preH.time = currentTime; 91 | // 92 | // dataH.preH = preH; 93 | // 94 | // // buffer 95 | // int headerlength = sizeof(dataH); 96 | // int totalLength = dataLength + headerlength; 97 | // 98 | // // srcbuffer 99 | // Byte *src = (Byte *)[data bytes]; 100 | // 101 | // // send buffer 102 | // char *buffer = (char *)malloc(totalLength * sizeof(char)); 103 | // memcpy(buffer, &dataH, headerlength); 104 | // memcpy(buffer + headerlength, src, dataLength); 105 | // 106 | // // tosend 107 | // [self sendBytes:buffer length:totalLength]; 108 | // 109 | // } 110 | // } 111 | //} 112 | ////-(void)cliectSend:(CMSampleBufferRef)sampleBuffer length:(size_t)length{ 113 | //// // 发送字符串测试 114 | //// // char sendData[32] = "hello service"; 115 | //// // ssize_t size_t = send(self.sock, sendData, strlen(sendData), 0); 116 | //// 117 | //// if (@available(iOS 9.0, *)) { 118 | //// @autoreleasepool { 119 | //// 120 | //// // time 121 | //// CMTime currentTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); 122 | //// 123 | //// // compress 124 | //// CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 125 | //// CGImageRef image = NULL; 126 | //// // data 127 | //// OSStatus createdImage = VTCreateCGImageFromCVPixelBuffer(imageBuffer, NULL, &image); 128 | //// UIImage * image1 = nil; 129 | //// if (createdImage == noErr) { 130 | //// image1 = [UIImage imageWithCGImage:image]; 131 | //// } 132 | //// image1 = [RongRTCBufferUtil compressImage:image1 newWidth:480]; 133 | //// NSData *data= UIImageJPEGRepresentation(image1, 0.1); 134 | //// CFRelease(image); 135 | //// //data length 136 | //// NSUInteger dataLength = data.length; 137 | //// 138 | //// // data header struct 139 | //// DataHeader dataH; 140 | //// memset((void *)&dataH, 0, sizeof(dataH)); 141 | //// 142 | //// // pre 143 | //// PreHeader preH; 144 | //// memset((void *)&preH, 0, sizeof(preH)); 145 | //// preH.pre[0] = '&'; 146 | //// preH.dataLength = dataLength; 147 | //// preH.time = currentTime; 148 | //// 149 | //// dataH.preH = preH; 150 | //// 151 | //// // buffer 152 | //// int headerlength = sizeof(dataH); 153 | //// int totalLength = dataLength + headerlength; 154 | //// 155 | //// // srcbuffer 156 | //// Byte *src = (Byte *)[data bytes]; 157 | //// 158 | //// // send buffer 159 | //// char *buffer = (char *)malloc(totalLength * sizeof(char)); 160 | //// memcpy(buffer, &dataH, headerlength); 161 | //// memcpy(buffer + headerlength, src, dataLength); 162 | //// 163 | //// // tosend 164 | //// [self sendBytes:buffer length:totalLength]; 165 | //// 166 | //// } 167 | //// } 168 | ////} 169 | //- (void)sendBytes:(char *)bytes length:(int )length { 170 | // __block char *datas = bytes; 171 | // __weak typeof(self)weakSelf = self; 172 | // [self.thread excuteTaskWithBlock:^{ 173 | // __strong typeof(self)strongSelf = weakSelf; 174 | // LOCK(strongSelf->lock); 175 | // int hasSendLength = 0; 176 | // while (hasSendLength < length) { 177 | // // connect socket success 178 | // if (self.sock > 0) { 179 | // // send 180 | // int sendRes = send(strongSelf.sock, datas, length - hasSendLength, 0); 181 | // if (sendRes == -1 || sendRes == 0) { 182 | // UNLOCK(strongSelf->lock); 183 | // NSLog(@"😁😁😁😁😁send buffer error"); 184 | // [self close]; 185 | // return ; 186 | // } 187 | // hasSendLength += sendRes; 188 | // datas += sendRes; 189 | // 190 | // } else { 191 | // NSLog(@"😁😁😁😁😁client socket connect error"); 192 | // UNLOCK(strongSelf->lock); 193 | // } 194 | // } 195 | // UNLOCK(strongSelf->lock); 196 | // }]; 197 | // 198 | //} 199 | // 200 | //- (NSData *)compressBuffer:(CMSampleBufferRef)sampleBuffer{ 201 | // CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 202 | // CGImageRef image = NULL; 203 | // // data 204 | // OSStatus createdImage = VTCreateCGImageFromCVPixelBuffer(imageBuffer, NULL, &image); 205 | // UIImage * image1 = nil; 206 | // if (createdImage == noErr) { 207 | // image1 = [UIImage imageWithCGImage:image]; 208 | // } 209 | // image1 = [RongRTCBufferUtil compressImage:image1 newWidth:480]; 210 | // NSData *data= UIImageJPEGRepresentation(image1, 0.1); 211 | // CFRelease(image); 212 | // return data; 213 | //} 214 | // 215 | //-(void)dealloc{ 216 | // [self.thread stop]; 217 | // NSLog(@"😁😁😁😁😁dealoc cliect socket"); 218 | //} 219 | //@end 220 | 221 | 222 | 223 | // 软解 224 | 225 | // 226 | // RongRTCServerSocket.m 227 | // SealRTC 228 | // 229 | // Created by 孙承秀 on 2020/5/7. 230 | // Copyright © 2020 RongCloud. All rights reserved. 231 | // 232 | 233 | //#import "RongRTCServerSocket.h" 234 | //#import 235 | //#import 236 | //#import 237 | //#import 238 | //#import 239 | //#import 240 | //#import "RongRTCThread.h" 241 | //#import "RongRTCSocketHeader.h" 242 | // 243 | //@interface RongRTCServerSocket() 244 | //{ 245 | // pthread_mutex_t lock; 246 | //} 247 | //@property (nonatomic, assign) int acceptSock; 248 | // 249 | ///** 250 | // data length 251 | // */ 252 | //@property(nonatomic , assign)NSUInteger dataLength; 253 | // 254 | ///** 255 | // timeData 256 | // */ 257 | //@property(nonatomic , strong)NSData *timeData; 258 | ///** 259 | // thread 260 | // */ 261 | //@property(nonatomic , strong)RongRTCThread *thread; 262 | //@end 263 | //@implementation RongRTCServerSocket 264 | // 265 | //- (BOOL)createServerSocket{ 266 | // if ([self createSocket] == -1) { 267 | // return NO; 268 | // } 269 | // [self setRecvBuffer]; 270 | // [self setRecvTimeout]; 271 | // BOOL isB = [self bind]; 272 | // BOOL isL = [self listen]; 273 | // 274 | // if (isB && isL) { 275 | // self.thread = [[RongRTCThread alloc] init]; 276 | // [self.thread run]; 277 | // [self receive]; 278 | // return YES; 279 | // } else { 280 | // return NO; 281 | // } 282 | //} 283 | //-(void)recvData{ 284 | // struct sockaddr_in rest; 285 | // socklen_t rest_size = sizeof(struct sockaddr_in); 286 | // self.acceptSock = accept(self.sock, (struct sockaddr *) &rest, &rest_size); 287 | // while (self.acceptSock != -1) { 288 | // DataHeader dataH; 289 | // memset(&dataH, 0, sizeof(dataH)); 290 | // 291 | // if (![self receveData:(char *)&dataH length:sizeof(dataH)]) { 292 | // continue; 293 | // } 294 | // PreHeader preH = dataH.preH; 295 | // char pre = preH.pre[0]; 296 | // if (pre == '&') { 297 | // // rongcloud socket 298 | // NSUInteger dataLenght = preH.dataLength; 299 | // CMTime time = preH.time; 300 | // char *buff = (char *)malloc(sizeof(char) * dataLenght); 301 | // if ([self receveData:(char *)buff length:dataLenght]) { 302 | // NSData *data = [NSData dataWithBytes:buff length:dataLenght]; 303 | // // recv data success 304 | // UIImage *image = [UIImage imageWithData:data]; 305 | // if (image) { 306 | // CVPixelBufferRef pix = [RongRTCBufferUtil CVPixelBufferRefFromUiImage:image]; 307 | // CMSampleBufferRef sam = [RongRTCBufferUtil sampleBufferFromPixbuffer:pix time:time]; 308 | // if (self.delegate && [self.delegate respondsToSelector:@selector(didProcessSampleBuffer:)]) { 309 | // [self.delegate didProcessSampleBuffer:sam]; 310 | // } 311 | // } else { 312 | // self.dataLength = 50000; 313 | // } 314 | // } 315 | // } else { 316 | // NSLog(@"😁😁😁😁😁pre is not &"); 317 | // return; 318 | // } 319 | // } 320 | //} 321 | //- (BOOL)receveData:(char *)data length:(NSUInteger)length{ 322 | // LOCK(lock); 323 | // int recvLength = 0; 324 | // while (recvLength < length) { 325 | // ssize_t res = recv(self.acceptSock, data, length - recvLength, 0); 326 | // if (res == -1 || res == 0) { 327 | // UNLOCK(lock); 328 | // NSLog(@"😁😁😁😁😁recv data error"); 329 | // return NO; 330 | // } 331 | // recvLength += res; 332 | // data += res; 333 | // } 334 | // UNLOCK(lock); 335 | // return YES; 336 | //} 337 | //- (size_t)getCMTimeSize{ 338 | // size_t size = sizeof(CMTime); 339 | // return size; 340 | //} 341 | // 342 | //-(void)close{ 343 | // int res = close(self.acceptSock); 344 | // NSLog(@"😁😁😁😁😁shut down server: %d",res); 345 | // [super close]; 346 | //} 347 | //-(void)dealloc{ 348 | // [self.thread stop]; 349 | // NSLog(@"😁😁😁😁😁dealoc server socket"); 350 | //} 351 | //@end 352 | 353 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ios 利用 socket 传输 replykit 屏幕共享数据到主 app 2 | 3 | ## 先上 [demo](https://github.com/sunchengxiu/Socket_ReplyKit) 4 | 5 | 6 | 我这里只讲代码,文章知识点什么的,大家自己搜索,网上太多了,比我说的好 7 | 8 | ## 1. replykit 使用 9 | 10 | ``` 11 | // 12 | // ViewController.m 13 | // Socket_Replykit 14 | // 15 | // Created by 孙承秀 on 2020/5/19. 16 | // Copyright © 2020 RongCloud. All rights reserved. 17 | // 18 | 19 | #import "ViewController.h" 20 | #import 21 | #import "RongRTCServerSocket.h" 22 | @interface ViewController () 23 | @property (nonatomic, strong) RPSystemBroadcastPickerView *systemBroadcastPickerView; 24 | /** 25 | server socket 26 | */ 27 | @property(nonatomic , strong)RongRTCServerSocket *serverSocket; 28 | @end 29 | 30 | @implementation ViewController 31 | 32 | - (void)viewDidLoad { 33 | [super viewDidLoad]; 34 | self.view.backgroundColor = [UIColor whiteColor]; 35 | // Do any additional setup after loading the view. 36 | [self.serverSocket createServerSocket]; 37 | self.systemBroadcastPickerView = [[RPSystemBroadcastPickerView alloc] initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, 80)]; 38 | self.systemBroadcastPickerView.preferredExtension = @"cn.rongcloud.sealrtc.RongRTCRP"; 39 | self.systemBroadcastPickerView.backgroundColor = [UIColor colorWithRed:53.0/255.0 green:129.0/255.0 blue:242.0/255.0 alpha:1.0]; 40 | self.systemBroadcastPickerView.showsMicrophoneButton = NO; 41 | [self.view addSubview:self.systemBroadcastPickerView]; 42 | } 43 | 44 | -(RongRTCServerSocket *)serverSocket{ 45 | if (!_serverSocket) { 46 | RongRTCServerSocket *socket = [[RongRTCServerSocket alloc] init]; 47 | socket.delegate = self; 48 | 49 | _serverSocket = socket; 50 | } 51 | return _serverSocket; 52 | } 53 | -(void)didProcessSampleBuffer:(CMSampleBufferRef)sampleBuffer{ 54 | // 这里拿到了最终的数据,比如最后可以使用融云的音视频SDK RTCLib 进行传输就可以了 55 | } 56 | @end 57 | 58 | 59 | @end 60 | 61 | 62 | ``` 63 | 64 | 打开一个屏幕共享就是这么容易, 65 | 66 | 其中,也包括了,创建 server soket 的步骤,我们把主app当做server,然后屏幕共享 extension 当做 client ,通过socket像我们主app发送数据 67 | 68 | 69 | 在extension 里面,我们拿到屏幕共享数据之后 70 | 71 | 72 | ``` 73 | // 74 | // SampleHandler.m 75 | // SocketReply 76 | // 77 | // Created by 孙承秀 on 2020/5/19. 78 | // Copyright © 2020 RongCloud. All rights reserved. 79 | // 80 | 81 | 82 | #import "SampleHandler.h" 83 | #import "RongRTCClientSocket.h" 84 | @interface SampleHandler() 85 | 86 | /** 87 | client servert 88 | */ 89 | @property(nonatomic , strong)RongRTCClientSocket *clientSocket; 90 | @end 91 | @implementation SampleHandler 92 | 93 | - (void)broadcastStartedWithSetupInfo:(NSDictionary *)setupInfo { 94 | // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional. 95 | self.clientSocket = [[RongRTCClientSocket alloc] init]; 96 | [self.clientSocket createCliectSocket]; 97 | } 98 | 99 | - (void)broadcastPaused { 100 | // User has requested to pause the broadcast. Samples will stop being delivered. 101 | } 102 | 103 | - (void)broadcastResumed { 104 | // User has requested to resume the broadcast. Samples delivery will resume. 105 | } 106 | 107 | - (void)broadcastFinished { 108 | // User has requested to finish the broadcast. 109 | } 110 | 111 | - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { 112 | 113 | switch (sampleBufferType) { 114 | case RPSampleBufferTypeVideo: 115 | // Handle video sample buffer 116 | [self sendData:sampleBuffer]; 117 | break; 118 | case RPSampleBufferTypeAudioApp: 119 | // Handle audio sample buffer for app audio 120 | break; 121 | case RPSampleBufferTypeAudioMic: 122 | // Handle audio sample buffer for mic audio 123 | break; 124 | 125 | default: 126 | break; 127 | } 128 | } 129 | - (void)sendData:(CMSampleBufferRef)sampleBuffer{ 130 | 131 | [self.clientSocket encodeBuffer:sampleBuffer]; 132 | 133 | } 134 | @end 135 | 136 | 137 | ``` 138 | 139 | 140 | 可见 ,这里我们创建了一个 client socket,然后拿到屏幕共享的视频buffer之后,通过socket发给我们的主app,这就是屏幕共享额流程 141 | 142 | 143 | ## 2. local socket 的使用 144 | 145 | ``` 146 | // 147 | // RongRTCSocket.m 148 | // SealRTC 149 | // 150 | // Created by 孙承秀 on 2020/5/7. 151 | // Copyright © 2020 RongCloud. All rights reserved. 152 | // 153 | 154 | #import "RongRTCSocket.h" 155 | #import 156 | #import 157 | #import 158 | #import 159 | #import 160 | #import "RongRTCThread.h" 161 | @interface RongRTCSocket() 162 | 163 | /** 164 | rec thread 165 | */ 166 | @property(nonatomic , strong)RongRTCThread *recvThread; 167 | @end 168 | @implementation RongRTCSocket 169 | - (int)createSocket{ 170 | int sock = socket(AF_INET, SOCK_STREAM, 0); 171 | self.sock = sock; 172 | if (self.sock == -1) { 173 | close(self.sock); 174 | NSLog(@"😁😁😁😁😁socket error : %d",self.sock); 175 | } 176 | self.recvThread = [[RongRTCThread alloc] init]; 177 | [self.recvThread run]; 178 | return sock; 179 | } 180 | - (void)setSendBuffer{ 181 | int optVal = 1024 * 1024 * 2; 182 | int optLen = sizeof(int); 183 | int res = setsockopt(self.sock, SOL_SOCKET,SO_SNDBUF,(char*)&optVal,optLen ); 184 | NSLog(@"😁😁😁😁😁set send buffer:%d",res); 185 | } 186 | - (void)setRecvBuffer{ 187 | int optVal = 1024 * 1024 * 2; 188 | int optLen = sizeof(int); 189 | int res = setsockopt(self.sock, SOL_SOCKET,SO_RCVBUF,(char*)&optVal,optLen );; 190 | NSLog(@"😁😁😁😁😁set send buffer:%d",res); 191 | } 192 | - (void)setSendingTimeout{ 193 | struct timeval timeout = {10,0}; 194 | int res = setsockopt(self.sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int)); 195 | NSLog(@"😁😁😁😁😁set send timeout:%d",res); 196 | } 197 | - (void)setRecvTimeout{ 198 | struct timeval timeout = {10,0}; 199 | int res = setsockopt(self.sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int)); 200 | NSLog(@"😁😁😁😁😁set send timeout:%d",res); 201 | } 202 | - (BOOL)connect{ 203 | NSString *serverHost = [self ip]; 204 | struct hostent *server = gethostbyname([serverHost UTF8String]); 205 | if (server == NULL) { 206 | close(self.sock); 207 | NSLog(@"😁😁😁😁😁get host error"); 208 | return NO; 209 | } 210 | 211 | struct in_addr *remoteAddr = (struct in_addr *)server->h_addr_list[0]; 212 | struct sockaddr_in addr; 213 | addr.sin_family = AF_INET; 214 | addr.sin_addr = *remoteAddr; 215 | addr.sin_port = htons(CONNECTPORT); 216 | int res = connect(self.sock, (struct sockaddr *) &addr, sizeof(addr)); 217 | if (res == -1) { 218 | close(self.sock); 219 | NSLog(@"😁😁😁😁😁connect error"); 220 | return NO; 221 | } 222 | NSLog(@"😁😁😁😁😁socket connect to server success"); 223 | return YES; 224 | } 225 | - (BOOL)bind{ 226 | struct sockaddr_in client; 227 | client.sin_family = AF_INET; 228 | NSString *ipStr = [self ip]; 229 | if (ipStr.length <= 0) { 230 | return NO; 231 | } 232 | const char *ip = [ipStr cStringUsingEncoding:NSASCIIStringEncoding]; 233 | client.sin_addr.s_addr = inet_addr(ip); 234 | client.sin_port = htons(CONNECTPORT); 235 | int bd = bind(self.sock, (struct sockaddr *) &client, sizeof(client)); 236 | if (bd == -1) { 237 | close(self.sock); 238 | NSLog(@"😁😁😁😁😁bind error : %d",bd); 239 | return NO; 240 | } 241 | return YES; 242 | } 243 | 244 | - (BOOL)listen{ 245 | int ls = listen(self.sock, 128); 246 | if (ls == -1) { 247 | close(self.sock); 248 | NSLog(@"😁😁😁😁😁listen error : %d",ls); 249 | return NO; 250 | } 251 | return YES; 252 | } 253 | - (void)receive{ 254 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 255 | [self recvData]; 256 | }); 257 | } 258 | - (NSString *)ip{ 259 | NSString *ip = nil; 260 | struct ifaddrs *addrs = NULL; 261 | struct ifaddrs *tmpAddrs = NULL; 262 | BOOL res = getifaddrs(&addrs); 263 | if (res == 0) { 264 | tmpAddrs = addrs; 265 | while (tmpAddrs != NULL) { 266 | if(tmpAddrs->ifa_addr->sa_family == AF_INET) { 267 | // Check if interface is en0 which is the wifi connection on the iPhone 268 | NSLog(@"%@",[NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)tmpAddrs->ifa_addr)->sin_addr)]); 269 | if([[NSString stringWithUTF8String:tmpAddrs->ifa_name] isEqualToString:@"en0"]) { 270 | // Get NSString from C String 271 | ip = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)tmpAddrs->ifa_addr)->sin_addr)]; 272 | } 273 | } 274 | tmpAddrs = tmpAddrs->ifa_next; 275 | } 276 | } 277 | // Free memory 278 | freeifaddrs(addrs); 279 | NSLog(@"😁😁😁😁😁%@",ip); 280 | return ip; 281 | } 282 | -(void)close{ 283 | int res = close(self.sock); 284 | NSLog(@"😁😁😁😁😁shut down : %d",res); 285 | } 286 | - (void)recvData{ 287 | 288 | } 289 | -(void)dealloc{ 290 | [self.recvThread stop]; 291 | } 292 | @end 293 | 294 | 295 | ``` 296 | 297 | 我创建了一个 socket 的父类,然后 server 和 client 分别继承这个类,来实现,链接绑定等操作,可以看到有很多数据可以设置,有些可以不用,这里不是核心,核心是怎样收发数据 298 | 299 | 300 | ### 发送屏幕贡共享数据 301 | 302 | 303 | ``` 304 | 305 | // 306 | // RongRTCClientSocket.m 307 | // SealRTC 308 | // 309 | // Created by 孙承秀 on 2020/5/7. 310 | // Copyright © 2020 RongCloud. All rights reserved. 311 | // 312 | 313 | #import "RongRTCClientSocket.h" 314 | #import 315 | #import 316 | #import 317 | #import 318 | #import 319 | #import "RongRTCThread.h" 320 | #import "RongRTCSocketHeader.h" 321 | #import "RongRTCVideoEncoder.h" 322 | @interface RongRTCClientSocket(){ 323 | pthread_mutex_t lock; 324 | } 325 | 326 | /** 327 | video encoder 328 | */ 329 | @property(nonatomic , strong)RongRTCVideoEncoder *encoder; 330 | 331 | /** 332 | encode queue 333 | */ 334 | @property(nonatomic , strong)dispatch_queue_t encodeQueue; 335 | @end 336 | @implementation RongRTCClientSocket 337 | - (BOOL)createCliectSocket{ 338 | if ([self createSocket] == -1) { 339 | return NO; 340 | } 341 | BOOL isC = [self connect]; 342 | [self setSendBuffer]; 343 | [self setSendingTimeout]; 344 | if (isC) { 345 | _encodeQueue = dispatch_queue_create("com.rongcloud.encodequeue", NULL); 346 | [self createVideoEncoder]; 347 | return YES; 348 | } else { 349 | return NO; 350 | } 351 | } 352 | - (void)createVideoEncoder{ 353 | self.encoder = [[RongRTCVideoEncoder alloc] init]; 354 | self.encoder.delegate = self; 355 | RongRTCVideoEncoderSettings *settings = [[RongRTCVideoEncoderSettings alloc] init]; 356 | settings.width = 720; 357 | settings.height = 1280; 358 | settings.startBitrate = 300; 359 | settings.maxFramerate = 30; 360 | settings.minBitrate = 1000; 361 | [self.encoder configWithSettings:settings onQueue:_encodeQueue]; 362 | } 363 | -(void)cliectSend:(NSData *)data{ 364 | 365 | //data length 366 | NSUInteger dataLength = data.length; 367 | 368 | // data header struct 369 | DataHeader dataH; 370 | memset((void *)&dataH, 0, sizeof(dataH)); 371 | 372 | // pre 373 | PreHeader preH; 374 | memset((void *)&preH, 0, sizeof(preH)); 375 | preH.pre[0] = '&'; 376 | preH.dataLength = dataLength; 377 | 378 | dataH.preH = preH; 379 | 380 | // buffer 381 | int headerlength = sizeof(dataH); 382 | int totalLength = dataLength + headerlength; 383 | 384 | // srcbuffer 385 | Byte *src = (Byte *)[data bytes]; 386 | 387 | // send buffer 388 | char *buffer = (char *)malloc(totalLength * sizeof(char)); 389 | memcpy(buffer, &dataH, headerlength); 390 | memcpy(buffer + headerlength, src, dataLength); 391 | 392 | // tosend 393 | [self sendBytes:buffer length:totalLength]; 394 | free(buffer); 395 | 396 | } 397 | - (void)encodeBuffer:(CMSampleBufferRef)sampleBuffer{ 398 | [self.encoder encode:sampleBuffer]; 399 | } 400 | 401 | - (void)sendBytes:(char *)bytes length:(int )length { 402 | LOCK(self->lock); 403 | int hasSendLength = 0; 404 | while (hasSendLength < length) { 405 | // connect socket success 406 | if (self.sock > 0) { 407 | // send 408 | int sendRes = send(self.sock, bytes, length - hasSendLength, 0); 409 | if (sendRes == -1 || sendRes == 0) { 410 | UNLOCK(self->lock); 411 | NSLog(@"😁😁😁😁😁send buffer error"); 412 | [self close]; 413 | break; 414 | } 415 | hasSendLength += sendRes; 416 | bytes += sendRes; 417 | 418 | } else { 419 | NSLog(@"😁😁😁😁😁client socket connect error"); 420 | UNLOCK(self->lock); 421 | } 422 | } 423 | UNLOCK(self->lock); 424 | 425 | } 426 | -(void)spsData:(NSData *)sps ppsData:(NSData *)pps{ 427 | [self cliectSend:sps]; 428 | [self cliectSend:pps]; 429 | } 430 | -(void)naluData:(NSData *)naluData{ 431 | [self cliectSend:naluData]; 432 | } 433 | -(void)dealloc{ 434 | 435 | NSLog(@"😁😁😁😁😁dealoc cliect socket"); 436 | } 437 | @end 438 | 439 | ``` 440 | 441 | 这里核心思想是拿到我们屏幕共享的数据之后,要先经过压缩,压缩完成,会通过回调,会给我们当前类,然后通过 `cliectSend `方法,发给主app,我这里是自定义了一个头部,头部添加了一个前缀和一个每次发送字节的长度,然后接收端去解析这个数据就行,核心都在这里 442 | 443 | 444 | ``` 445 | -(void)cliectSend:(NSData *)data{ 446 | 447 | //data length 448 | NSUInteger dataLength = data.length; 449 | 450 | // data header struct 451 | DataHeader dataH; 452 | memset((void *)&dataH, 0, sizeof(dataH)); 453 | 454 | // pre 455 | PreHeader preH; 456 | memset((void *)&preH, 0, sizeof(preH)); 457 | preH.pre[0] = '&'; 458 | preH.dataLength = dataLength; 459 | 460 | dataH.preH = preH; 461 | 462 | // buffer 463 | int headerlength = sizeof(dataH); 464 | int totalLength = dataLength + headerlength; 465 | 466 | // srcbuffer 467 | Byte *src = (Byte *)[data bytes]; 468 | 469 | // send buffer 470 | char *buffer = (char *)malloc(totalLength * sizeof(char)); 471 | memcpy(buffer, &dataH, headerlength); 472 | memcpy(buffer + headerlength, src, dataLength); 473 | 474 | // tosend 475 | [self sendBytes:buffer length:totalLength]; 476 | free(buffer); 477 | 478 | } 479 | 480 | ``` 481 | 482 | 大家仔细理解一下。 483 | 484 | ### 接收屏幕共享数据 485 | 486 | ``` 487 | 488 | // 489 | // RongRTCServerSocket.m 490 | // SealRTC 491 | // 492 | // Created by 孙承秀 on 2020/5/7. 493 | // Copyright © 2020 RongCloud. All rights reserved. 494 | // 495 | 496 | #import "RongRTCServerSocket.h" 497 | #import 498 | #import 499 | #import 500 | #import 501 | #import 502 | #import 503 | 504 | 505 | #import "RongRTCThread.h" 506 | #import "RongRTCSocketHeader.h" 507 | #import "RongRTCVideoDecoder.h" 508 | @interface RongRTCServerSocket() 509 | { 510 | pthread_mutex_t lock; 511 | int _frameTime; 512 | CMTime _lastPresentationTime; 513 | Float64 _currentMediaTime; 514 | Float64 _currentVideoTime; 515 | dispatch_queue_t _frameQueue; 516 | } 517 | @property (nonatomic, assign) int acceptSock; 518 | 519 | /** 520 | data length 521 | */ 522 | @property(nonatomic , assign)NSUInteger dataLength; 523 | 524 | /** 525 | timeData 526 | */ 527 | @property(nonatomic , strong)NSData *timeData; 528 | 529 | /** 530 | decoder queue 531 | */ 532 | @property(nonatomic , strong)dispatch_queue_t decoderQueue; 533 | 534 | /** 535 | decoder 536 | */ 537 | @property(nonatomic , strong)RongRTCVideoDecoder *decoder; 538 | @end 539 | @implementation RongRTCServerSocket 540 | 541 | - (BOOL)createServerSocket{ 542 | if ([self createSocket] == -1) { 543 | return NO; 544 | } 545 | [self setRecvBuffer]; 546 | [self setRecvTimeout]; 547 | BOOL isB = [self bind]; 548 | BOOL isL = [self listen]; 549 | 550 | if (isB && isL) { 551 | _decoderQueue = dispatch_queue_create("com.rongcloud.decoderQueue", NULL); 552 | _frameTime = 0; 553 | [self createDecoder]; 554 | [self receive]; 555 | return YES; 556 | } else { 557 | return NO; 558 | } 559 | } 560 | - (void)createDecoder{ 561 | self.decoder = [[RongRTCVideoDecoder alloc] init]; 562 | self.decoder.delegate = self; 563 | RongRTCVideoEncoderSettings *settings = [[RongRTCVideoEncoderSettings alloc] init]; 564 | settings.width = 720; 565 | settings.height = 1280; 566 | settings.startBitrate = 300; 567 | settings.maxFramerate = 30; 568 | settings.minBitrate = 1000; 569 | [self.decoder configWithSettings:settings onQueue:_decoderQueue]; 570 | } 571 | -(void)recvData{ 572 | struct sockaddr_in rest; 573 | socklen_t rest_size = sizeof(struct sockaddr_in); 574 | self.acceptSock = accept(self.sock, (struct sockaddr *) &rest, &rest_size); 575 | while (self.acceptSock != -1) { 576 | DataHeader dataH; 577 | memset(&dataH, 0, sizeof(dataH)); 578 | 579 | if (![self receveData:(char *)&dataH length:sizeof(dataH)]) { 580 | continue; 581 | } 582 | PreHeader preH = dataH.preH; 583 | char pre = preH.pre[0]; 584 | if (pre == '&') { 585 | // rongcloud socket 586 | NSUInteger dataLenght = preH.dataLength; 587 | char *buff = (char *)malloc(sizeof(char) * dataLenght); 588 | if ([self receveData:(char *)buff length:dataLenght]) { 589 | NSData *data = [NSData dataWithBytes:buff length:dataLenght]; 590 | [self.decoder decode:data]; 591 | free(buff); 592 | } 593 | } else { 594 | NSLog(@"😁😁😁😁😁pre is not &"); 595 | return; 596 | } 597 | } 598 | } 599 | - (BOOL)receveData:(char *)data length:(NSUInteger)length{ 600 | LOCK(lock); 601 | int recvLength = 0; 602 | while (recvLength < length) { 603 | ssize_t res = recv(self.acceptSock, data, length - recvLength, 0); 604 | if (res == -1 || res == 0) { 605 | UNLOCK(lock); 606 | NSLog(@"😁😁😁😁😁recv data error"); 607 | break; 608 | } 609 | recvLength += res; 610 | data += res; 611 | } 612 | UNLOCK(lock); 613 | return YES; 614 | } 615 | 616 | -(void)didGetDecodeBuffer:(CVPixelBufferRef)pixelBuffer { 617 | _frameTime += 1000; 618 | CMTime pts = CMTimeMake(_frameTime, 1000); 619 | CMSampleBufferRef sampleBuffer = [RongRTCBufferUtil sampleBufferFromPixbuffer:pixelBuffer time:pts]; 620 | // 查看解码数据是否有问题,如果image能显示,就说明对了。 621 | // 通过打断点 将鼠标放在 iamge 脑袋上,就可以看到数据了,点击那个小眼睛 622 | UIImage *image = [RongRTCBufferUtil imageFromBuffer:sampleBuffer]; 623 | [self.delegate didProcessSampleBuffer:sampleBuffer]; 624 | CFRelease(sampleBuffer); 625 | } 626 | 627 | -(void)close{ 628 | int res = close(self.acceptSock); 629 | self.acceptSock = -1; 630 | NSLog(@"😁😁😁😁😁shut down server: %d",res); 631 | [super close]; 632 | } 633 | -(void)dealloc{ 634 | NSLog(@"😁😁😁😁😁dealoc server socket"); 635 | } 636 | @end 637 | 638 | 639 | ``` 640 | 641 | 这里,通过 socket 收到数据之后,会循环一直收数据,然后进行解码,最后通过 代理 `didGetDecodeBuffer ` 回调数据,然后再抛出代理给app层,通过第三方SDK发送,就可以了 642 | 643 | 644 | ## 3. videotoolbox 硬编码 645 | 646 | 647 | ``` 648 | 649 | // 650 | // RongRTCVideoEncoder.m 651 | // SealRTC 652 | // 653 | // Created by 孙承秀 on 2020/5/13. 654 | // Copyright © 2020 RongCloud. All rights reserved. 655 | // 656 | 657 | #import "RongRTCVideoEncoder.h" 658 | 659 | #import "helpers.h" 660 | 661 | @interface RongRTCVideoEncoder(){ 662 | VTCompressionSessionRef _compressionSession; 663 | int _frameTime; 664 | 665 | } 666 | /** 667 | settings 668 | */ 669 | @property(nonatomic , strong )RongRTCVideoEncoderSettings *settings; 670 | 671 | /** 672 | callback queue 673 | */ 674 | @property(nonatomic , strong )dispatch_queue_t callbackQueue; 675 | - (void)sendSpsAndPPSWithSampleBuffer:(CMSampleBufferRef)sampleBuffer; 676 | - (void)sendNaluData:(CMSampleBufferRef)sampleBuffer; 677 | @end 678 | 679 | void compressionOutputCallback(void *encoder, 680 | void *params, 681 | OSStatus status, 682 | VTEncodeInfoFlags infoFlags, 683 | CMSampleBufferRef sampleBuffer){ 684 | RongRTCVideoEncoder *videoEncoder = (__bridge RongRTCVideoEncoder *)encoder; 685 | if (status != noErr) { 686 | return; 687 | } 688 | if (infoFlags & kVTEncodeInfo_FrameDropped) { 689 | return; 690 | } 691 | BOOL isKeyFrame = NO; 692 | CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, 0); 693 | if (attachments != nullptr && CFArrayGetCount(attachments)) { 694 | CFDictionaryRef attachment = static_cast(CFArrayGetValueAtIndex(attachments, 0)) ; 695 | isKeyFrame = !CFDictionaryContainsKey(attachment, kCMSampleAttachmentKey_NotSync); 696 | } 697 | CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(sampleBuffer); 698 | CMBlockBufferRef contiguous_buffer = nullptr; 699 | if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) { 700 | status = CMBlockBufferCreateContiguous( 701 | nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer); 702 | if (status != noErr) { 703 | return; 704 | } 705 | } else { 706 | contiguous_buffer = block_buffer; 707 | CFRetain(contiguous_buffer); 708 | block_buffer = nullptr; 709 | } 710 | size_t block_buffer_size = CMBlockBufferGetDataLength(contiguous_buffer); 711 | if (isKeyFrame) { 712 | [videoEncoder sendSpsAndPPSWithSampleBuffer:sampleBuffer]; 713 | } 714 | if (contiguous_buffer) { 715 | CFRelease(contiguous_buffer); 716 | } 717 | [videoEncoder sendNaluData:sampleBuffer]; 718 | } 719 | 720 | @implementation RongRTCVideoEncoder 721 | 722 | @synthesize settings = _settings; 723 | @synthesize callbackQueue = _callbackQueue; 724 | 725 | - (BOOL)configWithSettings:(RongRTCVideoEncoderSettings *)settings onQueue:(nonnull dispatch_queue_t)queue{ 726 | self.settings = settings; 727 | if (queue) { 728 | _callbackQueue = queue; 729 | } else { 730 | _callbackQueue = dispatch_get_main_queue(); 731 | } 732 | if ([self resetCompressionSession:settings]) { 733 | _frameTime = 0; 734 | return YES; 735 | } else { 736 | return NO; 737 | } 738 | } 739 | - (BOOL)resetCompressionSession:(RongRTCVideoEncoderSettings *)settings { 740 | [self destroyCompressionSession]; 741 | OSStatus status = VTCompressionSessionCreate(nullptr, settings.width, settings.height, kCMVideoCodecType_H264, nullptr, nullptr, nullptr, compressionOutputCallback, (__bridge void * _Nullable)(self), &_compressionSession); 742 | if (status != noErr) { 743 | return NO; 744 | } 745 | [self configureCompressionSession:settings]; 746 | return YES; 747 | } 748 | - (void)configureCompressionSession:(RongRTCVideoEncoderSettings *)settings{ 749 | if (_compressionSession) { 750 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_RealTime, true); 751 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel); 752 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, false); 753 | 754 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, 10); 755 | uint32_t targetBps = settings.startBitrate * 1000; 756 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_AverageBitRate, targetBps); 757 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate, settings.maxFramerate); 758 | int bitRate = settings.width * settings.height * 3 * 4 * 4; 759 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_AverageBitRate, bitRate); 760 | int bitRateLimit = settings.width * settings.height * 3 * 4; 761 | SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_DataRateLimits, bitRateLimit); 762 | } 763 | } 764 | -(void)encode:(CMSampleBufferRef)sampleBuffer{ 765 | // CFRetain(sampleBuffer); 766 | // dispatch_async(_encodeQueue, ^{ 767 | CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer); 768 | CMTime pts = CMTimeMake(self->_frameTime++, 1000); 769 | VTEncodeInfoFlags flags; 770 | OSStatus res = VTCompressionSessionEncodeFrame(self->_compressionSession, 771 | imageBuffer, 772 | pts, 773 | kCMTimeInvalid, 774 | NULL, NULL, &flags); 775 | 776 | // CFRelease(sampleBuffer); 777 | if (res != noErr) { 778 | NSLog(@"encode frame error:%d", (int)res); 779 | VTCompressionSessionInvalidate(self->_compressionSession); 780 | CFRelease(self->_compressionSession); 781 | self->_compressionSession = NULL; 782 | return; 783 | } 784 | // }); 785 | 786 | } 787 | - (void)sendSpsAndPPSWithSampleBuffer:(CMSampleBufferRef)sampleBuffer{ 788 | CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer); 789 | const uint8_t *sps ; 790 | const uint8_t *pps; 791 | size_t spsSize ,ppsSize , spsCount,ppsCount; 792 | OSStatus spsStatus = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sps, &spsSize, &spsCount, NULL); 793 | OSStatus ppsStatus = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pps, &ppsSize, &ppsCount, NULL); 794 | if (spsStatus == noErr && ppsStatus == noErr) { 795 | const char bytes[] = "\x00\x00\x00\x01"; 796 | size_t length = (sizeof bytes) - 1; 797 | 798 | NSMutableData *spsData = [NSMutableData dataWithCapacity:4+ spsSize]; 799 | NSMutableData *ppsData = [NSMutableData dataWithCapacity:4 + ppsSize]; 800 | [spsData appendBytes:bytes length:length]; 801 | [spsData appendBytes:sps length:spsSize]; 802 | 803 | [ppsData appendBytes:bytes length:length]; 804 | [ppsData appendBytes:pps length:ppsSize]; 805 | if (self && self.callbackQueue) { 806 | dispatch_async(self.callbackQueue, ^{ 807 | if (self.delegate && [self.delegate respondsToSelector:@selector(spsData:ppsData:)]) { 808 | [self.delegate spsData:spsData ppsData:ppsData]; 809 | } 810 | }); 811 | } 812 | } else { 813 | NSLog(@"😁 sps status:%@,pps status:%@",@(spsStatus),@(ppsStatus)); 814 | } 815 | 816 | } 817 | - (void)sendNaluData:(CMSampleBufferRef)sampleBuffer{ 818 | size_t totalLength = 0; 819 | size_t lengthAtOffset=0; 820 | char *dataPointer; 821 | CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); 822 | OSStatus status1 = CMBlockBufferGetDataPointer(blockBuffer, 0, &lengthAtOffset, &totalLength, &dataPointer); 823 | if (status1 != noErr) { 824 | NSLog(@"video encoder error, status = %d", (int)status1); 825 | return; 826 | } 827 | static const int h264HeaderLength = 4; 828 | size_t bufferOffset = 0; 829 | while (bufferOffset < totalLength - h264HeaderLength) { 830 | 831 | uint32_t naluLength = 0; 832 | memcpy(&naluLength, dataPointer + bufferOffset, h264HeaderLength); 833 | naluLength = CFSwapInt32BigToHost(naluLength); 834 | 835 | const char bytes[] = "\x00\x00\x00\x01"; 836 | NSMutableData *naluData = [NSMutableData dataWithCapacity:4 + naluLength]; 837 | [naluData appendBytes:bytes length:4]; 838 | [naluData appendBytes:dataPointer + bufferOffset + h264HeaderLength length:naluLength]; 839 | dispatch_async(self.callbackQueue, ^{ 840 | if (self.delegate && [self.delegate respondsToSelector:@selector(naluData:)]) { 841 | [self.delegate naluData:naluData]; 842 | } 843 | }); 844 | bufferOffset += naluLength + h264HeaderLength; 845 | } 846 | } 847 | - (void)destroyCompressionSession{ 848 | if (_compressionSession) { 849 | VTCompressionSessionInvalidate(_compressionSession); 850 | CFRelease(_compressionSession); 851 | _compressionSession = nullptr; 852 | } 853 | } 854 | - (void)dealloc 855 | { 856 | if (_compressionSession) { 857 | VTCompressionSessionCompleteFrames(_compressionSession, kCMTimeInvalid); 858 | VTCompressionSessionInvalidate(_compressionSession); 859 | CFRelease(_compressionSession); 860 | _compressionSession = NULL; 861 | } 862 | } 863 | @end 864 | 865 | 866 | ``` 867 | 868 | 869 | ## 4. videotoolbox 解码 870 | 871 | 872 | ``` 873 | 874 | // 875 | // RongRTCVideoDecoder.m 876 | // SealRTC 877 | // 878 | // Created by 孙承秀 on 2020/5/14. 879 | // Copyright © 2020 RongCloud. All rights reserved. 880 | // 881 | 882 | #import "RongRTCVideoDecoder.h" 883 | #import 884 | 885 | #import "helpers.h" 886 | @interface RongRTCVideoDecoder(){ 887 | uint8_t *_sps; 888 | NSUInteger _spsSize; 889 | uint8_t *_pps; 890 | NSUInteger _ppsSize; 891 | CMVideoFormatDescriptionRef _videoFormatDescription; 892 | VTDecompressionSessionRef _decompressionSession; 893 | } 894 | /** 895 | settings 896 | */ 897 | @property(nonatomic , strong )RongRTCVideoEncoderSettings *settings; 898 | 899 | /** 900 | callback queue 901 | */ 902 | @property(nonatomic , strong )dispatch_queue_t callbackQueue; 903 | @end 904 | void DecoderOutputCallback(void * CM_NULLABLE decompressionOutputRefCon, 905 | void * CM_NULLABLE sourceFrameRefCon, 906 | OSStatus status, 907 | VTDecodeInfoFlags infoFlags, 908 | CM_NULLABLE CVImageBufferRef imageBuffer, 909 | CMTime presentationTimeStamp, 910 | CMTime presentationDuration ) { 911 | if (status != noErr) { 912 | NSLog(@"😁 decoder callback error :%@", @(status)); 913 | return; 914 | } 915 | CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon; 916 | *outputPixelBuffer = CVPixelBufferRetain(imageBuffer); 917 | RongRTCVideoDecoder *decoder = (__bridge RongRTCVideoDecoder *)(decompressionOutputRefCon); 918 | dispatch_async(decoder.callbackQueue, ^{ 919 | [decoder.delegate didGetDecodeBuffer:imageBuffer]; 920 | CVPixelBufferRelease(imageBuffer); 921 | }); 922 | } 923 | @implementation RongRTCVideoDecoder 924 | 925 | @synthesize settings = _settings; 926 | @synthesize callbackQueue = _callbackQueue; 927 | 928 | 929 | -(BOOL)configWithSettings:(RongRTCVideoEncoderSettings *)settings onQueue:(dispatch_queue_t)queue{ 930 | self.settings = settings; 931 | if (queue) { 932 | _callbackQueue = queue; 933 | } else { 934 | _callbackQueue = dispatch_get_main_queue(); 935 | } 936 | return YES; 937 | } 938 | - (BOOL)createVT{ 939 | if (_decompressionSession) { 940 | return YES; 941 | } 942 | const uint8_t * const parameterSetPointers[2] = {_sps, _pps}; 943 | const size_t parameterSetSizes[2] = {_spsSize, _ppsSize}; 944 | int naluHeaderLen = 4; 945 | OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault, 2, parameterSetPointers, parameterSetSizes, naluHeaderLen, &_videoFormatDescription ); 946 | if (status != noErr) { 947 | NSLog(@"😁😁😁😁😁CMVideoFormatDescriptionCreateFromH264ParameterSets error:%@", @(status)); 948 | return false; 949 | } 950 | NSDictionary *destinationImageBufferAttributes = 951 | @{ 952 | (id)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange], 953 | (id)kCVPixelBufferWidthKey: [NSNumber numberWithInteger:self.settings.width], 954 | (id)kCVPixelBufferHeightKey: [NSNumber numberWithInteger:self.settings.height], 955 | (id)kCVPixelBufferOpenGLCompatibilityKey: [NSNumber numberWithBool:true] 956 | }; 957 | VTDecompressionOutputCallbackRecord CallBack; 958 | CallBack.decompressionOutputCallback = DecoderOutputCallback; 959 | CallBack.decompressionOutputRefCon = (__bridge void * _Nullable)(self); 960 | status = VTDecompressionSessionCreate(kCFAllocatorDefault, _videoFormatDescription, NULL, (__bridge CFDictionaryRef _Nullable)(destinationImageBufferAttributes), &CallBack, &_decompressionSession); 961 | 962 | if (status != noErr) { 963 | NSLog(@"😁😁😁😁😁VTDecompressionSessionCreate error:%@", @(status)); 964 | return false; 965 | } 966 | status = VTSessionSetProperty(_decompressionSession, kVTDecompressionPropertyKey_RealTime,kCFBooleanTrue); 967 | 968 | return YES; 969 | } 970 | 971 | - (CVPixelBufferRef)decode:(uint8_t *)frame withSize:(uint32_t)frameSize { 972 | 973 | CVPixelBufferRef outputPixelBuffer = NULL; 974 | CMBlockBufferRef blockBuffer = NULL; 975 | CMBlockBufferFlags flag0 = 0; 976 | 977 | OSStatus status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, frame, frameSize, kCFAllocatorNull, NULL, 0, frameSize, flag0, &blockBuffer); 978 | 979 | if (status != kCMBlockBufferNoErr) { 980 | NSLog(@"😁😁😁😁😁VCMBlockBufferCreateWithMemoryBlock code=%d", (int)status); 981 | CFRelease(blockBuffer); 982 | return outputPixelBuffer; 983 | } 984 | 985 | CMSampleBufferRef sampleBuffer = NULL; 986 | const size_t sampleSizeArray[] = {frameSize}; 987 | 988 | status = CMSampleBufferCreateReady(kCFAllocatorDefault, blockBuffer, _videoFormatDescription, 1, 0, NULL, 1, sampleSizeArray, &sampleBuffer); 989 | 990 | if (status != noErr || !sampleBuffer) { 991 | NSLog(@"😁😁😁😁😁CMSampleBufferCreateReady failed status=%d", (int)status); 992 | CFRelease(blockBuffer); 993 | return outputPixelBuffer; 994 | } 995 | 996 | VTDecodeFrameFlags flag1 = kVTDecodeFrame_1xRealTimePlayback; 997 | VTDecodeInfoFlags infoFlag = kVTDecodeInfo_Asynchronous; 998 | 999 | status = VTDecompressionSessionDecodeFrame(_decompressionSession, sampleBuffer, flag1, &outputPixelBuffer, &infoFlag); 1000 | 1001 | if (status == kVTInvalidSessionErr) { 1002 | NSLog(@"😁😁😁😁😁decode frame error with session err status =%d", (int)status); 1003 | [self resetVT]; 1004 | } else { 1005 | if (status != noErr) { 1006 | NSLog(@"😁😁😁😁😁decode frame error with status =%d", (int)status); 1007 | } 1008 | 1009 | } 1010 | 1011 | CFRelease(sampleBuffer); 1012 | CFRelease(blockBuffer); 1013 | 1014 | return outputPixelBuffer; 1015 | } 1016 | - (void)resetVT{ 1017 | [self destorySession]; 1018 | [self createVT]; 1019 | } 1020 | -(void)decode:(NSData *)data{ 1021 | // dispatch_async(_callbackQueue, ^{ 1022 | uint8_t *frame = (uint8_t*)[data bytes]; 1023 | uint32_t length = data.length; 1024 | uint32_t nalSize = (uint32_t)(length - 4); 1025 | uint32_t *pNalSize = (uint32_t *)frame; 1026 | *pNalSize = CFSwapInt32HostToBig(nalSize); 1027 | 1028 | int type = (frame[4] & 0x1F); 1029 | CVPixelBufferRef pixelBuffer = NULL; 1030 | switch (type) { 1031 | case 0x05: 1032 | if ([self createVT]) { 1033 | pixelBuffer= [self decode:frame withSize:length]; 1034 | } 1035 | break; 1036 | case 0x07: 1037 | self->_spsSize = length - 4; 1038 | self->_sps = (uint8_t *)malloc(self->_spsSize); 1039 | memcpy(self->_sps, &frame[4], self->_spsSize); 1040 | break; 1041 | case 0x08: 1042 | self->_ppsSize = length - 4; 1043 | self->_pps = (uint8_t *)malloc(self->_ppsSize); 1044 | memcpy(self->_pps, &frame[4], self->_ppsSize); 1045 | break; 1046 | default: 1047 | if ([self createVT]) { 1048 | pixelBuffer = [self decode:frame withSize:length]; 1049 | } 1050 | break; 1051 | } 1052 | // }); 1053 | } 1054 | 1055 | - (void)dealloc 1056 | { 1057 | [self destorySession]; 1058 | 1059 | } 1060 | - (void)destorySession{ 1061 | if (_decompressionSession) { 1062 | VTDecompressionSessionInvalidate(_decompressionSession); 1063 | CFRelease(_decompressionSession); 1064 | _decompressionSession = NULL; 1065 | } 1066 | } 1067 | @end 1068 | 1069 | 1070 | ``` 1071 | 1072 | ## 5. 工具类 1073 | 1074 | ``` 1075 | // 1076 | // RongRTCBufferUtil.m 1077 | // SealRTC 1078 | // 1079 | // Created by 孙承秀 on 2020/5/8. 1080 | // Copyright © 2020 RongCloud. All rights reserved. 1081 | // 1082 | 1083 | #import "RongRTCBufferUtil.h" 1084 | 1085 | /// 下面的这些方法,一定要记得release,有的没有在方法里面release,但是在外面release了,要不然会内存泄漏 1086 | @implementation RongRTCBufferUtil 1087 | + (UIImage *)imageFromBuffer:(CMSampleBufferRef)buffer { 1088 | 1089 | CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(buffer); 1090 | 1091 | CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer]; 1092 | 1093 | CIContext *temporaryContext = [CIContext contextWithOptions:nil]; 1094 | CGImageRef videoImage = [temporaryContext createCGImage:ciImage fromRect:CGRectMake(0, 0, CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer))]; 1095 | 1096 | UIImage *image = [UIImage imageWithCGImage:videoImage]; 1097 | CGImageRelease(videoImage); 1098 | 1099 | return image; 1100 | } 1101 | 1102 | + (UIImage *)compressImage:(UIImage *)image newWidth:(CGFloat)newImageWidth 1103 | { 1104 | if (!image) return nil; 1105 | float imageWidth = image.size.width; 1106 | float imageHeight = image.size.height; 1107 | float width = newImageWidth; 1108 | float height = image.size.height/(image.size.width/width); 1109 | float widthScale = imageWidth /width; 1110 | float heightScale = imageHeight /height; 1111 | UIGraphicsBeginImageContext(CGSizeMake(width, height)); 1112 | if (widthScale > heightScale) { 1113 | [image drawInRect:CGRectMake(0, 0, imageWidth /heightScale , height)]; 1114 | } 1115 | else { 1116 | [image drawInRect:CGRectMake(0, 0, width , imageHeight /widthScale)]; 1117 | } 1118 | UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 1119 | UIGraphicsEndImageContext(); 1120 | return newImage; 1121 | 1122 | } 1123 | +(CVPixelBufferRef)CVPixelBufferRefFromUiImage:(UIImage *)img { 1124 | 1125 | CGSize size = img.size; 1126 | CGImageRef image = [img CGImage]; 1127 | 1128 | NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 1129 | [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, 1130 | [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil]; 1131 | CVPixelBufferRef pxbuffer = NULL; 1132 | CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options, &pxbuffer); 1133 | 1134 | NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); 1135 | 1136 | CVPixelBufferLockBaseAddress(pxbuffer, 0); 1137 | void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); 1138 | NSParameterAssert(pxdata != NULL); 1139 | 1140 | CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 1141 | CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, 4*size.width, rgbColorSpace, kCGImageAlphaPremultipliedFirst); 1142 | NSParameterAssert(context); 1143 | 1144 | CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); 1145 | 1146 | CGColorSpaceRelease(rgbColorSpace); 1147 | CGContextRelease(context); 1148 | 1149 | CVPixelBufferUnlockBaseAddress(pxbuffer, 0); 1150 | 1151 | return pxbuffer; 1152 | } 1153 | + (CMSampleBufferRef)sampleBufferFromPixbuffer:(CVPixelBufferRef)pixbuffer time:(CMTime)time{ 1154 | 1155 | CMSampleBufferRef sampleBuffer = NULL; 1156 | 1157 | // //获取视频信息 1158 | CMVideoFormatDescriptionRef videoInfo = NULL; 1159 | OSStatus result = CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixbuffer, &videoInfo); 1160 | CMTime currentTime = time; 1161 | 1162 | // CMSampleTimingInfo timing = {currentTime, currentTime, kCMTimeInvalid}; 1163 | CMSampleTimingInfo timing = {currentTime, currentTime, kCMTimeInvalid}; 1164 | result = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault,pixbuffer, true, NULL, NULL, videoInfo, &timing, &sampleBuffer); 1165 | CFRelease(videoInfo); 1166 | return sampleBuffer; 1167 | } 1168 | 1169 | + (size_t)getCMTimeSize{ 1170 | size_t size = sizeof(CMTime); 1171 | return size; 1172 | } 1173 | 1174 | @end 1175 | 1176 | 1177 | ``` 1178 | 1179 | 1180 | 这个类主要是 cpu 级别的,CMSampleBufferRef 转 UIImage,UIImage 转 CVPixelBufferRef, CVPixelBufferRef 转 CMSampleBufferRef,还有裁剪图片,注意这里有的没有release是我在外面release了,一定要注意内存泄漏问题,要不然你的app会内存暴涨, 1181 | 1182 | # 总结 1183 | 1184 | 糖果的坑: 1185 | 1186 | 1. 这里可能都是贴的代码,文字很少,时间紧迫,给大家提供思路和我经历的坑就好了,一开始做这个的时候,没有使用 videotoolbox,使用cpu对bugger进行处理,软编软解,其实也是通过socket发送出去,但是发现,extension 屏幕是有内存限制的,最大50M,在extension 我通过裁剪和压缩的代码,发现经常会崩溃,超过50M,程序被杀死,然后每次压缩的数据其实也很大,效果很不好,后来想到了用苹果的 videotoolbox。 1187 | 2. videotoolbox 后台解码一直失败,肯定不行的,屏幕共享是必须要在后台可以录制的,经过 google 之后,发现,把videotoolbox 重启一下就可以了,在我的代码里面有体现 1188 | 3. 解码成功,但是通过融云的库发出去,帧率很低不连贯,图片都是有的,而且要是渲染也是没有问题的,但是通过我们融云的webrtc发送的话,发现帧率为0或者1然后就开始改pts,想过用我们融云的SDK采集的摄像头的pts发现可以,但是,有个问题,开发者不可能一直这么用,最后经过改造之后,终于可以了,这个坑,憋了我好几天,终于在不依赖我们SDK的情况下,实现无缝抽出屏幕共享模块。 1189 | 1190 | 1191 | 上面的代码可能还有bug和问题,写到这里,demo已经能看到效果了,如果有什么bug或者问题,你们给我留言我改下就可以了,但至少我觉得思路是正确没有问题的应该。 1192 | 1193 | 上面的代码在github可以下载,要想看到效果,就在 1194 | 1195 | ``` 1196 | -(void)didGetDecodeBuffer:(CVPixelBufferRef)pixelBuffer { 1197 | _frameTime += 1000; 1198 | CMTime pts = CMTimeMake(_frameTime, 1000); 1199 | CMSampleBufferRef sampleBuffer = [RongRTCBufferUtil sampleBufferFromPixbuffer:pixelBuffer time:pts]; 1200 | // 查看解码数据是否有问题,如果image能显示,就说明对了。 1201 | // 通过打断点 将鼠标放在 iamge 脑袋上,就可以看到数据了,点击那个小眼睛 1202 | UIImage *image = [RongRTCBufferUtil imageFromBuffer:sampleBuffer]; 1203 | [self.delegate didProcessSampleBuffer:sampleBuffer]; 1204 | CFRelease(sampleBuffer); 1205 | } 1206 | 1207 | ``` 1208 | 1209 | 这个方法的image下面,打一个断点,鼠标放在 image上面,然后点击小眼睛,就可以看到extension发过来的每一帧图片数据了。 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | -------------------------------------------------------------------------------- /Socket_Replykit/Socket_Replykit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E8A89B9D2473B0D400A92284 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89B9C2473B0D400A92284 /* AppDelegate.m */; }; 11 | E8A89BA02473B0D400A92284 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89B9F2473B0D400A92284 /* SceneDelegate.m */; }; 12 | E8A89BA32473B0D400A92284 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89BA22473B0D400A92284 /* ViewController.m */; }; 13 | E8A89BA62473B0D400A92284 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E8A89BA42473B0D400A92284 /* Main.storyboard */; }; 14 | E8A89BA82473B0D900A92284 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E8A89BA72473B0D900A92284 /* Assets.xcassets */; }; 15 | E8A89BAB2473B0DA00A92284 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E8A89BA92473B0D900A92284 /* LaunchScreen.storyboard */; }; 16 | E8A89BAE2473B0DA00A92284 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89BAD2473B0DA00A92284 /* main.m */; }; 17 | E8A89BB82473B0DA00A92284 /* Socket_ReplykitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89BB72473B0DA00A92284 /* Socket_ReplykitTests.m */; }; 18 | E8A89BC32473B0DA00A92284 /* Socket_ReplykitUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89BC22473B0DA00A92284 /* Socket_ReplykitUITests.m */; }; 19 | E8A89C182473B10500A92284 /* RongRTCCodec.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C0B2473B10500A92284 /* RongRTCCodec.m */; }; 20 | E8A89C192473B10500A92284 /* helpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C0C2473B10500A92284 /* helpers.mm */; }; 21 | E8A89C1A2473B10500A92284 /* RongRTCVideoEncoderSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C0E2473B10500A92284 /* RongRTCVideoEncoderSettings.m */; }; 22 | E8A89C1B2473B10500A92284 /* RongRTCVideoDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C142473B10500A92284 /* RongRTCVideoDecoder.mm */; }; 23 | E8A89C1C2473B10500A92284 /* RongRTCVideoEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C172473B10500A92284 /* RongRTCVideoEncoder.mm */; }; 24 | E8A89C242473B18500A92284 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A89C232473B18500A92284 /* ReplayKit.framework */; }; 25 | E8A89C282473B18500A92284 /* SampleHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C272473B18500A92284 /* SampleHandler.m */; }; 26 | E8A89C2F2473B18500A92284 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A89C232473B18500A92284 /* ReplayKit.framework */; }; 27 | E8A89C312473B18500A92284 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8A89C302473B18500A92284 /* UIKit.framework */; }; 28 | E8A89C352473B18500A92284 /* BroadcastSetupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C342473B18500A92284 /* BroadcastSetupViewController.m */; }; 29 | E8A89C392473B18500A92284 /* SocketReplySetupUI.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E8A89C2E2473B18500A92284 /* SocketReplySetupUI.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 30 | E8A89C3C2473B18500A92284 /* SocketReply.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E8A89C212473B18500A92284 /* SocketReply.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 31 | E8A89C442473B1C800A92284 /* RongRTCVideoDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C142473B10500A92284 /* RongRTCVideoDecoder.mm */; }; 32 | E8A89C452473B1CD00A92284 /* RongRTCCodec.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C0B2473B10500A92284 /* RongRTCCodec.m */; }; 33 | E8A89C462473B1D100A92284 /* RongRTCVideoEncoderSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C0E2473B10500A92284 /* RongRTCVideoEncoderSettings.m */; }; 34 | E8A89C472473B1D700A92284 /* helpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C0C2473B10500A92284 /* helpers.mm */; }; 35 | E8A89C482473B1DB00A92284 /* RongRTCVideoEncoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C172473B10500A92284 /* RongRTCVideoEncoder.mm */; }; 36 | E8A89C552473B22400A92284 /* RongRTCBufferUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C4E2473B22400A92284 /* RongRTCBufferUtil.m */; }; 37 | E8A89C562473B22400A92284 /* RongRTCClientSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C502473B22400A92284 /* RongRTCClientSocket.m */; }; 38 | E8A89C572473B22400A92284 /* RongRTCServerSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C512473B22400A92284 /* RongRTCServerSocket.m */; }; 39 | E8A89C582473B22400A92284 /* RongRTCThread.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C522473B22400A92284 /* RongRTCThread.m */; }; 40 | E8A89C592473B22400A92284 /* RongRTCSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C542473B22400A92284 /* RongRTCSocket.m */; }; 41 | E8A89C5A2473B2B900A92284 /* RongRTCSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C542473B22400A92284 /* RongRTCSocket.m */; }; 42 | E8A89C5B2473B2BC00A92284 /* RongRTCClientSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C502473B22400A92284 /* RongRTCClientSocket.m */; }; 43 | E8A89C5C2473B2BF00A92284 /* RongRTCServerSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C512473B22400A92284 /* RongRTCServerSocket.m */; }; 44 | E8A89C5D2473B2C100A92284 /* RongRTCBufferUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C4E2473B22400A92284 /* RongRTCBufferUtil.m */; }; 45 | E8A89C5E2473B2C800A92284 /* RongRTCThread.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C522473B22400A92284 /* RongRTCThread.m */; }; 46 | E8A89C622473C32200A92284 /* OtherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E8A89C612473C32200A92284 /* OtherTest.m */; }; 47 | /* End PBXBuildFile section */ 48 | 49 | /* Begin PBXContainerItemProxy section */ 50 | E8A89BB42473B0DA00A92284 /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = E8A89B902473B0D400A92284 /* Project object */; 53 | proxyType = 1; 54 | remoteGlobalIDString = E8A89B972473B0D400A92284; 55 | remoteInfo = Socket_Replykit; 56 | }; 57 | E8A89BBF2473B0DA00A92284 /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = E8A89B902473B0D400A92284 /* Project object */; 60 | proxyType = 1; 61 | remoteGlobalIDString = E8A89B972473B0D400A92284; 62 | remoteInfo = Socket_Replykit; 63 | }; 64 | E8A89C372473B18500A92284 /* PBXContainerItemProxy */ = { 65 | isa = PBXContainerItemProxy; 66 | containerPortal = E8A89B902473B0D400A92284 /* Project object */; 67 | proxyType = 1; 68 | remoteGlobalIDString = E8A89C2D2473B18500A92284; 69 | remoteInfo = SocketReplySetupUI; 70 | }; 71 | E8A89C3A2473B18500A92284 /* PBXContainerItemProxy */ = { 72 | isa = PBXContainerItemProxy; 73 | containerPortal = E8A89B902473B0D400A92284 /* Project object */; 74 | proxyType = 1; 75 | remoteGlobalIDString = E8A89C202473B18500A92284; 76 | remoteInfo = SocketReply; 77 | }; 78 | /* End PBXContainerItemProxy section */ 79 | 80 | /* Begin PBXCopyFilesBuildPhase section */ 81 | E8A89C432473B18500A92284 /* Embed App Extensions */ = { 82 | isa = PBXCopyFilesBuildPhase; 83 | buildActionMask = 2147483647; 84 | dstPath = ""; 85 | dstSubfolderSpec = 13; 86 | files = ( 87 | E8A89C3C2473B18500A92284 /* SocketReply.appex in Embed App Extensions */, 88 | E8A89C392473B18500A92284 /* SocketReplySetupUI.appex in Embed App Extensions */, 89 | ); 90 | name = "Embed App Extensions"; 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | /* End PBXCopyFilesBuildPhase section */ 94 | 95 | /* Begin PBXFileReference section */ 96 | E8A89B982473B0D400A92284 /* Socket_Replykit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Socket_Replykit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97 | E8A89B9B2473B0D400A92284 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 98 | E8A89B9C2473B0D400A92284 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 99 | E8A89B9E2473B0D400A92284 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 100 | E8A89B9F2473B0D400A92284 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 101 | E8A89BA12473B0D400A92284 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 102 | E8A89BA22473B0D400A92284 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 103 | E8A89BA52473B0D400A92284 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 104 | E8A89BA72473B0D900A92284 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 105 | E8A89BAA2473B0D900A92284 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 106 | E8A89BAC2473B0DA00A92284 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 107 | E8A89BAD2473B0DA00A92284 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 108 | E8A89BB32473B0DA00A92284 /* Socket_ReplykitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Socket_ReplykitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 109 | E8A89BB72473B0DA00A92284 /* Socket_ReplykitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Socket_ReplykitTests.m; sourceTree = ""; }; 110 | E8A89BB92473B0DA00A92284 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 111 | E8A89BBE2473B0DA00A92284 /* Socket_ReplykitUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Socket_ReplykitUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 112 | E8A89BC22473B0DA00A92284 /* Socket_ReplykitUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Socket_ReplykitUITests.m; sourceTree = ""; }; 113 | E8A89BC42473B0DA00A92284 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 114 | E8A89C0B2473B10500A92284 /* RongRTCCodec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RongRTCCodec.m; sourceTree = ""; }; 115 | E8A89C0C2473B10500A92284 /* helpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = helpers.mm; sourceTree = ""; }; 116 | E8A89C0D2473B10500A92284 /* RongRTCCodecProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCCodecProtocol.h; sourceTree = ""; }; 117 | E8A89C0E2473B10500A92284 /* RongRTCVideoEncoderSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RongRTCVideoEncoderSettings.m; sourceTree = ""; }; 118 | E8A89C0F2473B10500A92284 /* RongRTCCodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCCodec.h; sourceTree = ""; }; 119 | E8A89C102473B10500A92284 /* RongRTCVideoEncoderSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCVideoEncoderSettings.h; sourceTree = ""; }; 120 | E8A89C112473B10500A92284 /* helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = helpers.h; sourceTree = ""; }; 121 | E8A89C132473B10500A92284 /* RongRTCVideoDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCVideoDecoder.h; sourceTree = ""; }; 122 | E8A89C142473B10500A92284 /* RongRTCVideoDecoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RongRTCVideoDecoder.mm; sourceTree = ""; }; 123 | E8A89C162473B10500A92284 /* RongRTCVideoEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCVideoEncoder.h; sourceTree = ""; }; 124 | E8A89C172473B10500A92284 /* RongRTCVideoEncoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RongRTCVideoEncoder.mm; sourceTree = ""; }; 125 | E8A89C212473B18500A92284 /* SocketReply.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SocketReply.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 126 | E8A89C232473B18500A92284 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; }; 127 | E8A89C262473B18500A92284 /* SampleHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleHandler.h; sourceTree = ""; }; 128 | E8A89C272473B18500A92284 /* SampleHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleHandler.m; sourceTree = ""; }; 129 | E8A89C292473B18500A92284 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 130 | E8A89C2E2473B18500A92284 /* SocketReplySetupUI.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SocketReplySetupUI.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 131 | E8A89C302473B18500A92284 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 132 | E8A89C332473B18500A92284 /* BroadcastSetupViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BroadcastSetupViewController.h; sourceTree = ""; }; 133 | E8A89C342473B18500A92284 /* BroadcastSetupViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BroadcastSetupViewController.m; sourceTree = ""; }; 134 | E8A89C362473B18500A92284 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 135 | E8A89C4A2473B22400A92284 /* RongRTCClientSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCClientSocket.h; sourceTree = ""; }; 136 | E8A89C4B2473B22400A92284 /* RongRTCThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCThread.h; sourceTree = ""; }; 137 | E8A89C4C2473B22400A92284 /* RongRTCServerSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCServerSocket.h; sourceTree = ""; }; 138 | E8A89C4D2473B22400A92284 /* RongRTCSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCSocket.h; sourceTree = ""; }; 139 | E8A89C4E2473B22400A92284 /* RongRTCBufferUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RongRTCBufferUtil.m; sourceTree = ""; }; 140 | E8A89C4F2473B22400A92284 /* RongRTCSocketHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCSocketHeader.h; sourceTree = ""; }; 141 | E8A89C502473B22400A92284 /* RongRTCClientSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RongRTCClientSocket.m; sourceTree = ""; }; 142 | E8A89C512473B22400A92284 /* RongRTCServerSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RongRTCServerSocket.m; sourceTree = ""; }; 143 | E8A89C522473B22400A92284 /* RongRTCThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RongRTCThread.m; sourceTree = ""; }; 144 | E8A89C532473B22400A92284 /* RongRTCBufferUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RongRTCBufferUtil.h; sourceTree = ""; }; 145 | E8A89C542473B22400A92284 /* RongRTCSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RongRTCSocket.m; sourceTree = ""; }; 146 | E8A89C602473C32200A92284 /* OtherTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OtherTest.h; sourceTree = ""; }; 147 | E8A89C612473C32200A92284 /* OtherTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OtherTest.m; sourceTree = ""; }; 148 | /* End PBXFileReference section */ 149 | 150 | /* Begin PBXFrameworksBuildPhase section */ 151 | E8A89B952473B0D400A92284 /* Frameworks */ = { 152 | isa = PBXFrameworksBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | E8A89BB02473B0DA00A92284 /* Frameworks */ = { 159 | isa = PBXFrameworksBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | ); 163 | runOnlyForDeploymentPostprocessing = 0; 164 | }; 165 | E8A89BBB2473B0DA00A92284 /* Frameworks */ = { 166 | isa = PBXFrameworksBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | E8A89C1E2473B18500A92284 /* Frameworks */ = { 173 | isa = PBXFrameworksBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | E8A89C242473B18500A92284 /* ReplayKit.framework in Frameworks */, 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | }; 180 | E8A89C2B2473B18500A92284 /* Frameworks */ = { 181 | isa = PBXFrameworksBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | E8A89C2F2473B18500A92284 /* ReplayKit.framework in Frameworks */, 185 | E8A89C312473B18500A92284 /* UIKit.framework in Frameworks */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXFrameworksBuildPhase section */ 190 | 191 | /* Begin PBXGroup section */ 192 | E8A89B8F2473B0D400A92284 = { 193 | isa = PBXGroup; 194 | children = ( 195 | E8A89B9A2473B0D400A92284 /* Socket_Replykit */, 196 | E8A89BB62473B0DA00A92284 /* Socket_ReplykitTests */, 197 | E8A89BC12473B0DA00A92284 /* Socket_ReplykitUITests */, 198 | E8A89C252473B18500A92284 /* SocketReply */, 199 | E8A89C322473B18500A92284 /* SocketReplySetupUI */, 200 | E8A89C222473B18500A92284 /* Frameworks */, 201 | E8A89B992473B0D400A92284 /* Products */, 202 | ); 203 | sourceTree = ""; 204 | }; 205 | E8A89B992473B0D400A92284 /* Products */ = { 206 | isa = PBXGroup; 207 | children = ( 208 | E8A89B982473B0D400A92284 /* Socket_Replykit.app */, 209 | E8A89BB32473B0DA00A92284 /* Socket_ReplykitTests.xctest */, 210 | E8A89BBE2473B0DA00A92284 /* Socket_ReplykitUITests.xctest */, 211 | E8A89C212473B18500A92284 /* SocketReply.appex */, 212 | E8A89C2E2473B18500A92284 /* SocketReplySetupUI.appex */, 213 | ); 214 | name = Products; 215 | sourceTree = ""; 216 | }; 217 | E8A89B9A2473B0D400A92284 /* Socket_Replykit */ = { 218 | isa = PBXGroup; 219 | children = ( 220 | E8A89C5F2473C30F00A92284 /* OtherTest */, 221 | E8A89C492473B22400A92284 /* Socket */, 222 | E8A89C0A2473B10500A92284 /* Codec */, 223 | E8A89B9B2473B0D400A92284 /* AppDelegate.h */, 224 | E8A89B9C2473B0D400A92284 /* AppDelegate.m */, 225 | E8A89B9E2473B0D400A92284 /* SceneDelegate.h */, 226 | E8A89B9F2473B0D400A92284 /* SceneDelegate.m */, 227 | E8A89BA12473B0D400A92284 /* ViewController.h */, 228 | E8A89BA22473B0D400A92284 /* ViewController.m */, 229 | E8A89BA42473B0D400A92284 /* Main.storyboard */, 230 | E8A89BA72473B0D900A92284 /* Assets.xcassets */, 231 | E8A89BA92473B0D900A92284 /* LaunchScreen.storyboard */, 232 | E8A89BAC2473B0DA00A92284 /* Info.plist */, 233 | E8A89BAD2473B0DA00A92284 /* main.m */, 234 | ); 235 | path = Socket_Replykit; 236 | sourceTree = ""; 237 | }; 238 | E8A89BB62473B0DA00A92284 /* Socket_ReplykitTests */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | E8A89BB72473B0DA00A92284 /* Socket_ReplykitTests.m */, 242 | E8A89BB92473B0DA00A92284 /* Info.plist */, 243 | ); 244 | path = Socket_ReplykitTests; 245 | sourceTree = ""; 246 | }; 247 | E8A89BC12473B0DA00A92284 /* Socket_ReplykitUITests */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | E8A89BC22473B0DA00A92284 /* Socket_ReplykitUITests.m */, 251 | E8A89BC42473B0DA00A92284 /* Info.plist */, 252 | ); 253 | path = Socket_ReplykitUITests; 254 | sourceTree = ""; 255 | }; 256 | E8A89C0A2473B10500A92284 /* Codec */ = { 257 | isa = PBXGroup; 258 | children = ( 259 | E8A89C152473B10500A92284 /* Encoder */, 260 | E8A89C122473B10500A92284 /* Decoder */, 261 | E8A89C0F2473B10500A92284 /* RongRTCCodec.h */, 262 | E8A89C0B2473B10500A92284 /* RongRTCCodec.m */, 263 | E8A89C102473B10500A92284 /* RongRTCVideoEncoderSettings.h */, 264 | E8A89C0E2473B10500A92284 /* RongRTCVideoEncoderSettings.m */, 265 | E8A89C0D2473B10500A92284 /* RongRTCCodecProtocol.h */, 266 | E8A89C112473B10500A92284 /* helpers.h */, 267 | E8A89C0C2473B10500A92284 /* helpers.mm */, 268 | ); 269 | path = Codec; 270 | sourceTree = ""; 271 | }; 272 | E8A89C122473B10500A92284 /* Decoder */ = { 273 | isa = PBXGroup; 274 | children = ( 275 | E8A89C132473B10500A92284 /* RongRTCVideoDecoder.h */, 276 | E8A89C142473B10500A92284 /* RongRTCVideoDecoder.mm */, 277 | ); 278 | path = Decoder; 279 | sourceTree = ""; 280 | }; 281 | E8A89C152473B10500A92284 /* Encoder */ = { 282 | isa = PBXGroup; 283 | children = ( 284 | E8A89C162473B10500A92284 /* RongRTCVideoEncoder.h */, 285 | E8A89C172473B10500A92284 /* RongRTCVideoEncoder.mm */, 286 | ); 287 | path = Encoder; 288 | sourceTree = ""; 289 | }; 290 | E8A89C222473B18500A92284 /* Frameworks */ = { 291 | isa = PBXGroup; 292 | children = ( 293 | E8A89C232473B18500A92284 /* ReplayKit.framework */, 294 | E8A89C302473B18500A92284 /* UIKit.framework */, 295 | ); 296 | name = Frameworks; 297 | sourceTree = ""; 298 | }; 299 | E8A89C252473B18500A92284 /* SocketReply */ = { 300 | isa = PBXGroup; 301 | children = ( 302 | E8A89C262473B18500A92284 /* SampleHandler.h */, 303 | E8A89C272473B18500A92284 /* SampleHandler.m */, 304 | E8A89C292473B18500A92284 /* Info.plist */, 305 | ); 306 | path = SocketReply; 307 | sourceTree = ""; 308 | }; 309 | E8A89C322473B18500A92284 /* SocketReplySetupUI */ = { 310 | isa = PBXGroup; 311 | children = ( 312 | E8A89C332473B18500A92284 /* BroadcastSetupViewController.h */, 313 | E8A89C342473B18500A92284 /* BroadcastSetupViewController.m */, 314 | E8A89C362473B18500A92284 /* Info.plist */, 315 | ); 316 | path = SocketReplySetupUI; 317 | sourceTree = ""; 318 | }; 319 | E8A89C492473B22400A92284 /* Socket */ = { 320 | isa = PBXGroup; 321 | children = ( 322 | E8A89C9624761F6500A92284 /* Server */, 323 | E8A89C9524761F5D00A92284 /* Client */, 324 | E8A89C4F2473B22400A92284 /* RongRTCSocketHeader.h */, 325 | E8A89C4D2473B22400A92284 /* RongRTCSocket.h */, 326 | E8A89C542473B22400A92284 /* RongRTCSocket.m */, 327 | E8A89C532473B22400A92284 /* RongRTCBufferUtil.h */, 328 | E8A89C4E2473B22400A92284 /* RongRTCBufferUtil.m */, 329 | E8A89C4B2473B22400A92284 /* RongRTCThread.h */, 330 | E8A89C522473B22400A92284 /* RongRTCThread.m */, 331 | ); 332 | path = Socket; 333 | sourceTree = ""; 334 | }; 335 | E8A89C5F2473C30F00A92284 /* OtherTest */ = { 336 | isa = PBXGroup; 337 | children = ( 338 | E8A89C602473C32200A92284 /* OtherTest.h */, 339 | E8A89C612473C32200A92284 /* OtherTest.m */, 340 | ); 341 | path = OtherTest; 342 | sourceTree = ""; 343 | }; 344 | E8A89C9524761F5D00A92284 /* Client */ = { 345 | isa = PBXGroup; 346 | children = ( 347 | E8A89C4A2473B22400A92284 /* RongRTCClientSocket.h */, 348 | E8A89C502473B22400A92284 /* RongRTCClientSocket.m */, 349 | ); 350 | path = Client; 351 | sourceTree = ""; 352 | }; 353 | E8A89C9624761F6500A92284 /* Server */ = { 354 | isa = PBXGroup; 355 | children = ( 356 | E8A89C4C2473B22400A92284 /* RongRTCServerSocket.h */, 357 | E8A89C512473B22400A92284 /* RongRTCServerSocket.m */, 358 | ); 359 | path = Server; 360 | sourceTree = ""; 361 | }; 362 | /* End PBXGroup section */ 363 | 364 | /* Begin PBXNativeTarget section */ 365 | E8A89B972473B0D400A92284 /* Socket_Replykit */ = { 366 | isa = PBXNativeTarget; 367 | buildConfigurationList = E8A89BC72473B0DA00A92284 /* Build configuration list for PBXNativeTarget "Socket_Replykit" */; 368 | buildPhases = ( 369 | E8A89B942473B0D400A92284 /* Sources */, 370 | E8A89B952473B0D400A92284 /* Frameworks */, 371 | E8A89B962473B0D400A92284 /* Resources */, 372 | E8A89C432473B18500A92284 /* Embed App Extensions */, 373 | ); 374 | buildRules = ( 375 | ); 376 | dependencies = ( 377 | E8A89C382473B18500A92284 /* PBXTargetDependency */, 378 | E8A89C3B2473B18500A92284 /* PBXTargetDependency */, 379 | ); 380 | name = Socket_Replykit; 381 | productName = Socket_Replykit; 382 | productReference = E8A89B982473B0D400A92284 /* Socket_Replykit.app */; 383 | productType = "com.apple.product-type.application"; 384 | }; 385 | E8A89BB22473B0DA00A92284 /* Socket_ReplykitTests */ = { 386 | isa = PBXNativeTarget; 387 | buildConfigurationList = E8A89BCA2473B0DA00A92284 /* Build configuration list for PBXNativeTarget "Socket_ReplykitTests" */; 388 | buildPhases = ( 389 | E8A89BAF2473B0DA00A92284 /* Sources */, 390 | E8A89BB02473B0DA00A92284 /* Frameworks */, 391 | E8A89BB12473B0DA00A92284 /* Resources */, 392 | ); 393 | buildRules = ( 394 | ); 395 | dependencies = ( 396 | E8A89BB52473B0DA00A92284 /* PBXTargetDependency */, 397 | ); 398 | name = Socket_ReplykitTests; 399 | productName = Socket_ReplykitTests; 400 | productReference = E8A89BB32473B0DA00A92284 /* Socket_ReplykitTests.xctest */; 401 | productType = "com.apple.product-type.bundle.unit-test"; 402 | }; 403 | E8A89BBD2473B0DA00A92284 /* Socket_ReplykitUITests */ = { 404 | isa = PBXNativeTarget; 405 | buildConfigurationList = E8A89BCD2473B0DA00A92284 /* Build configuration list for PBXNativeTarget "Socket_ReplykitUITests" */; 406 | buildPhases = ( 407 | E8A89BBA2473B0DA00A92284 /* Sources */, 408 | E8A89BBB2473B0DA00A92284 /* Frameworks */, 409 | E8A89BBC2473B0DA00A92284 /* Resources */, 410 | ); 411 | buildRules = ( 412 | ); 413 | dependencies = ( 414 | E8A89BC02473B0DA00A92284 /* PBXTargetDependency */, 415 | ); 416 | name = Socket_ReplykitUITests; 417 | productName = Socket_ReplykitUITests; 418 | productReference = E8A89BBE2473B0DA00A92284 /* Socket_ReplykitUITests.xctest */; 419 | productType = "com.apple.product-type.bundle.ui-testing"; 420 | }; 421 | E8A89C202473B18500A92284 /* SocketReply */ = { 422 | isa = PBXNativeTarget; 423 | buildConfigurationList = E8A89C402473B18500A92284 /* Build configuration list for PBXNativeTarget "SocketReply" */; 424 | buildPhases = ( 425 | E8A89C1D2473B18500A92284 /* Sources */, 426 | E8A89C1E2473B18500A92284 /* Frameworks */, 427 | E8A89C1F2473B18500A92284 /* Resources */, 428 | ); 429 | buildRules = ( 430 | ); 431 | dependencies = ( 432 | ); 433 | name = SocketReply; 434 | productName = SocketReply; 435 | productReference = E8A89C212473B18500A92284 /* SocketReply.appex */; 436 | productType = "com.apple.product-type.app-extension"; 437 | }; 438 | E8A89C2D2473B18500A92284 /* SocketReplySetupUI */ = { 439 | isa = PBXNativeTarget; 440 | buildConfigurationList = E8A89C3D2473B18500A92284 /* Build configuration list for PBXNativeTarget "SocketReplySetupUI" */; 441 | buildPhases = ( 442 | E8A89C2A2473B18500A92284 /* Sources */, 443 | E8A89C2B2473B18500A92284 /* Frameworks */, 444 | E8A89C2C2473B18500A92284 /* Resources */, 445 | ); 446 | buildRules = ( 447 | ); 448 | dependencies = ( 449 | ); 450 | name = SocketReplySetupUI; 451 | productName = SocketReplySetupUI; 452 | productReference = E8A89C2E2473B18500A92284 /* SocketReplySetupUI.appex */; 453 | productType = "com.apple.product-type.app-extension"; 454 | }; 455 | /* End PBXNativeTarget section */ 456 | 457 | /* Begin PBXProject section */ 458 | E8A89B902473B0D400A92284 /* Project object */ = { 459 | isa = PBXProject; 460 | attributes = { 461 | LastUpgradeCheck = 1140; 462 | ORGANIZATIONNAME = RongCloud; 463 | TargetAttributes = { 464 | E8A89B972473B0D400A92284 = { 465 | CreatedOnToolsVersion = 11.4.1; 466 | }; 467 | E8A89BB22473B0DA00A92284 = { 468 | CreatedOnToolsVersion = 11.4.1; 469 | TestTargetID = E8A89B972473B0D400A92284; 470 | }; 471 | E8A89BBD2473B0DA00A92284 = { 472 | CreatedOnToolsVersion = 11.4.1; 473 | TestTargetID = E8A89B972473B0D400A92284; 474 | }; 475 | E8A89C202473B18500A92284 = { 476 | CreatedOnToolsVersion = 11.4.1; 477 | }; 478 | E8A89C2D2473B18500A92284 = { 479 | CreatedOnToolsVersion = 11.4.1; 480 | }; 481 | }; 482 | }; 483 | buildConfigurationList = E8A89B932473B0D400A92284 /* Build configuration list for PBXProject "Socket_Replykit" */; 484 | compatibilityVersion = "Xcode 9.3"; 485 | developmentRegion = en; 486 | hasScannedForEncodings = 0; 487 | knownRegions = ( 488 | en, 489 | Base, 490 | ); 491 | mainGroup = E8A89B8F2473B0D400A92284; 492 | productRefGroup = E8A89B992473B0D400A92284 /* Products */; 493 | projectDirPath = ""; 494 | projectRoot = ""; 495 | targets = ( 496 | E8A89B972473B0D400A92284 /* Socket_Replykit */, 497 | E8A89BB22473B0DA00A92284 /* Socket_ReplykitTests */, 498 | E8A89BBD2473B0DA00A92284 /* Socket_ReplykitUITests */, 499 | E8A89C202473B18500A92284 /* SocketReply */, 500 | E8A89C2D2473B18500A92284 /* SocketReplySetupUI */, 501 | ); 502 | }; 503 | /* End PBXProject section */ 504 | 505 | /* Begin PBXResourcesBuildPhase section */ 506 | E8A89B962473B0D400A92284 /* Resources */ = { 507 | isa = PBXResourcesBuildPhase; 508 | buildActionMask = 2147483647; 509 | files = ( 510 | E8A89BAB2473B0DA00A92284 /* LaunchScreen.storyboard in Resources */, 511 | E8A89BA82473B0D900A92284 /* Assets.xcassets in Resources */, 512 | E8A89BA62473B0D400A92284 /* Main.storyboard in Resources */, 513 | ); 514 | runOnlyForDeploymentPostprocessing = 0; 515 | }; 516 | E8A89BB12473B0DA00A92284 /* Resources */ = { 517 | isa = PBXResourcesBuildPhase; 518 | buildActionMask = 2147483647; 519 | files = ( 520 | ); 521 | runOnlyForDeploymentPostprocessing = 0; 522 | }; 523 | E8A89BBC2473B0DA00A92284 /* Resources */ = { 524 | isa = PBXResourcesBuildPhase; 525 | buildActionMask = 2147483647; 526 | files = ( 527 | ); 528 | runOnlyForDeploymentPostprocessing = 0; 529 | }; 530 | E8A89C1F2473B18500A92284 /* Resources */ = { 531 | isa = PBXResourcesBuildPhase; 532 | buildActionMask = 2147483647; 533 | files = ( 534 | ); 535 | runOnlyForDeploymentPostprocessing = 0; 536 | }; 537 | E8A89C2C2473B18500A92284 /* Resources */ = { 538 | isa = PBXResourcesBuildPhase; 539 | buildActionMask = 2147483647; 540 | files = ( 541 | ); 542 | runOnlyForDeploymentPostprocessing = 0; 543 | }; 544 | /* End PBXResourcesBuildPhase section */ 545 | 546 | /* Begin PBXSourcesBuildPhase section */ 547 | E8A89B942473B0D400A92284 /* Sources */ = { 548 | isa = PBXSourcesBuildPhase; 549 | buildActionMask = 2147483647; 550 | files = ( 551 | E8A89BA32473B0D400A92284 /* ViewController.m in Sources */, 552 | E8A89C1C2473B10500A92284 /* RongRTCVideoEncoder.mm in Sources */, 553 | E8A89C582473B22400A92284 /* RongRTCThread.m in Sources */, 554 | E8A89C622473C32200A92284 /* OtherTest.m in Sources */, 555 | E8A89C1A2473B10500A92284 /* RongRTCVideoEncoderSettings.m in Sources */, 556 | E8A89C1B2473B10500A92284 /* RongRTCVideoDecoder.mm in Sources */, 557 | E8A89C562473B22400A92284 /* RongRTCClientSocket.m in Sources */, 558 | E8A89C192473B10500A92284 /* helpers.mm in Sources */, 559 | E8A89C182473B10500A92284 /* RongRTCCodec.m in Sources */, 560 | E8A89C592473B22400A92284 /* RongRTCSocket.m in Sources */, 561 | E8A89B9D2473B0D400A92284 /* AppDelegate.m in Sources */, 562 | E8A89C552473B22400A92284 /* RongRTCBufferUtil.m in Sources */, 563 | E8A89BAE2473B0DA00A92284 /* main.m in Sources */, 564 | E8A89BA02473B0D400A92284 /* SceneDelegate.m in Sources */, 565 | E8A89C572473B22400A92284 /* RongRTCServerSocket.m in Sources */, 566 | ); 567 | runOnlyForDeploymentPostprocessing = 0; 568 | }; 569 | E8A89BAF2473B0DA00A92284 /* Sources */ = { 570 | isa = PBXSourcesBuildPhase; 571 | buildActionMask = 2147483647; 572 | files = ( 573 | E8A89BB82473B0DA00A92284 /* Socket_ReplykitTests.m in Sources */, 574 | ); 575 | runOnlyForDeploymentPostprocessing = 0; 576 | }; 577 | E8A89BBA2473B0DA00A92284 /* Sources */ = { 578 | isa = PBXSourcesBuildPhase; 579 | buildActionMask = 2147483647; 580 | files = ( 581 | E8A89BC32473B0DA00A92284 /* Socket_ReplykitUITests.m in Sources */, 582 | ); 583 | runOnlyForDeploymentPostprocessing = 0; 584 | }; 585 | E8A89C1D2473B18500A92284 /* Sources */ = { 586 | isa = PBXSourcesBuildPhase; 587 | buildActionMask = 2147483647; 588 | files = ( 589 | E8A89C442473B1C800A92284 /* RongRTCVideoDecoder.mm in Sources */, 590 | E8A89C5A2473B2B900A92284 /* RongRTCSocket.m in Sources */, 591 | E8A89C5C2473B2BF00A92284 /* RongRTCServerSocket.m in Sources */, 592 | E8A89C452473B1CD00A92284 /* RongRTCCodec.m in Sources */, 593 | E8A89C472473B1D700A92284 /* helpers.mm in Sources */, 594 | E8A89C5E2473B2C800A92284 /* RongRTCThread.m in Sources */, 595 | E8A89C282473B18500A92284 /* SampleHandler.m in Sources */, 596 | E8A89C482473B1DB00A92284 /* RongRTCVideoEncoder.mm in Sources */, 597 | E8A89C5D2473B2C100A92284 /* RongRTCBufferUtil.m in Sources */, 598 | E8A89C462473B1D100A92284 /* RongRTCVideoEncoderSettings.m in Sources */, 599 | E8A89C5B2473B2BC00A92284 /* RongRTCClientSocket.m in Sources */, 600 | ); 601 | runOnlyForDeploymentPostprocessing = 0; 602 | }; 603 | E8A89C2A2473B18500A92284 /* Sources */ = { 604 | isa = PBXSourcesBuildPhase; 605 | buildActionMask = 2147483647; 606 | files = ( 607 | E8A89C352473B18500A92284 /* BroadcastSetupViewController.m in Sources */, 608 | ); 609 | runOnlyForDeploymentPostprocessing = 0; 610 | }; 611 | /* End PBXSourcesBuildPhase section */ 612 | 613 | /* Begin PBXTargetDependency section */ 614 | E8A89BB52473B0DA00A92284 /* PBXTargetDependency */ = { 615 | isa = PBXTargetDependency; 616 | target = E8A89B972473B0D400A92284 /* Socket_Replykit */; 617 | targetProxy = E8A89BB42473B0DA00A92284 /* PBXContainerItemProxy */; 618 | }; 619 | E8A89BC02473B0DA00A92284 /* PBXTargetDependency */ = { 620 | isa = PBXTargetDependency; 621 | target = E8A89B972473B0D400A92284 /* Socket_Replykit */; 622 | targetProxy = E8A89BBF2473B0DA00A92284 /* PBXContainerItemProxy */; 623 | }; 624 | E8A89C382473B18500A92284 /* PBXTargetDependency */ = { 625 | isa = PBXTargetDependency; 626 | target = E8A89C2D2473B18500A92284 /* SocketReplySetupUI */; 627 | targetProxy = E8A89C372473B18500A92284 /* PBXContainerItemProxy */; 628 | }; 629 | E8A89C3B2473B18500A92284 /* PBXTargetDependency */ = { 630 | isa = PBXTargetDependency; 631 | target = E8A89C202473B18500A92284 /* SocketReply */; 632 | targetProxy = E8A89C3A2473B18500A92284 /* PBXContainerItemProxy */; 633 | }; 634 | /* End PBXTargetDependency section */ 635 | 636 | /* Begin PBXVariantGroup section */ 637 | E8A89BA42473B0D400A92284 /* Main.storyboard */ = { 638 | isa = PBXVariantGroup; 639 | children = ( 640 | E8A89BA52473B0D400A92284 /* Base */, 641 | ); 642 | name = Main.storyboard; 643 | sourceTree = ""; 644 | }; 645 | E8A89BA92473B0D900A92284 /* LaunchScreen.storyboard */ = { 646 | isa = PBXVariantGroup; 647 | children = ( 648 | E8A89BAA2473B0D900A92284 /* Base */, 649 | ); 650 | name = LaunchScreen.storyboard; 651 | sourceTree = ""; 652 | }; 653 | /* End PBXVariantGroup section */ 654 | 655 | /* Begin XCBuildConfiguration section */ 656 | E8A89BC52473B0DA00A92284 /* Debug */ = { 657 | isa = XCBuildConfiguration; 658 | buildSettings = { 659 | ALWAYS_SEARCH_USER_PATHS = NO; 660 | CLANG_ANALYZER_NONNULL = YES; 661 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 662 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 663 | CLANG_CXX_LIBRARY = "libc++"; 664 | CLANG_ENABLE_MODULES = YES; 665 | CLANG_ENABLE_OBJC_ARC = YES; 666 | CLANG_ENABLE_OBJC_WEAK = YES; 667 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 668 | CLANG_WARN_BOOL_CONVERSION = YES; 669 | CLANG_WARN_COMMA = YES; 670 | CLANG_WARN_CONSTANT_CONVERSION = YES; 671 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 672 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 673 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 674 | CLANG_WARN_EMPTY_BODY = YES; 675 | CLANG_WARN_ENUM_CONVERSION = YES; 676 | CLANG_WARN_INFINITE_RECURSION = YES; 677 | CLANG_WARN_INT_CONVERSION = YES; 678 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 679 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 680 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 681 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 682 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 683 | CLANG_WARN_STRICT_PROTOTYPES = YES; 684 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 685 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 686 | CLANG_WARN_UNREACHABLE_CODE = YES; 687 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 688 | COPY_PHASE_STRIP = NO; 689 | DEBUG_INFORMATION_FORMAT = dwarf; 690 | ENABLE_STRICT_OBJC_MSGSEND = YES; 691 | ENABLE_TESTABILITY = YES; 692 | GCC_C_LANGUAGE_STANDARD = gnu11; 693 | GCC_DYNAMIC_NO_PIC = NO; 694 | GCC_NO_COMMON_BLOCKS = YES; 695 | GCC_OPTIMIZATION_LEVEL = 0; 696 | GCC_PREPROCESSOR_DEFINITIONS = ( 697 | "DEBUG=1", 698 | "$(inherited)", 699 | ); 700 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 701 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 702 | GCC_WARN_UNDECLARED_SELECTOR = YES; 703 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 704 | GCC_WARN_UNUSED_FUNCTION = YES; 705 | GCC_WARN_UNUSED_VARIABLE = YES; 706 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 707 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 708 | MTL_FAST_MATH = YES; 709 | ONLY_ACTIVE_ARCH = YES; 710 | SDKROOT = iphoneos; 711 | }; 712 | name = Debug; 713 | }; 714 | E8A89BC62473B0DA00A92284 /* Release */ = { 715 | isa = XCBuildConfiguration; 716 | buildSettings = { 717 | ALWAYS_SEARCH_USER_PATHS = NO; 718 | CLANG_ANALYZER_NONNULL = YES; 719 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 720 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 721 | CLANG_CXX_LIBRARY = "libc++"; 722 | CLANG_ENABLE_MODULES = YES; 723 | CLANG_ENABLE_OBJC_ARC = YES; 724 | CLANG_ENABLE_OBJC_WEAK = YES; 725 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 726 | CLANG_WARN_BOOL_CONVERSION = YES; 727 | CLANG_WARN_COMMA = YES; 728 | CLANG_WARN_CONSTANT_CONVERSION = YES; 729 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 730 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 731 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 732 | CLANG_WARN_EMPTY_BODY = YES; 733 | CLANG_WARN_ENUM_CONVERSION = YES; 734 | CLANG_WARN_INFINITE_RECURSION = YES; 735 | CLANG_WARN_INT_CONVERSION = YES; 736 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 737 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 738 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 739 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 740 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 741 | CLANG_WARN_STRICT_PROTOTYPES = YES; 742 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 743 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 744 | CLANG_WARN_UNREACHABLE_CODE = YES; 745 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 746 | COPY_PHASE_STRIP = NO; 747 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 748 | ENABLE_NS_ASSERTIONS = NO; 749 | ENABLE_STRICT_OBJC_MSGSEND = YES; 750 | GCC_C_LANGUAGE_STANDARD = gnu11; 751 | GCC_NO_COMMON_BLOCKS = YES; 752 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 753 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 754 | GCC_WARN_UNDECLARED_SELECTOR = YES; 755 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 756 | GCC_WARN_UNUSED_FUNCTION = YES; 757 | GCC_WARN_UNUSED_VARIABLE = YES; 758 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 759 | MTL_ENABLE_DEBUG_INFO = NO; 760 | MTL_FAST_MATH = YES; 761 | SDKROOT = iphoneos; 762 | VALIDATE_PRODUCT = YES; 763 | }; 764 | name = Release; 765 | }; 766 | E8A89BC82473B0DA00A92284 /* Debug */ = { 767 | isa = XCBuildConfiguration; 768 | buildSettings = { 769 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 770 | CODE_SIGN_STYLE = Automatic; 771 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 772 | INFOPLIST_FILE = Socket_Replykit/Info.plist; 773 | LD_RUNPATH_SEARCH_PATHS = ( 774 | "$(inherited)", 775 | "@executable_path/Frameworks", 776 | ); 777 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-Replykit"; 778 | PRODUCT_NAME = "$(TARGET_NAME)"; 779 | TARGETED_DEVICE_FAMILY = "1,2"; 780 | }; 781 | name = Debug; 782 | }; 783 | E8A89BC92473B0DA00A92284 /* Release */ = { 784 | isa = XCBuildConfiguration; 785 | buildSettings = { 786 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 787 | CODE_SIGN_STYLE = Automatic; 788 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 789 | INFOPLIST_FILE = Socket_Replykit/Info.plist; 790 | LD_RUNPATH_SEARCH_PATHS = ( 791 | "$(inherited)", 792 | "@executable_path/Frameworks", 793 | ); 794 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-Replykit"; 795 | PRODUCT_NAME = "$(TARGET_NAME)"; 796 | TARGETED_DEVICE_FAMILY = "1,2"; 797 | }; 798 | name = Release; 799 | }; 800 | E8A89BCB2473B0DA00A92284 /* Debug */ = { 801 | isa = XCBuildConfiguration; 802 | buildSettings = { 803 | BUNDLE_LOADER = "$(TEST_HOST)"; 804 | CODE_SIGN_STYLE = Automatic; 805 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 806 | INFOPLIST_FILE = Socket_ReplykitTests/Info.plist; 807 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 808 | LD_RUNPATH_SEARCH_PATHS = ( 809 | "$(inherited)", 810 | "@executable_path/Frameworks", 811 | "@loader_path/Frameworks", 812 | ); 813 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-ReplykitTests"; 814 | PRODUCT_NAME = "$(TARGET_NAME)"; 815 | TARGETED_DEVICE_FAMILY = "1,2"; 816 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Socket_Replykit.app/Socket_Replykit"; 817 | }; 818 | name = Debug; 819 | }; 820 | E8A89BCC2473B0DA00A92284 /* Release */ = { 821 | isa = XCBuildConfiguration; 822 | buildSettings = { 823 | BUNDLE_LOADER = "$(TEST_HOST)"; 824 | CODE_SIGN_STYLE = Automatic; 825 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 826 | INFOPLIST_FILE = Socket_ReplykitTests/Info.plist; 827 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 828 | LD_RUNPATH_SEARCH_PATHS = ( 829 | "$(inherited)", 830 | "@executable_path/Frameworks", 831 | "@loader_path/Frameworks", 832 | ); 833 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-ReplykitTests"; 834 | PRODUCT_NAME = "$(TARGET_NAME)"; 835 | TARGETED_DEVICE_FAMILY = "1,2"; 836 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Socket_Replykit.app/Socket_Replykit"; 837 | }; 838 | name = Release; 839 | }; 840 | E8A89BCE2473B0DA00A92284 /* Debug */ = { 841 | isa = XCBuildConfiguration; 842 | buildSettings = { 843 | CODE_SIGN_STYLE = Automatic; 844 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 845 | INFOPLIST_FILE = Socket_ReplykitUITests/Info.plist; 846 | LD_RUNPATH_SEARCH_PATHS = ( 847 | "$(inherited)", 848 | "@executable_path/Frameworks", 849 | "@loader_path/Frameworks", 850 | ); 851 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-ReplykitUITests"; 852 | PRODUCT_NAME = "$(TARGET_NAME)"; 853 | TARGETED_DEVICE_FAMILY = "1,2"; 854 | TEST_TARGET_NAME = Socket_Replykit; 855 | }; 856 | name = Debug; 857 | }; 858 | E8A89BCF2473B0DA00A92284 /* Release */ = { 859 | isa = XCBuildConfiguration; 860 | buildSettings = { 861 | CODE_SIGN_STYLE = Automatic; 862 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 863 | INFOPLIST_FILE = Socket_ReplykitUITests/Info.plist; 864 | LD_RUNPATH_SEARCH_PATHS = ( 865 | "$(inherited)", 866 | "@executable_path/Frameworks", 867 | "@loader_path/Frameworks", 868 | ); 869 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-ReplykitUITests"; 870 | PRODUCT_NAME = "$(TARGET_NAME)"; 871 | TARGETED_DEVICE_FAMILY = "1,2"; 872 | TEST_TARGET_NAME = Socket_Replykit; 873 | }; 874 | name = Release; 875 | }; 876 | E8A89C3E2473B18500A92284 /* Debug */ = { 877 | isa = XCBuildConfiguration; 878 | buildSettings = { 879 | CODE_SIGN_STYLE = Automatic; 880 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 881 | INFOPLIST_FILE = SocketReplySetupUI/Info.plist; 882 | IPHONEOS_DEPLOYMENT_TARGET = 13.3; 883 | LD_RUNPATH_SEARCH_PATHS = ( 884 | "$(inherited)", 885 | "@executable_path/Frameworks", 886 | "@executable_path/../../Frameworks", 887 | ); 888 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-Replykit.SocketReplySetupUI"; 889 | PRODUCT_NAME = "$(TARGET_NAME)"; 890 | SKIP_INSTALL = YES; 891 | TARGETED_DEVICE_FAMILY = "1,2"; 892 | }; 893 | name = Debug; 894 | }; 895 | E8A89C3F2473B18500A92284 /* Release */ = { 896 | isa = XCBuildConfiguration; 897 | buildSettings = { 898 | CODE_SIGN_STYLE = Automatic; 899 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 900 | INFOPLIST_FILE = SocketReplySetupUI/Info.plist; 901 | IPHONEOS_DEPLOYMENT_TARGET = 13.3; 902 | LD_RUNPATH_SEARCH_PATHS = ( 903 | "$(inherited)", 904 | "@executable_path/Frameworks", 905 | "@executable_path/../../Frameworks", 906 | ); 907 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-Replykit.SocketReplySetupUI"; 908 | PRODUCT_NAME = "$(TARGET_NAME)"; 909 | SKIP_INSTALL = YES; 910 | TARGETED_DEVICE_FAMILY = "1,2"; 911 | }; 912 | name = Release; 913 | }; 914 | E8A89C412473B18500A92284 /* Debug */ = { 915 | isa = XCBuildConfiguration; 916 | buildSettings = { 917 | CODE_SIGN_STYLE = Automatic; 918 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 919 | INFOPLIST_FILE = SocketReply/Info.plist; 920 | LD_RUNPATH_SEARCH_PATHS = ( 921 | "$(inherited)", 922 | "@executable_path/Frameworks", 923 | "@executable_path/../../Frameworks", 924 | ); 925 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-Replykit.SocketReply"; 926 | PRODUCT_NAME = "$(TARGET_NAME)"; 927 | SKIP_INSTALL = YES; 928 | TARGETED_DEVICE_FAMILY = "1,2"; 929 | }; 930 | name = Debug; 931 | }; 932 | E8A89C422473B18500A92284 /* Release */ = { 933 | isa = XCBuildConfiguration; 934 | buildSettings = { 935 | CODE_SIGN_STYLE = Automatic; 936 | DEVELOPMENT_TEAM = 9CVMN4UZK4; 937 | INFOPLIST_FILE = SocketReply/Info.plist; 938 | LD_RUNPATH_SEARCH_PATHS = ( 939 | "$(inherited)", 940 | "@executable_path/Frameworks", 941 | "@executable_path/../../Frameworks", 942 | ); 943 | PRODUCT_BUNDLE_IDENTIFIER = "RongCloud.Socket-Replykit.SocketReply"; 944 | PRODUCT_NAME = "$(TARGET_NAME)"; 945 | SKIP_INSTALL = YES; 946 | TARGETED_DEVICE_FAMILY = "1,2"; 947 | }; 948 | name = Release; 949 | }; 950 | /* End XCBuildConfiguration section */ 951 | 952 | /* Begin XCConfigurationList section */ 953 | E8A89B932473B0D400A92284 /* Build configuration list for PBXProject "Socket_Replykit" */ = { 954 | isa = XCConfigurationList; 955 | buildConfigurations = ( 956 | E8A89BC52473B0DA00A92284 /* Debug */, 957 | E8A89BC62473B0DA00A92284 /* Release */, 958 | ); 959 | defaultConfigurationIsVisible = 0; 960 | defaultConfigurationName = Release; 961 | }; 962 | E8A89BC72473B0DA00A92284 /* Build configuration list for PBXNativeTarget "Socket_Replykit" */ = { 963 | isa = XCConfigurationList; 964 | buildConfigurations = ( 965 | E8A89BC82473B0DA00A92284 /* Debug */, 966 | E8A89BC92473B0DA00A92284 /* Release */, 967 | ); 968 | defaultConfigurationIsVisible = 0; 969 | defaultConfigurationName = Release; 970 | }; 971 | E8A89BCA2473B0DA00A92284 /* Build configuration list for PBXNativeTarget "Socket_ReplykitTests" */ = { 972 | isa = XCConfigurationList; 973 | buildConfigurations = ( 974 | E8A89BCB2473B0DA00A92284 /* Debug */, 975 | E8A89BCC2473B0DA00A92284 /* Release */, 976 | ); 977 | defaultConfigurationIsVisible = 0; 978 | defaultConfigurationName = Release; 979 | }; 980 | E8A89BCD2473B0DA00A92284 /* Build configuration list for PBXNativeTarget "Socket_ReplykitUITests" */ = { 981 | isa = XCConfigurationList; 982 | buildConfigurations = ( 983 | E8A89BCE2473B0DA00A92284 /* Debug */, 984 | E8A89BCF2473B0DA00A92284 /* Release */, 985 | ); 986 | defaultConfigurationIsVisible = 0; 987 | defaultConfigurationName = Release; 988 | }; 989 | E8A89C3D2473B18500A92284 /* Build configuration list for PBXNativeTarget "SocketReplySetupUI" */ = { 990 | isa = XCConfigurationList; 991 | buildConfigurations = ( 992 | E8A89C3E2473B18500A92284 /* Debug */, 993 | E8A89C3F2473B18500A92284 /* Release */, 994 | ); 995 | defaultConfigurationIsVisible = 0; 996 | defaultConfigurationName = Release; 997 | }; 998 | E8A89C402473B18500A92284 /* Build configuration list for PBXNativeTarget "SocketReply" */ = { 999 | isa = XCConfigurationList; 1000 | buildConfigurations = ( 1001 | E8A89C412473B18500A92284 /* Debug */, 1002 | E8A89C422473B18500A92284 /* Release */, 1003 | ); 1004 | defaultConfigurationIsVisible = 0; 1005 | defaultConfigurationName = Release; 1006 | }; 1007 | /* End XCConfigurationList section */ 1008 | }; 1009 | rootObject = E8A89B902473B0D400A92284 /* Project object */; 1010 | } 1011 | --------------------------------------------------------------------------------