iOS-直播开发(开发从底层做起)
代码链接: Github: https://github.com/jessonliu/JFLivePlaye
技术部分------ ⬇️
脑涂: ![ 直播思维导图.png ]
视频直播的大概流程就上脑涂上所画的, 还有一些没列出来, 比如, 聊天, 送礼, 踢出, 禁言, 等等一系列功能, 但本文只是针对视频直播的简单实现!
下边来说一下以下的几个点和使用到的类(后边会附上demo, 里边还有详细的备注)
1. 音视频采集
音视频采集, 网上也有很多大神些的技术博客, demo 等, 我这里边只针对iOS 原声的来介绍以下
利用AVFoundation框架, 进行音视频采集
AVCaptureSession // 音视频录制期间管理者
AVCaptureDevice // 设备管理者, (用来操作所闪光灯, 聚焦, 摄像头切换等)
AVCaptureDeviceInput // 音视频输入数据的管理对象
AVCaptureVideoDataOutput // 视频输出数据的管理者
AVCaptureAudioDataOutput // 音频输出数据的管理者
AVCaptureVideoPreviewLayer // 用来展示视频的图像
注意, 必须要设置音视频输出对象的代理方法, 然后在代理方法中获取sampleBuffer, 然后判断captureOutput是音频还是视频, 来进行音视频数据相应的编码
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
}
也可以利用GPUImageVideoCamera 来进行视频数据的采集获取, 可以利用GPUImage 进行美颜, 添加水印, 人脸识别等
2.流媒体
流媒体是指采用流式传输的方式在网上播放的媒体格式, 是边传边播的媒体,是多媒体的一种!
然后就是大家需要了解的几个关键词
帧:视频是由很多连续图像组成, 每一帧就代表一幅静止的图像
GOP:(Group of Pictures)画面组,一个GOP就是一组连续的画面,每个画面都是一帧,GOP就是很多帧的集合!
帧的分类:I帧、P帧、B帧
为了提高压缩比例,降低视频文件的大小,在针对连续动态图像编码时,一般会将连续若干幅图像编码为P、B、I三种帧类型
I帧:一组连续画面(GOP)的第一个帧, I帧采用帧内压缩法(也成关键帧压缩法), I帧的压缩不依靠与其他帧, 靠尽可能去除图像空间冗余信息来压缩的, 可以单独作为图像!
P帧:预测帧(也叫前向参考帧), P帧的压缩依赖于前一帧, 通过充分降低与图像序列中前面已编码帧的时间冗余信息来压缩传输数据量的编码图像!
B帧:也叫双向预测帧, 当把一帧压缩成B帧时,它根据邻近的前几帧、本帧以及后几帧数据的不同点来压缩本帧,也即仅记录本帧与前后帧的差值。
帧率:就是在1秒钟时间里传输的图片的帧数,也可以理解为图形处理器每秒钟能够刷新几次,通常用FPS表示, 每秒钟帧数 (fps) 愈多,所显示的动作就会愈流畅!
码率: 也成为比特率, 是指每秒传送的比特(bit)数, 比特率越高,传送数据速度越快, 单位为 bps(Bit Per Second)。
3. 音视频的编解码
音视频编解码, 说白了就是对音视频数据进行压缩, 减少数据对空间的占用, 便于网络传输, 存储和使用!
目前直播常用的音视频编解码方式是h.264/AVC, AAC/MP3
硬软编解码的区别:
硬解码:由显卡核心GPU来对高清视频进行解码工作,CPU占用率很低,画质效果比软解码略差一点,需要对播放器进行设置。
优点:播放流畅、低功耗
缺点:受视频格式限制、功耗大、画质没有软解码好
软解码:由CPU负责解码进行播放
优点:不受视频格式限制、画质略好于硬解
缺点:会占用过高的资源、对于高清视频可能没有硬解码流畅(主要看CPU的能力)
苹果API有提供音视频硬编解码接口, 但只针对iOS8.0以上版本!
利用VideoToolbox 和AudioToolbox 这连个框架进行音视频的硬编码!
这里附上前辈们的关于VideoToolbox使用的简书, http://www.jianshu.com/p/6dfe49b5dab8
和AudioToolbox的技术简书http://www.jianshu.com/p/a671f5b17fc1
感兴趣的话可以研究一下!
4.流媒体数据封装
TS: 是流媒体封装格式的一种,流媒体封装的好处就是不需要加载索引再播放,大大降低了首次载入的延迟,两个TS片段可以无缝拼接,播放器能连续播放!
FLV: 也是一种流媒体的封装格式,但他形成的文件极小、加载速度极快,使得网络观看视频文件成为可能,因此FLV格式成为了当今主流视频格式
5.RTMP推流
大家先看一张图, 常用的直播协议比较
这里只介绍一下RTMP协议, 如果还想了解更多的可在网上查找一下, 有很多关于流媒体协议的技术博客!
RTMP协议是基于TCP/IP 的协议簇;RTMP(Real Time Messaging Protocol)实时消息传送协议是Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输 开发的开放协议
它有多种变种:
a, RTMP工作在TCP之上,默认使用端口1935;
b, RTMPE在RTMP的基础上增加了加密功能;
c, RTMPT封装在HTTP请求之上,可穿透防火墙;
d, RTMPS类似RTMPT,增加了TLS/SSL的安全功能;
它是一个互联网TCP/IP体系结构中应用层的协议。RTMP协议中基本的数据单元称为消息(Message)。当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunk)。RTMP传输媒体数据的过程中,发送端首先把媒体数据封装成消息,然后把消息分割成消息块,最后将分割后的消息块通过TCP协议发送出去。接收端在通过TCP协议收到数据后,首先把消息块重新组合成消息,然后通过对消息进行解封装处理就可以恢复出媒体数据。
播放一个RTMP协议的流媒体需要经过以下几个步骤:握手,建立连接,建立流,播放。
demo中RTMP协议推流, 用的是librtmp-iOS框架! 参考https://my.oschina.net/jerikc/blog/501948
6. 播放器
IJKPlayer 是一个基于 ffplay 的轻量级 Android/iOS 视频播放器。API 易于集成;编译配置可裁剪,方便控制安装包大小;支持 硬件加速解码,更加省电。而DanmakuFlameMaster(开源弹幕框架) 架构清晰,简单易用,支持多种高效率绘制方式选择,支持多种自定义功能设置!
代码:
#import "JFLiveShowVC.h" 该类负责音视频采集及展示, 用于时间没问题, 没有吧音视频采集单独拿出来封装!
- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.// 需要用到的线程videoProcessingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);audioProcessingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);_jfEncodeQueue_video = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);_jfEncodeQueue_audio = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 检查权限和设备 [self checkDeviceAuth];// 数据保存路径self.documentDictionary = [(NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)) objectAtIndex:0];// 音频编码对象初始化self.audioEncoder = [[AACEncoder alloc] init];self.audioEncoder.delegate = self; // 设置代理self.videoEncoder = [[JFVideoEncoder alloc] init]; // 视频编码对象初始化self.videoEncoder.delegate = self; // 设置代理 _lock = dispatch_semaphore_create(1); // 当并行执行的处理更新数据时,会产生数据不一致的情况,使用Serial Dipatch queue 进行同步, 控制并发 } // 检查是否授权摄像头的使用权限 - (void)checkDeviceAuth {switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {case AVAuthorizationStatusAuthorized: // 已授权NSLog(@"已授权");[self initAVCaptureSession];break;case AVAuthorizationStatusNotDetermined: // 用户尚未进行允许或者拒绝, {[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {if (granted) {NSLog(@"已授权");[self initAVCaptureSession];} else {NSLog(@"用户拒绝授权摄像头的使用, 返回上一页, 请打开--> 设置 -- > 隐私 --> 通用等权限设置");}}];}break;default:{NSLog(@"用户尚未授权摄像头的使用权");}break;} }// 初始化 管理者 - (void)initAVCaptureSession {self.session = [[AVCaptureSession alloc] init];// 设置录像的分辨率// 先判断是被是否支持要设置的分辨率if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {// 如果支持则设置 [self.session canSetSessionPreset:AVCaptureSessionPreset1280x720];} else if ([self.session canSetSessionPreset:AVCaptureSessionPresetiFrame960x540]) {[self.session canSetSessionPreset:AVCaptureSessionPresetiFrame960x540];} else if ([self.session canSetSessionPreset:AVCaptureSessionPreset640x480]) {[self.session canSetSessionPreset:AVCaptureSessionPreset640x480];}// 开始配置 [self.session beginConfiguration];// 初始化视频管理self.videoDevice = nil;// 创建摄像头类型数组NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];// 便利管理抓捕道德所有支持制定类型的 设备集合for (AVCaptureDevice *device in devices) {if (device.position == AVCaptureDevicePositionFront) {self.videoDevice = device;}}// 视频 [self videoInputAndOutput];// 音频 [self audioInputAndOutput];// 录制的同时播放 [self initPreviewLayer];// 提交配置 [self.session commitConfiguration]; }// 视频输入输出 - (void)videoInputAndOutput {NSError *error;// 视频输入// 初始化 根据输入设备来初始化输出对象self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.videoDevice error:&error];if (error) {NSLog(@"-- 摄像头出错 -- %@", error);return;}// 将输入对象添加到管理者 -- AVCaptureSession 中// 先判断是否能搞添加输入对象if ([self.session canAddInput:self.videoInput]) {// 管理者能够添加 才可以添加 [self.session addInput:self.videoInput];}// 视频输出// 初始化 输出对象self.videoOutput = [[AVCaptureVideoDataOutput alloc] init];// 是否允许卡顿时丢帧self.videoOutput.alwaysDiscardsLateVideoFrames = NO;if ([self supportsFastTextureUpload]){// 是否支持全频色彩编码 YUV 一种色彩编码方式, 即YCbCr, 现在视频一般采用该颜色空间, 可以分离亮度跟色彩, 在不影响清晰度的情况下来压缩视频BOOL supportsFullYUVRange = NO;// 获取输出对象 支持的像素格式NSArray *supportedPixelFormats = self.videoOutput.availableVideoCVPixelFormatTypes;for (NSNumber *currentPixelFormat in supportedPixelFormats){if ([currentPixelFormat intValue] == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange){supportsFullYUVRange = YES;}}// 根据是否支持 来设置输出对象的视频像素压缩格式,if (supportsFullYUVRange){[self.videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];}else{[self.videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];}}else{[self.videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];}// 设置代理 [self.videoOutput setSampleBufferDelegate:self queue:videoProcessingQueue];// 判断管理是否可以添加 输出对象if ([self.session canAddOutput:self.videoOutput]) {[self.session addOutput:self.videoOutput];AVCaptureConnection *connection = [self.videoOutput connectionWithMediaType:AVMediaTypeVideo];// 设置视频的方向connection.videoOrientation = AVCaptureVideoOrientationPortrait;// 视频稳定设置if ([connection isVideoStabilizationSupported]) {connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;}connection.videoScaleAndCropFactor = connection.videoMaxScaleAndCropFactor;} }// 音频输入输出 - (void)audioInputAndOutput {NSError *jfError;// 音频输入设备self.audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];// 音频输入对象self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.audioDevice error:&jfError];if (jfError) {NSLog(@"-- 录音设备出错 -- %@", jfError);}// 将输入对象添加到 管理者中if ([self.session canAddInput:self.audioInput]) {[self.session addInput:self.audioInput];}// 音频输出对象self.audioOutput = [[AVCaptureAudioDataOutput alloc] init];// 将输出对象添加到管理者中if ([self.session canAddOutput:self.audioOutput]) {[self.session addOutput:self.audioOutput];}// 设置代理 [self.audioOutput setSampleBufferDelegate:self queue:audioProcessingQueue]; }// 播放同时进行播放 - (void)initPreviewLayer {[self.view layoutIfNeeded];// 初始化对象self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];self.previewLayer.frame = self.view.layer.bounds;self.previewLayer.connection.videoOrientation = [self.videoOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation;self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;self.previewLayer.position = CGPointMake(self.liveView.frame.size.width*0.5,self.liveView.frame.size.height*0.5);CALayer *layer = self.liveView.layer;layer.masksToBounds = true;[layer addSublayer:self.previewLayer]; }#pragma mark 返回上一级 - (IBAction)backAction:(id)sender {// 结束直播 [self.socket stop];[self.session stopRunning];[self.videoEncoder stopEncodeSession];fclose(_h264File);fclose(_aacFile);[self.navigationController popViewControllerAnimated:YES]; }#pragma mark 开始直播 - (IBAction)startLiveAction:(UIButton *)sender {_h264File = fopen([[NSString stringWithFormat:@"%@/jf_encodeVideo.h264", self.documentDictionary] UTF8String], "wb");_aacFile = fopen([[NSString stringWithFormat:@"%@/jf_encodeAudio.aac", self.documentDictionary] UTF8String], "wb");// 初始化 直播流信息JFLiveStreamInfo *streamInfo = [[JFLiveStreamInfo alloc] init];streamInfo.url = @"rtmp://192.168.1.110:1935/rtmplive/room";self.socket = [[JFRtmpSocket alloc] initWithStream:streamInfo];self.socket.delegate = self;[self.socket start];// 开始直播 [self.session startRunning];sender.hidden = YES; }#pragma mark -- AVCaptureAudioDataOutputSampleBufferDelegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {if (captureOutput == self.audioOutput) {[self.audioEncoder encodeSampleBuffer:sampleBuffer timeStamp:self.currentTimestamp completionBlock:^(NSData *encodedData, NSError *error) {fwrite(encodedData.bytes, 1, encodedData.length, _aacFile);}];} else {[self.videoEncoder encodeWithSampleBuffer:sampleBuffer timeStamp:self.currentTimestamp completionBlock:^(NSData *data, NSInteger length) {fwrite(data.bytes, 1, length, _h264File);}];} }- (void)dealloc {if ([self.session isRunning]) {[self.session stopRunning];}[self.videoOutput setSampleBufferDelegate:nil queue:dispatch_get_main_queue()];[self.audioOutput setSampleBufferDelegate:nil queue:dispatch_get_main_queue()]; }// 是否支持快速纹理更新 - (BOOL)supportsFastTextureUpload; { #if TARGET_IPHONE_SIMULATORreturn NO; #else#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-pointer-compare"return (CVOpenGLESTextureCacheCreate != NULL); #pragma clang diagnostic pop#endif }// 保存h264数据到文件 - (void) writeH264Data:(void*)data length:(size_t)length addStartCode:(BOOL)b {// 添加4字节的 h264 协议 start codeconst Byte bytes[] = "\x00\x00\x00\x01";if (_h264File) {if(b)fwrite(bytes, 1, 4, _h264File);fwrite(data, 1, length, _h264File);} else {NSLog(@"_h264File null error, check if it open successed");} }#pragma mark - JFRtmpSocketDelegate - (void)jf_videoEncoder_call_back_videoFrame:(JFVideoFrame *)frame {if (self.uploading) {[self.socket sendFrame:frame];} }#pragma mark - AACEncoderDelegate - (void)jf_AACEncoder_call_back_audioFrame:(JFAudioFrame *)audionFrame {if (self.uploading) {[self.socket sendFrame:audionFrame];} }#pragma mark -- JFRtmpSocketDelegate - (void)socketStatus:(nullable JFRtmpSocket *)socket status:(JFLiveState)status {switch (status) {case JFLiveReady:NSLog(@"准备");break;case JFLivePending:NSLog(@"链接中");break;case JFLiveStart:NSLog(@"已连接");if (!self.uploading) {self.timestamp = 0;self.isFirstFrame = YES;self.uploading = YES;}break;case JFLiveStop:NSLog(@"已断开");break;case JFLiveError:NSLog(@"链接出错");self.uploading = NO;self.isFirstFrame = NO;self.uploading = NO;break;default:break;} }// 获取当前时间戳 - (uint64_t)currentTimestamp{dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);uint64_t currentts = 0;if(_isFirstFrame == true) {_timestamp = NOW;_isFirstFrame = false;currentts = 0;}else {currentts = NOW - _timestamp;}dispatch_semaphore_signal(_lock);return currentts; }
// 注: 必须控制好线程, 不然很容易出现卡死或闪退的情况!
关于音视频编解码的代码, 就不在这里展示了, 放在demo 中, 有需要的话话可以下载!
Github: https://github.com/jessonliu/JFLivePlaye
本地流媒体服务器的搭建这个给大家一个连接: http://www.jianshu.com/p/8ea016b2720e
相关文章:

