iOS直播技术总结

GuadalupeRo 7年前
   <h2><strong>直播总结</strong></h2>    <h2><strong>1.概述</strong></h2>    <p>关于直播的技术文章不少,成体系的不多。我们将用这篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。</p>    <h3><strong>1.1 一个完整的直播APP原理</strong></h3>    <p>直播原理 : 把主播录制的视频,推流送到服务器,服务器经过处理(鉴黄等),通过CDN分发给观众看。</p>    <p>直播环节 : 推流端(采集、美颜、编码、推流),服务端处理(转码、录制、截图、鉴黄)、播放器(拉流、解码、渲染)、互动系统(聊天室、礼物系统、赞)</p>    <h3><strong>1.2 一个完整直播APP实现流程</strong></h3>    <p><strong>1.采集、2.滤镜处理、3.编码、4.推流、5.CDN分发、6.拉流、7.解码、8.播放、9.聊天互动 </strong></p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/f09c8fa86a7a516e2f1ae0d0a044b5c1.png"></p>    <h3><strong>1.3 一个完整直播APP架构</strong></h3>    <p><strong>1.采集端、服务端、播放端 </strong></p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/75b2c9d95dab2a24626285e0709217b3.png"></p>    <h3><strong>1.4 一个完整直播APP技术点</strong></h3>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/274c990495127e7f193dede20fa6ec06.jpg"></p>    <p>下面我们会选择一部分技术进行讲解。</p>    <h2><strong>2. 视频采集</strong></h2>    <h3><strong>2.1 基本知识介绍</strong></h3>    <p>AVFundation : 音视频数据采集需要用AVFundation框架</p>    <p>AVCaptureDevice : 硬件设备,包括麦克风、摄像头、通过该对象可以设置物理设备的一些属性。例如相机焦距,白平衡等</p>    <p>AVCaptureDeviceInput : 硬件输入对象,可以根据AVCaptureDevice创建对应的AVCaptureDeviceInput对象,用于管理硬件输入数据</p>    <p>AVCaptureOutput : 硬件输出对象,用于接收各类输出数据,通常使用对应的子类AVCaptureAudioDataOutput(声音数据输出对象), AVCaptureVideoDataOutput(视频输出对象)</p>    <p>AVCaptureConnection : 当把一个输入和输出添加到AVCaptureSession后。AVCaptureSession就会在输出、输出设备之间建立连接,而且通过AVCaptureOutput可以获得这个对象</p>    <p>AVCaptureVideoPreviewLayer : 相机拍摄预览图层,能实时查看相机效果。创建该对象需要指定对应的AVCaptureSession对象,因为AVCaptureSession包含输出数据,有视频数据才能显示。</p>    <p>AVCaptureSession : 协调输入与输出之间传递数据</p>    <h3><strong>2.2 捕获音视频步骤</strong></h3>    <p style="text-align:center">包含关系:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1f48149cbd7e12d0690e94ba51015323.png"></p>    <p>步骤:</p>    <ol>     <li>创建AVCaptureDevice(video或者audio)</li>     <li>根据AVCaptureDevice创建AVCaptureDeviceInput。</li>     <li>创建AVCaptureSession</li>     <li>把创建的AVCaptureDeviceInput加入AVCaptureSession</li>     <li>添加视频预览图层AVCaptureVideoPreviewLayer</li>     <li>创建AVCaptureAudioDataOutput,并加入AVCaptureSession</li>     <li>启动会话</li>    </ol>    <p>官方步骤(可以忽略):</p>    <ol>     <li>创建AVCaptureSession对象</li>     <li>获取AVCaptureDevice录像设备(摄像头),录音设备(麦克风)。只用于配置</li>     <li>根据音频/视频硬件设备(AVCaptureDevice)创建音频/视频硬件输入数据对象(AVCaptureDeviceInput),专门管理数据输入。</li>     <li>创建视频输出数据管理对象(AVCaptureVideoDataOutput),并且设置样品缓存代理(setSampleBufferDelegate)就可以通过它拿到采集到的视频数据</li>     <li>创建音频输出数据管理对象(AVCaptureAudioDataOutput),并且设置样品缓存代理(setSampleBufferDelegate)就可以通过它拿到采集到的音频数据</li>     <li>将数据输入对象AVCaptureDeviceInput、数据输出对象AVCaptureOutput添加到媒体会话管理对象AVCaptureSession中,就会自动让音频输入与输出和视频输入与输出产生连接.</li>     <li>创建视频预览图层AVCaptureVideoPreviewLayer并指定媒体会话,添加图层到显示容器layer中</li>     <li>启动AVCaptureSession,只有开启,才会开始输入到输出数据流传输</li>    </ol>    <p>其中 AVCaptureAudioDataOutput 、 AVCaptureVideoDataOutput 包含两个代理方法,可以一直监听捕获属性。</p>    <pre>  <code class="language-objectivec">- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection  {      if (self.videoConnection == connection)      {          NSLog(@"采集到视频");      }      else if(self.audioConnection == connection)      {          NSLog(@"采集到音频");      }  }    // 丢失帧会调用这里  - (void)captureOutput:(AVCaptureOutput *)captureOutput didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 6_0)  {      NSLog(@"丢失帧");  }</code></pre>    <h3><strong>2.3 捕捉图片</strong></h3>    <p>AVCaptureStillImageOutput 可以将捕获到的Video转换为图片。</p>    <ol>     <li>创建device</li>     <li>根据device创建deviceInput</li>     <li>添加deviceInput进session</li>     <li>添加预览图层</li>     <li>添加deviceOutput进session</li>     <li>调用AVCaptureConnection生成图片</li>    </ol>    <h3><strong>2.4 捕捉视频</strong></h3>    <p>AVCaptureMovieFileOutput 可以将捕捉到的视频输出到磁盘。可以设置录制最长时限或录制到特定大小,还可以配置成保留最小可用磁盘空间。</p>    <ol>     <li>创建device</li>     <li>根据device创建deviceInput</li>     <li>添加deviceInput进session</li>     <li>添加预览图层</li>     <li>添加deviceOutput进session</li>     <li>调用AVCaptureMovieFileOutput把视频写入文件</li>    </ol>    <p>AVCaptureMovieFileOutput 包含有几个代理方法。分别是 视频开始录制 , 视频暂停 , 视频恢复 , 视频将要录制完成 , 视频录制完成 。</p>    <h3><strong>2.5 采集音频视频按帧输出流程解析</strong></h3>    <p>1.找到物理设备摄像头_inputCamera、麦克风_microphone,创建摄像头输入videoInput和麦克风输入audioInput;</p>    <p>2.设置videoInput和audioInput为_captureSession的输入,同时设置videoOutput和audioOutput为_captureSession的输出,并且设置videoOutput和audioOutput的输出delegate;</p>    <p>3._captureSession调用startRunning,开始捕获信号;</p>    <p>4.音频数据到达,把数据转发给之前设置的audioEncodingTarget,并通过调用assetWriterAudioInput的appendSampleBuffer方法写入音频数据;</p>    <p>5.视频数据到达,视频数据传入响应链,经过处理后通过assetWriterPixelBufferInput的appendSampleBuffer方法写入视频数据;</p>    <p>6.选择保存后,文件通过ALAssertLibrary写入手机照片库。</p>    <p>流程图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f34a8d3bf797a4639414625ae1590378.png"></p>    <h3><strong>2.6 Demo在这里</strong></h3>    <p>代码 :捕获音视频Demo</p>    <h2><strong>3. GPUImage</strong></h2>    <p>前面好像没看懂,可以看这里吗?</p>    <p>可以,GPUImage对AVFundation进行了一层封装,就算你不会前面的也没关系。</p>    <h3><strong>3.1 基本概念</strong></h3>    <p>GPU 手机或者电脑用于处理图像渲染的硬件</p>    <p>OpenGL ES 一套图形与硬件接口,用于把处理好的图片显示到屏幕上。</p>    <p>GPUImage 是一个基于OpenGL ES 2.0图像和视频处理的开源iOS框架,提供各种各样的图像处理滤镜,并且支持照相机和摄像机的实时滤镜,内置120多种滤镜效果,并且能够自定义图像滤镜。</p>    <p>GPUImage 是一个基于OpenGL ES 2.0图像和视频处理的开源iOS框架,提供各种各样的图像处理滤镜,并且支持照相机和摄像机的实时滤镜,内置120多种滤镜效果,并且能够自定义图像滤镜。</p>    <p>滤镜处理的原理 就是把静态图片或者视频的每一帧进行图形变换再显示出来。它的本质就是像素点的坐标和颜色变化</p>    <h3><strong>3.1 利用GPUImage处理直播过程中美颜流程</strong></h3>    <p style="text-align: center;">采集视频 => 获取每一帧图片 => 滤镜处理 => GPUImageView展示 <img src="https://simg.open-open.com/show/ac91bdfe7012ad8dcd6388154a4cd0d1.png"></p>    <h3><strong>3.2 处理画面原理</strong></h3>    <p>GPUImage采用链式方式来处理画面,通过addTarget:方法为链条添加每个环节的对象,处理完一个target,就会把上一个环节处理好的图像数据传递下一个target去处理,称为GPUImage处理链。 一般的target可以分为两类: 中间环节 的target,一般是指各种filter,是GPUImageFilter或者是子类</p>    <p>最终环节 的target,GPUImageView 用于显示到屏幕上或者GPUImageMovieWriter写成视频文件。</p>    <p>主要分为三个环节: source(视频,图片源) => filter(滤镜) => final target(处理后的视频、图片)</p>    <h3><strong>3.3 美颜原理</strong></h3>    <p>磨皮(GPUImageBilateralFilter) :本质就是让像素点模糊,可以使用高斯模糊,但是可能导致边缘会不清晰,用双边滤波(Bilateral Filter) ,有针对性的模糊像素点,能保证边缘不被模糊。</p>    <p>美白(GPUImageBrightnessFilter) :本质就是提高亮度。</p>    <h3><strong>3.4 GPUImage源对象</strong></h3>    <p>GPUImage的数据源只能是4类:</p>    <p>GPUImageVideoCamera ios摄像头的实时美颜。GPUImageVideoCamera是GPUImageOutput的子类,提供来自摄像头的图像数据作为源数据,一般是响应链的源头。</p>    <p>GPUImageStillCamera 相机拍照</p>    <p>GPUImagePicture 处理静止图像</p>    <p>GPUImageMovie 电影</p>    <h3><strong>3.5 用法</strong></h3>    <ol>     <li>创建过滤器</li>     <li>创建源对象</li>     <li>把过滤器添加到源对象</li>     <li>生成target</li>    </ol>    <p>静态图片处理:</p>    <pre>  <code class="language-objectivec">UIImage *inputImage = [UIImage imageNamed:@"105"];    // 创建过滤器  GPUImageBrightnessFilter *filter = [[GPUImageBrightnessFilter alloc] init];  filter.brightness = 0.5;  [filter forceProcessingAtSize:inputImage.size];  [filter useNextFrameForImageCapture]; // 告诉系统从后来捕获过滤器    // 处理静止的图像  GPUImagePicture *stillPic = [[GPUImagePicture alloc] initWithImage:inputImage];  [stillPic addTarget:filter]; //添加过滤器  [stillPic processImage]; // 执行渲染    UIImage *newImage = [filter imageFromCurrentFramebuffer];    UIImageView *imageView = [[UIImageView alloc] initWithImage:newImage];  [imageView sizeToFit];  [self.view addSubview:imageView];   imageView.center = CGPointMake(CGRectGetWidth(self.view.frame)/2, CGRectGetHeight(self.view.frame)/2);</code></pre>    <p>实时美颜处理:</p>    <pre>  <code class="language-objectivec">// 创建视频源  GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetHigh cameraPosition:AVCaptureDevicePositionBack];  // 设置方向  videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;    // 创建预览View  GPUImageView *videoPreview = [[GPUImageView alloc] initWithFrame:self.view.bounds];  [self.view insertSubview:videoPreview atIndex:0];     // 添加预览图层到源  GPUImageBeautifyFilter *fiter = [[GPUImageBeautifyFilter alloc] init];  [_videoCamera addTarget:fiter];  [fiter addTarget:self.videoPreview];  // 开始采集视频  [videoCamera startCameraCapture];</code></pre>    <p>到这里,仅仅是屏幕显示的内容有滤镜效果,而作为直播应用,还需要输出带有美颜效果的视频流。 我们需要使用 GPUImageMovieWriter 类,才能处理视频流。</p>    <p>核心思路:</p>    <p>通过GPUImageVideoCamera采集视频和音频信息,音频信息直接发送给 GPUImageMovieWriter ,视频信息传入响应链作为源头,渲染后的视频再写入 GPUImageMovieWriter ,同时通过 GPUImageView 显示在屏幕上。只需要 addTarget 就可以添加 GPUImageMovieWriter ;</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d6e57d9bbbbcca55c10f1080d9a17f89.png"></p>    <h3><strong>3.6 实例代码在这里</strong></h3>    <h2><strong>4. 音视频编码,解码</strong></h2>    <p>这一章太难了,以后再写。</p>    <h3>VideoToolBox</h3>    <h3>AudioToolBox</h3>    <h2><strong>5. 流媒体服务器</strong></h2>    <p>国内外有很多好用的流媒体服务区。这里为了方便搭建我们采用 nginx+RTMP 搭建流媒体服务器。</p>    <h3><strong>5.1 MAC环境搭建</strong></h3>    <p><a href="/misc/goto?guid=4959723290945131301" rel="nofollow,noindex">MACOS上搭建nginx+rtmp环境</a></p>    <h3><strong>5.2 Centos环境搭建</strong></h3>    <p><a href="/misc/goto?guid=4959723291043244508" rel="nofollow,noindex">Centos下搭建的nginx + RTMP环境</a></p>    <h3><strong>5.3 服务端常用技术</strong></h3>    <p>CDN 直播数据上传到服务器后,观看直播的人比较多,服务器是承受不了的,会将数据分发到CDN,观众直接去CDN获取数据。减少服务器的负载。</p>    <p>负载均衡 由多台服务器组成一个服务器集群,每次请求的时候,会根据服务器负载选择处理请求的服务器。</p>    <h2><strong>6. 推流</strong></h2>    <h3><strong>6.1 推流协议的选择</strong></h3>    <p><a href="/misc/goto?guid=4959723291132500908" rel="nofollow,noindex">HLS和RTMP</a></p>    <h3><strong>6.2 推流原理</strong></h3>    <p>在iOS设备上进行各推流的话,是通过AVCaptureSession这么一个捕捉会话,指定两个AVCaptureDevice 也就是iOS的摄像头和麦克风,获取个原始视频和音频,然后需要进行个H.264的视频编码和AAC的音频编码,再将编码后的数据整合成一个音视频包,通过rmtp推送到nginx服务器</p>    <h3><strong>6.3 librtmp</strong></h3>    <p>这个参考资料很少。不过大部分都采用的这个。 因为涉及太多C/C++这里不讨论。</p>    <h2><strong>7. 音视频播放</strong></h2>    <h3><strong>7.1 播放框架的选择</strong></h3>    <p>iOS的播放框架主要有以下三种:</p>    <ol>     <li>AVPlayer 可以播放本地、远程视频,可以自定义UI进行控制</li>     <li>AVPlayerViewController 自带播放控制UI,不能自定义UI</li>     <li>MPMoviePlayerController,MPMoviePlayerViewController (iOS9后弃用)</li>    </ol>    <p>如果只是简单的播放视频,选择 AVPlayerViewController ,如果想自定义播放器,选择 AVPlayer 。</p>    <h3><strong>7.2 AVPlayer</strong></h3>    <p>AVPlayer是一个用来播放基于时间的流媒体控制对象。支持播放从本地、分布下载或通过HTTP Live Streaming协议得到的流媒体。 AVPlayer只管理一个单独资源的播放,不过框架还提供了AVPlayer的一个子类AVQueuePlayer,可以用来管理一个资源队列。当你需要在一个序列中播放多个条目或者为音频、视频资源设置播放循环时可以使用该子类。</p>    <p>AVPlayer视频播放使用步骤:</p>    <ol>     <li>创建视频资源地址URL,可以是网络URL</li>     <li>通过URL创建视频内容对象 AVPlayerItem ,一个视频对应一个 AVPlayerItem</li>     <li>创建 AVPlayer 视频播放对象,需要一个 AVPlayerItem 进行初始化</li>     <li>创建 AVPlayerLayer 播放图层对象,添加到现实视图上去</li>     <li>添加KVO监听。 监听到AVPlayerItemStatusReadyToPlay的时候调用play方法</li>    </ol>    <h3><strong>7.3 AVPlayerViewController</strong></h3>    <p>AVPlayerViewController 属于 AV Kit ,它是 UIViewController的子类 ,用于展示并控制AVPlayer实例的播放。</p>    <p>AVPlayerViewController 类使用步骤</p>    <ol>     <li>创建URL</li>     <li>创建AVPlayerViewController,并根据URL设置player属性</li>     <li>调用play方法</li>    </ol>    <p>Xcode8模拟器可能有问题,打开播放不了。</p>    <h3><strong>8.开源框架</strong></h3>    <p>前面所讲都有第三方框架支持。采集、美颜、推流有 LFLiveKit , 拉流播放有 IJKMediaFramework 。</p>    <p>LFLiveKit : LFLiveKit是iOS版开源RTMP流SDK。他支持后台录制、美颜功能、支持h264、AAC硬编码,动态改变速率,RTMP传输等</p>    <p>IJKMediaFramework : ijkplayer是B站开源的一款视频直播框架,它是基于ffmpeg。 如果从github下载是需要编译。</p>    <p>个人实验只需要配置Nginx+RTMP服务 这里我采用这两个第三方框架写了一个直播,包含在线观看直播,和直播推流,支持在线美颜,前后摄像头切换等你需要:</p>    <ol>     <li> <p>搭建 Nginx+RTMP环境 :MAC 或者Centos</p> </li>     <li> <p>下载项目真机运行。 https://github.com/tiantianlan/miaoboDemo 登陆界面</p> </li>    </ol>    <p style="text-align:center"><img src="https://simg.open-open.com/show/aba8628d22f2c32f1cefbd61f1f62756.png"></p>    <p style="text-align:center">主页</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c32aa721d11b0dd40a8ffa419141b2a1.png"></p>    <p style="text-align:center">直播页面</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/22900993e294b2891acdd49e5c736853.png"></p>    <p> </p>    <p> </p>    <p>来自:https://github.com/tiantianlan/LiveExplanation</p>    <p> </p>