-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1172 lines (448 loc) · 85.4 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html class="theme-next gemini use-motion" lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">
<link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">
<meta name="keywords" content="Hexo, NexT" />
<meta property="og:type" content="website">
<meta property="og:title" content="Frank">
<meta property="og:url" content="https://saufwy.github.io/index.html">
<meta property="og:site_name" content="Frank">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="fanwenyao">
<meta name="twitter:card" content="summary">
<script type="text/javascript" id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '',
scheme: 'Gemini',
version: '5.1.4',
sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
fancybox: true,
tabs: true,
motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
duoshuo: {
userId: '0',
author: 'Author'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
</script>
<link rel="canonical" href="https://saufwy.github.io/"/>
<title>Frank</title>
<meta name="generator" content="Hexo 5.4.0"></head>
<body itemscope itemtype="http://schema.org/WebPage" lang="en">
<div class="container sidebar-position-left
page-home">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-wrapper">
<div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">Frank</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<p class="site-subtitle"></p>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section">
<i class="menu-item-icon fa fa-fw fa-home"></i> <br />
Home
</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/categories/" rel="section">
<i class="menu-item-icon fa fa-fw fa-th"></i> <br />
Categories
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section">
<i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
Archives
</a>
</li>
</ul>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<section id="posts" class="posts-expand">
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://saufwy.github.io/2021/06/26/media-process-unit/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Frank">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2021/06/26/media-process-unit/" itemprop="url">MPU 服务设计与实现</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Post created" itemprop="dateCreated datePublished" datetime="2021-06-26T17:58:41+08:00">
2021-06-26
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%90%8E%E7%AB%AF%E6%9E%B6%E6%9E%84/" itemprop="url" rel="index">
<span itemprop="name">后端架构</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><p>MPU (Media Process Unit) : 音视频媒体处理服务, 提供对音视频流进行转码、转封装服务。</p>
<h3 id="需求来源"><a href="#需求来源" class="headerlink" title="需求来源"></a>需求来源</h3><p>随着直播行业大火, 各类直播产品层出不穷, 满足不同的用户需求及直播体验。不同应用场景、不同人群对直播延迟、清晰度、流畅度要求有所不同。例如, 才艺秀直播多数观众更关注画质、流畅度, 对直播延迟并不很敏感, 而参与连麦互动的观众为了能够保证正常的双向互动, 延迟要求要在500ms 以下, 还有部分观众因网络原因更愿意切换到清晰度较低的画质来保证流畅度。 </p>
<p>多直播协议 + 多码流 为直播各类需求提供了技术支撑。这就意味着一场直播需要多种直播协议、多清晰度流的存在。如果把这些功能需求放到主播端, 不仅对主播端直播设备性能有巨大挑战, 而且会影响直播质量, 不利于业务快速升级迭代。 业界一种通用做法是主播推一路高清流到服务, 通过MPU 服务实现多协议、多清晰度流的转换。</p>
<h3 id="场景描述"><a href="#场景描述" class="headerlink" title="场景描述"></a>场景描述</h3><p>下图展示了主播源端推 rtc 流到 rtc 服务, MPU 从rtc服务拉流, 将rtc流转换为rtmp协议, 推流到百度、腾讯 cdn。</p>
<p><img src="/images/live.png" alt="live"></p>
<h3 id="Pipeline-模型抽象"><a href="#Pipeline-模型抽象" class="headerlink" title="Pipeline 模型抽象"></a>Pipeline 模型抽象</h3><p>音视频转码转封装处理流程都可以用如下图所示简单模型表示:</p>
<p><img src="/images/pipeline.png" alt="pipeline"></p>
<p>这个简单模型真的可以满足所有的应用场景吗?接下来我们以 1 路高清 rtc 流转 2 路 rtmp 流和 1 路标清 rtc 流为例, 说明简单模型发挥的强大功效:<br>rtc 流输出h264 nalu frame 和 opus frame, rtmp 协议音频需要 aac 编码数据, 因此需要将 opus frame 先解码后 aac 编码输出给 rtmp 流; 标清 rtc 流需要更低的码率, 因此需要将视频数据先转码然后输出给 rtc 标清流。</p>
<p><img src="/images/zrtc2rtmp.png" alt="zrtc2rtmp"></p>
<h3 id="架构设计"><a href="#架构设计" class="headerlink" title="架构设计"></a>架构设计</h3><p>通过前两节的模型图, 我们可以看出 MPU 服务是由许多节点(Input、AudioFilter、VideoFilter、Output) 及节点之间的数据传递关系组成, 节点及节点数据传递关系最终构成一个图, 因此实现一个 MPU 服务的过程就是一个 build graph 过程。<br>我们将 MPU 服务拆分为两部分: graph generator + media-processing(本文主要阐述音视频处理部分, 为避免过多额外的逻辑负担, 省略了真实环境中集群分布式架构说明)</p>
<p><img src="/images/mpu-architecture.png" alt="mpu-architecture"></p>
<ul>
<li>graph generator : 接受不同的业务请求, 根据不同的业务场景生成对应的 graph protocol, 然后请求 media-processing 模块</li>
<li>media-processing : 根据 graph protocol 协议创建音视频处理节点, 建立节点之间的数据传输关系, 对音视频进行处理</li>
</ul>
<h3 id="通信协议设计"><a href="#通信协议设计" class="headerlink" title="通信协议设计"></a>通信协议设计</h3><p>下面我以 1 路 rtc 流转 2路 rtmp 流(音频需要转码)为例, 展示了graph protocol 的 协议表示:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line">// 输入节点</span><br><span class="line">"inputs" : [</span><br><span class="line"> {</span><br><span class="line"> "type" : "rtc",</span><br><span class="line"> "args": "stream_name=123456_1&uid=100"</span><br><span class="line"> }</span><br><span class="line">],</span><br><span class="line">// 音频处理节点</span><br><span class="line">"audiofilters" : [</span><br><span class="line"> {</span><br><span class="line"> "type" : "opusdecoder",</span><br><span class="line"> "args" : "sample_rate=48000&channels=1&fmt=s16le"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "type" : "aacencoder",</span><br><span class="line"> "args" : "sample_rate=48000&channels=1&fmt=s16le"</span><br><span class="line"> }</span><br><span class="line">],</span><br><span class="line">// 使用数据传递链的形式表达各节点数据传输关系</span><br><span class="line">// 输出节点 </span><br><span class="line">"outpus" : [</span><br><span class="line"> {</span><br><span class="line"> "type": "rtmp",</span><br><span class="line"> "args": "rtmp://baiducdn.com/live/test"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "type": "rtmp",</span><br><span class="line"> "args": "rtmp://tengxuncdn.com/live/test"</span><br><span class="line"> }</span><br><span class="line">],</span><br><span class="line">"links" : [</span><br><span class="line"> // 视频数据传递链条</span><br><span class="line"> {</span><br><span class="line"> {</span><br><span class="line"> "type" :"input",</span><br><span class="line"> "index" : 0,</span><br><span class="line"> "data_type" : "encoded_video"</span><br><span class="line"> }</span><br><span class="line"> {</span><br><span class="line"> "type" : "output",</span><br><span class="line"> "index", 0,</span><br><span class="line"> "data_type" : "encoded_video"</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> // 音频数据传递链条</span><br><span class="line"> {</span><br><span class="line"> {</span><br><span class="line"> "type" :"input",</span><br><span class="line"> "index" : 0,</span><br><span class="line"> "data_type" : "encoded_audio"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "type" : "audiofilter",</span><br><span class="line"> "index", 0,</span><br><span class="line"> "data_type" : "raw_audio"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "type" : "audiofilter",</span><br><span class="line"> "index", 1,</span><br><span class="line"> "data_type" : "encoded_audio"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "type" : "output",</span><br><span class="line"> "index", 0,</span><br><span class="line"> "data_type" : "encoded_audio"</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line">]</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="音视频处理模块设计"><a href="#音视频处理模块设计" class="headerlink" title="音视频处理模块设计"></a>音视频处理模块设计</h3><p>media-processing 需要对各类的场景的graph支持, 因此基于场景的编码实现并不能满足要求, 我们需要统一的构建流程对各类 graph 支持, 下面我通过 4 个小节阐述媒体处理的设计:</p>
<h4 id="1-基于抽象接口"><a href="#1-基于抽象接口" class="headerlink" title="1.基于抽象接口"></a>1.基于抽象接口</h4><p>我们将处理节点抽象为InputStream、AudioFilter、VideoFilter、OutputStream 4 类, 具体节点均需实现对应接口定义的功能。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">InputStreamInterface</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">// 添加订阅者, 把处理好的数据传递给后续节点</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">add_audio_sink</span><span class="params">(AudioFilterInterface* sink)</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">add_video_sink</span><span class="params">(VideoFilterInterface* sink)</span></span>;</span><br><span class="line">}; </span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AudioFilterInterface</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">// 接收前置节点的数据</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">on_audio_frame</span><span class="params">(AudioFrame frame)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 添加订阅者, 把处理好的数据传递给后续节点</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">add_audio_sink</span><span class="params">(AudioFilterInterface* sink)</span></span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">VideoFilterInterface</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">// 接收前置节点的数据</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">on_video_frame</span><span class="params">(VideoFrame frame)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 添加订阅者, 把处理好的数据传递给后续节点</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">add_video_sink</span><span class="params">(VideoFilterInterface* sink)</span></span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">OutputStreamInterface</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">// 接收音视频数据</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">on_audio_frame</span><span class="params">(AudioFrame frame)</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">on_video_frame</span><span class="params">(VideoFrame frame)</span></span>;</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<h4 id="2-多线程-or-多进程"><a href="#2-多线程-or-多进程" class="headerlink" title="2.多线程 or 多进程"></a>2.多线程 or 多进程</h4><p>多线程程序可以避免进程上下文切换带来的性能消耗, 但如果进程出现异常导致进程奔溃, 会引起全部任务中断, 多进程则可以保证单个任务的异常不会扩散。由于需要对接第三方服务带来的不可控因素, 我们真实环境选择了多进程模型实现。</p>
<h4 id="3-音视频同步"><a href="#3-音视频同步" class="headerlink" title="3.音视频同步"></a>3.音视频同步</h4><p>从上面的模型中, 我们可以看出, 音视频流是分离单独处理的, 音视频数据流经各个节点到输出节点, 由于中间节点数据处理时间消耗各不相同, 那么在输出节点如何保证同步呢? 答案是实现统一的音视频数据交织器InterLeaver, 在InterLeaver 模块中设置最大容忍的时间差, 如果超出预期需要及时报警处理。</p>
<h4 id="4-反应式编程在媒体处理中的应用"><a href="#4-反应式编程在媒体处理中的应用" class="headerlink" title="4.反应式编程在媒体处理中的应用"></a>4.反应式编程在媒体处理中的应用</h4><p>现在我们讨论单个节点如何进行驱动, 输入输出我们通常可以采用 单线程 + epoll io 模型进行驱动, 开源库 libev、 libuv 对此进行了很好的封装, 在这里我们不做重点讨论, 本节我们重点讨论 AudioFilter VideoFilter 节点该如何驱动。 </p>
<p>AudioFilter、VideoFilter 的特点是: 数据帧驱动。这与反应式编程完美契合, 下面我直接以音频编码器的例子展示其应用:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AudioEncoderWrapper</span> :</span> <span class="keyword">public</span> AudioFilterInterface {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">// 接收前置节点的数据</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">on_video_frame</span><span class="params">(AudioFrame frame)</span> </span>{</span><br><span class="line"> _taks_queue-><span class="built_in">post_task</span>([<span class="keyword">this</span>,frame](){</span><br><span class="line"> _encoder-><span class="built_in">encode</span>(frame);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 添加订阅者, 把处理好的数据传递给后续节点</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">add_video_sink</span><span class="params">(AudioFilterInterface* sink)</span> </span>{</span><br><span class="line"> _sinks.<span class="built_in">push_back</span>(sink);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 编码后的数据会回调到这里</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">on_encoded_frame</span><span class="params">(AudioFrame frame)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">auto</span>& sink : _sinks) {</span><br><span class="line"> sink-><span class="built_in">on_audio_frame</span>(frame);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line"> AACEncoder* _encoder = <span class="literal">nullptr</span>;</span><br><span class="line"> TaskQueue* _task_queue;</span><br><span class="line"> std::vector<AudioFilterInterface*> _sinks;</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>我们通过将一次编码任务封装成可执行对象, 投递到TaskQueue, TaskQueue 内部使用消息驱动的方式不断获取任务、执行任务, 使用TaskQueue 有如下优点: </p>
<ol>
<li>节点解耦, 异步执行, 前置节点只需要将收到的数据帧抛出即可</li>
<li>AACEncoder 编码功能与线程驱动解耦, AACEncoder 模块功能单一利于维护</li>
<li>节点间共享处理线程成为可能, 我们可以在上层应用创建TaskQueue, 将其传递给多个节点使用, 在实际应用中, 音频处理通常耗时较短, 可以多个处理节点共用一个TaskQueue(一个TaskQueue由一个线程驱动), 避免每个节点都要创建单独的驱动线程带来的cpu切换消耗</li>
</ol>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>build graph 架构本质是业务应用与媒体处理基础能力解耦, 在新增或变更业务需求时, 只需增加或修改对应的 graph, 做到快速业务响应。 </p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://saufwy.github.io/2021/06/26/single-responsibility-principle/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Frank">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2021/06/26/single-responsibility-principle/" itemprop="url">单一职责原则</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Post created" itemprop="dateCreated datePublished" datetime="2021-06-26T14:56:38+08:00">
2021-06-26
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" itemprop="url" rel="index">
<span itemprop="name">设计模式</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><p>单一职责原则(Sigle Responsibility Principle) : 就一个类而言, 应该仅有一个引起它变化的原因。</p>
<h3 id="如何理解单一职责原则"><a href="#如何理解单一职责原则" class="headerlink" title="如何理解单一职责原则"></a>如何理解单一职责原则</h3><p>单一职责的定义描述的很清楚, 一个类只应该完成一个功能, 但是在实践的过程中, 不同的人对功能粒度的定义不同。 A 认为一个功能应该拆分为3个类实现, 而 B 认为A是过度设计, 放到一个类中更有利于功能内聚, 便于维护。遇到类似的情况我们如何较为客观的做出评估呢? </p>
<p>事实上, 出现分歧(也可能是开发人员自我纠结)时, 我们应该回归到设计的目的、真实应用场景上: </p>
<ul>
<li>设计的目的: 可读、可单元测试、易维护、可扩展、可复用 </li>
<li>真实应用场景: 长期项目还是应急项目、 拆分的功能单元是否存在复用 </li>
</ul>
<p>通常对于短期应急项目, 我们并不需要做太多的设计, 我们更应该注重团队高效需求沟通, 快速功能实现; 对于长期项目来说, 我们更多的应该注重架构设计, 避免后期架构不能满足设计, 而对于模块级别的设计, 模块粒度可以稍大, 前期的开发中通常并不会有太多的复用存在, 大粒度的类更容易让我们聚焦全流程功能实现, 而不是陷于某一个类中, 当模块功能第一次发生复用的时, 应该及时进行类拆封, 避免逻辑冗余。 </p>
<p>通过重新审视设计目的以及回归应用场景,我们可以得出以下实践经验:<br>我们可以先实现一个粗粒度的类, 满足业务需求, 当以下情况发生时, 我们再对类进行拆分和细化, 也就是持续重构: </p>
<ul>
<li>类中部分逻辑在其他模块中可复用</li>
<li>类的代码行数、方法、属性过多, 直接从类的公有方法难以理解类的功能</li>
<li>类名与类功能不相符 </li>
</ul>
<p>以上3中情况是我在工作中常用的几个判断标准, 当然还有更多更好的判断类是否符合单一职责的方法, 正如单一职责原则我们没有办法给出一个统一确定的标准, 但是我们却可以在实践中不断总结最佳实践经验。</p>
<h3 id="过度设计会有哪些问题"><a href="#过度设计会有哪些问题" class="headerlink" title="过度设计会有哪些问题"></a>过度设计会有哪些问题</h3><p>我这里总结了自己工作中遇到的过度设计出现的问题, 不知看博客的你是否也有同样的感受:</p>
<ol>
<li>类文件过多, 阅读代码会跳来跳去, 不利于前期快速开发维护 </li>
<li>拆分功能的过程就是功能抽象的过程, 在功能没有发生复用时的过度拆分会引起模块拆分的不合理, 难以复用</li>
<li>单元测试简单而没有意义</li>
<li>给上层应用程序造成过多的调用逻辑负担</li>
</ol>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>单一职责原则为我们指明了基本的模块设计原则, 更多的我们需要根据具体情况来将设计原则应用于实践。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://saufwy.github.io/2021/06/06/pthread-lock/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Frank">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2021/06/06/pthread-lock/" itemprop="url">pthread_mutex_lock 源码剖析</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Post created" itemprop="dateCreated datePublished" datetime="2021-06-06T17:38:13+08:00">
2021-06-06
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/Linux/" itemprop="url" rel="index">
<span itemprop="name">Linux</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><p>多进程、多线程环境下同时读写共享数据资源会引起数据状态不一致, Linux 下使用pthread_mutex 来保护共享资源, 使得同一时间只能有一个进程或一个线程访问共享资源。pthread_mutex 值得注意的3个属性是: 类型属性、进程共享属性、健壮属性。本文以pthread_mutex 3 个重要属性为切入点, 结合glibc-2.12.2 mutex 相关源码分析mutex运作机制。</p>
<h3 id="类型属性"><a href="#类型属性" class="headerlink" title="类型属性"></a>类型属性</h3><p>pthread_mutex 共有4种类型, 分别是TIMED、RECURSIVE、ERRORCHECK、ADAPTIVE, 不同的类型的锁具有不同的特性, 这里需要强调的一点是, 这些特性均是用户态实现。类型定义如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">// 代码段摘自glibc-2.12.2 pthread.h</span><br><span class="line"></span><br><span class="line">enum</span><br><span class="line">{</span><br><span class="line"> PTHREAD_MUTEX_TIMED_NP,</span><br><span class="line"> PTHREAD_MUTEX_RECURSIVE_NP,</span><br><span class="line"> PTHREAD_MUTEX_ERRORCHECK_NP,</span><br><span class="line"> PTHREAD_MUTEX_ADAPTIVE_NP</span><br><span class="line">#if defined __USE_UNIX98 || defined __USE_XOPEN2K8</span><br><span class="line"> ,</span><br><span class="line"> PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_TIMED_NP,</span><br><span class="line"> PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,</span><br><span class="line"> PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,</span><br><span class="line"> PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL</span><br><span class="line">#endif</span><br><span class="line">#ifdef __USE_GNU</span><br><span class="line"> /* For compatibility. */</span><br><span class="line"> , PTHREAD_MUTEX_FAST_NP = PTHREAD_MUTEX_TIMED_NP</span><br><span class="line">#endif</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>4种类型锁特性描述如下:<br><img src="https://saufwy.github.io/images/pthread_mutex_types.gif" alt="pthread_mutex types"></p>
<p>了解了不同类型锁的特性后, 让我们通过glic-2.12.2 pthread_mutex_lock 源码分析不同类型锁特性的实现:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码段摘自 glibc-2.12.2 pthread_mutex_lock.c</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span></span><br><span class="line">__pthread_mutex_lock (mutex)</span><br><span class="line"> <span class="keyword">pthread_mutex_t</span> *mutex;</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">assert</span> (<span class="built_in"><span class="keyword">sizeof</span></span> (mutex->__size) >= <span class="built_in"><span class="keyword">sizeof</span></span> (mutex->__data));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> type = <span class="built_in">PTHREAD_MUTEX_TYPE</span> (mutex);</span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (type & ~PTHREAD_MUTEX_KIND_MASK_NP, <span class="number">0</span>))</span><br><span class="line"> <span class="keyword">return</span> __pthread_mutex_lock_full (mutex);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">pid_t</span> id = <span class="built_in">THREAD_GETMEM</span> (THREAD_SELF, tid);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP)</span><br><span class="line"> == PTHREAD_MUTEX_TIMED_NP)</span><br><span class="line"> {</span><br><span class="line"> simple:</span><br><span class="line"> <span class="comment">/* Normal mutex. */</span></span><br><span class="line"> <span class="built_in">LLL_MUTEX_LOCK</span> (mutex);</span><br><span class="line"> <span class="built_in">assert</span> (mutex->__data.__owner == <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (__builtin_expect (type == PTHREAD_MUTEX_RECURSIVE_NP, <span class="number">1</span>))</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* Recursive mutex. */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Check whether we already hold the mutex. */</span></span><br><span class="line"> <span class="keyword">if</span> (mutex->__data.__owner == id)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">/* Just bump the counter. */</span></span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (mutex->__data.__count + <span class="number">1</span> == <span class="number">0</span>, <span class="number">0</span>))</span><br><span class="line"> <span class="comment">/* Overflow of the counter. */</span></span><br><span class="line"> <span class="keyword">return</span> EAGAIN;</span><br><span class="line"></span><br><span class="line"> ++mutex->__data.__count;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* We have to get the mutex. */</span></span><br><span class="line"> <span class="built_in">LLL_MUTEX_LOCK</span> (mutex);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">assert</span> (mutex->__data.__owner == <span class="number">0</span>);</span><br><span class="line"> mutex->__data.__count = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (__builtin_expect (type == PTHREAD_MUTEX_ADAPTIVE_NP, <span class="number">1</span>))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (! __is_smp)</span><br><span class="line"> <span class="keyword">goto</span> simple;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">LLL_MUTEX_TRYLOCK</span> (mutex) != <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> cnt = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> max_cnt = <span class="built_in">MIN</span> (MAX_ADAPTIVE_COUNT,</span><br><span class="line"> mutex->__data.__spins * <span class="number">2</span> + <span class="number">10</span>);</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (cnt++ >= max_cnt)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">LLL_MUTEX_LOCK</span> (mutex);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> BUSY_WAIT_NOP</span></span><br><span class="line"> BUSY_WAIT_NOP;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> (<span class="built_in">LLL_MUTEX_TRYLOCK</span> (mutex) != <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> mutex->__data.__spins += (cnt - mutex->__data.__spins) / <span class="number">8</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">assert</span> (mutex->__data.__owner == <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">assert</span> (type == PTHREAD_MUTEX_ERRORCHECK_NP);</span><br><span class="line"> <span class="comment">/* Check whether we already hold the mutex. */</span></span><br><span class="line"> <span class="keyword">if</span> (__builtin_expect (mutex->__data.__owner == id, <span class="number">0</span>))</span><br><span class="line"> <span class="keyword">return</span> EDEADLK;</span><br><span class="line"> <span class="keyword">goto</span> simple;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Record the ownership. */</span></span><br><span class="line"> mutex->__data.__owner = id;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> NO_INCR</span></span><br><span class="line"> ++mutex->__data.__nusers;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>可以看到, pthread_mutex_lock 分别对4种不同类型的锁进行处理, 下面我们依次做简单介绍: </p>
<ul>
<li>PTHREAD_MUTEX_TIMED_NP : 普通锁, 直接进行LLL_MUTEX_LOCK 调用(稍后我们再来分析该函数实现) </li>
<li>PTHREAD_MUTEX_RECURSIVE_NP : 递归锁, 如果当前线程已经拥有锁, 则计数器加1(释放锁会对计数器减一, 计数器为0释放锁), 否则调用LLL_MUTEX_LOCK </li>
<li>PTHREAD_MUTEX_ADAPTIVE_NP : 自旋锁, 先尝试获取锁, 获取失败则开始自旋, 超过最大等待次数后, 直接调用LLL_MUTEX_LOCK(注意自旋最大次数的计算方式, 利用上次自旋结果, 为本次自旋计算一个合理的值) </li>
<li>PTHREAD_MUTEX_ERRORCHECK_NP : 检测是否拥有锁, 如果已经拥有锁, 则返回错误值 EDEADLK, 否则调用LLL_MUTEX_LOCK<br>接下来, 让我们继续分析 LLL_MUTEX_LOCK 源码<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码段摘自 glibc-2.12.2 pthread_mutex_lock.c lowerlevellock.c</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> LLL_MUTEX_LOCK</span></span><br><span class="line"><span class="meta"># <span class="meta-keyword">define</span> LLL_MUTEX_LOCK(mutex) \</span></span><br><span class="line"><span class="meta"> lll_lock ((mutex)->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex))</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> defined NOT_IN_libc || defined UP</span></span><br><span class="line"><span class="meta"># <span class="meta-keyword">define</span> __lll_lock_asm_start LOCK_INSTR <span class="meta-string">"cmpxchgl %1, %2\n\t"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line"><span class="meta"># <span class="meta-keyword">define</span> __lll_lock_asm_start <span class="meta-string">"cmpl $0, %%gs:%P6\n\t"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"je 0f\n\t"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"lock\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"0:\tcmpxchgl %1, %2\n\t"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> lll_lock(futex, private) \</span></span><br><span class="line"><span class="meta"> (void) \</span></span><br><span class="line"><span class="meta"> ({ int ignore1, ignore2; \</span></span><br><span class="line"><span class="meta"> <span class="meta-keyword">if</span> (__builtin_constant_p (private) && (private) == LLL_PRIVATE) \</span></span><br><span class="line"><span class="meta"> __asm __volatile (__lll_lock_asm_start \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"jnz _L_lock_%=\n\t"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">".subsection 1\n\t"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">".type _L_lock_%=,@function\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"_L_lock_%=:\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"1:\tleal %2, %%ecx\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"2:\tcall __lll_lock_wait_private\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"3:\tjmp 18f\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"4:\t.size _L_lock_%=, 4b-1b\n\t"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">".previous\n"</span> \</span></span><br><span class="line"><span class="meta"> LLL_STUB_UNWIND_INFO_3 \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"18:"</span> \</span></span><br><span class="line"><span class="meta"> : <span class="meta-string">"=a"</span> (ignore1), <span class="meta-string">"=c"</span> (ignore2), <span class="meta-string">"=m"</span> (futex) \</span></span><br><span class="line"><span class="meta"> : <span class="meta-string">"0"</span> (0), <span class="meta-string">"1"</span> (1), <span class="meta-string">"m"</span> (futex), \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"i"</span> (MULTIPLE_THREADS_OFFSET) \</span></span><br><span class="line"><span class="meta"> : <span class="meta-string">"memory"</span>); \</span></span><br><span class="line"><span class="meta"> <span class="meta-keyword">else</span> \</span></span><br><span class="line"><span class="meta"> { \</span></span><br><span class="line"><span class="meta"> int ignore3; \</span></span><br><span class="line"><span class="meta"> __asm __volatile (__lll_lock_asm_start \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"jnz _L_lock_%=\n\t"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">".subsection 1\n\t"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">".type _L_lock_%=,@function\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"_L_lock_%=:\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"1:\tleal %2, %%edx\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"0:\tmovl %8, %%ecx\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"2:\tcall __lll_lock_wait\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"3:\tjmp 18f\n"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"4:\t.size _L_lock_%=, 4b-1b\n\t"</span> \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">".previous\n"</span> \</span></span><br><span class="line"><span class="meta"> LLL_STUB_UNWIND_INFO_4 \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"18:"</span> \</span></span><br><span class="line"><span class="meta"> : <span class="meta-string">"=a"</span> (ignore1), <span class="meta-string">"=c"</span> (ignore2), \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"=m"</span> (futex), <span class="meta-string">"=&d"</span> (ignore3) \</span></span><br><span class="line"><span class="meta"> : <span class="meta-string">"1"</span> (1), <span class="meta-string">"m"</span> (futex), \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"i"</span> (MULTIPLE_THREADS_OFFSET), <span class="meta-string">"0"</span> (0), \</span></span><br><span class="line"><span class="meta"> <span class="meta-string">"g"</span> ((int) (private)) \</span></span><br><span class="line"><span class="meta"> : <span class="meta-string">"memory"</span>); \</span></span><br><span class="line"><span class="meta"> } \</span></span><br><span class="line"><span class="meta"> })</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span></span><br><span class="line">__lll_lock_wait_private (<span class="keyword">int</span> *futex)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> oldval = <span class="built_in">atomic_compare_and_exchange_val_24_acq</span> (futex, <span class="number">2</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (oldval != <span class="number">0</span>)</span><br><span class="line"> <span class="built_in">lll_futex_wait</span> (futex, <span class="number">2</span>, LLL_PRIVATE);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> (<span class="built_in">atomic_compare_and_exchange_val_24_acq</span> (futex, <span class="number">2</span>, <span class="number">0</span>) != <span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* These functions don't get included in libc.so */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> IS_IN_libpthread</span></span><br><span class="line"><span class="keyword">void</span></span><br><span class="line">__lll_lock_wait (<span class="keyword">int</span> *futex, <span class="keyword">int</span> <span class="keyword">private</span>)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> oldval = <span class="built_in">atomic_compare_and_exchange_val_24_acq</span> (futex, <span class="number">2</span>, <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (oldval != <span class="number">0</span>)</span><br><span class="line"> <span class="built_in">lll_futex_wait</span> (futex, <span class="number">2</span>, <span class="keyword">private</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> (<span class="built_in">atomic_compare_and_exchange_val_24_acq</span> (futex, <span class="number">2</span>, <span class="number">0</span>) != <span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
代码看着比较多, 我们重点看下 __lll_trylock_asm 宏 以及 __lll_lock_wait_private 函数。 </li>
<li>__lll_trylock_asm : lock cmpxchgl jnz 指令实现CAS, CAS 成功则直接返回 </li>
<li>__lll_lock_wait_private : __lll_trylock_asm CAS 失败后, 进入内核等待(暂时还没有搞清楚__lll_lock_wait 中对 futex 赋值 2 的真正原因), __lll_lock_wait_private 由系统调用futex提供支持</li>
</ul>
<h3 id="进程共享属性"><a href="#进程共享属性" class="headerlink" title="进程共享属性"></a>进程共享属性</h3><p>进程共享属性是指多个进程共享使用锁, 默认属性是多线程共享。进程共享需要在进程间共享内存上构建mutex, 通过阅读glibc-2.12.2 源码 pthread_mutex_init 并未发现其支持共享mutex。 本属性暂时作为一个概念了解, 如有知道的同学, 请不吝赐教, 提前谢谢啦。</p>
<h3 id="健壮属性"><a href="#健壮属性" class="headerlink" title="健壮属性"></a>健壮属性</h3><p>健壮属性与进程共享属性相关, 是指在多进程共享锁的情况下, 如果一个获得锁的进程在释放锁前就提前退出(crash 或 程序bug), 需要解决互斥量恢复的问题。</p>
<h3 id="应用小结"><a href="#应用小结" class="headerlink" title="应用小结"></a>应用小结</h3><ol>
<li>pthread_mutex_lock 在没有其他线程占用锁的情况下在用户态直接获取锁并返回, 此时锁并不会带来性能消耗 </li>
<li>使用自旋锁在特定应用环境下可以避免用户态、内核态切换, 提高应用程序性能</li>
</ol>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://saufwy.github.io/2021/06/05/aac-format/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Frank">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2021/06/05/aac-format/" itemprop="url">AAC 音频格式</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Post created" itemprop="dateCreated datePublished" datetime="2021-06-05T17:52:29+08:00">
2021-06-05
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E9%9F%B3%E8%A7%86%E9%A2%91%E5%A4%84%E7%90%86/" itemprop="url" rel="index">
<span itemprop="name">音视频处理</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><p>AAC(Advanced Audio Coding) 是一种专为音频编码设计的压缩标准, 编码后的数据通常包含两部分信息 descriptive information + compressed data, descriptive information 包含音频的采样率、通道数、编码profile等信息, compressed data 就是PCM原始数据经过压缩后的输出。 ADTS 与 AudioSpecificConfig + Compressed Data是最常见的AAC数据格式。 更多应用格式可参考 ISO/IEC 14496-3:2005 0.2.1, 其格式与本文所述两种格式类似。</p>
<hr>
<h3 id="ADTS-Format"><a href="#ADTS-Format" class="headerlink" title="ADTS Format"></a>ADTS Format</h3><h4 id="ADTS-简述"><a href="#ADTS-简述" class="headerlink" title="ADTS 简述"></a>ADTS 简述</h4><p>ADTS(Aduio Data Transport Stream) 通常用于MPEG TS, 直播协议HLS中音频数据使用该格式传输。adts 数据帧由adts header + compressed data 组成,adts header 即上文提到的description information。adts帧由于每帧都包含解码必备信息,任意一帧都可以作为解码的起始点,但是在音频描述信息不会变化的应用场景中,adts会引起部分传输带宽浪费。 adts header 包含adts fixed header + adts variable header。adts 语义可参考 ISO/IEC 14496-3:2005 Annex 1.A(标准文档对adts定义了许多扩展,大多数情况下用不到), 为了更方便adts header解析, 我以表格的形式展示如下:</p>
<h4 id="ADTS-语义"><a href="#ADTS-语义" class="headerlink" title="ADTS 语义"></a>ADTS 语义</h4><p><img src="https://saufwy.github.io/images/adts_fixed_header.gif" alt="adts fixed header"></p>
<p><img src="https://saufwy.github.io/images/adts_variable_header.gif" alt="adts fixed header"></p>
<h4 id="ADTS-实例解析"><a href="#ADTS-实例解析" class="headerlink" title="ADTS 实例解析"></a>ADTS 实例解析</h4><p>// 使用ffmpeg 生成adts数据文件<br>// ffmpeg -i test.mp4 -map 0:a -c copy test.aac</p>
<p>ADTS fixed header 实例分析(28bit,剩余bit是可变头信息)<br>hex : FF F1 54 80 03 FF FC<br>bin : 1111 1111 1111 0001 0101 0100 1000 0000 0000 0011 1111 1111 1111 1100<br>syncword = 0xfff<br>id = 0 // mpge-4<br>layer = 00<br>protection_absent = 1 // 没有crc校验<br>profile = 01 // aac low<br>sampling_frequency_index = 0101 // 5 = 32000Hz<br>private_bit = 0 // 编码设置为0<br>channel_configuration = 010 // 2 = 双声道<br>origin_copy = 0 // 编码时设置 </p>
<p>home = 0 // 编码时设置<br>copyrighted_id_bit = 0; 编码时设置为0<br>copyrighted_id_start = 0; 编码时设置为0<br>aac_frame_length = 31; 31 - 7 = 24, aac 数据长度为24字节<br>adts_buffer_fullness = 0x7ff; 表示码率可变<br>number_of_raw_data_blocks_in_frame = 0; 0 + 1 = 1表示包含一个原始帧 </p>
<h4 id="ADTS-ffmpeg-解析代码"><a href="#ADTS-ffmpeg-解析代码" class="headerlink" title="ADTS ffmpeg 解析代码"></a>ADTS ffmpeg 解析代码</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码段摘自 ffmpeg/libavcodec/adts_header.c</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">ff_adts_header_parse</span><span class="params">(GetBitContext *gbc, AACADTSHeaderInfo *hdr)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> size, rdb, ch, sr;</span><br><span class="line"> <span class="keyword">int</span> aot, crc_abs;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">get_bits</span>(gbc, <span class="number">12</span>) != <span class="number">0xfff</span>)</span><br><span class="line"> <span class="keyword">return</span> AAC_AC3_PARSE_ERROR_SYNC;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">skip_bits1</span>(gbc); <span class="comment">/* id */</span></span><br><span class="line"> <span class="built_in">skip_bits</span>(gbc, <span class="number">2</span>); <span class="comment">/* layer */</span></span><br><span class="line"> crc_abs = <span class="built_in">get_bits1</span>(gbc); <span class="comment">/* protection_absent */</span></span><br><span class="line"> aot = <span class="built_in">get_bits</span>(gbc, <span class="number">2</span>); <span class="comment">/* profile_objecttype */</span></span><br><span class="line"> sr = <span class="built_in">get_bits</span>(gbc, <span class="number">4</span>); <span class="comment">/* sample_frequency_index */</span></span><br><span class="line"> <span class="keyword">if</span> (!avpriv_mpeg4audio_sample_rates[sr])</span><br><span class="line"> <span class="keyword">return</span> AAC_AC3_PARSE_ERROR_SAMPLE_RATE;</span><br><span class="line"> <span class="built_in">skip_bits1</span>(gbc); <span class="comment">/* private_bit */</span></span><br><span class="line"> ch = <span class="built_in">get_bits</span>(gbc, <span class="number">3</span>); <span class="comment">/* channel_configuration */</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">skip_bits1</span>(gbc); <span class="comment">/* original/copy */</span></span><br><span class="line"> <span class="built_in">skip_bits1</span>(gbc); <span class="comment">/* home */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* adts_variable_header */</span></span><br><span class="line"> <span class="built_in">skip_bits1</span>(gbc); <span class="comment">/* copyright_identification_bit */</span></span><br><span class="line"> <span class="built_in">skip_bits1</span>(gbc); <span class="comment">/* copyright_identification_start */</span></span><br><span class="line"> size = <span class="built_in">get_bits</span>(gbc, <span class="number">13</span>); <span class="comment">/* aac_frame_length */</span></span><br><span class="line"> <span class="keyword">if</span> (size < AV_AAC_ADTS_HEADER_SIZE)</span><br><span class="line"> <span class="keyword">return</span> AAC_AC3_PARSE_ERROR_FRAME_SIZE;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">skip_bits</span>(gbc, <span class="number">11</span>); <span class="comment">/* adts_buffer_fullness */</span></span><br><span class="line"> rdb = <span class="built_in">get_bits</span>(gbc, <span class="number">2</span>); <span class="comment">/* number_of_raw_data_blocks_in_frame */</span></span><br><span class="line"></span><br><span class="line"> hdr->object_type = aot + <span class="number">1</span>;</span><br><span class="line"> hdr->chan_config = ch;</span><br><span class="line"> hdr->crc_absent = crc_abs;</span><br><span class="line"> hdr->num_aac_frames = rdb + <span class="number">1</span>;</span><br><span class="line"> hdr->sampling_index = sr;</span><br><span class="line"> hdr->sample_rate = avpriv_mpeg4audio_sample_rates[sr];</span><br><span class="line"> hdr->samples = (rdb + <span class="number">1</span>) * <span class="number">1024</span>;</span><br><span class="line"> hdr->bit_rate = size * <span class="number">8</span> * hdr->sample_rate / hdr->samples;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> size;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<hr>
<h3 id="AudioSpecificConfig-Compressed-Data"><a href="#AudioSpecificConfig-Compressed-Data" class="headerlink" title="AudioSpecificConfig + Compressed Data"></a>AudioSpecificConfig + Compressed Data</h3><h4 id="AudioSpecificConfig-简述"><a href="#AudioSpecificConfig-简述" class="headerlink" title="AudioSpecificConfig 简述"></a>AudioSpecificConfig 简述</h4><p>上文提到adts每个数据帧都包含描述信息, 这在音频属性不变的情况下会造成一定的带宽和存储空间浪费, AudioSepecificConfig + Compressed Data的封装格式与之不同, 只需要在数据流的起始数包含描述信息AudioSepecificConfig, 后续数据包只包含Compressed Data, 在音频属性改变时, 刷新AudioSepecificConfig 信息即可。 rtmp flv mp4 均使用该种格式传输、存储aac数据。</p>
<h4 id="AudioSpecificConfig-语义"><a href="#AudioSpecificConfig-语义" class="headerlink" title="AudioSpecificConfig 语义"></a>AudioSpecificConfig 语义</h4><p><img src="https://saufwy.github.io/images/aac_sequence_header.gif" alt="aac sequence header"></p>
<h4 id="AudioSpecificConfig-代码生成"><a href="#AudioSpecificConfig-代码生成" class="headerlink" title="AudioSpecificConfig 代码生成"></a>AudioSpecificConfig 代码生成</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码段摘自 faac-1.28</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> FAACAPI <span class="title">faacEncGetDecoderSpecificInfo</span><span class="params">(faacEncHandle hEncoder,<span class="keyword">unsigned</span> <span class="keyword">char</span>** ppBuffer,<span class="keyword">unsigned</span> <span class="keyword">long</span>* pSizeOfDecoderSpecificInfo)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> BitStream* pBitStream = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>((hEncoder == <span class="literal">NULL</span>) || (ppBuffer == <span class="literal">NULL</span>) || (pSizeOfDecoderSpecificInfo == <span class="literal">NULL</span>)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(hEncoder->config.mpegVersion == MPEG2){</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-2</span>; <span class="comment">/* not supported */</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> *pSizeOfDecoderSpecificInfo = <span class="number">2</span>;</span><br><span class="line"> *ppBuffer = <span class="built_in">malloc</span>(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(*ppBuffer != <span class="literal">NULL</span>){</span><br><span class="line"></span><br><span class="line"> <span class="built_in">memset</span>(*ppBuffer,<span class="number">0</span>,*pSizeOfDecoderSpecificInfo);</span><br><span class="line"> pBitStream = <span class="built_in">OpenBitStream</span>(*pSizeOfDecoderSpecificInfo, *ppBuffer);</span><br><span class="line"> <span class="built_in">PutBit</span>(pBitStream, hEncoder->config.aacObjectType, <span class="number">5</span>);</span><br><span class="line"> <span class="built_in">PutBit</span>(pBitStream, hEncoder->sampleRateIdx, <span class="number">4</span>);</span><br><span class="line"> <span class="built_in">PutBit</span>(pBitStream, hEncoder->numChannels, <span class="number">4</span>);</span><br><span class="line"> <span class="built_in">CloseBitStream</span>(pBitStream);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-3</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<hr>
<h3 id="特别说明"><a href="#特别说明" class="headerlink" title="特别说明"></a>特别说明</h3><p>1.ADTS profile 与 AudioSpecificConfig audioObjectType 均标识编码等级, 在mpeg-4下相同的编码等级对应的数值并不一样, 例如 AAC-Main 在 adts帧中用数值0表示, 在AudioSpecificConfig中用1表示, 更多详细信息可参考ISO/IEC 14496-3:2005</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
</section>
</div>
</div>
<div class="sidebar-toggle">
<div class="sidebar-toggle-line-wrap">
<span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
</div>
</div>
<aside id="sidebar" class="sidebar">
<div class="sidebar-inner">
<section class="site-overview-wrap sidebar-panel sidebar-panel-active">
<div class="site-overview">
<div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
<p class="site-author-name" itemprop="name"></p>
<p class="site-description motion-element" itemprop="description"></p>
</div>
<nav class="site-state motion-element">
<div class="site-state-item site-state-posts">
<a href="/archives/">
<span class="site-state-item-count">4</span>
<span class="site-state-item-name">posts</span>
</a>
</div>
<div class="site-state-item site-state-categories">
<a href="/categories/index.html">
<span class="site-state-item-count">4</span>
<span class="site-state-item-name">categories</span>
</a>
</div>
</nav>
</div>
</section>
</div>
</aside>
</div>
</main>
<footer id="footer" class="footer">
<div class="footer-inner">
<div class="copyright">© <span itemprop="copyrightYear">2021</span>
<span class="with-love">
<i class="fa fa-user"></i>
</span>
<span class="author" itemprop="copyrightHolder">fanwenyao</span>
</div>
<div class="powered-by">Powered by <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a></div>
<span class="post-meta-divider">|</span>