汇编程序设计与计算机体系结构软件工程师教程笔记:函数、字符串、浮点运算
《汇编程序设计与计算机体系结构: 软件工程师教程》这本书是由Brain R.Hall和Kevin J.Slonka著,由爱飞翔译。中文版是2019年出版的。个人感觉这本书真不错,书中介绍了三种汇编器GAS、NASM、MASM异同,全部示例代码都放在了GitHub上,…
《庆余年》值得一看吗?Python告诉你谁在关注 | CSDN原力计划
扫码参与CSDN“原力计划”作者 | A字头来源 | 数据札记倌庆余年电视剧终于在前两天上了,这两天赶紧爬取数据看一下它的表现。庆余年《庆余年》是作家猫腻的小说。这部从2007年就开更的作品拥有固定的书迷群体,也在文学IP价值榜上有名。期待已久的影视版的…

《C语言及程序设计》实践项目——画分支结构流程图
返回:贺老师课程教学链接 【单分支结构流程图-大值】问题:画流程图,输入两个整数a和b,输出其中的大值。提示:当a<b时,交换a和b,最后输出的a一定是其中的大值。流程图中可以直接给出交换a和b…

汇编程序设计与计算机体系结构软件工程师教程笔记:内联汇编与宏
《汇编程序设计与计算机体系结构: 软件工程师教程》这本书是由Brain R.Hall和Kevin J.Slonka著,由爱飞翔译。中文版是2019年出版的。个人感觉这本书真不错,书中介绍了三种汇编器GAS、NASM、MASM异同,全部示例代码都放在了GitHub上,…
无需标注数据,利用辅助性旋转损失的自监督GANs,效果堪比现有最好方法
作者 | Ting Chen译者 | 王红成出品 | AI科技大本营(ID:rgznai100)本文作者提出了一种自检督方式的生成对抗网络,通过辅助性的旋转损失来达到目的。因为通常主流方法来生成自然图像都是通过条件GAN来完成,但是这就需要很多的标签数…

