├── README.md ├── WebRTC 零基础开发者教程(中文).pdf ├── article ├── 001-WebRTC 发送方码率预估实现解析.md ├── 002-码率控制基本概念.md ├── 003-Speex回声消除代码分析.md ├── 004-房间声学原理与Schroeder混响算法实现.md ├── 005-H264系列--压缩编码技术.md ├── 006-RTSP 媒体协议流的录制方案及其覆盖策略详解.md ├── 007-webrtc建立连接之ICE框架.md ├── 008-流媒体协议介绍.md ├── 009-音视频同步原理及实现.md ├── 010-直播概念和流程框架.md ├── 011-CDN在直播中的运用.md ├── 012-常见音视频编码格式.md ├── 013-H.264官方软件JM源代码分析-编码器lencod.md ├── 014-H.264官方软件JM源代码分析-解码器ldecod.md ├── 015-Android 音视频技术.md ├── 016-Web前端WebRTC攻略-媒体协商与SDP简析.md ├── 017-基于FFmpeg的AVfilter的例子-纯净版.md ├── 018-WebRTC 传输安全机制第二话:深入显出 SRTP 协议.md ├── 019-WebRTC能给我带来什么?.md ├── 020-视音频数据处理:RGB、YUV像素数据处理.md ├── 021-视音频数据处理:PCM音频采样数据处理.md ├── 022-视音频数据处理:H.264视频码流解析.md ├── 023-视音频数据处理:AAC音频码流解析.md ├── 024-视音频数据处理:FLV封装格式解析.md ├── 025-视音频数据处理:UDP-RTP协议解析.md ├── 026-如何生成mp4文件.md ├── 027-ffmpeg滤镜的基本使用.md ├── 028-webRTC是如何实现音视频的录制.md ├── 029-音视频同步算法.md ├── 030-房间声学原理与Schroeder混响算法实现.md ├── 031-一个频域语音降噪算法实现及改进方法.md ├── 032-HEVC官方软件HM源代码分析-编码器TAppEncoder.md ├── 033-HEVC官方软件HM源代码分析-解码器TAppDecoder.md ├── 034-音视频编解码常用知识点.md ├── 035-微信小程序集成实时音视频通话功能.md ├── 036-视音频编解码技术零基础学习方法.md ├── 037-RTSP协议学习.md ├── 038-HEVC码流分析.md ├── 039-H.264简单码流分析.md ├── 040-MPEG2简单码流分析.md ├── 041-视频码流分析工具.md ├── 042-H.264分析器.md ├── 043-FFmpeg架构之IO模块分析.md ├── 044-[Video and Audio Data Processing] UDP-RTP协议解析.md ├── 045-RTSP协议实例分析.md ├── 046-RTSP协议之TCP或UDP问题.md ├── 047-ffplay工具命令使用技巧.md ├── 048-VLC RTSP网络串流播放失败.md ├── 049-RTMP协议详解.md └── 050-STUN 原理理解.md ├── books ├── 002-视频图像处理与性能优化.pdf ├── 003-数字图像与视频处理.pdf ├── 005-音视频开发进阶指南:基于Android与iOS平台的实践.pdf ├── 006-Real-Time Communication with WebRTC.pdf ├── 007-FFMPEG - From Zero to Hero.pdf ├── 008-Learning WebRTC.pdf ├── 010-FFmpeg Basics 2012.pdf └── README.md ├── case_interview ├── 001-README.md └── 002-README.md ├── paper ├── README.md ├── ffmpeg │ ├── H264 │ │ ├── 01594776.pdf │ │ ├── 10.1.1.669.484.pdf │ │ ├── 133112608024.pdf │ │ ├── 2013-ole-h264.pdf │ │ ├── 8116ijma03.pdf │ │ ├── A2126065115.pdf │ │ ├── AVC_overview_1.pdf │ │ ├── H.264-vs-H.265.pdf │ │ ├── H_264_Video_Frame_Size_prediction.pdf │ │ ├── JRTIP-2014-Real-time H264-AVCEncoderBasedOnEnhancedFrameLevelParallelismForSmartMulticoreDSPCamera.pdf │ │ ├── MMCN09-QoE.pdf │ │ ├── README.md │ │ ├── Tutorial_H264_MPEG4.pdf │ │ ├── cr1077.pdf │ │ ├── h264-AVC-Standard.pdf │ │ ├── nenciIROS14.pdf │ │ ├── parallel_scalability_of_h264.pdf │ │ ├── spie04-h264OverviewPaper.pdf │ │ └── swseo-samos09.pdf │ ├── README.md │ ├── Research on Audio-Video Codec Based on Android.PDF │ ├── aac │ │ ├── RANDOL AAC Pumpcell Paper.pdf │ │ ├── README.md │ │ └── chi2020_talkingboogie_paper.pdf │ ├── cloud-computing-quicksync-video-ffmpeg-white-paper.pdf │ ├── ffplay │ │ ├── 1709.pdf │ │ ├── 97_Andrew_Weaver_SP.pdf │ │ ├── README.md │ │ ├── iPres2019_paper_97.pdf │ │ ├── j-kaneda18mastersthesis-FunctionReallocationMec.pdf │ │ └── nsdi18-fouladi.pdf │ ├── fuzzeval.pdf │ ├── gg-paper.pdf │ ├── iPres2019_paper_97.pdf │ ├── mm19-miniview-comp.pdf │ └── sec20fall_jiang_prepub.pdf └── streaming_media │ ├── HLS │ ├── 1524_a_survey_and_evaluation_of_fpga_highlevel_synthesis_tools.pdf │ ├── Centrifuge_ICCAD.pdf │ ├── FPGA2021.pdf │ ├── README.md │ └── mantovani_cicc20.pdf │ ├── README.md │ ├── RTSP │ ├── 10.1.1.124.9084.pdf │ ├── 11647.pdf │ ├── 2014-Q2SWinet-Offload-Perf-Evaluation-Migault.pdf │ ├── 2019-07.pdf │ ├── 2b14328e0c0a65c14b2a307459f69a8c.DREAM A Data Streaming Application Using RTP RTSP in a Local Area Network.pdf │ ├── A201111-706_1322795059.pdf │ ├── FinalPaperA Survey on open Source Protocols SIP, RTP, RTCP, RTSP, H.264 for Video Conferencing System191361.pdf │ ├── MSE2002.pdf │ ├── PROMS2000.pdf │ ├── README.md │ ├── RTSP-live-streaming.pdf │ ├── cache.pdf │ ├── rossi06eurongi.pdf │ ├── rtp.pdf │ ├── rtsp.pdf │ ├── wp529-som-benchmarks.pdf │ └── wu01streaming.pdf │ └── rtmp │ ├── 2011-AES.pdf │ ├── 22-1.pdf │ ├── 32-_Fauzan_Masykur.pdf │ ├── 360_video_IFIP.pdf │ ├── 76533338.pdf │ ├── Cloud-Ingest-of-Live-Video-An-open-approach-to-RIST-SRT-and-retransmission-protocols.pdf │ ├── E3_Management_of_Traffic_During_Construction_v1.3.pdf │ ├── README.md │ ├── cired2005_0127.pdf │ ├── hockxyu-44.pdf │ ├── hotnets17-final39.pdf │ ├── imc2018.pdf │ ├── periscope-imc16.pdf │ └── symmetry-12-01491.pdf ├── practice_project ├── README.md ├── project_02.md ├── project_03.md └── project_04.md ├── protocol ├── Connection-rfc1242.txt.pdf ├── Gopher-rfc4266.txt.pdf ├── HTTP-rfc2068.txt.pdf ├── HTTP-rfc2069.txt.pdf ├── HTTP-rfc2227.txt.pdf ├── HTTP-rfc2518.txt.pdf ├── HTTP-rfc2585.txt.pdf ├── HTTP-rfc2817.txt.pdf ├── README.md ├── RTCP-rfc1889.txt.pdf ├── RTCP-rfc3556.txt.pdf ├── RTCP-rfc3605.txt.pdf ├── RTCP-rfc3611.txt.pdf ├── RTCP-rfc4571.txt.pdf ├── RTCP-rfc4585.txt.pdf ├── RTCP-rfc4588.txt.pdf ├── RTCP-rfc5124.txt.pdf ├── RTCP-rfc5506.txt.pdf ├── RTCP-rfc5725.txt.pdf ├── RTCP-rfc5760.txt.pdf ├── RTCP-rfc5764.txt.pdf ├── RTCP-rfc6035.txt.pdf ├── RTCP-rfc6051.txt.pdf ├── RTCP-rfc6128.txt.pdf ├── RTCP-rfc6222.txt.pdf ├── RTCP-rfc6263.txt.pdf ├── RTCP-rfc6332.txt.pdf ├── RTCP-rfc6642.txt.pdf ├── RTP-rfc1889.txt.pdf ├── RTP-rfc1890.txt.pdf ├── RTP-rfc2029.txt.pdf ├── RTP-rfc2035.txt.pdf ├── RTP-rfc2038.txt.pdf ├── RTP-rfc2198.txt.pdf ├── RTP-rfc2250.txt.pdf ├── RTP-rfc2429.txt.pdf ├── RTP-rfc2431.txt.pdf ├── RTP-rfc2435.txt.pdf ├── RTP-rfc2508.txt.pdf ├── RTP-rfc2733.txt.pdf ├── RTP-rfc2793.txt.pdf ├── RTP-rfc2833.txt.pdf ├── RTP-rfc2862.txt.pdf ├── RTP-rfc3016.txt.pdf ├── RTP-rfc3047.txt.pdf ├── RTSP-rfc2326.txt.pdf ├── RTSP-rfc4567.txt.pdf ├── RTSP-rfc7825.txt.pdf ├── RTSP-rfc7826.txt.pdf ├── RTSP-rfc8866.pdf ├── SDP-rfc2327.txt.pdf ├── SDP-rfc2848.txt.pdf ├── SDP-rfc3107.txt.pdf ├── SDP-rfc3264.txt.pdf ├── SDP-rfc3266.txt.pdf ├── SDP-rfc3388.txt.pdf ├── SDP-rfc3407.txt.pdf ├── SDP-rfc3485.txt.pdf ├── SDP-rfc3524.txt.pdf ├── SDP-rfc3556.txt.pdf ├── SDP-rfc3611.txt.pdf ├── SDP-rfc3890.txt.pdf ├── SDP-rfc4091.txt.pdf ├── SDP-rfc4145.txt.pdf ├── SDP-rfc4298.txt.pdf ├── SDP-rfc4566.txt.pdf ├── SDP-rfc4567.txt.pdf ├── TCP-rfc1001.txt.pdf ├── TCP-rfc1002.txt.pdf ├── TCP-rfc793.txt.pdf ├── UDP-rfc1001.txt.pdf ├── UDP-rfc1002.txt.pdf ├── UDP-rfc2013.txt.pdf ├── UDP-rfc2147.txt.pdf ├── UDP-rfc2508.txt.pdf ├── UDP-rfc3489.txt.pdf ├── UDP-rfc3519.txt.pdf ├── UDP-rfc3828.txt.pdf ├── UDP-rfc3948.txt.pdf ├── UDP-rfc4019.txt.pdf ├── UDP-rfc4113.txt.pdf └── UDP-rfc768.txt.pdf ├── video └── README.md └── 音视频流媒体开发知识归纳导图.png /WebRTC 零基础开发者教程(中文).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/WebRTC 零基础开发者教程(中文).pdf -------------------------------------------------------------------------------- /article/001-WebRTC 发送方码率预估实现解析.md: -------------------------------------------------------------------------------- 1 | # WebRTC 发送方码率预估实现解析 2 | 3 |
WebRTC使用的是Google Congestion Control (简称GCC)拥塞控制,目前有两种实现:
4 | * 旧的实现是接收方根据收到的音视频RTP报文, 预估码率,并使用REMB RTCP报文反馈回发送方。 5 | * 新的实现是在发送方根据接收方反馈的TransportFeedback RTCP报文,预估码率。 6 | 7 | ## 基于延迟的拥塞控制原理 8 | 9 | 先来看下Google Congestion Control(GCC)的标准草案:https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02 10 | 结合草案,可以得知GCC是基于网络延迟梯度的拥塞控制算法,判断的依据如下图: 11 |
12 | 13 |
14 | 网络延迟梯度的拥塞控制算法 15 |
16 | 17 | 20 | 21 | 发送方发送两个包组的间隔为 : Ds = Ts1 - Ts0 22 | 23 | 接收方接收两个包组的间隔为: Dr = Tr1 - Tr0 24 | 25 | 如果网络没有任何抖动,那么 [ delta = Dr - Ds ] 应该是一个恒定不变的值,但是现实中网络有抖动、拥塞、丢包等情况,所以delta也是一个抖动的值。 26 | GCC通过测量delta,来判断当前网络的使用情况,分为 OverUse (过载),Normal(正常),UnderUse(轻载) 这三种情况。 27 | 有同学可能会问,发送方和接收方时钟不统一,怎么计算差值呢,需要做时间对齐或者NTP同步吗? 28 | 29 | 不需要,因为我们对比的是delta,只需要单位一致即可,举个例子: 30 | seq1的包 发送时间为 16000ms(发送方时钟),接收时间为 900ms(接收方时钟) 31 | seq2的包 发送时间为 16001ms(发送方时钟),接收时间为 905ms(接收方时钟) 32 | 那么延迟梯度delta=(905-900) - (16001-16000) = 4 33 | 34 | ## Pacing和包组 35 | 36 | 值得注意的是,延迟梯度的判断是以包组为单位的,而且必须在发送方开启pacing发送, 有以下几点原因: 37 | 单个包测量误差会过大,基于包组的测量更能反应网络的情况。 38 | 39 | burst发送容易冲击网络,影响测量的精度。 40 | 那么怎么判断哪几个包属于一个包组呢,非常简单,按发送方的pacing速率分包组。 41 | WebRTC pacing默认是5ms一个包组,如下图所示 42 |
43 | 44 |
45 | WebRTC pacing 46 |
47 | 48 | 51 | 52 | ## TransportFeedback RTCP报文 53 | 54 | 再来看看transport-feedback的包结构:https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01 55 |
56 | 57 |
58 | transport-feedback的包结构 59 |
60 | 61 | 64 | 65 | 解析这个报文,我们可以得到下面的信息: 66 | 67 | * 接收到的包seq和包的接收时间 68 | * 丢失的包seq 69 | * 可以看到本质上transport-feedback是接收方对数据的ACK,并且捎带了接收的延迟梯度。 70 | 71 | ## 发送方码率预估 72 | 73 | 收到transport-feedback报文后需要怎么处理,结合GCC的算法来看,分为以下几步: 74 | 1.计算接收方ack了多少个字节, 统计在采样的时间窗口内接收方的接收速率 75 | 看看GCC怎么说: 76 |
77 | 78 |
79 | 采样的时间窗口内接收方的接收速率 80 |
81 | 82 | 85 | 86 | 按照这个算法实现acked_bitrate_estimate,可以计算出接收方在当前时间窗口内的接收速率。 87 | 88 | 2.将包按包组归类, 计算包组的发送时间 接收时间的差值 89 | 在前面的【Pacing和包组】中已经讲过,这里不再赘述 90 | 91 | 3.按包组的delta, 进行网络状态评估 92 | GCC的标准草案里面使用的是卡尔曼滤波器(接收方评估),发送方评估默认的实现是Trendline Filter。 93 | 94 | 基本的原理是最小二乘法, 将多个时间点的网络抖动(delta)拟合成一条直线,如下图所示: 95 |
96 | 97 |
98 | 网络抖动 99 |
100 | 101 | 104 | 105 | 根据直线斜率的变化趋势判断网络的负载情况。 106 | 上面已经得到了包组的delta,对delta做平滑计算后,按照(时间点, 平滑后的delta), 可以在坐标系上绘制出散点图,使用最小二乘法拟合出delta随时间变化的直线,根据直线斜率计算出变化趋势。 107 | 来看看GCC里面的说法: 108 |
109 | 110 |
111 | 平滑 112 |
113 | 114 | 117 | 118 | * m(i)为i时刻的包组delta,del_var_th(i)为当前判断是否过载的门槛 119 | * m(i) < -del_var_th(i),判断为under-use(低载) 120 | * m(i) > del_var_th(i) 且持续至少overuse_time_th时长,判断为over-use(过载) 121 | 122 |
123 | 124 |
125 | 过载的门槛 126 |
127 | 128 | 131 | 132 | del_var_th必须动态调整,不然可能会在跟TCP的竞争中被饿死(出于公平性考虑)。过大的del_var_th会对延迟变化不敏感,过小的del_var_th则会过于敏感,抖动容易被频繁误判为过载,必须动态调整,才能和基于丢包的连接(比如TCP)竞争。 133 | 134 | ## 根据探测的网络情况, 预估码率 135 | 136 | 总体的思想是根据当前接收方的接收码率,结合当前的网络负载情况,进行AIMD码率调整: 137 | * 在接近收敛前,使用乘性增,接近收敛时使,用加性增。 138 | * 当网络过载时,使用乘性减。 139 | 140 |
141 | 142 |
143 | AIMD码率调整 144 |
145 | 146 | 149 | 150 | 151 | 152 | 在Decrease状态下,会不停的计算平均最大码率(average max bitrate),当前预估码率和平均最大码率差值在3个标准差以内时,进行乘性增,否则进行加性增。如果包到达速率超过了平均最大码率的3个标准差,那么需要重新计算平均最大码率。 153 | 154 |
155 | 156 |
157 | 重新计算平均最大码率 158 |
159 | 160 | 163 | 164 | 乘性增期间,每秒最多增加8%的码率 165 | 166 |
167 | 168 |
169 | 增加8%的码率 170 |
171 | 172 | 175 | 176 | 加性增期间,每个rtt增加“半个”包大小 177 |
178 | 179 |
180 | 半个 181 |
182 | 183 | 186 | 187 | 评估出的码率不能超过接收速率的1.5倍 188 |
189 | 190 |
191 | 速率的1.5倍 192 |
193 | 194 | 197 | 198 | 当探测到网络过载时,按照0.85的速率降低码率 199 | 200 | ## 发送方码率预估的算法流程 201 | 将上面的几步结合起来,可以得到一个大致的算法框架 202 | ```C++ 203 | struct FeedbackResultVector { 204 | int64_t send_time_ms; // 包发送时间(发送时记录) 205 | int64_t recv_time_ms; // 包接收时间(从TransportFeedback RTCP报文解析得到) 206 | int packet_size; // 包大小 207 | }; 208 | 209 | // 解析TransportFeedback RTCP报文 210 | FeedbackResultVector feedback_result_vec = 211 | TransportFeedbackRtcp.Parse(rtcp_feedback); 212 | // 遍历每个包, 进行处理 213 | for (feedback_result : feedback_result_vec) { 214 | double delta = 0.0; 215 | // 计算ack速率(接收方接收速率) 216 | AckBitrateEstimate.Update(now_ms, feedback_result.packet_size); 217 | // 把接收反馈包按照包组分类,计算包组delta 218 | bool compute_delta = PacketGroup.AddPacket(feedback_result, delta); 219 | if (comupute_delta) { 220 | // 探测网络状态 221 | TrendLineFilter.Detect(delta); 222 | // 根据GCC状态机,进行AIMD码率调整 223 | AimdRateControl.Update( 224 | TrendLineFilter.NetState(), 225 | AckBitrateEstimate.Bitrate() 226 | ); 227 | } 228 | } 229 | ``` 230 | 231 | Reference: 232 |
233 | WebRTC研究:包组时间差计算-InterArrival: https://blog.jianchihu.net/webrtc-research-interarrival.html 234 |
235 | WebRTC研究:Trendline滤波器-TrendlineEstimator: https://blog.jianchihu.net/webrtc-research-trendlineestimator.html 236 |
237 | 袁荣喜的一个GCC C语言的实现: https://github.com/yuanrongxi/razor 238 | 239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 | 254 | 文章原作者:腾讯云音视频 255 | -------------------------------------------------------------------------------- /article/002-码率控制基本概念.md: -------------------------------------------------------------------------------- 1 | # 码率控制基本概念 2 | 3 | 码率控制是指视频编码中决定输出码率的过程。首先介绍一下 X264 中使用到的与码率控制相关的几个概念: 4 | 5 | CQP(Constant QP) 恒 定QP(Quantization Parameter),追求量化失真的恒定,瞬时码率会随场景 复杂度而波动,该模式基本被淘汰(被 CRF 取代),只有用”-pq 0”来进行无损编码还有价值。 6 | 7 | CRF(Constant Rate Factor),恒定质量因子,与恒定 QP 类似,但追求主观感知到的质量恒定,瞬时码率也 会随场景复杂度波动。对于快速运动或细节丰富的场景会适当增大量化失真(因为人眼不易注意到),反之 对于静止或平坦区域则减少量化失真。 8 | 9 | ABR(Average Bitrate),平均码率,追求整个文件的码率平均达到指定值(对于流媒体有何特殊之处?)。瞬时码率也会随着场景复杂度波动,但最终要受平均值的约束。 10 | 11 | CBR(Constant Bitrate),恒定码率。前面几个模式都属于可变码率(瞬时码率在波动),即VBR(Variable Bitrate);恒定码率与之相对,即码率保持不变。 12 | 13 | x264 并没有直接提供 CBR 这种模式,但可以通过在 VBR 模式的基础上做进一步限制来达到恒定码率的目标。CRF 和 ABR 模式都能通过--vbv-maxrate --vbv-bufsize来限制码率波动。 14 | 15 | >关于这几个概念的参考如下: 16 | 17 | + 1.Waht are CBR,VBV and CPB? 18 | + 2.FFmpeg and H.264 Encoding Guide 19 | + 3.CRF Guide(Constant Rate Factor in X264 and X265) 20 | + 4.MeGUI/x264 setting 21 | 22 | ## X264 中码率控制 23 | 24 | X264 中对于码率控制方法有三种:X264_RC_CQP、X264_RC_CRF、X264_RC_ABR。默认情况是选择 CRF 方法,设置是在 x264_param_default函数里设置的 25 | 26 | ```C++ 27 | param->rc.i_rc_method = X264_RC_CRF; 28 | param->rc.f_rf_constant = 23; 29 | ``` 30 | 31 | 关于这三种方法,网上有提到优先级是ABR>CQP>CRF的,但分析 X264 的源码,并没有看出有优先级顺序,关于码率控制方法的设置代码如下: 32 | 33 | ```C++ 34 | OPT("bitrate") 35 | { 36 | p->rc.i_bitrate = atoi(value); 37 | p->rc.i_rc_method = X264_RC_ABR; 38 | } 39 | OPT2("qp", "qp_constant") 40 | { 41 | p->rc.i_qp_constant = atoi(value); 42 | p->rc.i_rc_method = X264_RC_CQP; 43 | } 44 | OPT("crf") 45 | { 46 | p->rc.f_rf_constant = atof(value); 47 | p->rc.i_rc_method = X264_RC_CRF; 48 | } 49 | ``` 50 | 51 | ## X264 中关于 QP设置 52 | 首先看一段 X264 中关于 QP 值的代码,该段代码在x264_ratecontrol_new: 53 | 54 | ```C++ 55 | rc->ip_offset = 6.0 * log2f(h->param.rc.f_ip_factor); 56 | rc->pb_offset = 6.0 * log2f(h->param.rc.f_pb_factor); 57 | rc->qp_constant[SLICE_TYPE_P] = h->param.rc.i_qp_constant; 58 | rc->qp_constant[SLICE_TYPE_I] = x264_clip3(h->param.rc.i_qp_constant - rc->ip_offset + 0.5, 0, QP_MAX); 59 | rc->qp_constant[SLICE_TYPE_B] = x264_clip3(h->param.rc.i_qp_constant + rc->pb_offset + 0.5, 0, QP_MAX); 60 | ``` 61 | 62 | 从上面的代码可以看出,默认的i_qp_constant或者通过命令行传入的qp qp_constant实际设置的是 P 帧的 QP。I 帧和 B 帧的 QP 设置是根据f_ip_factor f_pb_factor计算得到。 63 | 64 | 在研究编码算法的时候,一般会选用 CQP 方法,设定 QP 为 24、28、32、36、40等(一般选 4 个 QP 值),然后比较算法优劣。在 X264 中,关于QPmin、QPmax、QPstep的默认设置如下: 65 | 66 | ```C++ 67 | param->rc.i_qp_min = 0; 68 | param->rc.i_qp_max = QP_MAX; 69 | param->rc.i_qp_step = 4; 70 | ``` 71 | 72 |

QPmin,默认值:

0. 定义 X264 可以使用的最小量化值,量化值越小,输出视频质量越好。当 QP 小于某一个值后, 编码输出的宏块质量与原始块极为相近,此时没必要继续降低 QP。如果开启了自适应量化器(默认开启),不建议 提高 QPmin 的值,因为这会降低平滑背景区域的视觉质量。 73 | 74 |

QPmax,默认值:

51. 定义 X264 可以使用的最大量化值。默认值 51 是 H.264 规格中可供使用的最大量化值。如果 想要控制 X264 输出的最低品质,可以将此值设置的小一些。QPmin 和 QPmax 在CRF,ABR方法下是有效的,过低的设置 QPmax,可能造成 ABR 码率控制失败。不建议调整该参数。 75 | 76 |

QPstep,默认值:

