{"id":13672121,"url":"https://github.com/guoxiaopang/LiveExplanation","last_synced_at":"2025-04-27T21:32:14.952Z","repository":{"id":37664963,"uuid":"71772823","full_name":"guoxiaopang/LiveExplanation","owner":"guoxiaopang","description":"直播技术的总结","archived":false,"fork":false,"pushed_at":"2016-11-08T08:08:14.000Z","size":14352,"stargazers_count":691,"open_issues_count":0,"forks_count":242,"subscribers_count":42,"default_branch":"master","last_synced_at":"2024-08-03T09:11:29.654Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/guoxiaopang.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-24T09:24:50.000Z","updated_at":"2024-07-25T15:20:48.000Z","dependencies_parsed_at":"2022-07-12T16:42:41.506Z","dependency_job_id":null,"html_url":"https://github.com/guoxiaopang/LiveExplanation","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guoxiaopang%2FLiveExplanation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guoxiaopang%2FLiveExplanation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guoxiaopang%2FLiveExplanation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guoxiaopang%2FLiveExplanation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guoxiaopang","download_url":"https://codeload.github.com/guoxiaopang/LiveExplanation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224087149,"owners_count":17253510,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T09:01:27.274Z","updated_at":"2024-11-11T10:30:23.186Z","avatar_url":"https://github.com/guoxiaopang.png","language":"Objective-C","funding_links":[],"categories":["Objective-C"],"sub_categories":[],"readme":"#直播总结\n\n##1.概述\n关于直播的技术文章不少，成体系的不多。我们将用这篇文章，更系统化地介绍当下大热的视频直播各环节的关键技术，帮助视频直播创业者们更全面、深入地了解视频直播技术，更好地技术选型。\n###1.1 一个完整的直播APP原理\n`直播原理` : 把主播录制的视频，推流送到服务器，服务器经过处理(鉴黄等),通过CDN分发给观众看。\n\n`直播环节` : 推流端(采集、美颜、编码、推流)，服务端处理(转码、录制、截图、鉴黄)、播放器(拉流、解码、渲染)、互动系统(聊天室、礼物系统、赞)\n###1.2 一个完整直播APP实现流程\n`1.采集、2.滤镜处理、3.编码、4.推流、5.CDN分发、6.拉流、7.解码、8.播放、9.聊天互动`\n![](直播app流程.png)\n###1.3 一个完整直播APP架构\n`1.采集端、服务端、播放端`\n![](liveApp.png)\n###1.4 一个完整直播APP技术点\n![](直播app技术点.jpeg)\n\n下面我们会选择一部分技术进行讲解。\n\n`提示`：[总结不好 图看不清 请点这里](http://www.jianshu.com/p/bd42bacbe4cc)\n\n##2. 视频采集\n###2.1 基本知识介绍\n`AVFundation` : 音视频数据采集需要用AVFundation框架\n\n`AVCaptureDevice` : 硬件设备，包括麦克风、摄像头、通过该对象可以设置物理设备的一些属性。例如相机焦距，白平衡等\n\n`AVCaptureDeviceInput` : 硬件输入对象，可以根据AVCaptureDevice创建对应的AVCaptureDeviceInput对象，用于管理硬件输入数据\n\n`AVCaptureOutput` : 硬件输出对象，用于接收各类输出数据，通常使用对应的子类AVCaptureAudioDataOutput(声音数据输出对象), AVCaptureVideoDataOutput(视频输出对象)\n\n`AVCaptureConnection` : 当把一个输入和输出添加到AVCaptureSession后。AVCaptureSession就会在输出、输出设备之间建立连接，而且通过AVCaptureOutput可以获得这个对象\n\n`AVCaptureVideoPreviewLayer` : 相机拍摄预览图层，能实时查看相机效果。创建该对象需要指定对应的AVCaptureSession对象，因为AVCaptureSession包含输出数据，有视频数据才能显示。\n\n`AVCaptureSession` : 协调输入与输出之间传递数据\n\n###2.2 捕获音视频步骤\n包含关系:\n![](AVCaptureSession.png)\n\n步骤:\n\u003e1.  创建AVCaptureDevice(video或者audio)\n\u003e2.  根据AVCaptureDevice创建AVCaptureDeviceInput。\n\u003e3.  创建AVCaptureSession\n\u003e4.  把创建的AVCaptureDeviceInput加入AVCaptureSession\n\u003e5.  添加视频预览图层AVCaptureVideoPreviewLayer\n\u003e6.  创建AVCaptureAudioDataOutput，并加入AVCaptureSession\n\u003e7.  启动会话\n\n官方步骤(可以忽略)：\n\u003e 1. 创建AVCaptureSession对象\n\u003e 2. 获取AVCaptureDevice录像设备（摄像头），录音设备（麦克风）。只用于配置\n\u003e 3. 根据音频/视频硬件设备(AVCaptureDevice)创建音频/视频硬件输入数据对象(AVCaptureDeviceInput)，专门管理数据输入。\n\u003e 4. 创建视频输出数据管理对象（AVCaptureVideoDataOutput），并且设置样品缓存代理(setSampleBufferDelegate)就可以通过它拿到采集到的视频数据\n\u003e 5. 创建音频输出数据管理对象（AVCaptureAudioDataOutput），并且设置样品缓存代理(setSampleBufferDelegate)就可以通过它拿到采集到的音频数据\n\u003e 6. 将数据输入对象AVCaptureDeviceInput、数据输出对象AVCaptureOutput添加到媒体会话管理对象AVCaptureSession中,就会自动让音频输入与输出和视频输入与输出产生连接.\n\u003e 7. 创建视频预览图层AVCaptureVideoPreviewLayer并指定媒体会话，添加图层到显示容器layer中\n\u003e 8. 启动AVCaptureSession，只有开启，才会开始输入到输出数据流传输\n\n\n其中`AVCaptureAudioDataOutput`、`AVCaptureVideoDataOutput`包含两个代理方法，可以一直监听捕获属性。\n\n```\n- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection\n{\n    if (self.videoConnection == connection)\n    {\n        NSLog(@\"采集到视频\");\n    }\n    else if(self.audioConnection == connection)\n    {\n        NSLog(@\"采集到音频\");\n    }\n}\n\n// 丢失帧会调用这里\n- (void)captureOutput:(AVCaptureOutput *)captureOutput didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 6_0)\n{\n    NSLog(@\"丢失帧\");\n}\n```\n\n不清楚可以看 : [官方文档](https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/04_MediaCapture.html)\n\n###2.3 捕捉图片\n`AVCaptureStillImageOutput`可以将捕获到的Video转换为图片。\n\u003e 1. 创建device\n\u003e 2. 根据device创建deviceInput\n\u003e 3. 添加deviceInput进session\n\u003e 4. 添加预览图层\n\u003e 5. 添加deviceOutput进session\n\u003e 6. 调用AVCaptureConnection生成图片\n \n###2.4 捕捉视频\n`AVCaptureMovieFileOutput` 可以将捕捉到的视频输出到磁盘。可以设置录制最长时限或录制到特定大小，还可以配置成保留最小可用磁盘空间。\n\u003e 1. 创建device\n\u003e 2. 根据device创建deviceInput\n\u003e 3. 添加deviceInput进session\n\u003e 4. 添加预览图层\n\u003e 5. 添加deviceOutput进session\n\u003e 6. 调用AVCaptureMovieFileOutput把视频写入文件\n\n`AVCaptureMovieFileOutput`包含有几个代理方法。分别是`视频开始录制`,`视频暂停`,`视频恢复`, `视频将要录制完成`, `视频录制完成`。\n\n#2.5 采集音频视频按帧输出流程解析\n\u003e 1.找到物理设备摄像头_inputCamera、麦克风_microphone，创建摄像头输入videoInput和麦克风输入audioInput；\n\u003e \n\u003e 2.设置videoInput和audioInput为_captureSession的输入，同时设置videoOutput和audioOutput为_captureSession的输出，并且设置videoOutput和audioOutput的输出delegate；\n\u003e \n\u003e 3._captureSession调用startRunning，开始捕获信号；\n\u003e \n\u003e 4.音频数据到达，把数据转发给之前设置的audioEncodingTarget，并通过调用assetWriterAudioInput的appendSampleBuffer方法写入音频数据；\n\u003e \n\u003e 5.视频数据到达，视频数据传入响应链，经过处理后通过assetWriterPixelBufferInput的appendSampleBuffer方法写入视频数据;\n\u003e \n\u003e 6.选择保存后，文件通过ALAssertLibrary写入手机照片库。\n\n流程图：\n\n![](捕获流程.png)\n\n###2.6 Demo在这里\n代码 : [捕获音视频Demo](https://github.com/tiantianlan/LiveExplanation/tree/master/%E6%8D%95%E8%8E%B7%E9%9F%B3%E8%A7%86%E5%B1%8F)\n\n##3. GPUImage\n`前面好像没看懂，可以看这里吗？`\n\n可以，GPUImage对AVFundation进行了一层封装，就算你不会前面的也没关系。\n###3.1 基本概念\n`GPU` 手机或者电脑用于处理图像渲染的硬件\n\n`OpenGL ES` 一套图形与硬件接口，用于把处理好的图片显示到屏幕上。\n\n`GPUImage` 是一个基于OpenGL ES 2.0图像和视频处理的开源iOS框架，提供各种各样的图像处理滤镜，并且支持照相机和摄像机的实时滤镜，内置120多种滤镜效果，并且能够自定义图像滤镜。\n\n`GPUImage` 是一个基于OpenGL ES 2.0图像和视频处理的开源iOS框架，提供各种各样的图像处理滤镜，并且支持照相机和摄像机的实时滤镜，内置120多种滤镜效果，并且能够自定义图像滤镜。\n\n`滤镜处理的原理` 就是把静态图片或者视频的每一帧进行图形变换再显示出来。它的本质就是像素点的坐标和颜色变化\n\n###3.1 利用GPUImage处理直播过程中美颜流程\n`采集视频 =\u003e 获取每一帧图片 =\u003e 滤镜处理 =\u003e GPUImageView展示`\n![](美颜流程.png)\n###3.2 处理画面原理\nGPUImage采用链式方式来处理画面,通过addTarget:方法为链条添加每个环节的对象，处理完一个target,就会把上一个环节处理好的图像数据传递下一个target去处理，称为GPUImage处理链。\n一般的target可以分为两类：\n`中间环节`的target，一般是指各种filter，是GPUImageFilter或者是子类\n\n`最终环节`的target，GPUImageView 用于显示到屏幕上或者GPUImageMovieWriter写成视频文件。\n\n主要分为三个环节：\n`source(视频，图片源) =\u003e filter(滤镜) =\u003e final target(处理后的视频、图片)`\n\n###3.3 美颜原理\n`磨皮(GPUImageBilateralFilter)`：本质就是让像素点模糊，可以使用高斯模糊，但是可能导致边缘会不清晰，用双边滤波(Bilateral Filter) ，有针对性的模糊像素点，能保证边缘不被模糊。\n\n`美白(GPUImageBrightnessFilter)`：本质就是提高亮度。\r\n\n###3.4 GPUImage源对象\nGPUImage的数据源只能是4类：\n\n`GPUImageVideoCamera` ios摄像头的实时美颜。GPUImageVideoCamera是GPUImageOutput的子类，提供来自摄像头的图像数据作为源数据，一般是响应链的源头。\n\n`GPUImageStillCamera` 相机拍照\n\n`GPUImagePicture` 处理静止图像\n\n`GPUImageMovie` 电影 \n\n###3.5 用法\n\u003e1. 创建过滤器\n\u003e2. 创建源对象\n\u003e3. 把过滤器添加到源对象\n\u003e4. 生成target\n\n静态图片处理：\n\n```\nUIImage *inputImage = [UIImage imageNamed:@\"105\"];\n    \n// 创建过滤器\nGPUImageBrightnessFilter *filter = [[GPUImageBrightnessFilter alloc] init];\nfilter.brightness = 0.5;\n[filter forceProcessingAtSize:inputImage.size];\n[filter useNextFrameForImageCapture]; // 告诉系统从后来捕获过滤器\n    \n// 处理静止的图像\nGPUImagePicture *stillPic = [[GPUImagePicture alloc] initWithImage:inputImage];\n[stillPic addTarget:filter]; //添加过滤器\n[stillPic processImage]; // 执行渲染\n    \nUIImage *newImage = [filter imageFromCurrentFramebuffer];\n    \nUIImageView *imageView = [[UIImageView alloc] initWithImage:newImage];\n[imageView sizeToFit];\n[self.view addSubview:imageView];\n imageView.center = CGPointMake(CGRectGetWidth(self.view.frame)/2, CGRectGetHeight(self.view.frame)/2);\n```\n\n实时美颜处理:\n\n```\n// 创建视频源\nGPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetHigh cameraPosition:AVCaptureDevicePositionBack];\n// 设置方向\nvideoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;\n    \n// 创建预览View\nGPUImageView *videoPreview = [[GPUImageView alloc] initWithFrame:self.view.bounds];\n[self.view insertSubview:videoPreview atIndex:0];\n    \n // 添加预览图层到源\nGPUImageBeautifyFilter *fiter = [[GPUImageBeautifyFilter alloc] init];\n[_videoCamera addTarget:fiter];\n[fiter addTarget:self.videoPreview];\n// 开始采集视频\n[videoCamera startCameraCapture];\n\n```\n\n到这里，仅仅是屏幕显示的内容有滤镜效果，而作为直播应用，还需要输出带有美颜效果的视频流。\n我们需要使用`GPUImageMovieWriter`类，才能处理视频流。\n\n核心思路：\n\n通过GPUImageVideoCamera采集视频和音频信息，音频信息直接发送给`GPUImageMovieWriter`，视频信息传入响应链作为源头，渲染后的视频再写入`GPUImageMovieWriter`，同时通过`GPUImageView`显示在屏幕上。只需要`addTarget`就可以添加`GPUImageMovieWriter`;\n\n![](GPUImageMovieWriter.png)\n\n\n###3.6 实例代码在这里\n[美颜的使用](https://github.com/tiantianlan/LiveExplanation/tree/master/GPUImage)\n\n##4. 音视频编码，解码\n这一章太难了，以后再写。\n### VideoToolBox\n### AudioToolBox\n\n##5. 流媒体服务器\n国内外有很多好用的流媒体服务区。这里为了方便搭建我们采用`nginx+RTMP`搭建流媒体服务器。\n###5.1 MAC环境搭建\n[MACOS上搭建nginx+rtmp环境](https://github.com/tiantianlan/LiveExplanation/blob/master/MACOS%E4%B8%8A%E6%90%AD%E5%BB%BAnginx%2Brtmp%E7%8E%AF%E5%A2%83.md)\n###5.2 Centos环境搭建\n[Centos下搭建的nginx + RTMP环境](https://github.com/tiantianlan/LiveExplanation/blob/master/Centos%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md)\n###5.3 服务端常用技术\n`CDN` 直播数据上传到服务器后，观看直播的人比较多，服务器是承受不了的，会将数据分发到CDN，观众直接去CDN获取数据。减少服务器的负载。\n\n`负载均衡` 由多台服务器组成一个服务器集群，每次请求的时候，会根据服务器负载选择处理请求的服务器。\n##6. 推流\n###6.1 推流协议的选择\n[HLS和RTMP](https://github.com/tiantianlan/LiveExplanation/blob/master/%E7%9B%B4%E6%92%AD%E5%8D%8F%E8%AE%AE%E7%9A%84%E9%80%89%E6%8B%A9.md)\n###6.2 推流原理\n在iOS设备上进行各推流的话，是通过AVCaptureSession这么一个捕捉会话，指定两个AVCaptureDevice 也就是iOS的摄像头和麦克风，获取个原始视频和音频，然后需要进行个H.264的视频编码和AAC的音频编码，再将编码后的数据整合成一个音视频包，通过rmtp推送到nginx服务器\r\n\n###6.3 librtmp\n这个参考资料很少。不过大部分都采用的这个。\n因为涉及太多C/C++这里不讨论。\n\n参考:[https://my.oschina.net/jerikc/blog/501948](https://my.oschina.net/jerikc/blog/501948)\n\n\n##7. 音视频播放\n###7.1 播放框架的选择\niOS的播放框架主要有以下三种：\n\u003e1. AVPlayer 可以播放本地、远程视频，可以自定义UI进行控制\n\u003e2. AVPlayerViewController 自带播放控制UI，不能自定义UI\n\u003e3. MPMoviePlayerController，MPMoviePlayerViewController (iOS9后弃用)\n\n如果只是简单的播放视频，选择`AVPlayerViewController`，如果想自定义播放器，选择`AVPlayer`。\n\n###7.2 AVPlayer\nAVPlayer是一个用来播放基于时间的流媒体控制对象。支持播放从本地、分布下载或通过HTTP Live Streaming协议得到的流媒体。\nAVPlayer只管理一个单独资源的播放，不过框架还提供了AVPlayer的一个子类AVQueuePlayer，可以用来管理一个资源队列。当你需要在一个序列中播放多个条目或者为音频、视频资源设置播放循环时可以使用该子类。\n\nAVPlayer视频播放使用步骤：\n\u003e1. 创建视频资源地址URL，可以是网络URL\n\u003e2. 通过URL创建视频内容对象`AVPlayerItem`,一个视频对应一个`AVPlayerItem`\n\u003e3. 创建`AVPlayer`视频播放对象，需要一个`AVPlayerItem`进行初始化\n\u003e4. 创建`AVPlayerLayer`播放图层对象，添加到现实视图上去\n\u003e5. 添加KVO监听。 监听到AVPlayerItemStatusReadyToPlay的时候调用play方法\n\n[AVPlayer的使用](https://github.com/tiantianlan/LiveExplanation/tree/master/AVPlayer)\n###7.3 AVPlayerViewController\n`AVPlayerViewController`属于`AV Kit`,它是`UIViewController的子类`，用于展示并控制AVPlayer实例的播放。\n\n`AVPlayerViewController `类使用步骤\n\u003e1. 创建URL\n\u003e2. 创建AVPlayerViewController，并根据URL设置player属性\n\u003e3. 调用play方法\n\n[AVPlayerViewController的使用](https://github.com/tiantianlan/LiveExplanation/tree/master/AVKit) \n\nXcode8模拟器可能有问题，打开播放不了。\n###8.开源框架\n前面所讲都有第三方框架支持。采集、美颜、推流有`LFLiveKit`, 拉流播放有`IJKMediaFramework`。\n\n[`LFLiveKit`](https://github.com/LaiFengiOS/LFLiveKit) : LFLiveKit是iOS版开源RTMP流SDK。他支持后台录制、美颜功能、支持h264、AAC硬编码，动态改变速率，RTMP传输等\n\n[`IJKMediaFramework`](https://github.com/Bilibili/ijkplayer) : ijkplayer是B站开源的一款视频直播框架，它是基于ffmpeg。 如果从github下载是需要编译。参考地址:[http://www.jianshu.com/p/1f06b27b3ac0](http://www.jianshu.com/p/1f06b27b3ac0)\n\n个人实验只需要配置Nginx+RTMP服务\n这里我采用这两个第三方框架写了一个直播，包含在线观看直播，和直播推流,支持在线美颜，前后摄像头切换等你需要:\n\n\u003e1. 搭建`Nginx+RTMP环境`:[MAC](https://github.com/tiantianlan/LiveExplanation/blob/master/MACOS%E4%B8%8A%E6%90%AD%E5%BB%BAnginx%2Brtmp%E7%8E%AF%E5%A2%83.md) 或者  [Centos](https://github.com/tiantianlan/LiveExplanation/blob/master/Centos%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md)\n\u003e2. 下载项目真机运行。[https://github.com/tiantianlan/miaoboDemo](https://github.com/tiantianlan/miaoboDemo)\n登陆界面\n![](miaobo1.png)\n\n主页\n![](miaobo2.png)\n\n直播页面\n![](miaobo3.png)\n\n\n\n\n\n\n参考文章：\n\u003e1. [知乎 如何搭建一个完整的直播系统](https://www.zhihu.com/question/42162310)\n\n\u003e2. [袁峥技术博客](http://www.jianshu.com/users/b09c3959ab3b/latest_articles)\n\n\u003e3. [GPUImage 基于GPU加速的图形和视频处理库](http://www.ios122.com/2015/08/gpuimage/)\n\u003e\n\u003e4. [iOS中为直播APP集成美颜功能](http://news.h5.com.cn/ios/49696.html)\n\u003e\n\u003e5. [iOS视频直播~推流、拉流原理](http://www.jianshu.com/p/e516a5b31480)\n\u003e\n\u003e6. [关于视频直播技术，你想要知道的都在这里了](http://www.jianshu.com/p/ddb640ac4fec)\n\u003e\n\u003e7. [macOS上搭建Nginx+rtmp服务器](https://www.xsd.me/mac_nginx_rtmp_server.html)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguoxiaopang%2FLiveExplanation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguoxiaopang%2FLiveExplanation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguoxiaopang%2FLiveExplanation/lists"}