iOS环信聊天界面中点击头像和消息的几种状态
/*环信自带头像点击事件*/ - (void)messageViewController:(EaseMessageViewController *)viewControllerdidSelectAvatarMessageModel:(id<IMessageModel>)messageModel {内容可以根据需要自己添加 }/*!methodbrief 点击了简历消息 (lyq添加)discussion 点击了简历消息,…

ASP.NET将Session保存到数据库中
因为ASP.NET中Session的存取机制与ASP相同,都是保存在进行中, 一旦进程崩溃,所有Session信息将会丢失,所以我采取了将Session信息保存到SQL Server中,尽管还有其它的几个方式(本文不作介绍)&…

iOS App上架流程
一、前言:作为一名iOSer,把开发出来的App上传到App Store是必要的。下面就来详细讲解一下具体流程步骤。 二、准备: 一个已付费的开发者账号(账号类型分为个人(Individual)、公司(Company&#…
不止Markov决策过程,全景式分析强化学习研究内容
作者 | 肖智清编辑 | 刘静来源 | CSDN(ID:CSDNnews)强化学习作为通用人工智能的希望,吸引了很多人工智能爱好者学习和研究。Markov决策过程是最知名的强化学习模型,强化学习教程也常以Markov决策过程作为起点。但是&am…

Windows下创建进程简介
正在执行的应用程序称为进程,进程不仅仅是指令和数据,它还有状态。状态是保存在处理器寄存器中的一组值,如当前执行指令的地址、保存在内存中的值,以及唯一定义进程在任一时刻任务的所有其他值。进程与应用程序的一个重要的区别在…

jQuery中鲜为人知的的几个方法
转来学习一下 jQuery中鲜为人知的的几个方法 jQuery近些年来仍旧是web开发中最受欢迎的类库,虽然大家褒贬不一,但是仍旧不失为一款最流行的Javascript,在今天这篇文章中,我们将介绍几个jQuery的相关方法,无论你是入门级…

Linux下创建进程简介
在博文https://blog.csdn.net/fengbingchun/article/details/108940548中简单介绍了Windows下通过函数CreateProcess创建进程的过程,这里简单介绍下Linux下通过fork函数创建进程的过程。很早之前在https://blog.csdn.net/fengbingchun/article/details/45690745中也…

热更新 FrameWork
工作中遇到想要绕过AppStore直接更新App的要求!这里友情提示下,看了很多资料只是是实现了功能,但在项目中并没有真正用到!资料大多都显示会被拒,这个说的是个人级的,好像企业级的不会这样,仅仅是项目需要做…
陆首群:评人工智能如何走向新阶段?
作者 | 陆首群,中国开源软件推进联盟名誉主席 出品 | AI科技大本营(ID:rgznai100) 编者按:近来,业内关于深度学习算法的潜力是否已达天花板的争论陆续发出。有人认为,基于深度学习算法的应用还有深度开拓空…

Mysql INSERT、REPLACE、UPDATE的区别
用于操作数据库的SQL一般分为两种,一种是查询语句,也就是我们所说的SELECT语句,另外一种就是更新语句,也叫做数据操作语句。言外之意,就是对数据进行修改。在标准的SQL中有3个语句,它们是INSERT、UPDATE以及…

软件调试的艺术笔记:GDB
很久之前,在https://blog.csdn.net/fengbingchun/article/details/41413381中简单整理过gdb中常用的一些命令,不齐全,这里按照《软件调试的艺术》一书中关于gdb的介绍再做次整理。《软件调试的艺术》于2009年由人民邮电出版社出版。 1. 预备…

App Store 审核被拒整理
整理以前和现在遇到的审核被拒第一:2.2 DetailsWe discovered one or more bugs in your app when reviewed on iPhone running iOS 8.1.3 on both Wi-Fi and cellular networks. Specifically, we were not able to complete the In App Purchase. When tapped on …
只服这篇“神文”:基于老子哲学、相对论的超级人工智能模型
作者 | Anonymous authors译者 | TroyChang出品 | AI科技大本营(ID:rgznai100)在此前我们为大家介绍 ICLR 2020 论文投稿情况时,提到了一篇“神作”在论文中作者们提出一个 ASI 概念(Artificial Super Intelligence&am…

Navicat Premium使用教程【比较详细】
Navicat Premium使用教程简介:Navicat Premium是众所周知的数据库操作软件。本文比较详细。1、打开Navicat Premium,点击连接,选择MySQL,创建新连接。输入安装MySQL是的用户名和密码。点击确定。2、admin数据连接已经创建成功。下…
如何用Neo4j和Scikit-Learn做机器学习任务?| 附超详细分步教程
作者 | Mark Needham译者 | Tianyu、Shawnice编辑 | Jane出品 | AI科技大本营(ID:rgznai100)图算法不是一个新兴技术领域,在开源库中已经有很多功能强大的算法实现。近两年,业内的学者与科学家都在积极探索可以弥补深度…

Docker在Ubuntu16.04和Windows10家庭版上安装操作步骤
之前在 https://blog.csdn.net/fengbingchun/article/details/109559500 中对Docker作了简单的介绍,这里介绍下Docker在Ubuntu16.04 x86_64 64位上和Windows10 x86_64 64位家庭版上的安装过程。 在Ubuntu上安装Docker(或Docker引擎),Ubuntu必须是64位的…

iOS 不同机型屏幕适配
// .pch 文件中写 // 判断是iPhone机型 /** 4s 960 * 640* 5/5s 1136 x 640* 6/6s/7/8 4.7英寸 1334 x 750* 6p/6sp/7p/8p 5.5英寸 1920 x 1080* X 5.8英寸 2436 x 1125}*/ #define IS_IPHONE_4s [UIScreen instancesRespondToSelector:selector(currentMode)] ? \ CGSizeEqua…