4.设置两帧间量化值的最大变化幅度。 77 | 78 | . 比较三种码率控制方式如下: 79 | 80 | 码率控制方法| 视觉质量稳定性 | 即时输出码率 | 输出文件大小 81 | :--|:--|:--|:-- 82 | CBR |不稳定| 恒定| 可控 83 | VBR |稳定 |变化 |不可控 84 | ABR |基本稳定| 变化| 可控 85 | 86 | 帧间 QP 变化,帧内宏块 QP 不变,输出码率未知,各帧输出视觉质量有变化(高 QP 低码率的情况下会更明显)。 87 | 88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | 99 | 文章原作者: 音视频开发训练营 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /article/003-Speex回声消除代码分析.md: -------------------------------------------------------------------------------- 1 | # Speex回声消除代码分析 2 | 3 | 首先说明,这里的代码流程是修改过的Speex流程,但与Speex代码差异不大,应该不影响阅读。 4 | 5 | 1. 用RemoveDCoffset函数进行去直流 6 | 2. 远端信号预加重后放入x[i+frame_size],近端信号预加重后放入input缓冲区 7 | 3. 前M-1帧的远端频域信号移位,为当前帧频域信号腾出空间 8 | 4. 用spx_fft函数进行FFT变换,变换后的系数存在X中 9 | 5. 计算当前远端信号当前帧的方差Sxx。(去直流操作后,意味着均值可以视为零) 10 | 6. 当前远端时域信号移位,x[i] = x[i+frame_size],50%交叠处理 11 | 7. 先用前景滤波器进行滤波 12 | 8. 将滤器后的输出进行反变换 13 | 9. 再用近端信号(麦克风语音)减去滤波输出得到前景滤波器的语差信号 14 | 10. 再用mdf_inner_prod计算前景滤后器的时域误差总功率 15 | 11. 使用mdf_adjust_prop计算背景滤波器各段(每个)相同频率的系数幅度,存放在prop( 这里进行了归一化处理)。 16 | 12. 用weighted_spectral_mul_conj函数计算背景滤波器频域系数需要调整多少量,(其思想为LMS:步长乘以W*E) 17 | 18 | >power_1 最优步长因子,具体可以参考Mader算法,与prop相乘得到最化步长
19 | >prop 为归一化后的背景滤波器各频率的系数幅度。与power_1相乘
20 | >X 为转换到频域后的远端信号
21 | >E 为转换到频域后的误差信号
22 | >PHI 输出结果:背景滤波器频域系数需要调整的量 23 | 24 | 13. 背景滤波器频域系数调整:W[j*N+i] = W[j*N+i]+ PHI[i] 25 | 14. 防止循环卷积处理(重叠保留法变循环卷积为线性卷积,把相关块的FFT系数中的后半部分置为零) 26 | 15. 根据调整后的背景滤波器对远端信号再进行频域滤波处理 27 | 16. 滤波后的输出进行反变换,存到y 28 | 17. 在时域计算两次滤波之间的误差及两次滤波之间误差的功率Dbf 29 | 18. 在时域计算背景滤波器滤波后的误差和背景滤波器滤波后误差的方差See。至此我们得到: 30 | 31 | >Sff 前景滤波后的方差
32 | >See 背景滤波后的方差
33 | >Dbf 前景滤波与背景滤波之间方差 34 | 35 | 19. 利用Sff、See、Dbf来计算是否需要更新前景滤波器(总的来说,当Dbf值比较大时需要更新前景滤波器系数) 36 | 20. 如果需要更新前景滤波器系数则把背景滤波器系数给前景滤波器,并平滑背景滤波后的时域误差(前景滤波时域语差加上背景滤波器的时域输出),得到总误差,存在e[i+frame_size],这里的总误差,就是总的远端信号回声。 37 | 21. 根据总的远端信号回声,计算回声消除后的语音信号,并进行预加重处理,存在out输出缓冲区中(这个步骤是我自己调的) 38 | 22. 误差缓冲区移动,为下一帧处理做好准备 39 | 23. 计算背景滤波器输出与误差的互相关Sey、背景滤波器的自相关Syy 40 | 24. 再把语差信号与输出信号(前面都置为0)都是变换到频域 41 | 25. 再计算变换后的E、Y、X的功率谱,对应的分别是Rf、Xf、Xf 42 | 26. 通过Xf平滑得到远端信号功率谱power 43 | 44 | >power[j] = (ss_1*power[j]) + 1 + (ss*Xf[j]); 45 | 46 | 27. 当(前一帧的)泄露系数小于一个阀值(0.5)时,重新估算远端信号功率谱power 47 | 28. 计算ey协方差,得到e和y的相关系数 Pey,标准差 Pyy。(Pey为相关系数:ey协方差/y标准差) 48 | 29. 计算远端信号泄露系数 49 | 50 | 参考论文:On Adjusting the Learning Rate in Frequency Domain Echo Cancellation With Double-Talk 51 | 52 | >先计算递归平均系数:
53 | > alpha = beta0*Syy/See; 这里beta0为泄露估计的学习速率
54 | > alpha_1 = 1-alpha;
55 | >再递归计算相关系数Pey和标准差Pyy
56 | >最后得到泄露系数:leak_estimate = Pey/Pyy
57 | 58 | 30. 计算残余回声与误差的比率,并做上下限处理。 59 | 60 | >RER = (0.0001*Sxx + 3.0f*leak_estimate*Syy) / See;
61 | >下限:Sey*Sey/(1+See*Syy)
62 | >上限:0.5 63 | 64 | 31. 判断收敛条件:sum_adapt > M 且leak_estimate > 0.03 65 | 66 | >如果滤波器收敛:
67 | > r = 0.7*leak_estimate*Yf + 0.3*RER*(RF+1) // 计算残余回声的功率
68 | > // 计算最优步长因子,具体可以参考Mader算法,或者到本文作者那里寻求帮助
69 | > power_1[i] = r / (e*(power[i]+10));
70 | >如果没有收敛
71 | > 当Sxx > N*1000时,按如下计算adapt,否则adapt_rate值为0
72 | > adapt_rate = 0.25f*Sxx/See;
73 | > sum_adapt += adapt_rate 74 | 75 | 32. 把残余回声保存在last_y,这是为了语音增强时去掉回声的影响 76 | 77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 87 | 原文作者:icoolmedia 88 | -------------------------------------------------------------------------------- /article/004-房间声学原理与Schroeder混响算法实现.md: -------------------------------------------------------------------------------- 1 | # 房间声学原理与Schroeder混响算法实现 2 | 3 | 因原文公式较多,因此直接贴上图片。 4 | 5 | ![房间声学原理与Schroeder混响算法实现](https://user-images.githubusercontent.com/87458342/127257027-3e980ee3-8042-4a5e-829b-afe5ed220d0d.png) 6 | 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 16 | 原文作者:icoolmedia 17 | -------------------------------------------------------------------------------- /article/005-H264系列--压缩编码技术.md: -------------------------------------------------------------------------------- 1 | # H264系列--压缩编码技术 2 | 3 | ## 概述 4 | H264 无疑是目前应用最广泛的编码技术。一些比较优秀的开源库x264/openh264, ffmpeg等让人们处理h264编解码变得相对容易。为了能更好地理解和处理h264问题,还是有必要了解相关的原理 5 | 6 | H264压缩技术主要采用了以下几种方法对视频数据进行压缩: 7 | 8 | * 帧内预测压缩,解决的是空域数据冗余问题。 9 | * 帧间预测压缩(运动估计与补偿),解决的是时域数据冗徐问题。 10 | * 整数离散余弦变换(DCT),将空间上的相关性变为频域上无关的数据然后进行量化。 11 | * CABAC熵编码, 对量化后的系数进一步的压缩 12 | 13 | 经过压缩后的帧分为:I帧,P帧和B帧: 14 | 15 | * I帧:关键帧,采用帧内压缩技术。 16 | * P帧:向前参考帧,在压缩时,只参考前面已经处理的帧。采用帧音压缩技术。 17 | * B帧:双向参考帧,在压缩时,它即参考前而的帧,又参考它后面的帧。采用帧间压缩技术。 18 | 19 | 两个I帧间的图像序列就称为GOP 20 | 21 | ![image](https://user-images.githubusercontent.com/87458342/127257556-d70e01a7-56ec-4b85-b3cb-e6712262b620.png) 22 | 23 | Tip:对目前比较流行的直播和短视频来说,短视频的数据I帧会比较少,因为I帧数据比较大。而直播的话I帧比较多,因为客户端需要一进入直播间就能马上播放,一般是2s左右一个I帧 24 | 25 | ## 宏块 26 | 27 | 宏块是编码标准的基本处理单元,通常它的大小也为16x16像素。16X16 的宏块上可以划分出更小的子块。子块的大小可以是 8X16、 16X8、 8X8、 4X8、 8X4、 4X4。这主要看图像细节的丰富程度。比如下面的图片 28 | 29 | ![image](https://user-images.githubusercontent.com/87458342/127257540-1813a72b-e14e-4019-b72f-7112c8e5ab25.png) 30 | 31 | 宏块划分好后,就可以对H264编码器缓存中的所有图片进行分组了 32 | 33 | ## 帧分组(即GOP) 34 | 35 | 对于视频数据主要有两类数据冗余,一类是时间上的数据冗余,另一类是空间上的数据冗余。其中时间上的数据冗余是最大的。下面我们就先来说说视频数据时间上的冗余问题。 36 | 37 | 为什么说时间上的冗余是最大的呢?假设摄像头每秒抓取30帧,这30帧的数据大部分情况下都是相关联的。也有可能不止30帧的的数据,可能几十帧,上百帧的数据都是关联特别密切的。 38 | 39 | 对于这些关联特别密切的帧,其实我们只需要保存一帧的数据,其它帧都可以通过这一帧再按某种规则预测出来,所以说视频数据在时间上的冗余是最多的。 40 | 41 | 下面是捕获的一组运动的台球的视频帧,台球从右上角滚到了左下角。 42 | 43 | ![image](https://user-images.githubusercontent.com/87458342/127257615-3d9fe1c5-fb0b-43e6-82a3-9c98e11f0d66.png) 44 | 45 | ![image](https://user-images.githubusercontent.com/87458342/127257633-4fd90f54-fc97-454a-a141-cf1712e66eab.png) 46 | 47 | 通过宏块扫描与宏块搜索可以发现这两个帧的关联度是非常高的。进而发现这一组帧的关联度都是非常高的。因此,上面这几帧就可以划分为一组。其算法是:在相邻几幅图像画面中,一般有差别的像素只有10%以内的点,亮度差值变化不超过2%,而色度差值的变化只有1%以内,我们认为这样的图可以分到一组。 48 | 49 | 在这样一组帧中,经过编码后,我们只保留第一帖的完整数据,其它帧都通过参考上一帧计算出来。我们称第一帧为IDR/I帧,其它帧我们称为P/B帧,这样编码后的数据帧组我们称为GOP 50 | 51 | 所以如果场景一直没什么变化,则一系列视频帧中I帧的数量会很少。如果场景变换很复杂,一直在场景变换大的场景切换时就会有I帧出现。 52 | 53 | ## 运动估计与运动补偿 54 | 55 | 在H264编码器中将帧分组后,就要计算帧组内物体的运动矢量了。还以上面运动的台球视频帧为例,我们来看一下它是如何计算运动矢量的。 56 | 57 | H264编码器首先按顺序从缓冲区头部取出两帧视频数据,然后进行宏块扫描。当发现其中一幅图片中有物体时,就在另一幅图的邻近位置(搜索窗口中)进行搜索。如果此时在另一幅图中找到该物体,那么就可以计算出物体的运动矢量了 58 | 59 | ![image](https://user-images.githubusercontent.com/87458342/127257684-9bda1598-366b-4e8f-a433-310fb76b80d5.png) 60 | 61 | 运动矢量计算出来后,将相同部分(也就是绿色部分)减去,就得到了补偿数据。我们最终只需要将补偿数据进行压缩保存,以后在解码时就可以恢复原图了。压缩补偿后的数据只需要记录很少的一点数据。如下所示: 62 | 63 | ![image](https://user-images.githubusercontent.com/87458342/127257707-dd0c8b03-1594-486f-970f-6b81d07f8462.png) 64 | 65 | 现在在电视和投影上经常看到运动补偿(MEMC)的广告,其实并不是什么高深的技术,比如在上面的例子中,就是根据运动矢量,在帧与帧间插入新运动矢量,使得整个GOP中矢量变化更加平滑。 66 | 67 | ## 帧内压缩(这部分也看不懂,后面补充) 68 | 69 | 计算残差数据 --->DCT ---> CABAC 70 | 71 | ## 帧内预测,计数残差值 72 | 73 | H264的帧内压缩与JPEG很相似。一幅图像被划分好宏块后,对每个宏块可以进行 9 种模式的预测。找出与原图最接近的一种预测模式。 74 | 75 | 然后,将原始图像与帧内预测后的图像相减得残差值。 76 | 77 | 再将我们之前得到的预测模式信息一起保存起来,这样我们就可以在解码时恢复原图了 78 | 79 | ## 对残差数据进行DCT 80 | 81 | ## CABAC 82 | 83 | 上面的帧内压缩是属于有损压缩技术。也就是说图像被压缩后,无法完全复原。而CABAC属于无损压缩技术 84 | 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | 93 | 94 | 原文作者: 简单程序员 95 | -------------------------------------------------------------------------------- /article/006-RTSP 媒体协议流的录制方案及其覆盖策略详解.md: -------------------------------------------------------------------------------- 1 | # RTSP 媒体协议流的录制方案及其覆盖策略详解 2 | 3 | ## 前言 4 | 5 | 在安防和监控领域,RTSP 媒体协议流有很广泛的使用。本文将介绍一种针对 RTSP 媒体流的录制方案及其相应的覆盖策略。据我所知,声网的实时录制功能支持三种模式,分别是云端录制、本地服务端录制和页面录制,今天我们介绍的录制方案和声网的云端录制类似。 6 | 7 | ## 正文 8 | 9 | 本文将从录制视频格式的调研、录制方案的选择、异常状况的处理、覆盖策略的执行四个大方面进行介绍。 10 | 11 | ### 1. 录制视频格式调研 12 | 如果想要实现 RTSP 媒体流的录制功能,就需要考虑录制目标文件的格式,也就是把媒体流录制成哪种格式的视频文件。起初我们预设了三种方案,经过一系列调研后,最终选择了 m3u8。接下来,我们简单介绍一下这个选择过程。 13 | 14 | ### 1.1 为什么不用 mp4 格式 15 | mp4 是点播视频中最为常见的视频格式,综合分析下来并不符合我们的使用场景。一般情况下,一个电影视频的最大时长也就两到三个小时左右,保存成一个 mp4 文件就够用了,但是在安防和监控场景下,一个摄像头对应的录制视频文件的长度可能是十几个小时,甚至是十几天。所以,对比下来,mp4 格式更适用于电影网站。 16 | 17 | 这就引出了 mp4 格式的一个缺点,如果录制存储为一个 mp4 格式,那文件体积可能会非常大。那么,存储的时候就会面临一系列问题,比如磁盘空间不足、大文件分片等状况的处理,特别是录制过程中数据流异常中断可能会导致已经录制的 mp4 文件不可用,这是其一。 18 | 19 | ![image](https://user-images.githubusercontent.com/87458342/127258635-5f46367d-75bd-4c31-bc59-9adb1365f011.png) 20 | 21 | 我们知道 mp4 文件是由许多 Box 和 FullBox 组成的,可以参考上图的 Box 树形图,其中,FullBox 是 Box 的扩展,每个 Box 又包含 Header 和 Data 两部分,moov Box 记录了整个 mp4 文件的音视频媒体信息。而 moov Box 一般是在 mp4 文件写完时才在文件尾部添加。因此,又引出了另外一个缺点,如果 mp4 文件特别大,那么在播放的时候,播放器需要加载全部的视频文件到内存中,如果视频文件特别大,这几乎是不现实的。因此,我们在录制结束保存 mp4 的时候,需要把 moov Box 调整到文件头部来避免这个问题。 22 | 23 | ### 1.2 为什么不用 mpd 格式 24 | mpd 格式类似于 m3u8 格式,但是它采用的是 XML 的组织形式。我们不选择它的原因也有两个,其一,mpd 格式在现有产品线上没有类似使用场景,我们使用更多的是 m3u8,换句话说就是技术储备不足。 25 | 26 | 其二,播放器方案的通用性上存在问题,如果使用 mpd 格式,那么我们的播放器方案需要调整,能够支持 mpd 格式媒体的播放,这样一来会给播放器带来一定的工作量和隐含的问题。 27 | 28 | 最后,给出一个 mpd 的文件示例,让大家对其有一个更加直观的了解。 29 | 30 | ```HTML 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | tears_audio_eng.mp4 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | tears_h264_baseline_240p_800.mp4 47 | 48 | 49 | 50 | 51 | 52 | tears_h264_main_480p_2000.mp4 53 | 54 | 55 | 56 | 57 | 58 | tears_h264_main_720p_8000.mp4 59 | 60 | 61 | 62 | 63 | 64 | tears_h264_high_1080p_20000.mp4 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ``` 73 | 74 | 通过上述文件,我们可以知道这个 mpd 文件包含了一路音频流,同时支持三种不同分辨率和码率的视频流。不同的媒体类型是用 AdaptationSet 标签表示的,内部还可以使用 Representation 标签标记不同分辨率和码率的媒体流。 75 | 76 | ### 1.3 为什么最终选择 m3u8 格式 77 | 选择 m3u8 的话,优势就会更加明显,除了规避上述方案的问题外,还有一些自身的优势,具体表现如下: 78 | 79 | 1. 本身就是 ts 分片存储形式,不需要再单独考虑大文件的切片问题。 80 | 2. 现有播放器方案支持 m3u8 格式,不需要再单独进行适配。 81 | 3. 具有一定的技术储备,开发上手快,开发周期可控。 82 | 4. 相应的覆盖策略执行起来会更加方便。 83 | 84 | 最后,给出一个 m3u8 的文件示例,让大家对其有一个更加直观的了解。 85 | 86 | ```C++ 87 | #EXTM3U 88 | #EXT-X-VERSION:3 89 | #EXT-X-TARGETDURATION:17 90 | #EXT-X-MEDIA-SEQUENCE:0 91 | #EXTINF:11.933333, 92 | index_0000.ts 93 | #EXTINF:3.866667, 94 | index_0001.ts 95 | #EXTINF:7.333333, 96 | index_0002.ts 97 | #EXTINF:16.666667, 98 | index_0003.ts 99 | #EXTINF:4.133333, 100 | index_0004.ts 101 | #EXT-X-ENDLIST 102 | ``` 103 | 104 | 通过上述文件,我们可以知道这个 m3u8 文件包含了 5 个 ts 分片,以及它们各自的时长信息。文件以 #EXTM3U 标签开始,并以 #EXT-X-ENDLIST 标签结束。这里有一点需要注意,如果是直播使用的 m3u8 文件,它是没有 #EXT-X-ENDLIST 标签的。 105 | 106 | ## 2. 录制方案选择 107 | 既然已经确定了目标文件的格式,那么我们就要考虑怎么实现了。目前有两个方案可以考虑,一个是 Golang 纯原生方案,另一个是利用 ffmpeg 实现,接下来分别介绍。 108 | 109 | ### 2.1 Go 原生 110 | 利用纯原生的 Golang 实现,其实,Golang 处理音视频数据还是有一定优势的,通过解封装 RTSP 媒体流,得到音频数据和视频数据,然后创建对应的解码器,得到对应的原始音频 PCM 数据和原始视频 YUV 数据,再分别编码成 AAC 的音频和 H264 的视频,最后保存成 m3u8 格式的录制文件。整个过程可以参考下图: 111 | 112 | ![image](https://user-images.githubusercontent.com/87458342/127258781-d55e8500-5f10-4a10-ac09-c1b368e910df.png) 113 | 114 | 这种方案,编码的工作量会稍微大一些,同时有很多音视频数据处理的细节问题,负载度和难易程度上不如 ffmpeg 方案。 115 | 116 | ### 2.2 ffmpeg 117 | 利用 ffmpeg 工具库,通过启用 ffmpeg 进程来完成对应的 RTSP 流数据接收和 m3u8 文件录制保存工作,这样会更加简单,我们只需要管理好进程的创建、释放和异常处理工作。 118 | 119 | ## 3. 异常处理 120 | 121 | 录制过程中会遇到各种各样的问题,接下会分别介绍。有一点是相同的,所有的异常状况都会通知到录制调度服务,由调度服务进行统一分析和管理,同时支持热备机制,我们通过 Nacos 的服务发现机制监测录制调度服务的运行状态,具体关系可以参考下图: 122 | 123 | ![image](https://user-images.githubusercontent.com/87458342/127258860-22903aed-7aeb-462b-9781-75da7d6ada33.png) 124 | 125 | ### 3.1 CPU、磁盘 126 | 127 | CPU 负载过高和磁盘空间不足是最为常见的两种录制时的异常状况,大致的处理逻辑也是较为相似的。 128 | 129 | CPU 过高的处理逻辑,可以参考下图: 130 | 131 | ![image](https://user-images.githubusercontent.com/87458342/127258876-e145330c-5e81-4a9a-874a-4243c2513c1b.png) 132 | 133 | 当前机器接收到任务后,进行自检操作,发现 CPU 负载过高会停止当前录制任务的执行,同时上报调度服务,重新分配别的机器执行该录制任务。 134 | 135 | 当前机器正在执行录制任务,突然发现 CPU 负载超过阈值,会持续观察一段时间,假定观察周期为 10 秒,如果 CPU 负载连续 10 秒钟超高,那么会停止当前录制任务,同时上报调度服务,请求别的机器继续执行该录制任务,最后将两台机器上的录制文件进行逻辑关联保存到数据库中。如果 CPU 负载在 10 秒内恢复到正常值,我们将继续执行当前录制任务。 136 | 137 | 磁盘空间不足的处理逻辑和 CPU 负载过高有类似的处理逻辑,具体可以参考下图: 138 | 139 | ![image](https://user-images.githubusercontent.com/87458342/127258907-35b3b4d6-e300-492a-87e0-4fe0bd560b2b.png) 140 | 141 | 通过流程图,我们也可以知道磁盘空间不足的处理逻辑和 CPU 负载过高时类似,上图已经展示的非常明确了,这里就不过多赘述了。 142 | 143 | ### 3.2 异常处理 144 | 145 | 一些其他的异常处理情况,比如崩溃,整体流程可以参考下图: 146 | 147 | ![image](https://user-images.githubusercontent.com/87458342/127258949-01491b74-1684-403d-ac5b-711c6accb1ea.png) 148 | 149 | 异常发生时,如果是一般异常,我们只需要将状态通知调度服务即可,调度服务记录相关日志,综合分析整个录制服务的状态。如果 60%的录制机器触发了相同的异常,调度服务就要采取相应的策略。如果是崩溃等重大异常,就需要重启机器或者调度新的机器继续执行录制任务。 150 | 151 | ### 3.3 录制超时 152 | 153 | 如果发生了录制超时,比如我们想录制 24 个小时的视频,现在时长已经录够了,接下来应该怎么做呢?一般有两种处理方法,第一种是直接停止当前录制,上报通知调度服务即可,这种处理方式比较简单粗暴,但是在安防和监控领域是不合适的。第二种是执行特定规则的覆盖策略,实现循环覆盖,始终保留最近 24 小时之内的视频画面内容。 154 | 155 | ![image](https://user-images.githubusercontent.com/87458342/127258966-0fe7e69c-4a29-4baf-b1f5-baa1c19597f0.png) 156 | 157 | 对比上述两种处理方式,当发生录制超时时,第二种方式是最符合安防和监控领域的通用做法。那么覆盖策略又是怎么实现的呢,这就引出了下面的内容——覆盖策略。 158 | 159 | ## 4. 覆盖策略 160 | 161 | 覆盖策略在原理上理解起来很简单,但是具体执行时,就不那么简单了。首先,我们也先通过一个流程图对覆盖策略的处理逻辑有一个整体上的认识。 162 | 163 | ![image](https://user-images.githubusercontent.com/87458342/127258979-f78e4f55-13dc-4c2c-8426-81c27f8d0780.png) 164 | 165 | 166 | ### 4.1 一级定时器 167 | 168 | 当录制任务启动时,我们同时启动一个定时器(一级定时器),定时器的时长就是录制任务的目标时长,这个非常好理解。但是,这个定时器只生效一次或者一次都不生效。只有一级定时器生效后,才会启动二级定时器。如果一级定时器没有启动,那么二级定时器也不会启动。 169 | 170 | 我们可以这样理解,只有一级定时器触发,录制服务才会执行对应的覆盖策略。当覆盖策略启动后,一级定时器销毁,二级定时器生效。 171 | 172 | ### 4.2 二级定时器 173 | 174 | 当文件时长达到了预设的最大时长时,我们将启动二级定时器。其实,二级定时器控制的是覆盖策略的删除频率,每次时间到了,就删除早些时候到录制文件分片。 175 | 176 | ### 4.3 执行覆盖 177 | 178 | 具体覆盖的执行逻辑是,根据 ts 分片的时长和二级定时器的时间周期,计算需要删除的 ts 分片个数,同时更新 m3u8 中的索引列表,然后循环执行该策略,最终实现动态循环的录制覆盖策略。 179 | 180 | ![image](https://user-images.githubusercontent.com/87458342/127258989-5ae6ab9b-ab25-4ab6-afd8-c2e915024dd3.png) 181 | 182 | 覆盖策略的执行过程如上图所示,相信通过上文的解释,大家理解起来还是非常容易的。需要特别说明的是,由于二级定时器执行周期 t 的限制,录制文件的实际时长在最大录制时长 T 和(T+t)之间。 183 | 184 | ## 结尾 185 | 186 | 好了,现在关于 RTSP 媒体流的录制方案和覆盖策略就介绍完了,相信大家对云端录制方案也有了一定认识,有自己想法和感兴趣的小伙伴,欢迎评论留言。关注我,分享更多音视频和流媒体服务器内容。 187 | 188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | 196 | 原文作者: 刘振 197 | -------------------------------------------------------------------------------- /article/007-webrtc建立连接之ICE框架.md: -------------------------------------------------------------------------------- 1 | # webrtc建立连接之ICE框架 2 | 3 | ## ICE介绍 4 | 5 | ICE全称Interactive Connectivity Establishment,即交互式连通建立方式,它是根据RFC5245实现,是一组基于Offer/Answer模式解决NAT穿越打洞的协议集合 6 | 7 | ## ICE架构 8 | 9 | ![image](https://user-images.githubusercontent.com/87458342/127259178-1d26f3a2-af6c-4065-91bb-b14df0b9695d.png) 10 | 11 | ICE框架如上图所示,Peer A和Peer B通过STUN/TURN,建立连接的过程,它是后续P2P媒体流通讯的基础。 12 | 13 | ## ICE基本功能 14 | 15 | 在了解ICE基本功能前,先了解ICE一些基本概念, 16 | 17 | * Candidate:媒体传播的候选地址,组成pair做连通性检查,确定传输路径 18 | * Candidate pair :由本地和远端candidate组成的pair 19 | * Checklist:由candidate pair生成的按优先级排序的链表,用于连通性检查 20 | * Validlist:连通性间检查成功的condidate pair按优先级排序生成链表,用于ICE提名和选择最佳路径 21 | 22 | ### ICE基本功能: 23 | 24 | 第一、收集所有的通路 25 | 第二、对通路进行连通性检查(先对pair进行排序,然后提名并选择最佳路径) 26 | 27 | ### 收集所有通路 28 | 29 | ![image](https://user-images.githubusercontent.com/87458342/127259269-d3414577-90da-4593-bf41-9f5f80cdbd84.png) 30 | 31 | 如上图所示,candidate类型有三种 32 | 33 | * 主机候选者(Local Address) 34 | * 反射候选者(Reflexive Address) 35 | * 中继候选者(Relayed Address) 36 | 37 | 拿到上面的三类候选者之后,要通过SDP交换数据。 38 | 一方收集到上面的所有三类候选者后,通过SDP信令传给对方。 39 | 同样另一方收集到候选者后,也做收集工作。 40 | 当双方拿到全部列表后,将候选者形成candidate pair 41 | 42 | ### 连通性检查 43 | 44 | 1. 对候选对进行优先级排序 45 | 2. 对每个候选对进行发送检查 46 | 3. 对每个候选对进行接收检查 47 | 48 | 第一个要进行排序,要把哪一些优先级高的先排队最先进行检测,这样可以节省时间。那在检测的时候呢,首先是要进行发送检测,那发送是OK的时候,然后再测试接收,其实在实际过程中为了这个节省时间是发送跟接收是同时进行的,所以如果我发送出去之后,然后再能收回我自己发送的信息,那么说明整个通路就是通过了,这其实在实现时还是非常简单的,这么说起来呢,就是要分为发送检测和接收检测。 49 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 58 | 59 | 原文作者: webrtc菜鸟笔记 60 | -------------------------------------------------------------------------------- /article/008-流媒体协议介绍.md: -------------------------------------------------------------------------------- 1 | # 流媒体协议介绍(rtp/rtcp/rtsp/rtmp/mms/hls) 2 | 3 | ## RTP 4 | 参考文档 RFC3550/RFC3551 5 | 6 | Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输层协议。RTP协议详细说明了在互联网上传递音频和视频的标准数据包格式。RTP协议常用于流媒体系统(配合RTCP协议),视频会议和一键通(Push to Talk)系统(配合H.323或SIP),使它成为IP电话产业的技术基础。RTP协议和RTP控制协议RTCP一起使用,而且它是建立在UDP协议上的。 7 | 8 | RTP 本身并没有提供按时发送机制或其它服务质量(QoS)保证,它依赖于低层服务去实现这一过程。 RTP 并不保证传送或防止无序传送,也不确定底层网络的可靠性。 RTP 实行有序传送, RTP 中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置,例如:在视频解码中,就不需要顺序解码。 9 | 10 | RTP 由两个紧密链接部分组成: RTP ― 传送具有实时属性的数据;RTP 控制协议(RTCP) ― 监控服务质量并传送正在进行的会话参与者的相关信息。 11 | 12 | ## RTCP 13 | 实时传输控制协议(Real-time Transport Control Protocol或RTP Control Protocol或简写RTCP)是实时传输协议(RTP)的一个姐妹协议。RTCP为RTP媒体流提供信道外(out-of-band)控制。RTCP本身并不传输数据,但和RTP一起协作将多媒体数据打包和发送。RTCP定期在流多媒体会话参加者之间传输控制数据。RTCP的主要功能是为RTP所提供的服务质量(Quality of Service)提供反馈。 14 | 15 | RTCP收集相关媒体连接的统计信息,例如:传输字节数,传输分组数,丢失分组数,jitter,单向和双向网络延迟等等。网络应用程序可以利用RTCP所提供的信息试图提高服务质量,比如限制信息流量或改用压缩比较小的编解码器。RTCP本身不提供数据加密或身份认证。SRTCP可以用于此类用途。 16 | 17 | ## SRTP & SRTCP 18 | 参考文档 RFC3711 19 | 20 | 安全实时传输协议(Secure Real-time Transport Protocol或SRTP)是在实时传输协议(Real-time Transport Protocol或RTP)基础上所定义的一个协议,旨在为单播和多播应用程序中的实时传输协议的数据提供加密、消息认证、完整性保证和重放保护。它是由David Oran(思科)和Rolf Blom(爱立信)开发的,并最早由IETF于2004年3月作为RFC3711发布。 21 | 22 | 由于实时传输协议和可以被用来控制实时传输协议的会话的实时传输控制协议(RTP Control Protocol或RTCP)有着紧密的联系,安全实时传输协议同样也有一个伴生协议,它被称为安全实时传输控制协议(Secure RTCP或SRTCP);安全实时传输控制协议为实时传输控制协议提供类似的与安全有关的特性,就像安全实时传输协议为实时传输协议提供的那些一样。 23 | 24 | 在使用实时传输协议或实时传输控制协议时,使不使用安全实时传输协议或安全实时传输控制协议是可选的;但即使使用了安全实时传输协议或安全实时传输控制协议,所有它们提供的特性(如加密和认证)也都是可选的,这些特性可以被独立地使用或禁用。唯一的例外是在使用安全实时传输控制协议时,必须要用到其消息认证特性。 25 | 26 | ## RTSP 27 | 28 | 参考文档 RFC2326 29 | 30 | 是由Real Networks和Netscape共同提出的。该协议定义了一对多应用程序如何有效地通过IP网络传送多媒体数据。RTSP提供了一个可扩展框架,使实时数据,如音频与视频的受控、点播成为可能。数据源包括现场数据与存储在剪辑中的数据。该协议目的在于控制多个数据发送连接,为选择发送通道,如UDP、多播UDP与TCP提供途径,并为选择基于RTP上发送机制提供方法。 31 | 32 | RTSP(Real Time Streaming Protocol)是用来控制声音或影像的多媒体串流协议,并允许同时多个串流需求控制,传输时所用的网络通讯协定并不在其定义的范围内,服务器端可以自行选择使用TCP或UDP来传送串流内容,它的语法和运作跟HTTP 1.1类似,但并不特别强调时间同步,所以比较能容忍网络延迟。而前面提到的允许同时多个串流需求控制(Multicast),除了可以降低服务器端的网络用量,更进而支持多方视讯会议(Video Conference)。 因为与HTTP1.1的运作方式相似,所以代理服务器《Proxy》的快取功能《Cache》也同样适用于RTSP,并因RTSP具有重新导向功能,可视实际负载情况来转换提供服务的服务器,以避免过大的负载集中于同一服务器而造成延迟。 33 | 34 | ## RTSP 和RTP的关系 35 | 36 | RTP不象http和ftp可完整的下载整个影视文件,它是以固定的数据率在网络上发送数据,客户端也是按照这种速度观看影视文件,当影视画面播放过后,就不可以再重复播放,除非重新向服务器端要求数据。 37 | 38 | RTSP与RTP最大的区别在于:RTSP是一种双向实时数据传输协议,它允许客户端向服务器端发送请求,如回放、快进、倒退等操作。当然,RTSP可基于RTP来传送数据,还可以选择TCP、UDP、组播UDP等通道来发送数据,具有很好的扩展性。它时一种类似与http协议的网络应用层协议。目前碰到的一个应用:服务器端实时采集、编码并发送两路视频,客户端接收并显示两路视频。由于客户端不必对视频数据做任何回放、倒退等操作,可直接采用UDP+RTP+组播实现。 39 | 40 | ![image](https://user-images.githubusercontent.com/87458342/127259690-1c59ba5c-3cf7-48ae-bbca-4e25a2450c73.png) 41 | 42 | RTP:实时传输协议(Real-time Transport Protocol) 43 | RTP/RTCP是实际传输数据的协议 44 | RTP传输音频/视频数据,如果是PLAY,Server发送到Client端,如果是RECORD,可以由Client发送到Server 45 | 整个RTP协议由两个密切相关的部分组成:RTP数据协议和RTP控制协议(即RTCP) 46 | RTSP:实时流协议(Real Time Streaming Protocol,RTSP) 47 | RTSP的请求主要有DESCRIBE,SETUP,PLAY,PAUSE,TEARDOWN,OPTIONS等,顾名思义可以知道起对话和控制作用 48 | RTSP的对话过程中SETUP可以确定RTP/RTCP使用的端口,PLAY/PAUSE/TEARDOWN可以开始或者停止RTP的发送,等等 49 | RTCP: 50 | RTP/RTCP是实际传输数据的协议 51 | RTCP包括Sender Report和Receiver Report,用来进行音频/视频的同步以及其他用途,是一种控制协议 52 | 53 | ## SDP 54 | 会话描述协议(SDP)为会话通知、会话邀请和其它形式的多媒体会话初始化等目的提供了多媒体会话描述。 55 | 会话目录用于协助多媒体会议的通告,并为会话参与者传送相关设置信息。SDP 即用于将这种信息传输到接收端。SDP 完全是一种会话描述格式 ― 它不属于传输协议 ― 它只使用不同的适当的传输协议,包括会话通知协议(SAP)、会话初始协议(SIP)、实时流协议(RTSP)、MIME 扩展协议的电子邮件以及超文本传输协议(HTTP)。 56 | 57 | SDP 的设计宗旨是通用性,它可以应用于大范围的网络环境和应用程序,而不仅仅局限于组播会话目录,但 SDP 不支持会话内容或媒体编码的协商。 58 | 在因特网组播骨干网(Mbone)中,会话目录工具被用于通告多媒体会议,并为参与者传送会议地址和参与者所需的会议特定工具信息,这由 SDP 完成。SDP 连接好会话后,传送足够的信息给会话参与者。SDP 信息发送利用了会话通知协议(SAP),它周期性地组播通知数据包到已知组播地址和端口处。这些信息是 UDP 数据包,其中包含 SAP 协议头和文本有效载荷(text payload)。这里文本有效载荷指的是 SDP 会话描述。此外信息也可以通过电子邮件或 WWW (World Wide Web) 进行发送。 59 | 60 | SDP 文本信息包括: 61 | 62 | >* 会话名称和意图; 63 | >* 会话持续时间; 64 | >* 构成会话的媒体; 65 | >* 有关接收媒体的信息(地址等) 66 | >* 协议结构 67 | 68 | SDP 信息是文本信息,采用 UTF-8 编 码中的 ISO 10646 字符集。SDP 会话描述如下:(标注 * 符号的表示可选字段): 69 | >v = (协议版本)
70 | >o = (所有者/创建者和会话标识符)
71 | >s = (会话名称)
72 | >i = * (会话信息)
73 | >u = * (URI 描述)
74 | >e = * (Email 地址)
75 | >p = * (电话号码)
76 | >c = * (连接信息 ― 如果包含在所有媒体中,则不需要该字段)
77 | >b = * (带宽信息) 78 | 79 | 一个或更多时间描述(如下所示): 80 | >z = * (时间区域调整)
81 | >k = * (加密密钥)
82 | >a = * (0 个或多个会话属性行)
83 | >0个或多个媒体描述(如下所示) 84 | 85 | 时间描述 86 | >t = (会话活动时间)
87 | >r = * (0或多次重复次数) 88 | 89 | 媒体描述 90 | 91 | >m = (媒体名称和传输地址)
92 | >i = * (媒体标题)
93 | >c = * (连接信息 — 如果包含在会话层则该字段可选)
94 | >b = * (带宽信息)
95 | >k = * (加密密钥)
96 | >a = * (0 个或多个会话属性行) 97 | 98 | ## RTMP/RTMPS 99 | RTMP(Real Time Messaging Protocol)实时消息传送协议是Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输 开发的开放协议。 100 | 它有三种变种: 101 | >1. 工作在TCP之上的明文协议,使用端口1935; 102 | >2. RTMPT封装在HTTP请求之中,可穿越防火墙; 103 | >3. RTMPS类似RTMPT,但使用的是HTTPS连接; 104 | 105 | RTMP协议(Real Time Messaging Protocol)是被Flash用于对象,视频,音频的传输.这个协议建立在TCP协议或者轮询HTTP协议之上.
106 | RTMP协议就像一个用来装数据包的容器,这些数据既可以是AMF格式的数据,也可以是FLV中的视/音频数据.一个单一的连接可以通过不同的通道传输多路网络流.这些通道中的包都是按照固定大小的包传输的. 107 | 108 | ## mms 109 | 110 | MMS (Microsoft Media Server Protocol),中文“微软媒体服务器协议”,用来访问并流式接收 Windows Media 服务器中 .asf 文件的一种协议。MMS 协议用于访问 Windows Media 发布点上的单播内容。MMS 是连接 Windows Media 单播服务的默认方法。若观众在 Windows Media Player 中键入一个 URL 以连接内容,而不是通过超级链接访问内容,则他们必须使用MMS 协议引用该流。MMS的预设埠(端口)是1755 111 | 112 | 当使用 MMS 协议连接到发布点时,使用协议翻转以获得最佳连接。“协议翻转”始于试图通过 MMSU 连接客户端。 MMSU 是 MMS 协议结合 UDP 数据传送。如果 MMSU 连接不成功,则服务器试图使用 MMST。MMST 是 MMS 协议结合 TCP 数据传送。 113 | 如果连接到编入索引的 .asf 文件,想要快进、后退、暂停、开始和停止流,则必须使用 MMS。不能用 UNC 路径快进或后退。若您从独立的 Windows Media Player 连接到发布点,则必须指定单播内容的 URL。若内容在主发布点点播发布,则 URL 由服务器名和 .asf 文件名组成。例如:mms://windows_media_server/sample.asf。其中 windows_media_server 是 Windows Media 服务器名,sample.asf 是您想要使之转化为流的 .asf 文件名。 114 | 若您有实时内容要通过广播单播发布,则该 URL 由服务器名和发布点别名组成。例如:mms://windows_media_server/LiveEvents。这里 windows_media_server 是 Windows Media 服务器名,而 LiveEvents 是发布点名 115 | 116 | ## HLS 117 | 118 | HTTP Live Streaming(HLS)是苹果公司(Apple Inc.)实现的基于HTTP的流媒体传输协议,可实现流媒体的直播和点播,主要应用在iOS系统,为iOS设备(如iPhone、iPad)提供音视频直播和点播方案。HLS点播,基本上就是常见的分段HTTP点播,不同在于,它的分段非常小。 119 | 120 | 相对于常见的流媒体直播协议,例如RTMP协议、RTSP协议、MMS协议等,HLS直播最大的不同在于,直播客户端获取到的,并不是一个完整的数据流。HLS协议在服务器端将直播数据流存储为连续的、很短时长的媒体文件(MPEG-TS格式),而客户端则不断的下载并播放这些小文件,因为服务器端总是会将最新的直播数据生成新的小文件,这样客户端只要不停的按顺序播放从服务器获取到的文件,就实现了直播。由此可见,基本上可以认为,HLS是以点播的技术方式来实现直播。由于数据通过HTTP协议传输,所以完全不用考虑防火墙或者代理的问题,而且分段文件的时长很短,客户端可以很快的选择和切换码率,以适应不同带宽条件下的播放。不过HLS的这种技术特点,决定了它的延迟一般总是会高于普通的流媒体直播协议。  121 | 122 |
123 | 根据以上的了解要实现HTTP Live Streaming直播,需要研究并实现以下技术关键点 124 | >* 采集视频源和音频源的数据 125 | >* 对原始数据进行H264编码和AAC编码 126 | >* 视频和音频数据封装为MPEG-TS包 127 | >* HLS分段生成策略及m3u8索引文件 128 | >* HTTP传输协议 129 | 130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | 原文作者: 雪影 138 | -------------------------------------------------------------------------------- /article/011-CDN在直播中的运用.md: -------------------------------------------------------------------------------- 1 | # CDN在直播中的运用 2 | 3 | ## CDN技术原理 4 | 5 | CDN的全称为Content Delivery Network,即内容分发网络,是一个策略性部署的整体系统,主要用来解决由于网络带宽小、用户访问量大、网点分布不均匀等导致用户访问网站速度慢的问题。这中间就有了很多的CDN节点,简单一点理解就相当于我们开始学习计算机选择网络。具体实现是通过在现有的网络中,增加一层新的网络架构,将网站的内容发布到离用户最近的网络节点上,这样用户可以就近获取所需的内容,解决之前网络拥塞、访问延迟高的问题,提高用户体验。 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127275150-8c70d67e-e560-4492-8dc5-8a3b24700813.png) 8 | ![image](https://user-images.githubusercontent.com/87458342/127275161-31b6156e-30f5-41a0-a673-408794069f42.png) 9 | 10 | 上图中,不同的流媒体走的节点和协议做了区分,网络拥塞减少,访问延迟降低,带宽得到良好的控制等等。 CDN直播中常用的流媒体协议包括RTMP,HLS,HTTP FLV等。 11 | 12 | RTMP(Real Time Messaging Protocol)是基于TCP的,由Adobe公司为Flash播放器和服务器之间音频、视频传输开发的开放协议。 HLS(HTTP Live Streaming)是基于HTTP的,是Apple公司开放的音视频传输协议。 HTTP FLV则是将RTMP封装在HTTP协议之上的,可以更好的穿透防火墙等。 13 | 14 | ## CDN的常用架构 15 | 16 | CDN架构设计比较复杂。不同的CDN厂商,也在对其架构进行不断的优化,所以架构不能统一而论。这里只是对一些基本的架构进行简单的剖析。 17 | CDN主要包含:源站、缓存服务器、智能DNS、客户端等几个主要组成部分。 18 | 源站:是指发布内容的原始站点。添加、删除和更改网站的文件,都是在源站上进行的;另外缓存服务器所抓取的对象也全部来自于源站。对于直播来说,源站为主播客户端。 19 | 缓存服务器:是直接提供给用户访问的站点资源,由一台或数台服务器组成;当用户发起访问时,他的访问请求被智能DNS定位到离他较近的缓存服务器。如果用户所请求的内容刚好在缓存里面,则直接把内容返还给用户;如果访问所需的内容没有被缓存,则缓存服务器向邻近的缓存服务器或直接向源站抓取内容,然后再返还给用户。 20 | 智能DNS:是整个CDN技术的核心,它主要根据用户的来源,以及当前缓存服务器的负载情况等,将其访问请求指向离用户比较近且负载较小的缓存服务器。通过智能DNS解析,让用户访问同服务商下、负载较小的服务器,可以消除网络访问慢的问题,达到加速作用。 21 | 客户端:即发起访问的普通用户。对于直播来说,就是观众客户端,例如手机客户端,PC客户端。 22 | 用图表示如下: 23 | 24 | ![image](https://user-images.githubusercontent.com/87458342/127275216-56f18e32-6d18-4791-b1a9-cb623154bd34.png) 25 | 26 | 整个流程描述如下: 27 | 28 | 主播开始进行直播,向智能DNS发送解析请求; 智能DNS返回最优CDN节点IP地址; 主播端采集音视频数据,发送给CDN节点,CDN节点进行缓存等处理; 观众端要观看此主播的视频,向智能DNS发送解析请求; 智能DNS返回最优CDN节点IP地址; 观众端向CDN节点请求音视频数据; CDN节点同步其他节点的音视频数据; CDN节点将音视频数据发送给观众端; 29 | 30 | ## 采用CDN的缺点 31 | 大概了解了CDN的技术原理后,我们在做直播选型时,还需要了解一个方案优缺点。接下来,我们来分析一下CDN的短板。 32 | 总结一下主要有如下短板: 33 | 34 | ### 播放延时(网络延时) 35 | 36 | 网络延时这里指的是从主播端采集,到观众端播放,这之间的时间差。这里不考虑主播段采集对视频进行编码的时间,以及观众端观看对视频进行解码的时间,仅考虑网络传输中的延时。例如说下图中的网络延时: 37 | ![image](https://user-images.githubusercontent.com/87458342/127275331-8aa6c0c9-172e-4a81-b09c-612daaed7d85.png) 38 | 39 | ### 网络抖动 40 | 41 | 网络抖动,是指数据包的到达顺序、间隔和发出时不一致。比如说,发送100个数据包,每个包间隔1s发出。结果第27个包在传输过程中遇到网络拥塞,造成包27不是紧跟着26到达的,而是延迟到87后面才达。在直播中,这种抖动的效果实际上跟丢包是一样的。因为你不能依照接收顺序把内容播放出来,否则会造成失真。网络抖动,会造成播放延时对应增大。如果网络中抖动较大,会造成播放卡顿等现象。这个之前在云计算上都不是什么难事。 42 | 43 | ![image](https://user-images.githubusercontent.com/87458342/127275377-3fa42883-7005-4460-9629-4cb4b98a3368.png) 44 | 45 | ### 网络丢包 46 | CDN直播中用到的RTMP、HLS、HTTP FLV等协议都是在TCP的基础之上。TCP一个很重要的特性是可靠性,即不会发生数据丢失的问题。为了保证可靠性,TCP在传输过程中有3次握手,见下图。首先客户端会向服务端发送连接请求,服务端同意后,客户端会确认这次连接。这就是3次握手。接着,客户端就开始发送数据,每次发送一批数据,得到服务端的“收到“确认后,继续发送下一批。TCP为了保证传到,会有自动重传机制。如果传输中发生了丢包,没有收到对端发出的“收到”信号,那么就会自动重传丢失的包,一直到超时。 47 | 由于互联网的网络状况是变化的,以及主播端的网络状况是无法控制的。所以当网络中丢包率开始升高时,重传会导致延时会不断增大,甚至导致不断尝试重连等情况,这样不能有效的缓存,严重情况下会导致观众端视频无法观看。 48 | 49 | 50 | 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 原文作者: HugoforAndroid 61 | -------------------------------------------------------------------------------- /article/012-常见音视频编码格式.md: -------------------------------------------------------------------------------- 1 | # 常见音视频编码格式 2 | 3 | ## 常见的音频编码格式 4 | 5 | ### MP3 6 | >这种压缩方式的全称叫MPEG Audio Layer3,MP3是利用MPEG Audio Layer 3的技术,将音乐以1:10甚至1:12 的压缩率,压缩成容量较小的file,换句话说,能够在音质丢失很小的情况下把文件压缩到更小的程度。>而且还非常好的保持了原来的音质。 7 | >* 压缩率:10~12倍 8 | >* 优点:压缩比高,适合用于互联网上的传播 9 | >* 缺点: MP3 在 128KBitrate 及以下时,会出现明显的高频丢失 10 | 11 | ### AAC高级音频编码 12 | >Advanced Audio Coding。一种专为声音数据设计的文件压缩格式,与MP3不同,它采用了全新的算法进行编码,更加高效,具有更高的“性价比”。利用AAC格式,可使人感觉声音质量没有明显降低aac标志的前提下,更加小巧。AAC属于有损压缩的格式,与时下流行的APE、FLAC等无损格式相比音质存在“本质上”的差距。加之,传输速度更快的USB3.0和16G以上大容量MP3正在加速普及,也使得AAC头上“小巧”的光环不复存在了。 13 | >* 优点:支持多种音频声道组合,提供优质的音质。 14 | 15 | ### WMA 16 | 17 | >WMA的全称是Windows Media Audio,是微软力推的一种音频格式。WMA格式是以减少数据流量但保持音质的方法来达到更高的压缩率目的,其压缩率一般可以达到1:18,生成的文件大小只有相应MP3文件的一半。 18 | >* 压缩率:10~12倍 19 | >* 缺点:在高比率的渲染能力低下,同音源的一个320KBPS的MP3与比较192KBPS的WMA相比,音质和渲染力很容易分别出是前者较优。因为: 20 | >* 当 Bitrate 小于 128K 时, WMA 最为出色且编码后得到的音频文件很小。 21 | >* 当 Bitrate 大于 128K 时, WMA 音质损失过大。 22 | >* 优点:WMA还可以通过DRM(Digital Rights Management)方案加入防止拷贝,或者加入限制播放时间和播放次数,甚至是播放机器的限制,可有力地防止盗版。 23 | 24 | ### WAV 25 | > WAV是录音时用的标准的windows文件格式,文件的扩展名为“.wav”,WAVE文件作为最经典的Windows多媒体音频格式,应用非常广泛。声道有单声道和立体声之分,采样频率一般有11kHz、22kHz和44kHz三种。 26 | > WAVE文件所占容量=(采样频率×采样位数×声道)×时间/8(1字节=8bit)。 27 | 28 | ### ATRAC 29 | >ATRAC压缩技术主要是利用了人耳的蔽遮效应,在心理声学原理上,在进行音频录入的同时,有许多外部的频段同时也会被录入,当人耳同时听到两个不同频率、不同音量的声音时,音量较小的低频及音量较小的高频连同不为人耳所察觉的频段信号都会被自动减弱或忽略不予记录,因此又可以称为适应性变换声码技术,由于近年來编码压缩技术(ATRAC的版本)越來越成熟,所以经过编码解码过程后的声音仍直逼CD, 30 | >ATRAC将16比特44.1KHz的数字信号以频率响应轴分成52个区段(在低频时分割较细而在高频时分割较粗),根据声音心理学的原理,将声音信号中人耳听不到和对人的听力影响不大的信息给剔除出去而达到缩小声音文件的目的。利用这种原理,ATRAC可以将录音的资料量压缩为原来的五分之一(即压缩比为1:5)。 31 | >* 压缩率:5倍 32 | >* 特点:自动减弱或者忽略外部频段的杂音 33 | 34 | ### PLAC 35 | >FLAC与MP3不同,MP3是音频压缩编码,但FLAC是无损压缩,也就是说音频以FLAC编码压缩后不会丢失任何信息,将FLAC文件还原为WAV文件后,与压缩前的WAV文件内容相同OGG 36 | 37 | ### OGG 38 | >OGG格式的全称应该是OGG Vobis。它是一种新的音频压缩格式,类似于MP3等现有的音乐格式。但有一点不同的是,它是完全免费、开放和没有专利限制的。OGG Vobis有一个很出众的特点,就是支持多声道, 39 | >OGG Vobis在压缩技术上比MP3好,而且它的多声道,免费,开源这些特点,使它很有可能成为一个流行的趋势,这也正是一些MP3播放器对其支持的原因 40 | 41 | >可以对所有的声道进行编码,而不是MP3只能编码2个声道。多声道音乐的兴起,给音乐欣赏带来了革命性的变化,尤其在欣赏交响时,会带来更多临场感。这场革命性的变化是MP3无法适应的。在以后的播放技术>不断提高以后,而且人们对音质要求不断提高,Ogg的优势将更加明显。 42 | >* 优点:完全免费。开放没有专利限制。支持多声道 43 | 44 | ### APE 45 | >APE的本质,其实它是一种无损压缩音频格式。庞大的WAV音频文件可以通过Monkey”s Audio这个软件进行“瘦身”压缩为APE。有时候它被用做网络音频文件传输,因为被压缩后的APE文件容量要比WAV源文件小一半多,可以节约传输所用的时间。更重要的是,通过Monkey”s Audio解压缩还原以后得到的WAV文件可以做到与压缩前的源文件完全一致,.。所以APE被誉为“无损音频压缩格式” 46 | >* 无损压缩 47 | 48 | ### 各种编码比较 49 | >* 1.压缩比比较: 50 | >aac>ogg>mp3(wma)>ape>flac>wav(同一音源条件下) 51 | >mp3和wma以192kbps为分界线,192kbps以上mp3好,192kbps以下wma好。 52 | >WMA(10~12),,APE(无损压缩,但庞大的WAV可以瘦身为APE),, 53 | >ATRAC(1:5),,MP3(10~12),,AAC(18~20),,OGG(),, 54 | >FLAC(1:2) 55 | 56 | >* 2.2.音质比较: 57 | >wav=flac=ape>aac>ogg>mp3>wma 58 | 59 | >* 3.3.硬件支持比较: 60 | >MP3播放器:mp3>wma>wav>flac>ape aac ogg 61 | >手机:mp3>wma>aac wav>flac ogg>ape 62 | 63 | ### 各种编码比较 64 | 65 | 种类| 压缩比 |支持声道数| 优点| 缺点 66 | :---|:---|:---|:---|:--- 67 | MP3| 10~12| 2个声道| 压缩比高,适合用于互联网上的传播| 在 128KBitrate 及以下时,会出现明显的高频丢失 68 | WMA| 10~12| |当 Bitrate 小于 128K 时, WMA 最为出色且编码后得到的音频文件很小。| 当 Bitrate 大于 128K 时, WMA 音质损失过大。 69 | OGG| |支持多声道 |OGG Vobis在压缩技术上比MP3好,而且它的多声道 | 70 | AAC| 18~20|支持多声道| 支持多种音频声道组合,提供优质的音质| 与时下流行的APE、FLAC等无损格式相比音质存在“本质上”的差距。加之,传输速度更快的USB3.0和16G以上大容量MP3正在加速普及,也使得AAC头上“小巧”的光环不复存在了。 71 | APE| 无损压缩| |用做网络音频文件传输,因为被压缩后的APE文件容量要比WAV源文件小一半多,可以节约传输所用的时间。 | 72 | FLAC| 2 | | 73 | WAV |无损压缩 |单声道和立体声| WAVE文件作为最经典的Windows多媒体音频格式 | 74 | 75 | ## 常见的视频编码格式 76 | 77 | 所谓视频编码方式就是指通过特定的压缩技术,将某个视频格式的文件转换成另 一种视频格式文件的方式。 78 | 79 | ### 视频传输中的编码标准 80 | 1. 国际电联的H.264 81 | 2. 国际标准化组织运动图像专家 组的MPEG系列标准 82 | 3. 微软公司的WMV 83 | 4. Apple公司的 QuickTime 84 | 5. google力推的WebM格式 85 | 86 | ### 视频编码格式 87 | 88 | 高清视频的编码格式有五种,即H.264、MPEG-4、MPEG-2、WMA-HD以及VC-1。 89 | 90 | ### H.264 91 | >H.264是由国际电信联盟(iTU-T)所制定的新一代的视频压缩格式。H.264的数据压缩比能比当前DVD系统中使用的 MPEG-2高2~3倍,比MPEG-4高1.5~2倍。正因为如此,经过H.264压缩的视频数据,在网络传输过程中所需要的带宽更少,也更加经济. 92 | 93 | ##### 缺点: 94 | 1. 与MPEG-4一样,经过H.264压缩的视频文件一般也是采用avi 作为其后缀名,同样不容易辨认,只能通过解码器来自己识别。 95 | 2. H.264编码的影片在播放的时候对硬件系统也提出了非常高的要求。 96 | 据相关资料显示,H.264的影片在编码的过程中复杂度是MPEG2的10倍,解码的复杂度是MPEG2的3倍,这对于CPU来说是很沉重的负担,而显卡芯片如果要整合硬件解码模块,其难度也随之加大。 97 | ##### 优点: 98 | 1. H.264 使图像压缩技术上升到了一个更高的阶段,能够在较低带宽上提供高质量的图像传输,该优点非常适合国内运营商用户量大、接入网/骨干网带宽相对有限的状况。 99 | ##### 特点: 100 | 101 | 1. 更高的编码效率 102 | 2. 高质量的视频画面 103 | 3. 提高网络适应能力:H.264可以工作在实时通信应用(如视频会议)低延时模式下,也可以工作在没有延时的视频存储或视频流服务器中。 104 | 4. 采用混合编码结构 105 | 5. 错误恢复功能:H.264提供了解决网络传输包丢失的问题的工具,适用于在高误码率传输的无线网络中传输视频数据。 106 | 6. 较高的复杂度:264性能的改进是以增加复杂性为代价而获得的。据估计,H.264编码的计算复杂度大约相当于H.263的3倍,解码复杂度大约相当于H.263的2倍。 107 | 108 | ##### MPEG4 109 | 由于MPEG4只处理图像帧与帧之间有差异的元素,而舍弃相同的元素,因此大大减少了合成多媒体文件的体积。应用MPEG4技术的影音文件最显著特点就是 压缩率高且成像清晰,一般来说,一小时的影像可以被压缩为350M左右的数据,而一部高清晰度的DVD电影, 可以压缩成两张甚至一张650M CD光碟来存储。 110 | 111 | ### MPEG-4 112 | >MPEG-4不仅可提供高压缩率,同时也可实现更好的多媒体内容互动性及全方位的存取性,它采用开放的编码系统,可随时加入新的编码算法模块,同时也可根据不同应用需求现场配置解码器,以支持多种多媒体应用。
113 | >优点:
114 | >1.具有很好的兼容性;
115 | >2.MPEG-4比其他算法提供更好的压缩比,最高达200:1;
116 | >3.MPEG-4在提供高压缩比的同时,对数据的损失很小。所以,mpeg-4的应用能大 幅度的降低录像存储容量,获得较高的录像清晰度,特别适用于长时间实时录像的需求,同时具备在低带宽上优良的网络传输能力。
117 | >4.采用开放的编码系统,可以加入新的编码算法模块. 118 | 119 | ### VC-1 120 | 121 | >VC-1是软件巨头微软力推的一种视频编码的格式
122 | >总的来说,从压缩比上来看,H.264的压缩比率更高一些,也就是同样的视频,通过H.264编码算法压出来的视频容量要比VC-1的更小,但是VC-1 格式的视频在解码计算方面则更小一些,一般通过高性能的CPU就可以很流畅的观看高清视频。(H.264对硬件的要求比较高) 123 | 124 | VC-1目前的气势依然弱于H.264,也弱于MPEG-4,一方面是VC-1在技术层面上的实际表现与H.264无太大差异,VC-1同样以MPEG-4为基础,但并没有特别的突出点或优越性,运营商从技术角度考虑没有必要非选择VC-1。 125 | 另外,从授权角度来看VC-1是否有优势呢?答案是三者中最不利的,碍于Microsoft一贯的推行策略,VC-1的授权来源仅只一家,授权价格与方式调整,以及后续版本的改进方向,都由微软一手掌握,无人能左右,眼前为与MPGE-4、H.264等竞争,VC-1授权自然不敢过高,但运营商依然对未来是否会涨价表示担心。 126 | 127 | ### MPEG-2 128 | 129 | >目前的MPEG-2的视频在蓝光时代一样是得到了重用,MPEG-2不是MPEG -1的简单升级,MPEG-2在系统和传送方面作了更加详细的规定和进一步的完善。MPEG-2特别适用于广播级的数字电视的编码和传送,被认定为SDTV和HDTV的编码标准。DVD影碟就是采用MPEG-2压缩标准。 130 | 131 | ### 各种视频编码格式性能比较 132 | 133 | 性能| MPEG2| MPEG4| H264| VC-1 134 | :--- | :--- | :--- | :--- | :--- 135 | 压缩比(MPEG2为1)| 100%| 50%~60% |25%~40% |30%~40% 136 | 对硬件的要求| 最低| 较低| 最高 | 较高 137 | 授权成本| 最低| 较高| 最低 | 较低 138 | 画面质量| 一般| 较好| 最好| 最好 139 | 适用场景| 适用于广播级的数字电视的编码和传送,被认定为SDTV和HDTV的编码标准。DVD影碟就是采用MPEG-2压缩标准。| MPEG-4在提供高压缩比的同时,对数据的损失很小。所以,mpeg-4的应用能大 幅度的降低录像存储容量,获得较高的录像清晰度,特别适用于长时间实时录像的需求 | H.264可以工作在实时通信应用(如视频会议)低延时模式下,也可以工作在没有延时的视频存储或视频流服务器中。| 高压缩比,画面质量好但是对硬件要求没有H264高的场景 140 | 141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | 原文作者: xixihaha331 150 | -------------------------------------------------------------------------------- /article/013-H.264官方软件JM源代码分析-编码器lencod.md: -------------------------------------------------------------------------------- 1 | # H.264官方软件JM源代码分析-编码器lencod 2 | 3 | ## 函数调用关系图 4 | 5 | JM中的H.264视频编码器lencod的函数调用关系图如下所示。 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127278908-b0cc2f83-78c5-4718-a849-05415c6e19a8.png) 8 | 9 | ### 下面解释一下图中关键标记的含义。 10 | 11 | >函数背景色
12 | >函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用:
13 | >* 白色背景的函数:不加区分的普通内部函数。 14 | >* 黄色背景函数:滤波函数(Filter)。用于环路滤波,半像素插值,SSIM/PSNR的计算。 15 | >* 绿色背景的函数:宏块编码函数(Encode)。通过对残差的DCT变换、量化等方式对宏块进行编码。 16 | >* 紫色背景的函数:熵编码函数(Entropy Coding)。对宏块编码后的数据进行CABAC或者CAVLC熵编码。 17 | >* 浅蓝色背景函数:码率控制函数(Rate Control)。对码率进行控制的函数。 18 | 19 | >箭头线
20 | >箭头线标志了函数的调用关系:
21 | >* 黑色箭头线:不加区别的调用关系。 22 | >* 黄色的箭头线:滤波函数(Filter)之间的调用关系。 23 | >* 绿色箭头线:宏块编码函数(Encode)之间的调用关系。 24 | >* 紫色箭头线:熵编码函数(Entropy Coding)之间的调用关系。 25 | 26 | >函数所在的文件
27 | >每个函数标识了它所在的文件路径。 28 | 29 | ### 下文记录结构图中几个关键的部分。 30 | 31 | ## 普通内部函数 32 | 33 | 普通内部函数指的是lencod中还没有进行分类的函数。例如:
34 | * 码器的main()函数中调用的参数配置函数Configure()、初始化编码器函数init_encoder()、编码函数encode_sequence()、释放编码器函数free_encoder()等。 35 | * 编码器主要编码函数encode_one_frame()逐层调用的函数perform_encode_frame()、frame_picture()、code_a_picture()、code_a_plane()等。 36 | 37 | ## 宏块编码函数 38 | 宏块编码函数通过运动估计、DCT变换、量化等步骤对图像数据进行编码。编码的工作都是在Slice结构体中的encode_one_macroblock()中完成的。encode_one_macroblock()调用PartitionMotionSearch()完成帧间宏块运动估计的工作;调用mode_decision_for_I8x8_MB()、mode_decision_for_I4x4_MB()完成帧内宏块的模式选择;调用Slice结构体的luma_residual_coding()完成了残差编码的工作。 39 | 40 | PartitionMotionSearch()调用了BlockMotionSearch()。BlockMotionSearch()调用了Macroblock结构体中的IntPelME()完成整像素的运动估计,又调用了SubPelME()完成了亚像素的运动估计。整像素的运动估计可以使用下面的方法: 41 | 42 | >EPZS_motion_estimation():EPZS算法;
43 | >full_search_motion_estimation():全搜索算法;
44 | >UMHEXIntegerPelBlockMotionSearch():UMHEX算法; 45 | 46 | 亚像素的运动估计可以使用下面的方法: 47 | >EPZS_sub_pel_motion_estimation():EPZS算法;
48 | >full_sub_pel_motion_estimation():全搜索算法;
49 | >UMHEXSubPelBlockME():UMHEX算法; 50 | 51 | ## 熵编码函数 52 | 53 | 熵编码函数使用CAVLC或者CABAC的方式对宏块编码后的数据进行熵编码。熵编码的工作都是在write_macroblock()中完成的。write_macroblock()调用了Slice结构体中的write_MB_layer()方法,根据Slice类型的不同,write_MB_layer()可以指向下面方法: 54 | >write_i_slice_MB_layer():I Slice宏块熵编码;
55 | >write_p_slice_MB_layer():P Slice宏块熵编码;
56 | >write_b_slice_MB_layer():B Slice宏块熵编码; 57 | 58 | ## 环路滤波函数 59 | 60 | 环路滤波函数对重建帧数据进行滤波,去除方块效应。去块效应滤波是在DeblockPicture()中完成的。DeblockPicture()调用了DeblockMb()。而DeblockMb()中调用GetStrengthVer()、GetStrengthHor()函数获取滤波强度;调用EdgeLoopLumaVer()、EdgeLoopLumaHor()进行滤波。 61 | 62 | ## 码率控制函数 63 | 64 | 码率控制模块函数分布在lencod源代码不同的地方,包括rc_init_seq()、rc_init_GOP()、rc_init_frame()、rc_handle_mb()等。 65 | 66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 75 | 原文作者: 雷霄骅 76 | -------------------------------------------------------------------------------- /article/014-H.264官方软件JM源代码分析-解码器ldecod.md: -------------------------------------------------------------------------------- 1 | # H.264官方软件JM源代码分析-解码器ldecod 2 | 3 | ## 函数调用关系图 4 | 5 | JM中的H.264视频解码器ldecod的函数调用关系图如下所示。 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127280008-eb5c551e-79ee-4e98-8004-e2eed3784d0f.png) 8 | 9 | 下面解释一下图中关键标记的含义。 10 | 11 | >函数背景色
12 | >函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用:
13 | >* 白色背景的函数:普通内部函数。 14 | >* 粉红色背景函数:解析函数(Parser)。这些函数用于解析SPS、PPS等信息。 15 | >* 紫色背景的函数:熵解码函数(Entropy Decoding)。这些函数读取码流数据并且进行CABAC或者CAVLC熵解码。 16 | >* 绿色背景的函数:解码函数(Decode)。这些函数通过帧内预测、帧间预测、DCT反变换等方法解码压缩数据。 17 | >* 黄色背景的函数:环路滤波函数(Loop Filter)。这些函数对解码后的数据进行滤波,去除方块效应。 18 | 19 | >箭头线
20 | >箭头线标志了函数的调用关系:
21 | >* 黑色箭头线:不加区别的调用关系。 22 | >* 粉红色的箭头线:解析函数(Parser)之间的调用关系。 23 | >* 紫色箭头线:熵解码函数(Entropy Decoding)之间的调用关系。 24 | >* 绿色箭头线:解码函数(Decode)之间的调用关系。 25 | >* 黄色箭头线:环路滤波函数(Loop Filter)之间的调用关系。 26 | 27 | >函数所在的文件
28 | >每个函数标识了它所在的文件路径。 29 | 30 | 下文记录结构图中的几个关键部分。 31 | 32 | ### 普通内部函数 33 | 普通内部函数指的是ldecod中还没有进行分类的函数。例如: 34 | * 解码器的main()函数中调用的参数配置函数Configure()、打开解码器函数OpenDecoder()、解码函数DecodeOneFrame()、输出函数WriteOneFrame()、关闭解码器函数CloseDecoder()等。 35 | * 解码器主要解码函数DecodeOneFrame()逐层调用的函数decode_one_frame()、decode_slice()、decode_one_slice()等。 36 | 37 | ### 解析函数(Parser) 38 | 解析函数(Parser)用于解析H.264码流中的一些信息(例如SPS、PPS、Slice Header等)。在read_new_slice()中都调用这些解析函数完成了解析。下面举几个解析函数的例子。 39 | >* read_next_nalu():解析NALU。这个函数是后几个解析函数的前提。 40 | >* ProcessPPS():解析Slice Header。其中调用了InterpretPPS()用于解析PPS。 41 | >* ProcessSPS():解析SEI。其中调用了InterpretSPS()用于解析SPS。 42 | 43 | 其中read_next_nalu()调用了下面几个函数: 44 | >* get_annex_b_NALU():读取annexb格式的NALU。 45 | >* GetRTPNALU():读取RTP格式的NALU。 46 | >* NALUtoRBSP():将NALU处理为RBSP格式。 47 | >* (PS:annexb和RTP是H.264码流的2种格式) 48 | 49 | ### 熵解码函数(Entropy Decoding) 50 | 熵解码函数(Entropy Decoding)读取码流数据并且进行CABAC或者CAVLC熵解码。熵解码工作是在Slice结构体的read_one_macroblock()函数中完成的。该函数根据宏块类型的不同,会调用不同的熵解码函数。CAVLC对应的解码函数是: 51 | 52 | >* read_one_macroblock_p_slice_cavlc():解码P宏块。 53 | >* read_one_macroblock_b_slice_cavlc():解码B宏块。 54 | >* read_one_macroblock_i_slice_cavlc():解码I宏块。 55 | 56 | CABAC对应的解码函数是: 57 | >* read_one_macroblock_p_slice_cabac():解码P宏块。 58 | >* read_one_macroblock_b_slice_cabac():解码B宏块。 59 | >* read_one_macroblock_i_slice_cabac():解码I宏块。 60 | 61 | ### 解码函数(Decode) 62 | 63 | 解码函数(Decode)通过帧内预测、帧间预测等方法解码宏块压缩数据。解码工作都是在decode_one_macroblock()中完成的。decode_one_macroblock()调用了Slice结构体中的decode_one_component()解码一个亮度分量。decode_one_component()根据宏块所在的Slice的不同,会调用不同的处理函数: 64 | 65 | >* decode_one_component_i_slice():解码I Slice中的宏块。 66 | >* decode_one_component_p_slice():解码P Slice中的宏块。 67 | >* decode_one_component_b_slice():解码B Slice中的宏块。 68 | 69 | I Slice中只包含Intra类型的宏块。因此decode_one_component_i_slice()调用下面函数对Intra类型宏块进行解码: 70 | 71 | >* mb_pred_intra16x16():帧内预测16x16宏块 72 | >* mb_pred_intra4x4():帧内预测4x4宏块 73 | >* mb_pred_intra8x8():帧内预测8x8宏块 74 | 75 | P Slice中包含Intra类型和Inter类型(单向)的宏块。因此decode_one_component_p_slice()调用下面函数对Intra类型宏块进行解码: 76 | 77 | >* mb_pred_intra16x16():同上 78 | >* mb_pred_intra4x4():同上 79 | >* mb_pred_intra8x8():同上 80 | >* mb_pred_p_inter16x16():单向帧间预测16x16宏块 81 | >* mb_pred_p_inter16x8():单向帧间预测16x8宏块 82 | >* mb_pred_p_inter8x16():单向帧间预测8x16宏块 83 | >* mb_pred_p_inter8x8():单向帧间预测8x8宏块 84 | 85 | B Slice中包含Intra类型和Inter类型(双向)的宏块。因此decode_one_component_b_slice()调用的函数和P Slice中的宏块是类似的,唯一的不同在于它增加了对B宏块的处理。 86 | 对于逐行扫描的Intra16x16宏块,具体的预测函数是intra_pred_16x16_normal(),其中根据帧内预测类型的不同,调用不同的函数进行处理: 87 | 88 | >* intra16x16_vert_pred():垂直模式 89 | >* intra16x16_hor_pred():水平模式 90 | >* intra16x16_dc_pred():DC模式 91 | >* intra16x16_plane_pred():Plane模式 92 | 93 | 对于逐行扫描的Inter16x16的P类型宏块,具体的预测函数是mb_pred_p_inter16x16(),其中会调用perform_mc()完成运动补偿。对于单向运动补偿,perform_mc()会调用perform_mc_single();对于双向运动补偿,perform_mc()会调用perform_mc_bi()。运动补偿的过程中,会调用get_block_luma()完成四分之一像素的插值工作。 94 | 无论Inter类型宏块还是Intra类型的宏块,最后都会调用iMBtrans4x4()完成DCT反变换和残差叠加的工作。其中DCT反变换在inverse4x4()中完成,而残差叠加在sample_reconstruct()中完成。 95 | 96 | 环路滤波函数(Loop Filter) 97 | 环路滤波函数(Loop Filter)对解码后的数据进行滤波,去除方块效应。去块效应滤波是在DeblockPicture()中完成的。DeblockPicture()调用了DeblockMb()。而DeblockMb()中调用GetStrengthVer()、GetStrengthHor()函数获取滤波强度;调用EdgeLoopLumaVer()、EdgeLoopLumaHor()进行滤波。 98 | 99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | 109 | 原文作者: 雷霄骅 110 | -------------------------------------------------------------------------------- /article/015-Android 音视频技术.md: -------------------------------------------------------------------------------- 1 | # Android 音视频技术 2 | 3 | ## 1. 整体流程 4 | 5 | 以手机直播为例,其整体流程如下: 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127281849-20717a61-791a-48b5-b987-b35fe0bfaecc.png) 8 | 9 | ## 2. 数据采集 10 | 11 | ### 2.1. 音频采集 12 | 13 | 音频采集涉及到以下几点: 14 | 15 | 检测麦克风是否可以使用; 16 | 需要检测手机对某个音频采样率的支持; 17 | 在一些情况下需要对音频进行回声消除处理; 18 | 音频采集时设置正确的缓冲区大小。 19 | 20 | 在 Android 系统中,一般使用 AudioRecord 或者 MediaRecord 来采集音频。AudioRecord 是一个比较偏底层的 API,它可以获取到一帧帧 PCM 数据,之后可以对这些数据进行处理。而 MediaRecorder 是基于 AudioRecorder 的 API (最终还是会创建AudioRecord 用来与 AudioFlinger 进行交互) ,它可以直接将采集到的音频数据转化为执行的编码格式,并保存。 21 | 22 | ### 2.2 视频采集 23 | 24 | 视频采集涉及到以下几点: 25 | 26 | 检测摄像头是否可以使用; 27 | 摄像头采集到的图像是横向的,需要对采集到的图像进行一定的旋转后再进行显示; 28 | 摄像头采集时有一系列的图像大小可以选择,当采集的图像大小和手机屏幕大小比例不一致时,需要进行特殊处理; 29 | Android 手机摄像头有一系列的状态,需要在正确的状态下才能对摄像头进行相应的操作。 30 | Android 手机摄像头的很多参数存在兼容性问题,需要较好地处理这些兼容性的问题。 31 | 32 | 在 Android 系统下有两套 API 可以进行视频采集,它们是 Camera 和 Camera2 。Camera是以前老的 API ,从 Android 5.0(21) 之后就已经放弃了。和音频一样,也有高层和低层的 API,高层就是 Camera 和 MediaRecorder,可以快速实现编码,低层就是直接使用 Camera,然后将采集的数据进行滤镜、降噪等前处理,处理完成后由 MediaCodec 进行硬件编码,最后采用 MediaMuxer 生成最终的视频文件。 33 | 34 | ## 3. 数据处理 35 | 36 | ### 3.1 音频处理 37 | 38 | 可以对音频的原始流做处理,如降噪、回音、以及各种 filter 效果。 39 | 40 | ### 3.2 视频处理 41 | 现在抖音、美图秀秀等,在拍摄,视频处理方面,都提供了很多视频滤镜,而且还有各种贴纸、场景、人脸识别、特效、添加水印等。 42 | 43 | 其实对视频进行美颜和添加特效都是通过 OpenGL 进行处理的。Android 中有 GLSurfaceView,这个类似于 SurfaceView,不过可以利用 Renderer 对其进行渲染。通过 OpenGL 可以生成纹理,通过纹理的 Id 可以生成 SurfaceTexture,而 SurfaceTexture 可以交给 Camera,最后通过纹理就将摄像头预览画面和 OpenGL 建立了联系,从而可以通过 OpenGL 进行一系列的操作。 44 | 45 | 美颜的整个过程无非是根据 Camera 预览的纹理通过 OpenGL 中 FBO 技术生成一个新的纹理,然后在 Renderer 中的onDrawFrame() 使用新的纹理进行绘制。添加水印也就是先将一张图片转换为纹理,然后利用 OpenGL 进行绘制。添加动态挂件特效则比较复杂,先要根据当前的预览图片进行算法分析识别人脸部相应部位,然后在各个相应部位上绘制相应的图像,整个过程的实现有一定的难度,人脸识别技术目前有 OpenCV、Dlib、MTCNN 等。 46 | 47 | ## 4. 数据编码 48 | 49 | ### 4.1 音频编码 50 | 51 | Android 中利用 AudioRecord 可以录制声音,录制出来的声音是 PCM 声音,使用三个参数来表示声音,它们是:声道数、采样位数和采样频率。如果音频全部用 PCM 的格式进行传输,则占用带宽比较大,因此在传输之前需要对音频进行编码。 52 | 53 | 现在已经有一些广泛使用的声音格式,如:WAV、MIDI、MP3、WMA、AAC、Ogg 等等。相比于 PCM 格式而言,这些格式对声音数据进行了压缩处理,可以降低传输带宽。对音频进行编码也可以分为软编和硬编两种。软编则下载相应的编码库,写好相应的 JNI,然后传入数据进行编码。硬编则是使用 Android 自身提供的 MediaCodec。 54 | 55 | >硬编码和软编码的区别是:软编码可以在运行时确定、修改;而硬编码是不能够改变的。 56 | 57 | ### 4.2 视频编码 58 | 59 | 在 Android 平台上实现视频的编码有两种实现方式:一种是软编,一种是硬编。软编的话,往往是依托于 cpu,利用 cpu 的计算能力去进行编码。比如我们可以下载 x264 编码库,写好相关的 JNI 接口,然后传入相应的图像数据。经过 x264 库的处理以后就将原始的图像转换成为 h264 格式的视频。 60 | 61 | 硬编则是采用 Android 自身提供的 MediaCodec,使用 MediaCodec 需要传入相应的数据,这些数据可以是 YUV 的图像信息,也可以是一个 Surface,一般推荐使用 Surface,这样的话效率更高。Surface 直接使用本地视频数据缓存,而没有映射或复制它们到 ByteBuffers;因此,这种方式会更加高效。在使用 Surface 的时候,通常不能直接访问原始视频数据,但是可以使用ImageReader 类来访问不可靠的解码后 (或原始) 的视频帧。这可能仍然比使用 ByteBuffers 更加高效,因为一些本地缓存可以被映射到 direct ByteBuffers。当使用 ByteBuffer 模式,可以利用 Image 类和 getInput/OutputImage(int) 方法来访问到原始视频数据帧。 62 | 63 | ## 5. 音视频混合 64 | 65 | 下面我盗了一张图,画图实在太费时间: 66 | 67 | ![image](https://user-images.githubusercontent.com/87458342/127282286-ce81d398-43a8-4864-a615-0b87af159e44.png) 68 | 69 | 以合成 MP4 视频为例: 70 | 1. 整体来看,合成的 MP4 文件,视频部分为 H.264 编码格式的数据,音频部分为 AAC 编码格式的数据。 71 | 2. 通过 MediaMuxer 提供的接口-writeSampleData(),将 H.264 和 AAC 数据分别同时写入到 MP4 文件。 72 | 73 | ## 6. 数据传输 74 | 75 | 目前比较主流的视频推流协议有 RTMP 协议、RTSP 协议。 76 | 77 | ### 7. 需要用到的技术 78 | 79 | 涉及到如下技术,我将从图像、音频、视频的顺序来罗列: 80 | * Camera、Camera2。 81 | * SurfaceView、TextureView、SurfaceTexture、GLSurfaceView。 82 | * OpenGL ES。 83 | * OpenCV、DLIB。 84 | * YUV、PCM、H.264、H.265、ACC。 85 | * AudioRecord、AudioTrack。 86 | * MediaRecorder。 87 | * MediaCodec。 88 | * MediaExtractor、MediaMuxer。 89 | * ffmpeg、ijkplayer。 90 | * RTMP、RTSP。 91 | 92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | 103 | 原文作者: 况众文 104 | -------------------------------------------------------------------------------- /article/016-Web前端WebRTC攻略-媒体协商与SDP简析.md: -------------------------------------------------------------------------------- 1 | # Web前端WebRTC攻略-媒体协商与SDP简析 2 | 3 | ## 1. 媒体协商 4 | 5 | 在音视频通讯场景中,由于两端之间所支持的音视频编解码、传输协议、传输的速率,都需要进行彼此通知对方。 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127283102-66985088-10fc-4a4f-acff-de0a1b6c2c69.png) 8 | 9 | 我们把一个 1 对 1 的音视频通讯,比喻成双方互送快递包裹的过程。 10 | 11 | ![image](https://user-images.githubusercontent.com/87458342/127283134-b034b5c0-4ff1-4c6b-b1f1-87ff7a3957f1.png) 12 | 13 | 首先这里有很多问题,双方要彼此告知对方后,才能寄送包裹。 14 | 比如: 15 | 16 | * 我不知道包裹要寄给谁?(我要和谁建立通讯) 17 | * 对方能否使用我的包裹?(我的媒体格式对方是否支持) 18 | * 对方在哪里,地址是什么?(对方所处网络的位置在哪) 19 | * 走那条路线寄送最快?(走哪种网络传输最效率) 20 | 21 | ![image](https://user-images.githubusercontent.com/87458342/127283187-3e52b847-f911-4381-9c14-21bc34f71e39.png) 22 | 23 | 实际场景中,我们要打电话互相告诉对方一些信息。而在音视频通讯中,也需要这个“打电话”步骤,形式上一般是通过建立“信令通道”来传送信令。对于 Web 前端来说最常见以 WebSocket 来作为信令通道,通过它来交换信令并进行协商。真正的媒体数据,则是通过 RTCPeerConnection 进行传输。 24 | 25 | ![image](https://user-images.githubusercontent.com/87458342/127283215-d090aa68-f894-4634-81a1-0df22822dced.png) 26 | 27 | 比如包含什么媒体流/轨,或者是我的编码是否被对方的解码器所支持等等这些问题,则通过 SDP 作为载体告诉给对方。 28 | 29 | ### 1.1 什么是媒体协商? 30 | 31 | 在没有建立 WebRTC 连接传输数据前,首先需要让本地端和远端确认彼此共同支持的媒体能力。如:音视频编解码器、使用的传输协议、IP 端口和传输速率等等。而这些信息需要通过前文所说的 SDP 来互换,这个过程称之为媒体协商。 32 | 33 | ### 1.2 媒体协商的流程 34 | 这里以在两个前端浏览器建立通讯来进行说明,我们暂且称“发起端”和“应答端”。 35 | 36 | ![image](https://user-images.githubusercontent.com/87458342/127283293-b1518bd4-d10d-4ea1-9eb0-c9595db1653b.png) 37 | 38 | 1. 首先双方连接信令通道,(一般由业务决定如何实现),并能交换信令。 39 | 2. 发起端调用 RTCPeerConnection.createOffer 创建一个offer,并调用 setLocalDescription 设置本地的 SDP。 40 | 3. 然后通过信令服务器 将含有 SDP 的 offer 设置给应答端。 41 | 4. 应答端拿到此 offer 以后调用 setRemoteDescription 将此 SDP 信息保存。 42 | 5. 应答端调用 RTCPeerConnection.createAnswer 创建一个 answer,并调用 setLocalDescription 设置本地的 SDP。 43 | 6. 通过信令服务器将含有 SDP 的 answer 发送给发起端。 44 | 7. 发起端调用 setRemoteDescription 将此 SDP 信息保存。 45 | 46 | 简单概括就是:发起端和应答端通过 creatOffer 和 createAnswer 创建 offer/answerSDP,然后通过信令服务互换,最后调用 setLocalDescription/setRemoteDescription 进行设置本地和远端的 SDP 以完成协商。 47 | 48 | 在双方都创建 RTCPeerConnection 之后,它们就可以开始进行媒体协商了。 49 | 50 | ### 1.3 媒体协商的前端代码实现 51 | 52 | ##### 1.3.1 呼叫方创建&发送 Offer 53 | 54 | ```C++ 55 | //local 56 | var pc_local = new RTCPeerConnection(otps1); 57 | 58 | pc_local.createOffer((offer)=>{ 59 | pc_local.setLocalDescription(offer); 60 | singalChannel.send(offer) 61 | }, handleError); 62 | ``` 63 | ##### 1.3.2 应答方收到 Offer 64 | ```C++ 65 | //remote 66 | var pc_remote = new RTCPeerConnection(otps2); 67 | 68 | signalChannel.on('message', (message)=>{ 69 | if(message.type === 'offer'){ 70 | pc_remote.setRemoteDescription( 71 | new RTCSessionDescription(message) 72 | ) 73 | } 74 | }) 75 | ``` 76 | 77 | ##### 1.3.3 应答方创建&发送 Answer 78 | ```C++ 79 | //remote 80 | pc_remote.createAnswer((answer)=>{ 81 | pc_remote.setLocalDescription(answer); 82 | singalChannel.send(answer); 83 | }, handleError ); 84 | ``` 85 | 86 | ##### 1.3.4 呼叫方收到 Answer 87 | ```C++ 88 | //local 89 | signalChannel.on('message',(message)=>{ 90 | if(message.type==='answer'){ 91 | pc_local.setRemoteDescription( 92 | new RTCSessionDescription(message) 93 | ) 94 | } 95 | }) 96 | ``` 97 | 98 | ## 2 SDP 99 | ### 2.1 什么是SDP? 100 | SDP 全称 SessionDescription Protocal,直译就是通用会话描述协议。 101 | 102 | 光看直面意思可能不太好理解,其实就是描述双方的会话信息,以及各端所具备能力的通用协议。 103 | 104 | 在 WebRTC 中 SDP 所描述的信息主要有: 105 | 1. 各端所支持音视频编解码器 106 | 2. 编解码所设定的参数 107 | 3. 所使用的的传输协议 108 | 4. ICE 连接候选项等 109 | 110 | ### 2.2 标准SDP规范 111 | 要注意的是 SDP 并不是 WebRTC 独有规范,关于标准的 SDP 规范可以查阅:IETFRFC4556规范。 112 | 标准 SDP 规范主要包括 SDP 描述格式和 SDP 结构,而 SDP 结构由会话描述和媒体信息描述两个部分组成。 113 | 114 | ![image](https://user-images.githubusercontent.com/87458342/127283741-f62022cb-423a-42de-8fff-63f604388722.png) 115 | 116 | ### 2.3 SDP的格式 117 | 118 | SDP 是由多个 = 这样的表达式组成的。 119 | 120 | ```C++ 121 | v=0 122 | o=- 7017624586836067756 2 IN IP4 127.0.0.1 123 | s=- 124 | t=0 0... 125 | ``` 126 | * type 只能为一个字符,代表属性。 127 | * value 为结构化文本,UTF-8 编码,代表属性值。 128 | * = 两边不能有空格。 129 | 130 | SDPLine 没有统一的 Schema 描述,也就是没有一个固定的规则能解析所有 Line,SDPGrammer 只是描述了 SDP 相关的属性,具体每个属性的表达需要根据属性定义 IETFRFC4556。 131 | 而 SDP 的结构有一个会话描述和零至多个媒体信息描述组成。 132 | 133 | ##### 2.3.1 会话描述 134 | 135 | 常见属性: 136 | 137 | v=SDP 协议版本 138 | ```C++ 139 | v=0 140 | ``` 141 | 142 | o=会话发起者描述 143 | ```C++ 144 | o=
145 | ``` 146 | >username:用户名
147 | >sess-id:会话id,在整个会话中是唯一的,建议使用NTP时间戳。
148 | >sess-version:会话版本,每次会话数据修改后,该版本值会递增。
149 | >nettype:网络类型,一般为“IN”。
150 | >addrtype:地址类型,一般为IP4。
151 | >address:IP地址。 152 | 153 | s=会话名 154 | ```C++ 155 | s= 156 | ``` 157 | >不关注时可为- 158 | 159 | t=会话活动时间 160 | ```C++ 161 | t= 162 | ``` 163 | >start-time:会话开始时间
164 | >stop-time:结束时间
165 | >均为NTP时间,单位是秒,均为0时表示持久会话。 166 | 167 | c=连接信息 168 | ```C++ 169 | c= 170 | ``` 171 | >nettype:网路类型
172 | >addrtype:地址类型
173 | >connection-address:连接地址 174 | 175 | ##### 2.3.2 媒体描述 176 | 177 | 会话级别描述完成后,后面就是零到多个媒体级别描述,比如: 178 | 179 | 常见属性: 180 | 181 | m=媒体描述 182 | ```C++ 183 | m= 184 | ``` 185 | >media:媒体类型(audio / video)
186 | >port:端口号
187 | >transport:传输协议 RTP/AVP(RTP/SAVP)或 UDP
188 | >fmt-list:媒体格式,表述 RTP 的数据负载类型(PayloadType)的列表,可以包含多个。分别代表音频和视频的编码格式,后面会跟着 rtpmap、rtcp-fb、fmtp 这些属性来做进一步的详细的描述。
189 | >RTP类型参考:RTPPayload 190 | 191 | a=附加描述 192 | 有以下两种格式: 193 | >a=
194 | >a=: 195 | 196 | SDP 解析时,每个 SDPLine 都是以 key=... 形式,解析出 key 是 a 后,可能有两种方式,可参考 RFC4566: 197 | >在 m= 之前,为会话附加描述;
198 | >在 m= 之后,为媒体附加描述。
199 | >其中可以关注 rtpmap 和 fmtp。 200 | 201 | a=rtpmap RTP参数映射表 202 | ```C++ 203 | a=rtpmap:// 204 | ``` 205 | >playload-type:数据负载类型
206 | >encoding-name:编码名称
207 | >sample-rate:采样率
208 | >encodingparameters:编码参数 209 | 210 | a=fmtp 格式参数 211 | ```C++ 212 | a=fmtp: 213 | ``` 214 | >playload-type:数据负载类型
215 | >specific-parameters:编码参数 216 | 217 | ### 2.4 SDP剖析的示例结构与说明 218 | ![image](https://user-images.githubusercontent.com/87458342/127285081-98428cff-f298-4c70-972b-df3558c169ae.png) 219 | 220 | https://webrtchacks.com/sdp-anatomy/ 这个站点给我们展示了一个详细的 SDP 例子。左侧为 SDP 文本,可以明显看出 SDP 的格式与结构,右侧则对每一行描述进行了说明。如果你不想看冗长的规范文档,这个例子是一个不错的学习材料。 221 | 222 | ### 2.5 WebRTC 的 SDP 总结 223 | 224 | 在 WebRTC 中的 SDP 相对于标准 SDP 规范中有点不一样,它对于 SDP 划分了更多部分,详情可以看下图: 225 | ![image](https://user-images.githubusercontent.com/87458342/127285193-233e0173-0f20-433d-8990-9bb5ba0ca01a.png) 226 | 227 | WebRTC 按功能将 SDP 划分成了五部分,即会话元数据、网络描述、流描述、安全描述以及服务质量描述。WebRTCSDP 中的会话元数据(SessionMetadata)其实就是 SDP 标准规范中的会话层描述;流描述、网络描述与 SDP 标准规范中的媒体层描述是一致的;而安全描述与服务质量描述都是新增的一些属性描述。SDP 作为 WebRTC 的核心部分,是你深入学习 WebRTC 前所要必须掌握的基础内容。 228 | 229 | ## 3 参考文章 230 | 231 | * SDP: Session Description Protocol 232 | * https://webrtchacks.com/ 233 | 234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 | 243 | 244 | 原文作者: 腾讯IMWeb前端团队 245 | -------------------------------------------------------------------------------- /article/017-基于FFmpeg的AVfilter的例子-纯净版.md: -------------------------------------------------------------------------------- 1 | # 基于FFmpeg的AVfilter的例子-纯净版 2 | 3 | ## 流程图 4 | 5 | 该程序的流程图如下所示。AVFilter的初始化比较复杂,而使用起来比较简单。初始化的时候需要调用avfilter_register_all()到avfilter_graph_config()一系列函数。而使用的时候只有两个函数:av_buffersrc_add_frame()用于向FilterGraph中加入一个AVFrame,而av_buffersink_get_frame()用于从FilterGraph中取出一个AVFrame。 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127287056-ceb1ff2f-5a67-4b04-8dcf-4f26ca093f49.png) 8 | 9 | 流程中的关键函数如下所示: 10 | 11 | >avfilter_register_all():注册所有AVFilter。
12 | >avfilter_graph_alloc():为FilterGraph分配内存。
13 | >avfilter_graph_create_filter():创建并向FilterGraph中添加一个Filter。
14 | >avfilter_graph_parse_ptr():将一串通过字符串描述的Graph添加到FilterGraph中。
15 | >avfilter_graph_config():检查FilterGraph的配置。
16 | >av_buffersrc_add_frame():向FilterGraph中加入一个AVFrame。
17 | >av_buffersink_get_frame():从FilterGraph中取出一个AVFrame。 18 | 19 | ## 代码 20 | 21 | ```C++ 22 | /** 23 | * 本程序使用FFmpeg的AVfilter实现了YUV像素数据的滤镜处理功能。 24 | * 可以给YUV数据添加各种特效功能。 25 | * 是最简单的FFmpeg的AVFilter方面的教程。 26 | * 适合FFmpeg的初学者。 27 | * 28 | * This software uses FFmpeg's AVFilter to process YUV raw data. 29 | * It can add many excellent effect to YUV data. 30 | * It's the simplest example based on FFmpeg's AVFilter. 31 | * Suitable for beginner of FFmpeg 32 | * 33 | */ 34 | #include 35 | 36 | #define __STDC_CONSTANT_MACROS 37 | 38 | #ifdef _WIN32 39 | #define snprintf _snprintf 40 | //Windows 41 | extern "C" 42 | { 43 | #include "libavfilter/avfiltergraph.h" 44 | #include "libavfilter/buffersink.h" 45 | #include "libavfilter/buffersrc.h" 46 | #include "libavutil/avutil.h" 47 | #include "libavutil/imgutils.h" 48 | }; 49 | #else 50 | //Linux... 51 | #ifdef __cplusplus 52 | extern "C" 53 | { 54 | #endif 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #ifdef __cplusplus 61 | }; 62 | #endif 63 | #endif 64 | 65 | 66 | 67 | 68 | int main(int argc, char* argv[]) 69 | { 70 | int ret; 71 | AVFrame *frame_in; 72 | AVFrame *frame_out; 73 | unsigned char *frame_buffer_in; 74 | unsigned char *frame_buffer_out; 75 | 76 | AVFilterContext *buffersink_ctx; 77 | AVFilterContext *buffersrc_ctx; 78 | AVFilterGraph *filter_graph; 79 | static int video_stream_index = -1; 80 | 81 | //Input YUV 82 | FILE *fp_in=fopen("sintel_480x272_yuv420p.yuv","rb+"); 83 | if(fp_in==NULL){ 84 | printf("Error open input file.\n"); 85 | return -1; 86 | } 87 | int in_width=480; 88 | int in_height=272; 89 | 90 | //Output YUV 91 | FILE *fp_out=fopen("output.yuv","wb+"); 92 | if(fp_out==NULL){ 93 | printf("Error open output file.\n"); 94 | return -1; 95 | } 96 | 97 | //const char *filter_descr = "lutyuv='u=128:v=128'"; 98 | const char *filter_descr = "boxblur"; 99 | //const char *filter_descr = "hflip"; 100 | //const char *filter_descr = "hue='h=60:s=-3'"; 101 | //const char *filter_descr = "crop=2/3*in_w:2/3*in_h"; 102 | //const char *filter_descr = "drawbox=x=100:y=100:w=100:h=100:color=pink@0.5"; 103 | //const char *filter_descr = "drawtext=fontfile=arial.ttf:fontcolor=green:fontsize=30:text='Lei Xiaohua'"; 104 | 105 | avfilter_register_all(); 106 | 107 | char args[512]; 108 | AVFilter *buffersrc = avfilter_get_by_name("buffer"); 109 | AVFilter *buffersink = avfilter_get_by_name("ffbuffersink"); 110 | AVFilterInOut *outputs = avfilter_inout_alloc(); 111 | AVFilterInOut *inputs = avfilter_inout_alloc(); 112 | enum PixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, PIX_FMT_NONE }; 113 | AVBufferSinkParams *buffersink_params; 114 | 115 | filter_graph = avfilter_graph_alloc(); 116 | 117 | /* buffer video source: the decoded frames from the decoder will be inserted here. */ 118 | snprintf(args, sizeof(args), 119 | "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", 120 | in_width,in_height,AV_PIX_FMT_YUV420P, 121 | 1, 25,1,1); 122 | 123 | ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", 124 | args, NULL, filter_graph); 125 | if (ret < 0) { 126 | printf("Cannot create buffer source\n"); 127 | return ret; 128 | } 129 | 130 | /* buffer video sink: to terminate the filter chain. */ 131 | buffersink_params = av_buffersink_params_alloc(); 132 | buffersink_params->pixel_fmts = pix_fmts; 133 | ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", 134 | NULL, buffersink_params, filter_graph); 135 | av_free(buffersink_params); 136 | if (ret < 0) { 137 | printf("Cannot create buffer sink\n"); 138 | return ret; 139 | } 140 | 141 | /* Endpoints for the filter graph. */ 142 | outputs->name = av_strdup("in"); 143 | outputs->filter_ctx = buffersrc_ctx; 144 | outputs->pad_idx = 0; 145 | outputs->next = NULL; 146 | 147 | inputs->name = av_strdup("out"); 148 | inputs->filter_ctx = buffersink_ctx; 149 | inputs->pad_idx = 0; 150 | inputs->next = NULL; 151 | 152 | if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_descr, 153 | &inputs, &outputs, NULL)) < 0) 154 | return ret; 155 | 156 | if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) 157 | return ret; 158 | 159 | frame_in=av_frame_alloc(); 160 | frame_buffer_in=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width,in_height,1)); 161 | av_image_fill_arrays(frame_in->data, frame_in->linesize,frame_buffer_in, 162 | AV_PIX_FMT_YUV420P,in_width, in_height,1); 163 | 164 | frame_out=av_frame_alloc(); 165 | frame_buffer_out=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width,in_height,1)); 166 | av_image_fill_arrays(frame_out->data, frame_out->linesize,frame_buffer_out, 167 | AV_PIX_FMT_YUV420P,in_width, in_height,1); 168 | 169 | frame_in->width=in_width; 170 | frame_in->height=in_height; 171 | frame_in->format=AV_PIX_FMT_YUV420P; 172 | 173 | while (1) { 174 | 175 | if(fread(frame_buffer_in, 1, in_width*in_height*3/2, fp_in)!= in_width*in_height*3/2){ 176 | break; 177 | } 178 | //input Y,U,V 179 | frame_in->data[0]=frame_buffer_in; 180 | frame_in->data[1]=frame_buffer_in+in_width*in_height; 181 | frame_in->data[2]=frame_buffer_in+in_width*in_height*5/4; 182 | 183 | if (av_buffersrc_add_frame(buffersrc_ctx, frame_in) < 0) { 184 | printf( "Error while add frame.\n"); 185 | break; 186 | } 187 | 188 | /* pull filtered pictures from the filtergraph */ 189 | ret = av_buffersink_get_frame(buffersink_ctx, frame_out); 190 | if (ret < 0) 191 | break; 192 | 193 | //output Y,U,V 194 | if(frame_out->format==AV_PIX_FMT_YUV420P){ 195 | for(int i=0;iheight;i++){ 196 | fwrite(frame_out->data[0]+frame_out->linesize[0]*i,1,frame_out->width,fp_out); 197 | } 198 | for(int i=0;iheight/2;i++){ 199 | fwrite(frame_out->data[1]+frame_out->linesize[1]*i,1,frame_out->width/2,fp_out); 200 | } 201 | for(int i=0;iheight/2;i++){ 202 | fwrite(frame_out->data[2]+frame_out->linesize[2]*i,1,frame_out->width/2,fp_out); 203 | } 204 | } 205 | printf("Process 1 frame!\n"); 206 | av_frame_unref(frame_out); 207 | } 208 | 209 | fclose(fp_in); 210 | fclose(fp_out); 211 | 212 | av_frame_free(&frame_in); 213 | av_frame_free(&frame_out); 214 | avfilter_graph_free(&filter_graph); 215 | 216 | return 0; 217 | } 218 | ``` 219 | 220 | ## 结果 221 | 222 | 本程序输入为一个名称为“sintel_480x272_yuv420p.yuv”的YUV420P视频数据,输出为一个名称为“output.yuv” 的YUV420P视频数据。输入的视频数据的内容如下所示。 223 | 224 | ![image](https://user-images.githubusercontent.com/87458342/127287258-df6e444f-4984-4f51-8aa7-238bebe9f68d.png) 225 | 226 | 程序中提供了几种特效: 227 | >* lutyuv='u=128:v=128' 228 | >* boxblur 229 | >* hflip 230 | >* hue='h=60:s=-3' 231 | >* crop=2/3*in_w:2/3*in_h 232 | >* drawbox=x=100:y=100:w=100:h=100:color=pink@0.5 233 | >* drawtext=fontfile=arial.ttf:fontcolor=green:fontsize=30:text='Lei Xiaohua' 234 | 235 | 可以通过修改程序中的filter_descr字符串实现上述几种特效。下面展示几种特效的效果图。 236 | 237 | #### lutyuv='u=128:v=128' 238 | 239 | ![image](https://user-images.githubusercontent.com/87458342/127287371-a6714abe-4e9a-47b9-bad2-334f32a2c8fd.png) 240 | 241 | #### boxblur 242 | 243 | ![image](https://user-images.githubusercontent.com/87458342/127287427-a8b18b71-57b9-4706-af99-d78eaf660515.png) 244 | 245 | #### hflip 246 | 247 | ![image](https://user-images.githubusercontent.com/87458342/127287465-0c7dcd1f-1c35-4708-a6b3-9711c5c8d5b4.png) 248 | 249 | #### hue='h=60:s=-3' 250 | 251 | ![image](https://user-images.githubusercontent.com/87458342/127287494-e9797a9c-a8b3-44f8-9fe0-864eed57e73a.png) 252 | 253 | #### crop=2/3*in_w:2/3*in_h 254 | 255 | ![image](https://user-images.githubusercontent.com/87458342/127287537-62b9f7c8-b12a-46b3-b6cc-a657b135cfb7.png) 256 | 257 | #### drawbox=x=100:y=100:w=100:h=100:color=pink@0.5 258 | 259 | ![image](https://user-images.githubusercontent.com/87458342/127287575-11989ac5-7b9b-40f7-b623-12a72d540cc8.png) 260 | 261 | #### drawtext=fontfile=arial.ttf:fontcolor=green:fontsize=30:text='Lei Xiaohua' 262 | 263 | ![image](https://user-images.githubusercontent.com/87458342/127287598-d8c30a62-0a07-44c6-af04-46385c313d27.png) 264 | 265 | ## 下载 266 | 267 | Github:https://github.com/leixiaohua1020/simplest_ffmpeg_video_filter 268 | 269 | 本程序使用包含下面两个项目: 270 | * simplest_ffmpeg_video_filter:可以将一张PNG图片作为水印叠加到视频上,结合使用了libavfilter,libavcodec等类库。 271 | * simplest_ffmpeg_video_filter_pure:可以给YUV像素数据加特效,只用了libavfilter库。 272 | 273 | 274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 | 284 | 原文作者: 雷霄骅 285 | -------------------------------------------------------------------------------- /article/019-WebRTC能给我带来什么?.md: -------------------------------------------------------------------------------- 1 | # WebRTC能给我带来什么? 2 | 3 | WebRTC现在已经成为了W3C的正式标准,提供具有NAT遍历功能的次秒级的点对点视频和音频流。次秒级延迟已经被广泛应用于视频会议之中,也一直是视频流公司的焦点,如Millicast和Limelight(仅举两个例子),这些公司旨在将这种点对点技术交付给成千上万的人。在不到一秒钟的时间内便实现了交互式视频、游戏流、拍卖和超低延迟的体育运动。 4 | 5 | 针对直接使用其他流媒体协议的用户,Pion的创建者肖恩•杜布瓦(Sean DuBois)在SF Video Tech上谈到了WebRTC带来的RTMP、SRT和RIST等协议。它的核心是WebRTC(如SRT和RIST)创建一个连接,通过它可以发送各种数据。虽然我们期望媒体被发送,但是实际上,文件传输可以很容易地被实现——让我们不要忘记整个SRT是建立在UDT之上的,而UDT是一个专门用于文件传输的实用程序。在可以实现文件传输的地方,实时数据和元数据传输也可以实现。 6 | 7 | Sean很快将WebRTC概括为(典型)浏览器之间的协议,这是一种点对点的安全连接,多个音频和视频流可以在其连接上流动。与RIST和其他最新的协议一样,它基于许多已有的协议:SRTP、DTLS、ICE和SDP等技术来提供信令、连接管理、加密和通信。 8 | 17 | 18 | 对于RTMP非常长的改进列表,它们都在视频中被简明扼要地展现出来了,所以我们在这里只突出几个要点。重要的是,低延迟是其中的关键。RTMP在当时是属于低延迟的,但并不是以今天的低延迟标准。Sean解释说,谷歌的Stadia可以为按键提供125毫秒的视频延迟。DTLS和SRTP对于安全性来说是必不可少的,但是它们是众所周知便于理解和可靠的保护数据的方法。DTLS与TLS几乎完全相同,TLS保护您的银行转账,只是将其改为UDP而不是TCP中。但是,WebRTC可以通过交换“指纹”(DTLS-SRTP)而不是支持web上TLS的完全可信的证书基础结构来工作。只要您有信心可以提前安全地交换指纹,那么取消对证书的要求对于灵活性和敏捷性是一个很大的提升。 19 | 20 | NAT遍历也是一大福音,即使两个端点都在防火墙后面,端点也总能找到通信的方法,尽管这确实意味着需要ICE服务器来促进连接。然而,在广播中,你更有可能控制一端,这样就不太需要这样做了。Sean强调了使用WebRTC的“同步广播”功能在同一流中发送多个质量级别的能力。 21 | 22 | 之后Sean着眼于SRT和RIST。这两种协议都是低延迟流协议,它们都可以提供次秒级的流传输,以实现RTT相对较低的良好连接。Sean强调了SRT和RIST在协商使用中的编解码器及其可选安全性方面的不足。由于更注重提供贡献源,它们往往具有更静态的配置,通常是在测试程序之后创建的,以确保其质量能够被广播商/流媒体提供商所接受。 23 | 24 | 最后,Sean重点介绍了WebRTC的一系列有趣的创新用途,从非正式的群组流媒体到无人机、共享在线游戏到文件传输等等。 25 | -------------------------------------------------------------------------------- /article/022-视音频数据处理:H.264视频码流解析.md: -------------------------------------------------------------------------------- 1 | # 视音频数据处理:H.264视频码流解析 2 | 3 | 本文介绍视音频码流的处理程序。本文介绍的程序是视频码流处理程序。视频码流在视频播放器中的位置如下所示。 4 | 5 | ![image](https://user-images.githubusercontent.com/87458342/127297309-8a7ce953-5d01-4aaa-93eb-521b3827706b.png) 6 | 7 | 本文中的程序是一个H.264码流解析程序。该程序可以从H.264码流中分析得到它的基本单元NALU,并且可以简单解析NALU首部的字段。通过修改该程序可以实现不同的H.264码流处理功能。 8 | 9 | ## 原理 10 | 11 | H.264原始码流(又称为“裸流”)是由一个一个的NALU组成的。他们的结构如下图所示。 12 | 13 | ![image](https://user-images.githubusercontent.com/87458342/127297368-ba890f93-1a2a-4852-af9a-af85eeae16e6.png) 14 | 15 | 其中每个NALU之间通过startcode(起始码)进行分隔,起始码分成两种:0x000001(3Byte)或者0x00000001(4Byte)。如果NALU对应的Slice为一帧的开始就用0x00000001,否则就用0x000001。 16 | H.264码流解析的步骤就是首先从码流中搜索0x000001和0x00000001,分离出NALU;然后再分析NALU的各个字段。本文的程序即实现了上述的两个步骤。 17 | 18 | ## 代码 19 | 20 | 整个程序位于simplest_h264_parser()函数中,如下所示。 21 | 22 | ```C++ 23 | /** 24 | * 25 | * 本项目包含如下几种视音频测试示例: 26 | * (1)像素数据处理程序。包含RGB和YUV像素格式处理的函数。 27 | * (2)音频采样数据处理程序。包含PCM音频采样格式处理的函数。 28 | * (3)H.264码流分析程序。可以分离并解析NALU。 29 | * (4)AAC码流分析程序。可以分离并解析ADTS帧。 30 | * (5)FLV封装格式分析程序。可以将FLV中的MP3音频码流分离出来。 31 | * (6)UDP-RTP协议分析程序。可以将分析UDP/RTP/MPEG-TS数据包。 32 | * 33 | * This project contains following samples to handling multimedia data: 34 | * (1) Video pixel data handling program. It contains several examples to handle RGB and YUV data. 35 | * (2) Audio sample data handling program. It contains several examples to handle PCM data. 36 | * (3) H.264 stream analysis program. It can parse H.264 bitstream and analysis NALU of stream. 37 | * (4) AAC stream analysis program. It can parse AAC bitstream and analysis ADTS frame of stream. 38 | * (5) FLV format analysis program. It can analysis FLV file and extract MP3 audio stream. 39 | * (6) UDP-RTP protocol analysis program. It can analysis UDP/RTP/MPEG-TS Packet. 40 | * 41 | */ 42 | #include 43 | #include 44 | #include 45 | 46 | typedef enum { 47 | NALU_TYPE_SLICE = 1, 48 | NALU_TYPE_DPA = 2, 49 | NALU_TYPE_DPB = 3, 50 | NALU_TYPE_DPC = 4, 51 | NALU_TYPE_IDR = 5, 52 | NALU_TYPE_SEI = 6, 53 | NALU_TYPE_SPS = 7, 54 | NALU_TYPE_PPS = 8, 55 | NALU_TYPE_AUD = 9, 56 | NALU_TYPE_EOSEQ = 10, 57 | NALU_TYPE_EOSTREAM = 11, 58 | NALU_TYPE_FILL = 12, 59 | } NaluType; 60 | 61 | typedef enum { 62 | NALU_PRIORITY_DISPOSABLE = 0, 63 | NALU_PRIRITY_LOW = 1, 64 | NALU_PRIORITY_HIGH = 2, 65 | NALU_PRIORITY_HIGHEST = 3 66 | } NaluPriority; 67 | 68 | 69 | typedef struct 70 | { 71 | int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested) 72 | unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU) 73 | unsigned max_size; //! Nal Unit Buffer size 74 | int forbidden_bit; //! should be always FALSE 75 | int nal_reference_idc; //! NALU_PRIORITY_xxxx 76 | int nal_unit_type; //! NALU_TYPE_xxxx 77 | char *buf; //! contains the first byte followed by the EBSP 78 | } NALU_t; 79 | 80 | FILE *h264bitstream = NULL; //!< the bit stream file 81 | 82 | int info2=0, info3=0; 83 | 84 | static int FindStartCode2 (unsigned char *Buf){ 85 | if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1) return 0; //0x000001? 86 | else return 1; 87 | } 88 | 89 | static int FindStartCode3 (unsigned char *Buf){ 90 | if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=0 || Buf[3] !=1) return 0;//0x00000001? 91 | else return 1; 92 | } 93 | 94 | 95 | int GetAnnexbNALU (NALU_t *nalu){ 96 | int pos = 0; 97 | int StartCodeFound, rewind; 98 | unsigned char *Buf; 99 | 100 | if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL) 101 | printf ("GetAnnexbNALU: Could not allocate Buf memory\n"); 102 | 103 | nalu->startcodeprefix_len=3; 104 | 105 | if (3 != fread (Buf, 1, 3, h264bitstream)){ 106 | free(Buf); 107 | return 0; 108 | } 109 | info2 = FindStartCode2 (Buf); 110 | if(info2 != 1) { 111 | if(1 != fread(Buf+3, 1, 1, h264bitstream)){ 112 | free(Buf); 113 | return 0; 114 | } 115 | info3 = FindStartCode3 (Buf); 116 | if (info3 != 1){ 117 | free(Buf); 118 | return -1; 119 | } 120 | else { 121 | pos = 4; 122 | nalu->startcodeprefix_len = 4; 123 | } 124 | } 125 | else{ 126 | nalu->startcodeprefix_len = 3; 127 | pos = 3; 128 | } 129 | StartCodeFound = 0; 130 | info2 = 0; 131 | info3 = 0; 132 | 133 | while (!StartCodeFound){ 134 | if (feof (h264bitstream)){ 135 | nalu->len = (pos-1)-nalu->startcodeprefix_len; 136 | memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len); 137 | nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit 138 | nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit 139 | nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit 140 | free(Buf); 141 | return pos-1; 142 | } 143 | Buf[pos++] = fgetc (h264bitstream); 144 | info3 = FindStartCode3(&Buf[pos-4]); 145 | if(info3 != 1) 146 | info2 = FindStartCode2(&Buf[pos-3]); 147 | StartCodeFound = (info2 == 1 || info3 == 1); 148 | } 149 | 150 | // Here, we have found another start code (and read length of startcode bytes more than we should 151 | // have. Hence, go back in the file 152 | rewind = (info3 == 1)? -4 : -3; 153 | 154 | if (0 != fseek (h264bitstream, rewind, SEEK_CUR)){ 155 | free(Buf); 156 | printf("GetAnnexbNALU: Cannot fseek in the bit stream file"); 157 | } 158 | 159 | // Here the Start code, the complete NALU, and the next start code is in the Buf. 160 | // The size of Buf is pos, pos+rewind are the number of bytes excluding the next 161 | // start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code 162 | 163 | nalu->len = (pos+rewind)-nalu->startcodeprefix_len; 164 | memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);// 165 | nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit 166 | nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit 167 | nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit 168 | free(Buf); 169 | 170 | return (pos+rewind); 171 | } 172 | 173 | /** 174 | * Analysis H.264 Bitstream 175 | * @param url Location of input H.264 bitstream file. 176 | */ 177 | int simplest_h264_parser(char *url){ 178 | 179 | NALU_t *n; 180 | int buffersize=100000; 181 | 182 | //FILE *myout=fopen("output_log.txt","wb+"); 183 | FILE *myout=stdout; 184 | 185 | h264bitstream=fopen(url, "rb+"); 186 | if (h264bitstream==NULL){ 187 | printf("Open file error\n"); 188 | return 0; 189 | } 190 | 191 | n = (NALU_t*)calloc (1, sizeof (NALU_t)); 192 | if (n == NULL){ 193 | printf("Alloc NALU Error\n"); 194 | return 0; 195 | } 196 | 197 | n->max_size=buffersize; 198 | n->buf = (char*)calloc (buffersize, sizeof (char)); 199 | if (n->buf == NULL){ 200 | free (n); 201 | printf ("AllocNALU: n->buf"); 202 | return 0; 203 | } 204 | 205 | int data_offset=0; 206 | int nal_num=0; 207 | printf("-----+-------- NALU Table ------+---------+\n"); 208 | printf(" NUM | POS | IDC | TYPE | LEN |\n"); 209 | printf("-----+---------+--------+-------+---------+\n"); 210 | 211 | while(!feof(h264bitstream)) 212 | { 213 | int data_lenth; 214 | data_lenth=GetAnnexbNALU(n); 215 | 216 | char type_str[20]={0}; 217 | switch(n->nal_unit_type){ 218 | case NALU_TYPE_SLICE:sprintf(type_str,"SLICE");break; 219 | case NALU_TYPE_DPA:sprintf(type_str,"DPA");break; 220 | case NALU_TYPE_DPB:sprintf(type_str,"DPB");break; 221 | case NALU_TYPE_DPC:sprintf(type_str,"DPC");break; 222 | case NALU_TYPE_IDR:sprintf(type_str,"IDR");break; 223 | case NALU_TYPE_SEI:sprintf(type_str,"SEI");break; 224 | case NALU_TYPE_SPS:sprintf(type_str,"SPS");break; 225 | case NALU_TYPE_PPS:sprintf(type_str,"PPS");break; 226 | case NALU_TYPE_AUD:sprintf(type_str,"AUD");break; 227 | case NALU_TYPE_EOSEQ:sprintf(type_str,"EOSEQ");break; 228 | case NALU_TYPE_EOSTREAM:sprintf(type_str,"EOSTREAM");break; 229 | case NALU_TYPE_FILL:sprintf(type_str,"FILL");break; 230 | } 231 | char idc_str[20]={0}; 232 | switch(n->nal_reference_idc>>5){ 233 | case NALU_PRIORITY_DISPOSABLE:sprintf(idc_str,"DISPOS");break; 234 | case NALU_PRIRITY_LOW:sprintf(idc_str,"LOW");break; 235 | case NALU_PRIORITY_HIGH:sprintf(idc_str,"HIGH");break; 236 | case NALU_PRIORITY_HIGHEST:sprintf(idc_str,"HIGHEST");break; 237 | } 238 | 239 | fprintf(myout,"%5d| %8d| %7s| %6s| %8d|\n",nal_num,data_offset,idc_str,type_str,n->len); 240 | 241 | data_offset=data_offset+data_lenth; 242 | 243 | nal_num++; 244 | } 245 | 246 | //Free 247 | if (n){ 248 | if (n->buf){ 249 | free(n->buf); 250 | n->buf=NULL; 251 | } 252 | free (n); 253 | } 254 | return 0; 255 | } 256 | ``` 257 | 258 | 上文中的函数调用方法如下所示。 259 | ```C++ 260 | simplest_h264_parser("sintel.h264"); 261 | ``` 262 | 263 | ## 结果 264 | 本程序的输入为一个H.264原始码流(裸流)的文件路径,输出为该码流的NALU统计数据,如下图所示。 265 | 266 | ![image](https://user-images.githubusercontent.com/87458342/127297535-4e84efb8-53ff-46ed-8b59-9c38222dc8a5.png) 267 | 268 | ## 项目主页 269 | 270 | Github:https://github.com/leixiaohua1020/simplest_mediadata_test 271 | 272 |
273 | 274 | 本项目包含如下几种视音频数据解析示例: 275 | 1. 像素数据处理程序。包含RGB和YUV像素格式处理的函数。 276 | 2. 音频采样数据处理程序。包含PCM音频采样格式处理的函数。 277 | 3. H.264码流分析程序。可以分离并解析NALU。 278 | 4. AAC码流分析程序。可以分离并解析ADTS帧。 279 | 5. FLV封装格式分析程序。可以将FLV中的MP3音频码流分离出来。 280 | 6. UDP-RTP协议分析程序。可以将分析UDP/RTP/MPEG-TS数据包。 281 | 282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 | 292 | 原文作者: 雷霄骅 293 | -------------------------------------------------------------------------------- /article/023-视音频数据处理:AAC音频码流解析.md: -------------------------------------------------------------------------------- 1 | # 视音频数据处理:AAC音频码流解析 2 | 3 | 本文介绍一个音频码流处理程序。音频码流在视频播放器中的位置如下所示。 4 | 5 | ![image](https://user-images.githubusercontent.com/87458342/127298479-1267f5f4-b1a6-485e-8604-5b22c0a98674.png) 6 | 7 | 本文中的程序是一个AAC码流解析程序。该程序可以从AAC码流中分析得到它的基本单元ADTS frame,并且可以简单解析ADTS frame首部的字段。通过修改该程序可以实现不同的AAC码流处理功能。 8 | 9 | ## 原理 10 | 11 | AAC原始码流(又称为“裸流”)是由一个一个的ADTS frame组成的。他们的结构如下图所示。 12 | 13 | ![image](https://user-images.githubusercontent.com/87458342/127298567-de22a967-c362-42fd-a220-8a5e09146742.png) 14 | 15 | 其中每个ADTS frame之间通过syncword(同步字)进行分隔。同步字为0xFFF(二进制“111111111111”)。AAC码流解析的步骤就是首先从码流中搜索0x0FFF,分离出ADTS frame;然后再分析ADTS frame的首部各个字段。本文的程序即实现了上述的两个步骤。 16 | 17 | ## 代码 18 | 19 | 整个程序位于simplest_aac_parser()函数中,如下所示。 20 | 21 | ```C++ 22 | /** 23 | * 本项目包含如下几种视音频测试示例: 24 | * (1)像素数据处理程序。包含RGB和YUV像素格式处理的函数。 25 | * (2)音频采样数据处理程序。包含PCM音频采样格式处理的函数。 26 | * (3)H.264码流分析程序。可以分离并解析NALU。 27 | * (4)AAC码流分析程序。可以分离并解析ADTS帧。 28 | * (5)FLV封装格式分析程序。可以将FLV中的MP3音频码流分离出来。 29 | * (6)UDP-RTP协议分析程序。可以将分析UDP/RTP/MPEG-TS数据包。 30 | * 31 | * This project contains following samples to handling multimedia data: 32 | * (1) Video pixel data handling program. It contains several examples to handle RGB and YUV data. 33 | * (2) Audio sample data handling program. It contains several examples to handle PCM data. 34 | * (3) H.264 stream analysis program. It can parse H.264 bitstream and analysis NALU of stream. 35 | * (4) AAC stream analysis program. It can parse AAC bitstream and analysis ADTS frame of stream. 36 | * (5) FLV format analysis program. It can analysis FLV file and extract MP3 audio stream. 37 | * (6) UDP-RTP protocol analysis program. It can analysis UDP/RTP/MPEG-TS Packet. 38 | * 39 | */ 40 | #include 41 | #include 42 | #include 43 | 44 | 45 | int getADTSframe(unsigned char* buffer, int buf_size, unsigned char* data ,int* data_size){ 46 | int size = 0; 47 | 48 | if(!buffer || !data || !data_size ){ 49 | return -1; 50 | } 51 | 52 | while(1){ 53 | if(buf_size < 7 ){ 54 | return -1; 55 | } 56 | //Sync words 57 | if((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0) ){ 58 | size |= ((buffer[3] & 0x03) <<11); //high 2 bit 59 | size |= buffer[4]<<3; //middle 8 bit 60 | size |= ((buffer[5] & 0xe0)>>5); //low 3bit 61 | break; 62 | } 63 | --buf_size; 64 | ++buffer; 65 | } 66 | 67 | if(buf_size < size){ 68 | return 1; 69 | } 70 | 71 | memcpy(data, buffer, size); 72 | *data_size = size; 73 | 74 | return 0; 75 | } 76 | 77 | int simplest_aac_parser(char *url) 78 | { 79 | int data_size = 0; 80 | int size = 0; 81 | int cnt=0; 82 | int offset=0; 83 | 84 | //FILE *myout=fopen("output_log.txt","wb+"); 85 | FILE *myout=stdout; 86 | 87 | unsigned char *aacframe=(unsigned char *)malloc(1024*5); 88 | unsigned char *aacbuffer=(unsigned char *)malloc(1024*1024); 89 | 90 | FILE *ifile = fopen(url, "rb"); 91 | if(!ifile){ 92 | printf("Open file error"); 93 | return -1; 94 | } 95 | 96 | printf("-----+- ADTS Frame Table -+------+\n"); 97 | printf(" NUM | Profile | Frequency| Size |\n"); 98 | printf("-----+---------+----------+------+\n"); 99 | 100 | while(!feof(ifile)){ 101 | data_size = fread(aacbuffer+offset, 1, 1024*1024-offset, ifile); 102 | unsigned char* input_data = aacbuffer; 103 | 104 | while(1) 105 | { 106 | int ret=getADTSframe(input_data, data_size, aacframe, &size); 107 | if(ret==-1){ 108 | break; 109 | }else if(ret==1){ 110 | memcpy(aacbuffer,input_data,data_size); 111 | offset=data_size; 112 | break; 113 | } 114 | 115 | char profile_str[10]={0}; 116 | char frequence_str[10]={0}; 117 | 118 | unsigned char profile=aacframe[2]&0xC0; 119 | profile=profile>>6; 120 | switch(profile){ 121 | case 0: sprintf(profile_str,"Main");break; 122 | case 1: sprintf(profile_str,"LC");break; 123 | case 2: sprintf(profile_str,"SSR");break; 124 | default:sprintf(profile_str,"unknown");break; 125 | } 126 | 127 | unsigned char sampling_frequency_index=aacframe[2]&0x3C; 128 | sampling_frequency_index=sampling_frequency_index>>2; 129 | switch(sampling_frequency_index){ 130 | case 0: sprintf(frequence_str,"96000Hz");break; 131 | case 1: sprintf(frequence_str,"88200Hz");break; 132 | case 2: sprintf(frequence_str,"64000Hz");break; 133 | case 3: sprintf(frequence_str,"48000Hz");break; 134 | case 4: sprintf(frequence_str,"44100Hz");break; 135 | case 5: sprintf(frequence_str,"32000Hz");break; 136 | case 6: sprintf(frequence_str,"24000Hz");break; 137 | case 7: sprintf(frequence_str,"22050Hz");break; 138 | case 8: sprintf(frequence_str,"16000Hz");break; 139 | case 9: sprintf(frequence_str,"12000Hz");break; 140 | case 10: sprintf(frequence_str,"11025Hz");break; 141 | case 11: sprintf(frequence_str,"8000Hz");break; 142 | default:sprintf(frequence_str,"unknown");break; 143 | } 144 | 145 | 146 | fprintf(myout,"%5d| %8s| %8s| %5d|\n",cnt,profile_str ,frequence_str,size); 147 | data_size -= size; 148 | input_data += size; 149 | cnt++; 150 | } 151 | 152 | } 153 | fclose(ifile); 154 | free(aacbuffer); 155 | free(aacframe); 156 | 157 | return 0; 158 | } 159 | ``` 160 | 161 | 上文中的函数调用方法如下所示。 162 | ```C++ 163 | simplest_aac_parser("nocturne.aac"); 164 | ``` 165 | 166 | ## 结果 167 | 168 | 本程序的输入为一个AAC原始码流(裸流)的文件路径,输出为该码流中ADTS frame的统计数据,如下图所示。 169 | ![image](https://user-images.githubusercontent.com/87458342/127298745-8c87f2cc-3daf-41d2-8e18-fb2f7ad57af4.png) 170 | 171 | ## 项目主页 172 | 173 | Github:https://github.com/leixiaohua1020/simplest_mediadata_test 174 | 175 | 本项目包含如下几种视音频数据解析示例: 176 | 1. 像素数据处理程序。包含RGB和YUV像素格式处理的函数。 177 | 2. 音频采样数据处理程序。包含PCM音频采样格式处理的函数。 178 | 3. H.264码流分析程序。可以分离并解析NALU。 179 | 4. AAC码流分析程序。可以分离并解析ADTS帧。 180 | 5. FLV封装格式分析程序。可以将FLV中的MP3音频码流分离出来。 181 | 6. UDP-RTP协议分析程序。可以将分析UDP/RTP/MPEG-TS数据包。 182 | 183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | 193 | 原文作者 : 雷霄骅 194 | 195 | -------------------------------------------------------------------------------- /article/025-视音频数据处理:UDP-RTP协议解析.md: -------------------------------------------------------------------------------- 1 | # 视音频数据处理:UDP-RTP协议解析 2 | 3 | 本文介绍网络协议数据的处理程序。网络协议数据在视频播放器中的位置如下所示。 4 | 5 | ![image](https://user-images.githubusercontent.com/87458342/127299682-8426891f-6b61-4227-951a-b7b51812cfe1.png) 6 | 7 | 本文中的程序是一个UDP/RTP协议流媒体数据解析器。该程序可以分析UDP协议中的RTP 包头中的内容,以及RTP负载中MPEG-TS封装格式的信息。通过修改该程序可以实现不同的UDP/RTP协议数据处理功能。 8 | 9 | ## 原理 10 | 11 | MPEG-TS封装格式数据打包为RTP/UDP协议然后发送出去的流程如下图所示。图中首先每7个MPEG-TS Packet打包为一个RTP,然后每个RTP再打包为一个UDP。其中打包RTP的方法就是在MPEG-TS数据前面加上RTP Header,而打包RTP的方法就是在RTP数据前面加上UDP Header。 12 | 13 | ![image](https://user-images.githubusercontent.com/87458342/127299752-d161a8b4-1dd7-4050-ae8e-146226dc050a.png) 14 | 15 | 有关MPEG-TS、RTP、UDP的知识不再详细介绍,可以参考相关的文档了解其中的细节信息。本文记录的程序是一个收取流媒体的程序,因此本文程序的流程和上述发送MPEG-TS的流程正好是相反的。该程序可以通过Socket编程收取UDP包,解析其中的RTP包的信息,然后再解析RTP包中MPEG-TS Packet的信息。 16 | 17 | ## 代码 18 | 19 | 整个程序位于simplest_udp_parser()函数中,如下所示。 20 | 21 | ```C++ 22 | /** 23 | * 本项目包含如下几种视音频测试示例: 24 | * (1)像素数据处理程序。包含RGB和YUV像素格式处理的函数。 25 | * (2)音频采样数据处理程序。包含PCM音频采样格式处理的函数。 26 | * (3)H.264码流分析程序。可以分离并解析NALU。 27 | * (4)AAC码流分析程序。可以分离并解析ADTS帧。 28 | * (5)FLV封装格式分析程序。可以将FLV中的MP3音频码流分离出来。 29 | * (6)UDP-RTP协议分析程序。可以将分析UDP/RTP/MPEG-TS数据包。 30 | * 31 | * This project contains following samples to handling multimedia data: 32 | * (1) Video pixel data handling program. It contains several examples to handle RGB and YUV data. 33 | * (2) Audio sample data handling program. It contains several examples to handle PCM data. 34 | * (3) H.264 stream analysis program. It can parse H.264 bitstream and analysis NALU of stream. 35 | * (4) AAC stream analysis program. It can parse AAC bitstream and analysis ADTS frame of stream. 36 | * (5) FLV format analysis program. It can analysis FLV file and extract MP3 audio stream. 37 | * (6) UDP-RTP protocol analysis program. It can analysis UDP/RTP/MPEG-TS Packet. 38 | * 39 | */ 40 | #include 41 | #include 42 | 43 | #pragma comment(lib, "ws2_32.lib") 44 | 45 | #pragma pack(1) 46 | 47 | /* 48 | * [memo] FFmpeg stream Command: 49 | * ffmpeg -re -i sintel.ts -f mpegts udp://127.0.0.1:8880 50 | * ffmpeg -re -i sintel.ts -f rtp_mpegts udp://127.0.0.1:8880 51 | */ 52 | 53 | typedef struct RTP_FIXED_HEADER{ 54 | /* byte 0 */ 55 | unsigned char csrc_len:4; /* expect 0 */ 56 | unsigned char extension:1; /* expect 1 */ 57 | unsigned char padding:1; /* expect 0 */ 58 | unsigned char version:2; /* expect 2 */ 59 | /* byte 1 */ 60 | unsigned char payload:7; 61 | unsigned char marker:1; /* expect 1 */ 62 | /* bytes 2, 3 */ 63 | unsigned short seq_no; 64 | /* bytes 4-7 */ 65 | unsigned long timestamp; 66 | /* bytes 8-11 */ 67 | unsigned long ssrc; /* stream number is used here. */ 68 | } RTP_FIXED_HEADER; 69 | 70 | typedef struct MPEGTS_FIXED_HEADER { 71 | unsigned sync_byte: 8; 72 | unsigned transport_error_indicator: 1; 73 | unsigned payload_unit_start_indicator: 1; 74 | unsigned transport_priority: 1; 75 | unsigned PID: 13; 76 | unsigned scrambling_control: 2; 77 | unsigned adaptation_field_exist: 2; 78 | unsigned continuity_counter: 4; 79 | } MPEGTS_FIXED_HEADER; 80 | 81 | 82 | 83 | int simplest_udp_parser(int port) 84 | { 85 | WSADATA wsaData; 86 | WORD sockVersion = MAKEWORD(2,2); 87 | int cnt=0; 88 | 89 | //FILE *myout=fopen("output_log.txt","wb+"); 90 | FILE *myout=stdout; 91 | 92 | FILE *fp1=fopen("output_dump.ts","wb+"); 93 | 94 | if(WSAStartup(sockVersion, &wsaData) != 0){ 95 | return 0; 96 | } 97 | 98 | SOCKET serSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 99 | if(serSocket == INVALID_SOCKET){ 100 | printf("socket error !"); 101 | return 0; 102 | } 103 | 104 | sockaddr_in serAddr; 105 | serAddr.sin_family = AF_INET; 106 | serAddr.sin_port = htons(port); 107 | serAddr.sin_addr.S_un.S_addr = INADDR_ANY; 108 | if(bind(serSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){ 109 | printf("bind error !"); 110 | closesocket(serSocket); 111 | return 0; 112 | } 113 | 114 | sockaddr_in remoteAddr; 115 | int nAddrLen = sizeof(remoteAddr); 116 | 117 | //How to parse? 118 | int parse_rtp=1; 119 | int parse_mpegts=1; 120 | 121 | printf("Listening on port %d\n",port); 122 | 123 | char recvData[10000]; 124 | while (1){ 125 | 126 | int pktsize = recvfrom(serSocket, recvData, 10000, 0, (sockaddr *)&remoteAddr, &nAddrLen); 127 | if (pktsize > 0){ 128 | //printf("Addr:%s\r\n",inet_ntoa(remoteAddr.sin_addr)); 129 | //printf("packet size:%d\r\n",pktsize); 130 | //Parse RTP 131 | // 132 | if(parse_rtp!=0){ 133 | char payload_str[10]={0}; 134 | RTP_FIXED_HEADER rtp_header; 135 | int rtp_header_size=sizeof(RTP_FIXED_HEADER); 136 | //RTP Header 137 | memcpy((void *)&rtp_header,recvData,rtp_header_size); 138 | 139 | //RFC3551 140 | char payload=rtp_header.payload; 141 | switch(payload){ 142 | case 0: 143 | case 1: 144 | case 2: 145 | case 3: 146 | case 4: 147 | case 5: 148 | case 6: 149 | case 7: 150 | case 8: 151 | case 9: 152 | case 10: 153 | case 11: 154 | case 12: 155 | case 13: 156 | case 14: 157 | case 15: 158 | case 16: 159 | case 17: 160 | case 18: sprintf(payload_str,"Audio");break; 161 | case 31: sprintf(payload_str,"H.261");break; 162 | case 32: sprintf(payload_str,"MPV");break; 163 | case 33: sprintf(payload_str,"MP2T");break; 164 | case 34: sprintf(payload_str,"H.263");break; 165 | case 96: sprintf(payload_str,"H.264");break; 166 | default:sprintf(payload_str,"other");break; 167 | } 168 | 169 | unsigned int timestamp=ntohl(rtp_header.timestamp); 170 | unsigned int seq_no=ntohs(rtp_header.seq_no); 171 | 172 | fprintf(myout,"[RTP Pkt] %5d| %5s| %10u| %5d| %5d|\n",cnt,payload_str,timestamp,seq_no,pktsize); 173 | 174 | //RTP Data 175 | char *rtp_data=recvData+rtp_header_size; 176 | int rtp_data_size=pktsize-rtp_header_size; 177 | fwrite(rtp_data,rtp_data_size,1,fp1); 178 | 179 | //Parse MPEGTS 180 | if(parse_mpegts!=0&&payload==33){ 181 | MPEGTS_FIXED_HEADER mpegts_header; 182 | for(int i=0;i 242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 | 252 | 原文作者: 雷霄骅 253 | -------------------------------------------------------------------------------- /article/027-ffmpeg滤镜的基本使用.md: -------------------------------------------------------------------------------- 1 | # ffmpeg滤镜的基本使用 2 | 3 | ## 滤镜 4 | 5 | 滤镜主要是用来实现图像的各种特殊效果。 6 | 7 | ffmpeg转码流程图: 8 | 9 | ![image](https://user-images.githubusercontent.com/87458342/127450563-55f6c0f1-1ec4-4ca0-9a2c-5fc87ced45f9.png) 10 | 11 | 从图中可以看到滤镜前后画的是虚线,表示可有可无,在术语中,滤镜指的是在编码之前针对解码器解码出来的原始数据(即音视频帧)进行处理的动作,我们还可以称它为过滤器。 12 | 13 | ffmpeg内置了大概近400种滤镜,我们可以用 ffmpeg -filters 命令查看所有的滤镜,也可以用命令 ffmpeg -h filter=xxx 或者查看官方文档了解每一种滤镜。 14 | 15 | 实际在大部分音视频的处理过程中都离不开滤镜,所以你应该能明白其重要性。 16 | 17 | 多个滤镜可以结合在一起使用形成滤镜链或者滤镜图,在每一个滤镜中,不仅可以对输入源进行处理,A滤镜处理好的结果还可以作为B滤镜的输入参数,通过B滤镜继续处理。 18 | 19 | 针对滤镜的处理,ffmpeg提供了两种处理方式,简单滤镜和复杂滤镜。 20 | 21 | ## 简单滤镜 22 | 23 | 简单滤镜指的是只有一个输入和输出,而且保证输入和输出的流类型相同。 24 | 25 | 比如在末尾提到的把原视频 r3.mp4 等比例缩放一倍 26 | ```C++ 27 | ffmpeg -i r3.mp4 -vf scale=272:480 -y filter.mp4 28 | ``` 29 | 30 | -vf 是 -filter:v 的简写,类似的我们还可以使用 -filter:a 或者 -af 针对音频流做处理。 31 | 32 | >-filter的语法规则:-filter[:stream_specifier] filtergraph (output,per-stream) 33 | >stream_specifier流的类型我们一般用a表示音频,v表示视频,filtergraph表示具体的滤镜,这里用的是scale滤镜。 34 | 35 | scale滤镜用于调整视频的大小,比如等比例缩放、等比例放大,不做等比例操作输出就变形了,变形结果我们一般不考虑。 36 | 37 | 因为我们知道原视频 r1ori.mp4 的分辨率是 544x960,所以等比例缩放一倍,上面的命令直接指定了 272x480,scale滤镜自带很多参数,我们介绍几个常用的。 38 | 39 | >in_w in_h 或者 iw ih 表示输入视频的宽高 40 | >out_w out_h 或者 ow oh 表示输出视频的宽高 41 | 42 | 当然不一定是视频,输入输出也可以是图片。 43 | 44 | 所以原视频缩放一倍我们还可以这样写: 45 | 46 | ```C++ 47 | ffmpeg -i r3.mp4 -vf scale=iw/2:ih/2 -y filter.mp4 48 | ``` 49 | 50 | ##### 问题1:如果要把原视频的宽度调整为300且保持原分辨率,怎么办? 51 | 52 | 列一个方程 544/960 = 300/x ,x=300x960/540,很麻烦,结果还不一定能整除,为此我们可以直接指定高度等于-1,它会自动做等比例处理。 53 | ```C++ 54 | ffmpeg -i r1ori.mp4 -vf scale=300:-1 -y filter.mp4 55 | ``` 56 | 结果发现转码失败了,提示 57 | 58 | ```C++ 59 | [libx264 @ 0x7ff509053a00] height not divisible by 2 (300x529) 60 | Error initializing output stream 0:0 -- 61 | Error while opening encoder for output stream #0:0 - 62 | maybe incorrect parameters such as bit_rate, rate, width or height [aac @ 0x7ff50904e200] 63 | Qavg: 28010.410 [aac @ 0x7ff50904e200] 2 frames left in the queue on closing 64 | ``` 65 | 66 | 提示我们 height not divisible by 2 (300x529)即高度529不能被2整除。这是因为一些编解码器要求很多视频的宽高必须是n的倍数(这里n是2),所以我们写脚本处理视频或者图片宽高的时候,切记不要使用-1,正确的用法是使用-2。 67 | 68 | ```C++ 69 | ffmpeg -i r1ori.mp4 -vf scale=300:-2 -y filter.mp4 输出结果视频的分辨率是 300 × 530 70 | ``` 71 | 72 | ##### 问题2:老板为了刁难你,提出了一个新的要求:“我想要所有输出视频的分辨率是 300x500且不能变形”,怎么办? 73 | 74 | 我们知道3:5的宽高比是很少见的,现在常见的分辨率是16:9、4:3,也就是说原视频我们必须要经过一番处理才可以满足老板的变态需求。 75 | 76 | 针对原视频 r1ori.mp4,如果保证宽度是300,等比例缩放后高度是530,强制设置高度为500就会变形,也就是说我们只能让高度等于500,尽量缩小宽度试试。 77 | 78 | ```C++ 79 | ffmpeg -i r1ori.mp4 -vf scale=-2:500 -y filter.mp4 输出的结果视频的分辨率是284x500 80 | ``` 81 | 82 | ![image](https://user-images.githubusercontent.com/87458342/127451223-9d5ef6d0-89dc-47f2-908d-263590f39fe8.png) 83 | 84 | 如上图,蓝色框表示视频的真实宽高,红色框表示目标宽高,有些像html中的css一样,可以给空出来的部分填充颜色即内边距不就可以了? 85 | 86 | 查阅了文档发现pad滤镜可以解决我们的问题。 87 | 88 | >pad滤镜的语法规则:-pad=width[:height[:x[:y[:color]]]] 89 | 90 | ```C++ 91 | 1、ffmpeg -i r1ori.mp4 -vf "scale=-2:500,pad=300:500:(300-iw)/2:0" -y filter2.mp4 92 | 2、ffmpeg -i r1ori.mp4 -vf scale=-2:500,pad=300:500:-1:0 -y filter.mp4 93 | 3、ffmpeg -i r1ori.mp4 -vf scale=-2:500,pad=300:500:-1:0:black -y filter.mp4 94 | 4、ffmpeg -i r1ori.mp4 -vf "scale=-2:500,pad=300:ih:(ow-iw)/2:0:green" -y filter.mp4 95 | ``` 96 | 97 | 上面提供4中写法,我们以方法4做个简单介绍。 98 | 99 | scale=-2:500,指原视频按照等比例缩放,高度等于500,就是上面大家看到的284x500。 100 | 101 | pad=300:ih:(ow-iw)/2:0:green,300:ih即300:500就是红色框的宽高(ow-iw)/2,指的是红色框和蓝色框差值的一半,即两边各需要填充的范围;最后一个参数表示需要填充的颜色,默认是黑色 black,为了调试方便我们把颜色设为green。 102 | 103 | 现在我们保证了当前视频一定会按照300x500的比例输出且不会变形,但是请注意老板说的“所有输出视频”,也就是说输入视频的分辨率可能是200x300、544x960、500x400、200x800等等各种比例都要保证按照300x500输出,很显然,上面的写法不完全通用,怎么办? 104 | 105 | 现在我们已知原输入视频的宽高和想要的宽高,针对这种情况,我们制定一套处理规则即可解决: 106 | 107 | 1. 宽高都偏小,不拉伸,不缩放 108 | 2. 宽高都偏大,等比例缩小,以高度为准 109 | 3. 宽超出范围,等比例缩小,以宽为准 110 | 4. 高超出范围,等比例缩小,以高为准 111 | 112 | 在实际的开发过程中,我们要跟代码打交道,平时在命令行中的实现都是练习,所以基于该规则,我们有了下面一段代码 113 | 114 | ```PHP 115 | $outWidth && $inputHeight > $outHeight) 139 | || ($inputHeight > $outHeight) 140 | ) { 141 | $scale = "scale=-2:{$outHeight},pad={$outWidth}:{$outHeight}:-1:0:green"; 142 | } elseif ($inputWidth > $outWidth) { 143 | $scale = "scale={$outWidth}:-2,pad={$outWidth}:{$outHeight}:0:-1:green"; 144 | } 145 | 146 | return $scale; 147 | } 148 | } 149 | 150 | $calculatorService = new CalculatorService(); 151 | var_dump($calculatorService->getSize(200, 300, 300, 500)); 152 | var_dump($calculatorService->getSize(544, 960, 300, 500)); 153 | var_dump($calculatorService->getSize(500, 400, 300, 500)); 154 | var_dump($calculatorService->getSize(200, 600, 300, 500)); 155 | 156 | // 结果 157 | string(37) "scale=200:300,pad=300:500:-1:-1:green" 158 | string(35) "scale=-2:500,pad=300:500:-1:0:green" 159 | string(35) "scale=300:-2,pad=300:500:0:-1:green" 160 | string(35) "scale=-2:500,pad=300:500:-1:0:green" 161 | ``` 162 | 163 | 为了方便理解,大家可以参考下面的图一一对应。 164 | 165 | ![image](https://user-images.githubusercontent.com/87458342/127451486-e2de6757-b64d-4d3d-b3b5-51a1bcc6c09e.png) 166 | 167 | ## 复杂滤镜 168 | 169 | 相对于简单滤镜,复杂滤镜是可以处理任意数量输入和输出效果的滤镜图,它几乎无所不能。 170 | 171 | 复杂滤镜用命令 -filter_complex 表示,它还有一个别名 -lavfi。 172 | 173 | 上篇文章介绍到流和滤镜结合是一种最重要、最常用的方法。依然是将输入视频 r3.mp4 等比例缩放一倍,我们以手动选择流的方式为例。 174 | 175 | ```C++ 176 | ffmpeg -i r3.mp4 -filter_complex "[0]scale=272:480[out]" -map 0:a -map "[out]" -y filter.mp4 177 | ``` 178 | 179 | 简单分析如下: 180 | 181 | 1. 命令 "[0]scale=272:480[out]" 中的[0]表示第一个输入的视频,因为要对视频做处理,所以也可以用[0:v]表示,如果要对音频单独处理,就需要用 [0:a] 了; 182 | 2. [0] 结合scale滤镜,表示的就是把第一个输入的视频作为scale滤镜的参数输入; 183 | 3. [out] 中括号是必须要的,out是自定义的一个别名,结合scale滤镜,表示的是把scale滤镜输出的结果命名为[out],但并非是最终输出的结果,只能作为中间过程输出的一个结果; 184 | 4. -map "[out]" 就是直接选择[out] 流作为输出 185 | 186 | 我们说过,一个滤镜的输出作为另一个滤镜的输入,这样就极大的避免了写多条命令反复编解码操作,我们的原则只有一个,能用一条命令处理的绝不用两条命令。 187 | 188 | >有损编解码器反复编解码操作会降低原视频质量。 189 | 190 | 比如现在要把原视频 r1ori.mp4 的中间部分裁剪出来,但仍保持原视频的分辨率544x960,如何做呢? 191 | 192 | ```C++ 193 | ffmpeg -i r1ori.mp4 -filter_complex "nullsrc=s=544x960[background]; \ 194 | crop=iw:(ih/2 - 110):0:250[middle]; \ 195 | [background][middle]overlay=shortest=1:x=(main_w-overlay_w)/2:y=(main_h-overlay_h)/2[out]" \ 196 | -map "[out]" 197 | -map 0:a 198 | -movflags +faststart 199 | -y fc.mp4 200 | ``` 201 | 202 | 这个命令就显得稍微长了一些,在这条命令中使用了nullsrc、crop、overlay三种常见滤镜。 203 | 204 | nullsrc滤镜用于创建一个空的视频,简单的说就是一个空的画布或者说是绿布,因为默认创建的颜色是绿色的。s用于指定画布的大小,默认是320x240,这里表示我们创建一个544x960的画布,并命名为background; 205 | 206 | 关于nullsrc还有很多种不同的用户,比如使用nullsrc和CIQRCodeGenerator创建一个“白狼栈”首页的二维码 207 | 208 | ```C++ 209 | ffmpeg -f lavfi -i nullsrc=s=200x200,coreimage=filter=CIQRCodeGenerator@inputMessage=\ 210 | http\\\\\://manks.top/@inputCorrectionLevel=H -frames:v 1 manks.png 211 | ``` 212 | 213 | crop滤镜用于裁剪视频,也就是说视频的任意区域任意大小,我们都可以裁剪出来。crop=iw:(ih/2 - 110):0:250[middle]; 这里我们裁剪原视频的中间部分并命名为middle; 214 | 215 | overlay滤镜表示两个视频相互叠加,shortest官网是这么介绍的:“If set to 1, force the output to terminate when the shortest input terminates. Default value is 0.”,因为我们使用nullsrc创建了一个没有时间轴的画布,所以这里需要以middle的视频时间为最终时间,故设置为1。main_w和main_h表示主视频的宽高,overlay_w和overlay_h表示叠加视频的宽高。如果要把A视频叠加到B视频上,则main_w和main_h表示B视频的宽高,overlay_w和overlay_h表示A视频的宽高。合起来便是把middle叠加到background之上且置于background的中间(相当于有个叠加层的概念); 216 | 217 | 最后一个参数是-movflags,它跟mp4的元数据有关,设为faststart表示会将moov移动到mdat的前面,在线播放的时候会稍微快一些。 218 | -------------------------------------------------------------------------------- /article/028-webRTC是如何实现音视频的录制.md: -------------------------------------------------------------------------------- 1 | # webRTC是如何实现音视频的录制 2 | 3 | ## 什么是webRTC 4 | 5 | “WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和(或)音频流或者其他任意数据的传输。WebRTC包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点(Peer-to-Peer)的数据分享和电话会议成为可能。” 6 | 总结下来,其实有四点: 7 | 8 | 1. 跨平台 9 | 2. (主要)用于浏览器 10 | 3. 实时传输 11 | 4. 音视频引擎 12 | 13 | 在稍稍深入学习了webRTC之后,我发现其不仅可以用于「音视频录制、视频通话」,还可以用在「照相机、音乐播放器、共享远程桌面、即时通信工具、P2P网络加速、文件传输、实时人脸识别」等场景上 —— 当然,是结合了其他众多技术的基础上完成。 14 | 不过这些中有几项似乎让我们很“熟悉”:照相机、人脸识别、共享桌面。这是因为RTC使用的是基于音视频流的API! 15 | 16 | ## webRTC音视频数据采集 17 | 18 | 实现数据传输最重要的就是数据采集了,这里有一个非常重要的API: 19 | 20 | ```C++ 21 | let promise=navigator.mediaDevices.getUserMedia(containts); 22 | ``` 23 | 24 | 这个API会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D转换器等等),也可能是其它轨道类型。 25 | 26 | 它返回一个 Promise 对象,成功后会resolve回调一个 MediaStream 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,promise会reject回调一个 PermissionDeniedError 或者 NotFoundError 。 27 | 28 | 这里最重要的就是“轨道”了:因为它是基于“流”的。它得到的是一个MediaSource 对象(这是一个stream对象)!也就意味着:如果要使用此结果,要么用srcObject属性,要么就得用 URL.createObjectURL() 转为url! 29 | 30 | ```C++ 31 | /** 32 | * params: 33 | * object: 用于创建URL的File 对象、Blob对象或者MediaSource对象。 34 | * return : 一个DOMString包含了一个对象URL,该URL可用于指定源 object的内容。 35 | */ 36 | objectURL= URL.create0bjectURL(object) ; 37 | ``` 38 | 39 | 回到API本身,可以这么理解:通过它可以得到当前页面所有的音视频通道的集合,根据不同的接口将它们分为不同的“轨道”,我们可以对它们进行设置和输出配置项。 40 | 41 | >在文章中可以看到实现“拍照”的部分就用到了这个API 42 | 43 | 我们先在页面上写个video元素: 44 | 45 | ```HTML 46 | 47 | ``` 48 | 49 | 因为需要获取音视频流,而HTML5中相关的元素也就video和audio了,能同时获取这两个的就只有video元素了(如果你只需要音频流,你完全可以用audio元素)。 50 | 51 | 根据上面getUserMedia API 的用法,不难写出如下的语法: 52 | 53 | ```C++ 54 | navigator.mediaDevices.getUserMedia(constraints) 55 | .then(gotMediaStream) 56 | .catch(handleError); 57 | ``` 58 | 59 | ## webRTC获取约束 60 | 61 | “约束”是指:控制对象的一些配置项。约束分为两类:视频约束和音频约束。 62 | 63 | 在webRTC中,常用的视频约束有: 64 | 65 | * width 66 | * height 67 | * aspectRatio:宽高比 68 | * frameRate 69 | * facingMode:摄像头翻转(前后摄像头)(这个配置主要是对于移动端来说,因为浏览器只有前置摄像头) 70 | * resizeMode:是否进行剪裁 71 | 72 | 而常用的音频约束有: 73 | 74 | * volume:音量 75 | * sampleRate:采样率 76 | * sampleSize:采样大小 77 | * echoCancellation:回音消除设置 78 | * autoGainControl:自动增益 79 | * noiseSuppression:降噪 80 | * latency:延迟(根据不同场景,一般来说越小延迟越小体验越好) 81 | * channelCount:单/双声道 82 | … 83 | 84 | contraints是什么?官方把它称作“MediaStreamContraints”。通过代码我们可以更清晰地看到它: 85 | ```C++ 86 | dictionary MediaStreamContraints{ 87 | (boolean or MediaTrackContaints) video = false; 88 | (boolean or MediaTrackContaints) audio = false; 89 | } 90 | ``` 91 | 92 | 它是负责对音视频约束的采集 93 | 94 | * 如果video/audio是简单的设置为bool类型,则只是简单决定是否要采集 95 | * 如果video/audio是Media Track,则可进一步设置比如:视频的分辨率、帧率、音频的音量、采样率等 96 | 97 | 根据上面说明,我们可以在代码中完善一下配置项 —— 当然,通常使用这种API第一件事是要判断用户当前浏览器是否支持: 98 | 99 | ```C++ 100 | if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia){ 101 | console.log('getUserMedia is not supported!'); 102 | return; 103 | }else{ 104 | var constraints={ 105 | // 这里也可以直接:video:false/true,则知识简单的表示不采集/采集视频 106 | video:{ 107 | width:640, 108 | height:480, 109 | frameRate:60 110 | }, 111 | // 这里也可以直接:audio:false/true,则只是简单的表示不采集/采集音频 112 | audio:{ 113 | noiseSuppression:true, 114 | echoCancellation:true 115 | } 116 | } 117 | navigator.mediaDevices.getUserMedia(constraints) 118 | .then(gotMediaStream) 119 | .catch(handleError); 120 | 121 | } 122 | ``` 123 | 124 | 下面就该实现getUserMedia API 成功和失败时的回调函数了,这里我们要先搞懂“在回调时要干什么” —— 其实无非是将“获取到的流”交出去:给全局变量保存起来或者直接作为video/audio的srcObject值(成功)或者抛出错误(失败) 125 | 126 | ```C++ 127 | var videoplay=document.querySelector('#player'); 128 | function gotMediaStream(stream){ 129 | // 【1】 130 | videoplay.srcObject=stream; 131 | // 【2】 132 | // return navigator.mediaDevices.enumerateDevices(); 133 | } 134 | function handleError(err){ 135 | console.log('getUserMedia error:',err); 136 | } 137 | ``` 138 | 139 | >注释中用了一个API:mediaDevices.enumerateDevices()。MediaDevices方法列举了所有可用的媒体输入和输出设备(如麦克风、相机、耳机等)。返回的承诺通过描述设备的MediaDevice 信息阵列得到解决。
140 | >也可以把它看做上面说的“轨道集合”。比如这里如果你把注释按promise方式写出,就会发现它是一个数组,里面包含了三个“轨道”。两个audio(输入、输出)一个video:
141 | >![image](https://user-images.githubusercontent.com/87458342/127454906-58feb013-531d-456c-b902-33c99bea710c.png)
142 | >而当你输出stream参数后你会发现
143 | >![image](https://user-images.githubusercontent.com/87458342/127454948-7fbf2ef1-ad14-4d05-82e7-a15224e4dc29.png)
144 | >在一些场景下,通过这个API我们可以将一些设备配置暴露给用户 145 | 146 | 至此视频中的第一个效果——实时捕获音视频就完成了。 147 | 这也是下面的基础:不能捕获,又怎么录制呢? 148 | 149 | 先再加一些需要的节点: 150 | 151 | ```HTML 152 | 153 | 154 | 155 | 156 | ``` 157 | 158 | 前面说了,经过捕获后得到的是一个“流”对象,那么接收时也一定需要一个能拿到流并进行操作的API :MediaStream API! 159 | 160 | MDN上是这样介绍的:“MediaRecorder 是 MediaStream Recording API 提供的用来进行媒体轻松录制的接口, 他需要通过调用 MediaRecorder() 构造方法进行实例化。” 161 | 因为是 MediaStream Recording API 提供的接口,所以它有一个构造函数: 162 | 163 | * MediaRecorder.MediaRecorder():创建一个新的MediaRecorder对象,对指定的MediaStream 对象进行录制,支持的配置项包括设置容器的MIME 类型 (例如"video/webm" 或者 “video/mp4”)和音频及视频的码率或者二者同用一个码率 164 | 165 | 根据MDN上提供的方法,我们可以得到一个比较清晰的思路:先调用构造函数,将前面获取到的流传入,经过API的解析后拿到一个对象,在规定的时间切片下将他们依次传入数组中(因为至此工作基本就已经完成了,用数组接收方便以后转为Blob对象操作): 166 | 167 | ```HTML 168 | function startRecord(){ 169 | // 定义一个接收数组 170 | buffer=[]; 171 | 172 | var options={ 173 | mimeType:'video/webm;codecs=vp8' 174 | } 175 | // 返回一个Boolean值,来表示设置的MIME type 是否被当前用户的设备支持. 176 | if(!MediaRecorder.isTypeSupported(options.mimeType)){ 177 | console.error(`${options.mimeType} is not supported`); 178 | return; 179 | } 180 | 181 | try{ 182 | mediaRecorder=new MediaRecorder(window.stream,options); 183 | }catch(e){ 184 | console.error('Fail to create'); 185 | return; 186 | } 187 | mediaRecorder.ondataavailable=handleDataAvailable; 188 | // 时间片 189 | // 开始录制媒体,这个方法调用时可以通过给timeslice参数设置一个毫秒值,如果设置这个毫秒值,那么录制的媒体会按照你设置的值进行分割成一个个单独的区块 190 | mediaRecorder.start(10); 191 | } 192 | ``` 193 | 194 | 这里面有两点需要注意的地方: 195 | 196 | * 获取的stream流:因为之前捕获音视频的函数必然要封装起来,所以这里如果再要使用就一定要把这个流对象设置为全局对象 —— 笔者直接将其挂载到了window对象上,在前面代码中注释[1]的地方接收对象: window.stream=stream; 197 | * 定义了一个buffer数组,和mediaRecorder对象一样,考虑到要在ondataavailable 方法中的使用以及在完成后需要停止继续捕获录制音视频流,也放在全局下声明变量 198 | 199 | ```C++ 200 | var buffer; 201 | var mediaRecorder; 202 | ``` 203 | 204 | ondataavailable方法就是在录制数据准备完成后才触发的。在里面我们需要做的就是按照之前的思路将切片好的数据依次存入 205 | 206 | ``` 207 | function handleDataAvailable(e){ 208 | // console.log(e) 209 | if(e && e.data && e.data.size>0){ 210 | buffer.push(e.data); 211 | } 212 | } 213 | 214 | //结束录制 215 | function stopRecord(){ 216 | mediaRecorder.stop(); 217 | } 218 | ``` 219 | 220 | 然后调用: 221 | 222 | ```C++ 223 | let recvideo=document.querySelector('#recplayer'); 224 | let btnRecord=document.querySelector('#record'); 225 | let btnPlay=document.querySelector('#recplay'); 226 | let btnDownload=document.querySelector('#download'); 227 | // 开始/停止录制 228 | btnRecord.onclick=()=>{ 229 | if(btnRecord.textContent==='Start Record'){ 230 | startRecord(); 231 | btnRecord.textContent='Stop Record'; 232 | btnPlay.disabled=true; 233 | btnDownload.disabled=true; 234 | }else{ 235 | stopRecord(); 236 | btnRecord.textContent='Start Record'; 237 | btnPlay.disabled=false; 238 | btnDownload.disabled=false; 239 | } 240 | } 241 | // 播放 242 | btnPlay.onclick=()=>{ 243 | var blob=new Blob(buffer,{type: 'video/webm'}); 244 | recvideo.src=window.URL.createObjectURL(blob); 245 | recvideo.srcObject=null; 246 | recvideo.controls=true; 247 | recvideo.play(); 248 | } 249 | // 下载 250 | btnDownload.onclick=()=>{ 251 | var blob=new Blob(buffer,{type:'video/webm'}); 252 | var url=window.URL.createObjectURL(blob); 253 | var a=document.createElement('a'); 254 | a.href=url; 255 | a.style.display='none'; 256 | a.download='recording.webm'; 257 | a.click(); 258 | } 259 | ``` 260 | 261 | 文章到这里视频中的功能就基本实现了,但是还有一点问题:如果只想要录制音频,这时候在你说话的时候因为捕获流一直在工作所以其实这时候有两个声源 —— 你的声音和捕获到的你的声音。 262 | 所以我们需要将捕获时的声音关掉! 263 | 但是如果你根据前面说的在getUserMedia API中添加 volume:0 是不会有任何效果的 —— 因为至今还没有任何浏览器对这个属性支持。 264 | 265 | 但是联想到我们承载音视频的是一个video/audio容器,所以我们其实可以直接用其对应DOM节点的volume属性控制: 266 | 267 | ```C++ 268 | // 在前面代码中注释为【2】的地方添加代码 269 | videoplay.volume=0; 270 | ``` 271 | 272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 | 282 | 原文作者: 恪愚 283 | -------------------------------------------------------------------------------- /article/029-音视频同步算法.md: -------------------------------------------------------------------------------- 1 | # 音视频同步算法 2 | 3 | 本文是对音视频同步算法的总结,以阅读ffplay.c源码为基础,结合各位博主的分析, 逐渐深入理解同步算法原理, 并根据自身理解, 编写一套简易的视频播放器,用于验证音视频同步算法。 4 | 5 | ## ffplay简介 6 | 7 | ffplay是FFmpeg提供的开源播放器,基于FFmpeg和SDL进行视频播放, 是研究视频播放器,音视频同步算法的很好的示例。ffplay源码涉及到很多音视频的基本概念, 在基础理论缺乏的情况下分析起来并不容易,在分析ffplay源码之前,要对音视频的相关概念有所了解,关于音视频的基本知识,在网络上有很多,也可以参考我的其他文章,这些也是我在学习中的经验总结。 8 | 9 | 在ffmpeg4.1.3中,ffplay源码约3700行,非常的小巧,关于ffplay原理分析的可以阅读雷霄骅的文章。 10 | 11 | 比较系统的介绍了ffplay,是学习ffplay很好的资料。 12 | 13 | 这里不再详细的分析ffplay的源码, 仅按照自己的理解对音视频同步算法进行总结, 并基于ffplay,自己动手编写一个简易视频播放器, 对音视频同步算法进行验证。 14 | 15 | ## 为什么要做音视频同步 16 | 17 | 如果仅仅是视频按帧率播放,音频按采样率播放,二者没有同步机制,即使一开始音视频是同步的,随着时间的流逝,音视频会渐渐失去同步,并且不同步的现象会随着时间会越来越严重。这是因为: 18 | 19 | ### 播放时间难以精确控制 20 | 21 | ### 异常、误差会随时间累积。 22 | 23 | 所以,必须要采用一定的同步策略,不断对音视频的时间差作校正,使图像显示与声音播放总体保持一致。 24 | 25 | ## 音视频同步算法 26 | 27 | 音视频同步算法的核心在于准确计算出音频与视频播放时间的偏差, 再根据这个偏差对双方进行调整,确保双方在你追我赶的过程中保持同步。 28 | 29 | ### 音视频同步介绍 30 | 31 | 视频同步到音频:即以音频为主时间轴作为同步源 32 | 33 | 音频同步到视频:即以视频为主时间轴作为同步源 34 | 35 | 音频和视频同步到系统时钟:即以系统时钟为主时间轴作为同步源 36 | 37 | ffplay默认采用第一种同步方式,本节主要阐述视频同步到音频方式。为什么大多播放器要采用视频同步到音频呢,因为音频的采样率是固定的,若音频稍有卡顿,都会很明显的听出来,反则视频则不如此,虽然表面上说的是25P(每秒25帧),不一定每一帧的间隔就必须精确到40ms(所以每帧间隔大约40ms,事实上,也很难做到精确的40ms),即便偶尔视频间隔延时大了点或小了点,人眼也是察觉不出来的,所以视频的帧率可以是动态的,并不是严格标准的! 38 | 39 | 视频同步到音频,即以音频作为主时间轴, 尽量不去干扰音频的播放,音频采用独立的线程独自解码播放(音频播放的速度在参数设置完毕后是固定的,因此我们也很容易计算音频播放的时间),在整个过程中,根据视频与音频时间差,来决策如何改变视频的播放速度,来确保视频与音频时间差控制在一定范围内, 当偏移在-90ms(音频滞后于视频)到+20ms(音频超前视频)之间时,人感觉不到视听质量的变化,这个区域可以认为是同步区域;当偏移在-185到+90之外时,音频和视频会出现严重的不同步现象,此区域认为是不同步区域。这里我们认为偏移diff在‘±一个视频帧间隔’范围内即认为是同步的,如下图所示: 40 | 41 | ![image](https://user-images.githubusercontent.com/87458342/127456349-e7e4b034-db27-4456-8b76-a164c04cc6c6.png) 42 | 43 | ### 音视频时间偏差计算 44 | 45 | 同步系统的关键就在于计算视频与音频时间偏差diff, 在ffplay.c源码中,是通过函数compute_target_delay实现的,函数源码如下: 46 | 47 | ```C++ 48 | static double compute_target_delay(double delay, VideoState *is) 49 | { 50 | double sync_threshold, diff = 0; 51 | 52 | /* update delay to follow master synchronisation source */ 53 | if (get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER) { 54 | /* if video is slave, we try to correct big delays by 55 | duplicating or deleting a frame */ 56 | diff = get_clock(&is->vidclk) - get_master_clock(is); 57 | 58 | /* skip or repeat frame. We take into account the 59 | delay to compute the threshold. I still don't know 60 | if it is the best guess */ 61 | sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay)); 62 | if (!isnan(diff) && fabs(diff) < is->max_frame_duration) { 63 | if (diff <= -sync_threshold) 64 | delay = FFMAX(0, delay + diff); 65 | else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD) 66 | delay = delay + diff; 67 | else if (diff >= sync_threshold) 68 | delay = 2 * delay; 69 | } 70 | } 71 | 72 | av_log(NULL, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n", 73 | delay, -diff); 74 | 75 | return delay; 76 | } 77 | ``` 78 | 79 | 根据自身的理解,结合实际测试,得出diff的计算方法: 80 | 81 | ![image](https://user-images.githubusercontent.com/87458342/127456489-773cb749-b9e6-48e1-bdd0-8a98abd05482.png) 82 | 83 | 当前视频帧pts:frame->pts * av_q2d(video_st->time_base) 84 | 85 | 当前视频帧至今流逝的时间: 代表当前视频帧从开始显示到现在的时间, 在ffplay中函数get_clock(&is->vidclk)给出了具体实现,在本次实验中, 通过nowtime - last_showtime来表示流逝的时间, 由于我们是在视频显示后立即计算diff, 这个流逝的时间几乎可以忽略不计,可以使用0表示。 86 | 87 | 音频帧播放时间 = 音频长度/采样率 88 | 89 | 当前音频帧播放完毕时间= 当前音频帧的pts + 当前音频帧长度 / 采样率 90 | 91 | > = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate; 92 | 93 | (在计算音频帧长度时需要考虑采样率, 通道数, 样本格式) 94 | 95 | 音频缓冲区中未播放数据的时间: 在ffplay.c中,采用如下公式来获取: 96 | 97 | ```C++ 98 | set_clock_at(&is->audclk, is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec, is->audio_clock_serial, audio_callback_time / 1000000.0); 99 | ``` 100 | 101 | 缓冲区数据总长度=SDL的A,B缓冲区总长度 + 当前音频帧尚未拷贝到SDL缓冲区的剩余长度 aduio_write_buf_size 到这里,我们就可以计算得到音视频的播放时间偏差diff, 结合上面的偏差图,我们很容易判断出是视频落后于音频,还是音频落后于视频。 102 | 103 | ### 量化视频播放的时间延时 104 | 105 | 通过第2步我们已经计算出音视频的时间偏差, 接下来我们就要根据这个偏差来量化视频延时的时间, 来控制下一个视频帧显示的时间。 106 | 107 | 我们参考ffplay.c中的代码片段: 108 | 109 | ```C++ 110 | last_duration = vp_duration(is, lastvp, vp); 111 | delay = compute_target_delay(last_duration, is); 112 | 113 | time= av_gettime_relative()/1000000.0; 114 | if (time < is->frame_timer + delay) { 115 | *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time); 116 | goto display; 117 | } 118 | 119 | is->frame_timer += delay; 120 | if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX) 121 | is->frame_timer = time; 122 | ``` 123 | 124 | remaining_time为下一帧播放的延时时间, ffplay.c借助frame_timer += delay来记录当前视频累计播放的时间。 125 | 126 | frame_timer + delay - av_gettime_relative()/1000000.0 :代表下一视频帧需要延时的时间,这里需要减去当前时间,是为了得到定时器或delay的时间。 127 | 128 | 另外, 我们约定任意两个视频帧的间隔至少为10ms,所以才有了: 129 | ```C++ 130 | *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time); 131 | ``` 132 | 133 | ### 编写简易的视频播放器 134 | 135 | ffplay.c中的同步算法对于初学者而言理解起来还是有些难度的, 结合自身对ffplay.c源码的阅读,以及音视频同步算法的理解, 对上述同步代码进行精简, 亦能达到音视频同步的效果代码片段如下。 136 | 137 | ```C++ 138 | if( pm->av_sync_type == AV_SYNC_AUDIO_MASTER){// 139 | master_clock = get_master_clock(pm); 140 | diff = vp->pts - master_clock; 141 | printf("vps:%lf, audioclock:%lf, diff:%lf\n", vp->pts, master_clock, diff); 142 | sync_threshold = (delay > AV_SYNC_THRESHOLD)?delay:AV_SYNC_THRESHOLD; 143 | 144 | if( diff <= -sync_threshold){ 145 | delay = 0; 146 | }else if( diff >= sync_threshold){ 147 | delay *= 2; 148 | } 149 | 150 | } 151 | ``` 152 | 153 | 直接根据diff的值来决策下一帧要延时的时间。 154 | 155 | 156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 | 167 | 原文作者: 挥剑踏苍穹 168 | -------------------------------------------------------------------------------- /article/030-房间声学原理与Schroeder混响算法实现.md: -------------------------------------------------------------------------------- 1 | # 房间声学原理与Schroeder混响算法实现 2 | 3 | 原文公式较多,因此直接贴上图片 4 | 5 | ![image](https://user-images.githubusercontent.com/87458342/127457266-98448581-5060-4ac6-b431-1366613b3988.png) 6 | 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 | 原文作者: icoolmedia 19 | -------------------------------------------------------------------------------- /article/031-一个频域语音降噪算法实现及改进方法.md: -------------------------------------------------------------------------------- 1 | # 一个频域语音降噪算法实现及改进方法 2 | 3 | 因原文公式较多,因此直接贴上图片 4 | 5 | ![image](https://user-images.githubusercontent.com/87458342/127457996-56622e30-ff38-4060-8d9a-800ee2b77d47.png) 6 | 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 | 20 | 原文作者: icoolmedia 21 | -------------------------------------------------------------------------------- /article/032-HEVC官方软件HM源代码分析-编码器TAppEncoder.md: -------------------------------------------------------------------------------- 1 | # HEVC官方软件HM源代码分析-编码器TAppEncoder 2 | 3 | ## 函数调用关系图 4 | 5 | HM中的HEVC视频编码器TAppEncoder的函数调用关系图如下所示。 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127458355-23bc552f-8c13-49d7-afb1-ba9b05f133d7.png) 8 | 9 | 下面解释一下图中关键标记的含义。 10 | 11 | >函数背景色
12 | >* 函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用: 13 | >* 白色背景的函数:不加区分的普通内部函数。 14 | >* 黄色背景函数:滤波函数(Filter)。用于环路滤波,半像素插值,SSIM/PSNR的计算。 15 | >* 绿色背景的函数:CU编码函数(Encode)。通过对残差的DCT变换、量化等方式对CU进行编码。 16 | >* 紫色背景的函数:熵编码函数(Entropy Coding)。对CU编码后的数据进行CABAC熵编码。 17 | >* 浅蓝色背景函数:码率控制函数(Rate Control)。对码率进行控制的函数。 18 | 19 | >箭头线
20 | >* 箭头线标志了函数的调用关系: 21 | >* 黑色箭头线:不加区别的调用关系。 22 | >* 黄色箭头线:滤波函数(Filter)之间的调用关系。 23 | >* 绿色箭头线:CU编码函数(Encode)之间的调用关系。 24 | >* 紫色箭头线:熵编码函数(Entropy Coding)之间的调用关系。 25 | 26 | >函数所在的文件
27 | >* 每个函数标识了它所在的文件路径。 28 | 29 | 下文记录结构图中的几个关键部分。 30 | 31 | ## 普通内部函数 32 | 33 | 普通内部函数指的是TAppEncoder中还没有进行分类的函数。例如: 34 | 35 | >编码器的main()函数中调用的TAppEncTop类的配置读取函数parseCfg()、编码函数encode()等。
36 | >编码器最主要的函数:TEncTop中的encode()、TEncGOP中的compressGOP()、TEncSlice的compressSlice()等。 37 | 38 | ## CU编码函数 39 | 40 | CU编码函数通过运动估计、DCT变换、量化等步骤对图像数据进行编码。编码的工作都是在TEncCu类中的compressCtu()中完成的。compressCtu()调用了xCompressCU()完成了CU的编码工作。xCompressCU()本身是一个递归调用的函数,其中的xCheckRDCostInter()完成了分析帧间CU编码代价的工作;其中的xCheckRDCostIntra()则完成了分析帧内CU编码代价的工作。 41 | 42 | ## 熵编码函数 43 | 44 | 熵编码函数使用CABAC的方式对CU编码后的数据进行熵编码。熵编码的工作都是在TEncCu类中的encodeCtu()中完成的。 45 | 46 | ## 环路滤波函数 47 | 48 | 环路滤波函数对重建帧数据进行滤波,去除方块效应和振铃效应。TComLoopFilter类的loopFilterPic()完成了去块效应滤波器的工作; TComSampleAdaptiveOffset类的SAOProcess()完成了去除振铃效应的SAO滤波器的工作。 49 | 50 | ## 码率控制函数 51 | 52 | 码率控制模块函数分布在lencod源代码不同的地方,包括rc_init_seq()、rc_init_GOP()、rc_init_frame()、rc_handle_mb()等。 53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | 原文作者:雷霄骅 65 | -------------------------------------------------------------------------------- /article/033-HEVC官方软件HM源代码分析-解码器TAppDecoder.md: -------------------------------------------------------------------------------- 1 | # HEVC官方软件HM源代码分析-解码器TAppDecoder 2 | 3 | ## 函数调用关系图 4 | 5 | HM中的HEVC视频解码器TAppDecoder的函数调用关系图如下所示。 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127459118-59538afd-5f62-447c-9c11-0d81e2d5f094.png) 8 | 9 | 下面解释一下图中关键标记的含义。 10 | 11 | >函数背景色
12 | >* 函数在图中以方框的形式表现出来。不同的背景色标志了该函数不同的作用: 13 | >* 白色背景的函数:普通内部函数。 14 | >* 粉红色背景函数:解析函数(Parser)。这些函数用于解析SPS、PPS等信息。 15 | >* 紫色背景的函数:熵解码函数(Entropy Decoding)。这些函数读取码流数据并且进行CABAC熵解码。 16 | >* 绿色背景的函数:解码函数(Decode)。这些函数通过帧内预测、帧间预测、DCT反变换等方法解码CU压缩数据。 17 | >* 黄色背景的函数:环路滤波函数(Loop Filter)。这些函数对解码后的数据进行滤波,去除方块效应和振铃效应。 18 | 19 | >箭头线
20 | >* 箭头线标志了函数的调用关系: 21 | >* 黑色箭头线:不加区别的调用关系。 22 | >* 粉红色箭头线:解析函数(Parser)之间的调用关系。 23 | >* 紫色箭头线:熵解码函数(Entropy Decoding)之间的调用关系。 24 | >* 绿色箭头线:解码函数(Decode)之间的调用关系。 25 | >* 黄色箭头线:环路滤波函数(Loop Filter)之间的调用关系。 26 | 27 | >函数所在的文件
28 | >* 每个函数标识了它所在的文件路径。 29 | 30 | 下文记录结构图中的几个关键部分。 31 | 32 | ## 普通内部函数 33 | 普通内部函数指的是TAppDecoder中还没有进行分类的函数。例如: 34 | * 解码器的main()函数中调用的TAppDecTop类相关的create()、parseCfg()、decode()、destroy()等方法。 35 | * 解码器最主要的解码函数:TDecTop中的decode()、xDecodeSlice();TDecGop中的decompressSlice();TDecSlice中的decompressSlice()等。 36 | 37 | ## 解析函数(Parser) 38 | 39 | 解析函数(Parser)用于解析HEVC码流中的一些信息(例如SPS、PPS等)。在TDecTop的decode()中都调用这些解析函数完成了解析。下面举几个解析函数的例子。 40 | 41 | >xDecodeVPS():解析VPS;
42 | >xDecodeSPS():解析SPS;
43 | >xDecodePPS():解析PPS; 44 | 45 | ## 熵解码函数(Entropy Decoding) 46 | 47 | 熵解码函数(Entropy Decoding)读取码流数据并且进行CABAC熵解码。熵解码工作是在TDecCu的decodeCtu ()函数中完成的。其中递归调用了xDecodeCU()完成了具体的熵解码工作。 48 | 49 | ## 解码函数(Decode) 50 | 51 | 解码函数(Decode)通过帧内预测、帧间预测等方法解码CU压缩数据。解码工作是在TDecCu的decompressCtu()函数中完成的。其中递归调用了xDecompressCU()完成了具体的解码工作。 52 | 53 | xDecompressCU()调用xReconIntraQT()完成帧内预测CU的解码;xReconInter()完成帧间预测CU的解码。 54 | 55 | xReconIntraQT()调用xIntraRecQT(),而xIntraRecQT()调用xIntraRecBlk()。xIntraRecBlk()调用了TComPrediction类的predIntraAng()完成了帧内预测的工作;调用了TComTrQuant类的invTransformNxN()完成了残差数据DCT反变换的工作。 56 | 57 | xReconInter()调用TComPrediction的motionCompensation()完成了运动补偿的工作;调用xDecodeInterTexture()完成了残差数据的DCT反变换工作。motionCompensation()调用了xPredInterUni()完成了单向预测的运动补偿;而调用xPredInterBi()完成了双向预测的运动补偿。其中xPredInterUni()调用xPredInterBlk()完成一个分量块的运动补偿,而xPredInterBlk()调用了TComInterpolationFilter类的filterHor()和filterVer()完成了亚像素的插值工作。 58 | 59 | xDecodeInterTexture()调用TComTrQuant类的invRecurTransformNxN(),而invRecurTransformNxN()调用了invTransformNxN()。invTransformNxN()调用xDeQuant()完成了反量化的工作,调用了xIT()完成了DCT反变换的工作。xIT()调用了xITrMxN()完成MxN维的DCT反变换,而xITrMxN()根据DCT矩阵维度的不同,分别调用了partialButterflyInverse4()、partialButterflyInverse8()、partialButterflyInverse16()、partialButterflyInverse32()几种蝶形算法。 60 | 61 | ## 环路滤波函数(Loop Filter) 62 | 63 | 环路滤波函数(Loop Filter)对解码后的数据进行滤波,去除方块效应和振铃效应。去块效应滤波是在TDecTop 的executeLoopFilters()中完成的。executeLoopFilters()调用了TDecGop 的filterPicture()。filterPicture()调用了TComLoopFilter类的loopFilterPic()完成了去块效应滤波器的工作;调用TComSampleAdaptiveOffset类的SAOProcess()完成了去除振铃效应的SAO滤波器的工作。 64 | 65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 75 | 原文作者: 雷霄骅 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /article/035-微信小程序集成实时音视频通话功能.md: -------------------------------------------------------------------------------- 1 | # 微信小程序集成实时音视频通话功能 2 | 3 | ## 背景 4 | 5 | 在项目的开发当中,很多时候,我们会有音频、视频通话的需求,但是一般都不会自己来写,所以我们就需要借助第三方来实现。尤其是这次的项目开发当中,需要在微信小程序当中集成实时音视频通话的功能,这里使用腾讯云的实时音视频服务。 6 | 7 | ## 腾讯云——实时音视频 8 | 9 | 实时音视频官方文档:https://cloud.tencent.com/document/product/647 10 | 11 | 文档位置:文档->视频服务->实时音视频 12 | 13 | 官方文档介绍的比较,详细,一般按照步骤做,都可以完成,但可能也会遇到一定的问题。 14 | 15 | ## 使用前提 16 | 17 | 想要使用腾讯云的服务的话,需要先注册腾讯云账号。登入进腾讯云控制台,进入“云产品->视频服务->实时音视频”模块。然后按照实时音视频文档中的一分钟开通服务的步骤来做。首次开通有免费的1000分钟,可以提供开发测试,时长过了的话,需要收费。 18 | 19 | ![image](https://user-images.githubusercontent.com/87458342/127466059-cb9f3a8e-93c2-4197-ad6c-bb8c25f2a12f.png) 20 | 21 | ## 快速入门 22 | 23 | 在快速入门这里可以选择想要在什么设备实现音视频通话,这里的话选择小程序。 24 | 25 | ![image](https://user-images.githubusercontent.com/87458342/127466119-de8543ba-587a-4ed2-b5e6-6789765db0aa.png) 26 | 27 | ### 可能遇到的问题 28 | 29 | 在步骤4中可能会遇到一些问题 30 | 想要在微信小程序中集成实时音视频的功能,就必须要在微信小程序的后台开通接口。但是在开通的接口的过程当中,发现并不能开通。原因是当前的小程序的服务类目不符合开通实时音视频的条件。 31 | 32 | ![image](https://user-images.githubusercontent.com/87458342/127466177-0564bea5-b4b0-4ea1-90ef-69b5bbd2054d.png) 33 | 34 | ## 解决办法 35 | 36 | 解决办法:在微信小程序后台的“设置->基本设置”里面设置服务类目。如果是测试的话,可以选择“工具>视频客服”,选择这个的话,好处是不用上传商户的资格证,可以直接测试使用。然后再去开通接口,就可以成功了。 37 | 38 | ![image](https://user-images.githubusercontent.com/87458342/127466220-ee000018-d083-427b-8b2f-5f987ffc27ad.png) 39 | 40 | ## 具体使用 41 | 42 | 之后下载Demo,然后在制定文件中,填写SDKAPPID和SECRETKEY就可以在微信开发者工具使用了。找两个小伙伴,用微信扫描预览的二维码,就可以实现实时音视频通话了。 43 | 44 | ![image](https://user-images.githubusercontent.com/87458342/127466285-640afaaa-e52a-4eed-b6d4-e5e6cba19691.png) 45 | 46 | ![image](https://user-images.githubusercontent.com/87458342/127466320-912fc95d-e8f0-4bb5-9a84-4862bdd0cca3.png) 47 | 48 | ![image](https://user-images.githubusercontent.com/87458342/127466349-536b6032-15ef-4cf8-8e0b-39e53b901dc8.png) 49 | 50 | ## 总结 51 | 52 | 此时已经可以解决微信小程序集成实时音视频通话功能了,至于具体怎么在小程序中实现,要看具体业务了,腾讯官方也提供了很多的接口,帮助我们达到一些需求。但是缺点也很明显,就是需要收费。 待我发现有不用收费的方式实现音视频通话了,再来分享。 53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | 66 | 原文作者: 愚公搬代码 67 | -------------------------------------------------------------------------------- /article/038-HEVC码流分析.md: -------------------------------------------------------------------------------- 1 | # HEVC码流分析 2 | 3 | 初步分析了一下HEVC的码流。 4 | 5 | 使用了Elecard HEVC Analyzer打开了一个《阿凡达>720P的视频片段 6 | 7 | P帧得到如下结果: 8 | 9 | 宏块划分 10 | 11 | ![image](https://user-images.githubusercontent.com/87458342/127624409-3feab762-ee7b-4042-9e3a-8a4d317ef572.png) 12 | 13 | 运动矢量: 14 | 15 | ![image](https://user-images.githubusercontent.com/87458342/127624455-8060f77a-d377-4de6-9f4e-5a1555d337d0.png) 16 | 17 | CU的大小: 18 | 19 | ![image](https://user-images.githubusercontent.com/87458342/127624475-fd91e3f6-f9e1-4706-a2ce-8629333f51c9.png) 20 | 21 | QP: 22 | 23 | ![image](https://user-images.githubusercontent.com/87458342/127624498-8ffd3607-5a58-4a76-99a5-964066651b78.png) 24 | 25 | 注:分析过程中发现QP值是恒定的 26 | 27 | B帧得到如下结果: 28 | 29 | 宏块划分: 30 | 31 | ![image](https://user-images.githubusercontent.com/87458342/127624557-f5ecb91c-333e-49c6-94d3-d1e511fd4b79.png) 32 | 33 | 运动矢量: 34 | 35 | ![image](https://user-images.githubusercontent.com/87458342/127624531-f5d90b5d-bb1f-4e9a-b91b-615d64e2ad9b.png) 36 | 37 | 38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 | 原文作者: 雷霄骅 51 | -------------------------------------------------------------------------------- /article/039-H.264简单码流分析.md: -------------------------------------------------------------------------------- 1 | # H.264简单码流分析 2 | 3 | 使用Elecard Stream Eye分析了一个H.264码流文件。 4 | 5 | 得到的结果如下: 6 | 7 | I帧: 8 | 9 | 宏块类型(红色代表I宏块) 10 | 11 | ![image](https://user-images.githubusercontent.com/87458342/127624899-3eb750d5-168b-4c0f-8cf7-2fb4a3e08133.png) 12 | 13 | 宏块划分(4x4,16x16) 14 | 15 | ![image](https://user-images.githubusercontent.com/87458342/127624935-aee9af5b-9952-4290-bf73-c8051f19763b.png) 16 | 17 | 宏块大小(颜色越浅,越大) 18 | 19 | ![image](https://user-images.githubusercontent.com/87458342/127624968-75134ea2-f8eb-4cf2-b265-636d3fea1af9.png) 20 | 21 | P帧: 22 | 23 | 宏块划分 24 | 25 | ![image](https://user-images.githubusercontent.com/87458342/127624991-d4d28881-bb0e-4ae7-9385-49c92ab81d29.png) 26 | 27 | 宏块类型(黄色是skip宏块,蓝色是P宏块): 28 | 29 | ![image](https://user-images.githubusercontent.com/87458342/127625015-47a49096-3410-42da-af0c-4785b88e9976.png) 30 | 31 | 运动矢量 32 | 33 | ![image](https://user-images.githubusercontent.com/87458342/127625035-8e81bc1f-6751-4330-8f05-03a4d32d0166.png) 34 | 35 | 36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 47 | 原文作者: 雷霄骅 48 | -------------------------------------------------------------------------------- /article/040-MPEG2简单码流分析.md: -------------------------------------------------------------------------------- 1 | # MPEG2简单码流分析 2 | 3 | 今天使用Elecard Stream Eye打开了一个MPEG2视频编码的TS封装格式的文件。 4 | 5 | 分析的结果如图所示: 6 | 7 | P帧: 8 | 9 | 像条: 10 | 11 | ![image](https://user-images.githubusercontent.com/87458342/127625268-a8af0a28-3164-4c07-ac83-b803a983e5ef.png) 12 | 13 | 宏块划分(只有一种): 14 | 15 | ![image](https://user-images.githubusercontent.com/87458342/127625291-e808b56a-5c03-4a9a-ae31-fd2238442dda.png) 16 | 17 | 运动矢量: 18 | 19 | ![image](https://user-images.githubusercontent.com/87458342/127625318-5b12e760-e012-4c1d-95ec-cd5ae1106049.png) 20 | 21 | 宏块类型(红:I宏块;蓝:P宏块;黄:skip宏块): 22 | 23 | ![image](https://user-images.githubusercontent.com/87458342/127625347-e6651517-12e9-42c6-982f-e88f60e32f64.png) 24 | 25 | 宏块大小(颜色越浅,数据量越大): 26 | 27 | ![image](https://user-images.githubusercontent.com/87458342/127625376-5f64b66c-5f81-4912-acc4-50529a40bb83.png) 28 | 29 | I帧: 30 | 31 | 宏块类型: 32 | 33 | ![image](https://user-images.githubusercontent.com/87458342/127625407-36aa020d-8f71-4482-8ea7-9ca2f24b2da9.png) 34 | -------------------------------------------------------------------------------- /article/041-视频码流分析工具.md: -------------------------------------------------------------------------------- 1 | # 视频码流分析工具 2 | 3 | Elecard: 4 | [www.elecard.com/en/index.html](http://www.elecard.com/en/index.html) 5 | 6 | ![image](https://user-images.githubusercontent.com/87458342/127629711-785a8298-5466-4ba9-9601-76d69306334f.png) 7 | 8 | 9 | CodecVisa: 10 | [www.codecian.com](http://www.codecian.com) 11 | 12 | ![image](https://user-images.githubusercontent.com/87458342/127629900-69a2c026-a817-41ac-8696-bf9c5d1d7191.png) 13 | 14 | 15 | Intel Video Pro Analyzer 2014: 16 | [software.intel.com](https://software.intel.com/en-us/media-solutions-portal) 17 | 18 | 该工具是专门针对最新的H.265和VP9的 19 | 20 | ![image](https://user-images.githubusercontent.com/87458342/127630028-6e880475-eef9-4767-a6ac-952a65b266d0.png) 21 | 22 | 23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 原文作者: 雷霄骅 34 | -------------------------------------------------------------------------------- /article/042-H.264分析器.md: -------------------------------------------------------------------------------- 1 | # 视音频编解码学习工程:H.264分析器 2 | 3 | 雷神开源小项目:H.264码流分析器。在学习过程中,从开源社区获得了很多的资源,在此也算是对开源社区的回馈了。 4 | 5 | 我这个项目规模不大,主要可以用来学习H.264码流结构。使用VC 2010的MFC开发完成。在对H.264的NAL进行解析的过程中,用到了另外一个开源工程:h264bitstream。h264bitstream工程本身还是挺优秀的,可以比较详细的列出NAL的信息。 6 | 7 | 软件的exe以及源代码已经上传到了SourceForge上。上传前又增加了一个英文界面,紧跟国际潮流~ 8 | 9 | ## 项目主页 10 | 11 | Github:[https://github.com/leixiaohua1020/h264_analysis](https://github.com/leixiaohua1020/h264_analysis) 12 | 13 | ## 软件使用介绍 14 | 15 | 其实软件的使用还是相当简单的,一共也没有几个按钮。 16 | 17 | 软件运行后,首先打开一个码流文件(支持拖拽)。然后单击“开始”,可以解析出一系列NAL,在左侧的列表中显示出来。列表中显示出了每一个NAL的nal_reference_idc、nal_type等信息,不同种类的NAL被标记成了不同的颜色。单击任意一个NAL,可以在右边显示出其详细信息。界面如图所示: 18 | 19 | ![image](https://user-images.githubusercontent.com/87458342/127630387-3680e9df-8f32-40a8-a57a-a99b46982c51.png) 20 | 21 | ## 软件源代码简析 22 | 23 | 源代码方面和普通的MFC程序差不太多,懂得MFC的人应该很快就能看懂。大部分地方都做了注释。唯一比较特殊的地方,可能就在于对开源项目h264bitstream进行了一个简单的封装,在此就不细说了。注释方面做得比较充分。 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 | 原文作者: 雷霄骅 37 | -------------------------------------------------------------------------------- /article/043-FFmpeg架构之IO模块分析.md: -------------------------------------------------------------------------------- 1 | # FFmpeg架构之I/O模块分析 2 | 3 | ## 1. 概述 4 | 5 | ffmpeg项目的数据IO部分主要是在libavformat库中实现,某些对于内存的操作部分在libavutil库中。数据IO是基于文件格式(Format)以及文件传输协议(Protocol)的,与具体的编解码标准无关。 ffmpeg工程转码时数据IO层次关系如图所示: 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127631298-83a483eb-7c18-4ea3-92ef-098231a10e2b.png) 8 | 9 | 对于上面的数据IO流程,具体可以用下面的例子来说明,我们从一个http服务器获取音视频数据,格式是flv的,需要通过转码后变成avi格式,然后通过udp协议进行发布。 10 | 11 | 其过程就如下所示: 12 | 13 | 1. 读入http协议数据流,根据http协议获取真正的文件数据(去除无关报文信息); 14 | 2. 根据flv格式对数据进行解封装; 15 | 3. 读取帧进行转码操作; 16 | 4. 按照目标格式avi进行封装; 17 | 5. 通过udp协议发送出去。 18 | 19 | ## 2. 相关数据结构介绍 20 | 21 | 在libavformat库中与数据IO相关的数据结构主要有URLProtocol、URLContext、ByteIOContext、AVFormatContext等,各结构之间的关系如图所示。 22 | 23 | ![image](https://user-images.githubusercontent.com/87458342/127631418-0f0a58db-4d48-4516-abd3-ea9efa95b2b0.png) 24 | 25 | ### 2.1 URLProtocol结构 26 | 27 | 表示广义的输入文件,该结构体提供了很多的功能函数,每一种广义的输入文件(如:file、pipe、tcp、rtp等等)对应着一个URLProtocol结构,在av_register_all()中将该结构体初始化为一个链表,表头为avio.c里的URLProtocol *first_protocol = NULL;保存所有支持的输入文件协议,该结构体的定义如下: 28 | 29 | ```C++ 30 | typedef struct URLProtocol 31 | { 32 | const char *name; 33 | int (*url_open)(URLContext *h, const char *url, int flags); 34 | int (*url_read)(URLContext *h, unsigned char *buf, int size); 35 | int (*url_write)(URLContext *h, const unsigned char *buf, int size); 36 | int64_t (*url_seek)(URLContext *h, int64_t pos, int whence); 37 | int (*url_close)(URLContext *h); struct URLProtocol *next; 38 | int (*url_read_pause)(URLContext *h, int pause); 39 | int64_t (*url_read_seek)(URLContext *h, int stream_index, 40 | int64_t timestamp, int flags); 41 | int (*url_get_file_handle)(URLContext *h); 42 | int priv_data_size; 43 | const AVClass *priv_data_class; 44 | int flags; 45 | int (*url_check)(URLContext *h, int mask); 46 | } URLProtocol; 47 | ``` 48 | 49 | 注意到,URLProtocol是一个链表结构,这是为了协议的统一管理,ffmpeg项目中将所有的用到的协议都存放在一个全局变量first_protocol中,协议的注册是在av_register_all中完成的,新添加单个协议可以调用av_register_protocol2函数实现。而协议的注册就是将具体的协议对象添加至first_protocol链表的末尾。 URLProtocol在各个具体的文件协议中有一个具体的实例,如在file协议中定义为: 50 | 51 | ```C++ 52 | URLProtocol ff_file_protocol = { 53 | .name = "file", 54 | .url_open = file_open, 55 | .url_read = file_read, 56 | .url_write = file_write, 57 | .url_seek = file_seek, 58 | .url_close = file_close, 59 | .url_get_file_handle = file_get_handle, 60 | .url_check = file_check, 61 | }; 62 | ``` 63 | 64 | ### 2.2 URLContext结构 65 | 66 | URLContext提供了与当前打开的具体的文件协议(URL)相关数据的描述,在该结构中定义了指定当前URL(即filename项)所要用到的具体的URLProtocol,即:提供了一个在URLprotocol链表中找到具体项的依据,此外还有一些其它的标志性的信息,如flags, is_streamed等。它可以看成某一种协议的载体。其结构定义如下: 67 | 68 | ```C++ 69 | typedef struct URLContext 70 | { 71 | const AVClass *av_class; ///< information for av_log(). Set by url_open(). 72 | struct URLProtocol *prot; 73 | int flags; 74 | int is_streamed; //< true if streamed (no seek possible), default = false * int max_packet_size; 75 | void *priv_data; 76 | char *filename; //< specified URL 77 | int is_connected; 78 | } URLContext; 79 | ``` 80 | 81 | 那么ffmpeg依据什么信息初始化URLContext?然后又是如何初始化URLContext的呢?在打开一个URL时,全局函数ffurl_open会根据filename的前缀信息来确定URL所使用的具体协议,并为该协议分配好资源,再调用ffurl_connect函数打开具体协议,即调用协议的url_open,调用关系如下: 82 | 83 | ```C++ 84 | int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, AVInputFormat *fmt, int buf_size, AVFormatParameters *ap) 85 | 86 | int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options) 87 | 88 | static int init_input(AVFormatContext *s, const char *filename) 89 | 90 | int avio_open(AVIOContext **s, const char *filename, int flags) 91 | 92 | int ffurl_open(URLContext **puc, const char *filename, int flags) 93 | 94 | int ffurl_alloc(URLContext **puc, const char *filename, int flags) 95 | 96 | static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up, const char *filename, int flags) 97 | ``` 98 | 99 | 浅蓝色部分的函数完成了URLContext函数的初始化,URLContext使ffmpeg外所暴露的接口是统一的,而不是对于不同的协议用不同的函数,这也是面向对象思维的体现。在此结构中还有一个值得说的是priv_data项,这是结构的一个可扩展项,具体协议可以根据需要添加相应的结构,将指针保存在这就行。 100 | 101 | ### 2.3 AVIOContext结构 102 | 103 | AVIOContext(即:ByteIOContext)是由URLProtocol和URLContext结构扩展而来,也是ffmpeg提供给用户的接口,它将以上两种不带缓冲的读取文件抽象为带缓冲的读取和写入,为用户提供带缓冲的读取和写入操作。数据结构定义如下: 104 | 105 | ```C++ 106 | typedef struct { 107 | unsigned char *buffer; /**< Start of the buffer. */ 108 | int buffer_size; /**< Maximum buffer size */ 109 | unsigned char *buf_ptr; /**< Current position in the buffer */ 110 | unsigned char *buf_end; 111 | void *opaque; /关联URLContext 112 | int (*read_packet)(void *opaque,uint8_t *buf,int buf_size); 113 | int (*write_packet)(void *opaque,uint8_t *buf,int buf_size); 114 | int64_t (*seek)(void *opaque,int64_t offset,int whence); 115 | int64_t pos; 116 | int must_flush; 117 | int eof_reached; /**< true if eof reached */ 118 | int write_flag; /**< true if open for writing */ 119 | int max_packet_size; 120 | unsigned long checksum; 121 | unsigned char *checksum_ptr; 122 | unsigned long (*update_checksum)(unsigned long checksum,const uint8_t *buf,unsigned int size); 123 | int error; 124 | int (*read_pause)(void *opaque,int pause) 125 | int64_t (*read_seek)(void *opaque,int stream_index,int64_t timestamp,int flags); 126 | int seekable; 127 | } AVIOContext; 128 | ``` 129 | 130 | 结构简单的为用户提供读写容易实现的四个操作,read_packet write_packet read_pause read_seek,极大的方便了文件的读取,四个函数在加了缓冲机制后被中转到,URLContext指向的实际的文件协议读写函数中。 下面给出0.8版本中是如何将AVIOContext的读写操作中转到实际文件中的。 在avio_open()函数中调用了ffio_fdopen()函数完成了对AVIOContex的初始化,其调用过程如下: 131 | 132 | ```C++ 133 | int avio_open(AVIOContext **s, const char *filename, int flags) 134 | 135 | ffio_fdopen(s, h); //h是URLContext指针 ffio_init_context(*s, buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, (void*) 136 | 137 | ffurl_read,(void*)ffurl_write,(void*)ffurl_seek) 138 | ``` 139 | 140 | 蓝色部分的函数调用完成了对AVIOContext的初始化,在初始化的过程中,将AVIOContext的read_packet、write_packet、seek分别初始化为:ffurl_read ffurl_write ffurl_seek,而这三个函数又将具体的读写操作中转为:h->prot->url_read、h->prot->url_write、h->prot->url_seek,另外两个变量初始化时也被相应的中转,如下: 141 | 142 | ```C++ 143 | (*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause; 144 | 145 | (*s)->read_seek = (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek; 146 | ``` 147 | 148 | 所以,可以简要的描述为:AVIOContext的接口口是加了缓冲后的URLProtocol的函数接口。 149 | 150 | 在aviobuf.c中定义了一系列关于ByteIOContext这个结构体的函数,如下 put_xxx系列: 151 | 152 | ```C++ 153 | put_xxx系列: 154 | void put_byte(ByteIOContext *s, int b); 155 | void put_buffer(ByteIOContext *s, const unsigned char *buf, int size); 156 | void put_le64(ByteIOContext *s, uint64_t val); 157 | void put_be64(ByteIOContext *s, uint64_t val); 158 | void put_le32(ByteIOContext *s, unsigned int val); 159 | void put_be32(ByteIOContext *s, unsigned int val); 160 | void put_le24(ByteIOContext *s, unsigned int val); 161 | void put_be24(ByteIOContext *s, unsigned int val); 162 | void put_le16(ByteIOContext *s, unsigned int val); 163 | void put_be16(ByteIOContext *s, unsigned int val); 164 | void put_tag(ByteIOContext *s, const char *tag); 165 | get_xxx系列: 166 | int get_buffer(ByteIOContext *s, unsigned char *buf, int size); 167 | int get_partial_buffer(ByteIOContext *s, unsigned char *buf, int size); 168 | int get_byte(ByteIOContext *s); 169 | unsigned int get_le24(ByteIOContext *s); 170 | unsigned int get_le32(ByteIOContext *s); 171 | uint64_t get_le64(ByteIOContext *s); 172 | unsigned int get_le16(ByteIOContext *s); 173 | char *get_strz(ByteIOContext *s, char *buf, int maxlen); 174 | unsigned int get_be16(ByteIOContext *s); 175 | unsigned int get_be24(ByteIOContext *s); 176 | unsigned int get_be32(ByteIOContext *s); 177 | uint64_t get_be64(ByteIOContext *s); 178 | ``` 179 | 180 | 这些put_xxx及get_xxx函数是用于从缓冲区buffer中写入或者读取若干个字节,对于读写整型数据,分别实现了大端和小端字节序的版本。而缓冲区buffer中的数据又是从何而来呢,有一个fill_buffer的函数,在fill_buffer函数中调用了ByteIOContext结构的read_packet接口。在调用put_xxx函数时,并没有直接进行真正写入操作,而是先缓存起来,直到缓存达到最大限制或调用flush_buffer函数对缓冲区进行刷新,才使用write_packet函数进行写入操作。 181 | 182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | 193 | 194 | 转载: 雷霄骅 195 | -------------------------------------------------------------------------------- /article/045-RTSP协议实例分析.md: -------------------------------------------------------------------------------- 1 | # RTSP协议实例分析 2 | 3 | ## 1. 前言 4 | 5 | 互联网上关于RTSP的文章很多,但是大多数都是抽象的理论介绍,本文将从实际例子解说RTSP协议,不求面面俱到,但求简单易懂。RTSP(Real-Time Streaming Protocol)实时流式协议是IETF的MMUSIC工作组开发的协议,现在已成为因特网建议标准[RFC 2326]。RTSP是为了给流式过程增加更多的功能(暂停、继续、播放、快进、快退)而设计的协议。需要注意的是,RTSP本身不传输数据,音视频流数据是通过RTP传输的。 6 | 7 | ## 2. RTSP的请求方法 8 | 9 | 在开始实例分析前先介绍RTSP很重的概念,RTSP请求方法,顾名思义,就是定义一系列方法来进行客户端与服务端通信。下面枚举是有关于RTSP的请求方法集合: 10 | 11 | ```C++ 12 | typedef enum RtspReqMethod 13 | { 14 | RTSP_REQ_METHOD_SETUP = 0, 15 | RTSP_REQ_METHOD_DESCRIBE, 16 | RTSP_REQ_METHOD_REDIRECT, 17 | RTSP_REQ_METHOD_PLAY, 18 | RTSP_REQ_METHOD_PAUSE, 19 | RTSP_REQ_METHOD_SESSION, 20 | RTSP_REQ_METHOD_OPTIONS, 21 | RTSP_REQ_METHOD_RECORD, 22 | RTSP_REQ_METHOD_TEARDOWN, 23 | RTSP_REQ_METHOD_GET_PARAM, 24 | RTSP_REQ_METHOD_SET_PARAM, 25 | RTSP_REQ_METHOD_EXTENSION, 26 | RTSP_REQ_METHOD_MAX, 27 | }RtspReqMethod_e; 28 | ``` 29 | 30 | 只要了解常用几个就好,其它是为了让协议具有兼容性而拓展的,在实际应用中遇到较少 31 | 32 | >* OPTIONS 请求用于返回服务端支持的 RTSP方法列表 。也可以定时发送这个请求来保活相关的 RTSP 会话。 33 | >* DESCRIBE 命令用于请求指定的媒体流的 SDP 描述信息(详细包括音视频流的帧率、编码类型等等媒体信息) 34 | >* SETUP 命令用于配置数据交互的方法。(比如制定音视频的传输方式TCP UDP) 35 | >* PLAY 用于启动 (当暂停时重启) 交付数据给客户端. PLAY 命令的应答消息包含如下附加的头字段: 36 | >* PAUSE 请求用于临时停止服务端的数据的交互。使用 PLAY 来重新启动数据交互。 37 | >* TEARDOWN 请求用于终止来自服务端的数据的传输。 38 | 39 | ## 3. RTSP的实例抓包分析 40 | 41 | 好了,有了以上这些知识,可以直接实例分析了,本抓包数据是用wireshark抓取NVR或者IPC RTSP服务端推送过来的流数据,如果没有NVR或者IPC可以用VLC作为RTSP服务器推流进行抓包分析。我们打开wireshark并输入相应的过滤规则(ip.addr==192.168.1.1 && rtsp)开始抓包。然后在VLC输入如rtsp://admin:12345@192.168.1.1:554/10来向服务器请求流。 42 | 43 | ![image](https://user-images.githubusercontent.com/87458342/127633651-cad4cd3e-0eaf-44d5-8e7e-f1864495f9e0.png) 44 | ![image](https://user-images.githubusercontent.com/87458342/127633665-9038a5e9-57ae-4d72-bbdd-25b162ae5bd1.png) 45 | ![image](https://user-images.githubusercontent.com/87458342/127633689-04f2d1f8-39ff-41b9-a7d5-2c1e3fb8239a.png) 46 | 47 | 为了更容易理解,这里再唠叨一下,上面的会话格式遵循RTSP语法: 48 | 49 | RTSP 的语法和 HTTP 的语法基本相同, 具体如下: 50 | 51 | ```HTML 52 | COMMAND rtsp_URL RTSP/1.0 53 | Headerfield1: val1 54 | Headerfield2: val2 55 | ... 56 | 57 | [Body] 58 | ``` 59 | 60 | 客户端经过TCP三次握手后,客户端发送 OPTIONP的方法询问服务器等提供的服务,此时Cseq为2,它只是记录回话的次数序号而已,可以看到RTSP服务器支持OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN, SET_PARAMETER这几种方法。 61 | 62 | >* Cseq为3时,则是客户端用DESCRIB方法主动告诉服务器自己的信息,服务器回的是未认证Unauthorized,即未登录。 63 | >* Cseq为4时,客户端用DESCRIB方法主动发送用户名及密码给服务端,用户名为字段username,密码则由nonce和 response加密组成,服务器成功认证的话会发送服务器的媒体信息。 64 | >* Cseq为5时,客户端用SETUP方法主动向服务端请求视频流(trackID=0)。 65 | >* Cseq为6时,客户端用SETUP方法主动向服务端请求音频流(trackID=1)。 66 | >* Cseq为7时,客户端用PLAY方法主动向服务端请求播放,服务端回应200 OK等信息后,开始向客户端推送RTP流。 67 | 68 | 下面是服务端回应的状态码结构体,跟http请求返回值类型码很类似,有兴趣可以了解一下。 69 | 70 | ```HTML 71 | RtspMethod_t gRtspStatu[] = { 72 | {"Continue", 100}, 73 | {"OK", 200}, 74 | {"Created", 201}, 75 | {"Accepted", 202}, 76 | {"Non-Authoritative Information", 203}, 77 | {"No Content", 204}, 78 | {"Reset Content", 205}, 79 | {"Partial Content", 206}, 80 | {"Multiple Choices", 300}, 81 | {"Moved Permanently", 301}, 82 | {"Moved Temporarily", 302}, 83 | {"Bad Request", 400}, 84 | {"Unauthorized", 401}, 85 | {"Payment Required", 402}, 86 | {"Forbidden", 403}, 87 | {"Not Found", 404}, 88 | {"Method Not Allowed", 405}, 89 | {"Not Acceptable", 406}, 90 | {"Proxy Authentication Required", 407}, 91 | {"Request Time-out", 408}, 92 | {"Conflict", 409}, 93 | {"Gone", 410}, 94 | {"Length Required", 411}, 95 | {"Precondition Failed", 412}, 96 | {"Request Entity Too Large", 413}, 97 | {"Request-URI Too Large", 414}, 98 | {"Unsupported Media Type", 415}, 99 | {"Bad Extension", 420}, 100 | {"Invalid Parameter", 450}, 101 | {"Parameter Not Understood", 451}, 102 | {"Conference Not Found", 452}, 103 | {"Not Enough Bandwidth", 453}, 104 | {"Session Not Found", 454}, 105 | {"Method Not Valid In This State", 455}, 106 | {"Header Field Not Valid for Resource", 456}, 107 | {"Invalid Range", 457}, 108 | {"Parameter Is Read-Only", 458}, 109 | {"Internal Server Error", 500}, 110 | {"Not Implemented", 501}, 111 | {"Bad Gateway", 502}, 112 | {"Service Unavailable", 503}, 113 | {"Gateway Time-out", 504}, 114 | {"RTSP Version Not Supported", 505}, 115 | {"Extended Error:", 911}, 116 | {0, RTSP_PARSE_INVALID_OPCODE} 117 | }; 118 | ``` 119 | 120 | ![image](https://user-images.githubusercontent.com/87458342/127633907-0be292e5-ce8b-492a-b57b-c4dce20700f8.png) 121 | 122 | 可以看到抓包序列从407到419为客户端与服务端信息交互的过程,从420开始则是服务端用RTP发送过来的音视频流数据。 123 | 124 | ## 4. RTP音视频数据的载体 125 | 126 | RTP(Real-Time Transport Protocol)实时运输协议是IEFT的AVT工作组开发的协议,为实时应用提供端到端的运输服务,但不提供任何服务质量的保证,它有两种工作模式,两者的区别归纳如下: 127 | 128 | 1. 使用udp传输需要为每一个连接设定本机的rtp和rtcp对应的两个端口用于rtp和rtcp的通讯,而tcp方式不需要。 129 | 2. 在收包的过程中,TCP流式和UDP包式的不同。 130 | 131 | 讲到协议可能会有点蒙,其实RTP协议构造很简单,它就是在音视频数据的头部加上RTP的数据头来区分识别音视频流数据,以确保客户端能正确解析数据而已。RTP协议头数据犹如结构体: 132 | 133 | ```C++ 134 | typedef struct RtpHdr_s 135 | { 136 | 137 | #if (BYTE_ORDER == LITTLE_ENDIAN) 138 | /* byte 0 */ 139 | u16 cc :4; /* CSRC count */ 140 | u16 x :1; /* header extension flag */ 141 | u16 p :1; /* padding flag */ 142 | u16 version :2; /* protocol version */ 143 | /* byte 1 */ 144 | u16 pt :7; /* payload type */ 145 | u16 marker :1; /* marker bit */ 146 | #elif (BYTE_ORDER == BIG_ENDIAN) 147 | /* byte 0 */ 148 | u16 version :2; /* protocol version */ 149 | u16 p :1; /* padding flag */ 150 | u16 x :1; /* header extension flag */ 151 | u16 cc :4; /* CSRC count */ 152 | /*byte 1*/ 153 | u16 marker :1; /* marker bit */ 154 | u16 pt :7; /* payload type */ 155 | #else 156 | #error YOU MUST DEFINE BYTE_ORDER == LITTLE_ENDIAN OR BIG_ENDIAN ! 157 | #endif 158 | /* bytes 2, 3 */ 159 | u16 seqno :16; /* sequence number */ 160 | /* bytes 4-7 */ 161 | int ts; /* timestamp in ms */ 162 | /* bytes 8-11 */ 163 | int ssrc; /* synchronization source */ 164 | }RtpHdr_t; 165 | ``` 166 | 167 | 由英文注释,可以大概了解其意思,我比较关注的是payload type 和marker bit ,payload type定义了RTP帧是视频还是音频,marker bit定义了RTP帧是否结束(RTP报文段必须小于MTU,所以一般的视频都有好几个报文段组成)。 168 | 169 | ![image](https://user-images.githubusercontent.com/87458342/127634041-6b854769-0a43-4ec8-b932-cdd4b291befe.png) 170 | 171 | 上图抓取了其中一个报文段来分析RTP协议数据,可以看出这是一帧视频流,而且尚未结束还有其他报文(marker bit为false)。下面再来看一个抓包截图: 172 | 173 | ![image](https://user-images.githubusercontent.com/87458342/127634071-af3eef33-2680-4176-8ed8-5d44232b0206.png) 174 | 175 | 176 | 前面提及,服务端从420就已经用RTP推送音视频流数据,直到484才收到第一帧视频,其中相隔64个报文段,而接来的视频一般只用5到6个报文段就能传输完成。其实这里涉及一点关于视频编码相关知识,I帧、P帧、B帧等。I帧能完全还原一幅图像,P帧、B帧则是参考其他帧来完成显示,其大小比I帧小很多。这就可以解释上面为什么第一帧视频这么大,而后面几帧就很小的缘故了。视频编码的知识在后续博文中将详细解析,敬请关注我的博客更新。 177 | 178 | 179 | ### 5. 总结 180 | 181 | rtsp协议在音视频流传输上具有很高的地位,在直播平台、流媒体平台、安防监控中使用较多,学会抓包分析rtsp连接问题,能事半功倍解决问题。原创不易,请点赞,转载说明出处。 182 | 183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | 195 | 196 | 原文作者: dosthing 197 | 198 | -------------------------------------------------------------------------------- /article/046-RTSP协议之TCP或UDP问题.md: -------------------------------------------------------------------------------- 1 | # RTSP协议之TCP/UDP问题 2 | 3 | ## 1. 前言 4 | 5 | RTSP(Real-Time StreamingProtocol)实时流式协议在直播、流媒体、视频会议等平台用得很多,它是基于TCP/IP开发的上层协议,所以音视频流数据可以用TCP或者UDP来传输。这篇文章目的主要是讲述这二者的区别,如果想了解更多RTSP相关的知识,可以参阅我之前的博文《RTSP协议实例分析》。 6 | 7 | ## 2. RTSP之TCP与UDP方式区别 8 | 9 | TCP与UDP方式的区别在客户端项服务端SETUP请求中的Transport项体现。RTSP客户端会根据自己的环境发出请求,以决定使用TCP还是UDP的方式,在比较完善的RTSP服务中这两种方式都支持,然而在我遇到的产品(某品牌NVR)中只支持TCP方式,在实测过程中,VLC连接时默认使用UDP方式连接时会失败,然后VLC会自动切成TCP的连接方式,而FFPALY则不会自动切换,这里为VLC点个赞。 10 | 11 | ### 2.1 TCP请求方式 12 | 13 | TCP请求方式,此方式比较灵活,它不用另外建立音视频传输的Socket,而直接使用RTSP的Socket,这样做可以节省不少资源开支。由于采用TCP传输,数据的可靠性得到保障。在分析抓包数据可以看出客户端在SETUP请求时的数据交互中的Transport项指定了TCP传输方式RTP/AVP/TCP。(以下抓包数据基于VLC RTSP连接NVR获取的音视频流) 14 | 15 | ```HTML 16 | OPTIONS rtsp://192.168.0.49:554/11 RTSP/1.0 17 | CSeq: 2 18 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 19 | 20 | RTSP/1.0 200 OK 21 | CSeq: 2 22 | Server: Rtsp Server 23 | Public: OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN, SET_PARAMETER 24 | 25 | DESCRIBE rtsp://192.168.0.49:554/11 RTSP/1.0 26 | CSeq: 3 27 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 28 | Accept: application/sdp 29 | 30 | RTSP/1.0 401 Unauthorized 31 | Cseq: 3 32 | Server: Rtsp Server 0*0*30*4096 33 | WWW-Authenticate: Digest realm="Surveillance Server", nonce="10839044" 34 | 35 | DESCRIBE rtsp://192.168.0.49:554/11 RTSP/1.0 36 | CSeq: 4 37 | Authorization: Digest username="admin", realm="Surveillance Server", nonce="10839044", uri="rtsp://192.168.0.49:554/11", response="f1bf854a901dc8a7379ff277ce1be0e3" 38 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 39 | Accept: application/sdp 40 | 41 | RTSP/1.0 200 OK 42 | Cseq: 4 43 | Server: Rtsp Server 0*0*30*4096 44 | Content-Type: application/sdp 45 | Content-length: 379 46 | Content-Base: rtsp://192.168.0.49/1 47 | 48 | v=0 49 | o=StreamingServer 3331435948 1116907222000 IN IP4 192.168.0.49 50 | s=h264.mp4 51 | c=IN IP4 0.0.0.0 52 | t=0 0 53 | a=control:* 54 | m=video 0 RTP/AVP 96 55 | a=control:trackID=0 56 | a=rtpmap:96 H264/90000 57 | a=ptime:40 58 | a=range:npt=0-0 59 | a=fmtp:96 packetization-mode=1; sprop-parameter-sets=(null) 60 | a=videoinfo:0*0*30*4096 61 | m=audio 0 RTP/AVP 0 62 | a=control:trackID=1 63 | a=rtpmap:0 PCMU/8000 64 | a=ptime:20 65 | 66 | SETUP rtsp://192.168.0.49/1/trackID=0 RTSP/1.0 67 | CSeq: 5 68 | Authorization: Digest username="admin", realm="Surveillance Server", nonce="10839044", uri="rtsp://192.168.0.49/1", response="8e69477a4b8602b118a0850dcf3dee51" 69 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 70 | Transport: RTP/AVP/TCP;unicast;interleaved=0-1 71 | 72 | RTSP/1.0 200 OK 73 | CSeq: 5 74 | Server: Rtsp Server 75 | Session: 06720925;timeout=120 76 | Transport: RTP/AVP/TCP;unicast;interleaved=0-1 77 | 78 | SETUP rtsp://192.168.0.49/1/trackID=1 RTSP/1.0 79 | CSeq: 6 80 | Authorization: Digest username="admin", realm="Surveillance Server", nonce="10839044", uri="rtsp://192.168.0.49/1", response="8e69477a4b8602b118a0850dcf3dee51" 81 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 82 | Transport: RTP/AVP/TCP;unicast;interleaved=2-3 83 | Session: 06720925 84 | 85 | RTSP/1.0 200 OK 86 | CSeq: 6 87 | Server: Rtsp Server 88 | Session: 06720925;timeout=120 89 | Transport: RTP/AVP/TCP;unicast;interleaved=2-3 90 | 91 | PLAY rtsp://192.168.0.49/1 RTSP/1.0 92 | CSeq: 7 93 | Authorization: Digest username="admin", realm="Surveillance Server", nonce="10839044", uri="rtsp://192.168.0.49/1", response="4990f23c2ddd70c8ee8b1711f0588609" 94 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 95 | Session: 06720925 96 | Range: npt=0.000- 97 | 98 | RTSP/1.0 200 OK 99 | CSeq: 7 100 | Server: Rtsp Server 101 | Session: 06720925;timeout=120 102 | ``` 103 | 104 | ### 2.2 UDP请求方式 105 | 106 | UDP请求方式,此方式需要多建立两个Socket,用于RTCP、RTP数据传送。分析抓包数据可以看出客户端在SETUP请求时的数据交互中Transport项指定了client_port=64790-64791,它是用来通知服务端与客户端建立Socket通信的。由此可见UDP方式,音视频数据传输与控制信号传输分开,这样导致系统性能开销增大,设计复杂,在嵌入式系统中比较少用。(以下抓包数据基于VLC RTSP连接NVR获取的音视频流) 107 | 108 | ```HTML 109 | OPTIONS rtsp://192.168.0.49:554/11 RTSP/1.0 110 | CSeq: 2 111 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 112 | 113 | RTSP/1.0 200 OK 114 | CSeq: 2 115 | Server: Rtsp Server 116 | Public: OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN, SET_PARAMETER 117 | 118 | DESCRIBE rtsp://192.168.0.49:554/11 RTSP/1.0 119 | CSeq: 3 120 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 121 | Accept: application/sdp 122 | 123 | RTSP/1.0 401 Unauthorized 124 | Cseq: 3 125 | Server: Rtsp Server 0*0*30*4096 126 | WWW-Authenticate: Digest realm="Surveillance Server", nonce="07492185" 127 | 128 | DESCRIBE rtsp://192.168.0.49:554/11 RTSP/1.0 129 | CSeq: 4 130 | Authorization: Digest username="admin", realm="Surveillance Server", nonce="07492185", uri="rtsp://192.168.0.49:554/11", response="e63e8eb892f773c59edaf53e314000b6" 131 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 132 | Accept: application/sdp 133 | 134 | RTSP/1.0 200 OK 135 | Cseq: 4 136 | Server: Rtsp Server 0*0*30*4096 137 | Content-Type: application/sdp 138 | Content-length: 379 139 | Content-Base: rtsp://192.168.0.49/1 140 | 141 | v=0 142 | o=StreamingServer 3331435948 1116907222000 IN IP4 192.168.0.49 143 | s=h264.mp4 144 | c=IN IP4 0.0.0.0 145 | t=0 0 146 | a=control:* 147 | m=video 0 RTP/AVP 96 148 | a=control:trackID=0 149 | a=rtpmap:96 H264/90000 150 | a=ptime:40 151 | a=range:npt=0-0 152 | a=fmtp:96 packetization-mode=1; sprop-parameter-sets=(null) 153 | a=videoinfo:0*0*30*4096 154 | m=audio 0 RTP/AVP 0 155 | a=control:trackID=1 156 | a=rtpmap:0 PCMU/8000 157 | a=ptime:20 158 | 159 | SETUP rtsp://192.168.0.49/1/trackID=0 RTSP/1.0 160 | CSeq: 5 161 | Authorization: Digest username="admin", realm="Surveillance Server", nonce="07492185", uri="rtsp://192.168.0.49/1", response="d559a804fe440390e40fd14251265ebc" 162 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 163 | Transport: RTP/AVP;unicast;client_port=64790-64791 164 | 165 | RTSP/1.0 200 OK 166 | CSeq: 5 167 | Server: Rtsp Server 168 | Session: 34202454 169 | Transport: RTP/AVP;unicast;client_port=64790-64791;source=192.168.0.49;server_port=32773-0;ssrc=00004E87 170 | 171 | SETUP rtsp://192.168.0.49/1/trackID=1 RTSP/1.0 172 | CSeq: 6 173 | Authorization: Digest username="admin", realm="Surveillance Server", nonce="07492185", uri="rtsp://192.168.0.49/1", response="d559a804fe440390e40fd14251265ebc" 174 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 175 | Transport: RTP/AVP;unicast;client_port=64792-64793 176 | Session: 34202454 177 | 178 | RTSP/1.0 200 OK 179 | CSeq: 6 180 | Server: Rtsp Server 181 | Session: 34202454 182 | Transport: RTP/AVP;unicast;client_port=64792-64793;source=192.168.0.49;server_port=32773-0;ssrc=00004E87 183 | 184 | PLAY rtsp://192.168.0.49/1 RTSP/1.0 185 | CSeq: 7 186 | Authorization: Digest username="admin", realm="Surveillance Server", nonce="07492185", uri="rtsp://192.168.0.49/1", response="e96c9c574774a24a5c372037ffaaad4e" 187 | User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22) 188 | Session: 34202454 189 | Range: npt=0.000- 190 | 191 | RTSP/1.0 200 OK 192 | CSeq: 7 193 | Server: Rtsp Server 194 | Session: 34202454;timeout=120 195 | ``` 196 | ## 3. 总结 197 | 198 | TCP传输方式使用的是原有的套接字,不用另开套接字,起到节省资源的作用,还能利用TCP的可靠性。另一方面它更具有穿墙的特性,在很多网络的路由中有设置不给于外网访问内网,此时用UDP方式,需要服务端主动连接客户端提供的UDP接口,这请求有可能被防火墙拦截,在抓包分析中表现出来是port unreachable的错误提示,可见这种方式是比较受限制的。综上,在可靠连接和资源方面考虑,在嵌入式安防产品中采用TCP方式较多。 199 | 200 | 201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | 214 | 215 | 216 | 原文作者: dosthing 217 | -------------------------------------------------------------------------------- /article/047-ffplay工具命令使用技巧.md: -------------------------------------------------------------------------------- 1 | # ffplay工具命令使用技巧 2 | 3 | ## 1. 前言 4 | 5 | ffplay是ffmpeg的一个子工具,它具有强大的音视频解码播放能力,目前它广泛被各种流行播放器(QQ影音、暴风影音……)集成应用。作为一款开源软件,ffplay囊括Linux、Windows、Ios、Android等众多主流系统平台,十分适合进行二次开发。这里有必要介绍一下它常用的技巧。首先下载ffmpeg代码包,里面有免编译版、源代码百、静态库版、动态库版,具体怎么下载安装请参考我的博文《FFmpeg简介、功能入门、源码下载安装、常规应用》。接下来以Windows平台为例子讲述一下具体用法。 6 | 7 | ## 2. 使用技巧 8 | 9 | Win+r组合键运行cmd进入Windows命令行控制界面,使用cd命令进入ffplay.exe的可执行目录(当然也可以使用环境变量等手段使ffplay.exe命令全局可用),其他平台如linux的操作也类似。ffplay的基本用法很简单,其一般形式如下: 10 | 11 | ```C++ 12 | ffplay [option] file 13 | ffplay [option] URL 14 | ``` 15 | 16 | 总结起来ffplay的用法就是option项加上资源路径,option项是用来指定播放时的一些参数的,如指定连接的协议、视频画面的大小,音视频解码器选用、传输码率设定等,一般这些参数我们很少会设置,使用默认就OK,此时option项可以直接忽略,ffplay会帮我们选择,这也是它功能强大的体现,option的更多具体选项可以参考其官方文档;资源路径则包括文件资源路径和网络资源路径,文件资源路径是指定需要播放的音视频文件,如*.mp3、*.mp4、*,avi、*.mkv、*.rmvb等等类型的文件,网络资源路径根据协议可以分为RTSP、RTMP、HTTP流资源,心情好,来个直播,如: 17 | 18 | ```HTML 19 | ffplay rtmp://live.hkstv.hk.lxdns.com/live/hks 20 | ``` 21 | 22 | 再或者,用http浏览一下视频,如: 23 | 24 | ```HTML 25 | ffplay http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8 26 | ``` 27 | 28 | RTSP播放也了解一下(对于RTSP播放有个坑,请参考《ffplay播放rtsp网络串流失败问题》),如: 29 | 30 | ```HTML 31 | rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov 32 | ``` 33 | 34 | 音视频文件指定分辨率播放 35 | 36 | ```HTML 37 | ffplay -vfscale=1920:1080 xxxx.avi 38 | ``` 39 | 40 | ![image](https://user-images.githubusercontent.com/87458342/127635425-764234a0-67be-42d6-abf6-8ac8f170a205.png) 41 | 42 | 下面是提供的测试连接 43 | 44 | ```HTML 45 | RTMP协议直播源 46 | 大熊兔(点播):rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov 47 | 香港卫视:rtmp://live.hkstv.hk.lxdns.com/live/hks 48 | 49 | RTSP协议直播源 50 | 珠海过澳门大厅摄像头监控:rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp 51 | 52 | HTTP协议直播源 53 | 香港卫视:http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8 54 | CCTV1高清:http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8 55 | CCTV3高清:http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8 56 | CCTV5高清:http://ivi.bupt.edu.cn/hls/cctv5hd.m3u8 57 | CCTV5+高清:http://ivi.bupt.edu.cn/hls/cctv5phd.m3u8 58 | CCTV6高清:http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8 59 | ``` 60 | 61 | 62 | ## 3. 番外篇 63 | 64 | 在安防等视频流媒体数据处理领域,我们可能更关注的是用ffplay播放RTSP音视频流,其实国内各大厂商的VMS(video management system)平台也是基于此设计的。每每使用它们的IPC、NVR时都需要下载它们,但是有了ffplay神器,一个就够了,它可以播放诸如海康、大华、长视等厂商IPC、NVR的RTSP流,视频监控就变得如此简单。这里很有必要介绍一下RTSP链接的格式。 65 | 66 | RTSP链接格式与HTTP链接格式类似,也是由URL(Uniform Resource Locator)发展继承而来。URL由三部分组成:资源类型、存放资源的主机域名、资源文件名,一般语法格式为(带方括号[]的为可选项): 67 | 68 | ```C++ 69 | protocol :// hostname[:port] / path / [;parameters][?query]#fragment 70 | ``` 71 | 72 | 这里就不一一解释其各项的含义了,我们重点关注RTSP链接的格式,相比于URL,RTSP由于参数表列是嵌入RTSP报文中的,格式上会少了parameters等参数选项,其一般格式如下: 73 | 74 | ```C++ 75 | rtsp://[username]:[password]@[ip]:[port]/path 76 | ``` 77 | 78 | ### 海康平台: 79 | 80 | ```HTML 81 | rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream 82 | 说明: 83 | username: 用户名。例如admin。 84 | password: 密码。例如12345。 85 | ip: 为设备IP。例如 192.168.1.1。 86 | port: 端口号默认为554,若为默认可不填写。 87 | codec:有h264、MPEG-4、mpeg4这几种。 88 | channel: 通道号,起始为1。例如通道1,则为ch1。 89 | subtype: 码流类型,主码流为main,辅码流为sub。 90 | 例如,请求海康摄像机通道1的主码流,Url如下 91 | 主码流: 92 | rtsp://admin:12345@192.168.1.1:554/h264/ch1/main/av_stream 93 | rtsp://admin:12345@192.168.1.1:554/MPEG-4/ch1/main/av_stream 94 | 子码流: 95 | rtsp://admin:12345@1192.168.1.1/mpeg4/ch1/sub/av_stream 96 | rtsp://admin:12345@192.168.1.1/h264/ch1/sub/av_stream 97 | ``` 98 | 99 | ### 大华平台: 100 | 101 | ```HTML 102 | rtsp://username:password@ip:port/cam/realmonitor?channel=1&subtype=0 103 | 说明: 104 | username: 用户名。例如admin。 105 | password: 密码。例如admin。 106 | ip: 为设备IP。例如 192.168.1.1。 107 | port: 端口号默认为554,若为默认可不填写。 108 | channel: 通道号,起始为1。例如通道2,则为channel=2。 109 | subtype: 码流类型,主码流为0(即subtype=0),辅码流为1(即subtype=1)。 110 | 例如,请求某设备的通道2的辅码流,Url如下 111 | 112 | rtsp://admin:admin@192.168.1.1:554/cam/realmonitor?channel=2&subtype=1 113 | ``` 114 | 115 | #### 长视平台: 116 | 117 | ```HTML 118 | rtsp://[username]:[password]@[ip]:[port]/[channel]/[subtype] 119 | 120 | username: 用户名。例如admin。 121 | 122 | password: 密码。例如12345。 123 | 124 | ip: 为设备IP。例如 192.168.1.1。 125 | port: 端口号默认为554,若为默认可不填写。 126 | channel: 通道号,起始为0。例如通道1,则为channel项为0。 127 | subtype: 码流类型,主码流为0(即subtype为0),子码流为1(即subtype为1)。 128 | 129 | rtsp://admin:admin@192.168.1.1:554/00 130 | ``` 131 | 132 | ## 4. 总结 133 | 134 | 运用ffplay播放小技巧可以轻松应对各种文件资源和网络资源的播放,特别是在安防监控领域,使用它播放个监控资源那简直太方便了,而且还可以用它来检查验证音视频格式封包是否异常,在调试优化过程ffplay总能带给你惊喜。 135 | 136 | 137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | 150 | 原文作者: dosthing 151 | -------------------------------------------------------------------------------- /article/048-VLC RTSP网络串流播放失败.md: -------------------------------------------------------------------------------- 1 | # VLC RTSP网络串流播放失败 2 | 3 | ## 问题描述: 4 | 5 | VLC播放RTSP网络串流失败,没有音视及图像。用wireshark网络抓包分析,发现网络Socket异常中断,初步分析是RTSP协议TCP/UDP问题。 6 | 7 | ![image](https://user-images.githubusercontent.com/87458342/127636119-5c5e5799-a696-4df6-93e1-f2356f78073f.png) 8 | 9 | ## 解决方法: 10 | 11 | * 1. 打开VLC工具->偏好设置 12 | * 2. 输入/编解码器->RTP over RTSP(TCP)->保存退出 13 | 14 | ![image](https://user-images.githubusercontent.com/87458342/127636154-50db5cd7-89d8-4464-9853-c5f6b041778c.png) 15 | 16 | ![image](https://user-images.githubusercontent.com/87458342/127636176-3b56d314-89e3-4607-8c7a-d9888dc1dc10.png) 17 | 18 | ## 解决效果: 19 | 20 | VLC能正常播放RTSP网络串流,再次wireshark网络抓包效果如图: 21 | 22 | ![image](https://user-images.githubusercontent.com/87458342/127636218-9a7d4fe1-475e-48a2-b86c-4aece21c4a55.png) 23 | -------------------------------------------------------------------------------- /article/049-RTMP协议详解.md: -------------------------------------------------------------------------------- 1 | # RTMP协议详解 2 | 3 | ## 1. RTMP协议介绍 4 | 5 | Real Time Messaging Protocol(实时消息传送协议协议)是Adobe Systems公司为Flash Player和服务器之间音频,视频和数据传输开发的私有协议,adobe目前提供了一个并不完整的rtmp specification给大众使用,所以在使用rtmp协议时需要按flash player返回的包进行解析. 6 | 目前rtmp有以下几个变种: 7 | 8 | * rtmp是工作在TCP之上的明文协议,默认使用1935端口 9 | * rtmps是rtmp使用TLS/SSL连接 10 | * rtmpe是adobe使用自己的加密机制对rtmp进行加密的,虽然加密机制是使用了行业标准,并且内部实现也是专有的,但rtmpe设计基本上错误的,它本身也不提供任何的安全性. 11 | * rtmpt是对rtmp协议提供了一个http的封装,主要是为了防止防火墙对其进行拦截. 12 | 13 | ## 2. 包结构 14 | 15 | * rtmp消息包使用的是二进制数据流,它们使用AMF0/AMF3进行编码.与其它协议一样,rtmp消息也是也包括消息头与消息体,而消息头又可以分为basic header,chunk header,timestamp. 16 | * basic header是此包的唯一不变的部分,并且由一个独立的byte构成,这其中包括了2个作重要的标志位,chunk type以及stream id.chunk type决定了消息头的编码格式,该字段的长度完全依赖于stream id,stream id是一个可变长的字段. 17 | * message header该字段包含了将要发送的消息的信息(或者是一部分,一个消息拆成多个chunk的情况下是一部分)该字段的长度由chunk basic header中的trunk type决定. 18 | timestamp扩展时间戳就比较好理解的,就是当chunk message header的时间戳大于等于0xffffff的时候chunk message header后面的四个字节就代表扩展时间. 19 | 20 | ## 3. 握手 21 | 22 | 在rtmp连接建立后,服务端与客户端需要通过3次交换报文完成握手. 23 | 握手其他的协议不同,是由三个静态大小的块,而不是可变大小的块组成的,客户端与服务器发送相同的三个chunk,客户端发送c0,c1,c2 chunk,服务端发送s0,s1,s2 chunk. 24 | 25 | ## 4. 发送顺序 26 | 27 | 1. 握手开始时,客户端将发送c0,c1 chunk,此时客户端必须等待,直到收到s1 chunk,才能发送c2 chunk. 28 | 2. 此时服务端必须等待,直到已收到c0后才能发送s0和s1,当然也可能会等到接收c1后才发送. 29 | 3. 当服务器收到c2后才能再发送的其他数据,同理,当客户端收到s2后才能发送其它数据. 30 | 31 | ## 5. 握手包格式 32 | 33 | * c0与s0格式 34 | c0和s0包是一个1字节,可以看作是一个byte 35 | 目前rtmp版本定义为3,0-2是早期的专利产品所使用的值,现已经废弃,4-31是预留值,32-255是禁用值(这样做是为了区分基于文本的协议,因为这些协议通常都是以一个可打印的字符开始),如果服务端不能识别客户请求的版本,那么它应该发送3的响应,客户端这时可以选择下降到版本3,也可以放弃这次握手. 36 | 37 | * c1与s1格式 38 | c1与s1长度为1536个字节,它们由以下字段组成 39 | 时间戳:该字段占4字节,包含了一个时间戳,它是所有从这个端点发送出去的将来数据块的起始点,它可以是零,或是任意值,为了同步多个数据块流,端点可能会将这个字段设成其它数据块流时间戳的当前值. 40 | 0:此标记位占4字节,并且必须是0 41 | 随机数:该字段占1528字节,可以是任意值,因为每个端点必须区分已经初始化的握手和对等端点初始化的握手的响应,所以这个数据要足够的随机,当然这个也不需要密码级的随机或是动态值. 42 | 43 | * c2与s2格式 44 | c2和s2包长都是1536字节,几乎是s1和c1的回显. 45 | 46 | * time1,time2,随机数回显 47 | time1 48 | 该字段占4字节,包含有对方发送过来s1或c1的时间戳 49 | time2 50 | 该字段占4字节,包含有对方发送过来的前一个包(s1或者c1)的时间戳 51 | 随机数回显 52 | 该字段占1528字节,包含有对方发送过来的随机数据字段,每个通信端点可以使用time和time2字段,以及当前的时间戳,来快速估计带宽和/或连接延时,但这个数值基本上没法用. 53 | 54 | ## 6. 握手状态 55 | 56 | * 未初始化:在这个阶段,协议版本被发送,客户和服务端都是未初始化的,客户端在包c1中发送协议版本,如果服务端支持这个版本,它将会发送s0和s1作为响应,如果不支持,则服务端会用相应的动作来响应,在RTMP中这个动作是结束这个连接. 57 | * 版本发送完成:客户端和服务端在未初始化状态之后都进入到版本发送完成状态,客户端等待包s1,而服务端等待包c1,在收到相应的包后,客户端发送包c2,而服务端发磅包s2,状态变成询问发送完成. 58 | * 询问发送完成:客户端和服务端等待s2和c2. 59 | * 握手完成:客户端和服务端开始交换消息. 60 | -------------------------------------------------------------------------------- /article/050-STUN 原理理解.md: -------------------------------------------------------------------------------- 1 | # STUN 原理理解 2 | 3 | ## STUN简介 4 | 5 | Simple Traversal of UDP over NATs, NAT的UDP的简单穿越,是一种网络协议。是客户机-服务器的一种协议,由RFC 3489 定义。该协议定义了一些消息格式,大体上分为Request/Response。这个协议主要作用就是可以用来在两个处于NAT路由器之后的主机之间建立UDP通信。它允许位于NAT后的客户端找出自己的公网地址,确定自己位于的NAT是哪种类型,以及NAT为这个客户端的本地端口所绑定的对外端口。 6 | 7 | 譬如,一个软件包可能会包括一个STUN客户端A,这个客户端A会向STUN服务器发送请求,之后,服务器就会向STUN客户端A发送NAT路由器的公网IP地址以及NAT为这个客户端A开通的端口号,这个端口号是允许从别的客户端B传入流量传回到这个客户端A的。 8 | 9 | ## 为什么需要STUN? 10 | 11 | 一般情况下,通信的两个客户端主机往往是位于NAT之后的,用传统的方法时无法建立连接的。 12 | 13 | ## STUN主要功能 14 | 15 | STUN主要有3个功能,分别是检测是否位于NAT后面,检测NAT的类型,获取经过NAT转换后的地址和端口。 16 | 在内网安装一个STUN Client,在公网上安装一个STUN Server。client 向server 发送request,server 发送response给client。 17 | 18 | 1. 检测是否位于NAT后面 19 | Server 在收到client 发送的UDP包以后,Server 将接收该包的地址和端口利用UDP再传回给client,client把Server发送过来的地址和端口信息与本机的ip地址和端口进行比较,如果不同,说明在NAT后面;如果相同就说明client位于NAT前面,client也是公网。 20 | 21 | 2. 检测NAT的类型 22 | 这个主要发送响应的时候使用不同IP地址和端口或者改变端口等等。这个检测是对NAT一般情况下有效,但是对防火墙就无能为力了,因为防火墙可能不会打开UDP端口。 23 | 24 | NAT 主要分为4种类型,即Full Cone、Restricted Cone、Port Restricted Cone和Symmetric。其中Full Cone、Restricted Cone、Port Restricted Cone可以统称为Cone NAT,它们有一个共同点就是,只要是从同一个内网的地址和端口出来的包,NAT转换后的公网地址和端口一定是相同的。Symmetric 是如果是同一个内网的地址和端口出来的包,到同一个外部目标地址和端口,那么NAT转换后的公网地址和端口号也是相同的,但是如果如果到不同的外部目标地址和端口,NAT会转换成不同的端口号(公网地址是不变的,因为只有一个) 25 | 26 | 1. Full Cone NAT 27 | 从内网主机 in ipx和端口in portx发送的数据会映射为相同的公网ip x 和端口 port x。从其他机器上如果通过UDP发送数据到公网ip x和端口 port x上,最终数据都会被转发到内网主机上(in ipx :in portx)。 发送给内网的ip 和 port 都不受限。 28 | 29 | 2. Restricted Cone 30 | 从内网主机 in ipx和端口in portx发送的数据会映射为相同的公网ip x 和端口 port x。外部机器主动请求通信的源IP地址必须和内部主机主动向这个外部机器发送请求时的外部机器接收ip地址一致。即ip地址受限,端口不限。内网能接收信息的外部机器必须是内网主动发送请求过的外部机器。 31 | 32 | 3. Port Restricted Cone 33 | 从内网主机 in ipx和端口in portx发送的数据会映射为相同的公网ip x 和端口 port x。外部机器主动请求通信的源IP地址、端口必须和内部主机主动向这个外部机器发送请求时外部机器接收的ip地址、端口一致。即ip地址受限,端口都受限。内网能接收信息的外部机器必须是内网主动发送请求过的外部机器(ip相同),同时外网给内网发送数据包的端口还必须是接收内网数据包时所采用的端口号(端口相同)。 34 | 35 | 4. 对称NAT 36 | 发送包的目的ip和port 相同,那么NAT 映射的ip和port会相同。如果目的地址不同,即使同一台内网机器、同一个端口,mapping的端口也不同,但是ip还是相同(因为同一个公网ip)。所以只有它主动连的服务器才会知道它的端口。 37 | 38 | 39 | 例子: 40 | 41 | A机器在内网(192.168.0.4) 42 | NAT服务器(210.21.12.140) 43 | B机器在公网(210.15.27.166) 44 | C机器在公网(210.15.27.140) 45 | 46 | 如果 A 连接过机器B,假设是如下:A(192.168.0.4:5000)-> NAT(转换后210.21.12.140:8000)-> B(210.15.27.166:2000)。A和C从来没有通讯过。 47 | 48 | 不同类型的NAT,分析: 49 | 50 | 1. Full Cone NAT 51 | C 将数据发送到 210.21.12.140:8000,NAT 会将数据包送到A(192.168.0.4:5000)。因为NAT上已经有192.168.0.4:5000到210.21.12.140:8000的映射。也就是说只要机器A与任何公网上机器通讯过,其它任何公网上机器都能发送数据给A,通过发送数据包给NAT上转换后的ip和端口之后,NAT会自动将数据包送到机器A。NAT 对发送给A的数据包来者不拒,不过滤。 52 | 53 | 2. Restricted Cone 54 | C无法和A进行通信,因为A从来没有和C通信过,NAT会拒绝C试图与A连接的动作,但是B可以通过发送数据到210.21.12.140:8000和A的192.168.0.4:5000通信,这里机器B自己可以使用任何端口和A通信,譬如210.15.27.166:2001 -> 210.21.12.140:8000,NAT会将数据送到A的5000端口上。注意这里B使用的端口是2001 不是初始A连接B时,B接收A数据使用的端口2000。即只能是通信过的机器才能够进行通信,但是再通信时端口不需要固定。 55 | 56 | 3. Port Restricted Cone 57 | C无法和A进行通信,因为A从来没有和C通信过,NAT会拒绝C试图与A连接的动作,而且机器B只能用它的210.15.27.166:2000与A的192.168.0.4:5000通信。即只有曾经收到内网地址A发送过数据包的公网机器,才能通过NAT映射后的地址向内网机制发送UDP包。 58 | 59 | 4. Symmetric NAT 60 | 如果A机器还想连接C机器,则NAT上产生一个新的映射,对应的转换可能为A(192.168.0.4:5000)-> NAT(转换后210.21.12.140:8001)-> C(210.15.27.140:2000)。 61 | 62 | B与A通信:B(210.15.27.166:2000)-> NAT(转换后210.21.12.140:8000)-> C(192.168.0.4:5000)。 63 | C与A通信:B(210.15.27.140:2000)-> NAT(转换后210.21.12.140:8001)-> C(192.168.0.4:5000)。 64 | 65 | ## stun 的NAT 类型探测流程 66 | 67 | ![image](https://user-images.githubusercontent.com/87458342/127641751-d6ef84e2-d0f2-480d-ba28-d2f963570c39.png) 68 | 69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | 81 | 原文作者: 玉兔金兔 82 | -------------------------------------------------------------------------------- /books/002-视频图像处理与性能优化.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/books/002-视频图像处理与性能优化.pdf -------------------------------------------------------------------------------- /books/003-数字图像与视频处理.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/books/003-数字图像与视频处理.pdf -------------------------------------------------------------------------------- /books/005-音视频开发进阶指南:基于Android与iOS平台的实践.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/books/005-音视频开发进阶指南:基于Android与iOS平台的实践.pdf -------------------------------------------------------------------------------- /books/006-Real-Time Communication with WebRTC.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/books/006-Real-Time Communication with WebRTC.pdf -------------------------------------------------------------------------------- /books/007-FFMPEG - From Zero to Hero.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/books/007-FFMPEG - From Zero to Hero.pdf -------------------------------------------------------------------------------- /books/008-Learning WebRTC.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/books/008-Learning WebRTC.pdf -------------------------------------------------------------------------------- /books/010-FFmpeg Basics 2012.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/books/010-FFmpeg Basics 2012.pdf -------------------------------------------------------------------------------- /books/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /case_interview/002-README.md: -------------------------------------------------------------------------------- 1 | #

面试题51

2 | 3 | 什么是GOP? 4 | 5 | ##### 参考答案 6 | 7 | GOP ( Group of Pictures ) 是一组连续的画面,由一张 I 帧和数张 B / P 帧组成,是视频图像编码器和解码器存取的基本单位。 8 | 也就是说GOP组是指一个关键帧I帧所在的组的长度,每个 GOP 组只有 1 个 I 帧。 9 | GOP 组的长度格式也决定了码流的大小。 10 | GOP越大,中间的P帧和B帧的数量就越多,所以解码出来的视频质量就越高,但是会影响编码效率。 11 | 12 | #

面试题52

13 | 14 | 音频测试的测试点,音频时延如何测试? 15 | 16 | ##### 参考答案 17 | 18 | 测试点:功能,性能,兼容性,耗电量,安全性,压力测试,客观音质POLQA分,音质主观体验,主播到观众时延,观众到观众时延,3A效果 19 | 音频时延:通过音频线将2个被测对象连接在电脑,用PESQ脚本算出音频时延 20 | 21 | #

面试题53

22 | 23 | 美颜的实现原理,具体实现步骤? 24 | 25 | #

面试题54

26 | 27 | 如何直播APP抓包过来的文件,如何过滤上行,下行,总码率?
28 | 上行:ip.src192.168.x.x
29 | 下行:ip.dst192.168.x.x
30 | 总码率:ip.src192.168.x.x and ip.dst192.168.x.x 31 | 32 | #

面试题55

33 | 34 | 如何测试一个美颜挂件? 35 | 36 | #

面试题56

37 | 38 | 为什么要用FLV? 39 | 40 | ##### 参考答案 41 | 42 | 是因为传输的协议要求,RTMP协议只支持FLV格式流 43 | 44 | #

面试题57

45 | 46 | 如何测试一个美颜挂件? 47 | 48 | #

面试题58

49 | 50 | 平常的视频格式? 51 | 52 | ##### 参考答案 53 | 54 | MP4/RMVB/FLY/AVI/MOV/MKV等 55 | 56 | #

面试题59

57 | 58 | 何为homebrew?你用它安装过什么?常用命令有哪些? 59 | 60 | ##### 参考答案 61 | 62 | homebrew是一个 Mac系统下所独有的套件管理器,我要做直播,需要 rtmp 和 nginx ,单独安装很复杂,只要在终端里输入简单的安装相应的套件命令即可完成安装,复杂的过程都靠 homebrew 规避掉了!我用它安装过很多东西,比如nginx 搭建流媒体服务器等。常用命令:brew install 、brew uninstall、brew search、brew list、brew update、brew help 等~ 63 | 64 | #

面试题60

65 | 66 | RTMP、HLS协议各自的默认端口号是? 67 | 68 | ##### 参考答案 69 | 70 | RTMP端口号:1935 71 | HLS端口号 :8080 72 | -------------------------------------------------------------------------------- /paper/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/ffmpeg/H264/01594776.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/01594776.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/10.1.1.669.484.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/10.1.1.669.484.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/133112608024.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/133112608024.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/2013-ole-h264.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/2013-ole-h264.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/8116ijma03.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/8116ijma03.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/A2126065115.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/A2126065115.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/AVC_overview_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/AVC_overview_1.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/H.264-vs-H.265.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/H.264-vs-H.265.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/H_264_Video_Frame_Size_prediction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/H_264_Video_Frame_Size_prediction.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/JRTIP-2014-Real-time H264-AVCEncoderBasedOnEnhancedFrameLevelParallelismForSmartMulticoreDSPCamera.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/JRTIP-2014-Real-time H264-AVCEncoderBasedOnEnhancedFrameLevelParallelismForSmartMulticoreDSPCamera.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/MMCN09-QoE.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/MMCN09-QoE.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/ffmpeg/H264/Tutorial_H264_MPEG4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/Tutorial_H264_MPEG4.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/cr1077.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/cr1077.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/h264-AVC-Standard.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/h264-AVC-Standard.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/nenciIROS14.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/nenciIROS14.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/parallel_scalability_of_h264.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/parallel_scalability_of_h264.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/spie04-h264OverviewPaper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/spie04-h264OverviewPaper.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/H264/swseo-samos09.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/H264/swseo-samos09.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/ffmpeg/Research on Audio-Video Codec Based on Android.PDF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/Research on Audio-Video Codec Based on Android.PDF -------------------------------------------------------------------------------- /paper/ffmpeg/aac/RANDOL AAC Pumpcell Paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/aac/RANDOL AAC Pumpcell Paper.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/aac/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/ffmpeg/aac/chi2020_talkingboogie_paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/aac/chi2020_talkingboogie_paper.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/cloud-computing-quicksync-video-ffmpeg-white-paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/cloud-computing-quicksync-video-ffmpeg-white-paper.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/ffplay/1709.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/ffplay/1709.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/ffplay/97_Andrew_Weaver_SP.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/ffplay/97_Andrew_Weaver_SP.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/ffplay/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/ffmpeg/ffplay/iPres2019_paper_97.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/ffplay/iPres2019_paper_97.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/ffplay/j-kaneda18mastersthesis-FunctionReallocationMec.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/ffplay/j-kaneda18mastersthesis-FunctionReallocationMec.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/ffplay/nsdi18-fouladi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/ffplay/nsdi18-fouladi.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/fuzzeval.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/fuzzeval.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/gg-paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/gg-paper.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/iPres2019_paper_97.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/iPres2019_paper_97.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/mm19-miniview-comp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/mm19-miniview-comp.pdf -------------------------------------------------------------------------------- /paper/ffmpeg/sec20fall_jiang_prepub.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/ffmpeg/sec20fall_jiang_prepub.pdf -------------------------------------------------------------------------------- /paper/streaming_media/HLS/1524_a_survey_and_evaluation_of_fpga_highlevel_synthesis_tools.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/HLS/1524_a_survey_and_evaluation_of_fpga_highlevel_synthesis_tools.pdf -------------------------------------------------------------------------------- /paper/streaming_media/HLS/Centrifuge_ICCAD.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/HLS/Centrifuge_ICCAD.pdf -------------------------------------------------------------------------------- /paper/streaming_media/HLS/FPGA2021.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/HLS/FPGA2021.pdf -------------------------------------------------------------------------------- /paper/streaming_media/HLS/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/streaming_media/HLS/mantovani_cicc20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/HLS/mantovani_cicc20.pdf -------------------------------------------------------------------------------- /paper/streaming_media/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/10.1.1.124.9084.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/10.1.1.124.9084.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/11647.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/11647.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/2014-Q2SWinet-Offload-Perf-Evaluation-Migault.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/2014-Q2SWinet-Offload-Perf-Evaluation-Migault.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/2019-07.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/2019-07.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/2b14328e0c0a65c14b2a307459f69a8c.DREAM A Data Streaming Application Using RTP RTSP in a Local Area Network.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/2b14328e0c0a65c14b2a307459f69a8c.DREAM A Data Streaming Application Using RTP RTSP in a Local Area Network.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/A201111-706_1322795059.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/A201111-706_1322795059.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/FinalPaperA Survey on open Source Protocols SIP, RTP, RTCP, RTSP, H.264 for Video Conferencing System191361.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/FinalPaperA Survey on open Source Protocols SIP, RTP, RTCP, RTSP, H.264 for Video Conferencing System191361.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/MSE2002.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/MSE2002.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/PROMS2000.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/PROMS2000.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/RTSP-live-streaming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/RTSP-live-streaming.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/cache.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/cache.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/rossi06eurongi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/rossi06eurongi.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/rtp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/rtp.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/rtsp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/rtsp.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/wp529-som-benchmarks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/wp529-som-benchmarks.pdf -------------------------------------------------------------------------------- /paper/streaming_media/RTSP/wu01streaming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/RTSP/wu01streaming.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/2011-AES.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/2011-AES.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/22-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/22-1.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/32-_Fauzan_Masykur.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/32-_Fauzan_Masykur.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/360_video_IFIP.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/360_video_IFIP.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/76533338.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/76533338.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/Cloud-Ingest-of-Live-Video-An-open-approach-to-RIST-SRT-and-retransmission-protocols.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/Cloud-Ingest-of-Live-Video-An-open-approach-to-RIST-SRT-and-retransmission-protocols.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/E3_Management_of_Traffic_During_Construction_v1.3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/E3_Management_of_Traffic_During_Construction_v1.3.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/cired2005_0127.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/cired2005_0127.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/hockxyu-44.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/hockxyu-44.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/hotnets17-final39.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/hotnets17-final39.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/imc2018.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/imc2018.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/periscope-imc16.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/periscope-imc16.pdf -------------------------------------------------------------------------------- /paper/streaming_media/rtmp/symmetry-12-01491.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/paper/streaming_media/rtmp/symmetry-12-01491.pdf -------------------------------------------------------------------------------- /practice_project/README.md: -------------------------------------------------------------------------------- 1 | # WebRTC入门项目 2 | 3 | RTCStartupDemo,致力于提供一套超级简单的信令服务器,以及配套的完全基于 WebRTC 官方 API 的客户端 demo 示例代码(含:Web/Android/iOS/Windows 全平台),目标是让所有有兴趣学习 WebRTC 的同学,都能快速把项目 run 起来,看到通话效果,理解核心 API,快速入门。 4 | 5 | ## 1. 效果图 6 | ![image](https://user-images.githubusercontent.com/87458342/127868691-fb7a4e8f-d79b-4ded-94cd-a1c371067d1a.png) 7 | 8 | ## 2. 目录说明 9 | 10 | ### RTCSignalServer: 11 | 12 | 一个简单的 Go 语言版本的 WebRTC 信令服务器,供 demo 使用 13 | 该信令服务器的 API 文档:[这里](https://github.com/Jhuster/RTCStartupDemo/tree/master/RTCSignalServer) 14 | 15 | ### RTCClientDemo: 16 | * Web 17 | * Android 18 | * iOS(coming soon) 19 | * Windows(coming soon) 20 | 21 | ## 3. 使用方法和限制条件 22 | 23 | 所有端的 demo 只支持 2 个人在局域网内通话,不同端之间也可以互相通话,比如:Android & Web 之间。 24 | 需要配合一台信令服务器,你可以参考项目文档自己编译和部署(推荐),也可以直接使用我部署好的服务器: 25 | 26 | >http://rtc-signal.jhuster.com:8080/socket.io 27 | 28 | 使用我部署的服务器,需要注意如下事项: 29 | 30 | 仅限于测试和学习,不保证服务器的可用性和稳定性 31 | 填写房间号的时候,注意填写一个复杂一点,因为可能会跟网上其他人冲突 32 | 33 | ### 4. 项目依赖 34 | * [WebRTC](https://webrtc.org/) 35 | * Socket.IO](https://socket.io/) 36 | -------------------------------------------------------------------------------- /practice_project/project_02.md: -------------------------------------------------------------------------------- 1 | # PlayerSDK 2 | 3 | [https://github.com/JeffMony/PlayerSDK.git](https://github.com/JeffMony/PlayerSDK.git) 4 | 5 | 这是一个专注音视频播放器的库,目前还在不断优化中,主要功能如下: 6 | * 支持ijkplayer 和 exoplayer 两种播放器 7 | * 支持进度条和seek功能 8 | * 支持二维码扫描网址功能 9 | * 支持循环播放的功能 10 | * 支持倍速的功能 11 | * 支持针对HLS Master视频的切换清晰度功能 12 | * 添加Player实例监控方法 13 | * 支持GLSurfaceView渲染视频 14 | * 播放时调整声音大小 15 | * 播放视频加水印 16 | 17 | ![image](https://user-images.githubusercontent.com/87458342/128356196-b277a4ee-0423-4ada-ac9c-138323e1b6c9.png) 18 | -------------------------------------------------------------------------------- /practice_project/project_03.md: -------------------------------------------------------------------------------- 1 | # VideoDownloader 2 | 3 | [https://github.com/JeffMony/VideoDownloader.git](https://github.com/JeffMony/VideoDownloader.git) 4 | 5 | ## 视频下载SDK功能 6 | 7 | * 下载整视频,如mp4/mkv/mov/3gp等视频 8 | * 下载HLS,即M3U8视频 9 | * M3U8 视频下载完成,会生成一个本地的local.m3u8文件 10 | * 视频下载完成,可以点击播放视频文件 11 | * 视频下载数据库记录视频下载信息 12 | * 增加视频下载队列 13 | * 增加M3U8合并为MP4的功能 14 | * 可以定制下载视频的标题和封面 15 | * 增加视频下载的group-name 16 | -------------------------------------------------------------------------------- /practice_project/project_04.md: -------------------------------------------------------------------------------- 1 | # MediaSDK 2 | 3 | [https://github.com/JeffMony/MediaSDK.git](https://github.com/JeffMony/MediaSDK.git) 4 | 5 | ## Android 平台视频边下边播技术 6 | 7 | ### 1.边下边播技术介绍 8 | 9 | 我们熟知的边下边播技术,是迅雷提供的,还有之前的快播、快车等工具,它们使用的技术基本上都是P2P下载技术; 10 | P2P下载技术,本质上它并不是C-S的架构,P2P----> Peer to Peer,实际上它将各个客户端的资源调度起来,给上传资源种子,方便后续的下载者可以快速有效的下载资源,这种方式需要服务器整合各个Client,在有用户需要下载的情况下,服务器能及时调度资源,开始给下载者提供资源信息,保证下载者下载资源越快越好; 11 | 对一个普通开发者而言,我不想这么费事,我能访问视频资源服务器,我直接从视频源服务器上下载不行吗?是可以的; 12 | 视频下载和视频播放本来是两件完全不相干的事情,但是也有共通之处:播放视频的同时就是需要请求视频资源的; 13 | 我们要实现边下边播,那就要在请求完视频资源的时候,给播放器送去数据,也要存在本地一份,这样才是边下边播;如何实现边下边播; 14 | 15 | ### 2.边下边播技术演进 16 | 17 | ![image](https://user-images.githubusercontent.com/87458342/128356606-161a32fd-dc99-47ed-abf0-ae979837df72.png) 18 | 19 | 正常的模型是**播放器 <----> 视频源服务器**模型,播放器请求视频资源,视频源服务器收到了请求,返回相应的数据,播放器播放视频数据,这种情况下,也是可以做边下边播的,但是有限制;限制主要是边下边播的控制逻辑非常复杂,因为边下边播的逻辑和播放器的控制逻辑势必搞在一起,这样不仅从架构上无法区分,而且代码上也不好分开,后续维护的成本比较高;一般情况下不建议这么做; 20 | 21 | **播放器 <----> 代理服务器 <----> 视频源服务器** 22 | 23 | 这是提出的一个改进的想法,改进的一个点就是在 播放器 和 视频源服务器之间架了 一个 代理服务器,代理服务器请求 视频源服务器数据,然后返回给播放器,这下就实现了将播放模块与下载模块隔离开来; 24 | 但是代理服务器是需要服务器配置的,一般公司没必要搞视频源代理服务器,太耗带宽了。 25 | 26 | 本地代理服务器替代一下这个代理服务器是比较好的一种方法,既可以实现将播放模块和下载模块分层,也可以实现边下边播的功能。 27 | 这就是演进到**播放器 <----> 本地代理服务器 <----> 视频源服务器** 28 | 29 | 我们本文所讲的 边下边播的技术就是 基于本地代理服务展开的。 30 | 31 | ## 3.边下边播技术点解析 32 | 33 | ### 3.1 视频类型 34 | 视频类型,我们知道有整视频和分片视频区分; 35 | 像 mp4 mov mkv avi rmvb 这些封装格式都是整视频,一般情况下,播放器可以一次请求,后续处理,这些视频最终会存储到一个文件中; 36 | 像现在mp4的封装格式应用的最广泛,有一个很优秀的开源库:[https://github.com/danikula/AndroidVideoCache](https://github.com/danikula/AndroidVideoCache) 37 | 主要是针对整视频的边下边播来的; 38 | 39 | 分片视频,就是一个整视频被分为若干个小分片视频,请求的时候不能一次性地请求所有的视频文件; 40 | HLS就是分片视频的典范,关于HLS是什么类型,可以参考文章---->HLS格式解析 41 | 42 | ```C++ 43 | #EXTM3U 44 | #EXT-X-TARGETDURATION:10 45 | 46 | #EXTINF:9.009, 47 | http://media.example.com/first.ts 48 | #EXTINF:9.009, 49 | http://media.example.com/second.ts 50 | #EXTINF:3.003, 51 | http://media.example.com/third.ts 52 | #EXT-X-ENDLIST 53 | ``` 54 | 55 | 这个HLS文件中有3个分片ts视频,我们请求的时候,需要一个一个请求,整视频请求数据是一次就可以的,后续使用206分段下载; 56 | 57 | 实现mp4 等整视频的边下边播是可以的,那么HLS分片视频如何实现边下边播呢? 58 | 59 | ### 3.2 分片视频如何处理 60 | 61 | HLS视频----> HTTP Live Streaming,就是熟知的M3U8视频; 62 | [https://tv.youkutv.cc/2019/10/28/6MSVuLec4zbpYFlj/playlist.m3u8](https://tv.youkutv.cc/2019/10/28/6MSVuLec4zbpYFlj/playlist.m3u8) 63 | 什么样算是HLS类型的视频? 64 | ![image](https://user-images.githubusercontent.com/87458342/128356986-1284e0d2-32c0-42e1-a397-67feab85ef60.png) 65 | 66 | 主要是请求视频url的Content-Type 数据: 67 | ```C++ 68 | public static String MIME_TYPE_M3U8_1 = "application/vnd.apple.mpegurl"; 69 | public static String MIME_TYPE_M3U8_2 = "application/x-mpegurl"; 70 | public static String MIME_TYPE_M3U8_3 = "vnd.apple.mpegurl"; 71 | public static String MIME_TYPE_M3U8_4 = "applicationnd.apple.mpegurl"; 72 | ``` 73 | 74 | 如果发现是上面四种类型,就是M3U8类型的视频,我们就可以按照M3U8解析的规则来解析这个视频url; 75 | 76 | 解析的视频url是: 77 | [http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.main.m3u8](http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.main.m3u8) 78 | 这个m3u8文件存储信息是: 79 | 80 | ```C++ 81 | #EXTM3U 82 | #EXT-X-VERSION:3 83 | #EXT-X-MEDIA-SEQUENCE:0 84 | #EXT-X-TARGETDURATION:11.0 85 | #EXTINF:10.12, 86 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_0_10.ts 87 | #EXTINF:9.88, 88 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_10_20.ts 89 | #EXTINF:10.08, 90 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_20_30.ts 91 | #EXTINF:10.28, 92 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_30_40.ts 93 | #EXTINF:9.68, 94 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_40_50.ts 95 | #EXTINF:10.12, 96 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_50_60.ts 97 | #EXTINF:3.04, 98 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_60_63.ts 99 | #EXT-X-ENDLIST 100 | ``` 101 | 102 | 转化成一个本地代理的文件: 103 | 104 | ```C++ 105 | #EXTM3U 106 | #EXT-X-VERSION:3 107 | #EXT-X-MEDIA-SEQUENCE:0 108 | #EXT-X-TARGETDURATION:11.0 109 | #EXTINF:10.12, 110 | http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_0_10.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts 111 | #EXTINF:9.88, 112 | http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_10_20.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_1.ts 113 | #EXTINF:10.08, 114 | http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_20_30.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_2.ts 115 | #EXTINF:10.28, 116 | http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_30_40.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_3.ts 117 | #EXTINF:9.68, 118 | http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_40_50.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_4.ts 119 | #EXTINF:10.12, 120 | http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_50_60.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_5.ts 121 | #EXTINF:3.04, 122 | http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_60_63.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_6.ts 123 | #EXT-X-ENDLIST 124 | ``` 125 | 126 | 请求一个http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_0_10.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts 127 | 这个http://127.0.0.1:3888的url会被拦截,直接解析出后续的参数: 128 | http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_0_10.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts 129 | 130 | 对这个url decode 一下: 131 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_0_10.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts 132 | 133 | 我们自己定义的一个隔离字符串 &jeffmony& 134 | 这个分隔符将字符串分为两部分: 135 | http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_0_10.ts 136 | 137 | /3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts 138 | 139 | 第一个表示当前分片视频的网络url;第二个表示当前文件本地存储的位置; 140 | 141 | 我们在解析的时候,先判断是否存在本地的分片视频,如果存在,直接读取本地的文件,如果不存在,那要去请求网络的分片url; 142 | 143 | 最终缓存文件夹下内容如下: 144 | 145 | ```C++ 146 | PD1710:/sdcard/Android/data/com.android.media/cache/.local/3bfd0b2eec722da9ed67509a9388dbe2 $ ls 147 | proxy.m3u8 remote.m3u8 seg_0.ts seg_1.ts seg_2.ts seg_3.ts seg_4.ts seg_5.ts seg_6.ts video.info 148 | ``` 149 | 分片视频都下载到了本地; 150 | 151 | 真正下载的逻辑应用不需要介绍了,这个大家直接看代码吧; 152 | 153 | ### 3.3 整视频分段如何处理 154 | 155 | 视频播放不是孤立的行为,用户有可能会拖动进度条的,拖动进度条,如何拖动到当前没有下载到的位置,那就必须要从拖动到位置向后重新下载,这个分段缓存片段的管理也是比较重要的; 156 | 157 | ![image](https://user-images.githubusercontent.com/87458342/128357302-c55a6053-ac97-41f8-997d-444142ffd147.png) 158 | 159 | 用户随意拖动进度条,可能会产生若干个分段的缓存块,这些缓存块是不连续的,但是一旦用户拖动进度条到之前的某个位置,下载资源的时候会将各个分段的缓存块连起来,连接起来之后就是一个完整的视频; 160 | 161 | 不过维护缓存块的逻辑是比较重要的,这儿主要讲解一下思想,具体看下项目代码吧,是按照上面阐述的思想来的; 162 | 163 | ### 3.4 边下边播架构图 164 | 165 | 下面是整个项目的架构图: 166 | 167 | ![image](https://user-images.githubusercontent.com/87458342/128357393-da0eeb02-bd51-4694-b811-f3152c66bfa7.png) 168 | 169 | 视频边下边播的库 ---- [https://github.com/JeffMony/MediaLocalProxyLib](https://github.com/JeffMony/MediaLocalProxyLib) 170 | 171 | 演示效果 172 | ![image](https://user-images.githubusercontent.com/87458342/128357524-2e927dab-eefa-4638-97dd-86023257aa9a.png) 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /protocol/Connection-rfc1242.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/Connection-rfc1242.txt.pdf -------------------------------------------------------------------------------- /protocol/Gopher-rfc4266.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/Gopher-rfc4266.txt.pdf -------------------------------------------------------------------------------- /protocol/HTTP-rfc2068.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/HTTP-rfc2068.txt.pdf -------------------------------------------------------------------------------- /protocol/HTTP-rfc2069.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/HTTP-rfc2069.txt.pdf -------------------------------------------------------------------------------- /protocol/HTTP-rfc2227.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/HTTP-rfc2227.txt.pdf -------------------------------------------------------------------------------- /protocol/HTTP-rfc2518.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/HTTP-rfc2518.txt.pdf -------------------------------------------------------------------------------- /protocol/HTTP-rfc2585.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/HTTP-rfc2585.txt.pdf -------------------------------------------------------------------------------- /protocol/HTTP-rfc2817.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/HTTP-rfc2817.txt.pdf -------------------------------------------------------------------------------- /protocol/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /protocol/RTCP-rfc1889.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc1889.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc3556.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc3556.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc3605.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc3605.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc3611.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc3611.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc4571.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc4571.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc4585.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc4585.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc4588.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc4588.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc5124.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc5124.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc5506.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc5506.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc5725.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc5725.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc5760.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc5760.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc5764.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc5764.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc6035.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc6035.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc6051.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc6051.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc6128.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc6128.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc6222.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc6222.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc6263.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc6263.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc6332.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc6332.txt.pdf -------------------------------------------------------------------------------- /protocol/RTCP-rfc6642.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTCP-rfc6642.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc1889.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc1889.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc1890.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc1890.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2029.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2029.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2035.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2035.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2038.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2038.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2198.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2198.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2250.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2250.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2429.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2429.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2431.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2431.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2435.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2435.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2508.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2508.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2733.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2733.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2793.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2793.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2833.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2833.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc2862.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc2862.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc3016.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc3016.txt.pdf -------------------------------------------------------------------------------- /protocol/RTP-rfc3047.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTP-rfc3047.txt.pdf -------------------------------------------------------------------------------- /protocol/RTSP-rfc2326.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTSP-rfc2326.txt.pdf -------------------------------------------------------------------------------- /protocol/RTSP-rfc4567.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTSP-rfc4567.txt.pdf -------------------------------------------------------------------------------- /protocol/RTSP-rfc7825.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTSP-rfc7825.txt.pdf -------------------------------------------------------------------------------- /protocol/RTSP-rfc7826.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTSP-rfc7826.txt.pdf -------------------------------------------------------------------------------- /protocol/RTSP-rfc8866.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/RTSP-rfc8866.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc2327.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc2327.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc2848.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc2848.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3107.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3107.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3264.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3264.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3266.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3266.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3388.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3388.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3407.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3407.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3485.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3485.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3524.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3524.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3556.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3556.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3611.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3611.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc3890.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc3890.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc4091.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc4091.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc4145.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc4145.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc4298.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc4298.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc4566.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc4566.txt.pdf -------------------------------------------------------------------------------- /protocol/SDP-rfc4567.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/SDP-rfc4567.txt.pdf -------------------------------------------------------------------------------- /protocol/TCP-rfc1001.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/TCP-rfc1001.txt.pdf -------------------------------------------------------------------------------- /protocol/TCP-rfc1002.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/TCP-rfc1002.txt.pdf -------------------------------------------------------------------------------- /protocol/TCP-rfc793.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/TCP-rfc793.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc1001.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc1001.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc1002.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc1002.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc2013.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc2013.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc2147.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc2147.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc2508.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc2508.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc3489.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc3489.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc3519.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc3519.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc3828.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc3828.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc3948.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc3948.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc4019.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc4019.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc4113.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc4113.txt.pdf -------------------------------------------------------------------------------- /protocol/UDP-rfc768.txt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/protocol/UDP-rfc768.txt.pdf -------------------------------------------------------------------------------- /video/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | -------------------------------------------------------------------------------- /音视频流媒体开发知识归纳导图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddc7451/git-github.com-0voice-audio_video_streaming/e80c94419c06636c16e5bbfcec32f1d1343e70c3/音视频流媒体开发知识归纳导图.png --------------------------------------------------------------------------------