├── Douyu.xcodeproj └── project.pbxproj ├── Douyu ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── timg-2.png │ │ ├── timg-2@2x.png │ │ ├── timg-3.png │ │ ├── timg0.5.png │ │ ├── timg64.png │ │ ├── timg64@16.png │ │ ├── timg64@32-1.png │ │ └── timg64@32.png ├── Base.lproj │ └── Main.storyboard ├── Frameworks │ └── BarrageRenderer │ │ ├── BarrageEngine │ │ ├── BarrageCanvas.h │ │ ├── BarrageCanvas.m │ │ ├── BarrageDescriptor.h │ │ ├── BarrageDescriptor.m │ │ ├── BarrageRenderer.h │ │ ├── BarrageRenderer.m │ │ ├── BarrageSpriteFactory.h │ │ └── BarrageSpriteFactory.m │ │ ├── BarrageHeader.h │ │ ├── BarrageLoader │ │ ├── BarrageLoader.h │ │ └── BarrageLoader.m │ │ ├── BarrageSpirit │ │ ├── BarrageFloatImageSprite.h │ │ ├── BarrageFloatImageSprite.m │ │ ├── BarrageFloatSprite.h │ │ ├── BarrageFloatSprite.m │ │ ├── BarrageFloatTextSprite.h │ │ ├── BarrageFloatTextSprite.m │ │ ├── BarrageSprite.h │ │ ├── BarrageSprite.m │ │ ├── BarrageSpriteProtocol.h │ │ ├── BarrageSpriteUtility.h │ │ ├── BarrageSpriteUtility.m │ │ ├── BarrageWalkImageSprite.h │ │ ├── BarrageWalkImageSprite.m │ │ ├── BarrageWalkSprite.h │ │ ├── BarrageWalkSprite.m │ │ ├── BarrageWalkTextSprite.h │ │ └── BarrageWalkTextSprite.m │ │ ├── NSLabel.h │ │ ├── NSLabel.m │ │ ├── NSValue+iOS.h │ │ ├── NSValue+iOS.m │ │ ├── NSView+UIView.h │ │ └── NSView+UIView.m ├── Info.plist ├── Model │ ├── DYDanmuProvider.h │ ├── DYDanmuProvider.m │ ├── DYRoomHistoryData.h │ ├── DYRoomHistoryData.m │ ├── DYRoomHistoryModel.h │ ├── DYRoomHistoryModel.m │ ├── DYRoomInfo.h │ ├── DYRoomInfo.m │ ├── NSString+InfoGet.h │ ├── NSString+InfoGet.m │ ├── Settings.h │ ├── Settings.m │ └── socket │ │ ├── AsyncSocket.h │ │ ├── AsyncSocket.m │ │ ├── AuthSocket.h │ │ ├── AuthSocket.m │ │ ├── DanmuModel.h │ │ ├── DanmuModel.m │ │ ├── DanmuSocket.h │ │ ├── DanmuSocket.m │ │ ├── DouyuTVSocket.h │ │ ├── DouyuTVSocket.m │ │ ├── RegexKitLite.h │ │ ├── RegexKitLite.m │ │ ├── ServerModel.h │ │ ├── ServerModel.m │ │ ├── SocketData.h │ │ └── SocketData.m ├── MpvClientOGLView.h ├── MpvClientOGLView.m ├── PlayerViewController.h ├── PlayerViewController.m ├── PlayerWindow.h ├── PlayerWindow.m ├── Resources │ ├── crypto-js.js │ └── img │ │ └── loading.gif ├── ViewController.h ├── ViewController.m └── main.m ├── LICENSE ├── Podfile ├── README.md └── screen-shot.png /Douyu/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Douyu/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | 22 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 23 | // Insert code here to tear down your application 24 | } 25 | 26 | - (void)application:(NSApplication *)application openURLs:(NSArray *)urls { 27 | [[NSNotificationCenter defaultCenter] postNotificationName:@"openUrl" object:urls]; 28 | } 29 | 30 | - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication 31 | hasVisibleWindows:(BOOL)flag{ 32 | if (!flag){ 33 | //主窗口显示 34 | [NSApp activateIgnoringOtherApps:NO]; 35 | [theApplication.windows.firstObject makeKeyAndOrderFront:self]; 36 | } 37 | return YES; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "timg64@16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "timg64@32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "timg64@32-1.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "timg64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "timg0.5.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "timg-3.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "timg-2.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "timg-2@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "idiom" : "mac", 53 | "size" : "512x512", 54 | "scale" : "1x" 55 | }, 56 | { 57 | "idiom" : "mac", 58 | "size" : "512x512", 59 | "scale" : "2x" 60 | } 61 | ], 62 | "info" : { 63 | "version" : 1, 64 | "author" : "xcode" 65 | } 66 | } -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/timg-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Assets.xcassets/AppIcon.appiconset/timg-2.png -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/timg-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Assets.xcassets/AppIcon.appiconset/timg-2@2x.png -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/timg-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Assets.xcassets/AppIcon.appiconset/timg-3.png -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/timg0.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Assets.xcassets/AppIcon.appiconset/timg0.5.png -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/timg64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Assets.xcassets/AppIcon.appiconset/timg64.png -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/timg64@16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Assets.xcassets/AppIcon.appiconset/timg64@16.png -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/timg64@32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Assets.xcassets/AppIcon.appiconset/timg64@32-1.png -------------------------------------------------------------------------------- /Douyu/Assets.xcassets/AppIcon.appiconset/timg64@32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Assets.xcassets/AppIcon.appiconset/timg64@32.png -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageEngine/BarrageCanvas.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import "NSView+UIView.h" 29 | 30 | @interface BarrageCanvas : NSView 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageEngine/BarrageCanvas.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageCanvas.h" 28 | 29 | @implementation BarrageCanvas 30 | 31 | - (instancetype)init 32 | { 33 | if (self = [super init]) { 34 | //self.userInteractionEnabled = NO; 35 | //self.backgroundColor = [NSColor clearColor]; 36 | // [self setAutoresizesSubviews:YES]; 37 | [self.layer setBackgroundColor:[NSColor clearColor].CGColor]; //RGB plus Alpha Channel 38 | } 39 | return self; 40 | } 41 | 42 | - (void)layoutSubviews 43 | { 44 | [super layoutSubviews]; 45 | if (self.superview && !CGRectEqualToRect(self.frame, self.superview.bounds)) { 46 | self.frame = self.superview.bounds; 47 | } 48 | } 49 | 50 | - (void)didMoveToSuperview 51 | { 52 | [self resizeSubviewsWithOldSize:[self frame].size]; 53 | //[self layoutIfNeeded]; 54 | } 55 | 56 | - (NSView *)hitTest:(NSPoint)aPoint{ 57 | return nil; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageEngine/BarrageDescriptor.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | /// 统一的持久化层 30 | @interface BarrageDescriptor : NSObject 31 | 32 | //TODO: 待实现 33 | - (instancetype)initWithString:(NSString *)xml; 34 | 35 | /// 类名,支持的类名参照BarrageSprite子类 36 | @property(nonatomic,strong)NSString * spriteName; 37 | 38 | /// 属性字典 39 | @property(nonatomic,strong,readonly)NSMutableDictionary * params; 40 | 41 | /// 弹幕标识符,用于防止弹幕重复,内部一次性生成 42 | @property(nonatomic,strong,readonly)NSString * identifier; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageEngine/BarrageDescriptor.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageDescriptor.h" 28 | 29 | @interface BarrageDescriptor() 30 | { 31 | NSString * _xml; 32 | } 33 | @end 34 | 35 | @implementation BarrageDescriptor 36 | 37 | - (instancetype)initWithString:(NSString *)xml 38 | { 39 | if (self = [super init]) { 40 | _xml = xml; 41 | _params = [[NSMutableDictionary alloc]init]; 42 | //TODO: 必须要加载 _identifier 字段 43 | } 44 | return self; 45 | } 46 | 47 | - (instancetype)init 48 | { 49 | if (self = [super init]) { 50 | _identifier = [[NSProcessInfo processInfo]globallyUniqueString]; 51 | _params = [[NSMutableDictionary alloc]init]; 52 | } 53 | return self; 54 | } 55 | 56 | - (instancetype)copyWithZone:(NSZone *)zone 57 | { 58 | BarrageDescriptor * copy = [BarrageDescriptor allocWithZone:zone]; 59 | copy->_params = [self.params mutableCopy]; 60 | copy->_spriteName = self.spriteName; 61 | copy->_identifier = [[NSProcessInfo processInfo]globallyUniqueString]; 62 | return copy; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageEngine/BarrageRenderer.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import 29 | 30 | @class BarrageDescriptor; 31 | @class BarrageRenderer; 32 | 33 | /// 弹幕渲染器 34 | @interface BarrageRenderer : NSObject 35 | 36 | #pragma mark - life cycle 37 | @property BOOL launched; 38 | 39 | #pragma mark - control 40 | 41 | /// 启动弹幕 42 | - (void)start; 43 | 44 | /// 停止弹幕渲染, 会清空所有; 再发弹幕就无效了; 一切都会停止; 45 | /// 此方法在不再需要弹幕的时候必须调用,否则可能造成内存泄露. 46 | - (void)stop; 47 | 48 | /// 接收弹幕消息, 如果尚未start, 则调用无效. 49 | - (void)receive:(BarrageDescriptor *)descriptor; 50 | 51 | #pragma mark - output 52 | 53 | /// 返回给外部的view 54 | @property(nonatomic,weak)NSView * view; 55 | 56 | /// 逻辑时间 57 | @property(nonatomic,assign,readonly)NSTimeInterval time; 58 | 59 | #pragma mark - z-index 60 | 61 | /// 是否开启z-index功能,开启之后,性能会稍有降低,绘图会按照z_index进行,值越大,越靠上;默认关闭. 62 | @property(nonatomic,assign)BOOL zIndex; 63 | 64 | #pragma mark - record 65 | 66 | /// 如需要记录,需要在运行start之后立即运行此函数,内部会通过时间差计算delay; 67 | /// 记录弹幕,可能会序列化到本地; 默认为NO. 68 | @property(nonatomic,assign)BOOL recording; 69 | 70 | /// 弹幕记录数组. 71 | - (NSArray *)records; 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageEngine/BarrageRenderer.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageRenderer.h" 28 | #import "BarrageCanvas.h" 29 | #import "BarrageSprite.h" 30 | #import "BarrageSpriteFactory.h" 31 | #import "BarrageDescriptor.h" 32 | #import "NSView+UIView.h" 33 | #import "NSValue+iOS.h" 34 | #import "BarrageWalkTextSprite.h" 35 | 36 | NSString * const kBarrageRendererContextCanvasBounds = @"kBarrageRendererContextCanvasBounds"; // 画布大小 37 | NSString * const kBarrageRendererContextRelatedSpirts = @"kBarrageRendererContextRelatedSpirts"; // 相关精灵 38 | NSString * const kBarrageRendererContextTimestamp = @"kBarrageRendererContextTimestamp"; // 时间戳 39 | 40 | @interface BarrageRenderer() 41 | { 42 | BarrageCanvas * _canvas; // 画布 43 | NSMutableDictionary * _context; // 渲染器上下文 44 | 45 | NSMutableArray * _records;//记录数组 46 | } 47 | @property(nonatomic,strong)NSMutableArray *sprites; 48 | @end 49 | 50 | @implementation BarrageRenderer 51 | #pragma mark - init 52 | - (instancetype)init 53 | { 54 | if (self = [super init]) { 55 | _canvas = [[BarrageCanvas alloc]init]; 56 | _sprites = [[NSMutableArray alloc] init]; 57 | _zIndex = NO; 58 | _context = [[NSMutableDictionary alloc]init]; 59 | _recording = NO; 60 | } 61 | return self; 62 | } 63 | 64 | #pragma mark - control 65 | - (void)receive:(BarrageDescriptor *)descriptor 66 | { 67 | if (!_launched) { 68 | return; 69 | } 70 | BarrageDescriptor * descriptorCopy = [descriptor copy]; 71 | BarrageSprite * sprite = [BarrageSpriteFactory createSpriteWithDescriptor:descriptorCopy]; 72 | [self activeSprite:sprite]; 73 | if (_recording) { 74 | [self recordDescriptor:descriptorCopy]; 75 | } 76 | } 77 | 78 | - (void)start 79 | { 80 | _launched = YES; 81 | } 82 | 83 | - (void)stop 84 | { 85 | _launched = NO; 86 | [self.sprites enumerateObjectsUsingBlock:^(BarrageSprite *sprite, NSUInteger idx, BOOL * _Nonnull stop) { 87 | [sprite.view.layer removeAllAnimations]; 88 | [sprite.view removeFromSuperview]; 89 | }]; 90 | [self.sprites removeAllObjects]; 91 | } 92 | 93 | #pragma mark - record 94 | /// 此方法会修改desriptor的值 95 | - (void)recordDescriptor:(BarrageDescriptor *)descriptor 96 | { 97 | __block BOOL exists = NO; 98 | [_records enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL * stop){ 99 | if([((BarrageDescriptor *)obj).identifier isEqualToString:descriptor.identifier]){ 100 | exists = YES; 101 | *stop = YES; 102 | } 103 | }]; 104 | if(!exists){ 105 | [_records addObject:descriptor]; 106 | } 107 | } 108 | 109 | - (NSArray *)records 110 | { 111 | return [_records copy]; 112 | } 113 | 114 | #pragma mark - BarrageDispatcherDelegate 115 | - (void)activeSprite:(BarrageSprite *)sprite 116 | { 117 | NSValue * value = [NSValue valueWithCGRect:_canvas.bounds]; 118 | [_context setObject:value forKey:kBarrageRendererContextCanvasBounds]; 119 | [_context setObject:[self.sprites copy] forKey:kBarrageRendererContextRelatedSpirts]; 120 | [_context setObject:@([[NSDate date] timeIntervalSince1970]) forKey:kBarrageRendererContextTimestamp]; 121 | 122 | [sprite activeWithContext:_context]; 123 | [_canvas addSubview:sprite.view]; 124 | [self.sprites addObject:sprite]; 125 | 126 | float speed = [[sprite valueForKey:@"speed"] floatValue]; 127 | CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"]; 128 | anim.duration = (_canvas.bounds.size.width + sprite.size.width*2)/speed; 129 | anim.fromValue = [NSValue valueWithCGPoint:sprite.origin]; 130 | anim.toValue = [sprite valueForKey:@"destination"]; 131 | anim.removedOnCompletion = NO; 132 | anim.fillMode = kCAFillModeForwards; 133 | [sprite.view.layer addAnimation:anim forKey:nil]; 134 | __weak BarrageSprite *weak_sprite = sprite; 135 | __weak BarrageRenderer *weak_self = self; 136 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(anim.duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 137 | [weak_self deactiveSprite:weak_sprite]; 138 | }); 139 | } 140 | 141 | - (void)deactiveSprite:(BarrageSprite *)sprite 142 | { 143 | [self.sprites removeObject:sprite]; 144 | [sprite.view removeFromSuperview]; 145 | [sprite.view.layer removeAllAnimations]; 146 | } 147 | 148 | #pragma mark - attributes 149 | 150 | - (NSView *)view 151 | { 152 | return _canvas; 153 | } 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageEngine/BarrageSpriteFactory.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @class BarrageSprite; 30 | @class BarrageDescriptor; 31 | 32 | @interface BarrageSpriteFactory : NSObject 33 | 34 | /// 通过描述符创建精灵 35 | + (BarrageSprite *)createSpriteWithDescriptor:(BarrageDescriptor *)descriptor; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageEngine/BarrageSpriteFactory.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSpriteFactory.h" 28 | #import "BarrageDescriptor.h" 29 | #import "BarrageSprite.h" 30 | 31 | @implementation BarrageSpriteFactory 32 | 33 | + (BarrageSprite *)createSpriteWithDescriptor:(BarrageDescriptor *)descriptor 34 | { 35 | BarrageSprite * sprite = nil; 36 | 37 | if (descriptor.spriteName.length != 0) { 38 | Class class = NSClassFromString(descriptor.spriteName); 39 | if (class) { 40 | sprite = [[class alloc]init]; 41 | } 42 | } 43 | if (sprite) { 44 | for (NSString * key in [descriptor.params allKeys]) { 45 | id value = descriptor.params[key]; 46 | [sprite setValue:value forKey:key]; 47 | } 48 | } 49 | return sprite; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageHeader.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #ifndef demo_app_main_BarrageHeader_h 28 | #define demo_app_main_BarrageHeader_h 29 | 30 | #import "BarrageRenderer.h" 31 | #import "BarrageLoader.h" 32 | #import "BarrageDescriptor.h" 33 | 34 | #import "BarrageWalkTextSprite.h" 35 | #import "BarrageFloatTextSprite.h" 36 | #import "BarrageWalkImageSprite.h" 37 | #import "BarrageFloatImageSprite.h" 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageLoader/BarrageLoader.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @interface BarrageLoader : NSObject 30 | 31 | /// 通过文件批量获取描述符 32 | + (NSArray *)readDescriptorsWithFile:(NSString *)file; 33 | 34 | /// 将描述符批量写入文件; additional表示是否要追加,默认是NO:覆盖. 35 | + (void)writeDescriptors:(NSArray *)descriptors toFile:(NSString *)file additional:(BOOL)additional; 36 | 37 | /// 支持damaku的文件格式 38 | + (NSArray *)readDescriptorsWithDamakuFile:(NSString *)file; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageLoader/BarrageLoader.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageLoader.h" 28 | 29 | @implementation BarrageLoader 30 | //TODO: 待实现 31 | + (NSArray *)readDescriptorsWithFile:(NSString *)file 32 | { 33 | return nil; 34 | } 35 | 36 | + (NSArray *)readDescriptorsWithDamakuFile:(NSString *)file 37 | { 38 | return nil; 39 | } 40 | 41 | + (void)writeDescriptors:(NSArray *)descriptors toFile:(NSString *)file additional:(BOOL)additional 42 | { 43 | return; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageFloatImageSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatSprite.h" 28 | 29 | /// 悬浮文字精灵 30 | @interface BarrageFloatImageSprite : BarrageFloatSprite 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageFloatImageSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatImageSprite.h" 28 | 29 | @implementation BarrageFloatImageSprite 30 | 31 | @synthesize image = _image; 32 | 33 | - (instancetype)init 34 | { 35 | if (self = [super init]) { 36 | _image = nil; 37 | } 38 | return self; 39 | } 40 | 41 | #pragma mark - launch 42 | 43 | - (NSView *)bindingView 44 | { 45 | NSImageView * imageView = [[NSImageView alloc]init]; 46 | imageView.image = _image; 47 | return imageView; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageFloatSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSprite.h" 28 | 29 | typedef NS_ENUM(NSUInteger, BarrageFloatDirection) { 30 | BarrageFloatDirectionT2B = 1, // 上往下 31 | BarrageFloatDirectionB2T = 2 // 下往上 32 | }; 33 | 34 | /// 悬浮文字精灵 35 | @interface BarrageFloatSprite : BarrageSprite 36 | 37 | /// 存活时间 38 | @property(nonatomic,assign)NSTimeInterval duration; 39 | 40 | /// 方向 41 | @property(nonatomic,assign)BarrageFloatDirection direction; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageFloatSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatSprite.h" 28 | 29 | @interface BarrageFloatSprite() 30 | { 31 | NSTimeInterval _leftActiveTime; 32 | } 33 | @end 34 | 35 | @implementation BarrageFloatSprite 36 | 37 | - (instancetype)init 38 | { 39 | if (self = [super init]) { 40 | _direction = BarrageFloatDirectionT2B; 41 | self.duration = 1.0f; 42 | } 43 | return self; 44 | } 45 | 46 | - (void)setDuration:(NSTimeInterval)duration 47 | { 48 | _duration = duration; 49 | _leftActiveTime = _duration; 50 | } 51 | 52 | - (void)updateWithTime:(NSTimeInterval)time 53 | { 54 | [super updateWithTime:time]; 55 | _leftActiveTime = self.duration - (time - self.timestamp); 56 | } 57 | 58 | - (NSTimeInterval)estimateActiveTime 59 | { 60 | return _leftActiveTime; 61 | } 62 | 63 | - (BOOL)validWithTime:(NSTimeInterval)time 64 | { 65 | return [self estimateActiveTime] > 0; 66 | } 67 | 68 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 69 | { 70 | // 获取同方向精灵 71 | NSMutableArray * synclasticSprites = [[NSMutableArray alloc]initWithCapacity:sprites.count]; 72 | for (BarrageFloatSprite * sprite in sprites) { 73 | if (sprite.direction == _direction) { 74 | [synclasticSprites addObject:sprite]; 75 | } 76 | } 77 | 78 | BOOL down = self.direction == BarrageFloatDirectionT2B; // 是否是朝下方向 79 | 80 | static BOOL const AVAERAGE_STRATEGY = NO; // YES:条纹平均精灵策略; NO:最快时间策略(体验会好一些) 81 | static NSUInteger const STRIP_NUM = 80; // 总共的网格条数 82 | NSTimeInterval stripMaxActiveTimes[STRIP_NUM]={0}; // 每一条网格 已有精灵中最后退出屏幕的时间 83 | NSUInteger stripSpriteNumbers[STRIP_NUM]={0}; // 每一条网格 包含精灵的数目 84 | CGFloat stripHeight = rect.size.height/STRIP_NUM; // 水平条高度 85 | 86 | NSUInteger overlandStripNum = (NSUInteger)ceil((double)self.size.height/stripHeight); // 横跨网格条数目 87 | NSUInteger availableFrom = 0; 88 | NSUInteger leastActiveTimeStrip = 0; // 最小时间的行 89 | NSUInteger leastActiveSpriteStrip = 0; // 最小网格精灵的行 90 | 91 | for (NSUInteger i = 0; i < STRIP_NUM; i++) { 92 | //寻找当前行里包含的sprites 93 | CGFloat stripFrom = down?(i * stripHeight+rect.origin.y):(rect.origin.y+rect.size.height - i * stripHeight); 94 | CGFloat stripTo = down?(stripFrom + stripHeight):(stripFrom-stripHeight); 95 | 96 | for (BarrageFloatSprite * sprite in synclasticSprites) { 97 | CGFloat spriteFrom = down?sprite.origin.y:(sprite.origin.y+sprite.size.height); 98 | CGFloat spriteTo = down?(sprite.origin.y + sprite.size.height):sprite.origin.y; 99 | if (fabs(spriteTo-spriteFrom)+fabs(stripTo-stripFrom)>MAX(fabs(stripTo-spriteFrom), fabs(spriteTo-stripFrom))) { // 在条条里 100 | stripSpriteNumbers[i]++; 101 | NSTimeInterval activeTime = [sprite estimateActiveTime]; 102 | if (activeTime > stripMaxActiveTimes[i]){ 103 | stripMaxActiveTimes[i] = activeTime; 104 | } 105 | } 106 | } 107 | if (stripMaxActiveTimes[i] > 0) { 108 | availableFrom = i+1; 109 | } 110 | else if (i - availableFrom >= overlandStripNum - 1){ 111 | break; // eureka! 112 | } 113 | if (i <= STRIP_NUM - overlandStripNum) { 114 | if (stripMaxActiveTimes[i] < stripMaxActiveTimes[leastActiveTimeStrip]) { 115 | leastActiveTimeStrip = i; 116 | } 117 | if (stripSpriteNumbers[i] < stripSpriteNumbers[leastActiveSpriteStrip]) { 118 | leastActiveSpriteStrip = i; 119 | } 120 | } 121 | } 122 | if (availableFrom > STRIP_NUM - overlandStripNum) { // 那就是没有找到喽 123 | availableFrom = AVAERAGE_STRATEGY?leastActiveSpriteStrip:leastActiveTimeStrip; // 使用最小个数 or 使用最短时间 124 | } 125 | 126 | CGPoint origin = CGPointZero; 127 | origin.x = (rect.origin.x+rect.size.width-self.size.width)/2; 128 | origin.y = down?(stripHeight * availableFrom+rect.origin.y):(rect.origin.y+rect.size.height-stripHeight * availableFrom - self.size.height); 129 | return origin; 130 | } 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageFloatTextSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatSprite.h" 28 | 29 | /// 悬浮文字精灵 30 | @interface BarrageFloatTextSprite : BarrageFloatSprite 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageFloatTextSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatTextSprite.h" 28 | #import "NSLabel.h" 29 | 30 | @implementation BarrageFloatTextSprite 31 | 32 | @synthesize fontSize = _fontSize; 33 | @synthesize textColor = _textColor; 34 | @synthesize text = _text; 35 | @synthesize fontFamily = _fontFamily; 36 | @synthesize shadowColor = _shadowColor; 37 | @synthesize shadowOffset = _shadowOffset; 38 | @synthesize attributedText = _attributedText; 39 | 40 | - (instancetype)init 41 | { 42 | if (self = [super init]) { 43 | _textColor = [NSColor blackColor]; 44 | _fontSize = 16.0f; 45 | _shadowColor = nil; 46 | _shadowOffset = CGSizeMake(0, -1); 47 | } 48 | return self; 49 | } 50 | 51 | #pragma mark - launch 52 | 53 | - (NSView *)bindingView 54 | { 55 | NSLabel * label = [[NSLabel alloc]init]; 56 | label.text = self.text; 57 | label.textColor = self.textColor; 58 | // Not supported yet 59 | //label.shadowColor = _shadowColor; 60 | //label.shadowOffset = _shadowOffset; 61 | label.font = self.fontFamily?[NSFont fontWithName:self.fontFamily size:self.fontSize]:[NSFont systemFontOfSize:self.fontSize]; 62 | if (self.attributedText) { 63 | label.attributedText = self.attributedText; 64 | } 65 | return label; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import "BarrageSpriteUtility.h" 29 | #import "BarrageSpriteProtocol.h" 30 | 31 | extern NSString * const kBarrageRendererContextCanvasBounds; // 画布大小 32 | extern NSString * const kBarrageRendererContextRelatedSpirts; // 相关精灵 33 | extern NSString * const kBarrageRendererContextTimestamp; // 时间戳 34 | 35 | /// 精灵基类 36 | @interface BarrageSprite : NSObject 37 | { 38 | CGPoint _origin; 39 | BOOL _valid; 40 | NSView * _view; 41 | 42 | NSColor * _backgroundColor; 43 | CGFloat _borderWidth; 44 | NSColor * _borderColor; 45 | CGFloat _cornerRadius; 46 | CGSize _mandatorySize; 47 | } 48 | 49 | /// 延时, 这个是相对于rendered的绝对时间/秒 50 | /// 如果delay<0, 当然不会提前显示 ^ - ^ 51 | @property(nonatomic,assign)NSTimeInterval delay; 52 | 53 | /// 创建的绝对时间,初始化的时候创建; 目前好像没啥用处 54 | @property(nonatomic,strong,readonly)NSDate * birth; 55 | 56 | /// 时间戳,表示这个弹幕处于的时间位置;相对于时间引擎启动的时候;精灵被激活的时候生成 57 | @property(nonatomic,assign,readonly)CFTimeInterval timestamp; 58 | 59 | // 最底层是0, 往上依次叠加; 默认值是0 60 | @property(nonatomic,assign)NSUInteger z_index; 61 | 62 | /// 起始位置,为了获取这个值,子类需要重写 originInBounds:withSprites: 方法 63 | @property(nonatomic,assign,readonly)CGPoint origin; 64 | 65 | /// 为了获取此值,子类可能需要在 updateWithTime: 中修改 _position成员变量 66 | @property(nonatomic,assign,readonly)CGPoint position; 67 | 68 | /// calculate inside, return for others;如果需要不同大小的size 69 | @property(nonatomic,assign,readonly)CGSize size; 70 | 71 | /// 是否有效,默认YES; 当过了动画时间之后,就会被标记成NO; 永世不得翻身;子类可能需要在 updateWithTime: 中修改 _valid成员变量 72 | @property(nonatomic,assign,readonly,getter=isValid)BOOL valid; 73 | 74 | /// 输出的view,这样就不必自己再绘制图形了,并且可以使用硬件加速 75 | @property(nonatomic,strong,readonly)NSView * view; 76 | 77 | #pragma mark - called 78 | 79 | /// 结合相关上下文激活精灵; 如要覆盖, 请要先调用super方法 80 | - (void)activeWithContext:(NSDictionary *)context; 81 | 82 | /// 用相对时间更新状态; 最好不要覆盖此方法; 如要覆盖, 请要先调用super方法 83 | - (void)updateWithTime:(NSTimeInterval)time; 84 | 85 | #pragma mark - override - 86 | 87 | #pragma mark update 88 | /// _position, 此时刻的位置 89 | - (CGRect)rectWithTime:(NSTimeInterval)time; 90 | 91 | /// _valid, 此时刻是否还有效 92 | - (BOOL)validWithTime:(NSTimeInterval)time; 93 | 94 | #pragma mark launch 95 | /// 返回弹幕的初始位置 96 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites; 97 | 98 | /// 绑定的view 99 | - (NSView *)bindingView; 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSprite.h" 28 | 29 | @interface BarrageSprite() { 30 | NSPoint _oldPosition; 31 | } 32 | 33 | @end 34 | 35 | @implementation BarrageSprite 36 | 37 | @synthesize backgroundColor = _backgroundColor; 38 | @synthesize borderWidth = _borderWidth; 39 | @synthesize borderColor = _borderColor; 40 | @synthesize cornerRadius = _cornerRadius; 41 | @synthesize mandatorySize = _mandatorySize; 42 | 43 | @synthesize origin = _origin; 44 | @synthesize valid = _valid; 45 | @synthesize view = _view; 46 | 47 | - (instancetype)init 48 | { 49 | if (self = [super init]) { 50 | _delay = 0.0f; 51 | _birth = [NSDate date]; 52 | _valid = YES; 53 | _origin.x = _origin.y = MAXFLOAT; 54 | _z_index = 0; 55 | 56 | _backgroundColor = [NSColor clearColor]; 57 | _borderWidth = 0.0f; 58 | _borderColor = [NSColor clearColor]; 59 | _cornerRadius = 0.0f; 60 | _mandatorySize = CGSizeZero; 61 | } 62 | return self; 63 | } 64 | 65 | #pragma mark - update 66 | 67 | - (void)updateWithTime:(NSTimeInterval)time 68 | { 69 | _valid = [self validWithTime:time]; 70 | _view.frame = [self rectWithTime:time]; 71 | } 72 | 73 | - (CGRect)rectWithTime:(NSTimeInterval)time 74 | { 75 | return CGRectMake(_origin.x, _origin.y, self.size.width, self.size.height); 76 | } 77 | 78 | - (BOOL)validWithTime:(NSTimeInterval)time 79 | { 80 | return YES; 81 | } 82 | 83 | #pragma mark - launch 84 | 85 | - (void)activeWithContext:(NSDictionary *)context 86 | { 87 | CGRect rect = [[context objectForKey:kBarrageRendererContextCanvasBounds]CGRectValue]; 88 | NSArray * sprites = [context objectForKey:kBarrageRendererContextRelatedSpirts]; 89 | NSTimeInterval timestamp = [[context objectForKey:kBarrageRendererContextTimestamp]doubleValue]; 90 | _timestamp = timestamp; 91 | _view = [self bindingView]; 92 | [self configView]; 93 | if (!CGSizeEqualToSize(_mandatorySize, CGSizeZero)) { 94 | _view.frame = CGRectMake(0, 0, _mandatorySize.width, _mandatorySize.height); 95 | } 96 | // [_view setAutoresizingMask:NSViewMaxXMargin|NSViewMinYMargin]; 97 | _origin = [self originInBounds:rect withSprites:sprites]; 98 | _oldPosition = NSMakePoint(rect.size.width - _origin.x, rect.size.height - _origin.y); 99 | _view.frame = CGRectMake(_origin.x, _origin.y, self.size.width, self.size.height); 100 | } 101 | 102 | //- (CGPoint)origin { 103 | // NSRect rect = _view.superview.bounds; 104 | // if (rect.size.width - _origin.x != _oldPosition.x) { 105 | // _origin = NSMakePoint(rect.size.width - _oldPosition.x, rect.size.height - _oldPosition.y); 106 | // } 107 | // return _origin; 108 | //} 109 | 110 | - (void)configView 111 | { 112 | // if (self.borderWidth || self.cornerRadius) { 113 | // [_view setWantsLayer:YES]; // view's backing store is using a Core Animation Layer 114 | // } 115 | [_view setWantsLayer:YES]; 116 | if (self.cornerRadius) { 117 | _view.layer.cornerRadius = self.cornerRadius; 118 | //_view.clipsToBounds = YES; 119 | } 120 | if (self.borderColor) { 121 | _view.layer.borderColor = self.borderColor.CGColor; 122 | } 123 | if (self.borderWidth) { 124 | _view.layer.borderWidth = self.borderWidth; 125 | } 126 | if (self.backgroundColor) { 127 | _view.layer.backgroundColor = [self.backgroundColor CGColor]; 128 | } 129 | } 130 | 131 | /// 返回绑定的view 132 | - (NSView *)bindingView 133 | { 134 | return [[NSView alloc]init]; 135 | } 136 | 137 | /// 区域内的初始位置,只在刚加入渲染器的时候被调用;子类继承需要override. 138 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 139 | { 140 | CGFloat x = random_between(rect.origin.x, rect.origin.x+rect.size.width-self.size.width); 141 | CGFloat y = random_between(rect.origin.y, rect.origin.y+rect.size.height-self.size.height); 142 | return CGPointMake(x, y); 143 | } 144 | 145 | #pragma mark - attributes 146 | 147 | - (CGPoint)position 148 | { 149 | return self.view.frame.origin; 150 | } 151 | 152 | - (CGSize)size 153 | { 154 | return self.view.bounds.size; 155 | } 156 | 157 | - (void)setValue:(id)value forUndefinedKey:(NSString *)key 158 | { 159 | #ifdef DEBUG 160 | NSLog(@"[Class:%@] hasNo - [Property:%@]; [Value:%@] will be discarded.",NSStringFromClass([self class]),key,value); 161 | #endif 162 | } 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageSpriteProtocol.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | /// NSView 弹幕协议 30 | @protocol BarrageViewProtocol 31 | 32 | @required 33 | @property(nonatomic,strong)NSColor * backgroundColor; 34 | @property(nonatomic,assign)CGFloat borderWidth; 35 | @property(nonatomic,strong)NSColor * borderColor; 36 | /// 圆角,此属性十分影响绘制性能,谨慎使用 37 | @property(nonatomic,assign)CGFloat cornerRadius; 38 | /// 强制性大小,默认为CGSizeZero,大小自适应; 否则使用mandatorySize的值来设置view大小 39 | @property(nonatomic,assign)CGSize mandatorySize; 40 | 41 | @end 42 | 43 | /// NSLabel 弹幕协议 44 | @protocol BarrageTextProtocol 45 | 46 | @required 47 | @property(nonatomic,strong)NSString * text; 48 | @property(nonatomic,strong)NSColor * textColor; // 字体颜色 49 | @property(nonatomic,assign)CGFloat fontSize; 50 | @property(nonatomic,strong)NSString * fontFamily; 51 | @property(nonatomic,retain)NSColor * shadowColor; 52 | @property(nonatomic)CGSize shadowOffset; 53 | @property(nonatomic,strong)NSAttributedString * attributedText; 54 | 55 | @end 56 | 57 | /// NSImageView 弹幕协议dsadasdsadsasda 58 | @protocol BarrageImageProtocol 59 | 60 | @required 61 | @property(nonatomic,strong)NSImage * image; 62 | 63 | @end -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageSpriteUtility.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | BOOL divideLine(CGFloat * from, CGFloat * to, CGFloat *begin, CGFloat *end); 34 | BOOL searchMaxSpace(CGFloat * p_from,CGFloat * p_to,CGFloat begins[],CGFloat ends[], NSInteger n, CGFloat threshold); 35 | /// 生成在[min,max]的随机数 36 | CGFloat random_between(CGFloat min, CGFloat max); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | @interface BarrageSpriteUtility : NSObject 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageSpriteUtility.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSpriteUtility.h" 28 | 29 | @implementation BarrageSpriteUtility 30 | 31 | /// 一维线段分割函数 32 | /// 如果返回YES,通过[from,to]输出最大剪枝; 如果返回NO, 不存在有效剪枝 33 | /// 左右分支相等,优先使用左边的; 34 | /// 除了from,to, 其余的参数都是不可变的 35 | /// threshold>=0 36 | ///TODO: 此算法尚未验证 37 | BOOL searchMaxSpace(CGFloat * p_from,CGFloat * p_to,CGFloat begins[],CGFloat ends[], NSInteger n, CGFloat threshold) 38 | { 39 | if (n <= 0) { 40 | return YES; 41 | } 42 | if (threshold < 0) { 43 | *p_to = *p_from -1; // 做死了 44 | return NO; 45 | } 46 | CGFloat begin = begins[0]; // 第一次分割 47 | CGFloat end = ends[0]; 48 | CGFloat from = *p_from; 49 | CGFloat to = *p_to; 50 | if (divideLine(&from, &to, &begin, &end)) { 51 | CGFloat len1 = to - from; 52 | CGFloat len2 = end - begin; 53 | if (n>1) { 54 | if (len1 >= threshold) { // 左侧剪枝 55 | searchMaxSpace(&from, &to, begins+1, ends+1, n-1, threshold); 56 | } 57 | if (len2 >= threshold) { // 右侧剪枝 58 | searchMaxSpace(&begin, &end, begins+1, ends+1, n-1, threshold); 59 | } 60 | 61 | } 62 | if (len1 >= len2 && len1 >= threshold) { 63 | *p_from = from; 64 | *p_to = to; 65 | return YES; 66 | } 67 | else if(len2 > len1 && len2 >= threshold) 68 | { 69 | *p_from = begin; 70 | *p_to = end; 71 | return YES; 72 | } 73 | *p_to = *p_from -1; // 做死了 74 | return NO; 75 | } 76 | *p_to = *p_from -1; // 做死了 77 | return NO; 78 | } 79 | 80 | ///TODO: 很奇怪objective-c为什么不能传引用呢? 81 | /// [1,5]/[2,4] => [1,2]/[4,5]; 82 | /// 如果 *to < *from || *end < *begin, return NO; 83 | BOOL divideLine(CGFloat * from, CGFloat * to, CGFloat *begin, CGFloat *end) 84 | { 85 | if (*to >= *from && *end >= *begin) { 86 | CGFloat tmp = *to; 87 | *to = (*begin <= tmp)?*begin:*from-1; 88 | *begin = *end; 89 | *end = (*end >= *from)?tmp:*begin-1; 90 | return YES; 91 | } 92 | return NO; 93 | } 94 | 95 | /// 生成在[min,max]的随机数 96 | CGFloat random_between(CGFloat min, CGFloat max) 97 | { 98 | if (min >= max) { 99 | return min; 100 | } 101 | CGFloat scale = max - min; 102 | return min + scale * random()/RAND_MAX; 103 | } 104 | 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageWalkImageSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkSprite.h" 28 | 29 | /// 移动文字精灵 30 | @interface BarrageWalkImageSprite : BarrageWalkSprite 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageWalkImageSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkImageSprite.h" 28 | 29 | @implementation BarrageWalkImageSprite 30 | 31 | @synthesize image = _image; 32 | 33 | - (instancetype)init 34 | { 35 | if (self = [super init]) { 36 | _image = nil; 37 | } 38 | return self; 39 | } 40 | 41 | #pragma mark - launch 42 | 43 | - (NSView *)bindingView 44 | { 45 | NSImageView * imageView = [[NSImageView alloc]init]; 46 | imageView.image = _image; 47 | return imageView; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageWalkSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSprite.h" 28 | 29 | typedef NS_ENUM(NSUInteger, BarrageWalkDirection) { 30 | BarrageWalkDirectionR2L = 1, // 右向左 31 | BarrageWalkDirectionL2R = 2, // 左向右 32 | BarrageWalkDirectionT2B = 3, // 上往下 33 | BarrageWalkDirectionB2T = 4 // 下往上 34 | }; 35 | 36 | /// 移动文字精灵 37 | @interface BarrageWalkSprite : BarrageSprite 38 | { 39 | CGPoint _destination; 40 | } 41 | 42 | /// 速度,point/second 43 | @property(nonatomic,assign)CGFloat speed; 44 | 45 | /// 运动方向 46 | @property(nonatomic,assign)BarrageWalkDirection direction; 47 | 48 | /// 需要在originInBounds:withSprites: 方法中修改 _destination的值以表示运动的终点 49 | @property(nonatomic,assign,readonly)CGPoint destination; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageWalkSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkSprite.h" 28 | 29 | @interface BarrageWalkSprite() 30 | { 31 | BarrageWalkDirection _direction; 32 | } 33 | @end 34 | 35 | @implementation BarrageWalkSprite 36 | @synthesize direction = _direction; 37 | @synthesize destination = _destination; 38 | 39 | - (instancetype)init 40 | { 41 | if (self = [super init]) { 42 | _direction = BarrageWalkDirectionR2L; 43 | _speed = 30.0f; // 默认值 44 | } 45 | return self; 46 | } 47 | 48 | #pragma mark - update 49 | 50 | - (BOOL)validWithTime:(NSTimeInterval)time 51 | { 52 | return [self estimateActiveTime] > 0; 53 | } 54 | 55 | - (CGRect)rectWithTime:(NSTimeInterval)time 56 | { 57 | NSTimeInterval duration = time - self.timestamp; 58 | CGPoint position; 59 | switch (_direction) { 60 | case BarrageWalkDirectionR2L: 61 | position = CGPointMake(self.origin.x - duration * self.speed, self.origin.y); 62 | break; 63 | case BarrageWalkDirectionL2R: 64 | position = CGPointMake(self.origin.x + duration * self.speed, self.origin.y); 65 | break; 66 | case BarrageWalkDirectionT2B: 67 | position = CGPointMake(self.origin.x, self.origin.y - duration * self.speed); 68 | break; 69 | case BarrageWalkDirectionB2T: 70 | position = CGPointMake(self.origin.x, self.origin.y + duration * self.speed); 71 | default: 72 | break; 73 | } 74 | return CGRectMake(position.x, position.y, self.size.width, self.size.height); 75 | } 76 | 77 | /// 估算精灵的剩余存活时间 78 | - (NSTimeInterval)estimateActiveTime 79 | { 80 | CGFloat activeDistance = 0; 81 | switch (_direction) { 82 | case BarrageWalkDirectionR2L: 83 | activeDistance = self.position.x - _destination.x; 84 | break; 85 | case BarrageWalkDirectionL2R: 86 | activeDistance = _destination.x - self.position.x; 87 | break; 88 | case BarrageWalkDirectionT2B: 89 | activeDistance = _destination.y - self.position.y; 90 | break; 91 | case BarrageWalkDirectionB2T: 92 | activeDistance = self.position.y - _destination.y; 93 | default: 94 | break; 95 | } 96 | return activeDistance/self.speed; 97 | } 98 | 99 | #pragma mark - launch 100 | 101 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 102 | { 103 | // 获取同方向精灵 104 | NSMutableArray * synclasticSprites = [[NSMutableArray alloc]initWithCapacity:sprites.count]; 105 | for (BarrageWalkSprite * sprite in sprites) { 106 | if (sprite.direction == _direction) { 107 | [synclasticSprites addObject:sprite]; 108 | } 109 | } 110 | 111 | // static BOOL const AVAERAGE_STRATEGY = NO; // YES:条纹平均精灵策略(体验会好一些); NO:最快时间策略 112 | static NSUInteger const STRIP_NUM = 160; // 总共的网格条数 113 | NSTimeInterval stripMaxActiveTimes[STRIP_NUM]={0}; // 每一条网格 已有精灵中最后退出屏幕的时间 114 | NSUInteger stripSpriteNumbers[STRIP_NUM]={0}; // 每一条网格 包含精灵的数目 115 | CGFloat stripHeight = rect.size.height/STRIP_NUM; // 水平条高度 116 | CGFloat stripWidth = rect.size.width/STRIP_NUM; // 竖直条宽度 117 | BOOL oritation = _direction == BarrageWalkDirectionL2R || _direction == BarrageWalkDirectionR2L; // 方向, YES代表水平弹幕 118 | /// 计算数据结构,便于应用算法 119 | NSUInteger overlandStripNum = 1; // 横跨网格条数目 120 | if (oritation) { // 水平 121 | overlandStripNum = (NSUInteger)ceil((double)self.size.height/stripHeight); 122 | } 123 | else // 竖直 124 | { 125 | overlandStripNum = (NSUInteger)ceil((double)self.size.width/stripWidth); 126 | } 127 | /// 当前精灵需要的时间,左边碰到边界, 不是真实的活跃时间 128 | NSTimeInterval maxActiveTime = oritation?rect.size.width/self.speed:rect.size.height/self.speed; 129 | NSUInteger availableFrom = 0; 130 | NSUInteger leastActiveTimeStrip = 0; // 最小时间的行 131 | NSUInteger leastActiveSpriteStrip = 0; // 最小网格的行 132 | 133 | for (NSUInteger i = 0; i < STRIP_NUM; i++) { 134 | //寻找当前行里包含的sprites 135 | CGFloat stripFrom = i * (oritation?stripHeight:stripWidth); 136 | CGFloat stripTo = stripFrom + (oritation?stripHeight:stripWidth); 137 | CGFloat lastDistanceAllOut = YES; 138 | for (BarrageWalkSprite * sprite in synclasticSprites) { 139 | CGFloat Sy = sprite.origin.y; 140 | if(oritation){ 141 | // Cocoa 的上下坐标是反的 142 | Sy = rect.size.height - sprite.size.height - Sy; 143 | } 144 | CGFloat spriteFrom = oritation?Sy:sprite.origin.x; 145 | CGFloat spriteTo = spriteFrom + (oritation?sprite.size.height:sprite.size.width); 146 | if ((spriteTo-spriteFrom)+(stripTo-stripFrom)>MAX(stripTo-spriteFrom, spriteTo-stripFrom)) { // 在条条里 147 | stripSpriteNumbers[i]++; 148 | NSTimeInterval activeTime = (rect.size.width+sprite.size.width*2)/sprite.speed-(self.timestamp - sprite.timestamp); 149 | if (activeTime > stripMaxActiveTimes[i]){ // 获取最慢的那个 150 | stripMaxActiveTimes[i] = activeTime; 151 | CGFloat distance = (self.timestamp - sprite.timestamp)*sprite.speed; 152 | lastDistanceAllOut = distance > (oritation?sprite.size.width:sprite.size.height); 153 | } 154 | } 155 | } 156 | if (stripMaxActiveTimes[i]>maxActiveTime || !lastDistanceAllOut) { 157 | availableFrom = i+1; 158 | } 159 | else if (i - availableFrom >= overlandStripNum - 1){ 160 | break; // eureka! 161 | } 162 | if (i <= STRIP_NUM - overlandStripNum) { 163 | if (stripMaxActiveTimes[i] < stripMaxActiveTimes[leastActiveTimeStrip]) { 164 | leastActiveTimeStrip = i; 165 | } 166 | if (stripSpriteNumbers[i] < stripSpriteNumbers[leastActiveSpriteStrip]) { 167 | leastActiveSpriteStrip = i; 168 | } 169 | } 170 | } 171 | if (availableFrom > STRIP_NUM - overlandStripNum) { // 那就是没有找到喽 172 | availableFrom = arc4random()%(160-overlandStripNum);//AVAERAGE_STRATEGY?leastActiveSpriteStrip:leastActiveTimeStrip; // 使用最小个数 or 使用最短时间 173 | } 174 | 175 | CGPoint origin = CGPointZero; 176 | if (oritation) { // 水平 177 | _destination.y = origin.y = rect.size.height - (stripHeight * availableFrom + rect.origin.y) - self.size.height; // Cocoa 的上下坐标是反的 178 | origin.x = (self.direction == BarrageWalkDirectionL2R)?rect.origin.x - self.size.width:rect.origin.x + rect.size.width; 179 | _destination.x = (self.direction == BarrageWalkDirectionL2R)?rect.origin.x + rect.size.width:rect.origin.x - self.size.width; 180 | } 181 | else 182 | { 183 | _destination.x = origin.x = stripWidth * availableFrom + rect.origin.x; 184 | origin.y = (self.direction == BarrageWalkDirectionT2B)?rect.origin.y - self.size.height:rect.origin.y + rect.size.height; 185 | _destination.y = (self.direction == BarrageWalkDirectionT2B)?rect.origin.y + rect.size.height:rect.origin.y - self.size.height; 186 | } 187 | return origin; 188 | } 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageWalkTextSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkSprite.h" 28 | 29 | /// 移动文字精灵 30 | @interface BarrageWalkTextSprite : BarrageWalkSprite 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/BarrageSpirit/BarrageWalkTextSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkTextSprite.h" 28 | #import "NSLabel.h" 29 | 30 | @implementation BarrageWalkTextSprite 31 | 32 | @synthesize fontSize = _fontSize; 33 | @synthesize textColor = _textColor; 34 | @synthesize text = _text; 35 | @synthesize fontFamily = _fontFamily; 36 | @synthesize shadowColor = _shadowColor; 37 | @synthesize shadowOffset = _shadowOffset; 38 | @synthesize attributedText = _attributedText; 39 | 40 | - (instancetype)init 41 | { 42 | if (self = [super init]) { 43 | _textColor = [NSColor whiteColor]; 44 | _fontSize = 16.0f; 45 | _shadowColor = [NSColor blackColor]; 46 | _shadowOffset = CGSizeMake(1, 1); 47 | } 48 | return self; 49 | } 50 | 51 | #pragma mark - launch 52 | 53 | - (NSView *)bindingView 54 | { 55 | NSLabel * label = [[NSLabel alloc] init]; 56 | label.text = self.text; 57 | label.textColor = self.textColor; 58 | NSShadow *dropShadow = [[NSShadow alloc] init]; 59 | [dropShadow setShadowColor:_shadowColor]; 60 | [dropShadow setShadowOffset:_shadowOffset]; 61 | // [dropShadow setShadowBlurRadius:10.0]; 62 | label.dropShadow = dropShadow; 63 | label.font = self.fontFamily?[NSFont fontWithName:self.fontFamily size:self.fontSize]:[NSFont boldSystemFontOfSize:self.fontSize]; 64 | if (self.attributedText) { 65 | label.attributedText = self.attributedText; 66 | } 67 | return label; 68 | } 69 | @end 70 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/NSLabel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Leonard Hecker 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #import 26 | 27 | IB_DESIGNABLE 28 | @interface NSLabel : NSView 29 | 30 | @property(nonatomic, copy) IBInspectable NSString* text; 31 | @property(nonatomic, copy) NSAttributedString* attributedText; 32 | @property(nonatomic, retain) NSFont* font; 33 | @property(nonatomic, retain) IBInspectable NSColor* textColor; 34 | @property(nonatomic, retain) IBInspectable NSColor* backgroundColor; 35 | @property(nonatomic, assign) IBInspectable NSInteger numberOfLines; 36 | @property(nonatomic, assign) NSTextAlignment textAlignment; 37 | @property(nonatomic, assign) NSLineBreakMode lineBreakMode; 38 | @property(nonatomic, assign) IBInspectable CGFloat preferredMaxLayoutWidth; 39 | @property(nonatomic, retain) NSShadow *dropShadow; 40 | - (instancetype)init; 41 | - (instancetype)initWithFrame:(NSRect)frameRect NS_DESIGNATED_INITIALIZER; 42 | - (instancetype)initWithCoder:(NSCoder*)coder NS_DESIGNATED_INITIALIZER; 43 | 44 | @end -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/NSLabel.m: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Leonard Hecker 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #import "NSLabel.h" 26 | 27 | 28 | @implementation NSLabel { 29 | NSRect _drawingRect; 30 | } 31 | 32 | #pragma mark - 33 | #pragma mark NSView overrides 34 | 35 | - (instancetype)init { 36 | return [self initWithFrame:NSZeroRect]; 37 | } 38 | 39 | - (instancetype)initWithFrame:(NSRect)frameRect { 40 | if (self = [super initWithFrame:frameRect]) { 41 | // _text, _attributedText and _preferredMaxLayoutWidth are nil/0 by default 42 | self.font = self.defaultFont; 43 | self.textColor = self.defaultTextColor; 44 | self.backgroundColor = self.defaultBackgroundColor; 45 | self.numberOfLines = 1; 46 | self.textAlignment = NSLeftTextAlignment; 47 | self.lineBreakMode = NSLineBreakByTruncatingTail; 48 | } 49 | 50 | return self; 51 | } 52 | 53 | - (instancetype)initWithCoder:(NSCoder*)coder { 54 | if (self = [super initWithCoder:coder]) { 55 | NSString* text = nil; 56 | NSAttributedString* attributedText = nil; 57 | 58 | if ((text = [coder decodeObjectForKey:@"text"])) { 59 | self.text = text; 60 | } else if ((attributedText = [coder decodeObjectForKey:@"attributedText"])) { 61 | self.attributedText = attributedText; 62 | } 63 | 64 | self.font = [coder decodeObjectForKey:@"font"]; 65 | self.textColor = [coder decodeObjectForKey:@"textColor"]; 66 | self.backgroundColor = [coder decodeObjectForKey:@"backgroundColor"]; 67 | self.numberOfLines = [coder containsValueForKey:@"numberOfLines"] ? [[coder decodeObjectForKey:@"numberOfLines"] integerValue] : 1; 68 | self.textAlignment = [coder containsValueForKey:@"numberOfLines"] ? [[coder decodeObjectForKey:@"textAlignment"] unsignedIntegerValue] : NSLeftTextAlignment; 69 | self.lineBreakMode = [coder containsValueForKey:@"numberOfLines"] ? [[coder decodeObjectForKey:@"lineBreakMode"] unsignedIntegerValue] : NSLineBreakByTruncatingTail; 70 | 71 | #if CGFLOAT_IS_DOUBLE 72 | self.preferredMaxLayoutWidth = [[coder decodeObjectForKey:@"preferredMaxLayoutWidth"] doubleValue]; 73 | #else 74 | self.preferredMaxLayoutWidth = [[coder decodeObjectForKey:@"preferredMaxLayoutWidth"] floatValue]; 75 | #endif 76 | } 77 | 78 | return self; 79 | } 80 | 81 | - (void)encodeWithCoder:(NSCoder*)aCoder { 82 | [super encodeWithCoder:aCoder]; 83 | 84 | NSString* text = nil; 85 | NSAttributedString* attributedText = nil; 86 | 87 | if ((text = self.text)) { 88 | [aCoder encodeObject:text forKey:@"text"]; 89 | } else if ((attributedText = self.attributedText)) { 90 | [aCoder encodeObject:attributedText forKey:@"attributedText"]; 91 | } 92 | 93 | [aCoder encodeObject:self.font forKey:@"font"]; 94 | [aCoder encodeObject:self.textColor forKey:@"textColor"]; 95 | [aCoder encodeObject:self.backgroundColor forKey:@"backgroundColor"]; 96 | [aCoder encodeObject:@(self.numberOfLines) forKey:@"numberOfLines"]; 97 | [aCoder encodeObject:@(self.textAlignment) forKey:@"textAlignment"]; 98 | [aCoder encodeObject:@(self.lineBreakMode) forKey:@"lineBreakMode"]; 99 | [aCoder encodeObject:@(self.preferredMaxLayoutWidth) forKey:@"preferredMaxLayoutWidth"]; 100 | } 101 | 102 | - (BOOL)isOpaque { 103 | return self.backgroundColor.alphaComponent == 1.0; 104 | } 105 | 106 | - (CGFloat)baselineOffsetFromBottom { 107 | return self.drawingRect.origin.y; 108 | } 109 | 110 | - (NSSize)intrinsicContentSize { 111 | return self.drawingRect.size; 112 | } 113 | 114 | - (void)invalidateIntrinsicContentSize { 115 | _drawingRect = NSZeroRect; 116 | [super invalidateIntrinsicContentSize]; 117 | } 118 | 119 | - (void)drawRect:(NSRect)dirtyRect { 120 | NSRect bounds = self.bounds; 121 | NSRect drawRect = {self.drawingRect.origin, bounds.size}; 122 | NSString* text = nil; 123 | NSAttributedString* attributedText = nil; 124 | 125 | [self.backgroundColor setFill]; 126 | NSRectFillUsingOperation(bounds, NSCompositeSourceOver); 127 | 128 | if ((text = self.text)) { 129 | [text drawWithRect:drawRect options:self.drawingOptions attributes:@{ 130 | NSFontAttributeName : self.font, 131 | NSForegroundColorAttributeName : self.textColor, 132 | NSStrokeColorAttributeName : self.dropShadow.shadowColor, 133 | NSStrokeWidthAttributeName : @-1 134 | }]; 135 | 136 | } else if ((attributedText = self.attributedText)) { 137 | [attributedText drawWithRect:drawRect options:self.drawingOptions]; 138 | } 139 | } 140 | 141 | #pragma mark - 142 | #pragma mark private helper methods 143 | 144 | - (NSRect)drawingRect { 145 | // invalidated by [NSLabel invalidateIntrinsicContentSize] 146 | 147 | NSString* text = nil; 148 | NSAttributedString* attributedText = nil; 149 | 150 | if (NSIsEmptyRect(_drawingRect) && ((text = self.text) || (attributedText = self.attributedText))) { 151 | NSSize size = NSMakeSize(self.preferredMaxLayoutWidth, 0.0); 152 | 153 | if (text) { 154 | _drawingRect = [text boundingRectWithSize:size options:self.drawingOptions attributes:@{ 155 | NSFontAttributeName : self.font, 156 | NSForegroundColorAttributeName : self.textColor 157 | // NSBackgroundColorAttributeName : self.backgroundColor, 158 | // NSParagraphStyleAttributeName : self.drawingParagraphStyle, 159 | }]; 160 | } else { 161 | _drawingRect = [attributedText boundingRectWithSize:size options:self.drawingOptions]; 162 | } 163 | 164 | _drawingRect = (NSRect) { 165 | { 166 | ceil(-_drawingRect.origin.x), 167 | ceil(-_drawingRect.origin.y), 168 | }, { 169 | ceil(_drawingRect.size.width), 170 | ceil(_drawingRect.size.height), 171 | } 172 | }; 173 | } 174 | return _drawingRect; 175 | } 176 | 177 | - (NSStringDrawingOptions)drawingOptions { 178 | NSStringDrawingOptions options = NSStringDrawingUsesFontLeading; 179 | 180 | if (self.numberOfLines == 0) { 181 | options |= NSStringDrawingUsesLineFragmentOrigin; 182 | } 183 | 184 | return options; 185 | } 186 | 187 | - (NSParagraphStyle*)drawingParagraphStyle { 188 | NSMutableParagraphStyle* ps = [NSMutableParagraphStyle new]; 189 | ps.alignment = self.textAlignment; 190 | 191 | if (self.numberOfLines) { 192 | ps.lineBreakMode = self.lineBreakMode; 193 | } 194 | 195 | return ps; 196 | } 197 | 198 | - (NSFont*)defaultFont { 199 | return [NSFont labelFontOfSize:12.0]; 200 | } 201 | 202 | - (NSColor*)defaultTextColor { 203 | return [NSColor blackColor]; 204 | } 205 | 206 | - (NSColor*)defaultBackgroundColor { 207 | return [NSColor clearColor]; 208 | } 209 | 210 | #pragma mark - 211 | #pragma mark setters which invalidate the view 212 | 213 | - (void)setText:(NSString*)text { 214 | _text = [text copy]; 215 | _attributedText = nil; 216 | [self invalidateIntrinsicContentSize]; 217 | [self setNeedsDisplay:YES]; 218 | } 219 | 220 | - (void)setAttributedText:(NSAttributedString*)attributedText { 221 | _text = nil; 222 | _attributedText = [attributedText copy]; 223 | [self invalidateIntrinsicContentSize]; 224 | [self setNeedsDisplay:YES]; 225 | } 226 | 227 | - (void)setFont:(NSFont*)font { 228 | _font = font ? font : self.defaultFont; 229 | [self invalidateIntrinsicContentSize]; 230 | [self setNeedsDisplay:YES]; 231 | 232 | super.frame = [self drawingRect]; 233 | } 234 | 235 | - (void)setTextColor:(NSColor*)textColor { 236 | _textColor = textColor ? textColor : self.defaultTextColor; 237 | [self setNeedsDisplay:YES]; 238 | } 239 | 240 | - (void)setBackgroundColor:(NSColor*)backgroundColor { 241 | _backgroundColor = backgroundColor ? backgroundColor : self.defaultBackgroundColor; 242 | [self setNeedsDisplay:YES]; 243 | } 244 | 245 | - (void)setTextAlignment:(NSTextAlignment)textAlignment { 246 | _textAlignment = textAlignment; 247 | [self invalidateIntrinsicContentSize]; 248 | [self setNeedsDisplay:YES]; 249 | } 250 | 251 | - (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode { 252 | _lineBreakMode = lineBreakMode; 253 | [self invalidateIntrinsicContentSize]; 254 | [self setNeedsDisplay:YES]; 255 | 256 | } 257 | 258 | @end 259 | -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/NSValue+iOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSValue+iOS.h 3 | // MacMapView 4 | // 5 | // Created by David Bainbridge on 5/14/13. 6 | // Copyright (c) 2013 David Bainbridge. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSValue (iOS) 12 | + (NSValue *)valueWithCGPoint:(CGPoint)point; 13 | - (CGPoint)CGPointValue; 14 | + (NSValue *)valueWithCGRect:(CGRect)rect; 15 | - (CGRect)CGRectValue; 16 | + (NSValue *)valueWithCGSize:(CGSize)size; 17 | - (CGSize)CGSizeValue; 18 | @end -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/NSValue+iOS.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSValue+iOS.m 3 | // MacMapView 4 | // 5 | // Created by David Bainbridge on 5/14/13. 6 | // Copyright (c) 2013 David Bainbridge. All rights reserved. 7 | // 8 | 9 | #import "NSValue+iOS.h" 10 | 11 | @implementation NSValue (iOS) 12 | + (NSValue *)valueWithCGPoint:(CGPoint)point 13 | { 14 | return [NSValue valueWithPoint:NSPointFromCGPoint(point)]; 15 | } 16 | 17 | - (CGPoint)CGPointValue 18 | { 19 | return NSPointToCGPoint([self pointValue]); 20 | } 21 | 22 | + (NSValue *)valueWithCGRect:(CGRect)rect 23 | { 24 | return [NSValue valueWithRect:NSRectFromCGRect(rect)]; 25 | } 26 | 27 | - (CGRect)CGRectValue 28 | { 29 | return NSRectToCGRect([self rectValue]); 30 | } 31 | 32 | + (NSValue *)valueWithCGSize:(CGSize)size 33 | { 34 | return [NSValue valueWithSize:NSSizeFromCGSize(size)]; 35 | } 36 | 37 | - (CGSize)CGSizeValue 38 | { 39 | return NSSizeToCGSize([self sizeValue]); 40 | } 41 | 42 | @end -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/NSView+UIView.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+UIView.h 3 | // MapView 4 | // 5 | // Created by David Bainbridge on 2/17/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSView (UIView) 12 | - (void)insertSubview:(NSView *)subview aboveSubview:(NSView *)above; 13 | - (void)insertSubview:(NSView *)view atIndex:(NSInteger)index; 14 | - (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2; 15 | - (void)layoutSubviews; 16 | @end -------------------------------------------------------------------------------- /Douyu/Frameworks/BarrageRenderer/NSView+UIView.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+UIView.m 3 | // MapView 4 | // 5 | // Created by David Bainbridge on 2/17/13. 6 | // 7 | // 8 | 9 | #import "NSView+UIView.h" 10 | 11 | @implementation NSView (UIView) 12 | 13 | - (void)insertSubview:(NSView *)subview aboveSubview:(NSView *)above 14 | { 15 | [self addSubview:subview positioned:NSWindowAbove relativeTo:above]; 16 | } 17 | 18 | - (void)insertSubview:(NSView *)view atIndex:(NSInteger)index 19 | { 20 | //TODO: use index 21 | [self addSubview:view]; 22 | } 23 | 24 | - (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2 25 | { 26 | NSMutableArray *subViews = [self.subviews mutableCopy]; 27 | [subViews exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; 28 | [self setSubviews:subViews]; 29 | 30 | } 31 | 32 | - (void) layoutSubviews 33 | { 34 | [self resizeSubviewsWithOldSize:[self frame].size]; 35 | } 36 | 37 | 38 | @end -------------------------------------------------------------------------------- /Douyu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.1.8 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Editor 26 | CFBundleURLIconFile 27 | 28 | CFBundleURLName 29 | douyu 30 | CFBundleURLSchemes 31 | 32 | dy 33 | 34 | 35 | 36 | CFBundleVersion 37 | 15 38 | LSApplicationCategoryType 39 | public.app-category.video 40 | LSMinimumSystemVersion 41 | $(MACOSX_DEPLOYMENT_TARGET) 42 | NSAppTransportSecurity 43 | 44 | NSAllowsArbitraryLoads 45 | 46 | 47 | NSHumanReadableCopyright 48 | Copyright © 2017年 Lanskaya. All rights reserved. 49 | NSMainStoryboardFile 50 | Main 51 | NSPrincipalClass 52 | NSApplication 53 | 54 | 55 | -------------------------------------------------------------------------------- /Douyu/Model/DYDanmuProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // DYDanmuProvider.h 3 | // vp_tucao 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DYRoomInfo.h" 11 | 12 | @protocol DYDanmuProviderDelegate 13 | 14 | - (void)onNewMessage:(NSString *)cmContent :(NSString *)userName :(int)ftype :(int)fsize :(NSColor *)color; 15 | 16 | @end 17 | 18 | @interface DYDanmuProvider : NSObject 19 | 20 | @property (nonatomic, weak) id delegate; 21 | 22 | - (void)loadWithInfo:(DYRoomInfo *)roomInfo; 23 | - (void)disconnect; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Douyu/Model/DYDanmuProvider.m: -------------------------------------------------------------------------------- 1 | // 2 | // DYDanmuProvider.m 3 | // vp_tucao 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "DYDanmuProvider.h" 10 | #import "DanmuSocket.h" 11 | #import "AuthSocket.h" 12 | #import "DanmuModel.h" 13 | 14 | @interface DYDanmuProvider () 15 | 16 | @property (nonatomic, strong) DanmuSocket *danmuSocket; 17 | 18 | @end 19 | 20 | 21 | @implementation DYDanmuProvider 22 | 23 | - (void)loadWithInfo:(DYRoomInfo *)roomInfo { 24 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMessageNotification:) name:@"kReceiveDYMessageNotification" object:nil]; 25 | _danmuSocket = [DanmuSocket sharedInstance]; 26 | _danmuSocket.room = roomInfo.roomId; 27 | [_danmuSocket connectSocketHost]; 28 | } 29 | 30 | - (void)receiveMessageNotification:(NSNotification *)notification { 31 | 32 | NSString *string = notification.object; 33 | //判断消息类型 34 | if ([string rangeOfString:@"type@=mrkl"].location == NSNotFound) { 35 | DanmuModel *model = [DanmuModel new]; 36 | if ([string rangeOfString:@"type@=chatmsg"].location != NSNotFound) { 37 | model.cellType = CellNewChatMessageType; 38 | 39 | }else if ([string rangeOfString:@"type@=dgb"].location != NSNotFound){ 40 | model.cellType = CellNewGiftType; 41 | 42 | }else if ([string rangeOfString:@"type@=uenter"].location != NSNotFound){ 43 | model.cellType = CellNewUserEnterType; 44 | 45 | }else if ([string rangeOfString:@"type@=blackres"].location != NSNotFound){ 46 | model.cellType = CellBanType; 47 | 48 | }else if ([string rangeOfString:@"type@=bc_buy_deserve"].location != NSNotFound){ 49 | model.cellType = CellDeserveType; 50 | }else{ 51 | // NSLog(@"%@",string); 52 | model = nil; 53 | return; 54 | } 55 | if (model.cellType == CellNewChatMessageType) { 56 | [model setModelFromStirng:string]; 57 | // NSLog(@"chatmsg=%@",model.dataString); 58 | [self.delegate onNewMessage:model.unColoredMsg :model.nickname :model.nc :24 :model.color]; 59 | } 60 | 61 | 62 | } 63 | 64 | } 65 | 66 | - (void)disconnect{ 67 | [_danmuSocket cutOffSocket]; 68 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 69 | } 70 | 71 | 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /Douyu/Model/DYRoomHistoryData.h: -------------------------------------------------------------------------------- 1 | // 2 | // DYRoomHistoryData.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/12/22. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface DYRoomHistoryData : NSObject 13 | 14 | @property (nonatomic,strong) NSString *roomId; 15 | @property (nonatomic,strong) NSString *nickname; 16 | @property (nonatomic,strong) NSDate *lastWatchTime; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Douyu/Model/DYRoomHistoryData.m: -------------------------------------------------------------------------------- 1 | // 2 | // DYRoomHistoryData.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/12/22. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "DYRoomHistoryData.h" 10 | 11 | @implementation DYRoomHistoryData 12 | 13 | + (LKDBHelper *)getUsingLKDBHelper 14 | { 15 | static LKDBHelper *helper; 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 19 | NSString *applicationSupportDirectory = [paths firstObject]; 20 | NSString *dbPath = [NSString stringWithFormat:@"%@/Douyu/history.db",applicationSupportDirectory]; 21 | helper = [[LKDBHelper alloc] initWithDBPath:dbPath]; 22 | }); 23 | return helper; 24 | } 25 | 26 | + (void)columnAttributeWithProperty:(LKDBProperty *)property { 27 | property.isNotNull = YES; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Douyu/Model/DYRoomHistoryModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // DYRoomHistoryModel.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/12/22. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DYRoomHistoryData.h" 11 | 12 | @interface DYRoomHistoryModel : NSObject 13 | 14 | + (NSArray *)getAll; 15 | + (DYRoomHistoryData *)getRoomId:(NSString *)roomId; 16 | + (void)saveRoomId:(NSString *)roomId withNickname:(NSString *)nickname; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Douyu/Model/DYRoomHistoryModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // DYRoomHistoryModel.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/12/22. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "DYRoomHistoryModel.h" 10 | 11 | @implementation DYRoomHistoryModel 12 | 13 | + (NSArray *)getAll { 14 | return [[DYRoomHistoryData searchWithWhere:nil orderBy:@"lastWatchTime DESC" offset:0 count:0] copy]; 15 | } 16 | 17 | + (DYRoomHistoryData *)getRoomId:(NSString *)roomId { 18 | if (!roomId.length) { 19 | return nil; 20 | } 21 | return [DYRoomHistoryData searchSingleWithWhere:@{@"roomId":roomId} orderBy:nil]; 22 | } 23 | 24 | + (void)saveRoomId:(NSString *)roomId withNickname:(NSString *)nickname { 25 | DYRoomHistoryData *data = [self getRoomId:roomId]; 26 | if (!data) { 27 | data = [[DYRoomHistoryData alloc] init]; 28 | data.roomId = roomId; 29 | data.nickname = nickname; 30 | } 31 | data.lastWatchTime = [NSDate date]; 32 | [data saveToDB]; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Douyu/Model/DYRoomInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // DYRoomInfo.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DYRoomInfo : NSObject 12 | 13 | @property (nonatomic, copy) NSString *roomId; 14 | @property (nonatomic, copy) NSString *roomName; 15 | @property (nonatomic, copy) NSString *nickName; 16 | @property (nonatomic, copy) NSString *videoUrl; 17 | @property (nonatomic, assign) BOOL showStatus; 18 | 19 | @property (nonatomic, copy) NSString *did; 20 | @property (nonatomic, copy) NSString *roomJS; 21 | 22 | - (NSString *)getRoomIdWithString:(NSString *)string; 23 | - (BOOL)getInfoWithRoomId:(NSString *)roomId rate:(int)rate; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Douyu/Model/DYRoomInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // DYRoomInfo.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "DYRoomInfo.h" 10 | #import "NSString+InfoGet.h" 11 | #import 12 | 13 | @implementation DYRoomInfo 14 | 15 | - (NSString *)getRoomIdWithString:(NSString *)string { 16 | NSString *roomId = string; 17 | NSString *roomInfoUrl = [@"http://open.douyucdn.cn/api/RoomApi/room/" stringByAppendingString:roomId]; 18 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:roomInfoUrl]]; 19 | request.timeoutInterval = 5.0f; 20 | 21 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 22 | 23 | __block NSData *roomData = nil; 24 | NSURLSession *session = [NSURLSession sharedSession]; 25 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 26 | if (!error) { 27 | roomData = data; 28 | } 29 | dispatch_semaphore_signal(semaphore); 30 | }]; 31 | [task resume]; 32 | 33 | dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER); 34 | 35 | if (!roomData) { 36 | return nil; 37 | } 38 | NSDictionary *respDic = [NSJSONSerialization JSONObjectWithData:roomData options:0 error:nil]; 39 | if (!respDic || ![respDic isKindOfClass:[NSDictionary class]] || [respDic[@"error"] intValue] != 0) { 40 | return nil; 41 | } 42 | NSDictionary *roomDic = respDic[@"data"]; 43 | NSString *nickName = roomDic[@"owner_name"]; 44 | NSString *roomName = roomDic[@"room_name"]; 45 | roomId = roomDic[@"room_id"]; 46 | self.roomId = roomId; 47 | self.roomName = roomName; 48 | self.nickName = nickName; 49 | self.showStatus = [roomDic[@"room_status"] intValue] == 1; 50 | return roomId; 51 | } 52 | 53 | - (BOOL)getDouyuDid { 54 | int random = arc4random() % 1000000; 55 | NSString *callback = [NSString stringWithFormat:@"jsonp_%d", random]; 56 | NSString *didUrl = [NSString stringWithFormat:@"https://passport.douyu.com/lapi/did/api/get?client_id=1&callback=%@", callback]; 57 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:didUrl]]; 58 | request.timeoutInterval = 5.0f; 59 | [request setValue:@"https://www.douyu.com" forHTTPHeaderField:@"Referer"]; 60 | 61 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 62 | 63 | __block NSData *respData = nil; 64 | NSURLSession *session = [NSURLSession sharedSession]; 65 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 66 | if (!error) { 67 | respData = data; 68 | } 69 | dispatch_semaphore_signal(semaphore); 70 | }]; 71 | [task resume]; 72 | 73 | dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER); 74 | NSString *resp = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding]; 75 | NSString *json = [[resp stringByReplacingOccurrencesOfString:callback withString:@""] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\n ()"]]; 76 | NSDictionary *respDic = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; 77 | if (!respDic || ![respDic isKindOfClass:[NSDictionary class]] || [respDic[@"error"] intValue] != 0) { 78 | return NO; 79 | } 80 | self.did = [respDic valueForKeyPath:@"data.did"]; 81 | return self.did == nil; 82 | } 83 | 84 | - (BOOL)getRoomJS:(NSString *)roomId { 85 | NSString *roomJSURL = [NSString stringWithFormat:@"https://www.douyu.com/swf_api/homeH5Enc?rids=%@",roomId]; 86 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:roomJSURL]]; 87 | request.timeoutInterval = 5.0f; 88 | 89 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 90 | 91 | __block NSData *roomData = nil; 92 | NSURLSession *session = [NSURLSession sharedSession]; 93 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 94 | if (!error) { 95 | roomData = data; 96 | } 97 | dispatch_semaphore_signal(semaphore); 98 | }]; 99 | [task resume]; 100 | 101 | dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER); 102 | NSDictionary *respDic = [NSJSONSerialization JSONObjectWithData:roomData options:0 error:nil]; 103 | if (!respDic || ![respDic isKindOfClass:[NSDictionary class]] || [respDic[@"error"] intValue] != 0) { 104 | return NO; 105 | } 106 | self.roomJS = [respDic valueForKeyPath:[NSString stringWithFormat:@"data.room%@", roomId]]; 107 | return self.roomJS == nil; 108 | } 109 | 110 | - (BOOL)getInfoWithRoomId:(NSString *)roomId rate:(int)rate { 111 | dispatch_group_t group = dispatch_group_create(); 112 | dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ 113 | [self getDouyuDid]; 114 | }); 115 | dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ 116 | [self getRoomJS:roomId]; 117 | }); 118 | 119 | dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 120 | 121 | if (!self.did || !self.roomJS) { 122 | return NO; 123 | } 124 | 125 | int time = NSDate.date.timeIntervalSince1970; 126 | NSURL *cryptoJsFileURL = [[NSBundle mainBundle] URLForResource:@"crypto-js" withExtension:@"js"]; 127 | NSError *error; 128 | NSString *cryptoJS = [NSString stringWithContentsOfURL:cryptoJsFileURL encoding:NSUTF8StringEncoding error:&error]; 129 | if (error) { 130 | return NO; 131 | } 132 | JSContext *jsContext = [JSContext new]; 133 | [jsContext evaluateScript:cryptoJS]; 134 | [jsContext evaluateScript:@"var CryptoJS = require('crypto-js');"]; 135 | [jsContext evaluateScript:self.roomJS]; 136 | 137 | NSString *result = [jsContext evaluateScript:[NSString stringWithFormat:@"ub98484234(%@, '%@', %d)", roomId, self.did, time]].toString; 138 | NSString *postString = [NSString stringWithFormat:@"%@&cdn=&rate=%d&ver=Douyu_219021902&iar=1&ive=0", result, rate]; 139 | 140 | NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://www.douyu.com/lapi/live/getH5Play/%@", roomId]]; 141 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 142 | request.timeoutInterval = 5.0f; 143 | [request setValue:@"Mozilla/5.0 (iPad; CPU OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B466 Safari/600.1.4" forHTTPHeaderField:@"User-Agent"]; 144 | 145 | request.HTTPMethod = @"POST"; 146 | request.HTTPBody = [postString dataUsingEncoding:NSUTF8StringEncoding]; 147 | 148 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 149 | 150 | __block NSData *roomData = nil; 151 | NSURLSession *session = [NSURLSession sharedSession]; 152 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 153 | if (!error) { 154 | roomData = data; 155 | } 156 | dispatch_semaphore_signal(semaphore); 157 | }]; 158 | [task resume]; 159 | 160 | dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER); 161 | if (!roomData) { 162 | return NO; 163 | } 164 | NSDictionary *respDic = [NSJSONSerialization JSONObjectWithData:roomData options:0 error:nil]; 165 | if (!respDic || ![respDic isKindOfClass:[NSDictionary class]] || [respDic[@"error"] intValue] != 0) { 166 | return NO; 167 | } 168 | self.showStatus = YES; 169 | NSDictionary *roomDic = respDic[@"data"]; 170 | 171 | NSString *rtmpPrefix = roomDic[@"rtmp_url"]; 172 | NSString *rtmpSuffix = roomDic[@"rtmp_live"]; 173 | NSString *videoUrl = [NSString stringWithFormat:@"%@/%@",rtmpPrefix,rtmpSuffix]; 174 | 175 | self.videoUrl = videoUrl; 176 | return YES; 177 | } 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /Douyu/Model/NSString+InfoGet.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+InfoGet.h 3 | // 4 | // 5 | // Created by LuChen on 16/2/26. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (InfoGet) 12 | 13 | - (NSString *)getMd5_32Bit;//MD5加密 14 | + (NSString *)uuid;//获取随机UUID 15 | + (NSString *)timeString;//获取时间戳(秒) 16 | - (BOOL)isPureInt; 17 | - (NSMutableData *)stringToHexData;//转换16位的data 18 | - (NSString *)componentsFirstMatchedByRegex:(NSString *)regexStr; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Douyu/Model/NSString+InfoGet.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+InfoGet.m 3 | // 4 | // 5 | // Created by LuChen on 16/2/26. 6 | // 7 | // 8 | 9 | #import "NSString+InfoGet.h" 10 | #import 11 | 12 | 13 | @implementation NSString (InfoGet) 14 | 15 | //static inline char itoh(int i) { 16 | // if (i > 9) return 'A' + (i - 10); 17 | // return '0' + i; 18 | //} 19 | 20 | - (NSString *)getMd5_32Bit { 21 | const char *cStr = [self UTF8String]; 22 | unsigned char digest[CC_MD5_DIGEST_LENGTH]; 23 | CC_MD5( cStr, (unsigned int)self.length, digest ); 24 | NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; 25 | for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) 26 | [result appendFormat:@"%02x", digest[i]]; 27 | return result; 28 | } 29 | 30 | + (NSString*)uuid{ 31 | NSString *deviceID =[[NSUUID UUID] UUIDString]; 32 | deviceID = [deviceID stringByReplacingOccurrencesOfString:@"-" withString:@""]; 33 | return deviceID; 34 | } 35 | 36 | + (NSString *)timeString{ 37 | NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0]; 38 | NSTimeInterval a=[dat timeIntervalSince1970]; 39 | NSString *timeString = [NSString stringWithFormat:@"%d",(int)a]; 40 | return timeString; 41 | } 42 | 43 | - (BOOL)isPureInt { 44 | NSScanner* scan = [NSScanner scannerWithString:self]; 45 | int val; 46 | return[scan scanInt:&val] && [scan isAtEnd]; 47 | } 48 | 49 | - (NSMutableData *)stringToHexData{ 50 | NSData *myD = [self dataUsingEncoding:NSUTF8StringEncoding]; 51 | Byte *bytes = (Byte *)[myD bytes]; 52 | //下面是Byte 转换为16进制。 53 | NSString *hexStr=@""; 54 | for(int i=0;i<[myD length];i++) 55 | 56 | { 57 | NSString *newHexStr = [NSString stringWithFormat:@"%x",bytes[i]&0xff];///16进制数 58 | 59 | if([newHexStr length]==1) 60 | 61 | hexStr = [NSString stringWithFormat:@"%@0%@",hexStr,newHexStr]; 62 | 63 | else 64 | 65 | hexStr = [NSString stringWithFormat:@"%@%@",hexStr,newHexStr]; 66 | } 67 | NSString *command = [hexStr stringByReplacingOccurrencesOfString:@" " withString:@""]; 68 | NSMutableData *commandToSend= [[NSMutableData alloc] init]; 69 | unsigned char whole_byte; 70 | char byte_chars[3] = {'\0','\0','\0'}; 71 | int i; 72 | for (i=0; i < [command length]/2; i++) { 73 | byte_chars[0] = [command characterAtIndex:i*2]; 74 | byte_chars[1] = [command characterAtIndex:i*2+1]; 75 | whole_byte = strtol(byte_chars, NULL, 16); 76 | [commandToSend appendBytes:&whole_byte length:1]; 77 | } 78 | return commandToSend; 79 | } 80 | 81 | - (NSString *)componentsFirstMatchedByRegex:(NSString *)regexStr { 82 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:nil]; 83 | NSRange range = [regex rangeOfFirstMatchInString:self options:NSMatchingReportProgress range:NSMakeRange(0, self.length)]; 84 | if (range.location != NSNotFound) { 85 | return [self substringWithRange:range]; 86 | } 87 | return nil; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /Douyu/Model/Settings.h: -------------------------------------------------------------------------------- 1 | // 2 | // Settings.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/22. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Settings : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Douyu/Model/Settings.m: -------------------------------------------------------------------------------- 1 | // 2 | // Settings.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/22. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "Settings.h" 10 | 11 | @implementation Settings 12 | 13 | + (instancetype)shareInstance { 14 | static Settings *instance = nil; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | instance = [[self alloc] init]; 18 | }); 19 | return instance; 20 | } 21 | 22 | - (instancetype)init { 23 | if (self) { 24 | self = [super init]; 25 | } 26 | return self; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Douyu/Model/socket/AuthSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // AuthSocket.h 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/3/2. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import "DouyuTVSocket.h" 10 | 11 | static const NSString *kMagicCode = @"7oE9nPEG9xXV69phU31FYCLUagKeYtsF"; 12 | 13 | @interface AuthSocket : DouyuTVSocket 14 | 15 | @property NSArray *servers; 16 | 17 | @property(nonatomic,copy)void(^InfoBlock)(NSString *vistorID,NSString *groupID); 18 | + (id)sharedInstance; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Douyu/Model/socket/AuthSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // AuthSocket.m 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/3/2. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import "AuthSocket.h" 10 | 11 | static AuthSocket *instance = nil; 12 | @implementation AuthSocket 13 | //单例 14 | + (id)sharedInstance{ 15 | @synchronized(self) { 16 | if (instance == nil) { 17 | 18 | instance = [[AuthSocket alloc]init]; 19 | } 20 | } 21 | return instance; 22 | } 23 | 24 | - (void)setServerConfig{ 25 | //转换成model,添加到属性 26 | ServerModel *model = [ServerModel new]; 27 | model.ip = @"openbarrage.douyutv.com"; 28 | model.port = 8601; 29 | [self.server addObject:model]; 30 | } 31 | 32 | #pragma marl --回调方法 33 | //连接成功 34 | - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ 35 | NSLog(@"---认证服务器连接成功---"); 36 | /* 37 | 验证服务器包 38 | type@=loginreq/username@=/ct@=0/password@=/roomid@=43053/devid@=479512DA70520865088A9A52B53242FB/rt@=1450509705/vk@=d2494e478b4229e3c98a398c8ae2c8f3/ver@=20150929/ 39 | roomid:房间id 40 | devid:随机UUID 41 | rt:时间戳 42 | vk:时间戳+"7oE9nPEG9xXV69phU31FYCLUagKeYtsF"+devid的字符串拼接结果的MD5值 43 | ver:版本号 44 | */ 45 | NSString *devid = [NSString uuid]; 46 | NSString *timeString = [NSString timeString]; 47 | NSString *unMD5vk = [NSString stringWithFormat:@"%@%@%@",timeString,kMagicCode,devid]; 48 | NSString *vk = [unMD5vk getMd5_32Bit]; 49 | 50 | NSString *postLogin = [NSString stringWithFormat:@"type@=loginreq/username@=/ct@=0/password@=/roomid@=%@/devid@=%@/rt@=%@/vk@=%@/ver@=20150929/",self.room,devid,timeString,vk]; 51 | NSData *postLoginData = [self packToData:postLogin]; 52 | [self.socket writeData:postLoginData withTimeout:30 tag:1]; 53 | 54 | } 55 | 56 | //接受数据 57 | - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ 58 | if (data.length != 0){ 59 | if (self.combieData == nil) { 60 | self.combieData = [[NSMutableData alloc]init]; 61 | } 62 | [self.combieData appendData:data]; 63 | NSMutableArray *contents = @[].mutableCopy; 64 | while (self.combieData.length > 12) { 65 | NSUInteger length = 0; 66 | [self.combieData getBytes:&length length:4]; 67 | length += 4; 68 | if (self.combieData.length >= length) { 69 | NSData *contentData = [self.combieData subdataWithRange:NSMakeRange(12,length-12)]; 70 | NSString *content = [[NSString alloc] initWithData:contentData encoding:NSUTF8StringEncoding]; 71 | if (content) { 72 | [contents addObject:content]; 73 | } 74 | self.combieData = [self.combieData subdataWithRange:NSMakeRange(length, self.combieData.length-length)].mutableCopy; 75 | } else { 76 | break; 77 | } 78 | } 79 | [SocketData readAuthMsg:contents]; 80 | } 81 | [self.socket readDataWithTimeout:kReadTimeOut buffer:nil bufferOffset:0 maxLength:kMaxBuffer tag:0]; 82 | } 83 | 84 | 85 | //断开链接 86 | - (void)onSocketDidDisconnect:(AsyncSocket *)sock{ 87 | NSLog(@"---认证服务器断开---"); 88 | self.combieData = nil; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /Douyu/Model/socket/DanmuModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // DanmuModel.h 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/3/12. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @class TYTextContainer; 12 | 13 | @interface DanmuModel : NSObject 14 | 15 | typedef NS_ENUM(NSInteger,CellType){ 16 | CellBanType, 17 | CellNewChatMessageType, 18 | CellNewGiftType, 19 | CellNewUserEnterType, 20 | CellDeserveType, 21 | }; 22 | @property (nonatomic,strong)TYTextContainer *textContainer; 23 | @property (nonatomic,copy)NSString *unColoredMsg; 24 | @property (nonatomic,copy)NSColor *color; 25 | @property (nonatomic,copy)NSString *nickname; 26 | @property (nonatomic,assign) int nc; 27 | @property (nonatomic,assign)CellType cellType; 28 | @property (nonatomic,strong)NSArray *gift; 29 | @property (nonatomic,copy)NSString *dataString; 30 | - (void)setModelFromStirng:(NSString *)string; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Douyu/Model/socket/DanmuModel.m: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // DanmuModel.m 4 | // DouyuTVDammu 5 | // 6 | // Created by LuChen on 16/3/12. 7 | // Copyright © 2016年 Bad Chen. All rights reserved. 8 | // 9 | 10 | #import "DanmuModel.h" 11 | #import "NSString+InfoGet.h" 12 | #import "RegexKitLite.h" 13 | 14 | @implementation DanmuModel 15 | 16 | #define RGB(r,g,b,a) [NSColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a] 17 | #define ColorFromRGBHex(rgbValue) \ 18 | [NSColor colorWithRed:((float) ((rgbValue & 0xFF0000) >> 16)) / 255.0 \ 19 | green:((float) ((rgbValue & 0xFF00) >> 8)) / 255.0 \ 20 | blue:((float) (rgbValue & 0xFF)) / 255.0 \ 21 | alpha:1.0] 22 | 23 | - (void)setModelFromStirng:(NSString *)string{ 24 | 25 | _dataString = string; 26 | NSString *msg; 27 | 28 | switch (self.cellType) { 29 | case CellNewChatMessageType: 30 | { 31 | 32 | 33 | NSString *nickPattern = @"(?<=/nn@=).*?(?=/)"; 34 | NSString *contentPattern = @"(?<=/txt@=).*?(?=/)"; 35 | NSString *colorPattern = @"(?<=/col@=)\\d?(?=/)"; 36 | NSString *ncPattern = @"(?<=/nc@=)\\d?(?=/)"; 37 | NSString *name = [string componentsFirstMatchedByRegex:nickPattern]; 38 | NSString *unReplaceTXT = [string componentsFirstMatchedByRegex:contentPattern]; 39 | NSString *replaceTXT = [unReplaceTXT stringByReplacingOccurrencesOfRegex:@"@A" withString:@"@"]; 40 | NSString *txt = [replaceTXT stringByReplacingOccurrencesOfRegex:@"@S" withString:@"/"]; 41 | _nc = [[string componentsFirstMatchedByRegex:ncPattern] intValue]; 42 | msg = txt; 43 | _nickname = name; 44 | int intColor = [[string componentsFirstMatchedByRegex:colorPattern] intValue]; 45 | switch (intColor) { 46 | case 1: 47 | _color = ColorFromRGBHex(0xFF2D2D); //红 48 | break; 49 | case 2: 50 | _color = ColorFromRGBHex(0x00ccff); //蓝 51 | break; 52 | case 3: 53 | _color = ColorFromRGBHex(0x9AFF02);//绿 54 | break; 55 | case 4: 56 | _color = [NSColor yellowColor];//黄 57 | break; 58 | case 5: 59 | _color = ColorFromRGBHex(0xBF3EFF); //紫 60 | break; 61 | case 6: 62 | _color = ColorFromRGBHex(0xFF60AF); //粉 63 | break; 64 | default: 65 | _color = [NSColor whiteColor]; 66 | break; 67 | } 68 | } 69 | break; 70 | case CellNewGiftType: 71 | { 72 | 73 | NSString *nickPattern = @"(?<=nn@=).*?(?=/)"; 74 | NSString *giftPattern = @"(?<=gfid@=).*?(?=/)"; 75 | NSString *hitPattern = @"(?<=hits@=).*?(?=/)"; 76 | 77 | NSString *name = [string componentsFirstMatchedByRegex:nickPattern]; 78 | NSString *gift = [string componentsFirstMatchedByRegex:giftPattern]; 79 | NSString *hits = [string componentsFirstMatchedByRegex:hitPattern]; 80 | if (hits == NULL) { 81 | hits = @"1"; 82 | } 83 | NSString *giftName; 84 | NSURL *giftIconURL; 85 | for (NSDictionary *dic in self.gift) { 86 | NSString *giftID = dic[@"id"]; 87 | if ([gift isEqualToString:giftID]) { 88 | giftName = dic[@"name"]; 89 | giftIconURL = [NSURL URLWithString:dic[@"mobile_icon_v2"]]; 90 | break; 91 | } 92 | } 93 | NSString *text = [NSString stringWithFormat:@"%@ 赠送给主播%@",name,giftName]; 94 | 95 | msg = [NSString stringWithFormat:@"%@%@连击",text,hits]; 96 | 97 | } 98 | break; 99 | case CellNewUserEnterType: 100 | { 101 | NSString *nickPattern = @"(?<=nn@=).*?(?=/)"; 102 | NSString *name = [string componentsFirstMatchedByRegex:nickPattern]; 103 | msg = [NSString stringWithFormat:@"%@ 进入了直播间",name]; 104 | } 105 | break; 106 | case CellBanType: 107 | { 108 | NSString *nickPattern = @"(?<=snick@=).*?(?=/)"; 109 | NSString *banedNamePattern = @"(?<=dnick@=).*?(?=/)"; 110 | NSString *name = [string componentsFirstMatchedByRegex:nickPattern]; 111 | NSString *banedName = [string componentsFirstMatchedByRegex:banedNamePattern]; 112 | msg = [NSString stringWithFormat:@"管理员%@封禁了%@",name,banedName]; 113 | 114 | } 115 | break; 116 | case CellDeserveType: 117 | { 118 | NSString *nickPattern = @"(?<=Snick@A=).*?(?=@)"; 119 | NSString *levPattern = @"(?<=lev@=).*?(?=/)"; 120 | NSString *hitPattern = @"(?<=hits@=).*?(?=/)"; 121 | NSString *name = [[string componentsSeparatedByRegex:nickPattern]firstObject]; 122 | NSInteger levle = [[[string componentsSeparatedByRegex:levPattern]firstObject]integerValue]; 123 | NSString *hits = [[string componentsSeparatedByRegex:hitPattern]firstObject]; 124 | NSString *deserve; 125 | 126 | switch (levle) { 127 | case 1: 128 | deserve = @"初级酬勤"; 129 | break; 130 | case 2: 131 | deserve = @"中级酬勤"; 132 | break; 133 | case 3: 134 | deserve = @"高级酬勤"; 135 | break; 136 | default: 137 | break; 138 | } 139 | msg = [NSString stringWithFormat:@"%@ 给主播赠送了%@%@连击",name,deserve,hits]; 140 | } 141 | default: 142 | break; 143 | } 144 | _unColoredMsg = msg; 145 | } 146 | 147 | 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /Douyu/Model/socket/DanmuSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // DanmuSocket.h 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/3/3. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import "DouyuTVSocket.h" 10 | 11 | @interface DanmuSocket : DouyuTVSocket 12 | 13 | @property (nonatomic,assign)BOOL isFirstDate; 14 | 15 | + (id)sharedInstance; 16 | - (void)startKLTimer; 17 | @end 18 | -------------------------------------------------------------------------------- /Douyu/Model/socket/DanmuSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // DanmuSocket.m 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/3/3. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import "DanmuSocket.h" 10 | 11 | static DanmuSocket *instance = nil; 12 | @implementation DanmuSocket 13 | 14 | + (id)sharedInstance{ 15 | @synchronized(self) { 16 | if (instance == nil) { 17 | 18 | instance = [[DanmuSocket alloc]init]; 19 | } 20 | } 21 | return instance; 22 | } 23 | 24 | - (void)setServerConfig{ 25 | NSString *ip = @"danmu.douyutv.com"; 26 | NSArray *sevArray = @[@{@"ip":ip,@"port":@"12061"}, 27 | @{@"ip":ip,@"port":@"12062"}, 28 | @{@"ip":ip,@"port":@"8601"}, 29 | @{@"ip":ip,@"port":@"8602"}, 30 | ]; 31 | self.server = @[].mutableCopy; 32 | for (int i = 0; i < sevArray.count; i++) { 33 | ServerModel *model = [ServerModel new]; 34 | NSDictionary *sevCfg = sevArray[i]; 35 | model.ip = sevCfg[@"ip"]; 36 | model.port = [sevCfg[@"port"] intValue]; 37 | [self.server addObject:model]; 38 | } 39 | 40 | } 41 | 42 | - (void)connectSocketHost{ 43 | self.socket = [[AsyncSocket alloc] initWithDelegate:self]; 44 | 45 | NSError *error = nil; 46 | NSString *ip = @"openbarrage.douyutv.com"; 47 | UInt16 port = 8601; 48 | 49 | [self.socket connectToHost:ip onPort:port withTimeout:30 error:&error]; 50 | } 51 | 52 | //心跳包 53 | - (void)longConnectToSocket{ 54 | //keep live 所发送的信息 55 | /* 56 | 心跳包内容 57 | type@=mrkl/ 58 | */ 59 | NSString *keepLive = @"type@=mrkl/"; 60 | NSData *postKLData = [self packToData:keepLive]; 61 | [self.socket writeData:postKLData withTimeout:30 tag:1]; 62 | 63 | } 64 | 65 | 66 | #pragma marl --回调方法 67 | //连接成功 68 | - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ 69 | NSLog(@"---弹幕服务器连接成功---"); 70 | 71 | /* 72 | 游客登陆信息 73 | type@=loginreq/username@=visitor13227520/password@=1234567890123456/roomid@=213116/ 74 | username: 75 | roomid: 76 | 加入弹幕组 77 | type@=joingroup/rid@=213116/gid@=1/ 78 | */ 79 | NSString *loginreq = [NSString stringWithFormat:@"type@=loginreq/roomid@=%@/", self.room]; 80 | self.isFirstDate = YES; 81 | [self.socket writeData:[self packToData:loginreq] withTimeout:30 tag:1]; 82 | 83 | } 84 | //断开链接 85 | - (void)onSocketDidDisconnect:(AsyncSocket *)sock{ 86 | NSLog(@"----弹幕服务器断开----"); 87 | self.combieData = nil; 88 | } 89 | 90 | 91 | //接受数据 从父类继承 92 | //- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ 93 | //} 94 | 95 | - (void)startKLTimer{ 96 | self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:20 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES]; 97 | [self.connectTimer fire]; 98 | } 99 | 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /Douyu/Model/socket/DouyuTVSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // DouyuTVSocket.h 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/2/26. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AsyncSocket.h" 11 | #import "ServerModel.h" 12 | #import "NSString+InfoGet.h" 13 | #import "SocketData.h" 14 | 15 | /* 16 | 向斗鱼发送的消息 17 | 1.通信协议长度,后四个部分的长度,四个字节 18 | 2.第二部分与第一部分一样 19 | 3.请求代码,发送给斗鱼的话,内容为0xb1,0x02, 斗鱼返回的代码为0xb2,0x02 20 | 4.发送内容 21 | 5.末尾字节 22 | */ 23 | 24 | 25 | struct postPack { 26 | unsigned int length; 27 | unsigned int lengthTwice; 28 | unsigned int postCode; 29 | }; 30 | typedef struct postPack PostPack; 31 | 32 | static const int kReadTimeOut = -1; 33 | static const unsigned int kMaxBuffer = 1024; 34 | static const unsigned int kPostCode = 0x2b1; 35 | static const unsigned int kEnd = 0; 36 | 37 | 38 | 39 | @interface DouyuTVSocket : NSObject 40 | 41 | @property (nonatomic,strong)AsyncSocket *socket;//Socket对象 42 | @property (nonatomic,strong) NSMutableArray *server;//服务器数组 43 | @property (nonatomic,copy)NSString *room;//房间ID 44 | @property (nonatomic,copy)NSString *vistorID;//游客ID 45 | @property (nonatomic,copy)NSString *groupID;//弹幕组ID 46 | @property (nonatomic,strong)NSTimer *connectTimer;//心跳keep live 47 | @property (nonatomic,strong)NSMutableData *combieData; 48 | 49 | - (void)setServerConfig; 50 | - (void)connectSocketHost; 51 | - (void)cutOffSocket; 52 | - (NSData *)packToData:(NSString *)string; 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Douyu/Model/socket/DouyuTVSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // DouyuTVSocket.m 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/2/26. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import "DouyuTVSocket.h" 10 | 11 | 12 | 13 | 14 | @implementation DouyuTVSocket 15 | 16 | - (void)setServerConfig{ 17 | 18 | } 19 | 20 | //连接服务器 21 | - (void)connectSocketHost{ 22 | self.socket = [[AsyncSocket alloc] initWithDelegate:self]; 23 | [self setServerConfig]; 24 | NSError *error = nil; 25 | ServerModel *sevCfg = self.server[0]; 26 | 27 | [self.socket connectToHost:sevCfg.ip onPort:sevCfg.port withTimeout:30 error:&error]; 28 | 29 | } 30 | //用户切断链接 31 | - (void)cutOffSocket{ 32 | NSString *logout = @"type@=logout/"; 33 | NSData *logoutData = [self packToData:logout]; 34 | [self.socket writeData:logoutData withTimeout:30 tag:1]; 35 | [self.connectTimer invalidate]; 36 | [self.socket disconnectAfterWriting]; 37 | } 38 | //心跳包 39 | - (void)longConnectToSocket{ 40 | 41 | //keep live 所发送的信息 42 | } 43 | 44 | #pragma marl --回调方法 45 | //连接成功 46 | - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ 47 | 48 | 49 | } 50 | //断开链接 51 | - (void)onSocketDidDisconnect:(AsyncSocket *)sock{ 52 | 53 | } 54 | 55 | //发送消息成功之后回调 56 | - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag 57 | { 58 | //读取消息 59 | [self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:kMaxBuffer tag:0]; 60 | } 61 | //接受数据 62 | - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ 63 | if (data.length != 0){ 64 | if (self.combieData == nil) { 65 | self.combieData = [[NSMutableData alloc]init]; 66 | } 67 | [self.combieData appendData:data]; 68 | NSMutableArray *contents = @[].mutableCopy; 69 | while (self.combieData.length > 12) { 70 | NSUInteger length = 0; 71 | [self.combieData getBytes:&length length:4]; 72 | length += 4; 73 | if (self.combieData.length >= length) { 74 | NSData *contentData = [self.combieData subdataWithRange:NSMakeRange(12,length-12)]; 75 | NSString *content = [[NSString alloc] initWithData:contentData encoding:NSUTF8StringEncoding]; 76 | if (content) { 77 | [contents addObject:content]; 78 | } 79 | self.combieData = [self.combieData subdataWithRange:NSMakeRange(length, self.combieData.length-length)].mutableCopy; 80 | } else { 81 | break; 82 | } 83 | } 84 | [SocketData readDanmuMsg:contents]; 85 | } 86 | [self.socket readDataWithTimeout:kReadTimeOut buffer:nil bufferOffset:0 maxLength:kMaxBuffer tag:0]; 87 | } 88 | 89 | - (NSData *)packToData:(NSString *)string{ 90 | NSMutableData *stringData = [string stringToHexData]; 91 | unsigned int hexLength = (int)string.length+9; 92 | PostPack pack = {hexLength,hexLength,kPostCode}; 93 | 94 | NSMutableData *postDate = [NSData dataWithBytes:&pack length:sizeof(pack)].mutableCopy; 95 | [postDate appendData:stringData]; 96 | [postDate appendBytes:&kEnd length:1]; 97 | return postDate; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /Douyu/Model/socket/RegexKitLite.h: -------------------------------------------------------------------------------- 1 | // 2 | // RegexKitLite.h 3 | // http://regexkit.sourceforge.net/ 4 | // Licensed under the terms of the BSD License, as specified below. 5 | // 6 | 7 | /* 8 | Copyright (c) 2008-2010, John Engelhart 9 | 10 | All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | * Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | 18 | * Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | * Neither the name of the Zang Industries nor the names of its 23 | contributors may be used to endorse or promote products derived from 24 | this software without specific prior written permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 32 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 34 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 35 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #ifdef __OBJC__ 40 | #import 41 | #import 42 | #import 43 | #import 44 | #import 45 | #endif // __OBJC__ 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #ifdef __cplusplus 54 | extern "C" { 55 | #endif 56 | 57 | #ifndef REGEXKITLITE_VERSION_DEFINED 58 | #define REGEXKITLITE_VERSION_DEFINED 59 | 60 | #define _RKL__STRINGIFY(b) #b 61 | #define _RKL_STRINGIFY(a) _RKL__STRINGIFY(a) 62 | #define _RKL_JOIN_VERSION(a,b) _RKL_STRINGIFY(a##.##b) 63 | #define _RKL_VERSION_STRING(a,b) _RKL_JOIN_VERSION(a,b) 64 | 65 | #define REGEXKITLITE_VERSION_MAJOR 4 66 | #define REGEXKITLITE_VERSION_MINOR 0 67 | 68 | #define REGEXKITLITE_VERSION_CSTRING _RKL_VERSION_STRING(REGEXKITLITE_VERSION_MAJOR, REGEXKITLITE_VERSION_MINOR) 69 | #define REGEXKITLITE_VERSION_NSSTRING @REGEXKITLITE_VERSION_CSTRING 70 | 71 | #endif // REGEXKITLITE_VERSION_DEFINED 72 | 73 | #if !defined(RKL_BLOCKS) && defined(NS_BLOCKS_AVAILABLE) && (NS_BLOCKS_AVAILABLE == 1) 74 | #define RKL_BLOCKS 1 75 | #endif 76 | 77 | #if defined(RKL_BLOCKS) && (RKL_BLOCKS == 1) 78 | #define _RKL_BLOCKS_ENABLED 1 79 | #endif // defined(RKL_BLOCKS) && (RKL_BLOCKS == 1) 80 | 81 | #if defined(_RKL_BLOCKS_ENABLED) && !defined(__BLOCKS__) 82 | #warning RegexKitLite support for Blocks is enabled, but __BLOCKS__ is not defined. This compiler may not support Blocks, in which case the behavior is undefined. This will probably cause numerous compiler errors. 83 | #endif // defined(_RKL_BLOCKS_ENABLED) && !defined(__BLOCKS__) 84 | 85 | // For Mac OS X < 10.5. 86 | #ifndef NSINTEGER_DEFINED 87 | #define NSINTEGER_DEFINED 88 | #if defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) 89 | typedef long NSInteger; 90 | typedef unsigned long NSUInteger; 91 | #define NSIntegerMin LONG_MIN 92 | #define NSIntegerMax LONG_MAX 93 | #define NSUIntegerMax ULONG_MAX 94 | #else // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) 95 | typedef int NSInteger; 96 | typedef unsigned int NSUInteger; 97 | #define NSIntegerMin INT_MIN 98 | #define NSIntegerMax INT_MAX 99 | #define NSUIntegerMax UINT_MAX 100 | #endif // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) 101 | #endif // NSINTEGER_DEFINED 102 | 103 | #ifndef RKLREGEXOPTIONS_DEFINED 104 | #define RKLREGEXOPTIONS_DEFINED 105 | 106 | // These must be identical to their ICU regex counterparts. See http://www.icu-project.org/userguide/regexp.html 107 | enum { 108 | RKLNoOptions = 0, 109 | RKLCaseless = 2, 110 | RKLComments = 4, 111 | RKLDotAll = 32, 112 | RKLMultiline = 8, 113 | RKLUnicodeWordBoundaries = 256 114 | }; 115 | typedef uint32_t RKLRegexOptions; // This must be identical to the ICU 'flags' argument type. 116 | 117 | #endif // RKLREGEXOPTIONS_DEFINED 118 | 119 | #ifndef RKLREGEXENUMERATIONOPTIONS_DEFINED 120 | #define RKLREGEXENUMERATIONOPTIONS_DEFINED 121 | 122 | enum { 123 | RKLRegexEnumerationNoOptions = 0UL, 124 | RKLRegexEnumerationCapturedStringsNotRequired = 1UL << 9, 125 | RKLRegexEnumerationReleaseStringReturnedByReplacementBlock = 1UL << 10, 126 | RKLRegexEnumerationFastCapturedStringsXXX = 1UL << 11, 127 | }; 128 | typedef NSUInteger RKLRegexEnumerationOptions; 129 | 130 | #endif // RKLREGEXENUMERATIONOPTIONS_DEFINED 131 | 132 | #ifndef _REGEXKITLITE_H_ 133 | #define _REGEXKITLITE_H_ 134 | 135 | #if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__APPLE_CC__) && (__APPLE_CC__ >= 5465) 136 | #define RKL_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) 137 | #else 138 | #define RKL_DEPRECATED_ATTRIBUTE 139 | #endif 140 | 141 | #if defined(NS_REQUIRES_NIL_TERMINATION) 142 | #define RKL_REQUIRES_NIL_TERMINATION NS_REQUIRES_NIL_TERMINATION 143 | #else // defined(NS_REQUIRES_NIL_TERMINATION) 144 | #define RKL_REQUIRES_NIL_TERMINATION 145 | #endif // defined(NS_REQUIRES_NIL_TERMINATION) 146 | 147 | // This requires a few levels of rewriting to get the desired results. 148 | #define _RKL_CONCAT_2(c,d) c ## d 149 | #define _RKL_CONCAT(a,b) _RKL_CONCAT_2(a,b) 150 | 151 | #ifdef RKL_PREPEND_TO_METHODS 152 | #define RKL_METHOD_PREPEND(x) _RKL_CONCAT(RKL_PREPEND_TO_METHODS, x) 153 | #else // RKL_PREPEND_TO_METHODS 154 | #define RKL_METHOD_PREPEND(x) x 155 | #endif // RKL_PREPEND_TO_METHODS 156 | 157 | // If it looks like low memory notifications might be available, add code to register and respond to them. 158 | // This is (should be) harmless if it turns out that this isn't the case, since the notification that we register for, 159 | // UIApplicationDidReceiveMemoryWarningNotification, is dynamically looked up via dlsym(). 160 | #if ((defined(TARGET_OS_EMBEDDED) && (TARGET_OS_EMBEDDED != 0)) || (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0))) && (!defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) || (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS != 0)) 161 | #define RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS 1 162 | #endif 163 | 164 | #ifdef __OBJC__ 165 | 166 | // NSException exception name. 167 | extern NSString * const RKLICURegexException; 168 | 169 | // NSError error domains and user info keys. 170 | extern NSString * const RKLICURegexErrorDomain; 171 | 172 | extern NSString * const RKLICURegexEnumerationOptionsErrorKey; 173 | extern NSString * const RKLICURegexErrorCodeErrorKey; 174 | extern NSString * const RKLICURegexErrorNameErrorKey; 175 | extern NSString * const RKLICURegexLineErrorKey; 176 | extern NSString * const RKLICURegexOffsetErrorKey; 177 | extern NSString * const RKLICURegexPreContextErrorKey; 178 | extern NSString * const RKLICURegexPostContextErrorKey; 179 | extern NSString * const RKLICURegexRegexErrorKey; 180 | extern NSString * const RKLICURegexRegexOptionsErrorKey; 181 | extern NSString * const RKLICURegexReplacedCountErrorKey; 182 | extern NSString * const RKLICURegexReplacedStringErrorKey; 183 | extern NSString * const RKLICURegexReplacementStringErrorKey; 184 | extern NSString * const RKLICURegexSubjectRangeErrorKey; 185 | extern NSString * const RKLICURegexSubjectStringErrorKey; 186 | 187 | @interface NSString (RegexKitLiteAdditions) 188 | 189 | + (void)RKL_METHOD_PREPEND(clearStringCache); 190 | 191 | // Although these are marked as deprecated, a bug in GCC prevents a warning from being issues for + class methods. Filed bug with Apple, #6736857. 192 | + (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex RKL_DEPRECATED_ATTRIBUTE; 193 | + (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex options:(RKLRegexOptions)options error:(NSError **)error RKL_DEPRECATED_ATTRIBUTE; 194 | 195 | - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex; 196 | - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex range:(NSRange)range; 197 | - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; 198 | 199 | - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex; 200 | - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex inRange:(NSRange)range; 201 | - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error; 202 | 203 | - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex; 204 | - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex capture:(NSInteger)capture; 205 | - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex inRange:(NSRange)range; 206 | - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; 207 | 208 | - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex; 209 | - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex capture:(NSInteger)capture; 210 | - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex inRange:(NSRange)range; 211 | - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; 212 | 213 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement; 214 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange; 215 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error; 216 | 217 | //// >= 3.0 218 | 219 | - (NSInteger)RKL_METHOD_PREPEND(captureCount); 220 | - (NSInteger)RKL_METHOD_PREPEND(captureCountWithOptions):(RKLRegexOptions)options error:(NSError **)error; 221 | 222 | - (BOOL)RKL_METHOD_PREPEND(isRegexValid); 223 | - (BOOL)RKL_METHOD_PREPEND(isRegexValidWithOptions):(RKLRegexOptions)options error:(NSError **)error; 224 | 225 | - (void)RKL_METHOD_PREPEND(flushCachedRegexData); 226 | 227 | - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex; 228 | - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex capture:(NSInteger)capture; 229 | - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex range:(NSRange)range; 230 | - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; 231 | 232 | 233 | - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex; 234 | - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range; 235 | - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; 236 | 237 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex; 238 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range; 239 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; 240 | 241 | //// >= 4.0 242 | 243 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 244 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 245 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 246 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList; 247 | 248 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count; 249 | 250 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 251 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 252 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 253 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList; 254 | 255 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count; 256 | 257 | #ifdef _RKL_BLOCKS_ENABLED 258 | 259 | - (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 260 | - (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 261 | 262 | - (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 263 | - (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 264 | 265 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 266 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 267 | 268 | #endif // _RKL_BLOCKS_ENABLED 269 | 270 | @end 271 | 272 | @interface NSMutableString (RegexKitLiteAdditions) 273 | 274 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement; 275 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange; 276 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error; 277 | 278 | //// >= 4.0 279 | 280 | #ifdef _RKL_BLOCKS_ENABLED 281 | 282 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 283 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 284 | 285 | #endif // _RKL_BLOCKS_ENABLED 286 | 287 | @end 288 | 289 | #endif // __OBJC__ 290 | 291 | #endif // _REGEXKITLITE_H_ 292 | 293 | #ifdef __cplusplus 294 | } // extern "C" 295 | #endif 296 | -------------------------------------------------------------------------------- /Douyu/Model/socket/ServerModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // ServerModel.h 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/2/26. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ServerModel : NSObject 12 | 13 | @property (nonatomic,copy)NSString *ip; 14 | @property (nonatomic,assign)UInt16 port; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Douyu/Model/socket/ServerModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // ServerModel.m 3 | // DouyuTVDammu 4 | // 5 | // Created by LuChen on 16/2/26. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import "ServerModel.h" 10 | 11 | @implementation ServerModel 12 | 13 | 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Douyu/Model/socket/SocketData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocketData.h 3 | // DouyuTVDammuAssistant 4 | // 5 | // Created by LuChen on 16/4/20. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SocketData : NSObject 12 | 13 | + (void)douyuData:(NSData *)data isAuthData:(BOOL)yesOrNo; 14 | + (void)readDanmuMsg:(NSArray *)array; 15 | + (void)readAuthMsg:(NSArray *)array; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Douyu/Model/socket/SocketData.m: -------------------------------------------------------------------------------- 1 | // 2 | // SocketData.m 3 | // DouyuTVDammuAssistant 4 | // 5 | // Created by LuChen on 16/4/20. 6 | // Copyright © 2016年 Bad Chen. All rights reserved. 7 | // 8 | 9 | #import "SocketData.h" 10 | #import "AuthSocket.h" 11 | #import "DanmuSocket.h" 12 | 13 | 14 | @implementation SocketData 15 | 16 | 17 | + (void)douyuData:(NSData *)data isAuthData:(BOOL)yesOrNo{ 18 | NSMutableArray *contents = @[].mutableCopy; 19 | NSData *subData = data.copy; 20 | NSUInteger _loction = 0; 21 | NSUInteger _length = 0; 22 | do { 23 | 24 | //获取数据长度 25 | if (subData.length < 12) { 26 | break; 27 | } 28 | [subData getBytes:&_length range:NSMakeRange(0, 4)]; 29 | _length -= 12; 30 | //截取相对应的数据 31 | // NSLog(@"subdatelength:%lu,range.loaction:%d,range.length:%lu",(unsigned long)subData.length,12,_length); 32 | NSData *contentData = [subData subdataWithRange:NSMakeRange(12, _length)]; 33 | NSString *content = [[NSString alloc]initWithData:contentData encoding:NSUTF8StringEncoding]; 34 | //截取余下的数据 35 | _loction += 12; 36 | // NSLog(@"datelength:%lu,range.loaction:%ld,range.length:%lu",(unsigned long)data.length,_length+_loction,data.length-_length-_loction); 37 | subData = [data subdataWithRange:NSMakeRange(_length+_loction, data.length-_length-_loction)]; 38 | if (content) { 39 | [contents addObject:content]; 40 | } 41 | 42 | _loction += _length; 43 | 44 | } while (_loction < data.length); 45 | if (yesOrNo) { 46 | [self readAuthMsg:contents]; 47 | }else{ 48 | [self readDanmuMsg:contents]; 49 | } 50 | 51 | 52 | } 53 | + (void)readDanmuMsg:(NSArray *)array{ 54 | DanmuSocket *danmuSocket = [DanmuSocket sharedInstance]; 55 | for (NSString * msg in array) { 56 | [[NSNotificationCenter defaultCenter]postNotificationName:@"kReceiveDYMessageNotification" object:msg]; 57 | if (!danmuSocket.connectTimer.isValid) { 58 | if ([msg rangeOfString:@"type@=login"].location != NSNotFound) { 59 | //加入弹幕组 60 | NSString *jionGroup = [NSString stringWithFormat:@"type@=joingroup/rid@=%@/gid@=%@/", danmuSocket.room, @"-9999"];//danmuSocket.groupID 61 | NSData *jGroupData = [danmuSocket packToData:jionGroup]; 62 | [danmuSocket.socket writeData:jGroupData withTimeout:30 tag:1]; 63 | //开始发送心跳包 64 | [danmuSocket startKLTimer]; 65 | } 66 | } 67 | } 68 | } 69 | + (void)readAuthMsg:(NSArray *)array{ 70 | AuthSocket *authSocket = [AuthSocket sharedInstance]; 71 | //遍历数组,提取ID 72 | for (NSString *msg in array) { 73 | 74 | if ([msg rangeOfString:@"loginres"].location != NSNotFound && [msg rangeOfString:@"username@="].location != NSNotFound) { 75 | NSRange range = [msg rangeOfString:@"username@="]; 76 | NSString *unSubString = [msg substringFromIndex:range.location + range.length]; 77 | authSocket.vistorID = [unSubString substringToIndex:[unSubString rangeOfString:@"/"].location]; 78 | } 79 | if ([msg rangeOfString:@"gid@="].location != NSNotFound) { 80 | NSRange range = [msg rangeOfString:@"gid@="]; 81 | NSString *unSubSring = [msg substringFromIndex:range.location + range.length]; 82 | authSocket.groupID = [unSubSring substringToIndex:[unSubSring rangeOfString:@"/"].location]; 83 | } 84 | } 85 | //将2个ID传出去 86 | if (authSocket.vistorID.length != 0 && authSocket.groupID.length != 0) { 87 | NSLog(@"---获得游客ID以及弹幕组---"); 88 | 89 | authSocket.InfoBlock(authSocket.vistorID,authSocket.groupID); 90 | [authSocket cutOffSocket]; 91 | } 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Douyu/MpvClientOGLView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MpvClientOGLView.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/23. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface MpvClientOGLView : NSOpenGLView 14 | @property mpv_render_context *mpvGL; 15 | @property BOOL pause; 16 | - (void)drawRect; 17 | - (void)fillBlack; 18 | @end 19 | -------------------------------------------------------------------------------- /Douyu/MpvClientOGLView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MpvClientOGLView.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/23. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "MpvClientOGLView.h" 10 | 11 | @interface MpvClientOGLView (){ 12 | NSRect rect; 13 | } 14 | 15 | @end 16 | 17 | @implementation MpvClientOGLView 18 | 19 | - (instancetype)initWithCoder:(NSCoder *)decoder { 20 | if (self = [super initWithCoder:decoder]) { 21 | [self setWantsBestResolutionOpenGLSurface:YES]; 22 | rect = [self convertRectToBacking:[self bounds]]; 23 | [self.openGLContext makeCurrentContext]; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)fillBlack 29 | { 30 | glClearColor(0, 0, 0, 0); 31 | glClear(GL_COLOR_BUFFER_BIT); 32 | } 33 | 34 | - (void)drawRect 35 | { 36 | if (self.mpvGL && !self.pause) { 37 | mpv_render_param params[] = { 38 | // Specify the default framebuffer (0) as target. This will 39 | // render onto the entire screen. If you want to show the video 40 | // in a smaller rectangle or apply fancy transformations, you'll 41 | // need to render into a separate FBO and draw it manually. 42 | {MPV_RENDER_PARAM_OPENGL_FBO, &(mpv_opengl_fbo){ 43 | .fbo = 0, 44 | .w = rect.size.width, 45 | .h = rect.size.height, 46 | }}, 47 | // Flip rendering (needed due to flipped GL coordinate system). 48 | {MPV_RENDER_PARAM_FLIP_Y, &(int){1}}, 49 | {0} 50 | }; 51 | mpv_render_context_render(self.mpvGL, params); 52 | } 53 | else{ 54 | rect = [self convertRectToBacking:[self bounds]]; 55 | [self fillBlack]; 56 | } 57 | glFlush(); 58 | } 59 | 60 | - (void)drawRect:(NSRect)dirtyRect 61 | { 62 | [self drawRect]; 63 | } 64 | @end 65 | -------------------------------------------------------------------------------- /Douyu/PlayerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerViewController.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "DYRoomInfo.h" 12 | #import "BarrageRenderer.h" 13 | #import "MpvClientOGLView.h" 14 | 15 | @interface PlayerViewController : NSViewController 16 | 17 | @property mpv_handle *mpv; 18 | @property (weak, nonatomic) IBOutlet MpvClientOGLView *glView; 19 | @property (strong, nonatomic) BarrageRenderer *barrageRenderer; 20 | 21 | - (void)loadPlayerWithInfo:(DYRoomInfo *)info; 22 | - (void)destroyPlayer; 23 | @end 24 | -------------------------------------------------------------------------------- /Douyu/PlayerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerViewController.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "PlayerViewController.h" 10 | #import "BarrageDescriptor.h" 11 | #import "BarrageWalkTextSprite.h" 12 | #import "DYDanmuProvider.h" 13 | 14 | 15 | #define RGB(r,g,b,a) [NSColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a] 16 | #define ColorFromRGBHex(rgbValue,alphaValue) \ 17 | [NSColor colorWithRed:((float) ((rgbValue & 0xFF0000) >> 16)) / 255.0 \ 18 | green:((float) ((rgbValue & 0xFF00) >> 8)) / 255.0 \ 19 | blue:((float) (rgbValue & 0xFF)) / 255.0 \ 20 | alpha:alphaValue] 21 | 22 | static void *get_proc_address(void *ctx, const char *name) 23 | { 24 | CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); 25 | void *addr = CFBundleGetFunctionPointerForName(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")), symbolName); 26 | CFRelease(symbolName); 27 | return addr; 28 | } 29 | 30 | static void glupdate(void *ctx) 31 | { 32 | MpvClientOGLView *glView = (__bridge MpvClientOGLView *)ctx; 33 | // I'm still not sure what the best way to handle this is, but this 34 | // works. 35 | dispatch_async(dispatch_get_main_queue(), ^{ 36 | [glView drawRect]; 37 | }); 38 | } 39 | 40 | @interface PlayerViewController () { 41 | BOOL endFile; 42 | NSTimer *hideCursorTimer; 43 | } 44 | 45 | @property (weak, nonatomic) IBOutlet NSView *loadingView; 46 | @property (strong, nonatomic) DYRoomInfo *roomInfo; 47 | @property (strong, nonatomic) DYDanmuProvider *danmuProvider; 48 | @property (strong, nonatomic) dispatch_queue_t queue; 49 | @end 50 | 51 | @implementation PlayerViewController 52 | 53 | void wakeup(void *context) { 54 | if(context){ 55 | // Damn ARC 56 | const uint8_t *data_ctx = (uint8_t *)context; 57 | if(data_ctx[0] == 0x88 && data_ctx[1] == 0x00 && data_ctx[2] == 0x00){ 58 | NSLog(@"Invalid callback context."); 59 | return; 60 | } 61 | PlayerViewController *a = (__bridge PlayerViewController *) context; 62 | if(a && a.className && 63 | [a respondsToSelector:@selector(readEvents)]){ 64 | [a readEvents]; 65 | } 66 | } 67 | } 68 | 69 | void check_error(int status) { 70 | if (status < 0) { 71 | NSLog(@"mpv API error: %s", mpv_error_string(status)); 72 | dispatch_async(dispatch_get_main_queue(), ^(void){ 73 | NSAlert *alert = [[NSAlert alloc] init]; 74 | [alert setMessageText:[NSString stringWithFormat:@"mpv API error: %s", mpv_error_string(status)]]; 75 | [alert runModal]; 76 | }); 77 | } 78 | } 79 | 80 | - (void)loadPlayerWithInfo:(DYRoomInfo *)info { 81 | [self.loadingView setWantsLayer:YES]; 82 | [self.loadingView.layer setBackgroundColor:ColorFromRGBHex(0xecebeb, 1).CGColor]; 83 | self.roomInfo = info; 84 | [self setTitle:[NSString stringWithFormat:@"【%@(%@)】%@",self.roomInfo.nickName,self.roomInfo.roomId,self.roomInfo.roomName]]; 85 | [self.view.window setTitle:self.title]; 86 | dispatch_async(dispatch_get_main_queue(), ^{ 87 | [self playVideo:self.roomInfo.videoUrl]; 88 | [self loadDanmu]; 89 | }); 90 | } 91 | 92 | - (void)viewDidLoad { 93 | [super viewDidLoad]; 94 | // Do view setup here. 95 | if(hideCursorTimer){ 96 | [hideCursorTimer invalidate]; 97 | hideCursorTimer = nil; 98 | } 99 | hideCursorTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(hideCursor) userInfo:nil repeats:YES]; 100 | } 101 | 102 | - (void)hideCursor{ 103 | NSInteger windowId = [NSWindow windowNumberAtPoint:[NSEvent mouseLocation] belowWindowWithWindowNumber:0]; 104 | if(windowId == self.view.window.windowNumber){ 105 | if (CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGEventMouseMoved) >= 5) { 106 | [NSCursor setHiddenUntilMouseMoves:YES]; 107 | } 108 | } 109 | } 110 | 111 | - (void)loadDanmu { 112 | self.danmuProvider = [[DYDanmuProvider alloc] init]; 113 | self.danmuProvider.delegate = self; 114 | [self.danmuProvider loadWithInfo:self.roomInfo]; 115 | [self.barrageRenderer start]; 116 | [self.view addSubview:self.barrageRenderer.view positioned:NSWindowAbove relativeTo:nil]; 117 | } 118 | 119 | - (void)onNewMessage:(NSString *)cmContent :(NSString *)userName :(int)ftype :(int)fsize :(NSColor *)color{ 120 | if (ftype == 0) { 121 | [self addSpritToVideo:ftype content:cmContent size:fsize color:color]; 122 | } else { 123 | [self addSpritToVideo:ftype content:[NSString stringWithFormat:@" %@:%@ ",userName,cmContent] size:fsize+2 color:color]; 124 | } 125 | } 126 | 127 | - (void)addSpritToVideo:(int)type content:(NSString*)content size:(int)size color:(NSColor *)color 128 | { 129 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 130 | descriptor.spriteName = NSStringFromClass([BarrageWalkTextSprite class]); 131 | descriptor.params[@"text"] = content; 132 | descriptor.params[@"textColor"] = color; 133 | descriptor.params[@"fontSize"] = @(size); 134 | descriptor.params[@"fontFamily"] = @"Helvetica Bold"; 135 | descriptor.params[@"speed"] = @(120+arc4random()%61); 136 | if (type != 0) { 137 | descriptor.params[@"backgroundColor"] = ColorFromRGBHex(0x2894FF,0.5); 138 | descriptor.params[@"cornerRadius"] = @(16); 139 | } 140 | 141 | // type is not supported right 142 | descriptor.params[@"direction"] = @(BarrageWalkDirectionR2L); 143 | dispatch_async(dispatch_get_main_queue(), ^(void){ 144 | [self.barrageRenderer receive:descriptor]; 145 | }); 146 | } 147 | 148 | - (void)setMPVOption:(const char *)name :(const char*)data{ 149 | int status = mpv_set_option_string(self.mpv, name, data); 150 | check_error(status); 151 | } 152 | 153 | - (void)playVideo:(NSString *)URL{ 154 | 155 | // Start Playing Video 156 | self.mpv = mpv_create(); 157 | 158 | [self setMPVOption:"input-default-bindings" :"yes"]; 159 | 160 | [self setMPVOption:"cache-default" :"75000"]; 161 | 162 | if(self.title){ 163 | [self setMPVOption:"force-media-title" :[self.title UTF8String]]; 164 | } 165 | [self setMPVOption:"hwdec" : "auto"]; 166 | [self setMPVOption:"opengl-hwdec-interop" :"auto"]; 167 | // [self setMPVOption: "vf" : "lavfi=\"fps=60:round=down\""]; 168 | [self setMPVOption:"vo" :"libmpv"]; 169 | [self loadMPVSettings]; 170 | // request important errors 171 | check_error(mpv_request_log_messages(self.mpv, "warn")); 172 | 173 | check_error(mpv_initialize(self.mpv)); 174 | mpv_render_param mpv_params[] = { 175 | {MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_OPENGL}, 176 | {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &(mpv_opengl_init_params){ 177 | .get_proc_address = get_proc_address, 178 | }}, 179 | {0} 180 | }; 181 | 182 | mpv_render_context *mpvGLContext; 183 | check_error(mpv_render_context_create(&mpvGLContext, self.mpv, mpv_params)); 184 | self.glView.mpvGL = mpvGLContext; 185 | mpv_render_context_set_update_callback(mpvGLContext, glupdate, (__bridge void *)self.glView); 186 | 187 | dispatch_async(self.queue, ^{ 188 | // Register to be woken up whenever mpv generates new events. 189 | mpv_set_wakeup_callback(self.mpv, wakeup, (__bridge void *) self); 190 | 191 | // Load the indicated file 192 | const char *cmd[] = {"loadfile", [URL cStringUsingEncoding:NSUTF8StringEncoding], NULL}; 193 | check_error(mpv_command(self.mpv, cmd)); 194 | }); 195 | } 196 | 197 | - (void) loadMPVSettings{ 198 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 199 | NSString *applicationSupportDirectory = [paths firstObject]; 200 | NSString *confDir = [NSString stringWithFormat:@"%@/Douyu/conf/",applicationSupportDirectory]; 201 | 202 | BOOL isDir = NO; 203 | BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:confDir isDirectory:&isDir]; 204 | if(!isExist){ 205 | [[NSFileManager defaultManager] createDirectoryAtPath:confDir withIntermediateDirectories:YES attributes:nil error:nil]; 206 | } 207 | mpv_set_option_string(self.mpv, "config-dir",[confDir UTF8String]); 208 | [self setMPVOption:"config" :"yes"]; 209 | } 210 | 211 | - (void) readEvents 212 | { 213 | dispatch_async(self.queue, ^{ 214 | while (self.mpv) { 215 | mpv_event *event = mpv_wait_event(self.mpv, 0); 216 | if(!event) 217 | break; 218 | if (event->event_id == MPV_EVENT_NONE) 219 | break; 220 | if(self && [self respondsToSelector:@selector(handleEvent:)]){ 221 | [self handleEvent:event]; 222 | }else{ 223 | return; 224 | } 225 | } 226 | }); 227 | } 228 | 229 | - (void) handleEvent:(mpv_event *)event 230 | { 231 | [self onMpvEvent:event]; 232 | switch (event->event_id) { 233 | case MPV_EVENT_SHUTDOWN: { 234 | if ([NSThread isMainThread]) { 235 | [self.view.window performClose:self]; 236 | } else { 237 | dispatch_sync(dispatch_get_main_queue(), ^{ 238 | [self.view.window performClose:self]; 239 | }); 240 | } 241 | NSLog(@"Stopping player"); 242 | break; 243 | } 244 | case MPV_EVENT_LOG_MESSAGE: { 245 | struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data; 246 | NSLog(@"[%s] %s: %s", msg->prefix, msg->level, msg->text); 247 | break; 248 | } 249 | case MPV_EVENT_START_FILE:{ 250 | endFile = NO; 251 | break; 252 | } 253 | case MPV_EVENT_PLAYBACK_RESTART: { 254 | dispatch_async(dispatch_get_main_queue(), ^{ 255 | [self.loadingView setHidden:YES]; 256 | }); 257 | break; 258 | } 259 | case MPV_EVENT_END_FILE:{ 260 | endFile = YES; 261 | break; 262 | } 263 | case MPV_EVENT_IDLE:{ 264 | if(endFile){ 265 | if ([NSThread isMainThread]) { 266 | [self.loadingView setHidden:NO]; 267 | [self.view.window performClose:self]; 268 | } else { 269 | dispatch_sync(dispatch_get_main_queue(), ^{ 270 | [self.loadingView setHidden:NO]; 271 | [self.view.window performClose:self]; 272 | }); 273 | } 274 | } 275 | break; 276 | } 277 | case MPV_EVENT_VIDEO_RECONFIG:{ 278 | mpv_observe_property(self.mpv, 0, "options/keepaspect", MPV_FORMAT_FLAG); 279 | break; 280 | } 281 | default: ; 282 | //NSLog(@"Player Event: %s", mpv_event_name(event->event_id)); 283 | } 284 | } 285 | 286 | - (void) mpv_cleanup 287 | { 288 | mpv_set_wakeup_callback(self.mpv, NULL,NULL); 289 | mpv_render_context_free(self.glView.mpvGL); 290 | mpv_terminate_destroy(self.mpv); 291 | self.glView.mpvGL = nil; 292 | self.mpv = nil; 293 | [self.glView clearGLContext]; 294 | [self.glView removeFromSuperview]; 295 | self.glView = nil; 296 | } 297 | 298 | - (void)destroyPlayer{ 299 | [self.view setWantsLayer:NO]; 300 | [hideCursorTimer invalidate]; 301 | hideCursorTimer = nil; 302 | [self.danmuProvider disconnect]; 303 | [self.barrageRenderer stop]; 304 | [self.barrageRenderer.view removeFromSuperview]; 305 | [self mpv_cleanup]; 306 | } 307 | 308 | - (void)onMpvEvent:(mpv_event *)event{ 309 | if(event->event_id == MPV_EVENT_GET_PROPERTY_REPLY || event->event_id == MPV_EVENT_PROPERTY_CHANGE){ 310 | mpv_event_property *propety = event->data; 311 | void *data = propety->data; 312 | if(!data){ 313 | return; 314 | } 315 | if(strcmp(propety->name, "options/keepaspect") == 0){ 316 | int keep = *(int *)data; 317 | [self onKeepAspect:keep]; 318 | } 319 | } 320 | } 321 | 322 | - (void)onKeepAspect:(int)keep{ 323 | dispatch_async(dispatch_get_main_queue(), ^(void){ 324 | // call SetFrame to force opengl canvas resize 325 | NSView *videoView = self.glView; 326 | NSRect rect = videoView.frame; 327 | rect.size.width += 1; 328 | [videoView setFrame:rect]; 329 | rect.size.width -= 1; 330 | [videoView setFrame:rect]; 331 | }); 332 | } 333 | 334 | #pragma mark - lazy init 335 | 336 | - (BarrageRenderer *)barrageRenderer { 337 | if (!_barrageRenderer) { 338 | BarrageRenderer *renderer = [[BarrageRenderer alloc] init]; 339 | [self.view setWantsLayer:YES]; 340 | [renderer.view setFrame:NSMakeRect(0,0,self.view.frame.size.width,self.view.frame.size.height)]; 341 | [renderer.view setAutoresizingMask:NSViewMaxYMargin|NSViewMinXMargin|NSViewWidthSizable|NSViewMaxXMargin|NSViewHeightSizable|NSViewMinYMargin]; 342 | [renderer.view setWantsLayer:YES]; 343 | _barrageRenderer = renderer; 344 | } 345 | return _barrageRenderer; 346 | } 347 | 348 | - (dispatch_queue_t)queue { 349 | if (!_queue) { 350 | _queue = dispatch_queue_create("douyu.mpv.quene", DISPATCH_QUEUE_SERIAL); 351 | } 352 | return _queue; 353 | } 354 | 355 | 356 | @end 357 | -------------------------------------------------------------------------------- /Douyu/PlayerWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerWindow.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/22. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PlayerWindow : NSWindow 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Douyu/PlayerWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerWindow.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/22. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "PlayerWindow.h" 10 | #import "PlayerViewController.h" 11 | #import 12 | 13 | @interface PlayerWindow () { 14 | BOOL shiftKeyPressed; 15 | } 16 | 17 | @end 18 | 19 | @implementation PlayerWindow 20 | 21 | - (void)flagsChanged:(NSEvent *) event { 22 | shiftKeyPressed = ([event modifierFlags] & NSShiftKeyMask) != 0; 23 | } 24 | 25 | - (void)keyDown:(NSEvent*)event { 26 | PlayerViewController *vc = (PlayerViewController *)self.contentViewController; 27 | if(!vc.mpv){ 28 | NSLog(@"MPV not exists"); 29 | return; 30 | } 31 | 32 | switch( [event keyCode] ) { 33 | case 53:{ // Esc key 34 | [self toggleFullScreen:self]; 35 | break; 36 | } 37 | case 3:{ 38 | dispatch_async(dispatch_get_main_queue(), ^{ 39 | NSUInteger flags = [[NSApp currentEvent] modifierFlags]; 40 | if ((flags & NSCommandKeyMask)) { 41 | [self toggleFullScreen:self]; // Command+F key to toggle fullscreen 42 | } 43 | }); 44 | break; 45 | } 46 | case 9:{ 47 | dispatch_async(dispatch_get_main_queue(), ^{ 48 | if (vc.barrageRenderer.launched) { 49 | [vc.barrageRenderer stop]; 50 | } else { 51 | [vc.barrageRenderer start]; 52 | } 53 | const char *args[] = {"show-text", vc.barrageRenderer.launched?"已开启弹幕":"已关闭弹幕" ,NULL}; 54 | mpv_command_async(vc.mpv,0, args); 55 | }); 56 | break; 57 | } 58 | case 17:{ 59 | dispatch_async(dispatch_get_main_queue(), ^{ 60 | if (self.level == NSFloatingWindowLevel) { 61 | [self setLevel:NSNormalWindowLevel]; 62 | } else { 63 | [self setLevel:NSFloatingWindowLevel]; 64 | } 65 | }); 66 | break; 67 | } 68 | default:{ 69 | [self handleKeyboardEvnet:event keyDown:YES]; 70 | break; 71 | } 72 | } 73 | } 74 | 75 | -(void)keyUp:(NSEvent*)event { 76 | [self flagsChanged:event]; 77 | [self handleKeyboardEvnet:event keyDown:NO]; 78 | } 79 | 80 | - (void)handleKeyboardEvnet:(NSEvent *)event keyDown:(BOOL)keyDown { 81 | PlayerViewController *vc = (PlayerViewController *)self.contentViewController; 82 | if(!vc.mpv){ 83 | return; 84 | } 85 | const char *keyState = keyDown?"keydown":"keyup"; 86 | NSString *str = [self stringByKeyEvent:event]; 87 | const char *args[] = {keyState, [str UTF8String], NULL}; 88 | mpv_command_async(vc.mpv, 0, args); 89 | } 90 | 91 | CFStringRef stringByKeyCode(CGKeyCode keyCode) 92 | { 93 | TISInputSourceRef currentKeyboard = TISCopyInputSourceForLanguage(CFSTR("en-US")); 94 | CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); 95 | if(!layoutData){ 96 | return NULL; 97 | } 98 | const UCKeyboardLayout *keyboardLayout = 99 | (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); 100 | 101 | UInt32 keysDown = 0; 102 | UniChar chars[4]; 103 | UniCharCount realLength; 104 | 105 | UCKeyTranslate(keyboardLayout, 106 | keyCode, 107 | kUCKeyActionDisplay, 108 | 0, 109 | LMGetKbdType(), 110 | kUCKeyTranslateNoDeadKeysBit, 111 | &keysDown, 112 | sizeof(chars) / sizeof(chars[0]), 113 | &realLength, 114 | chars); 115 | CFRelease(currentKeyboard); 116 | 117 | return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1); 118 | } 119 | 120 | 121 | - (NSString *)stringByKeyEvent:(NSEvent*)event 122 | { 123 | NSString *str = @""; 124 | int cocoaModifiers = [event modifierFlags]; 125 | if (cocoaModifiers & NSControlKeyMask) 126 | str = [str stringByAppendingString:@"Ctrl+"]; 127 | if (cocoaModifiers & NSCommandKeyMask) 128 | str = [str stringByAppendingString:@"Meta+"]; 129 | if (cocoaModifiers & NSAlternateKeyMask) 130 | str = [str stringByAppendingString:@"Alt+"]; 131 | if (cocoaModifiers & NSShiftKeyMask) 132 | str = [str stringByAppendingString:@"Shift+"]; 133 | 134 | NSString *keystr; 135 | 136 | CFStringRef keystr_ref = stringByKeyCode([event keyCode]); 137 | if(keystr_ref){ 138 | keystr = (__bridge NSString *)keystr_ref; 139 | }else{ 140 | // If can't get key data from UCKeyTranslate, just convert ascii code , this will get many key works 141 | int value = [event keyCode]; 142 | keystr = [NSString stringWithFormat:@"%c",(char)value]; 143 | } 144 | 145 | if(keystr){ 146 | str = [str stringByAppendingString:keystr]; 147 | } 148 | 149 | NSLog(@"[PlayerWindow] Key event: %@",str); 150 | return str; 151 | } 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /Douyu/Resources/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/Douyu/Resources/img/loading.gif -------------------------------------------------------------------------------- /Douyu/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : NSViewController 12 | 13 | @property (weak, nonatomic) IBOutlet NSButton *playButton; 14 | @property (weak, nonatomic) IBOutlet NSPopUpButton *videoQualityButton; 15 | @property (weak, nonatomic) IBOutlet NSComboBox *roomComboBox; 16 | 17 | - (void)reset; 18 | 19 | @end 20 | 21 | -------------------------------------------------------------------------------- /Douyu/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "DYRoomInfo.h" 11 | #import "PlayerViewController.h" 12 | #import "DYRoomHistoryModel.h" 13 | 14 | @interface ViewController () { 15 | NSTimer *resizingTimer; 16 | } 17 | 18 | @property (strong, nonatomic) NSWindowController *playerWindowController; 19 | @property (weak, nonatomic) PlayerViewController *playerViewController; 20 | @property (strong, nonatomic) id playingActivity; 21 | @property (strong, nonatomic) NSArray *roomHistory; 22 | 23 | @end 24 | 25 | @implementation ViewController 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | // Do any additional setup after loading the view. 31 | NSInteger videoQuality = [[NSUserDefaults standardUserDefaults] integerForKey:@"videoQuality"]; 32 | [self.videoQualityButton selectItemAtIndex:videoQuality]; 33 | self.roomComboBox.dataSource = self; 34 | self.roomComboBox.delegate = self; 35 | [self reloadHistory]; 36 | if (self.roomHistory.count) { 37 | [self.roomComboBox selectItemAtIndex:0]; 38 | } 39 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(openUrlNorification:) name:@"openUrl" object:nil]; 40 | } 41 | 42 | - (void)openUrlNorification:(NSNotification *)notification { 43 | NSArray *urls = notification.object; 44 | NSURL *url = urls.firstObject; 45 | if ([url.host isEqualToString:@"room"]) { 46 | if (url.lastPathComponent.length) { 47 | [self.playerWindowController.window performClose:nil]; 48 | [self playWithRoomString:url.lastPathComponent]; 49 | } 50 | } 51 | } 52 | 53 | - (void)viewWillAppear { 54 | [self reloadHistory]; 55 | } 56 | 57 | - (NSInteger)numberOfItemsInComboBox:(NSComboBox *)comboBox { 58 | return self.roomHistory.count; 59 | } 60 | 61 | - (id)comboBox:(NSComboBox *)comboBox objectValueForItemAtIndex:(NSInteger)index{ 62 | DYRoomHistoryData *roomData = self.roomHistory[index]; 63 | return [NSString stringWithFormat:@"%@(%@)",roomData.nickname,roomData.roomId]; 64 | } 65 | 66 | - (void)comboBoxSelectionDidChange:(NSNotification *)notification { 67 | dispatch_async(dispatch_get_main_queue(), ^{ 68 | self.roomComboBox.stringValue = self.roomHistory[self.roomComboBox.indexOfSelectedItem].roomId; 69 | }); 70 | } 71 | 72 | - (void)reloadHistory { 73 | self.roomHistory = [DYRoomHistoryModel getAll]; 74 | [self.roomComboBox reloadData]; 75 | } 76 | 77 | - (void)reset { 78 | 79 | } 80 | 81 | - (IBAction)playAction:(NSButton *)sender { 82 | [self.roomComboBox resignFirstResponder]; 83 | NSInteger videoQuality = self.videoQualityButton.indexOfSelectedItem; 84 | [[NSUserDefaults standardUserDefaults] setInteger:videoQuality forKey:@"videoQuality"]; 85 | NSString *room = [self.roomComboBox.stringValue stringByReplacingOccurrencesOfString:@" " withString:@""]; 86 | if (room.length == 0) { 87 | room = self.roomComboBox.placeholderString; 88 | } 89 | [self playWithRoomString:room]; 90 | } 91 | 92 | - (void)playWithRoomString:(NSString *)room { 93 | DYRoomInfo *roomInfo = [[DYRoomInfo alloc] init]; 94 | NSString *roomId = [roomInfo getRoomIdWithString:room]; 95 | if (!roomId.length) { 96 | [self showError:@"无法获取房间ID信息"]; 97 | return; 98 | } 99 | if (!roomInfo.showStatus) { 100 | [self showError:@"主播不在线"]; 101 | return; 102 | } 103 | NSInteger videoQuality = self.videoQualityButton.indexOfSelectedItem; 104 | int rate = 0; 105 | switch (videoQuality) { 106 | case 2: 107 | rate = 1; 108 | break; 109 | case 1: 110 | rate = 2; 111 | break; 112 | default: 113 | break; 114 | } 115 | if (![roomInfo getInfoWithRoomId:roomId rate:rate]) { 116 | [self showError:@"无法获取房间信息"]; 117 | return; 118 | } 119 | [DYRoomHistoryModel saveRoomId:roomInfo.roomId withNickname:roomInfo.nickName]; 120 | [self reloadHistory]; 121 | 122 | NSWindowController *playerWindowController = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"PlayerWindowController"]; 123 | [playerWindowController.window center]; 124 | [playerWindowController.window makeKeyAndOrderFront:nil]; 125 | [playerWindowController.window setDelegate:self]; 126 | self.playerWindowController = playerWindowController; 127 | PlayerViewController *playerViewController = (PlayerViewController *)playerWindowController.contentViewController; 128 | [playerViewController loadPlayerWithInfo:roomInfo]; 129 | self.playerViewController = playerViewController; 130 | self.playingActivity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityIdleDisplaySleepDisabled reason:@"playing video"]; 131 | [self.view.window performClose:nil]; 132 | } 133 | 134 | - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize { 135 | [resizingTimer invalidate]; 136 | resizingTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(resumePlay) userInfo:nil repeats:NO]; 137 | [self.playerViewController.glView setPause:YES]; 138 | return frameSize; 139 | } 140 | 141 | - (void)resumePlay { 142 | dispatch_async(dispatch_get_main_queue(), ^(void){ 143 | // call SetFrame to force opengl canvas resize 144 | NSView *videoView = self.playerViewController.glView; 145 | NSRect rect = videoView.frame; 146 | rect.size.width += 1; 147 | [videoView setFrame:rect]; 148 | rect.size.width -= 1; 149 | [videoView setFrame:rect]; 150 | [self.playerViewController.glView setPause:NO]; 151 | }); 152 | } 153 | 154 | - (void)showError:(NSString *)string { 155 | NSAlert *alert = [NSAlert new]; 156 | [alert addButtonWithTitle:@"确定"]; 157 | [alert setMessageText:string]; 158 | [alert setAlertStyle:NSWarningAlertStyle]; 159 | [alert beginSheetModalForWindow:[self.view window] completionHandler:nil]; 160 | } 161 | 162 | - (void)windowWillClose:(NSNotification *)notification{ 163 | [self.playerViewController destroyPlayer]; 164 | self.playerWindowController = nil; 165 | [[NSProcessInfo processInfo] endActivity:self.playingActivity]; 166 | [self reset]; 167 | [self.view.window makeKeyAndOrderFront:nil]; 168 | } 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /Douyu/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Douyu 4 | // 5 | // Created by Grayon on 2017/9/21. 6 | // Copyright © 2017年 Lanskaya. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | @autoreleasepool { 13 | return NSApplicationMain(argc, argv); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Grayon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :osx, '10.10' 3 | 4 | target 'Douyu' do 5 | # Uncomment the next line if you're using Swift or would like to use dynamic frameworks 6 | # use_frameworks! 7 | 8 | # Pods for Douyu 9 | 10 | pod 'LKDBHelper' 11 | 12 | end 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Douyu-for-Mac 2 | 非官方Mac版斗鱼直播播放器,实时弹幕显示 3 | 4 | ![](http://ww2.sinaimg.cn/large/0060lm7Tly1fjsi5xj9czj30qo0g8dpt.jpg) 5 | 6 | # Useage 7 | ### mpv.conf 8 | - 自定义 mpv.conf 放入 `~/Library/Application Support/Douyu/conf/` 文件夹下 9 | ### mpv键位控制 10 | - [mpv键盘使用说明](https://mpv.io/manual/master/#keyboard-control) 11 | - 9 / 0 控制音量 12 | - v 键开启关闭弹幕 13 | - T 键开启关闭 stay-on-top 14 | 15 | ### 油猴脚本 16 | [Mac斗鱼播放器油猴脚本](https://greasyfork.org/zh-CN/scripts/36608-mac斗鱼播放器) 17 | ![](http://ww2.sinaimg.cn/large/0060lm7Tly1fmsubfqgvtj30mv07kgmz.jpg) 18 | 19 | # Install 20 | ## 下载最新 releases 包 21 | [Douyu.zip](https://github.com/Grayon/Douyu-for-Mac/releases) 22 | 23 | # 支持一下 24 | 25 | 微信 26 | 27 | ![wechat](http://ww2.sinaimg.cn/large/0060lm7Tly1fmsuswk8iaj304h04fq2z.jpg) 28 | 29 | 30 | 31 | 支付宝 32 | 33 | ![alipay](http://ww1.sinaimg.cn/large/0060lm7Tly1fmsuswjai9j304h04gq2y.jpg) 34 | 35 | 36 | -------------------------------------------------------------------------------- /screen-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grayon/Douyu-for-Mac/33cc8bc84e9872770c0425b9c1cc8bb9fad7a4e1/screen-shot.png --------------------------------------------------------------------------------