北京中天荣泰视觉检测 仿真
www.romtek.cnhttp://jobs.zhaopin.com/191485013250433.htm?ssidkeyy&ff01&ss101转载于:https://www.cnblogs.com/pengkunfan/p/4316018.html

Docker客户端常用命令整理
之前在 https://blog.csdn.net/fengbingchun/article/details/109584460 中介绍过在Windows10家庭版和Ubuntu16.04上安装Docker的操作步骤,这里整理下Docker客户端常用命令。 在Windows10家庭版上运行Docker后,通过VMware就不能打开Ubuntu16.04虚拟机了…
深度学习入门笔记,三流程序员如何凭借实力逆袭高薪?你不服不行!
最近经常有朋友提及,想要入门深度学习,该如何学习?关于深度学习,网上的资料很多,不过貌似大部分都不太适合初学者。 我曾经是一名三流程序员,每天的工作内容就是在前人留下的 bug 上写新的bug,我…

Swift编程语言
The Swift Programming Language中文手册1.【精校版】The Swift Programming Language--欢迎使用Swift--关于Swift2.【精校版】The Swift Programming Language-欢迎使用Swift-Swift 初见3.The Swift Programming Language--语言指南--基础部分4.The Swift Programming Languag…

ondblog 修改informix日志模式
-N No Logging 没有日志-U Unbuffered Logging 非缓冲日志-B Buffered Logging 缓冲日志-A Unbuffered Logging, Mode ANSI ANSI模式No Logging 没有日志----“没有日志”模式只向逻辑日志写很少的信息,它只记录执行的DDL语句,这些语句影响到的行并…

iOS RunLoop详解
一、简介 CFRunLoopRef源码RunLoop是一个对象,这个对象在循环中用来处理程序运行过程中出现的各种事件(比如说触摸事件、UI刷新事件、定时器事件、Selector事件),从而保持程序的持续运行;而且在没有事件处理的时候&…

开源库jemalloc简介
jemalloc是通用的malloc(3)实现,它强调避免碎片和可扩展的并发支持。它的源码位于https://github.com/jemalloc/jemalloc,最新稳定版本为5.2.1。 glibc的内存分配算法是基于dlmalloc实现的ptmalloc;tcmalloc是Google开发的内存分配器&#x…
改善深度学习训练的trick总结 | CSDN博文精选
扫码参与CSDN“原力计划”作者 | ZesenChen来源 | CSDN博客精选在深度学习中,同样一个模型用不同的初始化,数据处理,batch size,学习率,优化器都能得到不同性能的参数。我根据自己参与过的比赛中经常用到的一些trick进…